iOS Renderer - Custom BindableProperty not updated in view after fist value

MatteoPiccioniMatteoPiccioni USMember ✭✭
edited June 17 in Xamarin.Forms

Hi,
looking into stackoverflow I will be able to find and adapt a way to write a gradiant background using Renderers.
Into Android I have no problems, but in iOS if I try to change the colors runtime, nothing change...as the BindableProperty are not working.

Here my code:

GradientLayout
using System;
using Xamarin.Forms;

namespace BugSlider.Renderers
{
    public class GradientLayout : StackLayout
    {
        public static BindableProperty StartColorProperty = BindableProperty.Create(nameof(StartColor), typeof(Color), typeof(GradientLayout), default(Color), defaultBindingMode: BindingMode.OneWay);

        public Color StartColor
        {
            get { return (Color)GetValue(StartColorProperty); }
            set { SetValue(StartColorProperty, value); }
        }


        public static BindableProperty EndColorProperty = BindableProperty.Create(nameof(EndColor), typeof(Color), typeof(GradientLayout), default(Color), defaultBindingMode: BindingMode.OneWay);

        public Color EndColor
        {
            get { return (Color)GetValue(EndColorProperty); }
            set { SetValue(EndColorProperty, value); }
        }

        public GradientColorStackMode Mode { get; set; }
    }

    public enum GradientColorStackMode
    {
        ToRight,
        ToLeft,
        ToTop,
        ToBottom,
        ToTopLeft,
        ToTopRight,
        ToBottomLeft,
        ToBottomRight
    }
}

iOS Renderer:
using System;
using CoreAnimation;
using CoreGraphics;
using BugSlider.iOS.Renderers;
using BugSlider.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using System.ComponentModel;
using System.Linq;

[assembly: ExportRenderer(typeof(GradientLayout), typeof(GradientLayoutRenderer))]
namespace BugSlider.iOS.Renderers
{
    public class GradientLayoutRenderer : VisualElementRenderer<StackLayout>
    {

        public override void Draw(CGRect rect)
        {
            base.Draw(rect);

            GradientLayout stack = (GradientLayout)this.Element;
            CGColor startColor = stack.StartColor.ToCGColor();

            CGColor endColor = stack.EndColor.ToCGColor();

            var gradientLayer = new CAGradientLayer();

            switch (stack.Mode)
            {
                default:
                case GradientColorStackMode.ToRight:
                    gradientLayer.StartPoint = new CGPoint(0, 0.5);
                    gradientLayer.EndPoint = new CGPoint(1, 0.5);
                    break;
                case GradientColorStackMode.ToLeft:
                    gradientLayer.StartPoint = new CGPoint(1, 0.5);
                    gradientLayer.EndPoint = new CGPoint(0, 0.5);
                    break;
                case GradientColorStackMode.ToTop:
                    gradientLayer.StartPoint = new CGPoint(0.5, 0);
                    gradientLayer.EndPoint = new CGPoint(0.5, 1);
                    break;
                case GradientColorStackMode.ToBottom:
                    gradientLayer.StartPoint = new CGPoint(0.5, 1);
                    gradientLayer.EndPoint = new CGPoint(0.5, 0);
                    break;
                case GradientColorStackMode.ToTopLeft:
                    gradientLayer.StartPoint = new CGPoint(1, 0);
                    gradientLayer.EndPoint = new CGPoint(0, 1);
                    break;
                case GradientColorStackMode.ToTopRight:
                    gradientLayer.StartPoint = new CGPoint(0, 1);
                    gradientLayer.EndPoint = new CGPoint(1, 0);
                    break;
                case GradientColorStackMode.ToBottomLeft:
                    gradientLayer.StartPoint = new CGPoint(1, 1);
                    gradientLayer.EndPoint = new CGPoint(0, 0);
                    break;
                case GradientColorStackMode.ToBottomRight:
                    gradientLayer.StartPoint = new CGPoint(0, 0);
                    gradientLayer.EndPoint = new CGPoint(1, 1);
                    break;
            }

            gradientLayer.Frame = rect;
            gradientLayer.Colors = new CGColor[] { startColor, endColor };

            NativeView.Layer.InsertSublayer(gradientLayer, 0);

        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if ((string.Equals(e.PropertyName, "StartColor", StringComparison.CurrentCultureIgnoreCase)) ||
                    (string.Equals(e.PropertyName, "EndColor", StringComparison.CurrentCultureIgnoreCase)))
            {
                SetNeedsDisplay();
            }
            base.OnElementPropertyChanged(sender, e);
        }

    }
}

XAML (cell in my case)
<?xml version="1.0" encoding="UTF-8"?>

        <renderers:GradientLayout x:Name="asd" StartColor="{Binding StartColor}" EndColor="{Binding EndColor}"
                       Padding="10,10,10,10"
                       IsClippedToBounds="true"
                       Mode="ToTop">  


        <StackLayout Orientation="Vertical">
            <Label x:Name="lbNome"
                   Grid.Column="0"
                   Grid.ColumnSpan="3"
                   Margin="40,0,40,0"
                   FontSize="Medium"
                   HorizontalTextAlignment="Center"
                   Text="{Binding Name}"
                   TextColor="Red" />       

            <Label x:Name="lbStart" Text="StartColor" BackgroundColor="White" TextColor = "{Binding StartColor}" />
            <Label x:Name="lbEnd" Text="EndColor" BackgroundColor="White" TextColor = "{Binding EndColor}" />

            <Button Text="Click me!" Clicked="Handle_Clicked" BackgroundColor="Green" TextColor="White" />

        </StackLayout>
</renderers:GradientLayout>

</ViewCell>

As I said, Android renderer is working right..
Into iOS renderer I try to add a SetNeedsDisplay(); command, but the colors doesnt change

Have you some hint?
Thanks!

Best Answer

  • MatteoPiccioniMatteoPiccioni US ✭✭
    Accepted Answer

    For now I solved modifying the renderer in this way:

            var gradLayer = Layer.Sublayers.Where(x => x.GetType() == typeof(CAGradientLayer)).FirstOrDefault();
            if (gradLayer != null)
                gradLayer.RemoveFromSuperLayer();
    
            NativeView.Layer.InsertSublayer(gradientLayer, 0);
    

Answers

  • MatteoPiccioniMatteoPiccioni USMember ✭✭
    Accepted Answer

    For now I solved modifying the renderer in this way:

            var gradLayer = Layer.Sublayers.Where(x => x.GetType() == typeof(CAGradientLayer)).FirstOrDefault();
            if (gradLayer != null)
                gradLayer.RemoveFromSuperLayer();
    
            NativeView.Layer.InsertSublayer(gradientLayer, 0);
    
Sign In or Register to comment.