Skip to content

Instantly share code, notes, and snippets.

@cipolleschi
Created December 16, 2019 09:08
Show Gist options
  • Save cipolleschi/4ff61f0a60fb3eafc774af31c8b32017 to your computer and use it in GitHub Desktop.
Save cipolleschi/4ff61f0a60fb3eafc774af31c8b32017 to your computer and use it in GitHub Desktop.
Custom Tabbar with Containment API
import UIKit
// MARK: - Tabbar VC
// Base view controller of the tabbar
class TabbarViewController: UIViewController {
// list of all the supported tabs
enum Tab: String {
case tab1, tab2, tab3
}
// local state of the tabbar
struct LocalState {
var selectedTab: Tab = .tab1
}
// MARK: - Variable declaration
var localState: LocalState = LocalState()
var rootView: TabbarView {
return self.view as! TabbarView
}
lazy var vcs: [Tab: UIViewController] = {
return [.tab1: VC1(),
.tab2: VC2(),
.tab3: VC3()]
}()
// MARK: - Initialization
override func loadView() {
self.view = TabbarView()
self.setupInteractions()
self.add(vcs[.tab1]!, frame: self.rootView.frame)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view
}
// MARK: - Interactions
func setupInteractions() {
self.rootView.tab1Interaction = { [weak self] in
self?.changeTab(to: .tab1)
}
self.rootView.tab2Interaction = { [weak self] in
self?.changeTab(to: .tab2)
}
self.rootView.tab3Interaction = { [weak self] in
self?.changeTab(to: .tab3)
}
}
// This function makes the magic happens.
// It verifies that the new tab is valid
// and then removes the current tab for and
// adds the new one.
func changeTab(to tab: Tab) {
// changing tab to the same tab
guard tab != self.localState.selectedTab,
let oldVC = self.vcs[self.localState.selectedTab],
let newVC = self.vcs[tab] else {
return
}
oldVC.remove()
self.localState.selectedTab = tab
self.add(newVC, frame: self.rootView.frame)
}
}
// MARK: Containment API primitives
@nonobjc extension UIViewController {
/// Add a view controller as child of the tabbar
///
/// - parameter child: the child vc to add
/// - parameter frame: the available frame for the child vc
func add(_ child: UIViewController, frame: CGRect? = nil) {
addChild(child)
if let frame = frame {
child.view.frame = frame
}
view.addSubview(child.view)
view.sendSubviewToBack(child.view)
child.didMove(toParent: self)
}
/// Remove a vc previously added from the children
func remove() {
willMove(toParent: nil)
view.removeFromSuperview()
removeFromParent()
}
}
// MARK: - MainTabbar View
class TabbarView: UIView {
// Variables
let container = UIView()
let tab1 = UIButton(type: .contactAdd)
let tab2 = UIButton(type: .system)
let tab3 = UIButton(type: .infoLight)
// Interactions
var tab1Interaction: (()->())?
var tab2Interaction: (()->())?
var tab3Interaction: (()->())?
override init(frame: CGRect) {
super.init(frame: frame)
self.setup()
self.style()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
self.setup()
self.style()
}
func setup() {
self.addSubview(self.container)
self.container.addSubview(self.tab1)
self.container.addSubview(self.tab2)
self.container.addSubview(self.tab3)
self.tab1.addTarget(self, action: #selector(self.tab1Tapped(_:)), for: .touchUpInside)
self.tab2.addTarget(self, action: #selector(self.tab2Tapped(_:)), for: .touchUpInside)
self.tab3.addTarget(self, action: #selector(self.tab3Tapped(_:)), for: .touchUpInside)
}
@objc func tab1Tapped(_ sender: UIButton) {
self.tab1Interaction?()
}
@objc func tab2Tapped(_ sender: UIButton) {
self.tab2Interaction?()
}
@objc func tab3Tapped(_ sender: UIButton) {
self.tab3Interaction?()
}
func style() {
self.backgroundColor = .black
self.container.backgroundColor = UIColor.white.withAlphaComponent(0.3)
self.tab1.setTitle("Tab 1", for: .normal)
self.tab2.setTitle("Tab 2", for: .normal)
self.tab3.setTitle("Tab 3", for: .normal)
}
func update() {
}
override func layoutSubviews() {
super.layoutSubviews()
self.container.frame = CGRect(x: 0,
y: self.bounds.height - 50 - self.safeAreaInsets.bottom,
width: self.bounds.width,
height: 50 + self.safeAreaInsets.bottom)
let buttonWidth = self.bounds.width / 3.0
self.tab1.frame = CGRect(x: 0,
y: 10,
width: buttonWidth,
height: 30)
self.tab2.frame = CGRect(x: buttonWidth,
y: 10,
width: buttonWidth,
height: 30)
self.tab3.frame = CGRect(x: 2*buttonWidth,
y: 10,
width: buttonWidth,
height: 30)
}
}
// MARK: - Other VCs
class VC1: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .red
}
}
class VC2: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .green
}
}
class VC3: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .blue
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment