mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
fix(ios): tapping on span does not emit linkTap event (#10028)
This commit is contained in:
committed by
GitHub
parent
bdade0f0d5
commit
ab3416d4d0
@@ -85,6 +85,7 @@
|
||||
} ];
|
||||
}
|
||||
|
||||
BOOL isLabel = [self isKindOfClass:[UILabel class]];
|
||||
if (lineHeight > 0) {
|
||||
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
|
||||
paragraphStyle.lineSpacing = lineHeight;
|
||||
@@ -92,10 +93,11 @@
|
||||
if ([self isKindOfClass:[UIButton class]]) {
|
||||
paragraphStyle.alignment = ((UIButton*)self).titleLabel.textAlignment;
|
||||
} else {
|
||||
// Paragraph alignment is also important for tappable spans as NSTextContainer takes it into account
|
||||
paragraphStyle.alignment = ((UILabel*)self).textAlignment;
|
||||
}
|
||||
|
||||
if ([self isKindOfClass:[UILabel class]]) {
|
||||
if (isLabel) {
|
||||
// make sure a possible previously set line break mode is not lost when line height is specified
|
||||
paragraphStyle.lineBreakMode = ((UILabel*)self).lineBreakMode;
|
||||
}
|
||||
@@ -103,9 +105,15 @@
|
||||
0,
|
||||
attrText.length
|
||||
}];
|
||||
} else if ([self isKindOfClass:[UITextView class]]) {
|
||||
} else if (isLabel || [self isKindOfClass:[UITextView class]]) {
|
||||
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
|
||||
|
||||
if (isLabel) {
|
||||
// It's important to set paragraph alignment for link tap to work on multi-line spans as NSTextContainer takes it into account
|
||||
paragraphStyle.alignment = ((UILabel*)self).textAlignment;
|
||||
} else {
|
||||
paragraphStyle.alignment = ((UITextView*)self).textAlignment;
|
||||
}
|
||||
[attrText addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:(NSRange){
|
||||
0,
|
||||
attrText.length
|
||||
|
||||
@@ -33,8 +33,23 @@ class UILabelClickHandlerImpl extends NSObject {
|
||||
public linkTap(tapGesture: UITapGestureRecognizer) {
|
||||
const owner = this._owner.get();
|
||||
if (owner) {
|
||||
// https://stackoverflow.com/a/35789589
|
||||
const label = <UILabel>owner.nativeTextViewProtected;
|
||||
|
||||
// This offset along with setting paragraph style alignment will achieve perfect horizontal alignment for NSTextContainer
|
||||
let offsetXMultiplier: number;
|
||||
switch (owner.textAlignment) {
|
||||
case 'center':
|
||||
offsetXMultiplier = 0.5;
|
||||
break;
|
||||
case 'right':
|
||||
offsetXMultiplier = 1.0;
|
||||
break;
|
||||
default:
|
||||
offsetXMultiplier = 0.0;
|
||||
break;
|
||||
}
|
||||
const offsetYMultiplier: number = 0.5; // Text is vertically aligned to center
|
||||
|
||||
const layoutManager = NSLayoutManager.alloc().init();
|
||||
const textContainer = NSTextContainer.alloc().initWithSize(CGSizeZero);
|
||||
const textStorage = NSTextStorage.alloc().initWithAttributedString(owner.nativeTextViewProtected['attributedText']);
|
||||
@@ -51,14 +66,30 @@ class UILabelClickHandlerImpl extends NSObject {
|
||||
const locationOfTouchInLabel = tapGesture.locationInView(label);
|
||||
const textBoundingBox = layoutManager.usedRectForTextContainer(textContainer);
|
||||
|
||||
const textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x, (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);
|
||||
|
||||
const textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * offsetXMultiplier - textBoundingBox.origin.x, (labelSize.height - textBoundingBox.size.height) * offsetYMultiplier - textBoundingBox.origin.y);
|
||||
const locationOfTouchInTextContainer = CGPointMake(locationOfTouchInLabel.x - textContainerOffset.x, locationOfTouchInLabel.y - textContainerOffset.y);
|
||||
|
||||
const indexOfCharacter = layoutManager.characterIndexForPointInTextContainerFractionOfDistanceBetweenInsertionPoints(locationOfTouchInTextContainer, textContainer, null);
|
||||
// Check if tap was inside text bounding rect
|
||||
if (CGRectContainsPoint(textBoundingBox, locationOfTouchInTextContainer)) {
|
||||
// According to Apple docs, if no glyph is under point, the nearest glyph is returned
|
||||
const glyphIndex = layoutManager.glyphIndexForPointInTextContainerFractionOfDistanceThroughGlyph(locationOfTouchInTextContainer, textContainer, null);
|
||||
// In order to determine whether the tap point actually lies within the bounds
|
||||
// of the glyph returned, we call the method below and test
|
||||
// whether the point falls in the rectangle returned by that method
|
||||
const glyphRect = layoutManager.boundingRectForGlyphRangeInTextContainer(
|
||||
{
|
||||
location: glyphIndex,
|
||||
length: 1,
|
||||
},
|
||||
textContainer
|
||||
);
|
||||
|
||||
// Ensure that an actual glyph was tapped
|
||||
if (CGRectContainsPoint(glyphRect, locationOfTouchInTextContainer)) {
|
||||
const indexOfCharacter = layoutManager.characterIndexForGlyphAtIndex(glyphIndex);
|
||||
|
||||
let span: Span = null;
|
||||
// try to find the corresponding span using the spanRanges
|
||||
// Try to find the corresponding span using the spanRanges
|
||||
for (let i = 0; i < owner._spanRanges.length; i++) {
|
||||
const range = owner._spanRanges[i];
|
||||
if (range.location <= indexOfCharacter && range.location + range.length > indexOfCharacter) {
|
||||
@@ -75,6 +106,8 @@ class UILabelClickHandlerImpl extends NSObject {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ObjCExposedMethods = {
|
||||
linkTap: { returns: interop.types.void, params: [interop.types.id] },
|
||||
|
||||
Reference in New Issue
Block a user