You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

154 lines
5.0 KiB

  1. //
  2. // TabbedView.swift
  3. // GME Remit
  4. //
  5. // Created by Armaan Shrestha on 21/08/2022.
  6. // Copyright © 2022 Gobal Money Express Co. Ltd. All rights reserved.
  7. //
  8. import UIKit
  9. protocol TabbedViewDelegate: AnyObject {
  10. func didMoveToTab(at index: Int)
  11. }
  12. class TabbedView: UIView {
  13. enum SizeConfiguration {
  14. case fillEqually(height: CGFloat, spacing: CGFloat = 0)
  15. case fixed(width: CGFloat, height: CGFloat, spacing: CGFloat = 0)
  16. var height: CGFloat {
  17. switch self {
  18. case let .fillEqually(height, _):
  19. return height
  20. case let .fixed(_, height, _):
  21. return height
  22. }
  23. }
  24. }
  25. // MARK: - Lifecycle
  26. init(sizeConfiguration: SizeConfiguration,
  27. tabs: [TabItemProtocol] = []) {
  28. self.sizeConfiguration = sizeConfiguration
  29. self.tabs = tabs
  30. super.init(frame: .zero)
  31. self.setupUI()
  32. }
  33. required init?(coder: NSCoder) {
  34. fatalError("init(coder:) has not been implemented")
  35. }
  36. // MARK: - Properties
  37. weak var delegate: TabbedViewDelegate?
  38. public let sizeConfiguration: SizeConfiguration
  39. public var tabs: [TabItemProtocol] {
  40. didSet {
  41. self.collectionView.reloadData()
  42. self.tabs[currentlySelectedIndex].onSelected()
  43. }
  44. }
  45. private var currentlySelectedIndex: Int = 0
  46. private lazy var collectionView: UICollectionView = {
  47. let layout = UICollectionViewFlowLayout()
  48. layout.scrollDirection = .horizontal
  49. layout.estimatedItemSize = .zero
  50. let collectionView = UICollectionView(
  51. frame: .zero,
  52. collectionViewLayout: layout
  53. )
  54. collectionView.backgroundColor = .white
  55. collectionView.register(TabCollectionViewCell.self, forCellWithReuseIdentifier: "TabCollectionViewCell")
  56. collectionView.dataSource = self
  57. collectionView.delegate = self
  58. collectionView.showsHorizontalScrollIndicator = false
  59. collectionView.translatesAutoresizingMaskIntoConstraints = false
  60. return collectionView
  61. }()
  62. // MARK: - Action
  63. public func moveToTab(at index: Int) {
  64. self.collectionView.scrollToItem(at: IndexPath(item: index, section: 0), at: .centeredHorizontally, animated: true)
  65. self.tabs[currentlySelectedIndex].onNotSelected()
  66. self.tabs[index].onSelected()
  67. self.currentlySelectedIndex = index
  68. }
  69. // MARK: UI Setup
  70. private func setupUI() {
  71. self.translatesAutoresizingMaskIntoConstraints = false
  72. self.addSubview(collectionView)
  73. NSLayoutConstraint.activate([
  74. collectionView.leftAnchor
  75. .constraint(equalTo: self.leftAnchor),
  76. collectionView.topAnchor
  77. .constraint(equalTo: self.topAnchor),
  78. collectionView.rightAnchor
  79. .constraint(equalTo: self.rightAnchor),
  80. collectionView.bottomAnchor
  81. .constraint(equalTo: self.bottomAnchor)
  82. ])
  83. }
  84. }
  85. extension TabbedView: UICollectionViewDelegateFlowLayout {
  86. func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
  87. switch sizeConfiguration {
  88. case let .fillEqually(height, spacing):
  89. let totalWidth = self.frame.width
  90. let widthPerItem = (
  91. totalWidth - (
  92. spacing * CGFloat((self.tabs.count + 1))
  93. )
  94. ) / CGFloat(self.tabs.count)
  95. return CGSize(width: widthPerItem,
  96. height: height)
  97. case let .fixed(width, height, spacing):
  98. return CGSize(width: width - (spacing * 2),
  99. height: height)
  100. }
  101. }
  102. func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
  103. switch sizeConfiguration {
  104. case let .fillEqually(_, spacing),
  105. let .fixed(_, _, spacing):
  106. return spacing
  107. }
  108. }
  109. }
  110. extension TabbedView: UICollectionViewDataSource {
  111. func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
  112. return tabs.count
  113. }
  114. func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
  115. let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TabCollectionViewCell", for: indexPath) as! TabCollectionViewCell
  116. cell.view = tabs[indexPath.row]
  117. return cell
  118. }
  119. }
  120. extension TabbedView: UICollectionViewDelegate {
  121. func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
  122. self.moveToTab(at: indexPath.item)
  123. self.delegate?.didMoveToTab(at: indexPath.item)
  124. }
  125. }