24

ナビゲーション コントローラーにラップされたテーブル ビュー コントローラーがあります。ナビゲーション コントローラは、テーブル ビュー コントローラを介して提示されると、正しいコンテンツ インセットを自動的に適用するようですpresentViewController:animated:completion:。(これが正確にどのように機能するかを誰かに説明できますか?)

ただし、組み合わせをカスタム コンテナー ビュー コントローラーでラップして表示するとすぐに、テーブル ビュー コンテンツの最上部がナビゲーション バーの後ろに隠れてしまいます。この構成でコンテンツの自動インセット動作を維持するためにできることはありますか? これを正しく機能させるには、コンテナー ビュー コントローラーで何かを「通過」する必要がありますか?

iOS 5 のサポートを継続したいので、コンテンツ インセットを手動または自動レイアウトで調整する必要は避けたいと思います。

4

6 に答える 6

12

同様の問題がありました。そのため、iOS 7 には、という新しいプロパティがありUIViewControllerますautomaticallyAdjustsScrollViewInsets。デフォルトでは、これは に設定されていYESます。ビュー階層が、ナビゲーションまたはタブ バー コントローラー内のスクロール/テーブル ビューよりも複雑な場合、このプロパティは機能しないように思われました。

私がしたことは次のとおりです。他のすべてのView Controllerが継承する基本View Controllerクラスを作成しました。そのView Controllerは、インセットを明示的に設定します。

ヘッダ:

#import <UIKit/UIKit.h>

@interface SPWKBaseCollectionViewController : UICollectionViewController

@end

実装:

#import "SPWKBaseCollectionViewController.h"

@implementation SPWKBaseCollectionViewController


- (void)viewDidLoad
{
    [super viewDidLoad];

    [self updateContentInsetsForInterfaceOrientation:self.interfaceOrientation];
}

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [self updateContentInsetsForInterfaceOrientation:toInterfaceOrientation];

    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
}

- (void)updateContentInsetsForInterfaceOrientation:(UIInterfaceOrientation)orientation
{
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
        UIEdgeInsets insets;

        if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
            insets = UIEdgeInsetsMake(64, 0, 56, 0);
        } else if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
            if (UIInterfaceOrientationIsPortrait(orientation)) {
                insets = UIEdgeInsetsMake(64, 0, 49, 0);
            } else {
                insets = UIEdgeInsetsMake(52, 0, 49, 0);
            }
        }

        self.collectionView.contentInset = insets;
        self.collectionView.scrollIndicatorInsets = insets;
    }
}

@end

これは Web ビューでも機能します。

self.webView.scrollView.contentInset = insets;
self.webView.scrollView.scrollIndicatorInsets = insets;

これを行うためのよりエレガントで信頼性の高い方法がある場合は、お知らせください。ハードコーディングされたインセット値はかなり悪臭を放っていますが、iOS 5 との互換性を維持したい場合、別の方法はないと思います。

于 2013-10-08T18:54:06.860 に答える
3

UINavigationController は、コンテンツ インセットを更新するために、コントローラー間の遷移で文書化されていないメソッド _updateScrollViewFromViewController:toViewController を呼び出します。これが私の解決策です:

UINavigationController+ContentInset.h

#import <UIKit/UIKit.h>
@interface UINavigationController (ContentInset)
- (void) updateScrollViewFromViewController:(UIViewController*) from toViewController:(UIViewController*) to;
@end

UINavigationController+ContentInset.m

#import "UINavigationController+ContentInset.h"
@interface UINavigationController()
- (void) _updateScrollViewFromViewController:(UIViewController*) from toViewController:(UIViewController*) to;
@end

@implementation UINavigationController (ContentInset)
- (void) updateScrollViewFromViewController:(UIViewController*) from toViewController:(UIViewController*) to {
    if ([UINavigationController instancesRespondToSelector:@selector(_updateScrollViewFromViewController:toViewController:)])
        [self _updateScrollViewFromViewController:from toViewController:to];
}
@end

カスタム コンテナ ビュー コントローラのどこかで updateScrollViewFromViewController:toViewController を呼び出します

[self addChildViewController:newContentViewController];
[self transitionFromViewController:previewsContentViewController
                  toViewController:newContentViewController
                          duration:0.5f
                           options:0
                        animations:^{
                            [self.navigationController updateScrollViewFromViewController:self toViewController:newContentViewController];

                    }
                    completion:^(BOOL finished) {
                        [newContentViewController didMoveToParentViewController:self];
                    }];
于 2014-01-17T12:01:07.570 に答える
3

ドキュメント化されていないメソッドを呼び出す必要はありません。に電話するだけsetNeedsLayoutですUINavigationController。この他の回答を参照してください: https://stackoverflow.com/a/33344516/5488931

于 2015-10-26T11:26:53.997 に答える
2

迅速なソリューション

このソリューションは、Swift の iOS 8 以降を対象としています。

私のアプリのルート ビュー コントローラーはナビゲーション コントローラーです。最初に、このナビゲーション コントローラーはUIViewControllerスタック上に 1 つあり、これを親ビュー コントローラーと呼びます。

ユーザーがナビゲーション バーのボタンをタップすると、親ビュー コントローラーは、コンテインメント API を使用して、ビュー上の 2 つの子ビュー コントローラーを切り替えます (マップされた結果とリストされた結果を切り替えます)。親ビュー コントローラーは、2 つの子ビュー コントローラーへの参照を保持します。2 番目の子 View Controller は、ユーザーがトグル ボタンを初めてタップするまで作成されません。automaticallyAdjustsScrollViewInsets2 番目の子ビュー コントローラーはテーブル ビュー コントローラーであり、プロパティの設定方法に関係なく、ナビゲーション バーの下に重なるという問題が発生しました。

これを修正するために、子テーブル ビュー コントローラーが作成された後、親ビュー コントローラーのメソッド内で呼び出しますadjustChild:tableView:insetTop(以下を参照) 。viewDidLayoutSubviews

子View Controllerのテーブルビューと親View Controllerのテーブルビューは、このようtopLayoutGuide.lengthにメソッドに渡されadjustChild:tableView:insetTopます...

// called right after childViewController is created
adjustChild(childViewController.tableView, insetTop: topLayoutGuide.length)

AdjustChild メソッドは...

private func adjustChild(tableView: UITableView, insetTop: CGFloat) {
    tableView.contentInset.top = insetTop
    tableView.scrollIndicatorInsets.top = insetTop

    // make sure the tableview is scrolled at least as far as insetTop
    if (tableView.contentOffset.y > -insetTop) {
        tableView.contentOffset.y = -insetTop
    }
}

このステートメントtableView.scrollIndicatorInsets.top = insetTopは、テーブル ビューの右側にあるスクロール インジケーターを調整して、ナビゲーション バーのすぐ下から開始するようにします。これは微妙で、気がつくまでは見過ごされがちです。

以下は、次のviewDidLayoutSubviewsように見えるものです...

override func viewDidLayoutSubviews() {
    if let childViewController = childViewController {
        adjustChild(childViewController.tableView, insetTop: topLayoutGuide.length)
    }
}

上記のコードはすべて、親ビュー コントローラーに表示されることに注意してください。

Christopher Pickslay の「iOS 7 のテーブル ビューでコンテンツ インセットを自動調整できない」という質問に対する回答の助けを借りて、これを理解しました。

于 2015-10-15T18:25:11.793 に答える