I want to create circular pulse animation in xamarin.iOS. I have refer this link: https://www.iostutorialjunction.com/2018/08/create-pulse-animaion-in-swift-tutorial-step-by-step-guide.html

Initialize array CAShapeLayer[] arrLayerShapes = new CAShapeLayer[2];

and created code for same in c#

void createPulse()
    {
        vwShowAnimation.UserInteractionEnabled = false;

        for (int i = 0; i < 2; i++)
        {
            var centerPoint = new PointF((float)0, (float)0);
            var circularPath = UIBezierPath.FromArc(center: centerPoint, radius: UIScreen.MainScreen.Bounds.Size.Width / 2, startAngle: 0, endAngle: 2 * (float)Math.PI, clockwise: true);
            circularPulseLayer = new CAShapeLayer();
            circularPulseLayer.Path = circularPath.CGPath;
            circularPulseLayer.LineWidth = 2.5f;
            circularPulseLayer.FillColor = UIColor.Clear.CGColor;
            circularPulseLayer.StrokeColor = UIColor.FromRGB(36, 229, 186).CGColor;
            circularPulseLayer.Opacity = 0;
            circularPulseLayer.LineCap = CAShapeLayer.CapRound;
            circularPulseLayer.Position = new CGPoint(vwAnimateLayer.Frame.Size.Width / 2, vwAnimateLayer.Frame.Size.Width / 2);
            vwAnimateLayer.Layer.AddSublayer(circularPulseLayer);
            arrLayerShapes.SetValue(circularPulseLayer, i);
        }

        var popTime = new DispatchTime(DispatchTime.Now, (long)(0.2));
        var popTime2 = new DispatchTime(DispatchTime.Now, (long)(0.4));
        var popTime3 = new DispatchTime(DispatchTime.Now, (long)(0.5));

        //animateCircularPulseAt(0);
        NSTimer.CreateScheduledTimer(2.3, true, (obj) =>
        {
            DispatchQueue.MainQueue.DispatchAfter(popTime, () =>
            {
                animateCircularPulseAt(0);
                DispatchQueue.MainQueue.DispatchAfter(popTime2, () =>
                {
                    animateCircularPulseAt(1);
                });
            });

        });
    }

Animate Method

void animateCircularPulseAt(int index)
        {
            arrLayerShapes[index].StrokeColor = UIColor.FromRGB(36, 229, 186).CGColor;
            var scaleAnimation = CABasicAnimation.FromKeyPath("transform.scale.xy");

            scaleAnimation.From = NSNumber.FromFloat(0);
            scaleAnimation.To = NSNumber.FromFloat(0.9f);


            var opacityAnimation = CABasicAnimation.FromKeyPath("opacity");

            opacityAnimation.From = NSNumber.FromFloat(0.9f);
            opacityAnimation.To = NSNumber.FromFloat(0);

            var groupAnimation = new CAAnimationGroup();
            groupAnimation.Animations = new CAAnimation[] { scaleAnimation, opacityAnimation };
            groupAnimation.Duration = 2.3f;
            groupAnimation.RepeatCount = (nint)float.MaxValue;
            groupAnimation.TimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseOut);
            arrLayerShapes[index].AddAnimation(groupAnimation, "groupanimation");
        }

2 Answers

2
Jack Hua - MSFT On Best Solutions

When you use DispatchQueue.MainQueue.DispatchAfter to execute the animation, it's a synchronous function which will block the MainQueue, so you will always see one single layer. Change the code to DispatchAsync would work:

    NSTimer.CreateScheduledTimer(2.3, true, (obj) =>
    {
        DispatchQueue.MainQueue.DispatchAsync(() =>
       {
           animateCircularPulseAt(0);
       });

    });

    NSTimer.CreateScheduledTimer(2.5, true, (obj) =>
    {
        DispatchQueue.MainQueue.DispatchAsync(() =>
        {
            animateCircularPulseAt(1);
        });
    });

I just tested and found this will be better:

    NSTimer.CreateScheduledTimer(2.3, true, async (obj) =>
    {
        await Task.Delay(TimeSpan.FromMilliseconds(200));
        animateCircularPulseAt(0);
        await Task.Delay(TimeSpan.FromMilliseconds(400));
        animateCircularPulseAt(1);         
    });
2
Cheesebaron On

You don't need to use that dispatcher stuff which is shown in the example. You can use regular Tasks and await them:

while(pulse)
{
    await Task.Delay(TimeSpan.FromMilliseconds(200));
    AnimateCircularPulseAt(0);
    await Task.Delay(TimeSpan.FromMilliseconds(400));
    AnimateCircularPulseAt(1);
    await Task.Delay(TimeSpan.FromMilliseconds(500));
    AnimateCircularPulseAt(2);

    await Task.Delay(TimeSpan.FromMilliseconds(500));
}

This keeps pulsing the animation while pulse is true.