我創建了一個非常簡單的 UIView 類(SpinningCircleView),它永遠執行旋轉的圓圈影片。我想從我的 ViewController 呼叫這個類來在螢屏上顯示旋轉的圓圈影片。雖然影片效果很好,但當應用程式置于后臺時,我觀察到不正確的行為。這是旋轉圓圈影片的樣子:

為了創建旋轉的圓圈,我使用了兩個單獨的 UIViewPropertyAnimator,一個將圓圈旋轉 180 度(即 Pi),另一個完成 360 度(即 0 度)。在第二個影片師的完成塊中,我遞回地呼叫 startSpinningCircleAnimation() 函式。我創建了一個計數器來跟蹤呼叫 startSpinningCircleAnimation() 的次數(即圓旋轉的次數)。當應用程式處于活動狀態時(在前臺),計數器按預期遞增,我可以在 Xcode 終端視窗中看到輸出:
Starting animation
1: START Spinning Circle Animation
2: START Spinning Circle Animation -> recursive call
3: START Spinning Circle Animation -> recursive call
4: START Spinning Circle Animation -> recursive call
問題或不正確的行為發生在我將應用程式置于后臺時......突然間,我在終端中看到數百個“ START Spinning Circle Animation -> recursive call”。當應用程式回到前臺時,計數器和終端輸出恢復正常的計數器增量。
當應用程式置于后臺時,為什么要對 startSpinningCircleAnimation() 函式進行數百次呼叫?當應用程式在背景和前景之間移動時,如何正確暫停影片并恢復影片?我已經瀏覽了各種帖子,但我無法找出解決方案。
請幫忙!!
這是我的 SpinningCircleView 類:
import UIKit
class SpinningCircleView: UIView
{
private lazy var spinningCircle = CAShapeLayer()
private lazy var animator1 = UIViewPropertyAnimator(duration: 1, curve: .linear, animations: nil)
private lazy var animator2 = UIViewPropertyAnimator(duration: 1, curve: .linear, animations: nil)
public var counter = 0
override init (frame: CGRect)
{
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder)
{
fatalError("init(coder:) has not been implemented")
}
private func configure()
{
frame = CGRect(x: 0, y: 0, width: 100, height: 100)
let rect = self.bounds
let circularPath = UIBezierPath(ovalIn: rect)
spinningCircle.path = circularPath.cgPath
spinningCircle.fillColor = UIColor.clear.cgColor
spinningCircle.strokeColor = UIColor.systemRed.cgColor
spinningCircle.lineWidth = 10
spinningCircle.strokeEnd = 0.25
spinningCircle.lineCap = .round
self.layer.addSublayer(spinningCircle)
}
func startSpinningCircleAnimation()
{
counter = 1
let criteria1 = animator1.state == .active && !animator1.isRunning
let criteria2 = animator2.state == .active && !animator2.isRunning
let criteria3 = animator1.state == .inactive && animator2.state == .inactive
let criteria4 = (animator1.state == .inactive && animator2.state.rawValue == 5) || (animator2.state == .inactive && animator1.state.rawValue == 5)
if (criteria1)
{
// Since animator1 is Paused, we will resume the animation
print("\(self.counter): ~ RESUME Spinning Circle Animation")
animator1.startAnimation()
} else if (criteria2)
{
// Since animator2 is Paused, we will resume the animation
print("\(self.counter): ~ RESUME Spinning Circle Animation")
animator2.startAnimation()
} else if (criteria3 || criteria4)
{
if (criteria3)
{
print("\(self.counter): START Spinning Circle Animation")
} else if (criteria4)
{
print("\(self.counter): START Spinning Circle Animation -> recursive call")
}
animator1.addAnimations
{
self.transform = CGAffineTransform(rotationAngle: .pi)
}
animator1.addCompletion
{ _ in
self.animator2.addAnimations
{
self.transform = CGAffineTransform(rotationAngle: 0)
}
self.animator2.addCompletion
{ _ in
// Recursively call this start spinning
self.startSpinningCircleAnimation()
}
self.animator2.startAnimation()
}
animator1.startAnimation()
} else
{
print("\(self.counter): >>>>>>>>> HERE <<<<<<<<<<< \(self.animator1.state) \(self.animator1.isRunning) \(self.animator2.state) \(self.animator2.isRunning)")
}
}
func stopSpinningCircleAnimation()
{
print("\(self.counter): - STOP Spinning Circle Animation Begin: \(self.animator1.state) \(self.animator1.isRunning) \(self.animator2.state) \(self.animator2.isRunning)")
if (self.animator1.isRunning)
{
self.animator1.pauseAnimation()
} else if (self.animator2.isRunning)
{
self.animator2.pauseAnimation()
}
print("\(self.counter): - STOP Spinning Circle Animation End: \(self.animator1.state) \(self.animator1.isRunning) \(self.animator2.state) \(self.animator2.isRunning)")
}
}
這是我的 ViewController 設定 SpinningCircleView 的實體并開始影片:
class ViewController: UIViewController
{
private lazy var spinningCircleView = SpinningCircleView()
override func viewDidLoad()
{
super.viewDidLoad()
// Setup the spinning circle and display the animation to the screen
spinningCircleView.frame = CGRect(x: view.center.x - 50, y: 100, width: 100, height: 100)
spinningCircleView.tag = 100
view.addSubview(spinningCircleView)
}
override func viewDidAppear(_ animated: Bool)
{
super.viewDidAppear(animated)
print("Starting animation")
spinningCircleView.startSpinningCircleAnimation()
}
override func viewDidDisappear(_ animated: Bool)
{
super.viewDidDisappear(animated)
print("Pausing animation")
spinningCircleView.stopSpinningCircleAnimation()
}
}
uj5u.com熱心網友回復:
我會回答你的每一個問題
當應用程式置于后臺時,為什么要對 startSpinningCircleAnimation() 函式進行數百次呼叫?
我認為因為每次你呼叫startSpinningCircleAnimation()完成并檢查state哪個是不活動的,......之前的影片。這里的錯誤可能在于您的遞回檢查狀態的邏輯。
我不會深入研究您的代碼以找到錯誤的代碼。但我會重構你的SpinningCircleView
首先,不需要UIViewPropertyAnimator為了讓視圖連續旋轉而使用兩個。這里的簡單邏輯就是讓視圖旋轉并重復它。
let rotation : CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
rotation.toValue = Double.pi * 2
rotation.duration = 1
rotation.isCumulative = true
rotation.repeatCount = .greatestFiniteMagnitude
rotation.isRemovedOnCompletion = false
self.layer.add(rotation, forKey: "rotateInfinityAnimation")
對于你的第二個問題
當應用程式在背景和前景之間移動時,如何正確暫停影片并恢復影片?
您只需要捕獲您的應用程式將進入前臺或后臺的通知NotificationCenter。
private func addNotification() {
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(appMovedToBackground), name: UIApplication.willResignActiveNotification, object: nil)
notificationCenter.addObserver(self, selector: #selector(appEnterForground), name: UIApplication.didBecomeActiveNotification, object: nil)
}
對于停止和恢復影片,您只需要知道當影片發生時,視圖層就是發生影片的層。所以你只需要在視圖的圖層上添加停止和恢復的功能。然后每次你想暫停或恢復時,只需從view.layer.resume()或view.layer.stop()
extension CALayer {
func pause() {
let pausedTime: CFTimeInterval = self.convertTime(CACurrentMediaTime(), from: nil)
self.speed = 0.0
self.timeOffset = pausedTime
}
func resume() {
let pausedTime: CFTimeInterval = self.timeOffset
self.speed = 1.0
self.timeOffset = 0.0
self.beginTime = 0.0
let timeSincePause: CFTimeInterval = self.convertTime(CACurrentMediaTime(), from: nil) - pausedTime
self.beginTime = timeSincePause
}
}
你的SpinningCircleView會是這樣的
class SpinningCircleView: UIView {
private lazy var spinningCircle = CAShapeLayer()
private var didStopAnimation = false
override init (frame: CGRect)
{
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder)
{
fatalError("init(coder:) has not been implemented")
}
private func configure()
{
didStopAnimation = false
frame = CGRect(x: 0, y: 0, width: 100, height: 100)
let rect = self.bounds
let circularPath = UIBezierPath(ovalIn: rect)
spinningCircle.path = circularPath.cgPath
spinningCircle.fillColor = UIColor.clear.cgColor
spinningCircle.strokeColor = UIColor.systemRed.cgColor
spinningCircle.lineWidth = 10
spinningCircle.strokeEnd = 0.25
spinningCircle.lineCap = .round
self.layer.addSublayer(spinningCircle)
self.addNotification()
}
private func addNotification() {
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(appMovedToBackground), name: UIApplication.willResignActiveNotification, object: nil)
notificationCenter.addObserver(self, selector: #selector(appEnterForeground), name: UIApplication.didBecomeActiveNotification, object: nil)
}
@objc func appEnterForeground() {
if didStopAnimation {
return
}
self.layer.resume()
}
@objc func appMovedToBackground() {
if didStopAnimation {
return
}
self.layer.pause()
}
func startSpinningCircleAnimation() {
if didStopAnimation {
return
}
func startSpinningCircleAnimation() {
if didStopAnimation {
return
}
let rotation : CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
rotation.toValue = Double.pi * 2
rotation.duration = 1
rotation.isCumulative = true
rotation.repeatCount = .greatestFiniteMagnitude
rotation.isRemovedOnCompletion = false
self.layer.add(rotation, forKey: "rotateInfinityAnimation")
}
func stopSpinningCircleAnimation() {
didStopAnimation = true
self.layer.removeAllAnimations()
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/528339.html
標籤:IOS迅速递归卡基本动画uiviewpropertyanimator
上一篇:將n維陣列的值顯示為字串
下一篇:如何按特定深度訪問物件條目?
