在Swift自定义动画中正确处理/清除CADisplayLink等问题? [英] Correct handling / cleanup / etc of CADisplayLink in Swift custom animation?

查看:250
本文介绍了在Swift自定义动画中正确处理/清除CADisplayLink等问题?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用 CADisplayLink

var link:CADisplayLink?
var startTime:Double = 0.0
let animTime:Double = 0.2
let animMaxVal:CGFloat = 0.4

private func yourAnim()
    {
    if ( link != nil )
        {
        link!.paused = true
        //A:
        link!.removeFromRunLoop(
          NSRunLoop.mainRunLoop(), forMode:NSDefaultRunLoopMode)
        link = nil
        }

    link = CADisplayLink(target: self, selector: #selector(doorStep) )
    startTime = CACurrentMediaTime()
    link!.addToRunLoop(
      NSRunLoop.currentRunLoop(), forMode:NSDefaultRunLoopMode)
    }

func doorStep()
    {
    let elapsed = CACurrentMediaTime() - startTime

    var ping = elapsed
    if (elapsed > (animTime / 2.0)) {ping = animTime - elapsed}

    let frac = ping / (animTime / 2.0)
    yourAnimFunction(CGFloat(frac) * animMaxVal)

    if (elapsed > animTime)
        {
        //B:
        link!.paused = true
        link!.removeFromRunLoop(
          NSRunLoop.mainRunLoop(), forMode:NSDefaultRunLoopMode)
        link = nil
        yourAnimFunction(0.0)
        }
    }

func killAnimation()
    {
    // for example if the cell disappears or is reused
    //C:
    ????!!!!
    }

似乎有很多问题。

在(A :),即使 link 不为空,也可能无法将其从运行循环中删除。 (例如,有人可能使用 link = link:CADisplayLink()对其进行了初始化-尝试进行崩溃。)

At (A:), even though link is not null, it may not be possible to remove it from a run loop. (For example, someone may have initialized it with link = link:CADisplayLink() - try it for a crash.)

第二个(B :)似乎是一团糟...肯定有更好的方法(和更多的Swift),即使时间刚到,如果它为零怎么办?

Secondly at (B:) it seems to be a mess ... surely there's a better (and more Swift) way, and what if it's nil even though the time just expired?

最后在(C :)中,如果您想破坏动画……我感到沮丧,不知道什么是最好的。

Finally in (C:) if you want to break the anim ... I got depressed and have no clue what is best.

推荐答案

这是一个一个简单的示例,展示了我将如何实现 CADisplayLink (在Swift 3中):

Here’s a simple example showing how I’d go about implementing a CADisplayLink (in Swift 3):

class C { // your view class or whatever

  private var displayLink: CADisplayLink?
  private var startTime = 0.0
  private let animLength = 5.0

  func startDisplayLink() {

    stopDisplayLink() // make sure to stop a previous running display link
    startTime = CACurrentMediaTime() // reset start time

    // create displayLink & add it to the run-loop
    let displayLink = CADisplayLink(
      target: self, selector: #selector(displayLinkDidFire)
    )
    displayLink.add(to: .main, forMode: .commonModes)
    self.displayLink = displayLink
  }

  @objc func displayLinkDidFire(_ displayLink: CADisplayLink) {

    var elapsed = CACurrentMediaTime() - startTime

    if elapsed > animLength {
      stopDisplayLink()
      elapsed = animLength // clamp the elapsed time to the anim length
    }

    // do your animation logic here
  }

  // invalidate display link if it's non-nil, then set to nil
  func stopDisplayLink() {
    displayLink?.invalidate()
    displayLink = nil
  }
}

注意事项:


  • 我们在这里使用 nil 来表示显示链接未运行的状态–

  • 我们不是使用 removeFromRunLoop(),而是使用 invalidate(),如果尚未将显示链接添加到运行循环中,则不会崩溃。但是,这种情况永远都不会出现-因为我们总是在创建显示链接后立即将其添加到运行循环中。

  • 我们制作了 displayLink 私有,以防止外部类将其置于意外状态(例如,使其无效但不将其设置为)。 / li>
  • 我们有一个 stopDisplayLink()方法,该方法会使显示链接无效(如果它不是nil)并将其设置为 nil –而不是复制并粘贴此逻辑。

  • 我们未设置已暂停更改为 true ,然后取消显示链接,因为这是多余的。

  • 不是强制展开 displayLink 检查是否为非零后,我们使用可选的链接,例如 displayLink?.invalidate()(将称为 invalidate()(如果显示链接不是nil)。尽管在您给定的情况下(当您检查是否为零时)强制展开可能是安全的 –在将来的重构中它可能是不安全的,因为您可以在不考虑这对强制展开产生什么影响的情况下重新构造逻辑。

  • 我们将已花费的时间限制为动画持续时间,以确保以后的动画逻辑不会产生值超出预期范围。

  • 我们的更新方法 displayLinkDidFire(_:)接受单个类型为 CADisplayLink ,根据文档需要

  • We’re using nil here to represent the state in which the display link isn’t running – as there’s no easy way of getting this information from an invalidated display link.
  • Instead of using removeFromRunLoop(), we’re using invalidate(), which will not crash if the display link hasn’t already been added to a run-loop. However this situation should never arise in the first place – as we’re always immediately adding the display link to the run-loop after creating it.
  • We’ve made the displayLink private in order to prevent outside classes from putting it in an unexpected state (e.g invalidating it but not setting it to nil).
  • We have a single stopDisplayLink() method that both invalidates the display link (if it is non-nil) and sets it to nil – rather than copy and pasting this logic.
  • We’re not setting paused to true before invalidating the display link, as this is redundant.
  • Instead of force unwrapping the displayLink after checking for non-nil, we’re using optional chaining e.g displayLink?.invalidate() (which will call invalidate() if the display link isn’t nil). While force unwrapping may be ‘safe’ in your given situation (as you’re checking for nil) – it’s potentially unsafe when it comes to future refactoring, as you may re-structure your logic without considering what impact this has on the force unwraps.
  • We’re clamping the elapsed time to the animation duration in order to ensure that the later animation logic doesn’t produce a value out of the expected range.
  • Our update method displayLinkDidFire(_:) takes a single argument of type CADisplayLink, as required by the documentation.

这篇关于在Swift自定义动画中正确处理/清除CADisplayLink等问题?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆