1
votes

I have the following CustomRenderer to apply a style to buttons in my iOS project of my cross-platform Xamarin.Forms app.

The button used to have rounded edges, white text, and a blue gradient background.

Everything has been working just fine up until I downloaded Xcode 8.1. Since moving to 8.1, the same code will not present the background gradient. Can anyone see how I could change my code to get the background gradient working again?

The border radius and text color are all working as usual - it's just the background gradient which is missing.

[assembly: ExportRenderer(typeof(CustomButton), typeof(CustomButtonRenderer))]
namespace MyApp.Forms.iOS.CustomRenderers
{
    class CustomButtonRenderer : 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(153, 204, 255).CGColor,
                    UIColor.FromRGB(51, 102, 204).CGColor
                };
                var layer = Control?.Layer.Sublayers.LastOrDefault();

                Control?.Layer.InsertSublayerBelow(gradient, layer);
                Control.SetTitleColor(UIColor.White, UIControlState.Normal);

                Control.Layer.BorderColor = UIColor.FromRGB(51, 102, 204).CGColor;
                Control.Layer.BorderWidth = 1;
            }
        }
    }
}
1
From this post it looks like Xcode 8 may have changed when you can apply the CornerRadius to a layer. One solution from that page is to do Control.LayoutIfNeeded() before setting your CornerRadius to force the Control's constraints to be applied. Although you might want to move the LayoutIfNeeded() to within an override of AwakeFromNib()hvaughan3
Does the gradient shows up if you rotate the phone/sim and rotate back?SushiHangover
@SushiHangover yes it does, but it doesn't fit the boundaries quite as expected.Laurence Frost
@LaurenceFrost The issue is in the fact that adding the CAGradientLayer at this point will not cause the the layout to be invalidated. You can try LayoutIfNeeded and/or ForceLayout, etc.. but the only thing I found that works correctly now is to subclass a UIButton, perform your customization in it and use SetNativeControl in your renderer to use your subclassed UIButton.SushiHangover
@SushiHangover it definitely sounds like this is the route to go. You don't happen to have any code examples or links I could refer to in order to help me do you?Laurence Frost

1 Answers

0
votes

Ok so I have finally worked out what was going on. In the LayoutSubviews method where I was setting each layer.Frame to the value of Control.Bounds, I noticed that Control.Bounds was a rectangle full of zeros, so my gradient was therefore 0 pixels in size.

I have modified the method as follows and it now works as expected again:

public override void LayoutSubviews()
{
    var newBounds = Element.Bounds.ToRectangleF();
    foreach (var layer in Control?.Layer.Sublayers.Where(layer => layer is CAGradientLayer))
    {
        layer.Frame = new CGRect(0, 0, newBounds.Width, newBounds.Height);
    }

    base.LayoutSubviews();
}

I'm not sure if this is a hack, but it seems to do the job - for now...