最近タブバーの上の情報を載るバーを追加したかったので、UITabBarをカスタマイズしてみました。

実現したい内容:

  • アプリの使用中に、条件によって、バーが表示/非表示に変更できる
  • タブの切り替えはバーの表示に影響を与えない

実装方針


実装イメージ


バーを表示したい時

  1. UITabBarの高さをバーの高さ分上げる
  2. barViewを子ビューとしてUITabBarの上に追加する

バーを非表示したい時

  1. barViewをUITabBarから外す
  2. UITabBarの高さを元に戻す

スペック

  • iPhone 11(iOS 14.0)
  • Xcode 12.0

準備


タブが2つあるアプリを作成し、画面レイアウトの変更をわかりやすくするため、それぞれ黄色と赤の子ビューを追加します。

実装コード

barViewの高さを36.0に設定します。

public static let BarViewHeight: CGFloat = 36.0

UITabBarの高さを変更するため、以下のコードを実行します。

public func showBar() {

    // タブバーの高さを変更する
    object_setClass(tabBar, CustomTabBar.self)

    self.addBarView()

    isBarViewShowing = true
}

class CustomTabBar: UITabBar {
    override func sizeThatFits(_ size: CGSize) -> CGSize {
        super.sizeThatFits(size)
        var sizeThatFits = super.sizeThatFits(size)
        sizeThatFits.height = sizeThatFits.height + CustomTabBarController.BarViewHeight
        return sizeThatFits
    }
}

目立つようにbarViewの背景色を青に設定し、実装後に画面の「表示」ボタンを押すと以下のようになりました。

タブの画像アイコンがbarViewに食い込んでいます。
そのため以下のようにUITabBarの画像位置を調整します。

// 画像を下に18ずらす
for item in (self.tabBar.items)! {
    item.imageInsets = UIEdgeInsets(top: CustomTabBarController.BarViewHeight/2,
                                    left: 0,
                                    bottom: -CustomTabBarController.BarViewHeight/2,
                                    right: 0)
}

これでbarViewは実装イメージ通りに実装できました。

今度はbarViewが非表示の時に元に戻したいので、barViewが非表示の時に下記メソッドを実行します。

public func hideBar() {
    barView.removeFromSuperview()
    // タブバーの高さを変更する
    object_setClass(tabBar, UITabBar.self)

    // 画像を元の位置に戻す
    for item in (self.tabBar.items)! {
        item.imageInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
    }

    isBarViewShowing = false
}

以上のコードを実装し、barViewが表示している状態でタブ切り替えしてからbarViewが非表示になると、以下のような画面崩れが発生してしまいました。

原因はUITabBarの高さが変更されましたが、画面のフレームが更新されていないことでした。そのためbarViewを表示・非表示した後に以下のメソッドを呼び、フレームを強制的に更新する必要があります。

private func currentViewLayoutSubviews() {
    self.viewControllers?[selectedIndex].view.setNeedsLayout()
    self.viewControllers?[selectedIndex].view.layoutSubviews()
}

おわりに

以上でUITabBarのカスタマイズができました。
デザイン変更だけのカスタマイズの経験が多いのですが、UITabBarをリアルタイムに変更するのは初めてなので、実行する前にフレーム更新などは全然考えていませんでした。
UITabBarをカスタマイズする方もいると思うので参考になれば幸いです。



ギャップロを運営しているアップフロンティア株式会社では、一緒に働いてくれる仲間を随時、募集しています。 興味がある!一緒に働いてみたい!という方は下記よりご応募お待ちしております。
採用情報をみる