Last active
December 17, 2015 23:02
-
-
Save benjaminsnorris/16d844087934a43534fa to your computer and use it in GitHub Desktop.
Custom Tab Bar
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// CustomTabBar.swift | |
// welbe | |
// | |
// Created by Ben Norris on 12/8/15. | |
// Copyright © 2015 O.C. Tanner Corporation. All rights reserved. | |
// | |
import UIKit | |
protocol CustomTabBarDelegate { | |
func tabBar(tabBar: CustomTabBar, didSelectTab tab: Int) | |
} | |
@IBDesignable class CustomTabBar: UIView { | |
// MARK: - Inspectable properties | |
@IBInspectable var underlineHeight: CGFloat = 2.0 { | |
didSet { | |
underlineHeightConstraint?.constant = underlineHeight | |
} | |
} | |
@IBInspectable var underlineOnBottom: Bool = true { | |
didSet { | |
configureUnderlineVerticalPosition() | |
} | |
} | |
@IBInspectable var selectedIndex: Int = 0 { | |
didSet { | |
updateButtons() | |
} | |
} | |
@IBInspectable var accentColor: UIColor = UIColor.blueColor() { | |
didSet { | |
updateColors() | |
} | |
} | |
@IBInspectable var defaultTitleColor: UIColor = UIColor.blackColor() { | |
didSet { | |
updateColors() | |
} | |
} | |
// MARK: - Exposed properties | |
var titles: [String] = ["one", "two"] { | |
didSet { | |
configureButtons() | |
} | |
} | |
var delegate: CustomTabBarDelegate? | |
// MARK: - Private properties | |
private let stackView = UIStackView() | |
private var buttons = [UIButton]() | |
private let underline = UIView() | |
private var underlineHeightConstraint: NSLayoutConstraint? | |
private var underlineWidthConstraint: NSLayoutConstraint? | |
private var underlinePositionContraint: NSLayoutConstraint? | |
private var underlineTopConstraint: NSLayoutConstraint? | |
private var underlineBottomConstraint: NSLayoutConstraint? | |
// MARK: - Method overrides | |
override init(frame: CGRect) { | |
super.init(frame: frame) | |
setupViews() | |
} | |
required init?(coder aDecoder: NSCoder) { | |
super.init(coder: aDecoder) | |
setupViews() | |
} | |
// MARK: - Exposed API | |
func tabSelected(button: UIButton) { | |
guard let index = buttons.indexOf(button) else { fatalError("Invalid button selected") } | |
selectedIndex = index | |
delegate?.tabBar(self, didSelectTab: index) | |
} | |
} | |
// MARK: - Private methods | |
private extension CustomTabBar { | |
func setupViews() { | |
stackView.translatesAutoresizingMaskIntoConstraints = false | |
stackView.distribution = .FillEqually | |
addSubview(stackView) | |
stackView.leadingAnchor.constraintEqualToAnchor(leadingAnchor).active = true | |
stackView.topAnchor.constraintEqualToAnchor(topAnchor).active = true | |
stackView.trailingAnchor.constraintEqualToAnchor(trailingAnchor).active = true | |
stackView.bottomAnchor.constraintEqualToAnchor(bottomAnchor).active = true | |
underline.translatesAutoresizingMaskIntoConstraints = false | |
addSubview(underline) | |
underlineBottomConstraint = underline.bottomAnchor.constraintEqualToAnchor(bottomAnchor) | |
underlineBottomConstraint?.active = true | |
underlineTopConstraint = underline.topAnchor.constraintEqualToAnchor(topAnchor) | |
underlineHeightConstraint = underline.heightAnchor.constraintEqualToConstant(underlineHeight) | |
underlineHeightConstraint?.active = true | |
underlinePositionContraint = underline.leadingAnchor.constraintEqualToAnchor(leadingAnchor) | |
underlinePositionContraint?.active = true | |
configureButtons() | |
configureUnderlineVerticalPosition() | |
updateColors() | |
if selectedIndex < buttons.count { | |
let button = buttons[selectedIndex] | |
tabSelected(button) | |
} | |
} | |
func configureButtons() { | |
for button in buttons { | |
button.removeFromSuperview() | |
} | |
buttons.removeAll() | |
for title in titles { | |
createButton(title) | |
} | |
configureUnderlineWidth() | |
if !(selectedIndex < buttons.count) { | |
fatalError("Invalid selection for buttons: \(selectedIndex)") | |
} | |
let selectedButton = buttons[selectedIndex] | |
tabSelected(selectedButton) | |
} | |
func createButton(title: String?) { | |
let button = UIButton(type: .System) | |
button.setTitle(title, forState: .Normal) | |
button.setTitleColor(defaultTitleColor, forState: .Normal) | |
button.titleLabel?.font = UIFont.appFont(17.0) | |
button.addTarget(self, action: "tabSelected:", forControlEvents: .TouchUpInside) | |
stackView.addArrangedSubview(button) | |
buttons.append(button) | |
} | |
func configureUnderlineWidth() { | |
underlineWidthConstraint = underline.widthAnchor.constraintEqualToAnchor(widthAnchor, multiplier: 1 / CGFloat(titles.count)) | |
underlineWidthConstraint?.active = true | |
} | |
func configureUnderlineVerticalPosition() { | |
if underlineOnBottom { | |
underlineBottomConstraint?.active = true | |
underlineTopConstraint?.active = false | |
} else { | |
underlineBottomConstraint?.active = false | |
underlineTopConstraint?.active = true | |
} | |
} | |
func updateColors() { | |
underline.backgroundColor = accentColor | |
for button in buttons { | |
button.setTitleColor(defaultTitleColor, forState: .Normal) | |
button.setTitleColor(accentColor, forState: .Highlighted) | |
} | |
} | |
func updateButtons() { | |
if !(selectedIndex < buttons.count) { | |
fatalError("Invalid index for buttons—selectedIndex: \(selectedIndex)") | |
} | |
for button in buttons { | |
button.setTitleColor(defaultTitleColor, forState: .Normal) | |
} | |
let currentSelection = buttons[selectedIndex] | |
currentSelection.setTitleColor(accentColor, forState: .Normal) | |
// TODO: Add animation | |
let position = frame.size.width / CGFloat(titles.count) * CGFloat(selectedIndex) | |
self.underlinePositionContraint?.constant = position | |
UIView.animateWithDuration(0.3, delay: 0.0, usingSpringWithDamping: 0.7, initialSpringVelocity: 1.0, options: [], animations: { () -> Void in | |
self.layoutIfNeeded() | |
}, completion: nil) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment