Search code examples
c#iosxamarin.formsuitextviewattributedstring

How do I get line heights of UITextView when using UIStringAttributes in Xamarin Forms iOS?


I have a problem with setting UIStringAttributes.Font.

Before setting it, the system defaults to Helvetica 12pt, with a line height of 13.8.

This line height seems accurate as shown by the image here:

enter image description here

enter image description here

However if I set the UIStringAttributes.Font property with Arial 17pt, the line height of 18.99 seems inaccurate as shown here:

enter image description here

enter image description here

enter image description here

My code is as follows:

void SetAttributes(string text)
{
  NSMutableAttributedString astr = new NSMutableAttributedString();
  UIStringAttributes attrs;
  NSAttributedString str;
  CheckBox cb;
  string[] lines;
  string line;

  lines = text.Split("\n", StringSplitOptions.None);
  for (int i = 0; i < lines.Count; i++)
  {
            cb = _checks[i];
            line = lines[i];

    if (i + 1 < _checks.Count)
      line += "\n";

    attrs = new UIStringAttributes();
    attrs.ForegroundColor = cb.Color;

    ////////////////////////////////
    //COMMENTING THIS LINE OUT ALLOWS A CORRECT
    //LINEHEIGHT TO BE GIVEN IN DRAW METHOD
    attrs.Font = Control.Font;
    ////////////////////////////////

    str = new NSAttributedString(line, attrs);

    astr.Append(str);
        }

  Control.AttributedText = astr;
}

public override void Draw(CGRect rect)
{
  double baseline = editor.Padding.Top + Control.Font.Ascender;
  double movingBaseline = baseline + BOTTOM_MARGIN;
  double scrollY = Control.ContentOffset.Y;

  double _lineHeight = Control.Font.LineHeight;

  //get graphics context
  using (CGContext g = UIGraphics.GetCurrentContext())
  {
    for (int i = 0; i < _lines.Count; i++)
    {
        CGPath path = new CGPath();

      //set up drawing attributes
      g.SetLineWidth(0.5f);
      UIColor.Black.SetStroke();

      //add lines to the touch points
      path.AddLines(new CGPoint[] { new CGPoint(0, movingBaseline - scrollY),
                  new CGPoint(300, movingBaseline - scrollY) });

      //add geometry to graphics context and draw it
      g.AddPath(path);

      g.DrawPath(CGPathDrawingMode.Stroke);

      //tying this to the control.baseline hopefully should help
      //to avoid accumulating rounding errors
      movingBaseline = baseline + BOTTOM_MARGIN + (_lineHeight * (double)(i + 1));
    }
  }


  base.Draw(rect);

}

I've tried:

  • 1 other font, Consolas -> height still wrong
  • NSString(text).StringSize -> gives height of 19 which is wrong
  • Setting ParagraphStyle.LineHeight in UIStringAttributes so I can control the height, but unfortunately it is not settable because Xamarin hasn't implemented a setter

Any ideas?


Solution

  • I believe I may have solved it, or at least have a workaround with:

    NSMutableParagraphStyle para = new NSMutableParagraphStyle();
    
    para.LineSpacing = LINESPACING;
    attrs.ParagraphStyle = para;
    

    Setting the Font on the attributes was likely adding a default linespacing which I had no visibility of. If I explicitly set it myself, at least I know what value is being used and can calculate the line height as:

    lineHeight = Control.Font.LineHeight + LINESPACING
    

    This seems to work for now. Will report back if it starts misbehaving again.