iOS UIMenuController Custom Renderer for Xamarin Forms - xamarin

I am trying to create a custom renderer so that a context menu is displayed when a user clicks a button. I have it working in Android and UWP but iOS is proving more difficult. When I click the button, everything runs with no errors but the UIMenuController is not displayed, although I cannot click the button again almost as though the view containing the button has overlaid the screen preventing access to the button. I've tried attaching the menu controller to the button, the ContextMenuView.
Here's the custom Xamarin Forms View -
public class ContextMenuView : View
{
public EventHandler MenuRequested;
public void RequestMenu(object sender)
{
if(MenuRequested != null)
{
MenuRequested(sender, EventArgs.Empty);
}
}
}
The ContextMenuView is instantiated from the click event of a button on Main.xaml. Main.xaml consists of an AbsoluteLayout that contains the button being clicked. Here's the click event of the button -
private void ContextMenuButton_Clicked(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine("Click");
var button = sender as Button;
if (_popupMenu == null)
{
_popupMenu = new ContextMenuView();
Rectangle menuPosition = new Rectangle { X = button.X, Y = button.Y, Width = 50, Height = 50 };
_popupMenu.Layout(((Button)sender).Bounds);
AbsLayout.Children.Add(_popupMenu, menuPosition);
_popupMenu.IsVisible = true;
}
else
{
Rectangle menuPosition = new Rectangle { X = button.X, Y = button.Y + button.Height, Width = 50, Height = 50 };
_popupMenu.Layout(((Button)sender).Bounds);
}
_popupMenu.RequestMenu(sender);
}
And the iOS renderer -
public class ContextMenuViewRendererIOS : ViewRenderer<ContextMenuView, UIView>
{
private UIView _nativeControl;
private ContextMenuView _xamarinControl;
private Xamarin.Forms.AbsoluteLayout _container;
private UIView _iosView;
private nfloat _height;
private nfloat _width;
protected override void OnElementChanged(ElementChangedEventArgs<ContextMenuView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
if (e.NewElement != null)
{
_xamarinControl = e.NewElement;
_xamarinControl.MenuRequested += OnMenuRequested;
}
_height = UIScreen.MainScreen.Bounds.Height;
_width = UIScreen.MainScreen.Bounds.Width;
_nativeControl = new UIView(new CGRect(0, 0, _width, _height));
SetNativeControl(_nativeControl);
}
}
private void OnMenuRequested(object sender, EventArgs e)
{
try
{
var _menu = UIMenuController.SharedMenuController;
BecomeFirstResponder();
var iterm = new UIMenuItem("John", new ObjCRuntime.Selector("MenuItemAction:"));
_menu.MenuItems = new[] { iterm };
_menu.SetTargetRect(new CGRect(10, 10, 100, 100), _nativeControl);
_menu.MenuVisible = true;
}
catch (Exception ex)
{
throw;
}
}
[Export("MenuItemAction:")]
private void MenuItemAction(UIMenuController controller)
{
System.Diagnostics.Debug.WriteLine("MenuItemAction");
}
}
Thanks in advance.

The custom renderer needs to override CanBecomeFirstResponder and CanPerform(Selector action, NSObject withSender) and return true from both.

Related

Adding left and right full swipe gesture on listview using xamarin forms

Currently i have a listview which stores the medications of my users, i want to implement a swipe gesture so that the user can simply swipe left or right to tell if the medication has been taken or not taken.
Is there a way to add a left and right full swipe gesture in a listview just like the way that apple has implemented in their mail.
Note:it cannot achieved moved item follow with fingertip, but could achieved left and right gestures
First, you should build swipe compoment using gesture
SwipeGestureGrid.cs
public class SwipeGestureGrid : Grid
{
#region Private Member
private double _gestureX { get; set; }
private double _gestureY { get; set; }
private bool IsSwipe { get; set; }
#endregion
#region Public Member
#region Events
#region Tapped
public event EventHandler Tapped;
protected void OnTapped(EventArgs e)
{
if (Tapped != null)
Tapped(this, e);
}
#endregion
#region SwipeUP
public event EventHandler SwipeUP;
protected void OnSwipeUP(EventArgs e)
{
if (SwipeUP != null)
SwipeUP(this, e);
}
#endregion
#region SwipeDown
public event EventHandler SwipeDown;
protected void OnSwipeDown(EventArgs e)
{
if (SwipeDown != null)
SwipeDown(this, e);
}
#endregion
#region SwipeRight
public event EventHandler SwipeRight;
protected void OnSwipeRight(EventArgs e)
{
if (SwipeRight != null)
SwipeRight(this, e);
}
#endregion
#region SwipeLeft
public event EventHandler SwipeLeft;
protected void OnSwipeLeft(EventArgs e)
{
if (SwipeLeft != null)
SwipeLeft(this, e);
}
#endregion
#endregion
public double Height
{
get
{
return HeightRequest;
}
set
{
HeightRequest = value;
}
}
public double Width
{
get
{
return WidthRequest;
}
set
{
WidthRequest = value;
}
}
#endregion
public SwipeGestureGrid()
{
PanGestureRecognizer panGesture = new PanGestureRecognizer();
panGesture.PanUpdated += PanGesture_PanUpdated;
TapGestureRecognizer tapGesture = new TapGestureRecognizer();
tapGesture.Tapped += TapGesture_Tapped;
GestureRecognizers.Add(panGesture);
GestureRecognizers.Add(tapGesture);
}
private void TapGesture_Tapped(object sender, EventArgs e)
{
try
{
if (!IsSwipe)
OnTapped(null);
IsSwipe = false;
}
catch (Exception ex)
{
}
}
private void PanGesture_PanUpdated(object sender, PanUpdatedEventArgs e)
{
try
{
switch (e.StatusType)
{
case GestureStatus.Running:
{
_gestureX = e.TotalX;
_gestureY = e.TotalY;
}
break;
case GestureStatus.Completed:
{
IsSwipe = true;
//Debug.WriteLine("{0} {1}", _gestureX, _gestureY);
if (Math.Abs(_gestureX) > Math.Abs(_gestureY))
{
if (_gestureX > 0)
{
OnSwipeRight(null);
}
else
{
OnSwipeLeft(null);
}
}
else
{
if (_gestureY > 0)
{
OnSwipeDown(null);
}
else
{
OnSwipeUP(null);
}
}
}
break;
}
}
catch (Exception ex)
{
}
}
}
Next using datatemplate in listview andattach event for GesturecompomentPage.cs
ListView lsvData = new ListView()
{
VerticalOptions = LayoutOptions.Fill,
HorizontalOptions = LayoutOptions.Fill,
BackgroundColor = Color.White,
HasUnevenRows = true,
};
List<string> lstData = new List<string>();
public Pages()
{
#region DataTemplate
DataTemplate ListDataTemplate = new DataTemplate(() =>
{
#region DataArea of Template
SwipeGestureGrid gridData = new SwipeGestureGrid()
{
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
HeightRequest = 60,
RowDefinitions =
{
new RowDefinition { },
},
ColumnDefinitions =
{
new ColumnDefinition { },
}
};
#endregion
#region Base of Template
Grid gridBase = new Grid()
{
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
HeightRequest = 60,
RowDefinitions =
{
new RowDefinition { },
},
ColumnDefinitions =
{
new ColumnDefinition { },
//Put Cells Data here
new ColumnDefinition { Width = new GridLength(0,
GridUnitType.Absolute)}, //Button for Cells here
},
};
#endregion
Label lblText = new Label
{
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
FontAttributes = FontAttributes.Bold,
VerticalTextAlignment = TextAlignment.End,
TextColor = Color.Black,
BackgroundColor = Color.Silver,
LineBreakMode = LineBreakMode.TailTruncation,
FontSize = 18,
};
lblText.SetBinding(Label.TextProperty, ".");
ImageButton btnCellDelete = new ImageButton() { Source = "Delete" };
gridData.Children.Add(lblText, 0, 0);
gridBase.Children.Add(gridData, 0, 0);
gridBase.Children.Add(btnCellDelete, 1, 0);
gridData.SwipeLeft += GridTemplate_SwipeLeft;
gridData.SwipeRight += GridTemplate_SwipeRight; ;
gridData.Tapped += GridTemplate_Tapped; ;
btnCellDelete.Clicked += BtnCellDelete_Clicked; ;
return new ViewCell
{
View = gridBase,
Height = 60,
};
});
#endregion
for (int i = 1; i <= 100; i++)
{
lstData.Add(i.ToString());
}
lsvData.ItemTemplate = ListDataTemplate;
lsvData.ItemsSource = lstData;
Content = lsvData;
}
Add the event.SwipeLeft to show DeleteButton
private void GridTemplate_SwipeLeft(object sender, EventArgs e)
{
try
{
if (sender is SwipeGestureGrid)
{
var templateGrid = ((SwipeGestureGrid)sender).Parent;
if (templateGrid != null && templateGrid is Grid)
{
var CellTemplateGrid = (Grid)templateGrid;
CellTemplateGrid.ColumnDefinitions[1].Width = new GridLength(60, GridUnitType.Absolute);
}
}
}
catch (Exception ex)
{
}
}
swiperight to hide delete button
private void GridTemplate_SwipeRight(object sender, EventArgs e)
{
try
{
if (sender is SwipeGestureGrid)
{
var templateGrid = ((SwipeGestureGrid)sender).Parent;
if (templateGrid != null && templateGrid is Grid)
{
var CellTemplateGrid = (Grid)templateGrid;
CellTemplateGrid.ColumnDefinitions[1].Width = new GridLength(0, GridUnitType.Absolute);
}
}
}
catch (Exception ex)
{
}
}
Delete button click event
private void BtnCellDelete_Clicked(object sender, EventArgs e)
{
try
{
if (sender is ImageButton)
{
var templateGrid = ((ImageButton)sender);
//templateGrid.Parent = gridBase
//templateGrid.Parent.Parent = cell
if (templateGrid.Parent != null && templateGrid.Parent.Parent != null && templateGrid.Parent.Parent.BindingContext != null && templateGrid.Parent.Parent.BindingContext is string)
{
var deletedate = templateGrid.Parent.Parent.BindingContext as string;
lstData.RemoveAll(f => f == deletedate);
lsvData.ItemsSource = null;
lsvData.ItemsSource = lstData;
}
}
}
catch (Exception ex)
{
}
}
There is all code.
https://github.com/act70255/ListViewSwipeGesture
I will create a listview with a custom cells
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/listview/customizing-cell-appearance
and i add swipe left and right gestures on a grid inside to a ViewCell
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/gestures/swipe
on left swipe i expand with animation something like a grid column where on start the width is 0 and after swap animated to some width. Same with right swipe.
Check for the animation the OnExpandExchange() method from here
https://github.com/xamarinium/FlippingAndResizableUI/blob/master/FlippingAndResizableView/BitcoinView.xaml.cs

Activity Indicator not working in Xamarin.Forms

I have an activity indicator designed inside a absolute layout. Based on a button click event, I try to show and hide the activity indicator alternatively. But due to some reason, I cannot see my activity Indicator.Any help will be greatly appreciated!!! Thanks in advance.
This is my .xaml.cs class:
public partial class PBTestPage : ContentPage
{
private bool _pbIndicator;
public PBTestPage()
{
InitializeComponent();
}
public bool PBIndicator{
get{
return _pbIndicator;
}set{
_pbIndicator = value;
OnPropertyChanged();
}
}
protected override void OnAppearing()
{
base.OnAppearing();
var parentLayout = new AbsoluteLayout();
var stackContent = new StackLayout();
AbsoluteLayout.SetLayoutFlags(stackContent,AbsoluteLayoutFlags.PositionProportional);
AbsoluteLayout.SetLayoutBounds(stackContent,new Rectangle(0f,0f,AbsoluteLayout.AutoSize,AbsoluteLayout.AutoSize));
var activityIndicator = new ActivityIndicator
{
Color = Color.Black,
IsRunning = PBIndicator,
IsVisible = PBIndicator
};
AbsoluteLayout.SetLayoutFlags(activityIndicator, AbsoluteLayoutFlags.PositionProportional);
AbsoluteLayout.SetLayoutBounds(activityIndicator, new Rectangle(.5, .5, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
var button = new Button
{
Text="Click",
VerticalOptions=LayoutOptions.CenterAndExpand,
HorizontalOptions=LayoutOptions.CenterAndExpand,
};
button.Clicked += OnClicked;
stackContent.Children.Add(button);
parentLayout.Children.Add(stackContent);
parentLayout.Children.Add(activityIndicator);
Content = parentLayout;
}
private void OnClicked(object sender, EventArgs e)
{
if(PBIndicator==false){
PBIndicator = true;
}else{
PBIndicator = false;
}
}
}
I'm inferring you're intending to use bindings by the use of OnPropertyChanged, so it's a good time to start do it.
I've made some changes in your code and I guess it will work properly now. The changes are:
Moved the layout creation to the constructor (I can't see create the whole same layout on every time the page is shown as a good choice );
The OnClicked event just invert the value of the property, no need to check it before with an if;
Using Bindings to handle the ActivityIndicator's properties state;
Set true to PBIndicator property on the OnAppearing event.
This is the changed code:
public partial class PBTestPage : ContentPage
{
private bool _pbIndicator;
public bool PBIndicator
{
get { return _pbIndicator; }
set
{
_pbIndicator = value;
OnPropertyChanged();
}
}
public PBTestPage()
{
InitializeComponent();
var parentLayout = new AbsoluteLayout();
var stackContent = new StackLayout();
AbsoluteLayout.SetLayoutFlags(stackContent, AbsoluteLayoutFlags.PositionProportional);
AbsoluteLayout.SetLayoutBounds(stackContent, new Rectangle(0f, 0f, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
var activityIndicator = new ActivityIndicator
{
Color = Color.Black
};
activityIndicator.SetBinding(ActivityIndicator.IsRunningProperty, new Binding(nameof(PBIndicator)));
activityIndicator.SetBinding(ActivityIndicator.IsVisibleProperty, new Binding(nameof(PBIndicator)));
activityIndicator.BindingContext = this;
AbsoluteLayout.SetLayoutFlags(activityIndicator, AbsoluteLayoutFlags.PositionProportional);
AbsoluteLayout.SetLayoutBounds(activityIndicator, new Rectangle(.5, .5, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
var button = new Button
{
Text = "Click",
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand,
};
button.Clicked += OnClicked;
stackContent.Children.Add(button);
parentLayout.Children.Add(stackContent);
parentLayout.Children.Add(activityIndicator);
Content = parentLayout;
}
protected override void OnAppearing()
{
base.OnAppearing();
PBIndicator = true;
}
private void OnClicked(object sender, EventArgs e)
{
PBIndicator = !PBIndicator;
}
}
Let me know if it works. I hope it helps.
Try this one
private void OnClicked(object sender, EventArgs e)
{
if(PBIndicator==false){
activityIndicator.IsRunning=true;
}else{
activityIndicator.IsRunning=false;
}
}

Receiving event Button in a Grid event from outside

I have created a custom control.
It consists of a Grid in which a Button is placed:
using Xamarin.Forms;
namespace MyApp
{
public class clsButton : ContentView
{
private Grid _grid;
private Button _button;
public clsButton()
{
_grid = new Grid
{
Margin = new Thickness(0),
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
};
_grid.BindingContext = this;
_button = new Button()
{
};
_button.Clicked += async (sender, e) =>
{
//I tried different things here, but none gave me the right results. I need to "bubble" this click to the outside
return;
};
_grid.Children.Add(_button, 0, 0);
this.Content = _grid;
}
}
}
I create some of these custom controls in a ContentPage like this:
_MyButton = new clsImageButton()
{
};
var nTapGestureRecognizer = new TapGestureRecognizer();
nTapGestureRecognizer.Tapped += OnButtonClicked;
_MyButton.GestureRecognizers.Add(nTapGestureRecognizer);
And this is the void in the same ContentPage:
async void OnButtonClicked(object sender,EventArgs e)
{
//I don't managed to get here
}
This doesn't work.
"OnButtonClicked" is never called.
I think I have to raise an event from within the custom control.
I tried some things, but none of them were successful.
How would I do this correctly?
in your clsButton, declare a public event
public EventHandler ButtonClicked { get; set; }
then raise the event when your button is clicked
_button.Clicked += async (sender, e) =>
{
if (ButtonClicked != null) ButtonClicked(this,e);
};
finally, where ever you are using clsButton, you can subscribe to the event (the gesture recognizer is not needed)
var btn = new clsButton();
btn.ButtonClicked += async (sender, e) => {
// respond here
}

How to change Picker Border color in xamarin forms

My borderless custom renderer for picker
public class BorderlessPickerRenderer : PickerRenderer
{
public static void Init() { }
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if (e.OldElement == null)
{
Control.Background = null;
}
}
}
It will change the picker list text color as white. please see the screenshot
If you check the source code of PickerRenderer, you will find that the Dialog is totally generated in the code behind.
So here to set a Transparent(border-less) background, we can re-write the Click event of this control, for example:
public class BorderlessPickerRenderer : Xamarin.Forms.Platform.Android.PickerRenderer
{
private IElementController ElementController => Element as IElementController;
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 NumberPicker(Context);
if (model.Items != null && model.Items.Any())
{
picker.MaxValue = model.Items.Count - 1;
picker.MinValue = 0;
picker.SetDisplayedValues(model.Items.ToArray());
picker.WrapSelectorWheel = false;
picker.DescendantFocusability = DescendantFocusability.BlockDescendants;
picker.Value = model.SelectedIndex;
}
var layout = new LinearLayout(Context) { Orientation = Orientation.Vertical };
layout.AddView(picker);
ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true);
var builder = new AlertDialog.Builder(Context);
builder.SetView(layout);
builder.SetTitle(model.Title ?? "");
builder.SetNegativeButton(global::Android.Resource.String.Cancel, (s, a) =>
{
ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, 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(global::Android.Resource.String.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.IsFocusedPropertyKey, 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.IsFocusedPropertyKey, false);
};
_dialog.Show();
_dialog.Window.SetBackgroundDrawable(new ColorDrawable(Android.Graphics.Color.Transparent));
}
}
Rendering image of this custom picker:
The font color and button's style can be modified as you need since you created this dialog by yourself. And the style of the dialog also depends on the style of your app.

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