【问题标题】:Xamarin.Forms gradient background for Button using Custom Renderer with iOSXamarin.Forms 使用带有 iOS 的自定义渲染器的 Button 渐变背景
【发布时间】:2016-09-07 07:20:15
【问题描述】:

我正在使用 Xamarin.Forms,我希望全局使按钮看起来更好一些。

对于使用自定义渲染器的 Android 版本,我已经很好地实现了这一点,但在 iOS 上我无法做到这一点。

在我的 XAML 页面中定义按钮时,我引用了“CustomButton”而不是“Button”,然后我的 iOS 应用程序中有以下 CustomButtonRenderer。

大多数样式更改都可以正常工作(边框半径等),但我似乎无法让它为按钮呈现背景渐变。

到目前为止,这是我的代码,但背景只显示为白色。我怎样才能让它显示一个渐变,上面有文字?

class CustomButtonRenderer : ButtonRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
    {
        base.OnElementChanged(e);

        if (Control != null)
        {
            var gradient = new CAGradientLayer();
            gradient.Frame = Control.Layer.Bounds;
            gradient.Colors = new CGColor[]
            {
                UIColor.FromRGB(51, 102, 204).CGColor,
                UIColor.FromRGB(51, 102, 204).CGColor
            };

            Control.Layer.AddSublayer(gradient);
            Control.Layer.CornerRadius = 10;
            Control.Layer.BorderColor = UIColor.FromRGB(51, 102, 204).CGColor;
            Control.Layer.BorderWidth = 1;

            Control.VerticalAlignment = UIControlContentVerticalAlignment.Center;
        }
    }
}

【问题讨论】:

  • 给您的个人建议:如果您在特定平台上遇到困难,请搜索该平台以及您想要做什么。由于 api 非常相似,您通常会轻松找到您的解决方案 :)

标签: xamarin xamarin.ios xamarin.forms


【解决方案1】:

1st) 不要使用AddSublayer,使用InsertSublayerBelow,这样Z 顺序就会正确,并且您的Title 文本会在顶部。

2nd) 覆盖 LayoutSubviews 并更新您的 CAGradientLayer 框架以匹配您的 UIButton

3rd) 享受渐变效果:

完整示例:

[assembly: ExportRenderer(typeof(CustomButton), typeof(CustomButtonRenderer))]
namespace AppCompatRender.iOS
{
    public class CustomButtonRenderer : Xamarin.Forms.Platform.iOS.ButtonRenderer
    {
        public override void LayoutSubviews()
        {
            foreach (var layer in Control?.Layer.Sublayers.Where(layer => layer is CAGradientLayer))
                layer.Frame = Control.Bounds;
            base.LayoutSubviews();
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
        {
            base.OnElementChanged(e);

            if (e.OldElement == null)
            {
                var gradient = new CAGradientLayer();
                gradient.CornerRadius = Control.Layer.CornerRadius = 10;
                gradient.Colors = new CGColor[]
                {
                    UIColor.FromRGB(51, 102, 104).CGColor,
                    UIColor.FromRGB(51, 202, 204).CGColor
                };
                var layer = Control?.Layer.Sublayers.LastOrDefault();
                Control?.Layer.InsertSublayerBelow(gradient, layer);
            }
        }

    }
}

更新:

如果您使用 iOS 10+ 更新版本的Xamarin.Forms,则在调用LayoutSubViews 期间的Control.Bounds 将全部为零。您需要在设置控件的Frame 属性期间设置渐变层Frame 的大小,即:

public class CustomButtonRenderer : Xamarin.Forms.Platform.iOS.ButtonRenderer
{
    public override CGRect Frame
    {
        get
        {
            return base.Frame;
        }
        set
        {
            if (value.Width > 0 && value.Height > 0)
            {
                foreach (var layer in Control?.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<Button> e)
    {
        base.OnElementChanged(e);

        if (e.OldElement == null)
        {
            var gradient = new CAGradientLayer();
            gradient.CornerRadius = Control.Layer.CornerRadius = 20;
            gradient.Colors = new CGColor[]
            {
                UIColor.FromRGB(51, 102, 104).CGColor,
                UIColor.FromRGB(51, 202, 204).CGColor
            };
            var layer = Control?.Layer.Sublayers.LastOrDefault();
            Control?.Layer.InsertSublayerBelow(gradient, layer);
        }
    }
}

【讨论】:

  • 很棒的解决方案——正是我所需要的。非常感谢。
  • 按钮的背景中没有显示任何内容,只是带有白色背景的文本
  • @Sheagorath 我更新了代码以反映对Xamarin.FormsiOS 10+ 的更改,请参阅我添加的Update 部分。
  • 当我同时使用两个版本的 iOS(即 iOS
  • @Noop 您需要对其进行调试以确定 sigsegv 的来源,您还可以根据 iOS 运行时版本有条件地测试和执行代码,if (UIDevice.CurrentDevice.CheckSystemVersion (10,0)) { ..... } :developer.xamarin.com/recipes/ios/general/projects/…
【解决方案2】:

在调用 OnElementChanged 的​​那一刻,Control.Layer.Bounds 完全归零。在您的渲染中,您需要添加方法来更新渐变的框架以匹配 Control.Layer 的框架。

【讨论】:

    【解决方案3】:

    响应 Robert Cafazzo 的评论,我可以帮助稍微调整此渲染以使其正常工作:

    public class GdyBtnRendererIos : ButtonRenderer
        {
            #region Colors
            static Color rosecolor = (Color)App.Current.Resources["ClrGeneralrose"];
            static Color orangecolor = (Color)App.Current.Resources["ClrRoseOrange"];
            CGColor roseCGcolor = rosecolor.ToCGColor();
            CGColor orangeCGcolor = orangecolor.ToCGColor();
            #endregion
    
            CAGradientLayer gradient;
    
            public override CGRect Frame
            {
                get => base.Frame;
                set
                {
                    if (value.Width > 0 && value.Height > 0)
                    {
                        if (Control?.Layer.Sublayers != null)
                            foreach (var layer in Control?.Layer.Sublayers.Where(layer => layer is CAGradientLayer))
                                layer.Frame = new CGRect(0, 0, value.Width, value.Height);
                    }
                    base.Frame = value;
                }
            }
    
            protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
            {
                base.OnElementPropertyChanged(sender, e);
    
                if (e.PropertyName == "Renderer")
                {
                    gradient.Frame = new CGRect(0, 0, Frame.Width, Frame.Height);
                }
            }
    
            protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
            {
                base.OnElementChanged(e);
    
                if (e.OldElement != null) return;
    
                gradient = new CAGradientLayer
                {
                    CornerRadius = Control.Layer.CornerRadius,
                    Colors = new CGColor[] { roseCGcolor, orangeCGcolor },
                    StartPoint = new CGPoint(0.1, 0.5),
                    EndPoint = new CGPoint(1.1, 0.5)
                };
    
                var layer = Control?.Layer.Sublayers.LastOrDefault();
                Control?.Layer.InsertSublayerBelow(gradient, layer);
    
                base.Draw(Frame);
            }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-05-31
      • 1970-01-01
      • 1970-01-01
      • 2012-12-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-29
      相关资源
      最近更新 更多