Xamarin.Tip – iOS CardView

We’ve talked here before plenty of times on ways we can bring Material Design to iOS, but mostly in the context of Xamarin.Forms. In this quick Xamarin.Tip, we will look at a super simple control to create a CardView in Xamarin.iOS – not Xamarin.Forms.

If you’re looking for the Xamarin.Forms implementation of this concept, check out this post: Xamarin.Tips – Making Your iOS Frame Shadows More Material

I may end up making a quick NuGet package for this control, even just to save me some time with implementing it over and over again, but don’t hold me to it.

Make Your Own CardView

The goal here is to create a view that fills the need of the Material Design CardView spec, and make it customizable within the storyboard editor (oh yeah, storyboard support is awesome).

This can all be achieved with a super simple class:

CardView.cs

    /// <summary>
    /// A simple view with child views to have a set shadow in design time.
    /// This can be imported into a storyboard or xib with these design time variables
    /// </summary>
    [Register("CardView"), DesignTimeVisible(true)]
    public class CardView : UIView
    {
        private float cornerRadius;
        private UIColor shadowColor;
        private int shadowOffsetHeight;
        private int shadowOffsetWidth;
        private float shadowOpacity;

        [DisplayName("Shadow Opacity"), Export("shadowOpacity"), Browsable(true)]
        public float ShadowOpacity
        {
            get { return shadowOpacity; }
            set
            {
                shadowOpacity = value;
                SetNeedsDisplay();
            }
        }

        [DisplayName("Shadow Offset Width"), Export("shadowOffsetWidth"), Browsable(true)]
        public int ShadowOffsetWidth
        {
            get { return shadowOffsetWidth; }
            set
            {
                shadowOffsetWidth = value;
                SetNeedsDisplay();
            }
        }

        [DisplayName("Shadow Offset Height"), Export("shadowOffsetHeight"), Browsable(true)]
        public int ShadowOffsetHeight
        {
            get { return shadowOffsetHeight; }
            set
            {
                shadowOffsetHeight = value;
                SetNeedsDisplay();
            }
        }

        [DisplayName("Corner Radius"), Export("CornerRadius"), Browsable(true)]
        public float CornerRadius
        {
            get { return cornerRadius; }
            set
            {
                cornerRadius = value;
                SetNeedsDisplay();
            }
        }

        [DisplayName("Shadow Color"), Export("ShadowColor"), Browsable(true)]
        public UIColor ShadowColor
        {
            get { return shadowColor; }
            set
            {
                shadowColor = value;
                SetNeedsDisplay();
            }
        }

        public CardView()
        {
            Initialize();
        }

        public CardView(NSCoder coder) : base(coder)
        {
            Initialize();
        }

        protected CardView(NSObjectFlag t) : base(t)
        {
            Initialize();
        }

        protected internal CardView(IntPtr handle) : base(handle)
        {
        }

        public CardView(CGRect frame) : base(frame)
        {
            Initialize();
        }


        public override void AwakeFromNib()
        {
            base.AwakeFromNib();
            Initialize();
        }

        private void Initialize()
        {
            ShadowColor = UIColor.Gray;
            CornerRadius = 0;
            ShadowOffsetHeight = 1;
            ShadowOffsetWidth = 0;
            ShadowOpacity = 0.2f;
            SetupCard();
        }

        private void SetupCard()
        {
            Layer.CornerRadius = CornerRadius;
            UIBezierPath bezierPath = UIBezierPath.FromRoundedRect(Bounds, CornerRadius);
            Layer.MasksToBounds = false;
            Layer.ShadowColor = ShadowColor.CGColor;
            Layer.ShadowOffset = new CGSize(shadowOffsetWidth, shadowOffsetHeight);
            Layer.ShadowOpacity = shadowOpacity;
            Layer.ShadowPath = bezierPath.CGPath;
        }

        public override void LayoutSubviews()
        {
            base.LayoutSubviews();
            SetupCard();
        }
    }

What’s going on here is that we register properties such as the CornerRadius and all the shadow properties, and also register the CardView class itself with decorator attributes DesignTimeVisible, Register, Export, and DisplayName.

So now, all we have to do is go to our storyboard, and change the class of a UIView to our CardView, then we will see these properties as options to edit.

You can now use the storyboard editor to add subview to the CardView and create a UI that is simply awesome, like this!
Screen Shot 2018-03-23 at 2.03.00 PM


If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

One thought on “Xamarin.Tip – iOS CardView”

Leave a comment