How to set height of ProgressBar on Xamarin Forms 3.3? - xamarin

I'm using Xamarin Forms 3.3, I want change height of ProgressBar. I execute it:
How to change height of progress bar in Xamarin Forms?
But I can't change height of ProgressBar. This is my code:
1) Class custom control ProgressBar:
public class ColorProgressBar : ProgressBar
{
public static BindableProperty BarColorProperty = BindableProperty.Create(
nameof(BarColor), typeof(Color), typeof(ColorProgressBar), default(Color));
public static BindableProperty ProgressTintColorProperty = BindableProperty.Create(
nameof(ProgressTintColor), typeof(Color), typeof(ColorProgressBar), default(Color));
public static BindableProperty TrackTintColorProperty = BindableProperty.Create(
nameof(TrackTintColor), typeof(Color), typeof(ColorProgressBar), default(Color));
public Color BarColor
{
get => (Color)GetValue(BarColorProperty);
set => SetValue(BarColorProperty, value);
}
public Color ProgressTintColor
{
get => (Color)GetValue(ProgressTintColorProperty);
set => SetValue(ProgressTintColorProperty, value);
}
public Color TrackTintColor
{
get => (Color)GetValue(TrackTintColorProperty);
set => SetValue(TrackTintColorProperty, value);
}
public float HeightOfProgressBar { get; set; }
}
2) For iOS:
public class ColorProgressBarRenderer : ProgressBarRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<ProgressBar> e)
=> iOS.Control.Logger.InvokeAction(() =>
{
base.OnElementChanged(e);
if (e.NewElement == null)
return;
if (Control != null)
{
UpdateBarColor();
}
});
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
=> iOS.Control.Logger.InvokeAction(() =>
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == ColorProgressBar.BarColorProperty.PropertyName)
{
UpdateBarColor();
}
});
private void UpdateBarColor() => iOS.Control.Logger.InvokeAction(() =>
{
var element = (ColorProgressBar) Element;
if (element == null)
return;
Control.TintColor = element.BarColor.ToUIColor();
Control.ProgressTintColor = element.ProgressTintColor.ToUIColor();
Control.TrackTintColor = element.TrackTintColor.ToUIColor();
});
public override void LayoutSubviews()
{
base.LayoutSubviews();
var element = (ColorProgressBar)Element;
if (element == null)
return;
var X = 1.0f;
var Y = element.HeightOfProgressBar > 0.0f ? element.HeightOfProgressBar : 1.0f;
var transform = CGAffineTransform.MakeScale(X, Y);
Transform = transform;
}
}
And At View .xaml:
<control:ColorProgressBar Grid.Row="1"
HeightOfProgressBar="8.0"
TrackTintColor="{x:Static color:BasePalette.DarkestColor}"
ProgressTintColor="{x:Static color:NeutralPalette.RedColor}"
Progress="{Binding PercentDataValue}">
</control:ColorProgressBar>
I test on device run iOS 12.0.
Thanks!

You missed a comment on the linked SO thread that said:
To get the iOS renderer to work I needed to change this.Transform = transform; to Control.Transform = transform;
So changing Transform = transform; in the LayoutSubviews method of the ColorProgressBarRenderer to Control.Transform = transform; makes your sample work as expected, i.e the height of the progress bar is changed:
public override void LayoutSubviews()
{
base.LayoutSubviews();
var element = (ColorProgressBar)Element;
if (element == null)
return;
var X = 1.0f;
var Y = element.HeightOfProgressBar > 0.0f ? element.HeightOfProgressBar : 1.0f;
var transform = CGAffineTransform.MakeScale(X, Y);
Control.Transform = transform; // <---- change here
}

Related

How to set picker row item long text in two lines using custom renderer in xamarin forms android?

I want to show long text of picker item in two lines using custom render how can I achieve it?
First , you need a custom renderer for picker .
To override the item you need to replace the original view with a AlertDialog .
Then custom a ListView and set it as AlertDialog.View .
Then you can customize everything in the Adapter(here we need to customize the textview ).
Sample code
[assembly: ExportRenderer(typeof(Picker), typeof(MyPickerRenderer))]
namespace FormsApp.Droid
{
public class MyAdapter : ArrayAdapter
{
private IList<string> _data;
Context _context;
public MyAdapter(Context context, int resource , IList<string> data) : base(context,resource)
{
_context = context;
_data = data;
}
public override int Count => _data.Count;
public override Android.Views.View GetView(int position, Android.Views.View convertView, ViewGroup parent)
{
TextView textview = new TextView(_context);
textview.TextSize = 18;
textview.SetTextColor(Android.Graphics.Color.DarkGray);
textview.Ellipsize = TruncateAt.End;
textview.SetMaxLines(2); //this line
textview.Text = _data[position];
return textview;
}
}
public class MyListView : Android.Widget.ListView
{
public MyListView(Context context, IList<string> data) : base(context)
{
this.DividerHeight = 0;
this.Adapter = new MyAdapter(context, 0, data);
}
}
class MyPickerRenderer : Xamarin.Forms.Platform.Android.AppCompat.PickerRenderer
{
IElementController ElementController => Element as IElementController;
public MyPickerRenderer(Context context):base(context)
{
}
private AlertDialog _dialog;
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if (e.NewElement == null || e.OldElement != null)
return;
Control.Click += Control_Click;
}
protected override void Dispose(bool disposing)
{
Control.Click -= Control_Click;
base.Dispose(disposing);
}
private void Control_Click(object sender, EventArgs e)
{
Picker model = Element;
var picker = new MyListView(Context, model.Items);
var layout = new LinearLayout(Context) { Orientation = Orientation.Vertical };
layout.SetPadding(35, 30, 35, 0);
layout.AddView(picker);
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, true);
var builder = new AlertDialog.Builder(Context);
builder.SetView(layout);
builder.SetTitle(model.Title ?? "");
builder.SetNegativeButton("Cancel ", (s, a) =>
{
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
// It is possible for the Content of the Page to be changed when Focus is changed.
// In this case, we'll lose our Control.
Control?.ClearFocus();
_dialog = null;
});
_dialog = builder.Create();
_dialog.DismissEvent += (ssender, args) =>
{
ElementController?.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
};
_dialog.Show();
}
}
}
Testing code in Forms xaml
<Picker x:Name="picker" Title="Select a monkey">
<Picker.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>abc</x:String>
<x:String>aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbccccccccccccccccdddddddddddddddddddddd</x:String>
</x:Array>
</Picker.ItemsSource>
</Picker>
Before
After

Custom Renderer for Picker in Xamarin.Forms

I want to customize my picker. I created a custom renderer for my picker but I dont know how the customization settings. How can I change the font style and size of the item? and How can I remove the two lines?
public class CustomPickerRenderer : PickerRenderer
{
public CustomPickerRenderer(Context context) : base(context)
{
AutoPackage = false;
}
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if (e.OldElement == null)
{
Control.Background = null;
var layoutParams = new MarginLayoutParams(Control.LayoutParameters);
layoutParams.SetMargins(0, 0, 0, 0);
Control.LayoutParameters = layoutParams;
Control.SetPadding(0, 0, 0, 0);
SetPadding(0, 0, 0, 0);
}
}
}
If you want to set the fontSize of the text , you first need to customize a subclass extends from NumberPicker and overwrite the method AddView.
public class TextColorNumberPicker: NumberPicker
{
public TextColorNumberPicker(Context context) : base(context)
{
}
public override void AddView(View child, int index, ViewGroup.LayoutParams #params)
{
base.AddView(child, index, #params);
UpdateView(child);
}
public void UpdateView(View view)
{
if ( view is EditText ) {
//set the font of text
((EditText)view).TextSize = 8;
}
}
}
If you want to remove the lines,you should rewrite the NumberPicker
in Android Custom Renderer
public class MyAndroidPicker:PickerRenderer
{
IElementController ElementController => Element as IElementController;
public MyAndroidPicker()
{
}
private AlertDialog _dialog;
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if (e.NewElement == null || e.OldElement != null)
return;
Control.Click += Control_Click;
}
protected override void Dispose(bool disposing)
{
Control.Click -= Control_Click;
base.Dispose(disposing);
}
private void SetPickerDividerColor(TextColorNumberPicker picker)
{
Field[] fields = picker.Class.GetDeclaredFields();
foreach (Field pf in fields)
{
if(pf.Name.Equals("mSelectionDivider"))
{
pf.Accessible = true;
// set the color as transparent
pf.Set(picker, new ColorDrawable(this.Resources.GetColor(Android.Resource.Color.Transparent)));
}
}
}
private void Control_Click(object sender, EventArgs e)
{
Picker model = Element;
var picker = new TextColorNumberPicker(Context);
if (model.Items != null && model.Items.Any())
{
picker.MaxValue = model.Items.Count - 1;
picker.MinValue = 0;
picker.SetBackgroundColor(Android.Graphics.Color.Yellow);
picker.SetDisplayedValues(model.Items.ToArray());
//call the method after you setting DisplayedValues
SetPickerDividerColor(picker);
picker.WrapSelectorWheel = false;
picker.Value = model.SelectedIndex;
}
var layout = new LinearLayout(Context) { Orientation = Orientation.Vertical };
layout.AddView(picker);
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, true);
var builder = new AlertDialog.Builder(Context);
builder.SetView(layout);
builder.SetTitle(model.Title ?? "");
builder.SetNegativeButton("Cancel ", (s, a) =>
{
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
// It is possible for the Content of the Page to be changed when Focus is changed.
// In this case, we'll lose our Control.
Control?.ClearFocus();
_dialog = null;
});
builder.SetPositiveButton("Ok ", (s, a) =>
{
ElementController.SetValueFromRenderer(Picker.SelectedIndexProperty, picker.Value);
// It is possible for the Content of the Page to be changed on SelectedIndexChanged.
// In this case, the Element & Control will no longer exist.
if (Element != null)
{
if (model.Items.Count > 0 && Element.SelectedIndex >= 0)
Control.Text = model.Items[Element.SelectedIndex];
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
// It is also possible for the Content of the Page to be changed when Focus is changed.
// In this case, we'll lose our Control.
Control?.ClearFocus();
}
_dialog = null;
});
_dialog = builder.Create();
_dialog.DismissEvent += (ssender, args) =>
{
ElementController?.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
};
_dialog.Show();
}
}
I also used this CustomRenderer which was posted before only instead of overriding it you can change the properties like this.
private void Control_Click(object sender, EventArgs e)
{
Picker model = Element;
var picker = new MyNumberPicker(Context);
if (model.Items != null && model.Items.Any())
{
// set style here
picker.MaxValue = model.Items.Count - 1;
picker.MinValue = 0;
picker.SetBackgroundColor(Android.Graphics.Color.Transparent);
picker.SetDisplayedValues(model.Items.ToArray());
//call the method after you setting DisplayedValues
SetPickerDividerColor(picker);
picker.WrapSelectorWheel = false;
picker.Value = model.SelectedIndex;
// change Text Size and Divider
picker.TextSize = 30;
picker.SelectionDividerHeight = 1;
}

Xamarin Forms Gradient Renderer not working on iOS

I'm trying to use a Gradient Renderer for which I have written a class in PCL and written a renderer for both Android and iOS. Android renderer is working but iOS renderer is not showing the gradient colour at all.
I'm using this Gradient code from XLabs. I'm not sure what's broken. A hint in the right direction would help.
PCL Code:
using Xamarin.Forms;
namespace gradient
{
public enum GradientOrientation
{
Vertical,
Horizontal
}
public class GradientContentView : ContentView
{
public GradientOrientation Orientation
{
get { return (GradientOrientation)GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}
public static readonly BindableProperty OrientationProperty =
BindableProperty.Create<GradientContentView, GradientOrientation>(x => x.Orientation, GradientOrientation.Vertical, BindingMode.OneWay);
public Color StartColor
{
get { return (Color)GetValue(StartColorProperty); }
set { SetValue(StartColorProperty, value); }
}
public static readonly BindableProperty StartColorProperty =
BindableProperty.Create<GradientContentView, Color>(x => x.StartColor, Color.White, BindingMode.OneWay);
public Color EndColor
{
get { return (Color)GetValue(EndColorProperty); }
set { SetValue(EndColorProperty, value); }
}
public static readonly BindableProperty EndColorProperty =
BindableProperty.Create<GradientContentView, Color>(x => x.EndColor, Color.Black, BindingMode.OneWay);
}
}
iOS Renderer code:
using CoreAnimation;
using CoreGraphics;
using gradient;
using gradient.iOS;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(GradientContentView), typeof(GradientContentViewRenderer))]
namespace gradient.iOS
{
class GradientContentViewRenderer : VisualElementRenderer<ContentView>
{
private GradientContentView GradientContentView
{
get { return (GradientContentView)Element; }
}
protected CAGradientLayer GradientLayer { get; set; }
protected override void OnElementChanged(ElementChangedEventArgs<ContentView> e)
{
base.OnElementChanged(e);
if (GradientContentView != null &&
NativeView != null)
{
// Create a gradient layer and add it to the
// underlying UIView
GradientLayer = new CAGradientLayer();
GradientLayer.Frame = NativeView.Bounds;
GradientLayer.Colors = new CGColor[]
{
GradientContentView.StartColor.ToCGColor(),
GradientContentView.EndColor.ToCGColor()
};
SetOrientation();
NativeView.Layer.InsertSublayer(GradientLayer, 0);
}
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (GradientLayer != null && GradientContentView != null)
{
// Turn off Animations
CATransaction.Begin();
CATransaction.DisableActions = true;
if (e.PropertyName == GradientContentView.StartColorProperty.PropertyName)
GradientLayer.Colors[0] = GradientContentView.StartColor.ToCGColor();
if (e.PropertyName == GradientContentView.EndColorProperty.PropertyName)
GradientLayer.Colors[1] = GradientContentView.EndColor.ToCGColor();
if (e.PropertyName == VisualElement.WidthProperty.PropertyName ||
e.PropertyName == VisualElement.HeightProperty.PropertyName)
GradientLayer.Frame = NativeView.Bounds;
if (e.PropertyName == GradientContentView.OrientationProperty.PropertyName)
SetOrientation();
CATransaction.Commit();
}
}
void SetOrientation()
{
if (GradientContentView.Orientation == GradientOrientation.Horizontal)
{
GradientLayer.StartPoint = new CGPoint(0, 0.5);
GradientLayer.EndPoint = new CGPoint(1, 0.5);
}
else
{
GradientLayer.StartPoint = new CGPoint(0.5, 0);
GradientLayer.EndPoint = new CGPoint(0.5, 1);
}
}
}
}
This is my code for rendering a gradient background, i am not using orientation, but maybe it helps.
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.OldElement == null) // perform initial setup
{
ModernOrderCalendar page = e.NewElement as ModernOrderCalendar;
var gradientLayer = new CAGradientLayer();
gradientLayer.Name = "gradient";
CGRect rect = View.Bounds;
gradientLayer.Frame = rect;
gradientLayer.Colors = new CGColor[] { page.StartColor.ToCGColor(), page.EndColor.ToCGColor() };
View.Layer.InsertSublayer(gradientLayer, 0);
}
}
public override void ViewWillLayoutSubviews()
{
base.ViewWillLayoutSubviews();
if (Xamarin.Forms.Device.Idiom == TargetIdiom.Tablet)
{
var gradientLayer = View.Layer.Sublayers.FirstOrDefault(l => l.Name == "gradient");
gradientLayer.Frame = View.Bounds;
View.Layer.Sublayers[0] = gradientLayer;
CGRect frame = View.Bounds;
View.Bounds = frame;
}
}
The main difference I see is that you don't seem to be overriding the ViewWillLayoutSubviews method. I had the same issue, which caused the gradient layer to be created with no height and width during the creation of the window (at that point the View has not layouted, yet).
Therefore I adapt the gradientlayer width and height when layouting the subviews, because at that point width and height of the view are definitely known.
You must update the layer's size in VisualElementRenderer.LayoutSubviews:
public override void LayoutSubviews()
{
base.LayoutSubviews();
CATransaction.Begin();
CATransaction.DisableActions = true;
GradientLayer.Frame = NativeView.Bounds;
CATransaction.Commit();
}

Round Corner Gridlayout Xamarin Android

I want to Create RoundCorner GridLayout.I have added round corner to Gridlayot Control in Xamarin but Gridlayout BackgroundColor display in Back Side like this.Please help me to resolve issue.
GridLayout after Drawable Add
Xamarin.Forms Xaml Code :
xmlns:local="clr-namespace:XamainControl.Renderer;assembly=XamainKcsControl"
<Grid BackgroundColor="Purple" WidthRequest="300">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<local:KcsGrid Grid.Row="0"
BorderColor="Blue"
BackgroundColor="Yellow"
BorderRadius="20"
BorderWidth="10" Padding="15" >
<Label Text="Customization Page"
FontSize="25"
TextColor="Red"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand"/>
</local:KcsGrid>
</Grid>
Custom Grid Property Create Xamarin.Forms
public class KcsGrid : Grid
{
#region Constructor
public KcsGrid() : base()
{
this.Margin = new Thickness(0, 0, 0, -6);
}
#endregion
#region Properties
public static BindableProperty BorderColorProperty = BindableProperty.Create<KcsGrid, Color>(o => o.BorderColor, Color.Transparent);
public Color BorderColor
{
get { return (Color)GetValue(BorderColorProperty); }
set { SetValue(BorderColorProperty, value); }
}
public static BindableProperty BorderWidthProperty = BindableProperty.Create<KcsGrid, float>(o => o.BorderWidth, 0);
public float BorderWidth
{
get { return (float)GetValue(BorderWidthProperty); }
set { SetValue(BorderWidthProperty, value); }
}
public static BindableProperty BorderRadiusProperty = BindableProperty.Create<KcsGrid, float>(o => o.BorderRadius, 0);
public float BorderRadius
{
get { return (float)GetValue(BorderRadiusProperty); }
set { SetValue(BorderRadiusProperty, value); }
}
public static BindableProperty WidthValueProperty = BindableProperty.Create<KcsGrid, int>(o => o.WidthValue,100);
public int WidthValue
{
get { return (int)GetValue(WidthValueProperty); }
set { SetValue(WidthValueProperty, value); }
}
public static BindableProperty HeightValueProperty = BindableProperty.Create<KcsGrid, int>(o => o.HeightValue,50);
public int HeightValue
{
get { return (int)GetValue(HeightValueProperty); }
set { SetValue(HeightValueProperty, value); }
}
#endregion
}
Xamarin.Android Code :
namespace XamainKcsControl.Droid.Renderer
{
public class CustomGridRender : ViewRenderer<KcsGrid,GridLayout>
{
private BorderRenderer _renderer;
GridLayout Cusgd;
protected override void OnElementChanged(ElementChangedEventArgs<KcsGrid> e)
{
base.OnElementChanged(e);
if (e.OldElement != null || this.Element == null)
return;
Cusgd = new GridLayout(Context);
if (Control == null)
{
var curgd = Element as KcsGrid;
SetNativeControl(Cusgd);
UpdateBackground(curgd);
//UpdateMeasurelayout(curgd);
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (Element == null)
return;
var Gd = Element as KcsGrid;
if (e.PropertyName == KcsGrid.BorderColorProperty.PropertyName || e.PropertyName == KcsGrid.BorderWidthProperty.PropertyName || e.PropertyName == KcsGrid.BorderRadiusProperty.PropertyName)
{
UpdateBackground(Gd);
}
if (e.PropertyName == KcsGrid.WidthValueProperty.PropertyName || e.PropertyName == KcsGrid.HeightValueProperty.PropertyName)
{
UpdateMeasurelayout(Gd);
}
//SetNativeControl(Cusgd);
}
void UpdateMeasurelayout(KcsGrid Gd)
{
GridLayout.LayoutParams Gdparm = new GridLayout.LayoutParams();
Gdparm.Width = (int)Forms.Context.ToPixels(Gd.WidthValue);
Gdparm.Height = (int)Forms.Context.ToPixels(Gd.HeightValue);
Cusgd.LayoutParameters = Gdparm;
//SetNativeControl(Cusgd);
}
void UpdateBackground(KcsGrid Gd)
{
if (_renderer != null)
{
_renderer.Dispose();
_renderer = null;
}
_renderer = new BorderRenderer();
Cusgd.Background = _renderer.GetBorderBackground(Gd.BorderColor,Gd.BackgroundColor, Gd.BorderWidth, Gd.BorderRadius);
}
}
}
Gradient Drawable
public Drawable GetBorderBackground(Color borderColor, Color backgroundColor, float borderWidth, float borderRadius)
{
if (_background != null)
{
_background.Dispose();
_background = null;
}
borderWidth = borderWidth > 0 ? borderWidth : 0;
borderRadius = borderRadius > 0 ? borderRadius : 0;
borderColor = borderColor != Color.Default ? borderColor : Color.Transparent;
backgroundColor = backgroundColor != Color.Default ? backgroundColor : Color.Transparent;
var strokeWidth = Xamarin.Forms.Forms.Context.ToPixels(borderWidth);
var radius = Xamarin.Forms.Forms.Context.ToPixels(borderRadius);
_background = new GradientDrawable();
_background.SetColor(backgroundColor.ToAndroid());
if (radius > 0)
_background.SetCornerRadius(radius);
if (borderColor != Color.Transparent && strokeWidth > 0)
{
_background.SetStroke((int)strokeWidth, borderColor.ToAndroid());
}
return _background;
}

Xamarin.Forms gradient background for Button using Custom Renderer with iOS

I am using Xamarin.Forms and I want to globally make the buttons look a little nicer.
I have achieved this just fine for the Android version using a custom renderer, but I am having trouble doing the same with iOS.
When defining buttons in my XAML pages, I reference "CustomButton" instead of "Button", and then I have the following CustomButtonRenderer in my iOS app.
Most of the style changes work just fine (border radius, etc), but I cannot seem to make it render a background gradient for the button.
Here is my code so far, but the background just displays as white. How can I make it display a gradient with the text on top?
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;
}
}
}
1st) Do not use AddSublayer, use InsertSublayerBelow so that the Z-order will be correct and your Title text will be on top.
2nd) Override LayoutSubviews and update your CAGradientLayer frame to match your UIButton.
3rd) Enjoy your gradient:
Complete Example:
[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);
}
}
}
}
Update:
If you are using iOS 10+ with newer version of Xamarin.Forms, the Control.Bounds during calls to LayoutSubViews will all be zeros. You will need to set the gradient layer Frame size during sets to the control's Frame property, i.e.:
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);
}
}
}
In the moment that OnElementChanged is called, Control.Layer.Bounds is completely zero'd out. In your rendered you will need to add methods to update the Gradient's Frame to match the Control.Layer's frame.
Responding to the comment by Robert Cafazzo, I can help to slightly adjust this render so that it works correctly:
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);
}

Resources