Implement The UI Touch Effects In Xamarin Forms

Introduction

In this article, I will explain UI Effects in xamarin forms. We can implement UI Effects for Android and iOS, and the plugin will create a custom renderer for each platform, and the effect will help you to animate touches with event handler. We can customize designs for all UIs using the support command and toucheffect. To view with borders, corner radius and clipping to bounds, and view based on content view, you can specify child through content property.

Important & Known Issues:

  • UI Effect does not work in views with another gestures and effect like button, Picker, Entry, Slider.
  • UI Effect does not work correctly with standard gestures.
  • If the UI effect doesn't work, try to wrap the view with Content View and add the effect to the wrapper.

Nuget Packages Installation

Install-Package XamEffects.

Step 1

Create a new XAML file and follow the code below to help you to define the xamls for fetching packages from nuget references.

xmlns:xe="clr-namespace:XamEffects;assembly=XamEffects"

Step 2

To create a UI border view with a label for performing the UI effect, follow the below code to better understand. Make sure to add the line xe:TouchEffect.Color.

<xe:BorderView
          HeightRequest="120"
          WidthRequest="200"
          HorizontalOptions="Center"
          VerticalOptions="Center"
          BackgroundColor="LightGray"
          CornerRadius="15"
          BorderColor="Green"
          BorderWidth="2"
          xe:TouchEffect.Color="Red"
          x:Name="touch">
        <Label x:Name="text"
               HorizontalOptions="Center"
               VerticalOptions="Center"
               Text="Click Me..!!!"
               TextColor="Black"/>
    </xe:BorderView>

Step 3

You need to create a new class file for exporting the effect in iOS. We can call it file as TouchEffectPlatform. Follow the code below to achieve the Effect in iOS.

using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using CoreGraphics;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using XamEffects;
using XamEffects.iOS;
using XamEffects.iOS.GestureCollectors;
using XamEffects.iOS.GestureRecognizers;
using System;
using System.Linq;
using Foundation;
[assembly: ExportEffect(typeof(TouchEffectPlatform), nameof(TouchEffect))]
namespace XamarinEffects.iOS {
    public class TouchEffectPlatform: PlatformEffect {
        public bool IsDisposed => (Container as IVisualElementRenderer)?.Element == null;
        public UIView View => Control ?? Container;
        UIView _layer;
        nfloat _alpha;
        protected override void OnAttached() {
            View.UserInteractionEnabled = true;
            _layer = new UIView {
                UserInteractionEnabled = false,
                    Opaque = false,
                    Alpha = 0,
                    TranslatesAutoresizingMaskIntoConstraints = false
            };
            UpdateEffectColor();
            TouchGestureCollector.Add(View, OnTouch);
            View.AddSubview(_layer);
            View.BringSubviewToFront(_layer);
            _layer.TopAnchor.ConstraintEqualTo(View.TopAnchor).Active = true;
            _layer.LeftAnchor.ConstraintEqualTo(View.LeftAnchor).Active = true;
            _layer.BottomAnchor.ConstraintEqualTo(View.BottomAnchor).Active = true;
            _layer.RightAnchor.ConstraintEqualTo(View.RightAnchor).Active = true;
        }
        protected override void OnDetached() {
            TouchGestureCollector.Delete(View, OnTouch);
            _layer?.RemoveFromSuperview();
            _layer?.Dispose();
        }
        void OnTouch(TouchGestureRecognizer.TouchArgs e) {
            switch (e.State) {
                case TouchGestureRecognizer.TouchState.Started:
                    BringLayer();
                    break;
                case TouchGestureRecognizer.TouchState.Ended:
                    EndAnimation();
                    break;
                case TouchGestureRecognizer.TouchState.Cancelled:
                    if (!IsDisposed && _layer != null) {
                        _layer.Layer.RemoveAllAnimations();
                        _layer.Alpha = 0;
                    }
                    break;
            }
        }
        protected override void OnElementPropertyChanged(PropertyChangedEventArgs e) {
            base.OnElementPropertyChanged(e);
            if (e.PropertyName == TouchEffect.ColorProperty.PropertyName) {
                UpdateEffectColor();
            }
        }
        void UpdateEffectColor() {
            var color = TouchEffect.GetColor(Element);
            if (color == Color.Default) {
                return;
            }
            _alpha = color.A < 1.0 ? 1 : (nfloat) 0.3;
            _layer.BackgroundColor = color.ToUIColor();
        }
        void BringLayer() {
            _layer.Layer.RemoveAllAnimations();
            _layer.Alpha = _alpha;
            View.BringSubviewToFront(_layer);
        }
        void EndAnimation() {
            if (!IsDisposed && _layer != null) {
                _layer.Layer.RemoveAllAnimations();
                UIView.Animate(0.225,
                    () => {
                        _layer.Alpha = 0;
                    });
            }
        }
        public static void Init() {}
    }
}

Step 4

You need to create a new class file for collecting the effect attribute details and for getting the values. We can call it file TouchGestureCollector. Follow the code below to achieve the Gesture in iOS.

using System;
using System.Collections.Generic;
using System.Linq;
using UIKit;
using XamEffects.iOS.GestureRecognizers;
namespace XamarinEffects.iOS {
    internal static class TouchGestureCollector {
        static Dictionary < UIView, GestureActionsContainer > Collection {
            get;
        } = new Dictionary < UIView, GestureActionsContainer > ();
        public static void Add(UIView view, Action < TouchGestureRecognizer.TouchArgs > action) {
            if (Collection.ContainsKey(view)) {
                Collection[view].Actions.Add(action);
            } else {
                var gest = new TouchGestureRecognizer {
                    CancelsTouchesInView = false,
                        Delegate = new TouchGestureRecognizerDelegate(view)
                };
                gest.OnTouch += ActionActivator;
                Collection.Add(view, new GestureActionsContainer {
                    Recognizer = gest,
                        Actions = new List < Action < TouchGestureRecognizer.TouchArgs >> {
                            action
                        }
                });
                view.AddGestureRecognizer(gest);
            }
        }
        public static void Delete(UIView view, Action < TouchGestureRecognizer.TouchArgs > action) {
            if (!Collection.ContainsKey(view)) return;
            var ci = Collection[view];
            ci.Actions.Remove(action);
            if (ci.Actions.Count != 0) return;
            view.RemoveGestureRecognizer(ci.Recognizer);
            Collection.Remove(view);
        }
        static void ActionActivator(object sender, TouchGestureRecognizer.TouchArgs e) {
            var gest = (TouchGestureRecognizer) sender;
            if (!Collection.ContainsKey(gest.View)) return;
            var actions = Collection[gest.View].Actions.ToArray();
            foreach(var valueAction in actions) {
                valueAction?.Invoke(e);
            }
        }
        class GestureActionsContainer {
            public TouchGestureRecognizer Recognizer {
                get;
                set;
            }
            public List < Action < TouchGestureRecognizer.TouchArgs >> Actions {
                get;
                set;
            }
        }
    }
}

Output

Conclusion

Hopefully, this article has given you sufficient information to create a UI Effect control for both Android and iOS. Feel free to leave a comment if you would like me to further elaborate on anything within this article.