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); }
protected override void OnElementChanged(ElementChangedEventArgs <MyLabel> e) { base.OnElementChanged(e); if (e.NewElement != null) { if (Control == null) { var uiLabel = new UILabel { UserInteractionEnabled = true, LineBreakMode = UILineBreakMode.WordWrap, Lines = 0, Text = "عندما يريد العالم أن يتكلّم ، فهو يتحدّث بلغة يونيكود. تسجّل الآن لحضور المؤتمر الدولي العاشر ليونيكود (Unicode Conference)، الذي سيعقد في 10-12 آذار 1997 بمدينة مَايِنْتْس، ألمانيا. و سيجمع المؤتمر بين خبراء من كافة قطاعات الصناعة على الشبكة العالمية انترنيت ويونيكود، حيث ستتم، على الصعيدين الدولي والمحلي على حد سواء مناقشة سبل استخدام يونكود في النظم القائمة وفيما يخص التطبيقات الحاسوبية، الخطوط، تصميم النصوص والحوسبة متعددة اللغات...TapMe" }; var uiTapGestureRecognizer = new UITapGestureRecognizer(); uiTapGestureRecognizer.AddTarget(() => { var label = uiLabel; var recognizer = uiTapGestureRecognizer; var range = new NSRange(uiLabel.Text.Length - "TapMe".Length, "TapMe".Length); using (var ts = new NSTextStorage()) { var lm = new NSLayoutManager(); var tc = new NSTextContainer(new CGSize(label.Frame.Width, double.MaxValue)); lm.AddTextContainer(tc); ts.Append(label.AttributedText); ts.AddLayoutManager(lm); tc.LineFragmentPadding = (float)0.0; tc.LineBreakMode = label.LineBreakMode; tc.MaximumNumberOfLines = (uint)label.Lines; tc.Size = label.Bounds.Size; var index = lm.GetCharacterIndex(recognizer.LocationOfTouch(0, label), tc); var isWithinRange = (nint)index >= range.Location && (nint)index < range.Location + range.Length; App.Current.MainPage.DisplayAlert("Alert", "Character count: " + label.Text.Length + "\nRange location: " + range.Location + "\nRange length:" + range.Length + "\nCharacter index: " + index + "\nIs within range: " + isWithinRange, "Dismiss"); } }); uiLabel.AddGestureRecognizer(uiTapGestureRecognizer); SetNativeControl(uiLabel); } } }
private int GetCharacterIndexAtPoint(Point point) { if (!_drawRect.Contains(point)) { return(-1); } // Find the tapped character's index var partialFraction = (nfloat)0; var pointInTextContainer = new CGPoint(point.X - _drawRect.X, point.Y - _drawRect.Y); #if NET6_0_OR_GREATER var characterIndex = (int)_layoutManager.GetCharacterIndex (pointInTextContainer, _layoutManager.TextContainers.FirstOrDefault(), out partialFraction); #else #pragma warning disable CS0618 // Type or member is obsolete (For VS2017 compatibility) var characterIndex = (int)_layoutManager.CharacterIndexForPoint (pointInTextContainer, _layoutManager.TextContainers.FirstOrDefault(), ref partialFraction); #pragma warning restore CS0618 // Type or member is obsolete #endif return(characterIndex); }
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; CGSize labelSize = label.Bounds.Size; textContainer.Size = labelSize; // Finds the tapped character location and compare it to the specified range CGPoint locationOfTouchInLabel = tap.LocationInView(label); CGRect 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); var indexOfCharacter = (nint)layoutManager.GetCharacterIndex(locationOfTouchInTextContainer, textContainer); 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 (LinkData link in linkList) { nint 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); }