123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- //
- // TSImageComparisonView.swift
- // testApp
- //
- // Created by 100Years on 2025/5/11.
- //
- import UIKit
- class TSImageComparisonView: UIView {
- // MARK: - 属性
- private let oldImageView = UIImageView()
- private let newImageView = UIImageView()
- private let lineView = UIView()
- private var displayLink: CADisplayLink?
- private var animationStartTime: CFTimeInterval = 0
- var duration: TimeInterval = 3.0
-
- var isRunloop:Bool = false //是否循环播放,从右到左,在从左到右
- private var reverse:Bool = false //动画反转(进度反转)
-
- // 动画方向枚举
- enum AnimationDirection {
- case leftToRight // 从左到右(默认)
- case rightToLeft // 从右到左
- }
- private var maskLayer = CAShapeLayer()
- var direction:AnimationDirection = .leftToRight
- // MARK: - 初始化
- override init(frame: CGRect) {
- super.init(frame: frame)
- setupViews()
- }
-
- required init?(coder: NSCoder) {
- super.init(coder: coder)
- setupViews()
- }
-
- // MARK: - 视图设置
- private func setupViews() {
- // 旧图片视图(初始显示)
- oldImageView.contentMode = .scaleAspectFill
- oldImageView.clipsToBounds = true
- addSubview(oldImageView)
-
- // 新图片视图(初始隐藏)
- newImageView.contentMode = .scaleAspectFill
- newImageView.clipsToBounds = true
- addSubview(newImageView)
- resetNewImageView()
- // 分割线样式
- lineView.backgroundColor = .white
- lineView.alpha = 0 // 初始隐藏
- addSubview(lineView)
- }
-
- // MARK: - 布局
- override func layoutSubviews() {
- super.layoutSubviews()
- oldImageView.frame = bounds
- newImageView.frame = bounds
- lineView.frame = CGRect(x: 0, y: 0, width: 1, height: bounds.height)
- }
-
- // MARK: - 公开方法
- func configure(oldImage: UIImage?, newImage: UIImage?) {
- oldImageView.image = oldImage
- newImageView.image = newImage
- reset()
- }
-
- func startAnimation(duration: TimeInterval = 1.5,direction:AnimationDirection = .leftToRight) {
- reset()
-
- self.duration = duration
- self.direction = direction
- lineView.alpha = 1
-
- startAnimationTime()
- }
-
-
- private func startAnimationTime(){
- // 启动动画
- animationStartTime = CACurrentMediaTime()
- displayLink = CADisplayLink(target: self, selector: #selector(updateAnimation))
- displayLink?.add(to: .main, forMode: .common)
- }
-
- func stopAnimation() {
- displayLink?.invalidate()
- displayLink = nil
- }
-
- func animationComplete() {
- if isRunloop {
- stopAnimation()
- lineView.alpha = 0.0
- DispatchQueue.main.asyncAfter(deadline: .now()+0.2){
- self.reverse = !self.reverse
- self.lineView.alpha = 1.0
- self.startAnimationTime()
- }
- } else {
- reset()
- }
- }
- func reset() {
- stopAnimation()
- lineView.alpha = 0
- lineView.frame.origin.x = 0
- self.newImageView.layer.mask = nil
- reverse = false
- }
-
- func resetNewImageView(){
- maskLayer.path = UIBezierPath(rect: CGRect(
- x: 0,
- y: 0,
- width: x,
- height: bounds.height
- )).cgPath
- newImageView.layer.mask = maskLayer
- }
-
- // MARK: - 动画更新
- @objc private func updateAnimation() {
- guard displayLink != nil else { return }
-
- let elapsed = CACurrentMediaTime() - animationStartTime
- let duration = self.duration
- var progress = min(CGFloat(elapsed / duration), 1.0)
-
- if reverse { //反转进度
- progress = 1.0 - progress
- }
-
- var x = bounds.width * progress - lineView.frame.size.width
- var path = UIBezierPath(rect: CGRect(
- x: 0,
- y: 0,
- width: x,
- height: bounds.height
- ))
- if self.direction == .rightToLeft {
- x = bounds.width - x
- path = UIBezierPath(rect: CGRect(
- x: x,
- y: 0,
- width: bounds.width - x,
- height: bounds.height
- ))
- }
-
- // 更新分割线位置
- lineView.frame.origin.x = x
-
- // 更新新图片显示区域
- maskLayer.path = path.cgPath
- newImageView.layer.mask = maskLayer
- // dePrint("progress = \(progress)")
- if progress == ((reverse == true) ? 0.0 : 1.0) { //因为反转了进度,所以结果也要反转
- // dePrint("animationComplete")
- animationComplete()
- }
- }
- }
|