使用选项卡栏关闭视图控制器后动画不会停止
Animations not stopping after view controller is dismissed using tab bar
问题
我有两个视图控制器,它们都包含在各自的 UINavigationController
和一个 UITabBarController
中。在其中一个视图控制器上,我正在创建一个气泡效果,我在屏幕上绘制气泡并为它们的位置设置动画。当我使用选项卡栏移动到另一个视图控制器时出现问题,这导致 CPU 出现峰值并保持在 100%,气泡继续动画。
代码
气泡的代码封装在 UIView
子类中。
override func draw(_ rect: CGRect) {
// spawn shapes
for _ in 1 ... 10 { // spawn 75 shapes initially
spawn()
}
}
drawRect
方法重复调用 spawn
函数以使用气泡填充视图。
fileprivate func spawn() {
let shape = CAShapeLayer()
shape.opacity = 0.0
// create an inital path at the starting position
shape.path = UIBezierPath(arcCenter: CGPoint.zero, radius: 1, startAngle: 0, endAngle: 360 * (CGFloat.pi / 180), clockwise: true).cgPath
shape.position = CGPoint.zero
layer.addSublayer(shape)
// add animation group
CATransaction.begin()
let radiusAnimation = CABasicAnimation(keyPath: "path")
radiusAnimation.fromValue = shape.path
radiusAnimation.toValue = UIBezierPath(arcCenter: center, radius: 100, startAngle: 0, endAngle: 360 * (CGFloat.pi / 180), clockwise: true).cgPath
CATransaction.setCompletionBlock { [unowned self] in
// remove the shape
shape.removeFromSuperlayer()
shape.removeAllAnimations()
// spawn a new shape
self.spawn()
}
let movementAnimation = CABasicAnimation(keyPath: "position")
movementAnimation.fromValue = NSValue(cgPoint: CGPoint.zero)
movementAnimation.toValue = NSValue(cgPoint: CGPoint(x: 100, y: 100))
let animationGroup = CAAnimationGroup()
animationGroup.animations = [radiusAnimation, movementAnimation]
animationGroup.fillMode = kCAFillModeForwards
animationGroup.isRemovedOnCompletion = false
animationGroup.duration = 2.0
shape.add(animationGroup, forKey: "bubble_spawn")
CATransaction.commit()
}
在 CATransaction
完成处理程序中,我从父视图中删除形状并创建一个新的。 对 self.spawn()
的函数调用似乎是问题所在
在包含视图控制器的 viewDidDisappear
上,我调用以下内容:
func removeAllAnimationsFromLayer() {
layer.sublayers?.forEach({ (layer) in
layer.removeAllAnimations()
layer.removeFromSuperlayer()
})
CATransaction.setCompletionBlock(nil)
}
答案尝试
我尝试将 removeAllAnimations
函数添加到 UITabBarControllerDelegate
extension BaseViewController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
bubblesView.removeAllAnimationsFromLayer()
}
}
在 UITabBarController 中,关联的视图控制器具有平面结构。即每个选项卡中的视图控制器 运行 独立。
因此,func removeAllAnimationsFromLayer()
必须在委托方法中添加
func tabBarController(tabBarController: UITabBarController, didSelectViewController viewController: UIViewController)
我认为你的问题是,你只使用一个线程来处理所有这些东西。请尝试将影响 GUI 的所有内容分派到主线程,并可能将新的 spawn
实例分派到其他线程。看看情况如何。像这样:
fileprivate func spawn() {
let shape = CAShapeLayer()
shape.opacity = 0.0
// create an inital path at the starting position
shape.path = UIBezierPath(arcCenter: CGPoint.zero, radius: 1, startAngle: 0, endAngle: 360 * (CGFloat.pi / 180), clockwise: true).cgPath
shape.position = CGPoint.zero
// create an inital path at the starting position
shape.path = UIBezierPath(arcCenter: startingPosition, radius: startRadius, startAngle: BubbleConstants.StartingAngle, endAngle: BubbleConstants.EndAngle, clockwise: true).cgPath
shape.position = startingPosition
// set the fill color
shape.fillColor = UIColor.white.cgColor
layer.addSublayer(shape)
shape.opacity = Float(opacity)
DispatchQueue.main.async {
self.layer.addSublayer(shape)
CATransaction.begin()
}
let radiusAnimation = CABasicAnimation(keyPath: "path")
radiusAnimation.fromValue = shape.path
radiusAnimation.toValue = UIBezierPath(arcCenter: center, radius: endRadius, startAngle: BubbleConstants.StartingAngle, endAngle: BubbleConstants.EndAngle, clockwise: true).cgPath
DispatchQueue.main.async { [unowned self] in
CATransaction.setCompletionBlock { [unowned self] in
// remove the shape
DispatchQueue.main.async {
shape.removeFromSuperlayer()
shape.removeAllAnimations()
}
DispatchQueue.global(qos: .background).async {
// spawn a new shape
self.spawn()
}
}
}
let movementAnimation = CABasicAnimation(keyPath: "position")
movementAnimation.fromValue = NSValue(cgPoint: startingPosition)
movementAnimation.toValue = NSValue(cgPoint: destination)
let animationGroup = CustomAnimationGroup()
animationGroup.animations = [radiusAnimation, movementAnimation]
animationGroup.fillMode = kCAFillModeForwards
animationGroup.isRemovedOnCompletion = false
animationGroup.duration = duration
shape.add(animationGroup, forKey: "bubble_spawn")
DispatchQueue.main.async {
CATransaction.commit()
}
}
问题
我有两个视图控制器,它们都包含在各自的 UINavigationController
和一个 UITabBarController
中。在其中一个视图控制器上,我正在创建一个气泡效果,我在屏幕上绘制气泡并为它们的位置设置动画。当我使用选项卡栏移动到另一个视图控制器时出现问题,这导致 CPU 出现峰值并保持在 100%,气泡继续动画。
代码
气泡的代码封装在 UIView
子类中。
override func draw(_ rect: CGRect) {
// spawn shapes
for _ in 1 ... 10 { // spawn 75 shapes initially
spawn()
}
}
drawRect
方法重复调用 spawn
函数以使用气泡填充视图。
fileprivate func spawn() {
let shape = CAShapeLayer()
shape.opacity = 0.0
// create an inital path at the starting position
shape.path = UIBezierPath(arcCenter: CGPoint.zero, radius: 1, startAngle: 0, endAngle: 360 * (CGFloat.pi / 180), clockwise: true).cgPath
shape.position = CGPoint.zero
layer.addSublayer(shape)
// add animation group
CATransaction.begin()
let radiusAnimation = CABasicAnimation(keyPath: "path")
radiusAnimation.fromValue = shape.path
radiusAnimation.toValue = UIBezierPath(arcCenter: center, radius: 100, startAngle: 0, endAngle: 360 * (CGFloat.pi / 180), clockwise: true).cgPath
CATransaction.setCompletionBlock { [unowned self] in
// remove the shape
shape.removeFromSuperlayer()
shape.removeAllAnimations()
// spawn a new shape
self.spawn()
}
let movementAnimation = CABasicAnimation(keyPath: "position")
movementAnimation.fromValue = NSValue(cgPoint: CGPoint.zero)
movementAnimation.toValue = NSValue(cgPoint: CGPoint(x: 100, y: 100))
let animationGroup = CAAnimationGroup()
animationGroup.animations = [radiusAnimation, movementAnimation]
animationGroup.fillMode = kCAFillModeForwards
animationGroup.isRemovedOnCompletion = false
animationGroup.duration = 2.0
shape.add(animationGroup, forKey: "bubble_spawn")
CATransaction.commit()
}
在 CATransaction
完成处理程序中,我从父视图中删除形状并创建一个新的。 对 self.spawn()
的函数调用似乎是问题所在
在包含视图控制器的 viewDidDisappear
上,我调用以下内容:
func removeAllAnimationsFromLayer() {
layer.sublayers?.forEach({ (layer) in
layer.removeAllAnimations()
layer.removeFromSuperlayer()
})
CATransaction.setCompletionBlock(nil)
}
答案尝试
我尝试将 removeAllAnimations
函数添加到 UITabBarControllerDelegate
extension BaseViewController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
bubblesView.removeAllAnimationsFromLayer()
}
}
在 UITabBarController 中,关联的视图控制器具有平面结构。即每个选项卡中的视图控制器 运行 独立。
因此,func removeAllAnimationsFromLayer()
必须在委托方法中添加
func tabBarController(tabBarController: UITabBarController, didSelectViewController viewController: UIViewController)
我认为你的问题是,你只使用一个线程来处理所有这些东西。请尝试将影响 GUI 的所有内容分派到主线程,并可能将新的 spawn
实例分派到其他线程。看看情况如何。像这样:
fileprivate func spawn() {
let shape = CAShapeLayer()
shape.opacity = 0.0
// create an inital path at the starting position
shape.path = UIBezierPath(arcCenter: CGPoint.zero, radius: 1, startAngle: 0, endAngle: 360 * (CGFloat.pi / 180), clockwise: true).cgPath
shape.position = CGPoint.zero
// create an inital path at the starting position
shape.path = UIBezierPath(arcCenter: startingPosition, radius: startRadius, startAngle: BubbleConstants.StartingAngle, endAngle: BubbleConstants.EndAngle, clockwise: true).cgPath
shape.position = startingPosition
// set the fill color
shape.fillColor = UIColor.white.cgColor
layer.addSublayer(shape)
shape.opacity = Float(opacity)
DispatchQueue.main.async {
self.layer.addSublayer(shape)
CATransaction.begin()
}
let radiusAnimation = CABasicAnimation(keyPath: "path")
radiusAnimation.fromValue = shape.path
radiusAnimation.toValue = UIBezierPath(arcCenter: center, radius: endRadius, startAngle: BubbleConstants.StartingAngle, endAngle: BubbleConstants.EndAngle, clockwise: true).cgPath
DispatchQueue.main.async { [unowned self] in
CATransaction.setCompletionBlock { [unowned self] in
// remove the shape
DispatchQueue.main.async {
shape.removeFromSuperlayer()
shape.removeAllAnimations()
}
DispatchQueue.global(qos: .background).async {
// spawn a new shape
self.spawn()
}
}
}
let movementAnimation = CABasicAnimation(keyPath: "position")
movementAnimation.fromValue = NSValue(cgPoint: startingPosition)
movementAnimation.toValue = NSValue(cgPoint: destination)
let animationGroup = CustomAnimationGroup()
animationGroup.animations = [radiusAnimation, movementAnimation]
animationGroup.fillMode = kCAFillModeForwards
animationGroup.isRemovedOnCompletion = false
animationGroup.duration = duration
shape.add(animationGroup, forKey: "bubble_spawn")
DispatchQueue.main.async {
CATransaction.commit()
}
}