web-dev-qa-db-ja.com

UIViewControllerの上部に20pxのギャップがあるのはなぜですか?

問題

次のコントローラー階層を考慮してください。

  • UINavigationController
    • UIViewController
      • UITableViewController

UIViewControllerの存在がレイアウトに影響しています。これがないと、UITableViewControllerUINavigationControllerの境界全体を占有します。

enter image description here

ただし、UIViewControllerUINavigationControllerの間にVanilla UITableViewControllerを追加すると、UIViewControllerの上部とUITableViewControllerの上部の間に20pxのギャップが表示されます。

enter image description here

コードを可能な限り単純なものに減らしても、この動作は観察されます。このアプリ委任コードを検討してください。

public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
    window = new UIWindow(UIScreen.MainScreen.Bounds);

    var tableView = new UITableViewController();
    var intermediateView = new UIViewController();
    var navigation = new UINavigationController(intermediateView);

    navigation.View.BackgroundColor = UIColor.Red;
    intermediateView.View.BackgroundColor = UIColor.Green;
    tableView.View.BackgroundColor = UIColor.Blue;

    intermediateView.AddChildViewController(tableView);
    intermediateView.View.AddSubview(tableView.View);
    tableView.DidMoveToParentViewController(intermediateView);

    window.RootViewController  = navigation;
    window.MakeKeyAndVisible();

    return true;
}

上記のstillは、UIViewの上部とUITableViewの上部との間に20pxのギャップを示しています。

問題に対する私の理解

somethingが誤ってステータスバーにスペースを割り当てていることを理解しています。 Revealを使用すると、FrameUITableViewControllerY値が20

試したこと

失敗した

  • WantsFullScreenLayouttrue、およびその両方でUIViewControllerUITableViewControllerに設定します
  • EdgesForExtendedLayoutExtendedLayoutIncludesOpaqueBarsの両方に対してUIViewControllerUITableViewControllerで遊ぶ
  • AutomaticallyAdjustsScrollViewInsetsUIViewControllerで遊んだ
  • PreservesSuperviewLayoutMarginsUITableViewで遊んだ
  • PrefersStatusBarHiddentrueの両方でUIViewControllerをオーバーライドしてUITableViewControllerを返しました

成功した

ViewDidLayoutSubviewsUITableViewControllerをオーバーライドする:

public override void ViewDidLayoutSubviews()
{
    base.ViewDidLayoutSubviews();
    this.View.Frame = this.View.Superview.Bounds;
}

知りたいこと

  • 私の目標を達成するためのきれいな(より)方法はありますか?
  • 実際に20pxのギャップを追加する原因は何ですか? UIViewControllerUITableViewController
  • view Controllerをさまざまなコンテキストで引き続き使用できるようにするためのベストプラクティスは何ですか?おそらくViewDidLayoutSubviewsをオーバーライドすると、View Controllerがビジュアルツリーのどこに表示されるかについての期待に結び付けられます。コントローラースタックの上位でホストされる場合、物事は正しく見えません。このカップリングを回避して再利用性を高める方法はありますか?
43
Kent Boogaart

表示されている動作はバグではなく、ビューを階層に追加するという誤用の副作用にすぎません。

TableViewをビューに追加するとき、そのtableViewを親に対して相対的にどのようにサイズ設定するかをUIKitに伝える必要があります。次の2つのオプションがあります。 自動レイアウト または自動サイズ変更マスク。ビューのレイアウト方法を説明せずにUIKitを階層にポップするだけで、デフォルトの実装はトップレイアウトガイド(たまたまステータスバーの高さ)の下にビューを配置します。これと同じくらい簡単なことでうまくいきます:

tableVC.View.Frame = rootVC.View.Bounds
tableVC.View.Autoresizingmask = UIViewAutoresizing.FlexibleWidth
tableVC.View.TranslatesAutoresizingMaskIntoConstraints = true// always Nice to explicitly use auto layout

この動作は、実際にはUITableViewControllerだけでなく、UICollectionViewControllerにも排他的です。彼らのデフォルトのviewLoading実装は、ステータスバーの下にビューを挿入すると信じています。 childControllerを単純なUIViewControllerサブクラスにすると、この動作は一切発生しません。ただし、これをバグと見なさないでください。それぞれのビューのレイアウト方法を明示的に宣言する場合、この問題は発生しません。当然、これはコンテナコントローラの主要な機能です。

AppDelegateの外観は次のとおりです。

シャマリン

public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
    window = new UIWindow(UIScreen.MainScreen.Bounds);

    var tableVC = new UITableViewController();
    var rootVC = new UIViewController();
    var navigationVC = new UINavigationController(intermediateView);

    navigationVC.View.BackgroundColor = UIColor.Red;
    rootVC.View.BackgroundColor = UIColor.Green;
    tableVC.View.BackgroundColor = UIColor.Blue;

    rootVC.AddChildViewController(tableVC);
    rootVC.View.AddSubview(tableVC.View);


    //YOU NEED TO CONFIGURE THE VIEWS FRAME
    //If you comment this out you will see the green view under the status bar
    tableVC.View.Frame = rootVC.View.Bounds
    tableVC.View.Autoresizingmask = UIViewAutoresizing.FlexibleWidth
    tableVC.View.TranslatesAutoresizingMaskIntoConstraints = true
    tableVC.DidMoveToParentViewController(rootVC);

    window.RootViewController  = navigationVC;
    window.MakeKeyAndVisible();

    return true;
}

Swift

var window: UIWindow?
var navigationControlller: UINavigationController!

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    let rootVC = UIViewController(nibName: nil, bundle: nil)
    let tableVC = UITableViewController(style: .Plain)
    let navVC = UINavigationController(rootViewController: rootVC)
    navVC.navigationBarHidden = true

    navVC.view.backgroundColor = UIColor.redColor()
    rootVC.view.backgroundColor = UIColor.greenColor()

    rootVC.view.addSubview(tableVC.view)

    //YOU NEED TO CONFIGURE THE VIEWS FRAME
    //If you comment this out you will see the green view under the status bar
    tableVC.view.frame = rootVC.view.bounds
    tableVC.view.autoresizingMask = .FlexibleWidth | .FlexibleHeight
    tableVC.view.setTranslatesAutoresizingMaskIntoConstraints(true)

    rootVC.addChildViewController(tableVC)
    tableVC.didMoveToParentViewController(rootVC)

    window = UIWindow(frame: UIScreen.mainScreen().bounds)
    window?.rootViewController = navVC
    window?.makeKeyAndVisible()

    return true
}

最近投稿 スーパービューを満たすようにビューを構成する方法を説明しました

36
Daniel Galasko

あなたの問題は、UITableViewControllerが別の子コントローラとして直接追加されるという根本的な問題に関連しているようです。

これを見回すことは新しい問題ではありません: iOS 7:ステータスバーの下にUITableViewが表示されます

その記事に関連して、私はあなたのコードを取り、次のオプションを試しました(C#8 ^にあることに気づいたとき):

  1. UITableViewControllerを使用する代わりに、UIViewControllerを追加してから、UITableViewの子としてUIViewControllerを追加します。これを行うと、20ptの問題はありません。これは、問題がUITableViewControllerクラスにあることを強調しています。

    このアプローチは、比較的クリーンな回避策であり、既にあるものに対するいくつかの追加手順のみです。

    元のコードに制約を追加してUITableViewControllerフレームを最上部に強制してみましたが、これを機能させることができませんでした。繰り返しになりますが、これはテーブルコントローラーが物事をオーバーライドすることになる可能性があります。

  2. ストーリーボードを使用してデモを作成すると、すべてが機能します。これは、IB自体がコンテナビューを使用して新しいView Controllerを埋め込むという事実にあると考えています。したがって、ストーリーボードを使用する場合、Appleは、使用する制約のフレームを設定できるビューを追加し、そのビュー内にUITableViewControllerを埋め込みますセグエを埋め込みます。

    したがって、1)のように、中央のビューを使用すると問題が解決するようであり、中央のビューframeを制御することが重要であると思われます。

    あなたの唯一の実行可能な回避策で、フレームを変更することが答えだということに気付きました。ただし、iOS7以降、フレームを変更することは、フレームを操作したい制約と競合する可能性があるため、推奨されないようです。

  3. edgesForExtendedLayoutのような他のオプションを試してみると、すべて失敗したようです。これらはコンテナビューコントローラのヒントのようで、UITableViewControllerはそれらを無視します。

私見は、オプション1)がレイアウトを完全に制御し、後で問題を引き起こす可能性のあるフレームオーバーライドでシステムと戦っていないため、最も安全なアプローチだと思います。オプション2)ストーリーボードを使用する場合にのみ本当に機能します。自分で同じことを手動で行うこともできますが、埋め込みセグエで何が起こっているかは誰が知っていますか?.

[〜#〜] edit [〜#〜]

Arkadiusz Holkoの回答で強調されているように、欠落しているステップがあり、テーブルビューのフレームを明示的に設定すると問題が解決するようです。

8
Rory McKinnel

子View Controllerを追加するときに、1つのステップを見逃しました-ビューのフレームを設定します。

2番目のステップを参照してください。

- (void) displayContentController: (UIViewController*) content;
{
  [self addChildViewController:content];                 // 1
  content.view.frame = [self frameForContentController]; // 2
  [self.view addSubview:self.currentClientView];
  [content didMoveToParentViewController:self];          // 3
}

ソース: https://developer.Apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/CreatingCustomContainerViewControllers/CreatingCustomContainerViewControllers.html

6
Arek Holko

ステータスバーには20ピクセルが使用されます。自動レイアウトでxibファイルまたはストーリーボードを使用している場合は、上部の制約を上部レイアウトガイドに設定して、20ピクセルの差異を処理できます。

4

私の目標を達成するためのきれいな(より)方法はありますか?

フレームを変更するだけです。

tableView.View.Frame = intermediateView.View.Bounds;
tableView.View.AutoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;

実際に20pxのギャップを追加する責任は?UIViewController?UITableViewController?

UIViewControllerを新しく作成したら、UIViewController.view.frameを記録できます。 UITableViewController.view.frameには常に20pxのギャップがあることがわかります。どうして? Appleは、UITableViewController.viewを20pxの上部パディングで画面サイズで初期化するだけです。

View Controllerをさまざまなコンテキストで引き続き使用できるようにするためのベストプラクティスは何ですか?...

UIViewController.viewを別のUIViewController.viewに追加する場合は、ストーリーボードを使用してContainer Viewを使用するのが最善の方法です。

ストーリーボードを使用したくない、または使用できない場合。サブクラスUIViewのみをお勧めします。 addChildViewControllerには、ライフサークルとレイアウトに迷惑な問題がある場合があります。

2
PowHu