药丸形 UIButton 摆动动画的奇怪行为

Strange behaviour with a wiggle animation of a pill shaped UIButton

我的视图有一个 UIButton,它通过将角半径设置为视图高度的一半而呈丸状。一切都很好,但是当它处于动画状态时(通过旋转摆动),角半径似乎发生了变化,使药丸形状变形。当动画结束时全部returns转为正常:

正常外观:

动画外观(见角半径):

这里显示了一段录音:

https://www.dropbox.com/s/xoowpfzdf0ijuql/Simulator%20Screen%20Recording%20-%20iPhone%2012%20-%202021-10-08%20at%2015.05.14.mp4?dl=0

动画代码 (Xamarin c#) 如下所示:

public static void Wiggle(UIView view, double duration = 0.1f, float repeatCount = 3.0f)
{
    UIView.Animate(duration,
        () => view.Transform = CGAffineTransform.MakeRotation((nfloat)(-5f * Math.PI / 180f)),
        () =>
        {
            UIView.AnimateNotify(duration, 0.0f, UIViewAnimationOptions.Repeat | UIViewAnimationOptions.Autoreverse | UIViewAnimationOptions.AllowUserInteraction,
                () =>
                {
                    UIView.SetAnimationRepeatCount(repeatCount);
                    view.Transform = CGAffineTransform.MakeRotation((nfloat)(5f * Math.PI / 180f));
                },
                (animationFinished) =>
                {
                    UIView.Animate(duration, 0, UIViewAnimationOptions.AllowUserInteraction,
                        () => { view.Transform = CGAffineTransform.MakeIdentity(); }, null);
                });
        });
}

正如视频中所见,并非总是如此。有时药丸的形状仍然存在。它发生在模拟器和真实设备上。有什么线索吗?谢谢

您没有显示“PillShape”代码,但我假设您在 “将角半径设置为视图高度的一半”LayoutSubviews()

如果是这样,问题可能是当您旋转 按钮时 它的高度在变化,您不再有正确的半径。

您最好使用图层动画,而不是旋转视图本身。

我不使用 Xamarin/C#,所以这是基于 Objective-C 代码...希望它能帮助您。

这使用了慢速(0.5 秒)的动画,可以很容易地看出半径在旋转过程中没有变化。点击按钮 start/stop 动画:

using Foundation;
using System;
using UIKit;
using CoreAnimation;
using ObjCRuntime;

namespace QuickTestApp
{
    public partial class ViewController : UIViewController
    {
        public WigglePillButton btn { get; private set; }
        public bool isWiggling { get; private set; }

        public ViewController(IntPtr handle) : base(handle)
        {
        }

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();
            // Perform any additional setup after loading the view, typically from a nib.

            btn = new WigglePillButton();
            btn.TranslatesAutoresizingMaskIntoConstraints = false;

            btn.SetTitle("Testing", UIControlState.Normal);
            btn.BackgroundColor = UIColor.SystemBlueColor;

            View.AddSubview(btn);

            // Get the parent view's layout
            var margins = View.SafeAreaLayoutGuide;

            btn.CenterXAnchor.ConstraintEqualTo(margins.CenterXAnchor).Active = true;
            btn.CenterYAnchor.ConstraintEqualTo(margins.CenterYAnchor).Active = true;
            btn.WidthAnchor.ConstraintEqualTo(120).Active = true;

            btn.AddTarget(this, new Selector("ButtonClickAction:"), UIControlEvent.TouchUpInside);

            isWiggling = false;
        }

        [Export("ButtonClickAction:")]
        public void ButtonClickAction(UIButton sender)
        {
            if (isWiggling)
            {
                btn.StopAnim();
            } else {
                btn.StartAnim();
            }
            isWiggling = !isWiggling;
        }

        public override void DidReceiveMemoryWarning()
        {
            base.DidReceiveMemoryWarning();
            // Release any cached data, images, etc that aren't in use.
        }
    }

    public class WigglePillButton : UIButton
    {
        public override void LayoutSubviews()
        {
            base.LayoutSubviews();
            var maskingShapeLayer = new CAShapeLayer()
            {
                Path = UIBezierPath.FromRoundedRect(Bounds, 12).CGPath
            };
            Layer.Mask = maskingShapeLayer;
        }

        public void StartAnim()
        {
            AddAnimations();

        }

        public void StopAnim()
        {
            Layer.RemoveAllAnimations();

        }

        private void AddAnimations()
        {
            CATransaction.Begin();

            Layer.AddAnimation(RotAnim(), "rot");

            CATransaction.Commit();

        }

        private CAKeyFrameAnimation RotAnim()
        {
            CAKeyFrameAnimation animation = CAKeyFrameAnimation.FromKeyPath("transform.rotation.z");
            double angle = 5f * Math.PI / 180f;
            animation.Values = new[] { NSNumber.FromDouble(angle), NSNumber.FromDouble(-angle) };
            animation.AutoReverses = true;
            animation.Duration = 0.5;
            animation.RepeatCount = float.PositiveInfinity;
            return animation;
        }

    }
}