如何让文本换行并在 Xamarin Forms iOS 自定义渲染器下方添加另一行?

How do I get text to wrap and add another line below in a Xamarin Forms iOS custom renderer?

我正在创建自定义渲染器以在图像上以半透明的白色 space 显示标题和说明。如果标题比图像宽,则标题应换行到第二行,如果有空间,下面可能会显示可选的描述。在 Android 中,我可以使用 StaticLayout 来做到这一点:

            // Create text rectangle
            var height = Height / 3;
            canvas.Save();
            canvas.ClipRect(0, Height - height, Width, Height);
            canvas.DrawARGB(191, 255, 255, 255);

            canvas.Restore();
            var item = ((ImageTile) Element).Item;
            var textSize = (height - 15) / 2;

            var textPaint = new TextPaint
            {
                StrokeWidth = 5,
                TextSize = textSize,
                FakeBoldText = true,
            };
            if (Build.VERSION.SdkInt >= BuildVersionCodes.Honeycomb)
                SetLayerType(LayerType.Software, textPaint);
            textPaint.SetStyle(Paint.Style.Fill);
            textPaint.Color = global::Android.Graphics.Color.Black;
            // init StaticLayout for text
            var titleLayout = new StaticLayout(
                item.Title, textPaint, Width - 10, Android.Text.Layout.Alignment.AlignNormal, 1.0f, 0.0f, false);
            canvas.Translate(5, Height - height + 5);
            titleLayout.Draw(canvas);
            canvas.Restore();

            textPaint = new TextPaint
            {
                StrokeWidth = 4,
                TextSize = textSize - 10,
            };
            if (Build.VERSION.SdkInt >= BuildVersionCodes.Honeycomb)
                SetLayerType(LayerType.Software, textPaint);
            var descLayout = new StaticLayout(
                item.Description, textPaint, Width - 10, Android.Text.Layout.Alignment.AlignNormal, 1.0f, 0.0f, false);
            canvas.Translate(5, Height - height + titleLayout.Height + 5);
            descLayout.Draw(canvas);
            canvas.Restore();

在 iOS 我正在使用 CATextLayers,但我无法让文本换行,即使我定义了框架并将 Wrapped 设置为 true 并将 TextTruncationMode 设置为 None。我也不知道如何获得 tilteLayer 的实际高度,所以我可以将 descLayer 放在它下面。这是我目前所拥有的,它在没有换行的情况下将标题和描述绘制在彼此之上。

                var textLayer = new CALayer();
                var textRec = new CGRect(0, element.HeightRequest - textheight, element.WidthRequest,
                    textheight);
                textLayer.Frame = textRec;
                var backgroundcolor = Color.FromRgba(255, 255, 255, .25).ToCGColor();
                textLayer.BackgroundColor = backgroundcolor;
                Control.Layer.AddSublayer(textLayer);
                var titleLayer = new CATextLayer
                {
                    String = element.Item.Title,
                    ForegroundColor = Color.Black.ToCGColor(),
                    FontSize = 14,
                    Wrapped = true,
                    TextTruncationMode = CATextLayerTruncationMode.None,
                    TextAlignmentMode = CATextLayerAlignmentMode.Left,
                    //Bounds = new CGRect(2, element.HeightRequest - textheight + 2, element.WidthRequest - 4,
                    //    textheight - 4),
                };
                var titleRec = new CGRect(2, element.HeightRequest - textheight + 2, element.WidthRequest - 4,
                    textheight - 4);
                titleLayer.Frame = titleRec;
                Control.Layer.AddSublayer(titleLayer);
                var descLayer = new CATextLayer
                {
                    String = element.Item.Description,
                    ForegroundColor = Color.Black.ToCGColor(),
                    FontSize = 12,
                    Wrapped = true,
                    TextTruncationMode = CATextLayerTruncationMode.None,
                };
                var descRec = new CGRect(2, element.HeightRequest - textheight + 2, element.WidthRequest - 4,
                    textheight - 4);
                descLayer.ContentsRect = descRec;
                Control.Layer.AddSublayer(descLayer);

为什么不试试 autoLayout?您想要在原始图像上添加背景视图和两种类型的文本。 CALayer 可以达到你的效果,但它不能使用autoLayout,所以你需要使用硬代码(计算文本高度和图层位置)来构造它。你也说

I am unable to get the text to wrap. And I also don't know how to get the actual height of the tilteLayer so I can position the descLayer below it.

在图像的自定义渲染器中,由于此控件尚未渲染,因此其 FrameHeightRequest 也是未知的。这样你就不会得到正确的框架,层也不会,所以文本不会显示。我认为最好的方法是使用 AutoLayout:

// Create a view to hold content just like your textLayer 
UIView bgView = new UIView();
bgView.BackgroundColor = UIColor.FromRGBA(1, 1, 1, 0.25f);
bgView.TranslatesAutoresizingMaskIntoConstraints = false;
Control.AddSubview(bgView);

bgView.LeadingAnchor.ConstraintEqualTo(Control.LeadingAnchor).Active = true;
bgView.TopAnchor.ConstraintGreaterThanOrEqualTo(Control.TopAnchor).Active = true;
bgView.TrailingAnchor.ConstraintEqualTo(Control.TrailingAnchor).Active = true;
bgView.BottomAnchor.ConstraintEqualTo(Control.BottomAnchor).Active = true;

UILabel titleLabel = new UILabel();
bgView.AddSubview(titleLabel);
titleLabel.TranslatesAutoresizingMaskIntoConstraints = false;
// Set this property to 0, then your label will move to several lines if your text is too large.
titleLabel.Lines = 0;
titleLabel.Font = UIFont.SystemFontOfSize(14);
titleLabel.Text = Element.Item.Title;

titleLabel.LeadingAnchor.ConstraintEqualTo(bgView.LeadingAnchor).Active = true;
titleLabel.TopAnchor.ConstraintEqualTo(bgView.TopAnchor).Active = true;
titleLabel.TrailingAnchor.ConstraintEqualTo(bgView.TrailingAnchor).Active = true;
// This constraint will show the titleLabel's content at high priority. It means show the descLabel if the image has enough place.
titleLabel.SetContentHuggingPriority(249, UILayoutConstraintAxis.Vertical);

UILabel descLabel = new UILabel();
bgView.AddSubview(descLabel);
descLabel.TranslatesAutoresizingMaskIntoConstraints = false;
descLabel.Lines = 0;
descLabel.Text = Element.Item.Description;
descLabel.Font = UIFont.SystemFontOfSize(12);

descLabel.LeadingAnchor.ConstraintEqualTo(bgView.LeadingAnchor).Active = true;
descLabel.TopAnchor.ConstraintEqualTo(titleLabel.BottomAnchor).Active = true;
descLabel.TrailingAnchor.ConstraintEqualTo(bgView.TrailingAnchor).Active = true;
descLabel.BottomAnchor.ConstraintEqualTo(bgView.BottomAnchor).Active = true;

这样bgView会根据titleLabel和descLabel展开高度。最大高度将是原始图像的高度。此外,titleLabel 将根据其内容自动计算其大小。此外,如果房间允许,descLabel 将始终位于 titleLabel 下方。

您可以调整这些限制以满足您自己的要求。