xyk blog

最近は iOS 開発の記事が多めです。

UITabBar の中央のタブを大きな画像ボタンに変更する

検証環境:
Xcode 12.3
Swift 5.3.2

※追記

この記事の実装方法では問題があることが発覚したので新しい記事で修正版を書いた。

xyk.hatenablog.com


多くのアプリでよく見かける、タブバーの中央に大きな画像ボタンを配置する方法について。

f:id:xyk:20201223181426p:plain

やり方はいろいろあるが、今回は中央のタブの上に別の UIView を貼り付ける方法で実現する。

実装例

まずは5つのタブがあるタブバーを用意する。
今回は StoryBoard 上で UITabBarController を配置、そこに5つの ViewController を接続する。

f:id:xyk:20201223000751p:plain

次に UITabBarController を継承したサブクラスを作成し、StoryBoard の UITabBarController のクラスとして設定する。
このクラス内で画像ボタンのビュー(今回は UIButton)を生成して貼り付け、 tabBar の中央に位置するように制約を付ける。

import UIKit

class MyTabBarController: UITabBarController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        delegate = self
        
        setupMiddleButton()
    }
    
    private func setupMiddleButton() {
        let middleButtonHeight: CGFloat = 100
        let middleButton = UIButton(type: .custom)
        middleButton.addTarget(self, action: #selector(handleMiddleButton), for: .touchUpInside)
        middleButton.setImage(UIImage.init(named: "play"), for: .normal)

        middleButton.translatesAutoresizingMaskIntoConstraints = false
        view.insertSubview(middleButton, aboveSubview: tabBar)
        middleButton.widthAnchor.constraint(equalToConstant: middleButtonHeight).isActive = true
        middleButton.heightAnchor.constraint(equalToConstant: middleButtonHeight).isActive = true
        middleButton.centerXAnchor.constraint(equalTo: tabBar.centerXAnchor).isActive = true
        let heightDifference = (tabBar.frame.height / 2) - (middleButtonHeight / 2)
        middleButton.topAnchor.constraint(equalTo: tabBar.topAnchor, constant: heightDifference).isActive = true
    }
    
    @objc func handleMiddleButton(_ sender: UIButton) {
        print("handleMiddleButton")
    }
}

extension MyTabBarController: UITabBarControllerDelegate {
    
    func tabBarController(_ tabBarController: UITabBarController,
                          shouldSelect viewController: UIViewController) -> Bool {
        // 選択されたタブが中央の場合は画面遷移が発生しないように false を返す。
        // ここでは中央のタブの viewController に ViewController2 を設定していたとする。
        // ちなみに tabBarController.selectedIndex はまだ遷移前の index が入っているので判定には使えない。  
        if viewController is ViewController2 {
            return false
        }
        return true
    }
    
    func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
        print("didSelect: \(tabBarController.selectedIndex)")
    }
}

UIButton の貼り付け先は UITabBarController.tabBar ではなく、UITabBarController.view にしている。
理由は tabBar に貼り付けると親ビューである tabBar の矩形からはみ出したビューに対してのタッチイベントが反応しなくなるため。