// // Tab.swift // VDS // // Created by Matt Bruce on 5/18/23. // import Foundation import VDSColorTokens import Combine //@objc(VDSCollectionViewCell) //open class CollectionViewCell: UICollectionViewCell, Handlerable, ViewProtocol, Resettable { // // //-------------------------------------------------- // // MARK: - Combine Properties // //-------------------------------------------------- // public var subscribers = Set() // // //-------------------------------------------------- // // MARK: - Properties // //-------------------------------------------------- // private var initialSetupPerformed = false // // open var surface: Surface = .light { didSet { setNeedsUpdate() }} // // open var disabled: Bool = false { didSet { setNeedsUpdate() } } // // public var shouldUpdateView: Bool = true // // //-------------------------------------------------- // // MARK: - Initializers // //-------------------------------------------------- // required public init() { // super.init(frame: .zero) // initialSetup() // } // // public override init(frame: CGRect) { // super.init(frame: .zero) // initialSetup() // } // // public required init?(coder: NSCoder) { // super.init(coder: coder) // initialSetup() // } // // //-------------------------------------------------- // // MARK: - Public Functions // //-------------------------------------------------- // open func initialSetup() { // if !initialSetupPerformed { // setup() // setNeedsUpdate() // } // } // // open func setup() {} // // open func reset() {} // // open func updateView() { // updateAccessibilityLabel() // } // // open func updateAccessibilityLabel() {} // // open override func prepareForReuse() { // surface = .light // disabled = false // } //} // // //@objc(VDSTabsDelegate) //public protocol TabsDelegate { // func shouldSelectItem(_ indexPath: IndexPath, tabs: Tabs) -> Bool // func didSelectItem(_ indexPath: IndexPath, tabs: Tabs) //} // //@objc(VDSTabs) //open class Tabs: View { // // private let layout = UICollectionViewFlowLayout() // public lazy var collectionView: UICollectionView = { // let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) // collectionView.translatesAutoresizingMaskIntoConstraints = false // collectionView.register(TabItemCell.self, forCellWithReuseIdentifier: TabCellId) // collectionView.backgroundColor = .clear // collectionView.showsVerticalScrollIndicator = false // collectionView.showsHorizontalScrollIndicator = false // collectionView.dataSource = self // collectionView.delegate = self // return collectionView // }() // // open var tabItemModels = [TabItemModel]() // // let bottomScrollView = UIScrollView(frame: .zero) // let bottomContentView = View() // let bottomLine = Line() // let selectionLine = View() // var selectionLineLeadingConstraint: NSLayoutConstraint? // var selectionLineWidthConstraint: NSLayoutConstraint? // // private var widthCell = TabItemCell() // // //delegate // weak public var delegate: TabsDelegate? // // //control var // public var selectedIndex: Int = 0 // public var paddingBeforeFirstTab: Bool = true // // //constant // let TabCellId = "TabCell" // public let itemSpacing: CGFloat = 20.0 // public let cellHeight: CGFloat = 28.0 // public let selectionLineHeight: CGFloat = 4.0 // public let minimumItemWidth: CGFloat = 32.0 // public let selectionLineMovingTime: TimeInterval = 0.2 // // //------------------------------------------------- // // MARK:- Layout Views // //------------------------------------------------- // // open override func reset() { // super.reset() // selectedIndex = 0 // paddingBeforeFirstTab = true // } // // open override func setup() { // super.setup() // backgroundColor = VDSColor.backgroundPrimaryLight // addSubview(bottomLine) // setupCollectionView() // setupSelectionLine() // setupConstraints() // } // // func setupCollectionView () { // layout.scrollDirection = .horizontal // layout.minimumLineSpacing = 0 // addSubview(collectionView) // } // // func setupSelectionLine() { // bottomScrollView.translatesAutoresizingMaskIntoConstraints = false // bottomScrollView.delegate = self // addSubview(bottomScrollView) // bottomScrollView.addSubview(bottomContentView) // selectionLine.backgroundColor = VDSColor.paletteRed // bottomContentView.addSubview(selectionLine) // bringSubviewToFront(bottomScrollView) // } // // func setupConstraints() { // //collection view // collectionView // .pinTop() // .pinLeading() // .pinTrailing() // // collectionView.heightAnchor.constraint(greaterThanOrEqualToConstant: cellHeight).isActive = true // // //selection line // bottomScrollView.heightAnchor.constraint(equalToConstant: selectionLineHeight).isActive = true // bottomScrollView.topAnchor.constraint(equalTo: collectionView.bottomAnchor).isActive = true // bottomScrollView // .pinLeading() // .pinTrailing() // // selectionLine.heightAnchor.constraint(equalToConstant: selectionLineHeight).isActive = true // selectionLine // .pinTop() // .pinBottom() // // selectionLineLeadingConstraint = selectionLine.leadingAnchor.constraint(equalTo: bottomContentView.leadingAnchor) // selectionLineLeadingConstraint?.isActive = true // selectionLineWidthConstraint = selectionLine.widthAnchor.constraint(equalToConstant: minimumItemWidth) // selectionLineWidthConstraint?.isActive = true // // bottomContentView.pinToSuperView() // // //bottom line // bottomLine.topAnchor.constraint(equalTo: bottomScrollView.bottomAnchor).isActive = true // bottomLine // .pinBottom() // .pinLeading() // .pinTrailing() // } // // //------------------------------------------------- // // MARK:- Control Methods // //------------------------------------------------- // // public func selectIndex(_ index: Int, animated: Bool) { // guard tabItemModels.count > 0 else { // selectedIndex = index // return // } // // DispatchQueue.main.async { [weak self] in // guard let self else { return } // let currentIndex = self.selectedIndex // self.selectedIndex = index // self.deselect(indexPath: IndexPath(row: currentIndex, section: 0)) // self.selectItem(atIndexPath: IndexPath(row: index, section: 0), animated: animated) // } // } // // public func reloadData() { // collectionView.reloadData() // } //} // ////------------------------------------------------- //// MARK:- Collection View Methods ////------------------------------------------------- // //extension Tabs: UICollectionViewDataSource { // public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { // tabItemModels.count // } // // public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { // guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TabCellId, for: indexPath) as? TabItemCell else { // return UICollectionViewCell() // } // let model = tabItemModels[indexPath.row] // cell.tabsCount = tabItemModels.count // cell.tabSelected = indexPath.row == selectedIndex // cell.text = model.text // cell.onClick = model.onClick // return cell // } //} // //extension Tabs: UICollectionViewDelegateFlowLayout { // // public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { // guard self.collectionView(collectionView, numberOfItemsInSection: indexPath.section) != 2 else { // // If two tabs, take up the screen // let insets = self.collectionView(collectionView, layout: collectionViewLayout, insetForSectionAt: indexPath.section) // let width = (collectionView.bounds.width / 2.0) - insets.left - insets.right // return CGSize(width: width, height: cellHeight) // } // return CGSize(width: max(minimumItemWidth, getLabelWidth(tabItemModels[indexPath.row]).width), height: cellHeight) // } // // //pre calculate the width of the collection cell // //when user select tabs, it will reload related collectionview, if we use autosize, it would relayout the width, need to keep the cell width constant. // func getLabelWidth(_ model: TabItemModel) -> CGSize { // widthCell.text = model.text // let cgSize = widthCell.label.intrinsicContentSize // widthCell.label.reset() // return cgSize // } // // public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { // guard section == 0 else { // return UIEdgeInsets(top: 0, left: itemSpacing, bottom: 0, right: 0) // } // guard paddingBeforeFirstTab else { // return .zero // } // return UIEdgeInsets(top: 0, left: VDSLayout.Spacing.space6X.value, bottom: 0, right: 0) // } // // public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { // // If two tabs, take up the screen, no space between items // guard self.collectionView(collectionView, numberOfItemsInSection: section) != 2 else { // return 0 // } // return itemSpacing // } // // public func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool { // return delegate?.shouldSelectItem(indexPath, tabs: self) ?? true // } // // public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { // selectIndex(indexPath.row, animated: true) // } // // public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { // guard let tabCell = cell as? TabItemCell else { return } // if indexPath.row == selectedIndex { // DispatchQueue.main.async { // self.moveSelectionLine(toIndex: indexPath, animated: false, cell: tabCell) // } // } // } // // func deselect(indexPath:IndexPath) { // collectionView.deselectItem(at: indexPath, animated: false) // collectionView.reloadItems(at: [indexPath]) // } // // func selectItem(atIndexPath indexPath: IndexPath, animated: Bool) { // // guard tabItemModels.count > 0 else { return } // // collectionView.selectItem(at: indexPath, animated: animated, scrollPosition: .centeredHorizontally) // guard let tabCell = collectionView.cellForItem(at: indexPath) as? TabItemCell else { return } // moveSelectionLine(toIndex: indexPath, animated: animated, cell: tabCell) // tabCell.tabSelected = true // tabCell.setNeedsDisplay() // tabCell.setNeedsLayout() // tabCell.layoutIfNeeded() // if let delegate = delegate { // delegate.didSelectItem(indexPath, tabs: self) // } else if let action = tabItemModels[selectedIndex].onClick { // action() // } // if UIAccessibility.isVoiceOverRunning { // UIAccessibility.post(notification: .layoutChanged, argument: tabCell) // } // } //} // // //extension Tabs: UIScrollViewDelegate { // public func scrollViewDidScroll(_ scrollView: UIScrollView) { // /*bottomScrollview is subview of self, it's not belongs to collectionview. // When collectionview is scrolling, bottomScrollView will stay without moving // Adding collectionview's offset to bottomScrollView, will make the bottomScrollview looks like scrolling with the selected tab item. // */ // let offsetX = collectionView.contentOffset.x // bottomScrollView.setContentOffset(CGPoint(x: offsetX, y: bottomScrollView.contentOffset.y), animated: false) // } //} // // ////------------------------------------------------- //// MARK:- Bottom Line Methods ////------------------------------------------------- //extension Tabs { // func moveSelectionLine(toIndex indexPath: IndexPath, animated: Bool, cell: TabItemCell) { // let size = collectionView(collectionView, layout: layout, sizeForItemAt: indexPath) // let animationBlock = { // [weak self] in // self?.selectionLineWidthConstraint?.constant = size.width // self?.selectionLineLeadingConstraint?.constant = cell.frame.origin.x // self?.bottomContentView.layoutIfNeeded() // } // if animated { // UIView.animate(withDuration: selectionLineMovingTime, animations: animationBlock) // } else { // animationBlock() // } // } // // /// Adjust the line based on the percentage // func progress(from index: Int, toIndex: Int, percentage: CGFloat) { // let fromIndexPath = IndexPath(row: index, section: 0) // let toIndexPath = IndexPath(row: toIndex, section: 0) // guard let fromCell = collectionView.cellForItem(at: fromIndexPath), // let toCell = collectionView.cellForItem(at: toIndexPath) else { return } // // // setting the width for percentage // selectionLineWidthConstraint?.constant = (toCell.bounds.width - fromCell.bounds.width) * percentage + fromCell.bounds.width // // // setting the x for percentage // let originalX = fromCell.frame.origin.x // let toX = toCell.frame.origin.x // let xDifference = toX - originalX // let finalX = (xDifference * percentage) + originalX // selectionLineLeadingConstraint?.constant = finalX // // bottomContentView.layoutIfNeeded() // } //} // //@objc(VDSTabItemCell) //open class TabItemCell: CollectionViewCell { // //-------------------------------------------------- // // MARK: - Properties // //-------------------------------------------------- // open var label = Label() // open var text: String = "" { didSet { setNeedsUpdate() }} // open var textStyle: TextStyle = .bodyLarge { didSet { setNeedsUpdate() }} // open var tabSelected: Bool = false { didSet { setNeedsUpdate() }} // open var tabsCount: Int = 0 { didSet { setNeedsUpdate() }} // open var onClick: (()->())? // // open override func setup() { // super.setup() // contentView.addSubview(label) // label // .pinLeading() // .pinTrailing() // .pinBottom(6) // label.baselineAdjustment = .alignCenters // } // // open var selectedColorConfiguration: AnyColorable = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark).eraseToAnyColorable() // // open var unSelectedColorConfiguration: AnyColorable = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark).eraseToAnyColorable() // // open override func updateView() { // super.updateView() // label.text = text // label.textStyle = textStyle // label.surface = surface // label.textColorConfiguration = tabSelected ? selectedColorConfiguration : unSelectedColorConfiguration // } // // open override func reset() { // super.reset() // label.reset() // label.textStyle = .bodyLarge // } // // open override func updateAccessibilityLabel() { // // } //} // //public struct TabItemModel { // public var text: String // public var onClick: (() -> ())? // // public init(text: String, onClick: (() -> Void)? = nil) { // self.text = text // self.onClick = onClick // } //}