Bindable 属性 未更新 iOS 上的视图

Bindable Property is not updating view on iOS

我正在 xamarin.forms 上创建跨平台应用程序。我需要一个带有渐变背景的自定义渲染器,我已经创建了它,它在 android 和 ios 上都运行良好。 然而,当我想改变渐变背景的颜色时,它在 iOS.

上不起作用

Test.xaml.cs

namespace KiaiDay
{
    public class TesteViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private Color _startColor = Color.Green;
        public Color SStartColor
        {
            get => _startColor;
            set { _startColor = value; OnPropertyChanged(nameof(SStartColor)); }
        }

        private Color _endColor = Color.Blue;
        public Color EEndColor
        {
            get => _endColor;
            set { _endColor = value; OnPropertyChanged(nameof(EEndColor)); }
        }

        public ICommand Select
        {
            get => new Command(() =>
            {
                SStartColor = Color.Red;
                EEndColor = Color.Brown;
            });
        }

        #region INotifyPropertyChanged Implementation
        void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            if (PropertyChanged == null)
                return;

            PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion
    }

    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class teste : ContentPage
    {
        public teste()
        {
            BindingContext = new TesteViewModel();
            InitializeComponent();
        }

    }
}

Test.xaml

    <ContentPage.Content>
        <StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
            <local:ShadowFrame StartColor="{Binding SStartColor}" EndColor="{Binding EEndColor}" HorizontalOptions="Center" VerticalOptions="Center">
                <Label Text="Teste"/>
                <local:ShadowFrame.GestureRecognizers>
                    <TapGestureRecognizer NumberOfTapsRequired="1" Command="{Binding Select}"/>
                </local:ShadowFrame.GestureRecognizers>
            </local:ShadowFrame>
        </StackLayout>
    </ContentPage.Content>

PCL

中的自定义渲染器
public class ShadowFrame : Frame
    {
        public static BindableProperty ElevationProperty = BindableProperty.Create(nameof(Elevation), typeof(float), typeof(ShadowFrame), default(float));
        public static readonly BindableProperty StartColorProperty = BindableProperty.Create(nameof(StartColor), typeof(Color), typeof(ShadowFrame), default(Color));
        public static readonly BindableProperty EndColorProperty = BindableProperty.Create(nameof(EndColor), typeof(Color), typeof(ShadowFrame), default(Color));

        public float Elevation
        {
            get { return (float)GetValue(ElevationProperty); }
            set { SetValue(ElevationProperty, value); }
        }

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

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

iOS渲染器(我觉得问题出在这里)

[assembly: ExportRenderer(typeof(ShadowFrame), typeof(ShadowFrameRenderer))]
namespace KiaiDay.iOS.Renderers
{
    public class ShadowFrameRenderer : FrameRenderer
    {
        CAGradientLayer gradientLayer;
        private Color StartColor { get; set; }
        private Color EndColor { get; set; }
        CGRect rect;

        public static void Initialize()
        {

            // empty, but used for beating the linker
        }

        public ShadowFrameRenderer() => gradientLayer = new CAGradientLayer();

        public override void Draw(CGRect rect)
        {
            this.rect = rect;
            var stack = (ShadowFrame)this.Element;
            StartColor = stack.StartColor;
            EndColor = stack.EndColor;
            CGColor startColor = this.StartColor.ToCGColor();
            CGColor endColor = this.EndColor.ToCGColor();
            #region for Vertical Gradient  
            //var gradientLayer = new CAGradientLayer();     
            #endregion
            #region for Horizontal Gradient  
            //var gradientLayer = new CAGradientLayer()
            //{
            //    StartPoint = new CGPoint(0, 0.5),
            //    EndPoint = new CGPoint(1, 0.5)
            //};
            #endregion
            gradientLayer.Frame = rect;
            gradientLayer.Colors = new CGColor[] {
                startColor,
                endColor
            };

            NativeView.Layer.InsertSublayer(gradientLayer, 0);
            base.Draw(rect);
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
        {
            base.OnElementChanged(e);
            var stack = (ShadowFrame)e.NewElement;

            if (e.OldElement != null || Element == null)
            {
                return;
            }

            try
            {
                this.StartColor = stack.StartColor;
                this.EndColor = stack.EndColor;
                UpdateShadow();
                Draw(rect);
            }

            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("ERROR:", ex.Message);
            }
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            var stack = (ShadowFrame) sender;

            if (e.PropertyName == ShadowFrame.ElevationProperty.PropertyName)
            {
                UpdateShadow();
            }
            if (e.PropertyName == ShadowFrame.StartColorProperty.PropertyName)
            {
                this.StartColor = stack.StartColor;
            }
            if (e.PropertyName == ShadowFrame.EndColorProperty.PropertyName)
            {
                this.EndColor = stack.EndColor;
            }
        }


        private void UpdateShadow()
        {

            var shadowFrame = (ShadowFrame)Element;

            // Update shadow to match better material design standards of elevation
            Layer.ShadowRadius = shadowFrame.Elevation;
            Layer.ShadowColor = UIColor.Gray.CGColor;
            Layer.ShadowOffset = new CGSize(2, 2);
            Layer.ShadowOpacity = 0.80f;
            Layer.ShadowPath = UIBezierPath.FromRect(Layer.Bounds).CGPath;
            Layer.MasksToBounds = false;
        }
    }
}

我想在通过命令更改颜色后立即更新iOS视图。

如果您更改 属性 而视图未更新,则问题必须在 OnElementPropertyChanged

您需要调用 UpdateShadow()Draw()

经过大量挖掘,我想我找到了解决方案:

iOS 自定义渲染器:

public class GradientStackColorRenderer : VisualElementRenderer<Frame>
    {
        private Color StartColor { get; set; }
        private Color EndColor { get; set; }

        public override CGRect Frame
        {
            get
            {
                return base.Frame;
            }
            set
            {
                if (value.Width > 0 && value.Height > 0)
                {
                    foreach (var layer in NativeView?.Layer.Sublayers.Where(layer => layer is CAGradientLayer))
                        layer.Frame = new CGRect(0, 0, value.Width, value.Height);
                }
                base.Frame = value;
            }
        }


        protected override void OnElementChanged(ElementChangedEventArgs<StackLayout> e)
        {
            base.OnElementChanged(e);
            if (e.OldElement == null)
            {
                try
                {
                    var stack = e.NewElement as GradientColorStack;
                    this.StartColor = stack.StartColor;
                    this.EndColor = stack.EndColor;
                    AdicionarGradiente();
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine(@"ERROR:", ex.Message);
                }
            }
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            var stack = this.Element as GradientColorStack;

            if(e.PropertyName == GradientColorStack.StartColorProperty.PropertyName)
            {
                this.StartColor = stack.StartColor;
                AdicionarGradiente();
            }

            if (e.PropertyName == GradientColorStack.EndColorProperty.PropertyName)
            {
                this.EndColor = stack.EndColor;
                AdicionarGradiente();
            }


        }

        public void AdicionarGradiente()
        {
            var gradient = new CAGradientLayer();
            gradient.CornerRadius = NativeView.Layer.CornerRadius = 5;
            gradient.Colors = new CGColor[] { StartColor.ToCGColor(), EndColor.ToCGColor() };
            var layer = NativeView?.Layer.Sublayers.LastOrDefault();
            NativeView?.Layer.InsertSublayerBelow(gradient, layer);
        }

        public static CGColor ToCGColor(Color color)
        {
            return new CGColor(CGColorSpace.CreateSrgb(), new nfloat[] { (float)color.R, (float)color.G, (float)color.B, (float)color.A });
        }


    }