129

新しい iOS7 の Facebook iPhone アプリでは、ユーザーが上にスクロールすると、 はnavigationBar徐々に非表示になり、完全に消えます。次に、ユーザーが下にスクロールすると、navigationBar徐々に表示されます。

この動作を自分でどのように実装しますか? 次の解決策を認識していますが、すぐに消えてしまい、ユーザーのスクロール ジェスチャの速度とはまったく関係がありません。

[navigationController setNavigationBarHidden: YES animated:YES];

「拡張/収縮」動作をどのように説明するのが最適かがわからないため、これが重複していないことを願っています。

4

20 に答える 20

52

編集: iOS 8 以降のみ。

使用してみることができます

self.navigationController.hidesBarsOnSwipe = YES;

私のために働きます。

迅速にコーディングする場合は、この方法を使用する必要があります(https://stackoverflow.com/a/27662702/2283308から)

navigationController?.hidesBarsOnSwipe = true
于 2015-02-24T15:59:55.750 に答える
43

ここにもう 1 つの実装があります: TLYShyNavBar v1.0.0 がリリースされました!

提供されたソリューションを試した後、自分で作成することにしましたが、パフォーマンスが悪いか、エントリと定型コードの障壁が高いか、ナビゲーションバーの下に拡張ビューが欠けていたかのいずれかでした。このコンポーネントを使用するには、次のことを行う必要があります。

self.shyNavBarManager.scrollView = self.scrollView;

ああ、それは私たち自身のアプリでバトルテストされています.

于 2014-06-27T14:28:07.737 に答える
33

私のGTScrollNavigationBarをご覧ください。UIScrollView のスクロールに基づいてスクロールするように、UINavigationBar をサブクラス化しました。

注: OPAQUE ナビゲーション バーがある場合、ナビゲーション バーが非表示になるため、スクロール ビューを展開する必要があります。これはまさに GTScrollNavigationBar が行うことです。(たとえば、iOS の Safari と同様です。)

于 2013-12-21T15:11:46.307 に答える
12

私はそのためのある種の迅速で汚い解決策を持っています。詳細なテストは行っていませんが、アイデアは次のとおりです。

そのプロパティは、私の UITableViewController クラスのナビゲーションバーにすべてのアイテムを保持します

@property (strong, nonatomic) NSArray *navBarItems;

私が持っている同じ UITableViewController クラスで:

-(void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
{
    if([[[UIDevice currentDevice] systemVersion] floatValue] < 7.0f){
        return;
    }

    CGRect frame = self.navigationController.navigationBar.frame;
    frame.origin.y = 20;

    if(self.navBarItems.count > 0){
        [self.navigationController.navigationBar setItems:self.navBarItems];
    }

    [self.navigationController.navigationBar setFrame:frame];
}

-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if([[[UIDevice currentDevice] systemVersion] floatValue] < 7.0f){
        return;
    }

    CGRect frame = self.navigationController.navigationBar.frame;
    CGFloat size = frame.size.height - 21;

    if([scrollView.panGestureRecognizer translationInView:self.view].y < 0)
    {
        frame.origin.y = -size;

        if(self.navigationController.navigationBar.items.count > 0){
            self.navBarItems = [self.navigationController.navigationBar.items copy];
            [self.navigationController.navigationBar setItems:nil];
        }
    }
    else if([scrollView.panGestureRecognizer translationInView:self.view].y > 0)
    {
        frame.origin.y = 20;

        if(self.navBarItems.count > 0){
            [self.navigationController.navigationBar setItems:self.navBarItems];
        }
    }

    [UIView beginAnimations:@"toggleNavBar" context:nil];
    [UIView setAnimationDuration:0.2];
    [self.navigationController.navigationBar setFrame:frame];
    [UIView commitAnimations];
}

それは ios >= 7 の場合のみです。醜いことはわかっていますが、これを達成するための簡単な方法です。コメント/提案は大歓迎です:)

于 2013-12-11T09:26:20.143 に答える
10

これが私の実装です: SherginScrollableNavigationBar

私のアプローチでは、 の状態KVOを監視するUIScrollViewために使用しているため、デリゲートを使用する必要はありません (このデリゲートは、他に必要なものに使用できます)。

于 2014-04-02T17:21:17.657 に答える
4

Iwburk の回答に加えて、非カスタム ナビゲーション バーのアルファの問題を修正し、viewWillDisappear メソッドでナビゲーション バーをリセットするために、次を追加しました。

- (void)updateBarButtonItems:(CGFloat)alpha
{
    for (UIView *view in self.navigationController.navigationBar.subviews) {
        NSString *className = NSStringFromClass([view class]);

        if ( ![className isEqualToString:@"_UINavigationBarBackground"] ) {
            view.alpha = alpha;
        }
    }
}

- (void)resetNavigationBar {
    CGRect frame = self.navigationController.navigationBar.frame;
    frame.origin.y = 20;
    [self.navigationController.navigationBar setFrame:frame];
    [self updateBarButtonItems:1.0f];
}
于 2014-03-21T15:41:40.230 に答える
3

UITableView についてカスタマイズされたヘッダーが必要な状況で、この動作をエミュレートしようとしていました。これはページ上の他の多くのものの下にあり、セクションヘッダーがデフォルトの「ドッキング」動作に従うようにしたかったので、私は独自の「ナビゲーション」バーをロールしました。私は、UITableView/UIScrollView を別のオブジェクトと一緒に、Facebook/Instagram/Chrome などで見られるのと同様のスタイルで調整するための、かなり巧妙で簡潔な方法を見つけたと思います。アプリ。

私の .xib ファイルでは、コンポーネントをフリーフォーム ビューにロードしています: http://imgur.com/0z9yebJ (申し訳ありませんが、画像をインライン化する担当者はいません)

左側のサイドバーでは、テーブルがメイン ヘッダー ビューの後ろに配置されていることに注意してください。スクリーンショットからはわかりませんが、メインのヘッダー ビューと同じ y 位置もあります。見えなくなっているため、UITableView の contentInset プロパティは 76 (メイン ヘッダー ビューの高さ) に設定されています。

UIScrollView と連動してメイン ヘッダー ビューをスライドさせるには、UIScrollViewDelegate の scrollViewDidScroll メソッドを使用していくつかの計算を実行し、UIScrollView の contentInset とメイン ヘッダー ビューのフレームを変更します。

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    UIEdgeInsets insets = scrollView.contentInset;
    //tableViewInsetDelta and tableViewOriginalInsetValue are NSInteger variables that I set to 0 and 76, respectively, in viewDidLoad
    tableViewInsetDelta = tableViewOriginalInsetValue + scrollView.contentOffset.y;
    insets.top = tableViewOriginalInsetValue - tableViewInsetDelta;

    if (scrollView.contentOffset.y > -76 && scrollView.contentOffset.y < 0) {
        [scrollView setContentInset:insets];
        self.pathTitleContainer.frame = CGRectMake(self.pathTitleContainer.frame.origin.x, 44 - tableViewInsetDelta, self.pathTitleContainer.frame.size.width, self.pathTitleContainer.frame.size.height);
    } else if (scrollView.contentOffset.y > 0) {
        insets.top = 0;
        [scrollView setContentInset:insets];
        self.pathTitleContainer.frame = CGRectMake(self.pathTitleContainer.frame.origin.x, -32, self.pathTitleContainer.frame.size.width, self.pathTitleContainer.frame.size.height);
    } else if (scrollView.contentOffset.y < -76) {
        insets.top = 76;
        [scrollView setContentInset:insets];
        self.pathTitleContainer.frame = CGRectMake(self.pathTitleContainer.frame.origin.x, 44, self.pathTitleContainer.frame.size.width, self.pathTitleContainer.frame.size.height);
    }
}

最初のifステートメントは手間のかかる作業のほとんどを行いますが、ユーザーが無理にドラッグし、scrollViewDidScroll に送信される contentOffset の初期値が最初のifステートメントの範囲外である状況に対処するために、他の 2 つを含める必要がありました。

最終的に、これは私にとって非常にうまく機能しています。肥大化したサブクラスの束をプロジェクトに積み込むのは嫌いです。これがパフォーマンスの点で最良のソリューションであるかどうかはわかりません (常に呼び出されるため、scrollViewDidScroll にコードを配置することを常に躊躇してきました) が、コードのフットプリントは、これまでに見た中で最小です。この問題の解決策であり、UIScrollView で UITableView をネストする必要はありません (Apple はドキュメントでこれに対してアドバイスしており、タッチ イベントは UITableView で少しファンキーになります)。これが誰かを助けることを願っています!

于 2014-08-28T21:27:11.937 に答える
2

GTScrollNavigationBar を実装しようとしましたが、アプリで自動レイアウト制約を変更する必要がありました。他の誰かが自動レイアウトでこれを行う必要がある場合に備えて、実装の例を GitHub に掲載することにしました。他のほとんどの実装で私が抱えていたもう1つの問題は、スクロールビューのサイズを同時にスクロールして調整するときに作成する視差スクロール効果を回避するために、スクロールビューの境界を設定しないことです。

自動レイアウトでこれを行う必要がある場合は、JSCollapsingNavBarViewControllerを確認してください。2 つのバージョンを含めました。1 つはナビゲーション バーのみ、もう 1 つはナビゲーション バーの下にサブバーがあり、ナビゲーション バーが折りたたまれる前に折りたたまれます。

于 2014-07-07T13:22:40.577 に答える
1

@Iwburk の回答の拡張...ナビゲーション バーの原点を変更する代わりに、ナビゲーション バーのサイズを拡大/縮小する必要がありました。

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    CGRect frame = self.previousRect; // a property set in the init method to hold the initial size of the uinavigationbar
    CGFloat size = frame.size.height;
    CGFloat framePercentageHidden = ((MINIMUMNAVBARHEIGHT - frame.origin.y) / (frame.size.height - 1));
    CGFloat scrollOffset = scrollView.contentOffset.y;
    CGFloat scrollDiff = scrollOffset - self.previousScrollViewYOffset;
    CGFloat scrollHeight = scrollView.frame.size.height;
    CGFloat scrollContentSizeHeight = scrollView.contentSize.height + scrollView.contentInset.bottom;

    if (scrollOffset <= -scrollView.contentInset.top) {
        frame.origin.y = -MINIMUMNAVBARHEIGHT;
    } else if ((scrollOffset + scrollHeight) >= scrollContentSizeHeight) {
        frame.origin.y = -size;
    } else {
        frame.origin.y = MIN(-MINIMUMNAVBARHEIGHT, MAX(-size, frame.origin.y - scrollDiff));
    }

    self.previousRect = CGRectMake(0, frame.origin.y, self.jsExtendedBarView.frame.size.width, 155);
    self.layoutConstraintExtendedViewHeight.constant = MAXIMUMNAVBARHEIGHT + frame.origin.y + MINIMUMNAVBARHEIGHT;
    [self updateBarButtonItems:(1 - framePercentageHidden)];
    self.previousScrollViewYOffset = scrollOffset;
}

それはstoppedScrollingまだメソッドで動作しません。私がそれを持っているときに更新を投稿してください

于 2015-11-03T00:13:34.013 に答える