我正在尝试在个人资料图片后面编写一个
animation
,以鼓励用户单击它。
这是一个逐渐变大和变小的圆圈。
override func layoutSubviews() {
super.layoutSubviews()
UIView.animate(withDuration: 1.0, delay: 0, options: [.repeat, .autoreverse], animations: {
self.bouncedView.transform = CGAffineTransform(scaleX:
1.3, y: 1.3)
}, completion: nil)
}
问题是,当我转到另一个
viewController
时,动画停止,圆圈保持在屏幕截图上。你知道我怎样才能避免这个问题吗?
在 ViewDidAppear 上转换为 .identity。类似于下面的代码:
class HomeController: UIViewController {
@IBOutlet weak var viewToAnimate: UIView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
self.viewToAnimate.transform = .identity
animateView()
}
func animateView(){
UIView.animate(withDuration: 1.0, delay: 0, options: [.repeat, .autoreverse], animations: {
self.viewToAnimate.transform = CGAffineTransform(scaleX:
1.3, y: 1.3)
}, completion: nil)
}
}
您可能已经猜到的问题是 UIView.animate 仅在 ViewDidLoad 方法上调用,并且由于我们在从另一个 ViewController 返回时无法访问该代码,因此最好在 ViewWillAppear 方法中启动动画.
如果在选项卡之间切换时出现相同的问题,请为要设置动画的视图创建一个单独的 UIView 子类,然后按以下步骤操作:
class AnimateView: UIView {
override func awakeFromNib() {
super.awakeFromNib()
}
override func willMove(toWindow newWindow: UIWindow?) {
super.willMove(toWindow: newWindow)
self.transform = .identity
animateView()
}
func animateView(){
UIView.animate(withDuration: 1.0, delay: 0, options: [.repeat, .autoreverse], animations: {
self.transform = CGAffineTransform(scaleX:
1.3, y: 1.3)
}, completion: nil)
}
}
在这里,您已将 animate 函数应用于 UIView 对象,每当视图出现时,动画就会重置。
嗯,我还没有一个令人满意的答案(至少这会让我满意),但我想至少添加一些我通过一些实验获得的见解。
首先,当
viewController
不是当前呈现的动画时,动画似乎就结束了。在您的情况下,这意味着动画停止并以视图放大 1.3 倍的状态结束,并停止重复。仅当您第一次展示时,layoutSubviews
才会被调用。至少 viewController.viewDidLayoutSubviews
仅在开始时被调用,而不是在您返回时被调用(因此当视图重新出现时 layoutSubviews
将不会被执行) - 因此动画不会重新启动。
我尝试将动画移动到
viewDidAppear
的 UIViewController
- 这也不起作用,因为停止动画会导致视图已缩放 1.3 倍的状态。在创建动画之前将状态重置为 CGAffineTransform.identity
确实有效。
现在正如我所说,这可以作为您如何使其工作的解决方法,但是,我认为您真正正在寻找的是一些钩子,可以告诉您的视图(而不是 viewController)它再次呈现。但下面这个最小的例子至少可以帮助其他人看一下,而不是从头开始。
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
let animator = UIViewPropertyAnimator(duration: 1, timingParameters: UICubicTimingParameters(animationCurve: .linear))
init(title: String) {
super.init(nibName: nil, bundle: nil)
self.title = title
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
let animatableView = UIView()
override func loadView() {
let view = UIView()
view.backgroundColor = .white
animatableView.frame = CGRect(x: 150, y: 400, width: 100, height: 100)
animatableView.backgroundColor = UIColor.magenta
view.addSubview(animatableView)
self.view = view
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.animatableView.transform = CGAffineTransform.identity
UIView.animate(withDuration: 1.0, delay: 0, options: [.repeat, .autoreverse], animations: {
self.animatableView.transform = CGAffineTransform(scaleX:
1.3, y: 1.3)
}, completion: nil)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
print("\(self.title) >> Lay out")
}
}
let tabBar = UITabBarController()
tabBar.viewControllers = [MyViewController(title: "first"), MyViewController(title: "second"), MyViewController(title: "third")]
tabBar.selectedIndex = 0
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = tabBar
编辑
我添加了几行来检查
self.animatableView.layer.animationKeys()
是否包含任何动画,似乎切换选项卡会从视图层中删除动画 - 所以你必须找到一种方法在每次视图重新出现时添加动画。
编辑2 所以我会同意@SWAT的答案并使用
willMove(toWindow:)
而不是layoutSubviews
。
您可以添加完成块,您将在其中创建第一个视图状态。例如:
UIView.animate(withDuration:1.0,延迟:0,选项:[.repeat,.autoreverse],动画:{
self.bouncedView.transform = CGAffineTransform(scaleX:
1.3, y: 1.3)
} completion: { _ in
self.bouncedView.transform = CGAffineTransform(scaleX:
1.0, y: 1.0)
}