【问题标题】:Breathing animation in Xcode-Swift while app being responsive应用响应时 Xcode-Swift 中的呼吸动画
【发布时间】:2017-03-05 15:19:02
【问题描述】:

我正在尝试让明星在循环中“呼吸”的图像。变大变小,然后不断重复。我希望这在我的应用程序仍在运行且响应迅速时继续。当我在我的应用程序中转到另一个视图时,我希望动画停止。

这是我得到的

    override func viewDidLoad() {
    super.viewDidLoad()

    DispatchQueue.main.async {
            self.animateStars()
    }
}

func animateStars() {
  UIView.animate(withDuration: 4, animations : {
        self.star.transform = CGAffineTransform(scaleX: 2.5, y: 2.5)

    }) { finished in
        NSLog("Star is big. Finished = \(finished)")
    }

    UIView.animate(withDuration: 4, animations : {
        self.star.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)

    }) { finished in
        NSLog("Star is small. Finished = \(finished)")
    }
}

这并不难循环。而且我不知道如何让它循环而不让它在后台像疯了一样运行,当用户点击带有 segue 的按钮时,转到另一个视图。

我在 Objective C 中找到了一种解决方案,但我似乎不明白我应该如何将它翻译成 Swift 3.0。此处示例:https://stackoverflow.com/a/31978173/5385322

 [UIView animateWithDuration:1
                  delay:0
                options:UIViewKeyframeAnimationOptionAutoreverse | UIViewKeyframeAnimationOptionRepeat
             animations:^{
                 yourView.transform = CGAffineTransformMakeScale(1.5, 1.5);
             }
             completion:nil];

感谢您的宝贵时间和建议! //西蒙

【问题讨论】:

  • UIView.animate(withDuration: 1, delay: 0, options: [.autoreverse, .repeat], animations: { self.star.transform = CGAffineTransform(scaleX: 2.5, y: 2.5) })
  • 这绝对很棒!一个棘手的问题,我不确定是否与此有关。但是当我转到另一个视图,然后按返回返回到第一个视图(带有动画)时,动画被冻结并且不起作用。知道如何解决这个问题,以便每次我进入这个主视图时动画都会运行吗?
  • 为什么我得到-1? :)
  • 我不知道你为什么得到 -1。不是我。在您的 viewWillDisappear 中,您应该调用 removeAllAnimations 并将转换设置回身份。然后在您的 viewWillAppear 中,您应该重新调用动画。

标签: ios swift xcode uiviewanimation dispatch-async


【解决方案1】:

您发布的 Objective-C 重复动画代码的 Swift 版本是:

UIView.animate(withDuration: 1.0,
               delay: 0,
               options: [.autoreverse, .repeat, .allowUserInteraction],
               animations: {
                self.star.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
},
               completion: nil

)

(在 Swift 中,大多数选项标志参数,如该动画代码中的 options 参数都是 OptionSet 类型,这是支持 Swift Set 语法的位标志,因此您可以在方括号中列出所需的选项, 用逗号分隔。

请注意,@TungFam 的解决方案不会像所写的那样工作,但如果您使用完成块检查 animate 布尔值并重新调用动画方法(如果为真),则可以使用该方法。

编辑:

如果要停止动画,请使用

self.star.layer.removeAllAnimations()
self.star.layer.transform = CGAffineTransform.identity

编辑#2:

如果您希望能够随意启动和停止动画,可以使用如下代码:

func animateView(_ view: UIView, animate: Bool) {
  if animate {
    UIView.animate(withDuration: 1.0,
                   delay: 0,
                   options: [.autoreverse, .repeat, .allowUserInteraction],
                   animations: {
                    view.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
    },
                   completion: nil

    )
  } else {
    self.button.layer.removeAllAnimations()
    UIView.animate(withDuration: 1.0,
                   delay: 0,
                   options: [.curveEaseOut, .beginFromCurrentState, .allowUserInteraction],
                   animations: {
                    view.transform = CGAffineTransform.identity
    },
                   completion: nil

    )
  }
}

当动画停止时,该代码将使视图平滑地动画回到正常大小。它还将 .allowUserInteraction 标志添加到动画中,以防您正在制作动画的视图是您希望在其大小发生变化时能够点击的按钮。

【讨论】:

  • 稍作改动以适应我自己的代码...这创造了奇迹!非常感谢你。绝对是老板的功劳!我将用我最终得到的结果来回答我自己的问题。 :)
  • 哦,对不起,对!你去吧。如果您是对我的问题打-1的人,我希望这会将其更改为+1 :)
  • Duncan C ... 您的代码使我的按钮具有动画效果,但现在它不可点击。有什么建议吗?
  • 确保在所有动画中使用.allowUserInteraction 属性。在我的代码中,我没有在第二个动画中包含该选项。
【解决方案2】:

尝试添加保存 bool 值的变量,该值表示它是否需要动画。并在用户点击按钮转到下一个屏幕时更改它。

类似这样的:

var animate = true

override func viewDidLoad() {
   super.viewDidLoad()

   self.animateStars()
}

func animateStars() {
  while animate == true   {
    // breathing animation
  }
}

@IBAction func buttonPressed(_ sender: UIButton) {
   animate = false

   // perform your segue
}

如果您想在用户返回此屏幕时再次对其进行动画处理,请将self.animateStars() 放在viewWillAppear 方法中。

【讨论】:

  • 这不太对。您需要使用 animate(withDuration:delay:options:animations:completion:) 形式的动画调用,如果 animate 标志仍然为真,则使用完成处理程序重新调用动画方法。
  • @DuncanC 同意你的看法。没有检查动画的代码。认为问题是如何在去另一个 VC 之前停止动画。谢谢
  • @DuncanC ,您能否更彻底地解释一下。不幸的是,TungFam 的解决方案导致了一个不合理的应用程序和一个无限循环,因为之前在评论中建议的动画是:beyowulf,有效,让它重复自己。所以动画变量永远不会变成假。
  • @SimonRiemertzon 我读到这个“这绝对是伟大的!一个棘手的问题,我不确定是否与此有关。但是当我转到另一个视图时,然后按返回返回第一个视图(带有动画),动画被冻结并且不起作用。知道如何解决这个问题,以便每次我进入这个主视图时动画都会运行吗? --- 结论就是在viewWillAppear 中调用self.animateStars()。用while循环忽略我的回答。
  • @SimonRiemertzon,看看我的回答。
【解决方案3】:

借助@DuncanC 的一些见解,我能够很好地完成这项工作。现在,当我转到另一个视图时动画会停止,当我返回原始视图时会继续。

代码如下所示:

class MainViewController: UIViewController {

@IBOutlet weak var star: UIImageView!

var queue = DispatchQueue.main

  override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    self.star.layer.removeAllAnimations()
    self.star.transform = CGAffineTransform.identity

    queue.async {
        self.animateStars()
    }
}
    @IBAction func someAction(_ sender: Any) {
    performSegue(withIdentifier: "someSegue", sender: self)
    }

    func animateStars() {
    UIView.animate(withDuration: 1,
                   delay: 0,
                   options:[.autoreverse, .repeat],
                   animations : {
                    self.star.transform = CGAffineTransform(scaleX: 2.5, y: 2.5)})
    { finished in
        UIView.animate(withDuration: 1.0,
                       delay: 0,
                       options: [.curveEaseOut, .beginFromCurrentState],
                       animations: {
                        self.star.transform = CGAffineTransform.identity
        },
                       completion: nil
        )
    }
    UIView.animate(withDuration: 1, 
                   delay: 0, 
                   options: [.autoreverse, .repeat], 
                   animations: { self.star.transform = CGAffineTransform(scaleX: 2.5, y: 2.5) })
}
}

我对编程还是很陌生,正在为它上学,到目前为止我还没有学到太多关于测试的知识。正因为如此,我还没有测试过这个内存泄漏和诸如此类的东西。但是代码有效,这意味着我暂时很高兴。

感谢大家的帮助!如果您认为可以改进代码,请务必提出建议。 :)

//西蒙

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-09
    • 2017-08-30
    • 2017-05-03
    • 2017-02-17
    相关资源
    最近更新 更多