(Swift 3) - 如何在自定义 UIView 中包含动画

(Swift 3) - How to include animations in custom UIView

我有一个名为 ViewUIView subclass,其中我希望 "ripple" 从用户双击的任何地方向外移动(以及预成型其他绘图功能)。这是我的draw方法的相关部分,目前:

override func draw(_ rect: CGRect) {
    for r in ripples {
        r.paint()
    }
}

这就是 r.paint() 的实现方式,在 class Ripple:

func paint() {
    let c = UIColor(white: CGFloat(1.0 - (Float(iter))/100), alpha: CGFloat(1))
    print(iter, " - ",Float(iter)/100)
    c.setFill()
    view.fillCircle(center: CGPoint(x:x,y:y), radius: CGFloat(iter))
}

iter 应该每 0.1 秒增加一个 Timer,它在 Ripple:

的构造函数中启动
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: move)

move实现如下:

func move(_ timer: Timer) {
    while (tier<100) {
        iter += 1
        DispatchQueue.main.async {
            self.view.setNeedsDisplay()
        }
    }
    timer.invalidate()
}

应该发生的是,当计时器触发时每 0.1 秒,它会递增 iter 并告诉 View 重绘,然后它将使用 Ripple.paint() 方法,它使用 iter 来确定圆的颜色和半径。

然而,正如使用 print 语句所揭示的那样,实际发生的是 Timer 在重绘实际发生之前全部触发 100 次。我尝试通过将 DispatchQueue.main.async 替换为 DispatchQueue.main.sync 来解决这个问题,但这只会让应用程序挂起。我究竟做错了什么?如果这不是解决问题的正确方法,那么在应用程序仍然可以执行其他功能(例如产生更多涟漪)的同时,我怎样才能获得平滑的涟漪动画(其中包括一个在改变颜色的同时增长的圆圈)? (这意味着多个涟漪需要能够同时起作用。)

您可以通过 CABasicAnimation 和自定义 CALayer 实现您想要的效果,例如:

class MyLayer : CALayer
{
    var iter = 0

    override class func needsDisplay(forKey key: String) -> Bool
    {
        let result = super.needsDisplay(forKey: key)
        return result || key == "iter"
    }

    override func draw(in ctx: CGContext) {
        UIGraphicsPushContext(ctx)
        UIColor.red.setFill()
        ctx.fill(self.bounds)
        UIGraphicsPopContext()
        NSLog("Drawing, \(iter)")
    }
}

class MyView : UIView
{
    override class var layerClass: Swift.AnyClass {
        get {
            return MyLayer.self
        }
    }
}

class ViewController: UIViewController {

    let myview = MyView()

    override func viewDidLoad() {
        super.viewDidLoad()

        myview.frame = self.view.bounds
        self.view.addSubview(myview)
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        let animation = CABasicAnimation(keyPath: "iter")
        animation.fromValue = 0
        animation.toValue = 100
        animation.duration = 10.0
        myview.layer.add(animation, forKey: "MyIterAnimation")
        (myview.layer as! MyLayer).iter = 100
    }
}

自定义 UIView,例如 Swift 3 和 Swift 4

的带有动画的 AlertView

您可以使用扩展程序:

extension UIVIew{
  func customAlertView(frame: CGRect, message: String, color: UIColor, startY: CGFloat, endY: CGFloat) -> UIVIew{

     //Adding label to view
     let label = UILabel()
     label.frame = CGRect(x: 0, y: 0, width: frame.width, height:70)
     label.textAlignment = .center
     label.textColor = .white
     label.numberOfLines = 0
     label.text = message
     self.addSubview(label)
     self.backgroundColor = color

     //Adding Animation to view
     UIView.animate(withDuration:0.5, delay: 0, options: 
     [.curveEaseOut], animations:{
        self.frame = CGRect(x: 0, y: startY, width: frame.width, height: 64)
        }) { _ in
           UIView.animate(withDuration: 0.5, delay: 4, options: [.curveEaseOut], animations: {
                 self.frame = CGRect(x: 0, y: endY, width: frame.width, height:64)
           }, completion: {_ in
               self.removeFromSuperview()
           })
        }
     return self
  }

并且您可以在 class 中使用它。这个例子 startY 是当你有一个 navigationController:

class MyClassViewController: UIViewController{

      override func viewDidLoad(){
         super.viewDidLoad()
         self.view.addSubView(UIView().addCustomAlert(frame: self.view.frame, message: "No internet connection", color: .red, startY:64, endY:-64))
      }
}