George
George

Reputation: 2997

Xamarin forms IOS Buttons looking distorted

I have the following XAML code below :

    <StackLayout
        Grid.Row="2"
        Orientation="Horizontal"
        VerticalOptions="End"
        Margin="0,0,0,20"
        Spacing="28">

        <Button
            x:Name="SignInButton"
            Visual="Material"
            Padding="5"
            Margin="10,0,0,0"
            Style="{DynamicResource ButtonSecondary}"
            HorizontalOptions="FillAndExpand"              
            Text="Sign In"
            Clicked="SignInButton_Clicked"/>

        <Button
            x:Name="JoinUsButton"
            Visual="Material"
            Padding="5"
            Margin="0,0,10,0"
            Style="{DynamicResource ButtonPrimary}"
            HorizontalOptions="FillAndExpand"
            VerticalOptions="End"
            Text="Join Us"
            Clicked="JoinUsButton_Clicked"/>
    </StackLayout>

The dynamic resources currently stored in the App.xaml file are as follows :

<Style x:Name="ButtonSecondary" x:Key="ButtonSecondary" TargetType="Button" ApplyToDerivedTypes="True">
    <Setter Property="BackgroundColor"
            Value="{DynamicResource SecondaryColor}" />
    <Setter Property="TextColor"
            Value="{DynamicResource PrimaryTextColor}" />
    <Setter Property="BorderWidth"
            Value="1" />
    <Setter Property="BorderColor"
            Value="{DynamicResource SecondaryBorderColor}" />
    <Setter Property="CornerRadius"
            Value="50" />            
</Style>

However, when I run the app on iOS the buttons look like the image below.

iPhone 12 Pro Max

However, on the android device, the buttons look like the image below :

enter image description here

Upvotes: 1

Views: 217

Answers (2)

Bijington
Bijington

Reputation: 3751

While I can't see the exact issue you are seeing with the distortion I do see an inconsistency between platforms. This ultimately comes down to how the individual platforms render the CornerRadius property. Android will limit it to what is visibly sensible (basically half the height/width, whichever is smaller) whereas iOS will just do as you ask.

This image shows on the left what I currently see, the middle is my second solution and the right is my first solution.

Issue I see plus solutions

My possible solutions are:

Attach a Behavior

public class RoundCornerBehavior : Behavior<Button>
{
    protected override void OnAttachedTo(Button button)
    {
        button.SizeChanged += OnSizeChanged;
        base.OnAttachedTo(button);
    }

    protected override void OnDetachingFrom(Button button)
    {
        button.SizeChanged -= OnSizeChanged;
        base.OnDetachingFrom(button);
    }

    private void OnSizeChanged(object sender, EventArgs e)
    {
        var button = (Button) sender;
        button.CornerRadius = (int)Math.Min(button.Width, button.Height) / 2;
    }

    public static readonly BindableProperty AttachBehaviorProperty =
            BindableProperty.CreateAttached("AttachBehavior", typeof(bool), typeof(RoundCornerBehavior), false, propertyChanged: OnAttachBehaviorChanged);

    public static bool GetAttachBehavior(BindableObject view)
    {
        return (bool)view.GetValue(AttachBehaviorProperty);
    }

    public static void SetAttachBehavior(BindableObject view, bool value)
    {
        view.SetValue(AttachBehaviorProperty, value);
    }

    static void OnAttachBehaviorChanged(BindableObject view, object oldValue, object newValue)
    {
        if (!(view is Button button))
        {
            return;
        }

        var attachBehavior = (bool)newValue;
        if (attachBehavior)
        {
            button.Behaviors.Add(new RoundCornerBehavior());
        }
        else
        {
            var toRemove = button.Behaviors.FirstOrDefault(b => b is RoundCornerBehavior);
            if (toRemove != null)
            {
                button.Behaviors.Remove(toRemove);
            }
        }
    }
}

Then simply attach in your style:

<Setter Property="roundButton:RoundCornerBehavior.AttachBehavior" Value="true" />

I would suggest writing some kind of Behavior to provide the sensible CornerRadius which would essentially take the Width and Height properties of the control and simply set the CornerRadius to half the smallest value. I will see if I can knock something up to provide a concrete example shortly.

The nice result of this approach will allow you to continue to define the controls as you were previously and keep the logic self contained in the attached behavior.

Sub class button

An alternative would be to sub class Button and created your own RoundedButton that could do the same as the Behavior approach. Then

public class RoundedButton : Button
{
    protected override void OnSizeAllocated(double width, double height)
    {
        base.OnSizeAllocated(width, height);

        this.CornerRadius = (int)Math.Min(width, height) / 2;
    }
}

Upvotes: 0

Lucas Zhang
Lucas Zhang

Reputation: 18861

Cauuse : In iOS , if you want to achieve the effect like the above image which you get in Android , you need to set the CornerRadius as half of its HeightRequest .

Solution

Option 1

If the size of the button is always a fixed value , you just need to set the HeightRequest in the style

<Style x:Name="ButtonSecondary" x:Key="ButtonSecondary" TargetType="Button" ApplyToDerivedTypes="True">
            <Setter Property="BackgroundColor"
            Value="{DynamicResource SecondaryColor}" />
            <Setter Property="TextColor"
            Value="{DynamicResource PrimaryTextColor}" />
            <Setter Property="BorderWidth"
            Value="1" />
            <Setter Property="BorderColor"
            Value="{DynamicResource SecondaryBorderColor}" />
            <Setter Property="CornerRadius"
            Value="25" />
            <Setter Property="HeightRequest"
            Value="50" />  // double of CornerRadius
        </Style>

Option 2 :

If the size of Button will change in runtime , you could use Custom Renderer to set the CornerRadius in iOS platform .

in Forms

create a custom button

public class MyButton:Button
{
}

in iOS

using Foundation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UIKit;

using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

using App6;
using App6.iOS;
using System.ComponentModel;

[assembly:ExportRenderer(typeof(MyButton),typeof(MyButtonRenderer))]
namespace App6.iOS
{
    public  class MyButtonRenderer:ButtonRenderer
    {
       
        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            if(e.PropertyName=="Height")
            {
                var height = Element.Height;

                Control.Layer.MasksToBounds = true;
                Control.Layer.BorderColor = UIColor.Black.CGColor;
                Control.Layer.CornerRadius = (nfloat)(height / 2.0);
                Control.Layer.BorderWidth = (nfloat)0.5;

            }

        }
    }
}

in xaml

<local:MyButton
            x:Name="SignInButton"
            Visual="Material"
            Padding="5"
            Margin="10,0,0,0"
            Style="{DynamicResource ButtonSecondary}"
            HorizontalOptions="FillAndExpand"              
            Text="Sign In"
           />

enter image description here

Upvotes: 3

Related Questions