iOS 仿微信长按录制视频按钮

2020-06-28  本文已影响0人  gaookey
import UIKit

enum RecordType: Int {
    case click
    case longPressBegin
    case longPressMoving
    case longPressDone
    case longPressCancel
}

protocol SPRecordButtonDelegate: AnyObject {
    
    func button(_ button: SPRecordButton, shouldChangeRecordType type: RecordType, isDismissHandler: ((_ dismiss: Bool) -> ())?)
}

class SPRecordButton: UIView {
    
    weak var delegate: SPRecordButtonDelegate?
    
    var timeInterval: CGFloat = 15
    var outCircleColor = UIColor.lightGray
    var centerCircleColor = UIColor.white
    var progressColor = UIColor.orange
    
    var outCircleNormalScale: CGFloat = 0.8
    var centerCircleNormalScale: CGFloat = 0.5
    var centerCircleProgressingScale: CGFloat = 0.3
    var progressWidthScale: CGFloat = 0.1
    
    private var tempInterval: CGFloat = 0
    private var progress: CGFloat = 0
    private var isProgress = false
    private var isCancel = false
    
    lazy var link: CADisplayLink = {
        let link = CADisplayLink(target: self, selector: #selector(runlink))
        link.preferredFramesPerSecond = 60
        link.add(to: RunLoop.current, forMode: .default)
        link.isPaused = true
        return link
    }()
    
    private lazy var progressLayer: CAShapeLayer = {
        let layer = CAShapeLayer()
        layer.lineCap = .round
        layer.lineJoin = .round
        layer.fillColor = backgroundColor?.cgColor
        return layer
    }()
    
    private lazy var outCircle = CAShapeLayer()
    private lazy var centerCircle = CAShapeLayer()
    
    override init(frame: CGRect) {
        super.init(frame: CGRect(x: frame.origin.x, y: frame.origin.y, width: frame.size.width, height: frame.size.width))
        
        backgroundColor = .clear
        
        setupUI()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func draw(_ rect: CGRect) {
        
        var outCircleRadius: CGFloat = 0
        var centerCircleRadius: CGFloat = 0
        if isProgress {
            outCircleRadius = bounds.size.width / 2
            centerCircleRadius = bounds.size.width * centerCircleProgressingScale / 2
        } else {
            outCircleRadius = bounds.size.width * outCircleNormalScale / 2
            centerCircleRadius = bounds.size.width * centerCircleNormalScale / 2
        }
        
        let outCirclePath = UIBezierPath(arcCenter: CGPoint(x: bounds.size.width / 2, y: bounds.size.width / 2), radius: outCircleRadius, startAngle: 0, endAngle: .pi * 2, clockwise: true)
        outCircle.path = outCirclePath.cgPath
        
        let centerCirclePath = UIBezierPath(arcCenter: CGPoint(x: centerCircle.frame.size.width / 2, y: centerCircle.frame.size.width / 2), radius: centerCircleRadius, startAngle: 0, endAngle: .pi * 2, clockwise: true)
        centerCircle.path = centerCirclePath.cgPath
        
        let path = UIBezierPath()
        path.addArc(withCenter: CGPoint(x: bounds.size.width / 2, y: bounds.size.width / 2), radius: bounds.size.width / 2 * (1 - progressWidthScale / 2), startAngle: -.pi / 2, endAngle: .pi / 2 * 3, clockwise: true)
        progressLayer.path = path.cgPath
        progressLayer.strokeEnd = progress
    }
    
    override func layoutSubviews() {
        
        layer.cornerRadius = frame.size.width / 2
        layer.masksToBounds = true
        
        outCircle.frame = self.bounds
        centerCircle.frame = self.bounds
        
        outCircle.fillColor = self.outCircleColor.cgColor
        centerCircle.fillColor = self.centerCircleColor.cgColor
        progressLayer.strokeColor = progressColor.cgColor
        
        progressLayer.lineWidth = bounds.size.width * progressWidthScale / 2
    }
    
    deinit {
        print("SPRecordButton deinit")
    }
}

extension SPRecordButton {
    
    func setupUI() {
        
        layer.addSublayer(outCircle)
        layer.addSublayer(centerCircle)
        layer.addSublayer(progressLayer)
        
        self.addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: #selector(longPressGesture(_:))))
        self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapGesture(_:))))
    }
    
    @objc func tapGesture(_ gesture: UILongPressGestureRecognizer) {
        
        self.delegate?.button(self, shouldChangeRecordType: .click, isDismissHandler: { [weak self] (dismiss) in
            if dismiss {
                self?.link.invalidate()
            }
        })
    }
    
    @objc func longPressGesture(_ gesture: UILongPressGestureRecognizer) {
        
        if gesture.state == .began {
            
            start()
        } else if gesture.state == .changed {
            let point: CGPoint = gesture.location(in: self)
            if self.point(inside: point, with: nil) {
                self.delegate?.button(self, shouldChangeRecordType: .longPressMoving, isDismissHandler: { [weak self] (dismiss) in
                    if dismiss {
                        self?.link.invalidate()
                    }
                })
            } else {
                if !isCancel {
                    stop(.longPressCancel)
                }
            }
        } else if gesture.state == .ended {
            if !isCancel {
                stop(.longPressDone)
            }
        } else if gesture.state == .cancelled {
            if !isCancel {
                stop(.longPressCancel)
            }
        }
    }
    
    @objc func runlink() {
        
        guard tempInterval <= timeInterval else {
            stop(.longPressDone)
            return
        }
        
        tempInterval += 1 / 60
        progress = tempInterval / timeInterval
        setNeedsDisplay()
    }
    
    func stop(_ type: RecordType) {
        
        if type == .longPressCancel {
            isCancel = true
        }
        
        link.isPaused = true
        isProgress = false
        progress = 0
        tempInterval = 0
        progressLayer.isHidden = true
        self.delegate?.button(self, shouldChangeRecordType: type, isDismissHandler: { [weak self] (dismiss) in
            if dismiss {
                self?.link.invalidate()
            }
        })
        setNeedsDisplay()
    }
    
    func start() {
        
        isCancel = false
        isProgress = true
        progressLayer.isHidden = false
        link.isPaused = false
        
        self.delegate?.button(self, shouldChangeRecordType: .longPressBegin, isDismissHandler: { [weak self] (dismiss) in
            if dismiss {
                self?.link.invalidate()
            }
        })
    }
}
上一篇 下一篇

猜你喜欢

热点阅读