private Size LayoutTypography(Size size) { if (UseLayoutManager) { if (_textContainer == null) { return(default(Size)); } _textContainer.Size = size; #if NET6_0_OR_GREATER return(_layoutManager.GetUsedRect #else return _layoutManager.GetUsedRectForTextContainer #endif (_textContainer).Size); } else { if (_attributedString == null) { return(default(Size)); } return(_attributedString.GetBoundingRect(size, NSStringDrawingOptions.UsesLineFragmentOrigin, null).Size); } }
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); }
private Size LayoutTypography(Size size) { _textContainer.Size = size; // Required for GetUsedRectForTextContainer to return a value. _layoutManager.GetGlyphRange(_textContainer); return(_layoutManager.GetUsedRectForTextContainer(_textContainer).Size); }
// 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); }
nfloat HeightWrappedToViewWidth(NSTextView textView, float width) { NSLayoutManager layoutManager = textView.LayoutManager; NSTextContainer container = textView.TextContainer; layoutManager.GetGlyphRange(container); //force layout nfloat height = layoutManager.GetUsedRectForTextContainer(container).Height; return(height); }
private Size LayoutTypography(Size size) { if (UseLayoutManager) { _textContainer.Size = size; return(_layoutManager.GetUsedRectForTextContainer(_textContainer).Size); } else { return(_attributedString.GetBoundingRect(size, NSStringDrawingOptions.UsesLineFragmentOrigin, null).Size); } }
public CGSize GetSize() { using (var TextLayout = new NSLayoutManager()) { TextLayout.AddTextContainer(TextContainer); TextStorage.AddLayoutManager(TextLayout); TextLayout.GlyphRangeForBoundingRect(new CGRect(CGPoint.Empty, TextContainer.Size), TextContainer); var s = TextLayout.GetUsedRectForTextContainer(TextContainer); TextStorage.RemoveLayoutManager(TextLayout); TextLayout.RemoveTextContainer(0); return(s.Size); } }
nfloat HeightWrappedToWidth(float width) { nfloat height = 0; NSTextStorage storage = new NSTextStorage(); NSLayoutManager layoutManager = new NSLayoutManager(); CGSize size = new CGSize(width, 100); NSTextContainer container = new NSTextContainer(size); layoutManager.AddTextContainer(container); storage.AddLayoutManager(layoutManager); layoutManager.GetGlyphRange(container); //force layout height = layoutManager.GetUsedRectForTextContainer(container).Height; return(height); }
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 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); }
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 Size LayoutTypography(Size size) { if (UseLayoutManager) { if (_textContainer == null) { return(default(Size)); } _textContainer.Size = size; // Required for GetUsedRectForTextContainer to return a value. _layoutManager.GetGlyphRange(_textContainer); return(_layoutManager.GetUsedRectForTextContainer(_textContainer).Size); } else { if (_attributedString == null) { return(default(Size)); } return(_attributedString.BoundingRectWithSize(size, NSStringDrawingOptions.UsesLineFragmentOrigin, null).Size); } }
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); }
private Size LayoutTypography(Size size) { _textContainer.Size = size; return(_layoutManager.GetUsedRectForTextContainer(_textContainer).Size); }
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); }