JXSegmentedTitleDataSource.swift 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  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. /// 如果将JXSegmentedView嵌套进UITableView的cell,每次重用的时候,JXSegmentedView进行reloadData时,会重新计算所有的title宽度。所以该应用场景,需要UITableView的cellModel缓存titles的文字宽度,再通过该闭包方法返回给JXSegmentedView。
  13. open var widthForTitleClosure: ((String)->(CGFloat))?
  14. /// label的numberOfLines
  15. open var titleNumberOfLines: Int = 1
  16. /// title普通状态的textColor
  17. open var titleNormalColor: UIColor = .black
  18. /// title选中状态的textColor
  19. open var titleSelectedColor: UIColor = .red
  20. /// title普通状态时的字体
  21. open var titleNormalFont: UIFont = UIFont.systemFont(ofSize: 15)
  22. /// title选中时的字体。如果不赋值,就默认与titleNormalFont一样
  23. open var titleSelectedFont: UIFont?
  24. /// title的颜色是否渐变过渡
  25. open var isTitleColorGradientEnabled: Bool = false
  26. /// title是否缩放。使用该效果时,务必保证titleNormalFont和titleSelectedFont值相同。
  27. open var isTitleZoomEnabled: Bool = false
  28. /// isTitleZoomEnabled为true才生效。是对字号的缩放,比如titleNormalFont的pointSize为10,放大之后字号就是10*1.2=12。
  29. open var titleSelectedZoomScale: CGFloat = 1.2
  30. /// title的线宽是否允许粗细。使用该效果时,务必保证titleNormalFont和titleSelectedFont值相同。
  31. open var isTitleStrokeWidthEnabled: Bool = false
  32. /// 用于控制字体的粗细(底层通过NSStrokeWidthAttributeName实现),负数越小字体越粗。
  33. open var titleSelectedStrokeWidth: CGFloat = -2
  34. /// title是否使用遮罩过渡
  35. open var isTitleMaskEnabled: Bool = false
  36. deinit {
  37. widthForTitleClosure = nil
  38. }
  39. open override func preferredItemModelInstance() -> JXSegmentedBaseItemModel {
  40. return JXSegmentedTitleItemModel()
  41. }
  42. open override func reloadData(selectedIndex: Int) {
  43. super.reloadData(selectedIndex: selectedIndex)
  44. for (index, _) in titles.enumerated() {
  45. let itemModel = preferredItemModelInstance()
  46. preferredRefreshItemModel(itemModel, at: index, selectedIndex: selectedIndex)
  47. dataSource.append(itemModel)
  48. }
  49. }
  50. open override func preferredRefreshItemModel( _ itemModel: JXSegmentedBaseItemModel, at index: Int, selectedIndex: Int) {
  51. super.preferredRefreshItemModel(itemModel, at: index, selectedIndex: selectedIndex)
  52. guard let myItemModel = itemModel as? JXSegmentedTitleItemModel else {
  53. return
  54. }
  55. myItemModel.title = titles[index]
  56. myItemModel.textWidth = widthForItem(title: myItemModel.title ?? "")
  57. myItemModel.titleNumberOfLines = titleNumberOfLines
  58. myItemModel.isSelectedAnimable = isSelectedAnimable
  59. myItemModel.titleNormalColor = titleNormalColor
  60. myItemModel.titleSelectedColor = titleSelectedColor
  61. myItemModel.titleNormalFont = titleNormalFont
  62. myItemModel.titleSelectedFont = titleSelectedFont != nil ? titleSelectedFont! : titleNormalFont
  63. myItemModel.isTitleZoomEnabled = isTitleZoomEnabled
  64. myItemModel.isTitleStrokeWidthEnabled = isTitleStrokeWidthEnabled
  65. myItemModel.isTitleMaskEnabled = isTitleMaskEnabled
  66. myItemModel.titleNormalZoomScale = 1
  67. myItemModel.titleSelectedZoomScale = titleSelectedZoomScale
  68. myItemModel.titleSelectedStrokeWidth = titleSelectedStrokeWidth
  69. myItemModel.titleNormalStrokeWidth = 0
  70. if index == selectedIndex {
  71. myItemModel.titleCurrentColor = titleSelectedColor
  72. myItemModel.titleCurrentZoomScale = titleSelectedZoomScale
  73. myItemModel.titleCurrentStrokeWidth = titleSelectedStrokeWidth
  74. }else {
  75. myItemModel.titleCurrentColor = titleNormalColor
  76. myItemModel.titleCurrentZoomScale = 1
  77. myItemModel.titleCurrentStrokeWidth = 0
  78. }
  79. }
  80. open func widthForItem(title: String) -> CGFloat {
  81. if widthForTitleClosure != nil {
  82. return widthForTitleClosure!(title)
  83. }else {
  84. let textWidth = NSString(string: title).boundingRect(with: CGSize(width: CGFloat.infinity, height: CGFloat.infinity), options: [.usesFontLeading, .usesLineFragmentOrigin], attributes: [NSAttributedString.Key.font : titleNormalFont], context: nil).size.width
  85. return CGFloat(ceilf(Float(textWidth)))
  86. }
  87. }
  88. /// 因为该方法会被频繁调用,所以应该在`preferredRefreshItemModel( _ itemModel: JXSegmentedBaseItemModel, at index: Int, selectedIndex: Int)`方法里面,根据数据源计算好文字宽度,然后缓存起来。该方法直接使用已经计算好的文字宽度即可。
  89. open override func preferredSegmentedView(_ segmentedView: JXSegmentedView, widthForItemAt index: Int) -> CGFloat {
  90. var itemWidth = super.preferredSegmentedView(segmentedView, widthForItemAt: index)
  91. if itemContentWidth == JXSegmentedViewAutomaticDimension {
  92. itemWidth += (dataSource[index] as! JXSegmentedTitleItemModel).textWidth
  93. }else {
  94. itemWidth += itemContentWidth
  95. }
  96. return itemWidth
  97. }
  98. //MARK: - JXSegmentedViewDataSource
  99. open override func registerCellClass(in segmentedView: JXSegmentedView) {
  100. segmentedView.collectionView.register(JXSegmentedTitleCell.self, forCellWithReuseIdentifier: "cell")
  101. }
  102. open override func segmentedView(_ segmentedView: JXSegmentedView, cellForItemAt index: Int) -> JXSegmentedBaseCell {
  103. let cell = segmentedView.dequeueReusableCell(withReuseIdentifier: "cell", at: index)
  104. return cell
  105. }
  106. open override func refreshItemModel(_ segmentedView: JXSegmentedView, leftItemModel: JXSegmentedBaseItemModel, rightItemModel: JXSegmentedBaseItemModel, percent: CGFloat) {
  107. super.refreshItemModel(segmentedView, leftItemModel: leftItemModel, rightItemModel: rightItemModel, percent: percent)
  108. guard let leftModel = leftItemModel as? JXSegmentedTitleItemModel, let rightModel = rightItemModel as? JXSegmentedTitleItemModel else {
  109. return
  110. }
  111. if isTitleZoomEnabled && isItemTransitionEnabled {
  112. leftModel.titleCurrentZoomScale = JXSegmentedViewTool.interpolate(from: leftModel.titleSelectedZoomScale, to: leftModel.titleNormalZoomScale, percent: CGFloat(percent))
  113. rightModel.titleCurrentZoomScale = JXSegmentedViewTool.interpolate(from: rightModel.titleNormalZoomScale, to: rightModel.titleSelectedZoomScale, percent: CGFloat(percent))
  114. }
  115. if isTitleStrokeWidthEnabled && isItemTransitionEnabled {
  116. leftModel.titleCurrentStrokeWidth = JXSegmentedViewTool.interpolate(from: leftModel.titleSelectedStrokeWidth, to: leftModel.titleNormalStrokeWidth, percent: CGFloat(percent))
  117. rightModel.titleCurrentStrokeWidth = JXSegmentedViewTool.interpolate(from: rightModel.titleNormalStrokeWidth, to: rightModel.titleSelectedStrokeWidth, percent: CGFloat(percent))
  118. }
  119. if isTitleColorGradientEnabled && isItemTransitionEnabled {
  120. leftModel.titleCurrentColor = JXSegmentedViewTool.interpolateColor(from: leftModel.titleSelectedColor, to: leftModel.titleNormalColor, percent: percent)
  121. rightModel.titleCurrentColor = JXSegmentedViewTool.interpolateColor(from:rightModel.titleNormalColor , to:rightModel.titleSelectedColor, percent: percent)
  122. }
  123. }
  124. open override func refreshItemModel(_ segmentedView: JXSegmentedView, currentSelectedItemModel: JXSegmentedBaseItemModel, willSelectedItemModel: JXSegmentedBaseItemModel, selectedType: JXSegmentedViewItemSelectedType) {
  125. super.refreshItemModel(segmentedView, currentSelectedItemModel: currentSelectedItemModel, willSelectedItemModel: willSelectedItemModel, selectedType: selectedType)
  126. guard let myCurrentSelectedItemModel = currentSelectedItemModel as? JXSegmentedTitleItemModel, let myWillSelectedItemModel = willSelectedItemModel as? JXSegmentedTitleItemModel else {
  127. return
  128. }
  129. myCurrentSelectedItemModel.titleCurrentColor = myCurrentSelectedItemModel.titleNormalColor
  130. myCurrentSelectedItemModel.titleCurrentZoomScale = myCurrentSelectedItemModel.titleNormalZoomScale
  131. myCurrentSelectedItemModel.titleCurrentStrokeWidth = myCurrentSelectedItemModel.titleNormalStrokeWidth
  132. myCurrentSelectedItemModel.indicatorConvertToItemFrame = CGRect.zero
  133. myWillSelectedItemModel.titleCurrentColor = myWillSelectedItemModel.titleSelectedColor
  134. myWillSelectedItemModel.titleCurrentZoomScale = myWillSelectedItemModel.titleSelectedZoomScale
  135. myWillSelectedItemModel.titleCurrentStrokeWidth = myWillSelectedItemModel.titleSelectedStrokeWidth
  136. }
  137. }