TSImageComparisonView.swift 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. //
  2. // TSImageComparisonView.swift
  3. // testApp
  4. //
  5. // Created by 100Years on 2025/5/11.
  6. //
  7. import UIKit
  8. class TSImageComparisonView: UIView {
  9. // MARK: - 属性
  10. private let oldImageView = UIImageView()
  11. private let newImageView = UIImageView()
  12. private let lineView = UIView()
  13. private var displayLink: CADisplayLink?
  14. private var animationStartTime: CFTimeInterval = 0
  15. var duration: TimeInterval = 3.0
  16. var isRunloop:Bool = false //是否循环播放,从右到左,在从左到右
  17. private var reverse:Bool = false //动画反转(进度反转)
  18. // 动画方向枚举
  19. enum AnimationDirection {
  20. case leftToRight // 从左到右(默认)
  21. case rightToLeft // 从右到左
  22. }
  23. private var maskLayer = CAShapeLayer()
  24. var direction:AnimationDirection = .leftToRight
  25. // MARK: - 初始化
  26. override init(frame: CGRect) {
  27. super.init(frame: frame)
  28. setupViews()
  29. }
  30. required init?(coder: NSCoder) {
  31. super.init(coder: coder)
  32. setupViews()
  33. }
  34. // MARK: - 视图设置
  35. private func setupViews() {
  36. // 旧图片视图(初始显示)
  37. oldImageView.contentMode = .scaleAspectFill
  38. oldImageView.clipsToBounds = true
  39. addSubview(oldImageView)
  40. // 新图片视图(初始隐藏)
  41. newImageView.contentMode = .scaleAspectFill
  42. newImageView.clipsToBounds = true
  43. addSubview(newImageView)
  44. resetNewImageView()
  45. // 分割线样式
  46. lineView.backgroundColor = .white
  47. lineView.alpha = 0 // 初始隐藏
  48. addSubview(lineView)
  49. }
  50. // MARK: - 布局
  51. override func layoutSubviews() {
  52. super.layoutSubviews()
  53. oldImageView.frame = bounds
  54. newImageView.frame = bounds
  55. lineView.frame = CGRect(x: 0, y: 0, width: 1, height: bounds.height)
  56. }
  57. // MARK: - 公开方法
  58. func configure(oldImage: UIImage?, newImage: UIImage?) {
  59. oldImageView.image = oldImage
  60. newImageView.image = newImage
  61. reset()
  62. }
  63. func startAnimation(duration: TimeInterval = 1.5,direction:AnimationDirection = .leftToRight) {
  64. reset()
  65. self.duration = duration
  66. self.direction = direction
  67. lineView.alpha = 1
  68. startAnimationTime()
  69. }
  70. private func startAnimationTime(){
  71. // 启动动画
  72. animationStartTime = CACurrentMediaTime()
  73. displayLink = CADisplayLink(target: self, selector: #selector(updateAnimation))
  74. displayLink?.add(to: .main, forMode: .common)
  75. }
  76. func stopAnimation() {
  77. displayLink?.invalidate()
  78. displayLink = nil
  79. }
  80. func animationComplete() {
  81. if isRunloop {
  82. stopAnimation()
  83. lineView.alpha = 0.0
  84. DispatchQueue.main.asyncAfter(deadline: .now()+0.2){
  85. self.reverse = !self.reverse
  86. self.lineView.alpha = 1.0
  87. self.startAnimationTime()
  88. }
  89. } else {
  90. reset()
  91. }
  92. }
  93. func reset() {
  94. stopAnimation()
  95. lineView.alpha = 0
  96. lineView.frame.origin.x = 0
  97. self.newImageView.layer.mask = nil
  98. reverse = false
  99. }
  100. func resetNewImageView(){
  101. maskLayer.path = UIBezierPath(rect: CGRect(
  102. x: 0,
  103. y: 0,
  104. width: x,
  105. height: bounds.height
  106. )).cgPath
  107. newImageView.layer.mask = maskLayer
  108. }
  109. // MARK: - 动画更新
  110. @objc private func updateAnimation() {
  111. guard displayLink != nil else { return }
  112. let elapsed = CACurrentMediaTime() - animationStartTime
  113. let duration = self.duration
  114. var progress = min(CGFloat(elapsed / duration), 1.0)
  115. if reverse { //反转进度
  116. progress = 1.0 - progress
  117. }
  118. var x = bounds.width * progress - lineView.frame.size.width
  119. var path = UIBezierPath(rect: CGRect(
  120. x: 0,
  121. y: 0,
  122. width: x,
  123. height: bounds.height
  124. ))
  125. if self.direction == .rightToLeft {
  126. x = bounds.width - x
  127. path = UIBezierPath(rect: CGRect(
  128. x: x,
  129. y: 0,
  130. width: bounds.width - x,
  131. height: bounds.height
  132. ))
  133. }
  134. // 更新分割线位置
  135. lineView.frame.origin.x = x
  136. // 更新新图片显示区域
  137. maskLayer.path = path.cgPath
  138. newImageView.layer.mask = maskLayer
  139. // dePrint("progress = \(progress)")
  140. if progress == ((reverse == true) ? 0.0 : 1.0) { //因为反转了进度,所以结果也要反转
  141. // dePrint("animationComplete")
  142. animationComplete()
  143. }
  144. }
  145. }