以拨打视频电话时的拨打按钮抖动为例
import UIKit
class PACallPrepareAlertView: UIView {
  
    @IBOutlet weak var callIV: UIImageView!
    private var isLeave: Bool = false
      deinit {
        isLeave = true
      }
  
      override func awakeFromNib() {
        super.awakeFromNib()
        shakeAction()
      }
  
      private func shakeAction() {
        if isLeave { return } // 避免无限套娃
        callIV.shake(false, 2, 0.2, 1) { [weak self] in
            self?.shakeAction()
        }
      }
  
      @IBAction func tapReturnAction(_ sender: Any) {
         isLeave = true
      }
  
}import Foundation
extension UIView {
    
    /// 扩展UIView增加抖动方法。使用时,要避免离开页面后方法仍然在调用。
    /// - Parameters:
    ///   - isVertical: 抖动方向(默认是水平方向)
    ///   - times: 抖动次数(默认5次)
    ///   - interval: 每次抖动时间(默认0.1秒)
    ///   - delta: 抖动偏移量
    ///   - completion: 动画结束回调
    public func shake(_ isVertical: Bool = false, _ times: Int = 5, _ interval: TimeInterval = 0.1, _ delta: CGFloat = 2, _ completion: (() -> ())? = nil) {
        UIView.animate(withDuration: interval) {
            var transform: CGAffineTransform?
            if isVertical == true {
                transform = CGAffineTransform(translationX: delta, y: 0)
            } else {
                transform = CGAffineTransform(translationX: 0, y: delta)
            }
            self.layer.setAffineTransform(transform!)
        } completion: {_ in
            if (times == 0) {
                UIView.animate(withDuration: interval) {
                    self.layer.setAffineTransform(CGAffineTransform.identity)
                } completion: { _ in
                    completion?()
                }
            } else {
                self.shake(isVertical, times - 1, interval, -delta, completion)
            }
        }
    }
    
}示意图:




















