UILabel with padding in Xamarin.iOS?
Asked Answered
G

6

12

I'm trying to create a UILabel with padding in my Xamarin.iOS app. The most popular solution in native Objective-C apps is overriding drawTextInRect:

- (void)drawTextInRect:(CGRect)rect {
    UIEdgeInsets insets = {0, 5, 0, 5};
    return [super drawTextInRect:UIEdgeInsetsInsetRect(rect, insets)];
}

As simple as this seems, I can't quite figure out how to translate it to C#. Here's my best stab at it:

internal class PaddedLabel : UILabel
{
    public UIEdgeInsets Insets { get; set; }

    public override void DrawText(RectangleF rect)
    {
        var padded = new RectangleF(rect.X + Insets.Left, rect.Y, rext.Width + Insets.Left + Insets.Right, rect.Height);

        base.DrawText(padded);
    }
}

This does seem to move the label's text, but it doesn't resize the label.

I think the main issue is that I can't find the Xamarin equivalent of UIEdgeInsetsInsetRect.

Any suggestions?

Garcon answered 16/1, 2014 at 14:28 Comment(0)
L
18

The C# equivalent of the ObjC function UIEdgeInsetsInsetRect is a instance method of UIEdgeInsets named InsetRect and it's not identical to your RectangleF calculations (which is likely your problem).

To use it you can do:

public override void DrawText(RectangleF rect)
{
    base.DrawText (Insets.InsetRect (rect));
}
Levantine answered 16/1, 2014 at 14:36 Comment(6)
Thanks! This appears to be the correct translation of the Objective-C version. It shifts the text over as expected, but the text is clipped to the right. In other words, the UILabel doesn't resize to accommodate the insets. That might be a separate issue, though.Garcon
That code only change the drawing, so the UILabel is not aware of the inset (and won't change it's behaviour).Levantine
That makes sense. Any suggestions as to what I should look at to take the insets into consideration when determining the width of the label? I'd like it to resize dynamically if possible.Garcon
I'm not sure what exact result you're looking for - but there's many similar questions on SO, e.g. https://mcmap.net/q/910634/-is-uilabel-inset-effect-possible-duplicate/220643 that you might want to look into (and convert to C#). If nothing match your need then ask another (more precise, e.g. picture) question for help.Levantine
Thanks--I posted a follow-up with a screen shot: #21167726Garcon
This seems to be depreciated now, see my answer belowBautzen
A
7

You have to override both DrawText and TextRectForBounds. If you don't override TextRectForBounds, the text will be clipped. Actually, you override this method to compensate the space which is occupied by padding and ask iOS to draw the text in a bigger rectangle.

public partial class LabelWithBorder
    : UILabel
{
    private UIEdgeInsets EdgeInsets = new UIEdgeInsets(5, 5, 5, 5);
    private UIEdgeInsets InverseEdgeInsets = new UIEdgeInsets(-5, -5, -5, -5);

    public LabelWithBorder(IntPtr handle) : base(handle)
    {
    }

    public override CoreGraphics.CGRect TextRectForBounds(CoreGraphics.CGRect bounds, nint numberOfLines)
    {
        var textRect = base.TextRectForBounds(EdgeInsets.InsetRect(bounds), numberOfLines);
        return InverseEdgeInsets.InsetRect(textRect);
    }

    public override void DrawText(CoreGraphics.CGRect rect)
    {
        base.DrawText(EdgeInsets.InsetRect(rect));
    }
}
Attlee answered 7/3, 2018 at 13:59 Comment(1)
This worked great, except I had to add a constructor that accepts a CGRect and passes it to the base constructor of that type.Distort
C
3

Rather than overriding DrawText() in the subclass of UILabel, override it's intrinsic content size. This way auto-layout takes the padding into consideration. For example here's my derived class of UILabel:

public class PaddedLabel : UILabel
{
    private readonly float _top;
    private readonly float _left;
    private readonly float _right;
    private readonly float _bottom;

    public PaddedLabel(float top, float left, float right, float bottom)
    {
        _top = top;
        _left = left;
        _right = right;
        _bottom = bottom;
    }

    public override CGSize IntrinsicContentSize => new CGSize(
        base.IntrinsicContentSize.Width + _left + _right,
        base.IntrinsicContentSize.Height + _top + _bottom
    );
}
Czarist answered 20/10, 2016 at 18:49 Comment(0)
T
1

I have created a generic Padding UIView class that wraps any IOS UI element that is derived from UIView.

Basically it nests the desired UIView into another view and takes care of all the padding work.

usage:

var myPaddedView = new PaddedUIView<UILabel>();
myPaddedView.Frame = TheActualFrame;
myPaddedView.Padding = 15f
myPaddedView.NestedView.Text = "Hello padded world";  // all the label Properties are available without side effects

Here is the class:

public class PaddedUIView<T>: UIView where T : UIView, new()
{
    private float _padding;
    private T _nestedView;

    public PaddedUIView()
    {
        Initialize();
    }

    public PaddedUIView(RectangleF bounds)
        : base(bounds)
    {
        Initialize();
    }

    void Initialize()
    {   
        if(_nestedView == null)
        { 
            _nestedView = new T();
            this.AddSubview(_nestedView);
        }
        _nestedView.Frame =  new RectangleF(_padding,_padding,Frame.Width - 2 * _padding, Frame.Height - 2 * _padding);
    }

    public T NestedView
    { 
        get { return _nestedView; }
    }

    public float Padding
    {
        get { return _padding; }
        set { if(value != _padding) { _padding = value; Initialize(); }}
    }

    public override RectangleF Frame
    {
        get { return base.Frame; }
        set { base.Frame = value; Initialize(); }
    }
}
Tartu answered 22/4, 2014 at 22:45 Comment(1)
public override RectangleF Frame , this method doesnt resolveQuarterdeck
B
0

The correct way to do this seems to have slightly changed since the original answer was given, and it took me awhile to figure out the new correct syntax. Here it is for anyone else who stumbles across this. This code would put padding of 5 on both the left and right sides of the view. This is in Xamarin.iOS

public override void DrawText(CGRect rect)
{
     rect.X = 5; 
     rect.Width = rect.Width - 10; // or whatever padding settings you want
     base.DrawText(AlignmentRectInsets.InsetRect(rect));
}
Bautzen answered 1/12, 2017 at 19:59 Comment(0)
H
0

poupou's solution marked as the best answer doesn't work. I found another way to add padding to label.

    public class PaddedLabel : UILabel
    {
        public PaddedLabel(IntPtr intPtr) : base(intPtr)
        {
            TextAlignment = UITextAlignment.Center;
        }

        public override CGSize IntrinsicContentSize
        {
            get
            {
                var size = base.IntrinsicContentSize;

                return new CGSize(size + 16, size);
            }
        }
    }
Hottempered answered 24/9, 2021 at 9:5 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Scrutiny

© 2022 - 2024 — McMap. All rights reserved.