protected override Size MeasureOverride(Size size) { // Size used to compare with the previous one. We don't want to use this one for the calculation. var ceiledNewSize = new CGSize(Math.Ceiling(size.Width), Math.Ceiling(size.Height)); var hasSameDesiredSize = !_measureInvalidated && _previousAvailableSize != null && _previousDesiredSize.Width == ceiledNewSize.Width && _previousDesiredSize.Height == ceiledNewSize.Height; var isSingleLineNarrower = !_measureInvalidated && _previousAvailableSize != null && _previousDesiredSize.Width <= ceiledNewSize.Width && _previousDesiredSize.Height == ceiledNewSize.Height; if (hasSameDesiredSize || isSingleLineNarrower) { return(_previousDesiredSize); } else { _previousAvailableSize = size; _measureInvalidated = false; UpdateTypography(); var padding = Padding; // available size considering padding size = size.Subtract(padding); var result = LayoutTypography(size); if (result.Height == 0) // this can happen when Text is null or empty { // This measures the height correctly, even if the Text is null or empty // This matches Windows where empty TextBlocks still have a height (especially useful when measuring ListView items with no DataContext) var font = UIFontHelper.TryGetFont((float)FontSize, FontWeight, FontStyle, FontFamily); result = (Text ?? NSString.Empty).StringSize(font, size); } result = result.Add(padding); return(_previousDesiredSize = new CGSize(Math.Ceiling(result.Width), Math.Ceiling(result.Height))); } }
protected override Size MeasureOverride(Size size) { // `size` is used to compare with the previous one `_previousDesiredSize` // We need to apply `Math.Ceiling` to compare them correctly var isSameOrNarrower = !_measureInvalidated && _previousAvailableSize != null && _previousDesiredSize.Width <= Math.Ceiling(size.Width) && _previousDesiredSize.Height == Math.Ceiling(size.Height); if (isSameOrNarrower) { return(_previousDesiredSize); } else { _previousAvailableSize = size; _measureInvalidated = false; UpdateTypography(); var padding = Padding; // available size considering padding size = size.Subtract(padding); var result = LayoutTypography(size); if (result.Height == 0) // this can happen when Text is null or empty { // This measures the height correctly, even if the Text is null or empty // This matches Windows where empty TextBlocks still have a height (especially useful when measuring ListView items with no DataContext) var font = UIFontHelper.TryGetFont((float)FontSize, FontWeight, FontStyle, FontFamily); #pragma warning disable BI1234 // error BI1234: 'UIStringDrawing.StringSize(string, UIFont, CGSize)' is obsolete: 'Starting with ios7.0 use NSString.GetBoundingRect (CGSize, NSStringDrawingOptions, UIStringAttributes, NSStringDrawingContext) instead.' result = (Text ?? NSString.Empty).StringSize(font, size); #pragma warning restore BI1234 } result = result.Add(padding); return(_previousDesiredSize = new Size(Math.Ceiling(result.Width), Math.Ceiling(result.Height))); } }
private UIStringAttributes GetAttributes() { var attributes = new UIStringAttributes(); var font = UIFontHelper.TryGetFont((float)FontSize, FontWeight, FontStyle, FontFamily); attributes.Font = font; attributes.ForegroundColor = (Foreground as SolidColorBrush)?.ColorWithOpacity; if (TextDecorations != TextDecorations.None) { attributes.UnderlineStyle = (TextDecorations & TextDecorations.Underline) == TextDecorations.Underline ? NSUnderlineStyle.Single : NSUnderlineStyle.None; attributes.StrikethroughStyle = (TextDecorations & TextDecorations.Strikethrough) == TextDecorations.Strikethrough ? NSUnderlineStyle.Single : NSUnderlineStyle.None; } var paragraphStyle = new NSMutableParagraphStyle() { MinimumLineHeight = (nfloat)LineHeight, Alignment = TextAlignment.ToNativeTextAlignment(), LineBreakMode = GetLineBreakMode(), }; // For unknown reasons, the LineBreakMode must be set to WordWrap // when applied to a NSTextStorage for text to wrap. if (UseLayoutManager) { paragraphStyle.LineBreakMode = UILineBreakMode.WordWrap; } if (LineStackingStrategy != LineStackingStrategy.MaxHeight) { paragraphStyle.MaximumLineHeight = (nfloat)LineHeight; } attributes.ParagraphStyle = paragraphStyle; if (LineHeight != 0 && font != null) { // iOS puts text at the bottom of the line box, whereas Windows puts it at the top. // Empirically this offset gives similar positioning to Windows. // Note: Descender is typically a negative value. var verticalOffset = LineHeight - font.LineHeight + font.Descender; // Because we're trying to move the text up (toward the top of the line box), // we only set BaselineOffset to a positive value. // A negative value indicates that the the text is already bottom-aligned. attributes.BaselineOffset = Math.Max(0, (float)verticalOffset); } if (CharacterSpacing != 0) { //CharacterSpacing is in 1/1000 of an em, iOS KerningAdjustment is in points. 1 em = 12 points attributes.KerningAdjustment = (CharacterSpacing / 1000f) * 12; } return(attributes); }