Bindable Property не обновляет представление на 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 Renderer (я думаю, проблема здесь)

[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 сразу после того, как я изменю цвет с помощью команды.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
384
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Если вы меняете свойство, а представление не обновляется, проблема должна быть в 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 });
        }


    }

Другие вопросы по теме