JXSegmentedIndicatorGradientView.swift 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. //
  2. // JXSegmentedIndicatorGradientView.swift
  3. // JXSegmentedView
  4. //
  5. // Created by jiaxin on 2019/1/16.
  6. // Copyright © 2019 jiaxin. All rights reserved.
  7. //
  8. import UIKit
  9. /// 整个背景是一个渐变色layer,通过gradientMaskLayer遮罩显示不同位置,达到不同文字底部有不同的渐变色。
  10. open class JXSegmentedIndicatorGradientView: JXSegmentedIndicatorBaseView {
  11. @available(*, deprecated, renamed: "indicatorWidthIncrement")
  12. open var gradientViewWidthIncrement: CGFloat = 20 {
  13. didSet {
  14. indicatorWidthIncrement = gradientViewWidthIncrement
  15. }
  16. }
  17. /// 渐变colors
  18. open var gradientColors = [CGColor]()
  19. /// 渐变CAGradientLayer,通过它设置startPoint、endPoint等其他属性
  20. open var gradientLayer: CAGradientLayer {
  21. return layer as! CAGradientLayer
  22. }
  23. public let gradientMaskLayer: CAShapeLayer = CAShapeLayer()
  24. open class override var layerClass: AnyClass {
  25. return CAGradientLayer.self
  26. }
  27. private var gradientMaskLayerFrame = CGRect.zero
  28. open override func commonInit() {
  29. super.commonInit()
  30. indicatorWidthIncrement = 20
  31. indicatorHeight = 26
  32. indicatorPosition = .center
  33. verticalOffset = 0
  34. gradientColors = [UIColor(red: 194.0/255, green: 229.0/255, blue: 156.0/255, alpha: 1).cgColor, UIColor(red: 100.0/255, green: 179.0/255, blue: 244.0/255, alpha: 1).cgColor]
  35. gradientLayer.startPoint = CGPoint(x: 0, y: 0)
  36. gradientLayer.endPoint = CGPoint(x: 1, y: 0)
  37. layer.mask = gradientMaskLayer
  38. }
  39. open override func refreshIndicatorState(model: JXSegmentedIndicatorSelectedParams) {
  40. super.refreshIndicatorState(model: model)
  41. gradientLayer.colors = gradientColors
  42. let width = getIndicatorWidth(itemFrame: model.currentSelectedItemFrame, itemContentWidth: model.currentItemContentWidth)
  43. let height = getIndicatorHeight(itemFrame: model.currentSelectedItemFrame)
  44. let x = model.currentSelectedItemFrame.origin.x + (model.currentSelectedItemFrame.size.width - width)/2
  45. var y: CGFloat = 0
  46. switch indicatorPosition {
  47. case .top:
  48. y = verticalOffset
  49. case .bottom:
  50. y = model.currentSelectedItemFrame.size.height - height - verticalOffset
  51. case .center:
  52. y = (model.currentSelectedItemFrame.size.height - height)/2 + verticalOffset
  53. }
  54. gradientMaskLayerFrame = CGRect(x: x, y: y, width: width, height: height)
  55. let path = UIBezierPath(roundedRect: gradientMaskLayerFrame, cornerRadius: getIndicatorCornerRadius(itemFrame: model.currentSelectedItemFrame))
  56. CATransaction.begin()
  57. CATransaction.setDisableActions(true)
  58. gradientMaskLayer.path = path.cgPath
  59. CATransaction.commit()
  60. if let collectionViewContentSize = model.collectionViewContentSize {
  61. frame = CGRect(x: 0, y: 0, width: collectionViewContentSize.width, height: collectionViewContentSize.height)
  62. }
  63. }
  64. open override func contentScrollViewDidScroll(model: JXSegmentedIndicatorTransitionParams) {
  65. super.contentScrollViewDidScroll(model: model)
  66. guard canHandleTransition(model: model) else {
  67. return
  68. }
  69. let rightItemFrame = model.rightItemFrame
  70. let leftItemFrame = model.leftItemFrame
  71. let percent = model.percent
  72. var targetWidth = getIndicatorWidth(itemFrame: leftItemFrame, itemContentWidth: model.leftItemContentWidth)
  73. let leftWidth = targetWidth
  74. let rightWidth = getIndicatorWidth(itemFrame: rightItemFrame, itemContentWidth: model.rightItemContentWidth)
  75. let leftX = leftItemFrame.origin.x + (leftItemFrame.size.width - leftWidth)/2
  76. let rightX = rightItemFrame.origin.x + (rightItemFrame.size.width - rightWidth)/2
  77. let targetX = JXSegmentedViewTool.interpolate(from: leftX, to: rightX, percent: CGFloat(percent))
  78. if indicatorWidth == JXSegmentedViewAutomaticDimension {
  79. targetWidth = JXSegmentedViewTool.interpolate(from: leftWidth, to: rightWidth, percent: CGFloat(percent))
  80. }
  81. gradientMaskLayerFrame.origin.x = targetX
  82. gradientMaskLayerFrame.size.width = targetWidth
  83. let path = UIBezierPath(roundedRect: gradientMaskLayerFrame, cornerRadius: getIndicatorCornerRadius(itemFrame: leftItemFrame))
  84. CATransaction.begin()
  85. CATransaction.setDisableActions(true)
  86. gradientMaskLayer.path = path.cgPath
  87. CATransaction.commit()
  88. }
  89. open override func selectItem(model: JXSegmentedIndicatorSelectedParams) {
  90. super.selectItem(model: model)
  91. let width = getIndicatorWidth(itemFrame: model.currentSelectedItemFrame, itemContentWidth: model.currentItemContentWidth)
  92. var toFrame = gradientMaskLayerFrame
  93. toFrame.origin.x = model.currentSelectedItemFrame.origin.x + (model.currentSelectedItemFrame.size.width - width)/2
  94. toFrame.size.width = width
  95. let path = UIBezierPath(roundedRect: toFrame, cornerRadius: getIndicatorCornerRadius(itemFrame: model.currentSelectedItemFrame))
  96. if canSelectedWithAnimation(model: model) {
  97. gradientMaskLayer.removeAnimation(forKey: "path")
  98. let animation = CABasicAnimation(keyPath: "path")
  99. animation.fromValue = gradientMaskLayer.path
  100. animation.toValue = path.cgPath
  101. animation.duration = scrollAnimationDuration
  102. animation.timingFunction = CAMediaTimingFunction(name: .easeOut)
  103. gradientMaskLayer.add(animation, forKey: "path")
  104. gradientMaskLayer.path = path.cgPath
  105. }else {
  106. CATransaction.begin()
  107. CATransaction.setDisableActions(true)
  108. gradientMaskLayer.path = path.cgPath
  109. CATransaction.commit()
  110. }
  111. }
  112. }