mirror of https://github.com/oxen-io/session-ios
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
109 lines
4.5 KiB
Swift
109 lines
4.5 KiB
Swift
4 years ago
|
import UIKit
|
||
5 years ago
|
|
||
4 years ago
|
public final class TabBar : UIView {
|
||
5 years ago
|
private let tabs: [Tab]
|
||
|
private var accentLineViewHorizontalCenteringConstraint: NSLayoutConstraint!
|
||
|
private var accentLineViewWidthConstraint: NSLayoutConstraint!
|
||
|
|
||
|
// MARK: Components
|
||
|
private lazy var tabLabels: [UILabel] = tabs.map { tab in
|
||
|
let result = UILabel()
|
||
|
result.font = .boldSystemFont(ofSize: Values.mediumFontSize)
|
||
|
result.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity)
|
||
|
result.textAlignment = .center
|
||
|
result.text = tab.title
|
||
|
result.set(.height, to: Values.tabBarHeight - Values.separatorThickness - Values.accentLineThickness)
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
private lazy var accentLineView: UIView = {
|
||
|
let result = UIView()
|
||
|
result.backgroundColor = Colors.accent
|
||
|
return result
|
||
|
}()
|
||
|
|
||
|
// MARK: Types
|
||
4 years ago
|
public struct Tab {
|
||
5 years ago
|
let title: String
|
||
|
let onTap: () -> Void
|
||
4 years ago
|
|
||
|
public init(title: String, onTap: @escaping () -> Void) {
|
||
|
self.title = title
|
||
|
self.onTap = onTap
|
||
|
}
|
||
5 years ago
|
}
|
||
|
|
||
|
// MARK: Lifecycle
|
||
4 years ago
|
public init(tabs: [Tab]) {
|
||
5 years ago
|
self.tabs = tabs
|
||
|
super.init(frame: CGRect.zero)
|
||
|
setUpViewHierarchy()
|
||
|
}
|
||
|
|
||
4 years ago
|
public override init(frame: CGRect) {
|
||
5 years ago
|
preconditionFailure("Use init(tabs:) instead.")
|
||
|
}
|
||
|
|
||
4 years ago
|
public required init?(coder: NSCoder) {
|
||
5 years ago
|
preconditionFailure("Use init(tabs:) instead.")
|
||
|
}
|
||
|
|
||
|
private func setUpViewHierarchy() {
|
||
|
set(.height, to: Values.tabBarHeight)
|
||
|
tabLabels.forEach { tabLabel in
|
||
|
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTabLabelTapped(_:)))
|
||
|
tabLabel.addGestureRecognizer(tapGestureRecognizer)
|
||
|
}
|
||
|
let tabLabelStackView = UIStackView(arrangedSubviews: tabLabels)
|
||
|
tabLabelStackView.axis = .horizontal
|
||
|
tabLabelStackView.distribution = .fillEqually
|
||
|
tabLabelStackView.spacing = Values.mediumSpacing
|
||
|
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTabLabelTapped(_:)))
|
||
|
tabLabelStackView.addGestureRecognizer(tapGestureRecognizer)
|
||
|
tabLabelStackView.set(.height, to: Values.tabBarHeight - Values.separatorThickness - Values.accentLineThickness)
|
||
|
addSubview(tabLabelStackView)
|
||
|
let separator = UIView()
|
||
|
separator.backgroundColor = Colors.separator
|
||
|
separator.set(.height, to: Values.separatorThickness)
|
||
|
addSubview(separator)
|
||
|
accentLineView.set(.height, to: Values.accentLineThickness)
|
||
|
addSubview(accentLineView)
|
||
|
tabLabelStackView.pin(.leading, to: .leading, of: self)
|
||
|
tabLabelStackView.pin(.top, to: .top, of: self)
|
||
|
pin(.trailing, to: .trailing, of: tabLabelStackView)
|
||
|
separator.pin(.leading, to: .leading, of: self)
|
||
|
separator.pin(.top, to: .bottom, of: tabLabelStackView)
|
||
|
pin(.trailing, to: .trailing, of: separator)
|
||
|
accentLineView.translatesAutoresizingMaskIntoConstraints = false
|
||
|
selectTab(at: 0, withAnimatedTransition: false)
|
||
|
accentLineView.pin(.top, to: .bottom, of: separator)
|
||
|
pin(.bottom, to: .bottom, of: accentLineView)
|
||
|
}
|
||
|
|
||
|
// MARK: Updating
|
||
4 years ago
|
public func selectTab(at index: Int, withAnimatedTransition isAnimated: Bool = true) {
|
||
5 years ago
|
let tabLabel = tabLabels[index]
|
||
|
accentLineViewHorizontalCenteringConstraint?.isActive = false
|
||
|
accentLineViewHorizontalCenteringConstraint = accentLineView.centerXAnchor.constraint(equalTo: tabLabel.centerXAnchor)
|
||
|
accentLineViewHorizontalCenteringConstraint.isActive = true
|
||
|
accentLineViewWidthConstraint?.isActive = false
|
||
|
accentLineViewWidthConstraint = accentLineView.widthAnchor.constraint(equalTo: tabLabel.widthAnchor)
|
||
|
accentLineViewWidthConstraint.isActive = true
|
||
|
var tabLabelsCopy = tabLabels
|
||
|
tabLabelsCopy.remove(at: index)
|
||
|
UIView.animate(withDuration: isAnimated ? 0.25 : 0) {
|
||
|
tabLabel.textColor = Colors.text
|
||
|
tabLabelsCopy.forEach { $0.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) }
|
||
|
self.layoutIfNeeded()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MARK: Interaction
|
||
|
@objc private func handleTabLabelTapped(_ sender: UITapGestureRecognizer) {
|
||
|
guard let tabLabel = tabLabels.first(where: { $0.bounds.contains(sender.location(in: $0)) }), let index = tabLabels.firstIndex(of: tabLabel) else { return }
|
||
|
selectTab(at: index)
|
||
|
let tab = tabs[index]
|
||
|
tab.onTap()
|
||
|
}
|
||
|
}
|