From 81547ef45090b65027de8deff72e89aee6ff982f Mon Sep 17 00:00:00 2001 From: Carl de Billy Date: Tue, 5 Oct 2021 12:03:39 -0400 Subject: [PATCH] fix(animation): Fixed a problematic case where a native animation can be 'lost' if the GC is run during the duration of the animation, preventing the animation from being removed and leaving the UI in an undetermined undesired state. --- .../UI/Composition/CoreAnimation.iOSmacOS.cs | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/Uno.UWP/UI/Composition/CoreAnimation.iOSmacOS.cs b/src/Uno.UWP/UI/Composition/CoreAnimation.iOSmacOS.cs index 7101bf2cb1bc..5b423c2b797d 100644 --- a/src/Uno.UWP/UI/Composition/CoreAnimation.iOSmacOS.cs +++ b/src/Uno.UWP/UI/Composition/CoreAnimation.iOSmacOS.cs @@ -23,7 +23,7 @@ internal class UnoCoreAnimation : IDisposable private static int _id = 0; - private readonly WeakReference _layer; + private readonly CALayer _layer; private readonly string _property; private readonly string _key; private readonly float _from; @@ -74,7 +74,7 @@ public UnoCoreAnimation( Action? prepare = null, Action? cleanup = null) { - _layer = new WeakReference(layer); + _layer = layer; _property = property; _key = property + Interlocked.Increment(ref _id).ToStringInvariant(); _from = from; @@ -133,15 +133,16 @@ public void Resume() public void Cancel() { + if (this.Log().IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + this.Log().DebugFormat("CoreAnimation '{0}': animation cancelled (.Cancel).", _key); + } StopAnimation(StopReason.Canceled); } private void StartAnimation(float from, float to, float delayMilliseconds, float durationMilliseconds) { - if (!_layer.TryGetTarget(out var layer)) - { - return; - } + var layer = _layer; if (this.Log().IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) { @@ -191,11 +192,17 @@ private void StartAnimation(float from, float to, float delayMilliseconds, float private void StopAnimation(StopReason reason, long? time = default, float? value = default) { - if (_current.animation == null || !_layer.TryGetTarget(out var layer)) + if (_current.animation == null) { + if (this.Log().IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + this.Log().DebugFormat("CoreAnimation '{0}' unable to remove native animation: no running animation. Already disposed?", _key); + } return; } + var layer = _layer; + if (this.Log().IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) { this.Log().DebugFormat("CoreAnimation '{0}' is forcefully stopping.", _key); @@ -259,6 +266,10 @@ void Handler(object? sender, CAAnimationStateEventArgs args) var (currentAnim, from, to) = _current; if (currentAnim != animation) { + if (this.Log().IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + { + this.Log().DebugFormat("CoreAnimation '{0}' [{1}]: unable to {2} because another animation is running.", _key, _property, _stop.reason); + } return; // We are no longer the current animation, do not interfere with the current } @@ -266,11 +277,11 @@ void Handler(object? sender, CAAnimationStateEventArgs args) if (this.Log().IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) { - this.Log().DebugFormat("CoreAnimation on property {0} has been {1}.", _property, _stop.reason); + this.Log().DebugFormat("CoreAnimation {0} has been {1}.", _key, _stop.reason); } // First commit the expected final (end, current or initial) value. - if (_layer.TryGetTarget(out var layer)) + var layer = _layer; { var keyPath = new NSString(_property); NSObject finalValue; @@ -303,7 +314,7 @@ void Handler(object? sender, CAAnimationStateEventArgs args) if (this.Log().IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) { - this.Log().Debug($"CoreAnimation Stopped (reason: {_stop.reason}, Finished:{args.Finished})"); + this.Log().DebugFormat("CoreAnimation {0} [{1}] Stopped (reason: {2}, Finished:{3})", _key, _property, _stop.reason, args.Finished); } // Finally raise callbacks