【问题标题】:iOS custom render not apply corner radiusiOS自定义渲染不应用角半径
【发布时间】:2020-07-11 02:56:07
【问题描述】:

我创建了一个基于谷歌搜索的自定义渲染,它允许Frame 完全控制其所有角半径(左、上、右、下)。但是,即使它设置为60, 60, 0, 0,我也认为它不尊重这些标准。在 Android 上,它按预期工作。

iOS 屏幕:

安卓:

在调试时,我可以清楚地看到自定义渲染适当地获取了数据,即角半径被相应地传递下来。这是自定义渲染器代码:

public override void LayoutSubviews()
{
   base.LayoutSubviews();

   UpdateCornerRadius();
   UpdateShadow();
}

protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
{
   base.OnElementChanged(e);

   if (e.NewElement == null)
       return;

   UpdateShadow();
   UpdateCornerRadius();
}

protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
   base.OnElementPropertyChanged(sender, e);

   if (e.PropertyName == nameof(MultiCornerFrame.CornerRadius) || e.PropertyName == nameof(MultiCornerFrame))
   {
       UpdateCornerRadius();
   }

   if(e.PropertyName == nameof(MultiCornerFrame.Elevation))
   {
       UpdateShadow();
   }
}

private void UpdateShadow()
{

   var materialFrame = (MultiCornerFrame) Element;

   // Update shadow to match better material design standards of elevation
   Layer.ShadowRadius = materialFrame.Elevation;
   Layer.ShadowColor = UIColor.Gray.CGColor;
   Layer.ShadowOffset = new CGSize(2, 2);
   Layer.ShadowOpacity = 0.80f;
   Layer.ShadowPath = UIBezierPath.FromRect(Layer.Bounds).CGPath;
   Layer.MasksToBounds = false;

}

// A very basic way of retrieving same one value for all of the corners
private double RetrieveCommonCornerRadius(CornerRadius cornerRadius)
{
   var commonCornerRadius = cornerRadius.TopLeft;
   if (commonCornerRadius <= 0)
   {
       commonCornerRadius = cornerRadius.TopRight;
       if (commonCornerRadius <= 0)
       {
           commonCornerRadius = cornerRadius.BottomLeft;
           if (commonCornerRadius <= 0)
           {
               commonCornerRadius = cornerRadius.BottomRight;
           }
       }
   }

   return commonCornerRadius;
}

private UIRectCorner RetrieveRoundedCorners(CornerRadius cornerRadius)
{
   var roundedCorners = default(UIRectCorner);

   if (cornerRadius.TopLeft > 0)
   {
       roundedCorners |= UIRectCorner.TopLeft;
   }

   if (cornerRadius.TopRight > 0)
   {
       roundedCorners |= UIRectCorner.TopRight;
   }

   if (cornerRadius.BottomLeft > 0)
   {
       roundedCorners |= UIRectCorner.BottomLeft;
   }

   if (cornerRadius.BottomRight > 0)
   {
       roundedCorners |= UIRectCorner.BottomRight;
   }

   return roundedCorners;
}

private void UpdateCornerRadius()
{
   var cornerRadius = (Element as MultiCornerFrame)?.CornerRadius;
   if (!cornerRadius.HasValue)
   {
       return;
   }

   var roundedCornerRadius = RetrieveCommonCornerRadius(cornerRadius.Value);
   if (roundedCornerRadius <= 0)
   {
       return;
   }

   var roundedCorners = RetrieveRoundedCorners(cornerRadius.Value);

   var path = UIBezierPath.FromRoundedRect(Bounds, roundedCorners, new CGSize(roundedCornerRadius, roundedCornerRadius));
   var mask = new CAShapeLayer { Path = path.CGPath};
   NativeView.Layer.Mask = mask;

}

从中调用它的 XAML 如下所示:

<customcontrols:MultiCornerFrame Elevation="48" CornerRadius="60, 60, 0, 0"  Grid.Row="1" BackgroundColor="White" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">

如果我需要发布更多代码,请告诉我

【问题讨论】:

  • 您好,您是否检查过在渲染器代码中添加断点以检查UpdateCornerRadius(); 是否运行。

标签: c# xaml xamarin.forms custom-renderer


【解决方案1】:

检查后,没有在OnElementChanged 方法中更新CornerRadius。我认为这些更改将在 Frame Renderer 创建之前调用,然后这将不起作用。您可以尝试在 LayoutSublayersOfLayer 方法中执行此操作。

我通过创建一个空的 CustomFrame 进行了测试:

public class CustomFrame : Frame{}

Xaml 中:

<StackLayout Padding="20">
    <!-- Place new controls here -->
    <Label Text="Welcome to Xamarin.Forms!" 
       HorizontalOptions="Center"
       VerticalOptions="CenterAndExpand" />
    <appframerenderer:CustomFrame BackgroundColor="Accent"
                                  HorizontalOptions="FillAndExpand"
                                  VerticalOptions="FillAndExpand"/>
</StackLayout>

最后,CustomFrameRenderer编码如下:

[assembly: ExportRenderer(typeof(CustomFrame), typeof(CustomFrameRenderer))]
namespace AppFrameRenderer.iOS
{
    public class CustomFrameRenderer : FrameRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
        {
            base.OnElementChanged(e);
        }

        public override void LayoutSublayersOfLayer(CALayer layer)
        {
            base.LayoutSublayersOfLayer(layer);
            var path = UIBezierPath.FromRoundedRect(Bounds, UIRectCorner.TopLeft | UIRectCorner.TopRight, new CGSize(50, 50));
            var maskLayer = new CAShapeLayer { Frame = Bounds, Path = path.CGPath };
            layer.Mask = maskLayer;
        }
    }
}

效果符合预期:

所以,这里可以将UpdateShadow(); UpdateCornerRadius();移到LayoutSublayersOfLayer方法试试。

【讨论】:

  • 感谢您的回答,我想我也能够找到一种高度相似的方法。我会把你的标记为正确,因为它也有效
  • @SomeStudent 谢谢,我也投票赞成你的答案,这也是一个很好的解决方案:)
【解决方案2】:

所以经过一些调整后,我认为这与我们设置遮罩的时间和/或我们到底要遮罩的内容有关。我决定创建一个名为 UpdateCorners 的方法,如下所示:

 private void UpdateCorners()
        {
            var path = UIBezierPath.FromRoundedRect(this.Bounds, UIRectCorner.TopLeft | UIRectCorner.TopRight, new CGSize(60, 60));
            //var mask = new CAShapeLayer();
            //mask.Path = path.CGPath;
            //this.Layer.Mask = mask;
            //right side
            this.ClipsToBounds = true;
            this.Layer.CornerRadius = 60;
            this.Layer.MaskedCorners = CACornerMask.MinXMinYCorner | CACornerMask.MaxXMinYCorner;  

             var materialFrame = (MultiCornerFrame) Element;
            // Update shadow to match better material design standards of elevation
            Layer.ShadowRadius = materialFrame.Elevation;
            Layer.ShadowColor = UIColor.Gray.CGColor;
            Layer.ShadowOffset = new CGSize(2, 2);
            Layer.ShadowOpacity = 0.80f;
            Layer.ShadowPath = UIBezierPath.FromRect(Layer.Bounds).CGPath;
            Layer.MasksToBounds = false;
        }

注意这个方法是如何更新阴影的,因为某些原因,当它在它自己的外部方法中时它没有这样做。现在我曾经调用 UpdateCornerRadius,我只是用新方法替换它。

【讨论】:

    猜你喜欢
    • 2021-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-22
    • 1970-01-01
    • 1970-01-01
    • 2022-07-18
    • 2015-03-29
    相关资源
    最近更新 更多