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) {
|
if (lineHeight > 0) {
|
||||||
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
|
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
|
||||||
paragraphStyle.lineSpacing = lineHeight;
|
paragraphStyle.lineSpacing = lineHeight;
|
||||||
@@ -92,10 +93,11 @@
|
|||||||
if ([self isKindOfClass:[UIButton class]]) {
|
if ([self isKindOfClass:[UIButton class]]) {
|
||||||
paragraphStyle.alignment = ((UIButton*)self).titleLabel.textAlignment;
|
paragraphStyle.alignment = ((UIButton*)self).titleLabel.textAlignment;
|
||||||
} else {
|
} else {
|
||||||
|
// Paragraph alignment is also important for tappable spans as NSTextContainer takes it into account
|
||||||
paragraphStyle.alignment = ((UILabel*)self).textAlignment;
|
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
|
// make sure a possible previously set line break mode is not lost when line height is specified
|
||||||
paragraphStyle.lineBreakMode = ((UILabel*)self).lineBreakMode;
|
paragraphStyle.lineBreakMode = ((UILabel*)self).lineBreakMode;
|
||||||
}
|
}
|
||||||
@@ -103,9 +105,15 @@
|
|||||||
0,
|
0,
|
||||||
attrText.length
|
attrText.length
|
||||||
}];
|
}];
|
||||||
} else if ([self isKindOfClass:[UITextView class]]) {
|
} else if (isLabel || [self isKindOfClass:[UITextView class]]) {
|
||||||
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
|
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
|
||||||
paragraphStyle.alignment = ((UITextView*)self).textAlignment;
|
|
||||||
|
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){
|
[attrText addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:(NSRange){
|
||||||
0,
|
0,
|
||||||
attrText.length
|
attrText.length
|
||||||
|
|||||||
@@ -33,8 +33,23 @@ class UILabelClickHandlerImpl extends NSObject {
|
|||||||
public linkTap(tapGesture: UITapGestureRecognizer) {
|
public linkTap(tapGesture: UITapGestureRecognizer) {
|
||||||
const owner = this._owner.get();
|
const owner = this._owner.get();
|
||||||
if (owner) {
|
if (owner) {
|
||||||
// https://stackoverflow.com/a/35789589
|
|
||||||
const label = <UILabel>owner.nativeTextViewProtected;
|
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 layoutManager = NSLayoutManager.alloc().init();
|
||||||
const textContainer = NSTextContainer.alloc().initWithSize(CGSizeZero);
|
const textContainer = NSTextContainer.alloc().initWithSize(CGSizeZero);
|
||||||
const textStorage = NSTextStorage.alloc().initWithAttributedString(owner.nativeTextViewProtected['attributedText']);
|
const textStorage = NSTextStorage.alloc().initWithAttributedString(owner.nativeTextViewProtected['attributedText']);
|
||||||
@@ -51,27 +66,45 @@ class UILabelClickHandlerImpl extends NSObject {
|
|||||||
const locationOfTouchInLabel = tapGesture.locationInView(label);
|
const locationOfTouchInLabel = tapGesture.locationInView(label);
|
||||||
const textBoundingBox = layoutManager.usedRectForTextContainer(textContainer);
|
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 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
|
||||||
|
);
|
||||||
|
|
||||||
let span: Span = null;
|
// Ensure that an actual glyph was tapped
|
||||||
// try to find the corresponding span using the spanRanges
|
if (CGRectContainsPoint(glyphRect, locationOfTouchInTextContainer)) {
|
||||||
for (let i = 0; i < owner._spanRanges.length; i++) {
|
const indexOfCharacter = layoutManager.characterIndexForGlyphAtIndex(glyphIndex);
|
||||||
const range = owner._spanRanges[i];
|
|
||||||
if (range.location <= indexOfCharacter && range.location + range.length > indexOfCharacter) {
|
let span: Span = null;
|
||||||
if (owner.formattedText && owner.formattedText.spans.length > i) {
|
// Try to find the corresponding span using the spanRanges
|
||||||
span = owner.formattedText.spans.getItem(i);
|
for (let i = 0; i < owner._spanRanges.length; i++) {
|
||||||
|
const range = owner._spanRanges[i];
|
||||||
|
if (range.location <= indexOfCharacter && range.location + range.length > indexOfCharacter) {
|
||||||
|
if (owner.formattedText && owner.formattedText.spans.length > i) {
|
||||||
|
span = owner.formattedText.spans.getItem(i);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (span && span.tappable) {
|
if (span && span.tappable) {
|
||||||
// if the span is found and tappable emit the linkTap event
|
// if the span is found and tappable emit the linkTap event
|
||||||
span._emit(Span.linkTapEvent);
|
span._emit(Span.linkTapEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user