Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TIMOB-23887](6_1_X):Attributed string link on a TextArea doesn't fire link event #9013

Merged
merged 2 commits into from
May 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions iphone/Classes/TiUITextArea.h
Expand Up @@ -27,6 +27,7 @@
-(void)setShowUndoRedoActions:(id)value;

-(UIView<UITextInputTraits>*)textWidgetView;
-(void)checkLinkForTouch:(UITouch *)touch;

@end

Expand Down
93 changes: 91 additions & 2 deletions iphone/Classes/TiUITextArea.m
Expand Up @@ -5,14 +5,17 @@
* Please see the LICENSE included with this distribution for details.
*/
#ifdef USE_TI_UITEXTAREA

#import "TiUITextArea.h"
#import "TiUITextAreaProxy.h"

#import "TiUtils.h"
#import "Webcolor.h"
#import "TiApp.h"

#ifdef USE_TI_UIATTRIBUTEDSTRING
#import "TiUIAttributedStringProxy.h"
#endif

@implementation TiUITextViewImpl

-(void)setTouchHandler:(TiUIView*)handler
Expand Down Expand Up @@ -40,12 +43,16 @@ - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
//propagated. If it is dragging do not do anything.
//The reason we are not checking tracking (like in scrollview) is because for some
//reason UITextView always returns true for tracking after the initial focus

UITouch *touch = [touches anyObject];
[(TiUITextArea *)touchHandler checkLinkForTouch:touch];

if (!self.dragging && self.userInteractionEnabled && (touchedContentView == nil) ) {
[touchHandler processTouchesBegan:touches withEvent:event];
}
[super touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!self.dragging && self.userInteractionEnabled && (touchedContentView == nil) ) {
[touchHandler processTouchesMoved:touches withEvent:event];
Expand Down Expand Up @@ -121,6 +128,88 @@ -(void)adjustOffsetIfRequired:(UITextView*)tv
}
}

-(void)checkLinkForTouch:(UITouch *)touch
{
// TIMOB-23887: This is similar to UILabel implementation, rather native link recognizer of UITextView, to support TIMOB-19165.
BOOL testLink = (textWidgetView != nil) && ([(TiViewProxy *)[self proxy] _hasListeners:@"link" checkParent:NO]);
BOOL isEditable = ((UITextView *)[self textWidgetView]).isEditable;
BOOL isLinkEnabled = [TiUtils boolValue:[[self proxy] valueForUndefinedKey:@"handleLinks"] def:YES];
if (testLink && isLinkEnabled && !isEditable)
{
UITextView *textView = (UITextView *)[self textWidgetView];
CGPoint tapPoint = [touch locationInView:textView];
[self checkLinkAttributeForString:[textView attributedText] atPoint:tapPoint];
}
}

- (NSUInteger)characterIndexAtPoint:(NSAttributedString *)theString atPoint:(CGPoint)point
{
UITextView *textView = ((UITextView *)[self textWidgetView]);
NSTextContainer *textContainer = textView.textContainer;
NSLayoutManager *layoutManager = textView.layoutManager;

// Manage point with respect to padding
point.x -= textView.textContainerInset.left;
point.y -= textView.textContainerInset.top;

NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
CGRect wholeTextRect = [layoutManager boundingRectForGlyphRange:glyphRange
inTextContainer:textContainer];
// Bail early if point outside the whole text bounding rect
if (!CGRectContainsPoint(wholeTextRect, point))
{
return NSNotFound;
}

// ask the layoutManager which glyph is under this tapped point
NSUInteger glyphIdx = [layoutManager glyphIndexForPoint:point
inTextContainer:textContainer
fractionOfDistanceThroughGlyph:NULL];

// as explained in Apple's documentation the previous method returns the nearest glyph
// if no glyph was present at that point. So if we want to ensure the point actually
// lies on that glyph, we should check that explicitly
CGRect glyphRect = [layoutManager boundingRectForGlyphRange:NSMakeRange(glyphIdx, 1)
inTextContainer:textContainer];
if (CGRectContainsPoint(glyphRect, point))
{
NSUInteger index = [layoutManager characterIndexForGlyphAtIndex:glyphIdx];
return index;
}
else
{
return NSNotFound;
}
}

-(BOOL)checkLinkAttributeForString:(NSAttributedString *)theString atPoint:(CGPoint)p
{
NSUInteger idx = [self characterIndexAtPoint:theString atPoint:p];

if (idx == NSNotFound)
{
return NO;
}

NSRange theRange = NSMakeRange(0, 0);
NSString *url = nil;
#ifdef USE_TI_UIATTRIBUTEDSTRING
TiUIAttributedStringProxy *tempString = [[self proxy] valueForKey:@"attributedString"];
url = [tempString getLink:idx];
#endif

if (url == nil || url.length == 0)
{
return NO;
}

NSDictionary *eventDict = [NSDictionary dictionaryWithObjectsAndKeys:
url, @"url",
[NSArray arrayWithObjects:NUMUINTEGER(theRange.location), NUMUINTEGER(theRange.length),nil],@"range",
nil];
[[self proxy] fireEvent:@"link" withObject:eventDict propagate:NO reportSuccess:NO errorCode:0 message:nil];
return YES;
}
#pragma mark Public APIs

-(void)setShowUndoRedoActions:(id)value
Expand Down