Hi all,
I have a custom renderer to add an icon to the right and/or left side of a date picker and to allow to draw a border on any side of the date picker.
This works fine as long as the icon I add is defined smaller than the size of the native controls frame.
If the icon is bigger than it seems the new size of the left/right view is not conciderered and the frame size stays the same.
I was able to fix that at least in drawing as the control is now drawn big enough.
However for some reason it seems the new size is not respected by Forms. For example if I have my custom date picker in a Stacklayouts and a button under it, then the button is very close to the date picker but there should be spacing. If I rotate from landscape to portrait, then the button even "slides" into the date picker.
My code for the custom iOS renderer looks like this:
public class CustomDatePickerIOSRenderer : DatePickerRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
{
base.OnElementChanged(e);
if(Element is CustomDatePicker customPicker)
{
UpdateLeftIcon(customPicker);
UpdateRightIcon(customPicker);
}
}
public override void LayoutSubviews()
{
base.LayoutSubviews();
if (Element is CustomDatePicker customPicker)
{
ApplyBorder(customPicker);
}
}
protected virtual void ApplyBorder(CustomDatePicker customPicker)
{
Control.BorderStyle = UITextBorderStyle.None;
RemoveBorders();
nfloat height = Control.Frame.Size.Height;
if (leftView != null && leftView.Frame.Size.Height > height)
height = leftView.Frame.Size.Height;
if (rightView != null && rightView.Frame.Size.Height > height)
height = rightView.Frame.Size.Height;
Control.Frame = new CGRect(Control.Frame.X, Control.Frame.Y, Control.Frame.Width, height);
CGColor color = Element.IsFocused ? customPicker.BorderFocusedColor.ToCGColor() : customPicker.BorderColor.ToCGColor();
if (customPicker.Border.Left > 0)
CreateBorderSide(0, 0, customPicker.Border.Left, Control.Frame.Size.Height,color);
if(customPicker.Border.Right > 0)
CreateBorderSide(Control.Frame.Size.Width-customPicker.Border.Right,0, customPicker.Border.Right, Control.Frame.Size.Height, color);
if(customPicker.Border.Top > 0)
CreateBorderSide(0, 0, Control.Frame.Size.Width,customPicker.Border.Top, color);
if(customPicker.Border.Bottom > 0)
CreateBorderSide(0, Control.Frame.Size.Height-customPicker.Border.Bottom, Control.Frame.Size.Width, customPicker.Border.Bottom, color);
}
protected virtual void RemoveBorders()
{
//remove all previously added borders again (if any)
foreach (var layer in Control.Layer.Sublayers)
{
if (layer.Name != null && layer.Name.Contains("customBorder"))
layer.RemoveFromSuperLayer();
}
}
protected virtual void CreateBorderSide(double x,double y,double width,double height,CGColor color)
{
CALayer borderLayer = new CALayer();
borderLayer.Frame = new CGRect(x, y, width, height);
borderLayer.BackgroundColor = color;
borderLayer.Name = "customBorder";
Control.Layer.AddSublayer(borderLayer);
}
private UIView leftView, rightView;
protected virtual async void UpdateLeftIcon(CustomDatePicker customPicker)
{
if(customPicker.LeftIcon == null)
{
Control.LeftViewMode = UIKit.UITextFieldViewMode.Never;
Control.LeftView = null;
leftView = null;
}
else
{
leftView = await CreateSideView(customPicker.LeftIcon, customPicker.LeftIconSize);
Control.LeftView = leftView;
Control.LeftViewMode = UIKit.UITextFieldViewMode.Always;
}
}
protected virtual async void UpdateRightIcon(CustomDatePicker customPicker)
{
if (customPicker.RightIcon == null)
{
Control.RightViewMode = UIKit.UITextFieldViewMode.Never;
Control.RightView = null;
rightView = null;
}
else
{
rightView = await CreateSideView(customPicker.RightIcon, customPicker.RightIconSize, false);
Control.RightView = rightView;
Control.RightViewMode = UIKit.UITextFieldViewMode.Always;
}
}
protected virtual async Task<UIView> CreateSideView(ImageSource icon, int size, bool isLeft = true)
{
UIImage leftIconImage = await IosImageHelper.GetUIImageFromImageSourceAsync(icon);
CGSize iconSize = leftIconImage.Size;
if (size > -1)
iconSize = new CGSize((float)size, (float)size);
UIView paddingView = new UIView(new CGRect(0, 0, iconSize.Width + 8, iconSize.Height+8));
UIImageView sideView = new UIImageView(new CGRect(isLeft?8:0, 4, iconSize.Width, iconSize.Height));
sideView.Image = leftIconImage;
paddingView.AddSubview(sideView);
return paddingView;
}
}
So for me it highly seems like setting Control.LeftView or Control.RightView does not affect the actual frame size of the UIControl + it does not update the Xamarin Forms view bounds.
That's why I added the manual "calculation" of the border in ApplyBorder, but as it changes the frame of the iOS native control correctly, it seems to not update the information in the Xamarin Forms Control?
I guess the solution is very simple, somehow to notify the Xamarin Forms Control to remeasure or something but I just can't figure it out right now.
Here are screenshots showing what I mean by not keeping the distance and sliding into the control.
Landscape:
Portrait: