Bindable Property не обновляет представление на iOS - PullRequest
0 голосов
/ 05 июля 2019

Я создаю кроссплатформенное приложение для 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 сразу после изменения цвета черезкоманда.

Ответы [ 2 ]

0 голосов
/ 08 июля 2019

После долгих раскопок я думаю, что нашел решение:

iOS Custom Renderer:

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 });
        }


    }
0 голосов
/ 05 июля 2019

Если вы изменили свойство, а представление не обновилось, проблема должна быть в OnElementPropertyChanged.

Вам необходимо позвонить UpdateShadow() и Draw().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...