JXSegmentedTitleDataSource.swift 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. //
  2. // JXSegmentedTitleView.swift
  3. // JXSegmentedView
  4. //
  5. // Created by jiaxin on 2018/12/26.
  6. // Copyright © 2018 jiaxin. All rights reserved.
  7. //
  8. import UIKit
  9. open class JXSegmentedTitleDataSource: JXSegmentedBaseDataSource {
  10. /// title数组
  11. open var titles = [String]()
  12. /// 根据index配置cell的不同属性
  13. open weak var configuration: JXSegmentedTitleDynamicConfiguration?
  14. /// 如果将JXSegmentedView嵌套进UITableView的cell,每次重用的时候,JXSegmentedView进行reloadData时,会重新计算所有的title宽度。所以该应用场景,需要UITableView的cellModel缓存titles的文字宽度,再通过该闭包方法返回给JXSegmentedView。
  15. open var widthForTitleClosure: ((String)->(CGFloat))?
  16. /// label的numberOfLines
  17. open var titleNumberOfLines: Int = 1
  18. /// title普通状态的textColor
  19. open var titleNormalColor: UIColor = .black
  20. /// title选中状态的textColor
  21. open var titleSelectedColor: UIColor = .red
  22. /// title普通状态时的字体
  23. open var titleNormalFont: UIFont = UIFont.systemFont(ofSize: 15)
  24. /// title选中时的字体。如果不赋值,就默认与titleNormalFont一样
  25. open var titleSelectedFont: UIFont?
  26. /// title的颜色是否渐变过渡
  27. open var isTitleColorGradientEnabled: Bool = false
  28. /// title是否缩放。使用该效果时,务必保证titleNormalFont和titleSelectedFont值相同。
  29. open var isTitleZoomEnabled: Bool = false
  30. /// isTitleZoomEnabled为true才生效。是对字号的缩放,比如titleNormalFont的pointSize为10,放大之后字号就是10*1.2=12。
  31. open var titleSelectedZoomScale: CGFloat = 1.2
  32. /// title的线宽是否允许粗细。使用该效果时,务必保证titleNormalFont和titleSelectedFont值相同。
  33. open var isTitleStrokeWidthEnabled: Bool = false
  34. /// 用于控制字体的粗细(底层通过NSStrokeWidthAttributeName实现),负数越小字体越粗。
  35. open var titleSelectedStrokeWidth: CGFloat = -2
  36. /// title是否使用遮罩过渡
  37. open var isTitleMaskEnabled: Bool = false
  38. open override func preferredItemCount() -> Int {
  39. return titles.count
  40. }
  41. open override func preferredItemModelInstance() -> JXSegmentedBaseItemModel {
  42. return JXSegmentedTitleItemModel()
  43. }
  44. open override func preferredRefreshItemModel( _ itemModel: JXSegmentedBaseItemModel, at index: Int, selectedIndex: Int) {
  45. super.preferredRefreshItemModel(itemModel, at: index, selectedIndex: selectedIndex)
  46. guard let myItemModel = itemModel as? JXSegmentedTitleItemModel else {
  47. return
  48. }
  49. myItemModel.title = titles[index]
  50. myItemModel.textWidth = widthForTitle(myItemModel.title ?? "", index)
  51. myItemModel.titleNumberOfLines = innerTitleNumberOfLines(at: index)
  52. myItemModel.isSelectedAnimable = isSelectedAnimable
  53. myItemModel.titleNormalColor = innerTitleNormalColor(at: index)
  54. myItemModel.titleSelectedColor = innerTitleSelectedColor(at: index)
  55. myItemModel.titleNormalFont = innerTitleNormalFont(at: index)
  56. if let selectedFont = innerTitleSelectedFont(at: index) {
  57. myItemModel.titleSelectedFont = selectedFont
  58. } else {
  59. myItemModel.titleSelectedFont = innerTitleNormalFont(at: index)
  60. }
  61. myItemModel.isTitleZoomEnabled = isTitleZoomEnabled
  62. myItemModel.isTitleStrokeWidthEnabled = isTitleStrokeWidthEnabled
  63. myItemModel.isTitleMaskEnabled = isTitleMaskEnabled
  64. myItemModel.titleNormalZoomScale = 1
  65. myItemModel.titleSelectedZoomScale = titleSelectedZoomScale
  66. myItemModel.titleSelectedStrokeWidth = titleSelectedStrokeWidth
  67. myItemModel.titleNormalStrokeWidth = 0
  68. if index == selectedIndex {
  69. myItemModel.titleCurrentColor = innerTitleSelectedColor(at: index)
  70. myItemModel.titleCurrentZoomScale = titleSelectedZoomScale
  71. myItemModel.titleCurrentStrokeWidth = titleSelectedStrokeWidth
  72. }else {
  73. myItemModel.titleCurrentColor = innerTitleNormalColor(at: index)
  74. myItemModel.titleCurrentZoomScale = 1
  75. myItemModel.titleCurrentStrokeWidth = 0
  76. }
  77. }
  78. open func widthForTitle(_ title: String, _ index: Int) -> CGFloat {
  79. if widthForTitleClosure != nil {
  80. return widthForTitleClosure!(title)
  81. }else {
  82. let textWidth = NSString(string: title).boundingRect(with: CGSize(width: CGFloat.infinity, height: CGFloat.infinity), options: [.usesFontLeading, .usesLineFragmentOrigin], attributes: [NSAttributedString.Key.font : innerTitleNormalFont(at: index)], context: nil).size.width
  83. return CGFloat(ceilf(Float(textWidth)))
  84. }
  85. }
  86. /// 因为该方法会被频繁调用,所以应该在`preferredRefreshItemModel( _ itemModel: JXSegmentedBaseItemModel, at index: Int, selectedIndex: Int)`方法里面,根据数据源计算好文字宽度,然后缓存起来。该方法直接使用已经计算好的文字宽度即可。
  87. open override func preferredSegmentedView(_ segmentedView: JXSegmentedView, widthForItemAt index: Int) -> CGFloat {
  88. var width = super.preferredSegmentedView(segmentedView, widthForItemAt: index)
  89. if itemWidth == JXSegmentedViewAutomaticDimension {
  90. width += (dataSource[index] as! JXSegmentedTitleItemModel).textWidth
  91. }else {
  92. width += itemWidth
  93. }
  94. return width
  95. }
  96. //MARK: - JXSegmentedViewDataSource
  97. open override func registerCellClass(in segmentedView: JXSegmentedView) {
  98. segmentedView.collectionView.register(JXSegmentedTitleCell.self, forCellWithReuseIdentifier: "cell")
  99. }
  100. open override func segmentedView(_ segmentedView: JXSegmentedView, cellForItemAt index: Int) -> JXSegmentedBaseCell {
  101. let cell = segmentedView.dequeueReusableCell(withReuseIdentifier: "cell", at: index)
  102. return cell
  103. }
  104. public override func segmentedView(_ segmentedView: JXSegmentedView, widthForItemContentAt index: Int) -> CGFloat {
  105. let model = dataSource[index] as! JXSegmentedTitleItemModel
  106. if isTitleZoomEnabled {
  107. return model.textWidth*model.titleCurrentZoomScale
  108. }else {
  109. return model.textWidth
  110. }
  111. }
  112. open override func refreshItemModel(_ segmentedView: JXSegmentedView, leftItemModel: JXSegmentedBaseItemModel, rightItemModel: JXSegmentedBaseItemModel, percent: CGFloat) {
  113. super.refreshItemModel(segmentedView, leftItemModel: leftItemModel, rightItemModel: rightItemModel, percent: percent)
  114. guard let leftModel = leftItemModel as? JXSegmentedTitleItemModel, let rightModel = rightItemModel as? JXSegmentedTitleItemModel else {
  115. return
  116. }
  117. if isTitleZoomEnabled && isItemTransitionEnabled {
  118. leftModel.titleCurrentZoomScale = JXSegmentedViewTool.interpolate(from: leftModel.titleSelectedZoomScale, to: leftModel.titleNormalZoomScale, percent: CGFloat(percent))
  119. rightModel.titleCurrentZoomScale = JXSegmentedViewTool.interpolate(from: rightModel.titleNormalZoomScale, to: rightModel.titleSelectedZoomScale, percent: CGFloat(percent))
  120. }
  121. if isTitleStrokeWidthEnabled && isItemTransitionEnabled {
  122. leftModel.titleCurrentStrokeWidth = JXSegmentedViewTool.interpolate(from: leftModel.titleSelectedStrokeWidth, to: leftModel.titleNormalStrokeWidth, percent: CGFloat(percent))
  123. rightModel.titleCurrentStrokeWidth = JXSegmentedViewTool.interpolate(from: rightModel.titleNormalStrokeWidth, to: rightModel.titleSelectedStrokeWidth, percent: CGFloat(percent))
  124. }
  125. if isTitleColorGradientEnabled && isItemTransitionEnabled {
  126. leftModel.titleCurrentColor = JXSegmentedViewTool.interpolateThemeColor(from: leftModel.titleSelectedColor, to: leftModel.titleNormalColor, percent: percent)
  127. rightModel.titleCurrentColor = JXSegmentedViewTool.interpolateThemeColor(from:rightModel.titleNormalColor , to:rightModel.titleSelectedColor, percent: percent)
  128. }
  129. }
  130. open override func refreshItemModel(_ segmentedView: JXSegmentedView, currentSelectedItemModel: JXSegmentedBaseItemModel, willSelectedItemModel: JXSegmentedBaseItemModel, selectedType: JXSegmentedViewItemSelectedType) {
  131. super.refreshItemModel(segmentedView, currentSelectedItemModel: currentSelectedItemModel, willSelectedItemModel: willSelectedItemModel, selectedType: selectedType)
  132. guard let myCurrentSelectedItemModel = currentSelectedItemModel as? JXSegmentedTitleItemModel, let myWillSelectedItemModel = willSelectedItemModel as? JXSegmentedTitleItemModel else {
  133. return
  134. }
  135. myCurrentSelectedItemModel.titleCurrentColor = myCurrentSelectedItemModel.titleNormalColor
  136. myCurrentSelectedItemModel.titleCurrentZoomScale = myCurrentSelectedItemModel.titleNormalZoomScale
  137. myCurrentSelectedItemModel.titleCurrentStrokeWidth = myCurrentSelectedItemModel.titleNormalStrokeWidth
  138. myCurrentSelectedItemModel.indicatorConvertToItemFrame = CGRect.zero
  139. myWillSelectedItemModel.titleCurrentColor = myWillSelectedItemModel.titleSelectedColor
  140. myWillSelectedItemModel.titleCurrentZoomScale = myWillSelectedItemModel.titleSelectedZoomScale
  141. myWillSelectedItemModel.titleCurrentStrokeWidth = myWillSelectedItemModel.titleSelectedStrokeWidth
  142. }
  143. // MARK: - Configuration
  144. private func innerTitleNumberOfLines(at index: Int) -> Int {
  145. if let configuration {
  146. return configuration.titleNumberOfLines(at: index)
  147. } else {
  148. return titleNumberOfLines
  149. }
  150. }
  151. private func innerTitleNormalColor(at index: Int) -> UIColor {
  152. if let configuration {
  153. return configuration.titleNormalColor(at: index)
  154. } else {
  155. return titleNormalColor
  156. }
  157. }
  158. private func innerTitleSelectedColor(at index: Int) -> UIColor {
  159. if let configuration {
  160. return configuration.titleSelectedColor(at: index)
  161. } else {
  162. return titleSelectedColor
  163. }
  164. }
  165. private func innerTitleNormalFont(at index: Int) -> UIFont {
  166. if let configuration {
  167. return configuration.titleNormalFont(at: index)
  168. } else {
  169. return titleNormalFont
  170. }
  171. }
  172. private func innerTitleSelectedFont(at index: Int) -> UIFont? {
  173. if let configuration {
  174. return configuration.titleSelectedFont(at: index)
  175. } else {
  176. return titleSelectedFont
  177. }
  178. }
  179. }