static double FindDefaultLineHeight(this NativeLabel control, int start, int length) { if (length == 0) { return(0.0); } var textStorage = new NSTextStorage(); #if __MOBILE__ textStorage.SetString(control.AttributedText.Substring(start, length)); #else textStorage.SetString(control.AttributedStringValue.Substring(start, length)); #endif var layoutManager = new NSLayoutManager(); textStorage.AddLayoutManager(layoutManager); var textContainer = new NSTextContainer(size: new SizeF(float.MaxValue, float.MaxValue)) { LineFragmentPadding = 0 }; layoutManager.AddTextContainer(textContainer); var rect = GetCharacterBounds(new NSRange(0, 1), layoutManager, textContainer); return(rect.Bottom - rect.Top); }
private void UpdateHyperlinkLayout() { // Configure textContainer var textContainer = new NSTextContainer(); textContainer.LineFragmentPadding = 0; textContainer.LineBreakMode = LineBreakMode; textContainer.MaximumNumberOfLines = (nuint)Lines; textContainer.Size = _drawRect.Size; // Configure layoutManager _layoutManager = new NSLayoutManager(); _layoutManager.AddTextContainer(textContainer); // Configure textStorage var textStorage = new NSTextStorage(); textStorage.SetString(GetAttributedText(NSAttributedStringTarget.NSTextStorage)); textStorage.AddLayoutManager(_layoutManager); if (FeatureConfiguration.TextBlock.ShowHyperlinkLayouts) { textStorage.AddAttributes( new UIStringAttributes() { ForegroundColor = UIColor.Red }, new NSRange(0, textStorage.Length) ); } }
int IndexAtPoint(Point point) { if (Control is UILabel control) { var cgPoint = new CGPoint(point.X, point.Y - control.Frame.Y); // init text storage var textStorage = new NSTextStorage(); var attrText = new NSAttributedString(control.AttributedText); textStorage.SetString(attrText); // init layout manager var layoutManager = new NSLayoutManager(); textStorage.AddLayoutManager(layoutManager); // init text container var textContainer = new NSTextContainer(new CGSize(control.Frame.Width, control.Frame.Height * 2)) { LineFragmentPadding = 0, MaximumNumberOfLines = (nuint)_currentDrawState.Lines, LineBreakMode = UILineBreakMode.WordWrap, Size = new CGSize(control.Frame.Width, control.Frame.Height * 2) }; layoutManager.AddTextContainer(textContainer); layoutManager.AllowsNonContiguousLayout = true; var characterIndex = layoutManager.GetCharacterIndex(cgPoint, textContainer); return((int)characterIndex); } return(-1); }
private static string DetectTappedUrl(UIGestureRecognizer tap, UILabel control) { CGRect bounds = control.Bounds; NSAttributedString attributedText = control.AttributedText; // Setup containers using var textContainer = new NSTextContainer(bounds.Size) { LineFragmentPadding = 0, LineBreakMode = control.LineBreakMode, MaximumNumberOfLines = (nuint)control.Lines }; using var layoutManager = new NSLayoutManager(); layoutManager.AddTextContainer(textContainer); using var textStorage = new NSTextStorage(); textStorage.SetString(attributedText); using var fontAttributeName = new NSString("NSFont"); var textRange = new NSRange(0, control.AttributedText.Length); textStorage.AddAttribute(fontAttributeName, control.Font, textRange); textStorage.AddLayoutManager(layoutManager); CGRect textBoundingBox = layoutManager.GetUsedRectForTextContainer(textContainer);
private string DetectTappedUrl(UIGestureRecognizer tap, UILabel label, IEnumerable <LinkData> linkList) { // Create instances of NSLayoutManager, NSTextContainer and NSTextStorage var layoutManager = new NSLayoutManager(); var textContainer = new NSTextContainer(); var textStorage = new NSTextStorage(); textStorage.SetString(label.AttributedText); // Configure layoutManager and textStorage layoutManager.AddTextContainer(textContainer); textStorage.AddLayoutManager(layoutManager); // Configure textContainer textContainer.LineFragmentPadding = 0; textContainer.LineBreakMode = label.LineBreakMode; textContainer.MaximumNumberOfLines = (nuint)label.Lines; var labelSize = label.Bounds.Size; textContainer.Size = labelSize; // Find the tapped character location and compare it to the specified range var locationOfTouchInLabel = tap.LocationInView(label); var textBoundingBox = layoutManager.GetUsedRectForTextContainer(textContainer); var textContainerOffset = new CGPoint((labelSize.Width - textBoundingBox.Size.Width) * 0.0 - textBoundingBox.Location.X, (labelSize.Height - textBoundingBox.Size.Height) * 0.0 - textBoundingBox.Location.Y); nfloat labelX; switch (Element.HorizontalTextAlignment) { case TextAlignment.End: labelX = locationOfTouchInLabel.X - (labelSize.Width - textBoundingBox.Size.Width); break; case TextAlignment.Center: labelX = locationOfTouchInLabel.X - (labelSize.Width - textBoundingBox.Size.Width) / 2; break; default: labelX = locationOfTouchInLabel.X; break; } var locationOfTouchInTextContainer = new CGPoint(labelX - textContainerOffset.X, locationOfTouchInLabel.Y - textContainerOffset.Y); nfloat partialFraction = 0; var indexOfCharacter = (nint)layoutManager.CharacterIndexForPoint(locationOfTouchInTextContainer, textContainer, ref partialFraction); foreach (var link in linkList) { // Xamarin version of NSLocationInRange? if ((indexOfCharacter >= link.Range.Location) && (indexOfCharacter < link.Range.Location + link.Range.Length)) { return(link.Url); } } return(null); }
// NOTE: this method only works with single line UILabels, or multi-line labels with left aligned text // Potential fix found on timbroder's comment on https://stackoverflow.com/questions/1256887/create-tap-able-links-in-the-nsattributedstring-of-a-uilabel // Based on https://stackoverflow.com/questions/1256887/create-tap-able-links-in-the-nsattributedstring-of-a-uilabel // Based on https://samwize.com/2016/03/04/how-to-create-multiple-tappable-links-in-a-uilabel/ /// <summary> /// Determines whether the tap is within the NSRange targetRange. Returns true if so, otherwise returns false. /// </summary> /// <returns><c>true</c>, if tap was within the targetRange of the label, <c>false</c> otherwise.</returns> /// <param name="label">The label containing the text.</param> /// <param name="targetRange">The target range of the tappable text within the label.</param> /// <param name="gestureRecognizer">Gesture recognizer.</param> /// <param name="textAlignment">Text alignment of the label.</param> public static bool DidTapAttributedTextInLabel(UILabel label, NSRange targetRange, UIGestureRecognizer gestureRecognizer, UITextAlignment textAlignment) { NSLayoutManager layoutManager = new NSLayoutManager(); NSTextContainer textContainer = new NSTextContainer(CGSize.Empty); NSTextStorage textStorage = new NSTextStorage(); textStorage.SetString(label.AttributedText); // Configure layoutManager and textStorage layoutManager.AddTextContainer(textContainer); textStorage.AddLayoutManager(layoutManager); // Configure textContainer textContainer.LineFragmentPadding = 0.0f; textContainer.LineBreakMode = label.LineBreakMode; textContainer.MaximumNumberOfLines = (System.nuint)label.Lines; CGSize labelSize = label.Bounds.Size; textContainer.Size = labelSize; // Find the tapped character location and compare it to the specified range CGPoint locationOfTouchInLabel = gestureRecognizer.LocationInView(label); CGRect textBoundingBox = layoutManager.GetUsedRectForTextContainer(textContainer); // Change based on the UITextAlignment enum float multiplier; if (textAlignment == UITextAlignment.Center) { multiplier = 0.5f; } else if (textAlignment == UITextAlignment.Right) { multiplier = 1.0f; } else { // textAlignment is Left, Natural, or Justified multiplier = 0.0f; } CGPoint textContainerOffset = new CGPoint( ((labelSize.Width - textBoundingBox.Size.Width) * multiplier - textBoundingBox.Location.X), ((labelSize.Height - textBoundingBox.Size.Height) * multiplier - textBoundingBox.Location.Y) ); CGPoint locationOfTouchInTextContainer = new CGPoint(locationOfTouchInLabel.X - textContainerOffset.X, locationOfTouchInLabel.Y - textContainerOffset.Y); nfloat partialFraction = 0; int indexOfCharacter = (int)layoutManager.CharacterIndexForPoint(locationOfTouchInTextContainer, textContainer, ref partialFraction); // Xamarin version of the function NSLocationInRange (Swift equivalent is to call ```NSLocationInRange(indexOfCharacter, targetRange);``` ) // Credit to https://forums.xamarin.com/discussion/56484/need-to-put-html-into-a-label bool isInRange = (indexOfCharacter >= targetRange.Location) && (indexOfCharacter <= targetRange.Location + targetRange.Length); return(isInRange); }
int IndexAtPoint(Point point) { var cgPoint = new CGPoint(point.X, point.Y - Control.Frame.Y); // init text storage var textStorage = new NSTextStorage(); //if (Control.AttributedText != null) var attrText = new NSAttributedString(Control.AttributedText); textStorage.SetString(attrText); /* * else * { * var attrString = new NSMutableAttributedString(Control.Text); * attrString.AddAttribute(UIStringAttributeKey.Font, Control.Font, new NSRange(0, Control.Text.Length)); * textStorage.SetString(attrString); * } */ // init layout manager var layoutManager = new NSLayoutManager(); textStorage.AddLayoutManager(layoutManager); // init text container var textContainer = new NSTextContainer(new CGSize(Control.Frame.Width, Control.Frame.Height * 2)); textContainer.LineFragmentPadding = 0; textContainer.MaximumNumberOfLines = (nuint)ControlLines; textContainer.LineBreakMode = UILineBreakMode.WordWrap;//Control.LineBreakMode; //if (Control.LineBreakMode == UILineBreakMode.TailTruncation || Control.LineBreakMode == UILineBreakMode.HeadTruncation || Control.LineBreakMode == UILineBreakMode.MiddleTruncation) // textContainer.LineBreakMode = UILineBreakMode.WordWrap; textContainer.Size = new CGSize(Control.Frame.Width, Control.Frame.Height * 2); layoutManager.AddTextContainer(textContainer); layoutManager.AllowsNonContiguousLayout = true; //layoutManager.SetTextContainer(textContainer,new NSRange(0,Control.AttributedText.Length)); //layoutManager.UsesFontLeading = true; //layoutManager.EnsureLayoutForCharacterRange(new NSRange(0,Control.AttributedText.Length)); //layoutManager.EnsureLayoutForTextContainer(textContainer); nfloat partialFraction = 0; var characterIndex = layoutManager.CharacterIndexForPoint(cgPoint, textContainer, ref partialFraction); //[self.layoutManager drawGlyphsForGlyphRange:NSMakeRange(0, self.textStorage.length) atPoint:CGPointMake(0, 0)]; //layoutManager.DrawGlyphs(new NSRange(0,Control.AttributedText.Length),Control.Frame.Location); return((int)characterIndex); }
public void GetGlyphsTest() { TestRuntime.AssertSystemVersion(ApplePlatform.iOS, 7, 0, throwIfOtherPlatform: false); using (var txt = new NSTextStorage()) { var str = "hello world\n\t"; txt.SetString(new NSAttributedString(str)); using (var lm = new NSLayoutManager()) { lm.TextStorage = txt; var glyphs = new short[str.Length]; var props = new NSGlyphProperty [glyphs.Length]; var charIndexBuffer = new nuint [glyphs.Length]; var bidiLevelBuffer = new byte [glyphs.Length]; lm.GetGlyphs(new NSRange(0, str.Length), glyphs, props, charIndexBuffer, bidiLevelBuffer); Assert.That(glyphs, Is.EqualTo(new short [] { 75, 72, 79, 79, 82, 3, 90, 82, 85, 79, 71, -1, -1 }), "glyphs"); Assert.That(props, Is.EqualTo(new NSGlyphProperty [] { 0, 0, 0, 0, 0, NSGlyphProperty.Elastic, 0, 0, 0, 0, 0, NSGlyphProperty.ControlCharacter, NSGlyphProperty.ControlCharacter }), "props"); Assert.That(charIndexBuffer, Is.EqualTo(new nuint [] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }), "charIndexBuffer"); Assert.That(bidiLevelBuffer, Is.EqualTo(new byte [str.Length]), "bidiLevelBuffer"); } } using (var txt = new NSTextStorage()) { var str = "hello world\n\t"; txt.SetString(new NSAttributedString(str)); using (var lm = new NSLayoutManager()) { lm.TextStorage = txt; var glyphs = new short[str.Length]; var charIndexBuffer = new nuint [glyphs.Length]; var bidiLevelBuffer = new byte [glyphs.Length]; lm.GetGlyphs(new NSRange(0, str.Length), glyphs, null, charIndexBuffer, bidiLevelBuffer); Assert.That(glyphs, Is.EqualTo(new short [] { 75, 72, 79, 79, 82, 3, 90, 82, 85, 79, 71, -1, -1 }), "glyphs"); Assert.That(charIndexBuffer, Is.EqualTo(new nuint [] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }), "charIndexBuffer"); Assert.That(bidiLevelBuffer, Is.EqualTo(new byte [str.Length]), "bidiLevelBuffer"); } } }
private CGSize textSize(string str, NSDictionary attributes) { // The three components var storage = new NSTextStorage(); var layout = new NSLayoutManager(); var container = new NSTextContainer(); // Bind them layout.AddTextContainer(container); storage.AddLayoutManager(layout); storage.SetString(new NSAttributedString(str, attributes)); // Compute layout.GetGlyphRange(container); // Get size CGSize size = layout.GetUsedRectForTextContainer(container).Size; return(size); }
private int GetCharacterIndexAtPoint(Point point) { if (!_drawRect.Contains(point)) { return(-1); } // Configure textContainer var textContainer = new NSTextContainer(); textContainer.LineFragmentPadding = 0; textContainer.LineBreakMode = LineBreakMode; textContainer.MaximumNumberOfLines = (nuint)Lines; textContainer.Size = _drawRect.Size; // Configure layoutManager var layoutManager = new NSLayoutManager(); layoutManager.AddTextContainer(textContainer); // Configure textStorage var textStorage = new NSTextStorage(); textStorage.SetString(AttributedText); textStorage.AddLayoutManager(layoutManager); textStorage.SetAttributes( new UIStringAttributes() { Font = Font }, new NSRange(0, textStorage.Length) ); // Find the tapped character's index var partialFraction = (nfloat)0; var pointInTextContainer = new CGPoint(point.X - _drawRect.X, point.Y - _drawRect.Y); var characterIndex = (int)layoutManager.CharacterIndexForPoint(pointInTextContainer, textContainer, ref partialFraction); return(characterIndex); }
void ResetAttributes(NSColor foregroundColorOverride = null) { // clear user attributes TextStorage.SetAttributes(new NSDictionary(), new NSRange(0, TextStorage.Length)); var r = new NSRange(0, Text.Length); TextStorage.SetString(new NSAttributedString(Text)); TextStorage.SetAlignment(TextAlignment.ToNSTextAlignment(), r); if (Font != null) { // set a global font TextStorage.AddAttribute(NSStringAttributeKey.Font, Font, r); } // paragraph style TextContainer.LineBreakMode = TextTrimming == TextTrimming.WordElipsis ? NSLineBreakMode.TruncatingTail : NSLineBreakMode.ByWordWrapping; var pstyle = NSParagraphStyle.DefaultParagraphStyle.MutableCopy() as NSMutableParagraphStyle; pstyle.Alignment = TextAlignment.ToNSTextAlignment(); if (TextTrimming == TextTrimming.WordElipsis) { pstyle.LineBreakMode = NSLineBreakMode.TruncatingTail; } TextStorage.AddAttribute(NSStringAttributeKey.ParagraphStyle, pstyle, r); // set foreground color override if (foregroundColorOverride != null) { TextStorage.AddAttribute(NSStringAttributeKey.ForegroundColor, foregroundColorOverride, r); } // restore user attributes foreach (var att in Attributes) { AddAttributeInternal(att); } }
public CGRect boundingRectForCharacter(NSRange range) { NSTextStorage textStorage = new NSTextStorage(); textStorage.SetString(this.AttributedText); NSLayoutManager layoutManager = new NSLayoutManager(); textStorage.AddLayoutManager(layoutManager); NSTextContainer textContainer = new NSTextContainer(this.Bounds.Size); textContainer.LineFragmentPadding = 0f; layoutManager.AddTextContainer(textContainer); NSRange glyphRange; glyphRange = layoutManager.CharacterRangeForGlyphRange(range); return(layoutManager.BoundingRectForGlyphRange(glyphRange, textContainer)); }
private void UpdateTypography() { _attributedString = GetAttributedText(); if (UseLayoutManager) { // Configure textContainer _textContainer = new NSTextContainer(); _textContainer.LineFragmentPadding = 0; _textContainer.LineBreakMode = GetLineBreakMode(); _textContainer.MaximumNumberOfLines = (nuint)GetLines(); // Configure layoutManager _layoutManager = new NSLayoutManager(); _layoutManager.AddTextContainer(_textContainer); // Configure textStorage _textStorage = new NSTextStorage(); _textStorage.AddLayoutManager(_layoutManager); _textStorage.SetString(_attributedString); } }
public void GetGlyphsTest() { if (!TestRuntime.CheckSystemAndSDKVersion(7, 0)) { Assert.Ignore("requires iOS7+"); } using (var txt = new NSTextStorage()) { var str = "hello world\n\t"; txt.SetString(new NSAttributedString(str)); using (var lm = new NSLayoutManager()) { lm.TextStorage = txt; var glyphs = new short[str.Length]; var props = new NSGlyphProperty [glyphs.Length]; var charIndexBuffer = new nuint [glyphs.Length]; var bidiLevelBuffer = new byte [glyphs.Length]; lm.GetGlyphs(new NSRange(0, str.Length), glyphs, props, charIndexBuffer, bidiLevelBuffer); Assert.That(glyphs, Is.EqualTo(new short [] { 75, 72, 79, 79, 82, 3, 90, 82, 85, 79, 71, -1, -1 }), "glyphs"); Assert.That(props, Is.EqualTo(new NSGlyphProperty [] { 0, 0, 0, 0, 0, NSGlyphProperty.Elastic, 0, 0, 0, 0, 0, NSGlyphProperty.ControlCharacter, NSGlyphProperty.ControlCharacter }), "props"); Assert.That(charIndexBuffer, Is.EqualTo(new nuint [] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }), "charIndexBuffer"); Assert.That(bidiLevelBuffer, Is.EqualTo(new byte [str.Length]), "bidiLevelBuffer"); } } }
bool didTapAttributedTextInLabel(UITapGestureRecognizer tap, UILabel label, NSRange targetRange) { var layoutManager = new NSLayoutManager(); var textContainer = new NSTextContainer(CGSize.Empty); var textStorage = new NSTextStorage(); textStorage.SetString(label.AttributedText); layoutManager.AddTextContainer(textContainer); textStorage.AddLayoutManager(layoutManager); textContainer.LineFragmentPadding = 0; textContainer.LineBreakMode = label.LineBreakMode; textContainer.MaximumNumberOfLines = (nuint)label.Lines; var labelSize = label.Bounds.Size; textContainer.Size = labelSize; var locationOfTouchInLabel = tap.LocationInView(label); var textBoundingBox = layoutManager.GetUsedRectForTextContainer(textContainer); var textContainerOffset = new CGPoint((labelSize.Width - textBoundingBox.Size.Width) * 0.5 - textBoundingBox.Location.X, (labelSize.Height - textBoundingBox.Size.Height) * 0.5 - textBoundingBox.Location.Y); var locationOfTouchInTextContainer = new CGPoint(locationOfTouchInLabel.X - textContainerOffset.X, locationOfTouchInLabel.Y - textContainerOffset.Y); nfloat partialFraction = 1; var indexOfCharacter = layoutManager.CharacterIndexForPoint(locationOfTouchInTextContainer, textContainer, ref partialFraction); if (((nint)indexOfCharacter >= targetRange.Location) && ((nint)indexOfCharacter < targetRange.Location + targetRange.Length)) { return(true); } return(false); }
private NSRange?AttributedTextRangeForPoint(CGPoint point) { var layoutManager = new NSLayoutManager(); var textContainer = new NSTextContainer(CGSize.Empty) { LineFragmentPadding = 0.0f, LineBreakMode = LineBreakMode, MaximumNumberOfLines = (nuint)Lines, Size = Bounds.Size }; layoutManager.AddTextContainer(textContainer); var textStorage = new NSTextStorage(); textStorage.SetString(AttributedText); textStorage.AddLayoutManager(layoutManager); var textBoundingBox = layoutManager.GetUsedRectForTextContainer(textContainer); var textContainerOffset = new CGPoint( (Bounds.Width - textBoundingBox.Width) * 0.5f - textBoundingBox.GetMinX(), (Bounds.Height - textBoundingBox.Height) * 0.5f - textBoundingBox.GetMinY()); var locationOfTouchInTextContainer = new CGPoint(point.X - textContainerOffset.X, point.Y - textContainerOffset.Y); var frac = new nfloat(0.0f); var indexOfCharacter = layoutManager.CharacterIndexForPoint(locationOfTouchInTextContainer, textContainer, ref frac); foreach (var pair in _handlerDictionary) { var range = pair.Key; if (range.LocationInRange((int)indexOfCharacter)) { return(range); } } return(null); }
public static void RecalculateSpanPositions(this NativeLabel control, Label element) { if (element == null) { return; } if (element.TextType == TextType.Html) { return; } if (element?.FormattedText?.Spans == null || element.FormattedText.Spans.Count == 0) { return; } var finalSize = control.Frame; if (finalSize.Width <= 0 || finalSize.Height <= 0) { return; } #if __MOBILE__ var inline = control.AttributedText; #else var inline = control.AttributedStringValue; #endif var range = new NSRange(0, inline.Length); NSTextStorage textStorage = new NSTextStorage(); textStorage.SetString(inline); var layoutManager = new NSLayoutManager(); textStorage.AddLayoutManager(layoutManager); var textContainer = new NSTextContainer(size: finalSize.Size) { LineFragmentPadding = 0 }; layoutManager.AddTextContainer(textContainer); var labelWidth = finalSize.Width; var currentLocation = 0; for (int i = 0; i < element.FormattedText.Spans.Count; i++) { var span = element.FormattedText.Spans[i]; var location = currentLocation; var length = span.Text?.Length ?? 0; if (length == 0) { continue; } var startRect = GetCharacterBounds(new NSRange(location, 1), layoutManager, textContainer); var endRect = GetCharacterBounds(new NSRange(location + length, 1), layoutManager, textContainer); var startLineHeight = startRect.Bottom - startRect.Top; var endLineHeight = endRect.Bottom - endRect.Top; var defaultLineHeight = control.FindDefaultLineHeight(location, length); var yaxis = startRect.Top; var lineHeights = new List <double>(); while ((endRect.Bottom - yaxis) > 0.001) { double lineHeight; if (yaxis == startRect.Top) // First Line { lineHeight = startRect.Bottom - startRect.Top; } else if (yaxis != endRect.Top) // Middle Line(s) { lineHeight = defaultLineHeight; } else // Bottom Line { lineHeight = endRect.Bottom - endRect.Top; } lineHeights.Add(lineHeight); yaxis += (float)lineHeight; } ((ISpatialElement)span).Region = Region.FromLines(lineHeights.ToArray(), finalSize.Width, startRect.X, endRect.X, startRect.Top).Inflate(10); // update current location currentLocation += length; } }
private string DetectTappedUrl(UIGestureRecognizer tap, UILabel label, IEnumerable <LinkData> linkList) { var layoutManager = new NSLayoutManager(); var textContainer = new NSTextContainer(); var textStorage = new NSTextStorage(); textStorage.SetString(label.AttributedText); layoutManager.AddTextContainer(textContainer); textStorage.AddLayoutManager(layoutManager); textContainer.LineFragmentPadding = 0; textContainer.LineBreakMode = label.LineBreakMode; textContainer.MaximumNumberOfLines = (nuint)label.Lines; var labelSize = label.Bounds.Size; textContainer.Size = labelSize; var locationOfTouchInLabel = tap.LocationInView(label); var textBoundingBox = layoutManager.GetUsedRectForTextContainer(textContainer); var textContainerOffset = new CGPoint( (labelSize.Width - textBoundingBox.Size.Width) * 0.0 - textBoundingBox.Location.X, (labelSize.Height - textBoundingBox.Size.Height) * 0.0 - textBoundingBox.Location.Y); nfloat labelX; switch (Element.HorizontalTextAlignment) { case TextAlignment.End: labelX = locationOfTouchInLabel.X - (labelSize.Width - textBoundingBox.Size.Width); break; case TextAlignment.Center: labelX = locationOfTouchInLabel.X - (labelSize.Width - textBoundingBox.Size.Width) / 2; break; default: labelX = locationOfTouchInLabel.X; break; } var locationOfTouchInTextContainer = new CGPoint(labelX - textContainerOffset.X, locationOfTouchInLabel.Y - textContainerOffset.Y); nfloat partialFraction = 0; var indexOfCharacter = (nint)layoutManager.CharacterIndexForPoint(locationOfTouchInTextContainer, textContainer, ref partialFraction); nint scaledIndexOfCharacter = 0; if (label.Font.Name == "NeoSans-Light") { scaledIndexOfCharacter = (nint)(indexOfCharacter * 1.13); } if (label.Font.Name.StartsWith("HelveticaNeue", StringComparison.InvariantCulture)) { scaledIndexOfCharacter = (nint)(indexOfCharacter * 1.02); } foreach (var link in linkList) { var rangeLength = link.Range.Length; var tolerance = 0; if (label.Font.Name == "NeoSans-Light") { rangeLength = (nint)(rangeLength * 1.13); tolerance = 25; indexOfCharacter = scaledIndexOfCharacter; } if (label.Font.Name.StartsWith("HelveticaNeue", StringComparison.InvariantCulture)) { if (link.Range.Location > 2000) { indexOfCharacter = scaledIndexOfCharacter; } } // Xamarin version of NSLocationInRange? if ((indexOfCharacter >= (link.Range.Location - tolerance)) && (indexOfCharacter < (link.Range.Location + rangeLength + tolerance))) { return(link.Url); } } return(null); }
public static SizeF MeasureString(NSAttributedString str, SizeF?availableSize = null) { SetContainerSize(availableSize); storage.SetString(str); return(layout.BoundingRectForGlyphRange(new NSRange(0, (int)str.Length), container).Size.ToEto()); }
private string DetectTappedUrl(UIGestureRecognizer tap, UILabel label, IEnumerable <LinkData> linkList) { // Creates instances of NSLayoutManager, NSTextContainer and NSTextStorage var layoutManager = new NSLayoutManager(); var textContainer = new NSTextContainer(); var textStorage = new NSTextStorage(); textStorage.SetString(label.AttributedText); // Configures layoutManager and textStorage layoutManager.AddTextContainer(textContainer); textStorage.AddLayoutManager(layoutManager); // Configures textContainer textContainer.LineFragmentPadding = 0; textContainer.LineBreakMode = label.LineBreakMode; textContainer.MaximumNumberOfLines = (nuint)label.Lines; var labelSize = label.Bounds.Size; textContainer.Size = labelSize; // Finds the tapped character location and compare it to the specified range var locationOfTouchInLabel = tap.LocationInView(label); var textBoundingBox = layoutManager.GetUsedRectForTextContainer(textContainer); var textContainerOffset = new CGPoint((labelSize.Width - textBoundingBox.Size.Width) * 0.0 - textBoundingBox.Location.X, (labelSize.Height - textBoundingBox.Size.Height) * 0.0 - textBoundingBox.Location.Y); nfloat labelX; switch (Element.HorizontalTextAlignment) { case TextAlignment.End: labelX = locationOfTouchInLabel.X - (labelSize.Width - textBoundingBox.Size.Width); break; case TextAlignment.Center: labelX = locationOfTouchInLabel.X - (labelSize.Width - textBoundingBox.Size.Width) / 2; break; default: labelX = locationOfTouchInLabel.X; break; } var locationOfTouchInTextContainer = new CGPoint(labelX - textContainerOffset.X, locationOfTouchInLabel.Y - textContainerOffset.Y); nfloat partialFraction = 0; var indexOfCharacter = (nint)layoutManager.CharacterIndexForPoint(locationOfTouchInTextContainer, textContainer, ref partialFraction); nint scaledIndexOfCharacter = 0; // Problem is that method CharacterIndexForPoint always returns index based on UILabel font // ".SFUIText" which is the default Helvetica iOS font // HACK is to scale indexOfCharacter for 13% because NeoSans-Light is narrower font than ".SFUIText" if (label.Font.Name == "NeoSans-Light") { scaledIndexOfCharacter = (nint)(indexOfCharacter * 1.13); } // HelveticaNeue font family works perfect until character position in the string is more than 2000 chars // some uncosnsistent behaviour // if string has <b> tag than label.Font.Name from HelveticaNeue-Thin goes to HelveticaNeue-Bold if (label.Font.Name.StartsWith("HelveticaNeue", StringComparison.InvariantCulture)) { scaledIndexOfCharacter = (nint)(indexOfCharacter * 1.02); } foreach (var link in linkList) { var rangeLength = link.Range.Length; var tolerance = 0; if (label.Font.Name == "NeoSans-Light") { rangeLength = (nint)(rangeLength * 1.13); tolerance = 25; indexOfCharacter = scaledIndexOfCharacter; } if (label.Font.Name.StartsWith("HelveticaNeue", StringComparison.InvariantCulture)) { if (link.Range.Location > 2000) { indexOfCharacter = scaledIndexOfCharacter; } } // Xamarin version of NSLocationInRange? if ((indexOfCharacter >= (link.Range.Location - tolerance)) && (indexOfCharacter < (link.Range.Location + rangeLength + tolerance))) { return(link.Url); } } return(null); }
public override void DrawRect(RectangleF dirtyRect) { var height = base.Bounds.Height; var calcValue = base.Bounds.Width * (Value * 0.01f); var context = NSGraphicsContext.CurrentContext.GraphicsPort; context.SetStrokeColor("#999999".ToCGColor()); context.SetLineWidth (1.0F); context.StrokeRect(new RectangleF(0, 0, base.Bounds.Width, height)); context.SetFillColor("#999999".ToCGColor()); context.FillRect(new RectangleF(0, 0, base.Bounds.Width, height)); context.SetFillColor(BackgroundColor.ToCGColor()); context.FillRect(new RectangleF(0, 0, calcValue, height)); var attrStr = "{0}".ToFormat(Label).CreateString("#ffffff".ToNSColor(), Font); var storage = new NSTextStorage(); storage.SetString(attrStr); var layout = new NSLayoutManager(); var container = new NSTextContainer(); layout.AddTextContainer(container); storage.AddLayoutManager(layout); // Get size // var size = layout.GetUsedRectForTextContainer (container).Size; // var width = size.Width / 2.0f; var width = (base.Bounds.Width / 2.0f) - (TextOffset.X); storage.DrawString(new PointF(width, TextOffset.Y)); }