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.
		
		
		
		
		
			
		
			
				
	
	
		
			112 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Swift
		
	
			
		
		
	
	
			112 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Swift
		
	
| import UIKit
 | |
| 
 | |
| public final class TabBar : UIView {
 | |
|     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.mediumOpacity)
 | |
|         result.textAlignment = .center
 | |
|         result.text = tab.title
 | |
|         result.set(.height, to: TabBar.snHeight - Values.separatorThickness - Values.accentLineThickness)
 | |
|         return result
 | |
|     }
 | |
|     
 | |
|     private lazy var accentLineView: UIView = {
 | |
|         let result = UIView()
 | |
|         result.backgroundColor = Colors.accent
 | |
|         return result
 | |
|     }()
 | |
|     
 | |
|     // MARK: Types
 | |
|     public struct Tab {
 | |
|         let title: String
 | |
|         let onTap: () -> Void
 | |
| 
 | |
|         public init(title: String, onTap: @escaping () -> Void) {
 | |
|             self.title = title
 | |
|             self.onTap = onTap
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     // MARK: Settings
 | |
|     public static let snHeight = isIPhone5OrSmaller ? CGFloat(32) : CGFloat(48)
 | |
|     
 | |
|     // MARK: Lifecycle
 | |
|     public init(tabs: [Tab]) {
 | |
|         self.tabs = tabs
 | |
|         super.init(frame: CGRect.zero)
 | |
|         setUpViewHierarchy()
 | |
|     }
 | |
|     
 | |
|     public override init(frame: CGRect) {
 | |
|         preconditionFailure("Use init(tabs:) instead.")
 | |
|     }
 | |
|     
 | |
|     public required init?(coder: NSCoder) {
 | |
|         preconditionFailure("Use init(tabs:) instead.")
 | |
|     }
 | |
|     
 | |
|     private func setUpViewHierarchy() {
 | |
|         set(.height, to: TabBar.snHeight)
 | |
|         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: TabBar.snHeight - 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
 | |
|     public func selectTab(at index: Int, withAnimatedTransition isAnimated: Bool = true) {
 | |
|         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.mediumOpacity) }
 | |
|             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()
 | |
|     }
 | |
| }
 |