JXSegmentedBaseDataSource.swift 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. //
  2. // JXSegmentedBaseDataSource.swift
  3. // JXSegmentedView
  4. //
  5. // Created by jiaxin on 2018/12/28.
  6. // Copyright © 2018 jiaxin. All rights reserved.
  7. //
  8. import Foundation
  9. import UIKit
  10. open class JXSegmentedBaseDataSource: JXSegmentedViewDataSource {
  11. /// 最终传递给JXSegmentedView的数据源数组
  12. open var dataSource = [JXSegmentedBaseItemModel]()
  13. /// cell的宽度。为JXSegmentedViewAutomaticDimension时就以内容计算的宽度为准,否则以itemWidth的具体值为准。
  14. open var itemWidth: CGFloat = JXSegmentedViewAutomaticDimension
  15. /// 真实的item宽度 = itemWidth + itemWidthIncrement。
  16. open var itemWidthIncrement: CGFloat = 0
  17. /// item之前的间距
  18. open var itemSpacing: CGFloat = 20
  19. /// 当collectionView.contentSize.width小于JXSegmentedView的宽度时,是否将itemSpacing均分。
  20. open var isItemSpacingAverageEnabled: Bool = true
  21. /// item左右滚动过渡时,是否允许渐变。比如JXSegmentedTitleDataSource的titleZoom、titleNormalColor、titleStrokeWidth等渐变。
  22. open var isItemTransitionEnabled: Bool = true
  23. /// 选中的时候,是否需要动画过渡。自定义的cell需要自己处理动画过渡逻辑,动画处理逻辑参考`JXSegmentedTitleCell`
  24. open var isSelectedAnimable: Bool = false
  25. /// 选中动画的时长
  26. open var selectedAnimationDuration: TimeInterval = 0.25
  27. /// 是否允许item宽度缩放
  28. open var isItemWidthZoomEnabled: Bool = false
  29. /// 是否允许item宽度缩放动画
  30. open var isItemWidthZoomAnimable: Bool = true
  31. /// item宽度选中时的scale
  32. open var itemWidthSelectedZoomScale: CGFloat = 1.5
  33. @available(*, deprecated, renamed: "itemWidth")
  34. open var itemContentWidth: CGFloat = JXSegmentedViewAutomaticDimension {
  35. didSet {
  36. itemWidth = itemContentWidth
  37. }
  38. }
  39. private var animator: JXSegmentedAnimator?
  40. deinit {
  41. animator?.stop()
  42. animator = nil
  43. }
  44. public init() {
  45. }
  46. /// 配置完各种属性之后,需要手动调用该方法,更新数据源
  47. ///
  48. /// - Parameter selectedIndex: 当前选中的index
  49. open func reloadData(selectedIndex: Int) {
  50. animator?.stop()
  51. animator = nil
  52. dataSource.removeAll()
  53. for index in 0..<preferredItemCount() {
  54. let itemModel = preferredItemModelInstance()
  55. preferredRefreshItemModel(itemModel, at: index, selectedIndex: selectedIndex)
  56. dataSource.append(itemModel)
  57. }
  58. }
  59. open func preferredItemCount() -> Int {
  60. return 0
  61. }
  62. /// 子类需要重载该方法,用于返回自己定义的JXSegmentedBaseItemModel子类实例
  63. open func preferredItemModelInstance() -> JXSegmentedBaseItemModel {
  64. return JXSegmentedBaseItemModel()
  65. }
  66. /// 子类需要重载该方法,用于返回索引为index的item宽度
  67. open func preferredSegmentedView(_ segmentedView: JXSegmentedView, widthForItemAt index: Int) -> CGFloat {
  68. return itemWidthIncrement
  69. }
  70. /// 子类需要重载该方法,用于更新索引为index的itemModel
  71. open func preferredRefreshItemModel(_ itemModel: JXSegmentedBaseItemModel, at index: Int, selectedIndex: Int) {
  72. itemModel.index = index
  73. itemModel.isItemTransitionEnabled = isItemTransitionEnabled
  74. itemModel.isSelectedAnimable = isSelectedAnimable
  75. itemModel.selectedAnimationDuration = selectedAnimationDuration
  76. itemModel.isItemWidthZoomEnabled = isItemWidthZoomEnabled
  77. itemModel.itemWidthNormalZoomScale = 1
  78. itemModel.itemWidthSelectedZoomScale = itemWidthSelectedZoomScale
  79. if index == selectedIndex {
  80. itemModel.isSelected = true
  81. itemModel.itemWidthCurrentZoomScale = itemModel.itemWidthSelectedZoomScale
  82. }else {
  83. itemModel.isSelected = false
  84. itemModel.itemWidthCurrentZoomScale = itemModel.itemWidthNormalZoomScale
  85. }
  86. }
  87. //MARK: - JXSegmentedViewDataSource
  88. open func itemDataSource(in segmentedView: JXSegmentedView) -> [JXSegmentedBaseItemModel] {
  89. return dataSource
  90. }
  91. /// 自定义子类请继承方法`func preferredWidthForItem(at index: Int) -> CGFloat`
  92. public final func segmentedView(_ segmentedView: JXSegmentedView, widthForItemAt index: Int) -> CGFloat {
  93. return preferredSegmentedView(segmentedView, widthForItemAt: index)
  94. }
  95. public func segmentedView(_ segmentedView: JXSegmentedView, widthForItemContentAt index: Int) -> CGFloat {
  96. return self.segmentedView(segmentedView, widthForItemAt: index)
  97. }
  98. open func registerCellClass(in segmentedView: JXSegmentedView) {
  99. }
  100. open func segmentedView(_ segmentedView: JXSegmentedView, cellForItemAt index: Int) -> JXSegmentedBaseCell {
  101. return JXSegmentedBaseCell()
  102. }
  103. open func refreshItemModel(_ segmentedView: JXSegmentedView, currentSelectedItemModel: JXSegmentedBaseItemModel, willSelectedItemModel: JXSegmentedBaseItemModel, selectedType: JXSegmentedViewItemSelectedType) {
  104. currentSelectedItemModel.isSelected = false
  105. willSelectedItemModel.isSelected = true
  106. if isItemWidthZoomEnabled {
  107. if (selectedType == .scroll && !isItemTransitionEnabled) ||
  108. selectedType == .click ||
  109. selectedType == .code {
  110. animator = JXSegmentedAnimator()
  111. animator?.duration = selectedAnimationDuration
  112. animator?.progressClosure = {[weak self] (percent) in
  113. guard let self = self else { return }
  114. currentSelectedItemModel.itemWidthCurrentZoomScale = JXSegmentedViewTool.interpolate(from: currentSelectedItemModel.itemWidthSelectedZoomScale, to: currentSelectedItemModel.itemWidthNormalZoomScale, percent: percent)
  115. currentSelectedItemModel.itemWidth = self.itemWidthWithZoom(at: currentSelectedItemModel.index, model: currentSelectedItemModel)
  116. willSelectedItemModel.itemWidthCurrentZoomScale = JXSegmentedViewTool.interpolate(from: willSelectedItemModel.itemWidthNormalZoomScale, to: willSelectedItemModel.itemWidthSelectedZoomScale, percent: percent)
  117. willSelectedItemModel.itemWidth = self.itemWidthWithZoom(at: willSelectedItemModel.index, model: willSelectedItemModel)
  118. segmentedView.collectionView.collectionViewLayout.invalidateLayout()
  119. }
  120. if isItemWidthZoomAnimable {
  121. animator?.start()
  122. }else {
  123. animator?.stop()
  124. }
  125. }
  126. }else {
  127. currentSelectedItemModel.itemWidthCurrentZoomScale = currentSelectedItemModel.itemWidthNormalZoomScale
  128. willSelectedItemModel.itemWidthCurrentZoomScale = willSelectedItemModel.itemWidthSelectedZoomScale
  129. }
  130. }
  131. open func refreshItemModel(_ segmentedView: JXSegmentedView, leftItemModel: JXSegmentedBaseItemModel, rightItemModel: JXSegmentedBaseItemModel, percent: CGFloat) {
  132. //如果正在进行itemWidth缩放动画,用户又立马滚动了contentScrollView,需要停止动画。
  133. animator?.stop()
  134. animator = nil
  135. if isItemWidthZoomEnabled && isItemTransitionEnabled {
  136. //允许itemWidth缩放动画且允许item渐变过渡
  137. leftItemModel.itemWidthCurrentZoomScale = JXSegmentedViewTool.interpolate(from: leftItemModel.itemWidthSelectedZoomScale, to: leftItemModel.itemWidthNormalZoomScale, percent: percent)
  138. leftItemModel.itemWidth = itemWidthWithZoom(at: leftItemModel.index, model: leftItemModel)
  139. rightItemModel.itemWidthCurrentZoomScale = JXSegmentedViewTool.interpolate(from: rightItemModel.itemWidthNormalZoomScale, to: rightItemModel.itemWidthSelectedZoomScale, percent: percent)
  140. rightItemModel.itemWidth = itemWidthWithZoom(at: rightItemModel.index, model: rightItemModel)
  141. segmentedView.collectionView.collectionViewLayout.invalidateLayout()
  142. }
  143. }
  144. /// 自定义子类请继承方法`func preferredRefreshItemModel(_ itemModel: JXSegmentedBaseItemModel, at index: Int, selectedIndex: Int)`
  145. public final func refreshItemModel(_ segmentedView: JXSegmentedView, _ itemModel: JXSegmentedBaseItemModel, at index: Int, selectedIndex: Int) {
  146. preferredRefreshItemModel(itemModel, at: index, selectedIndex: selectedIndex)
  147. }
  148. private func itemWidthWithZoom(at index: Int, model: JXSegmentedBaseItemModel) -> CGFloat {
  149. var width = self.segmentedView(JXSegmentedView(), widthForItemAt: index)
  150. if isItemWidthZoomEnabled {
  151. width *= model.itemWidthCurrentZoomScale
  152. }
  153. return width
  154. }
  155. }