自作タブ再び。UIPageViewController
を使うやつを以前書きました。
UIPageViewControllerを使って自前のタブ的なものを作る
今回はUIViewController
のみを使います。
成果物: Yaruki00/YKStretchTabViewController
ざっくりと中身説明
ビューの準備
ページの内容を表示するビューと、タブを表示するビューを用意します。
lib/YKStretchTabViewController.swift
...
lazy public var tabView = UIView(frame: CGRect.zero)
...
lazy private var containerView = UIView(frame: CGRect.zero)
...
override public func viewDidLoad() {
super.viewDidLoad()
// サブビューに追加
self.view.addSubview(self.containerView)
self.view.addSubview(self.tabView)
}
override public func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// サブビューのレイアウト調整
self.containerView.frame = self.view.frame
self.tabView.frame = CGRect(x: 0, y: self.view.frame.height - self.tabHeight, width: self.view.frame.width, height: self.tabHeight)
// タブ
let tabWidth = self.tabView.frame.width / CGFloat(self.tabInfoList.count)
var tabX: CGFloat = 0
for (index, tabInfo) in self.tabInfoList.enumerated() {
let tab = tabInfo.tab
tab.frame = CGRect(x: tabX, y: 0, width: ceil(CGFloat(index + 1) * tabWidth) - tabX, height: self.tabView.frame.height)
tabX = ceil(CGFloat(index + 1) * tabWidth)
}
}
...
タブを作る
何処かから渡ってくるViewController
のリスト、タブの選択時・非選択時の画像リストからタブを作ります。タブの実態はボタンです。
lib/YKStretchTabViewController.swift
...
// タブ情報を渡してタブを作る
public func setTabs(contentViewControllerList: [UIViewController], tabSelectedImageList: [UIImage], tabNormalImageList: [UIImage]) {
// エラーチェック
...
// 一旦初期化
for subview in self.tabView.subviews {
subview.removeFromSuperview()
}
self.tabInfoList = []
// タブ準備
for index in 0..<contentViewControllerList.count {
let tab: UIButton = {
let button = UIButton(frame: CGRect.zero)
button.tag = index
button.addTarget(self, action: #selector(YKStretchTabViewController.tabTapped(sender:)), for: .touchUpInside)
self.tabView.addSubview(button)
return button
}()
let tabInfo = TabInfo(vc: contentViewControllerList[index], tab: tab, onImage: tabSelectedImageList[index], offImage: tabNormalImageList[index])
tabInfo.setOffImage()
tab.setBackgroundImage(tabSelectedImageList[index], for: .highlighted)
self.tabInfoList.append(tabInfo)
}
// 最初の画面をセット
if self.selectedTabIndex < self.tabInfoList.count {
let newVC = self.tabInfoList[self.selectedTabIndex].vc
self.addChildViewController(newVC)
self.containerView.addSubview(newVC.view)
newVC.didMove(toParentViewController: self)
self.tabInfoList[self.selectedTabIndex].setOnImage()
}
else {
...
}
}
...
画面を入れ替える
タブが押されたら画面を入れ替えます。ViewController
とView
をそれぞれ新しいものにセットし直すことで画面を入れ替えることができます。
lib/YKStretchTabViewController.swift
...
// タブが押された
func tabTapped(sender: UIButton) {
self.switchContentViewController(index: sender.tag)
}
// 画面入れ替え
public func switchContentViewController(index: Int) {
// エラーチェック
...
// 前の画面を取り除く
self.childViewControllers.first!.willMove(toParentViewController: nil)
self.childViewControllers.first!.view.removeFromSuperview()
self.childViewControllers.first!.removeFromParentViewController()
// 新しい画面をセット
let newVC = self.tabInfoList[index].vc
self.addChildViewController(newVC)
self.containerView.addSubview(newVC.view)
newVC.didMove(toParentViewController: self)
// タブを切り替え
self.tabInfoList[self.selectedTabIndex].setOffImage()
self.selectedTabIndex = index
self.tabInfoList[self.selectedTabIndex].setOnImage()
}
...
使ってみる
デフォルト
初期化して、setTabs(...)
を呼べばokです。
SampleViewController.swift
...
let vc = YKStretchTabViewController()
vc.setTabs(
contentViewControllerList: [
{
let vc = UIViewController()
vc.view.backgroundColor = .red
vc.view.frame = self.view.frame
let label = UILabel(frame: CGRect(x: 200, y: 200, width: 50, height: 20))
label.text = "1"
vc.view.addSubview(label)
return vc
}(),
{
let vc = UIViewController()
vc.view.backgroundColor = .green
vc.view.frame = self.view.frame
let label = UILabel(frame: CGRect(x: 200, y: 200, width: 50, height: 20))
label.text = "2"
vc.view.addSubview(label)
return vc
}(),
{
let vc = UIViewController()
vc.view.backgroundColor = .blue
vc.view.frame = self.view.frame
let label = UILabel(frame: CGRect(x: 200, y: 200, width: 50, height: 20))
label.text = "3"
vc.view.addSubview(label)
return vc
}(),
],
tabSelectedImageList: [
UIImage(named: "tab1:3_on")!,
UIImage(named: "tab2:3_on")!,
UIImage(named: "tab3:3_on")!,
],
tabNormalImageList: [
UIImage(named: "tab1:3_off")!,
UIImage(named: "tab2:3_off")!,
UIImage(named: "tab3:3_off")!,
]
)
self.navigationController?.pushViewController(vc, animated: true)
...
オプション
タブの初期選択位置と高さが変えられます。
SampleViewController.swift
...
let vc = YKStretchTabViewController()
vc.selectedTabIndex = 2
vc.tabHeight = 100
vc.setTabs(
...
おわりに
今回のコードは、タブ領域全体に背景を置きたいという要求があって書いたものです(標準ではアイコン+テキストしかできない)。
UIPageViewController
を使うやつに比べると、だいぶシンプルに書けました。
今回やってないですが、containerView
にGestureRecognizer
をつければ、スワイプでページ切替もできるんじゃないでしょうか。
UIViewController
だけでも親子関係を作れるようになっているので、結構色々なものが自作できそうですね。