【问题标题】:(Swift 3) - How to include animations in custom UIView(Swift 3) - 如何在自定义 UIView 中包含动画
【发布时间】:2017-08-09 17:55:26
【问题描述】:

我有一个名为View 的UIView 子类,我希望在其中有一个“波纹”从用户双击的位置向外移动(以及执行其他绘图功能)。这是我的draw方法的相关部分,目前:

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

这就是r.paint() 的实现方式,在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.sync 替换DispatchQueue.main.async 来解决这个问题,但这只是让应用程序挂起。我究竟做错了什么?如果这不是解决问题的正确方法,我如何才能在应用程序仍然执行其他功能(例如产生更多涟漪)的同时获得平滑的涟漪动画(其中涉及一个在改变颜色的同时增长的圆圈)? (这意味着多个涟漪需要能够同时工作。)

【问题讨论】:

    标签: ios uiview timer uikit core-graphics


    【解决方案1】:

    您可以使用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
        }
    }
    

    【讨论】:

    • 谢谢,等我回到电脑前一定要试试这个
    • 每次动画变化时视图是否会重新加载iter?
    • 您必须将所有绘图代码放入 MyLayer draw(ctx) 函数中。当您启动应用程序时,您将在日志中看到您的 MyLayer 何时被绘制,它将在动画的每个渲染调用中完成,并在当前时刻使用来自 animation.values 数组的相应“iter”值。阅读有关 CAKeyframeAnimation 的更多信息,也许您会切换到计算值而不是值数组的插值函数。
    • 是的,我看到了看起来很有用的 CABasicAnimation;你认为这对我有用吗?
    • 是的,为什么不... fromValue = 0, toValue = 100。但是 BasicAnimation 可以将值插入为浮点数,考虑将 iter 类型更改为浮点数,这样您的动画会更流畅。
    【解决方案2】:

    自定义 UIView,例如带有动画的 AlertView,适用于 Swift 3 和 Swift 4

    你可以使用扩展:

    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
      }
    

    你可以在你的班级使用它。这个例子 startY 是当你有一个导航控制器时:

    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))
          }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多