LoginSignup
29
26

More than 5 years have passed since last update.

iPhoneでテザリング中にレイアウトが崩れる問題の解決方法

Last updated at Posted at 2016-05-13

TL;DR

  • iPhoneでテザリング中、UITabBarが画面からはみ出したりすることへの対応方法です。
  • RootViewControllerのviewにUITabBarControllerのviewをaddSubview:するときは、親viewの中に収まるようにAutoLayoutを設定します。
  • scrollView.contentInsetの調整にはtopLayoutGuide.topを使います。
  • サンプルアプリ

何が起きていたか

  • テザリング中や通話中などにレイアウトが崩れる
  • UITabBarが20pts下がって、画面からはみ出しまう

before.png

ViewController構成


UIViewController   // RootViewController
|- UITabBarController
   |- UINavigationController
   |  |- UITableViewController
   |- UINavigationController   
      |- UITableViewController
  • RootViewController内のviewDidLoadUITabBarControllerをコードで追加しています
  • UITabBarControllerをRootViewControllerとするXcode Projectでは、この問題は発生しません

Viewデバッガで見てみる

下記のような位置関係になっているため、 UITabBar が切れて見えます。

// UIWindowからの相対的なframe
UIWindow: (0, 0, 375, 667)
RootViewController: (0, 20, 375, 647)
UITabBarController: (0, 40, 375, 647)

before_view_debugger.png

UITabBarがはみ出してしまう問題の対応

UITabBarControllerのviewをaddSubviewした後、AutoLayoutを設定してUITabBarControllerのviewがsuperviewの中に収まるようにします

Before

// RootViewController.swift

override func viewDidLoad() {
    super.viewDidLoad()

    let tc: UITabBarController = createTabBarController()
    addChildViewController(tc)
    view.addSubview(tc.view)
    tc.didMoveToParentViewController(self)
}

After

// RootViewController.swift

override func viewDidLoad() {
    super.viewDidLoad()

    let tc: UITabBarController = createTabBarController()
    addChildViewController(tc)
    view.addSubview(tc.view)

    // view の中に収まるように、tabBarController.view に constraintを設定
    view.addFittingConstraintsFor(tc.view)

    tabBarController.didMoveToParentViewController(self)
}
extension UIView {

    /**
     childViewが同じサイズに収まるように、constraintsを設定する

     - parameter childView: 子View
     */
    func addFittingConstraintsFor(childView: UIView) {
        let constraints = [.Top, .Leading, .Bottom, .Trailing].map {
            NSLayoutConstraint(
                item: childView,
                attribute: $0,
                relatedBy: .Equal,
                toItem: self,
                attribute: $0,
                multiplier: 1.0,
                constant: 0.0)
        }
        childView.translatesAutoresizingMaskIntoConstraints = false
        addConstraints(constraints)
    }
}


## 修正結果

after.png

after_view_debugger.png

UITabBarControllerのviewはRootViewControllerのviewと同じ位置、サイズになりました。

コンテンツ開始位置のズレ

このサンプルでは特に問題はありませんが、テザリング中にUITableViewのコンテンツ開始位置がズレる現象もよく見かけます。

ステータスバーのサイズ

UIApplicationstatusBarFrameが変わります。
テザリング中は見た目通り、高さが40で返ってきます。

// 通常時
statusBarFrame: (0, 0, 375, 20)

// テザリング中
statusBarFrame: (0, 0, 375, 40)

しかし、上記のViewデバッガで見てわかるように、RootViewControllerが20だけ下がります。

下記のようなコードを書くと、通常時と比べてコンテンツ開始位置が20だけ下がって見えてしまうでしょう。

// 通常時: 20 / テザリング時: 40
scrollView.contentInset.top = statusBarFrame.height

topLayoutGuide を使う

UIViewControllertopLayoutGuideはテザリング中でも値が変わりません。

topLayotGuide.topはステータスバーやナビゲーションバーの高さも考慮した値を返します。
ランドスケープ時にステータスバーが消えた場合は、ナビゲーションバーの高さだけ返してくれます。

// 通常時: 20 / テザリング時: 20
scrollView.contentInset.top = topLayoutGuide.top

所感

特に情報が見つからなかったので、独自の解決策です。
もっと良い方法や、Appleの公式なドキュメントがあれば教えて下さい。

UITabBarControllerをRootViewControllerとしてStoryboardで実装した場合は、今回の問題は発生しませんでした。

国内外・有名無名問わず、いろんなアプリで同じようなレイアウトの崩れがいくつか見られます。
開発者が意識することなく、うまいことiOS側で管理してほしいものです。

29
26
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
26