Xamarin формирует кнопки IOS, выглядящие искаженными

#xaml #xamarin.forms #xamarin.ios

#xaml #xamarin.forms #xamarin.ios

Вопрос:

У меня есть следующий код XAML ниже :

     <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>
 

Динамические ресурсы, которые в настоящее время хранятся в файле App.xaml, следующие :

 <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>
 

Однако, когда я запускаю приложение на iOS, кнопки выглядят так, как показано на рисунке ниже.

iPhone 12 Pro Max

Однако на устройстве Android кнопки выглядят так, как показано на рисунке ниже :

введите описание изображения здесь

Комментарии:

1. Работает ли это сейчас?

Ответ №1:

Осторожно: в iOS, если вы хотите добиться эффекта, подобного приведенному выше изображению, которое вы получаете в Android, вам нужно установить cornerRadius как половину его HeightRequest .

Решение

Вариант 1

Если размер кнопки всегда имеет фиксированное значение, вам просто нужно задать HeightRequest в стиле

 <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>
 

Вариант 2 :

Если размер кнопки изменится во время выполнения, вы можете использовать пользовательский рендерер для настройки CornerRadius на платформе iOS.

в формах

создайте пользовательскую кнопку

 public class MyButton:Button
{
}
 

в 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;

            }

        }
    }
}
 

в xaml

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

введите описание изображения здесь

Комментарии:

1. @George Вы могли бы проверить мой ответ.

Ответ №2:

Хотя я не вижу точной проблемы, которую вы видите с искажением, я вижу несоответствие между платформами. В конечном итоге это сводится к тому, как отдельные платформы отображают CornerRadius свойство. Android ограничит его тем, что является явно разумным (в основном половиной высоты / ширины, в зависимости от того, что меньше), тогда как iOS просто сделает так, как вы просите.

На этом изображении слева показано то, что я вижу в данный момент, середина — мое второе решение, а справа — мое первое решение.

Проблема, которую я вижу, плюс решения

Мои возможные решения:

Прикрепите поведение

 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);
            }
        }
    }
}
 

Затем просто прикрепите в своем стиле:

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

Я бы предложил написать какой Behavior -нибудь способ обеспечения разумного CornerRadius , который, по сути, использовал бы свойства Width и Height элемента управления и просто установил значение CornerRadius в половину наименьшего значения. Я посмотрю, смогу ли я что-нибудь придумать, чтобы привести конкретный пример в ближайшее время.

Хороший результат такого подхода позволит вам продолжать определять элементы управления, как вы делали это ранее, и сохранить логику самодостаточной в прикрепленном поведении.

Кнопка подкласса

Альтернативой было бы создать подкласс Button и создать свой собственный RoundedButton , который мог бы делать то же самое Behavior , что и подход. Затем

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

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

Комментарии:

1. Я не уверен, почему этот ответ был отмечен, и я просто пытаюсь обдумать ваш ответ

2. @George я тоже, я ценю, что не привел пример, но сейчас я добавил 2 🙂