mirror of
https://github.com/zhigang1992/CCHLinkTextView.git
synced 2026-06-12 08:04:57 +08:00
Handle long and short taps in custom gesture recognizer
This commit is contained in:
@@ -9,6 +9,8 @@
|
||||
/* Begin PBXBuildFile section */
|
||||
623AC49518C11BAD000962A0 /* CCHLinkTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 623AC49418C11BAD000962A0 /* CCHLinkTextView.m */; };
|
||||
626F837918C75FBC004FEABB /* CCHLinkTextViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 626F837818C75FBC004FEABB /* CCHLinkTextViewTests.m */; };
|
||||
629350BF18C8841B000BFBA5 /* CCHLinkGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 629350BE18C8841B000BFBA5 /* CCHLinkGestureRecognizer.m */; };
|
||||
629350C118C89619000BFBA5 /* CCHLinkGestureRecognizerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 629350C018C89619000BFBA5 /* CCHLinkGestureRecognizerTests.m */; };
|
||||
62F55CC118C1180200A7E1CC /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 62F55CC018C1180200A7E1CC /* Foundation.framework */; };
|
||||
62F55CC318C1180200A7E1CC /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 62F55CC218C1180200A7E1CC /* CoreGraphics.framework */; };
|
||||
62F55CC518C1180200A7E1CC /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 62F55CC418C1180200A7E1CC /* UIKit.framework */; };
|
||||
@@ -39,6 +41,9 @@
|
||||
623AC49418C11BAD000962A0 /* CCHLinkTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCHLinkTextView.m; sourceTree = "<group>"; };
|
||||
623AC49618C11EAA000962A0 /* CCHLinkTextViewDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CCHLinkTextViewDelegate.h; sourceTree = "<group>"; };
|
||||
626F837818C75FBC004FEABB /* CCHLinkTextViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCHLinkTextViewTests.m; sourceTree = "<group>"; };
|
||||
629350BD18C8841B000BFBA5 /* CCHLinkGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCHLinkGestureRecognizer.h; sourceTree = "<group>"; };
|
||||
629350BE18C8841B000BFBA5 /* CCHLinkGestureRecognizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCHLinkGestureRecognizer.m; sourceTree = "<group>"; };
|
||||
629350C018C89619000BFBA5 /* CCHLinkGestureRecognizerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCHLinkGestureRecognizerTests.m; sourceTree = "<group>"; };
|
||||
62F55CBD18C1180200A7E1CC /* CCHLinkTextView Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "CCHLinkTextView Example.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
62F55CC018C1180200A7E1CC /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
62F55CC218C1180200A7E1CC /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
|
||||
@@ -89,6 +94,8 @@
|
||||
623AC49318C11BAD000962A0 /* CCHLinkTextView.h */,
|
||||
623AC49418C11BAD000962A0 /* CCHLinkTextView.m */,
|
||||
623AC49618C11EAA000962A0 /* CCHLinkTextViewDelegate.h */,
|
||||
629350BD18C8841B000BFBA5 /* CCHLinkGestureRecognizer.h */,
|
||||
629350BE18C8841B000BFBA5 /* CCHLinkGestureRecognizer.m */,
|
||||
);
|
||||
name = CCHLinkTextView;
|
||||
path = ../CCHLinkTextView;
|
||||
@@ -154,6 +161,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
626F837818C75FBC004FEABB /* CCHLinkTextViewTests.m */,
|
||||
629350C018C89619000BFBA5 /* CCHLinkGestureRecognizerTests.m */,
|
||||
62F55CE618C1180200A7E1CC /* Supporting Files */,
|
||||
);
|
||||
path = "CCHLinkTextView ExampleTests";
|
||||
@@ -268,6 +276,7 @@
|
||||
623AC49518C11BAD000962A0 /* CCHLinkTextView.m in Sources */,
|
||||
62F55CD718C1180200A7E1CC /* ViewController.m in Sources */,
|
||||
62F55CD118C1180200A7E1CC /* AppDelegate.m in Sources */,
|
||||
629350BF18C8841B000BFBA5 /* CCHLinkGestureRecognizer.m in Sources */,
|
||||
62F55CCD18C1180200A7E1CC /* main.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -276,6 +285,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
629350C118C89619000BFBA5 /* CCHLinkGestureRecognizerTests.m in Sources */,
|
||||
626F837918C75FBC004FEABB /* CCHLinkTextViewTests.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
// selecteable = YES + copy & paste
|
||||
// NSLinkAttribute -> textViewDelegate
|
||||
// Data detectors
|
||||
|
||||
self.storyboardTextView.editable = NO;
|
||||
self.storyboardTextView.selectable = NO;
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// CCHLinkGestureRecognizerTests.m
|
||||
// CCHLinkTextView Example
|
||||
//
|
||||
// Created by Hoefele, Claus on 06.03.14.
|
||||
// Copyright (c) 2014 Claus Höfele. All rights reserved.
|
||||
//
|
||||
|
||||
#import "CCHLinkGestureRecognizer.h"
|
||||
|
||||
#import <UIKit/UIGestureRecognizerSubclass.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
@interface CCHLinkGestureRecognizerTests : XCTestCase
|
||||
|
||||
@property (nonatomic, strong) CCHLinkGestureRecognizer *linkGestureRecognizer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation CCHLinkGestureRecognizerTests
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
[super setUp];
|
||||
|
||||
self.linkGestureRecognizer = [[CCHLinkGestureRecognizer alloc] init];
|
||||
}
|
||||
|
||||
- (void)testStateEnded
|
||||
{
|
||||
UITouch *touch = [[UITouch alloc] init];
|
||||
NSSet *touches = [NSSet setWithObject:touch];
|
||||
|
||||
[self.linkGestureRecognizer touchesBegan:touches withEvent:nil];
|
||||
XCTAssertEqual(self.linkGestureRecognizer.state, UIGestureRecognizerStateBegan);
|
||||
|
||||
[self.linkGestureRecognizer touchesMoved:touches withEvent:nil];
|
||||
XCTAssertEqual(self.linkGestureRecognizer.state, UIGestureRecognizerStateBegan);
|
||||
|
||||
[self.linkGestureRecognizer touchesEnded:touches withEvent:nil];
|
||||
XCTAssertEqual(self.linkGestureRecognizer.state, UIGestureRecognizerStateEnded);
|
||||
}
|
||||
|
||||
@end
|
||||
22
CCHLinkTextView/CCHLinkGestureRecognizer.h
Normal file
22
CCHLinkTextView/CCHLinkGestureRecognizer.h
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// CCHLinkGestureRecognizer.h
|
||||
// CCHLinkTextView Example
|
||||
//
|
||||
// Created by Hoefele, Claus on 06.03.14.
|
||||
// Copyright (c) 2014 Claus Höfele. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
/**
|
||||
A discreet gesture recognizer that sends action messages for touch down
|
||||
(UIGestureRecognizerStateBegan), touch up for a tap (UIGestureRecognizerStateRecognized,
|
||||
isLongPress == NO), and touch up for a long press (UIGestureRecognizerStateRecognized, isLongPress == NO).
|
||||
*/
|
||||
@interface CCHLinkGestureRecognizer : UIGestureRecognizer
|
||||
|
||||
@property (nonatomic, assign) CFTimeInterval minimumPressDuration;
|
||||
@property (nonatomic, assign, readonly, getter = isLongPress) BOOL longPress;
|
||||
@property (nonatomic, assign, getter = isLongPressEnabled) BOOL longPressEnabled;
|
||||
|
||||
@end
|
||||
123
CCHLinkTextView/CCHLinkGestureRecognizer.m
Normal file
123
CCHLinkTextView/CCHLinkGestureRecognizer.m
Normal file
@@ -0,0 +1,123 @@
|
||||
//
|
||||
// CCHLinkGestureRecognizer.m
|
||||
// CCHLinkTextView Example
|
||||
//
|
||||
// Created by Hoefele, Claus on 06.03.14.
|
||||
// Copyright (c) 2014 Claus Höfele. All rights reserved.
|
||||
//
|
||||
|
||||
#import "CCHLinkGestureRecognizer.h"
|
||||
|
||||
#import <UIKit/UIGestureRecognizerSubclass.h>
|
||||
|
||||
#define MAX_SQUARED_DISTANCE (10 * 10)
|
||||
|
||||
@interface CCHLinkGestureRecognizer ()
|
||||
|
||||
@property (nonatomic, assign) CGPoint initialPoint;
|
||||
@property (nonatomic, strong) NSTimer *timer;
|
||||
@property (nonatomic, assign, getter = isLongPress) BOOL longPress;
|
||||
|
||||
@end
|
||||
|
||||
// disable long tap
|
||||
|
||||
@implementation CCHLinkGestureRecognizer
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
[self setUp];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithTarget:(id)target action:(SEL)action
|
||||
{
|
||||
self = [super initWithTarget:target action:action];
|
||||
if (self) {
|
||||
[self setUp];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
self.minimumPressDuration = 0.3;
|
||||
self.longPressEnabled = YES;
|
||||
}
|
||||
|
||||
- (void)reset
|
||||
{
|
||||
[super reset];
|
||||
|
||||
self.longPress = NO;
|
||||
self.initialPoint = CGPointZero;
|
||||
[self.timer invalidate];
|
||||
self.timer = nil;
|
||||
}
|
||||
|
||||
- (void)longPressed:(NSTimer *)timer
|
||||
{
|
||||
[timer invalidate];
|
||||
|
||||
if (self.isLongPressEnabled) {
|
||||
self.longPress = YES;
|
||||
self.state = UIGestureRecognizerStateRecognized;
|
||||
} else {
|
||||
self.state = UIGestureRecognizerStateFailed;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[super touchesBegan:touches withEvent:event];
|
||||
|
||||
UITouch *touch = touches.anyObject;
|
||||
self.initialPoint = [touch locationInView:self.view];
|
||||
self.state = UIGestureRecognizerStateBegan;
|
||||
self.longPress = NO;
|
||||
|
||||
self.timer = [NSTimer scheduledTimerWithTimeInterval:self.minimumPressDuration target:self selector:@selector(longPressed:) userInfo:nil repeats:NO];
|
||||
}
|
||||
|
||||
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[super touchesMoved:touches withEvent:event];
|
||||
|
||||
if (![self touchIsCloseToInitialPoint:touches.anyObject]) {
|
||||
self.state = UIGestureRecognizerStateFailed;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[super touchesEnded:touches withEvent:event];
|
||||
|
||||
if ([self touchIsCloseToInitialPoint:touches.anyObject]) {
|
||||
self.state = UIGestureRecognizerStateRecognized;
|
||||
} else {
|
||||
self.state = UIGestureRecognizerStateFailed;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[super touchesCancelled:touches withEvent:event];
|
||||
|
||||
self.state = UIGestureRecognizerStateFailed;
|
||||
}
|
||||
|
||||
- (BOOL)touchIsCloseToInitialPoint:(UITouch *)touch
|
||||
{
|
||||
CGPoint point = [touch locationInView:self.view];
|
||||
CGFloat xDistance = (self.initialPoint.x - point.x);
|
||||
CGFloat yDistance = (self.initialPoint.y - point.y);
|
||||
CGFloat squaredDistance = (xDistance * xDistance) + (yDistance * yDistance);
|
||||
|
||||
BOOL isClose = (squaredDistance <= MAX_SQUARED_DISTANCE);
|
||||
return isClose;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -9,10 +9,12 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@protocol CCHLinkTextViewDelegate;
|
||||
@class CCHLinkGestureRecognizer;
|
||||
|
||||
@interface CCHLinkTextView : UITextView
|
||||
|
||||
@property (nonatomic, weak) id<CCHLinkTextViewDelegate> linkDelegate;
|
||||
@property (nonatomic, strong, readonly) CCHLinkGestureRecognizer *linkGestureRecognizer;
|
||||
|
||||
- (void)addLinkForRange:(NSRange)range;
|
||||
- (BOOL)enumerateLinkRangesIncludingCharacterIndex:(NSUInteger)characterIndex usingBlock:(void (^)(NSRange range))block;
|
||||
|
||||
@@ -11,18 +11,15 @@
|
||||
#import "CCHLinkTextView.h"
|
||||
|
||||
#import "CCHLinkTextViewDelegate.h"
|
||||
#import "CCHLinkGestureRecognizer.h"
|
||||
|
||||
// NSLinkAttributeName, linkTextAttributes + UITextViewDelegate - (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange
|
||||
// setAutomaticLinkDetectionEnabled
|
||||
// TTTAttributedLabel / OH...
|
||||
// TweetLabel
|
||||
// http://flyosity.com/mac-os-x/clickable-tweet-links-hashtags-usernames-in-a-custom-nstextview.php
|
||||
// http://shapeof.com/archives/2010/12/customizing_links_in_an_nstextview.html
|
||||
// http://stackoverflow.com/questions/15628133/uitapgesturerecognizer-make-it-work-on-touch-down-not-touch-up
|
||||
// Use subclass of UITextViewDelegate
|
||||
// Replace linkRanges with NSLinkAttribute attributes
|
||||
|
||||
@interface CCHLinkTextView ()
|
||||
|
||||
@property (nonatomic, strong) NSMutableArray *linkRanges;
|
||||
@property (nonatomic, strong) CCHLinkGestureRecognizer *linkGestureRecognizer;
|
||||
|
||||
@end
|
||||
|
||||
@@ -46,25 +43,33 @@
|
||||
{
|
||||
self.linkRanges = [NSMutableArray array];
|
||||
|
||||
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(textTapped:)];
|
||||
[self addGestureRecognizer:tapGestureRecognizer];
|
||||
self.linkGestureRecognizer = [[CCHLinkGestureRecognizer alloc] initWithTarget:self action:@selector(textTapped:)];
|
||||
// self.linkGestureRecognizer.longPressEnabled = NO;
|
||||
[self addGestureRecognizer:self.linkGestureRecognizer];
|
||||
|
||||
// UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(textTapped:)];
|
||||
// [self addGestureRecognizer:tapGestureRecognizer];
|
||||
}
|
||||
|
||||
- (void)textTapped:(UITapGestureRecognizer *)recognizer
|
||||
- (void)textTapped:(CCHLinkGestureRecognizer *)recognizer
|
||||
{
|
||||
if (recognizer.state == UIGestureRecognizerStateEnded) {
|
||||
CGPoint location = [recognizer locationInView:self];
|
||||
location.x -= self.textContainerInset.left;
|
||||
location.y -= self.textContainerInset.top;
|
||||
|
||||
NSUInteger characterIndex = [self.layoutManager characterIndexForPoint:location inTextContainer:self.textContainer fractionOfDistanceBetweenInsertionPoints:NULL];
|
||||
BOOL linkTapped = [self enumerateLinkRangesIncludingCharacterIndex:characterIndex usingBlock:^(NSRange range) {
|
||||
[self didTapLinkAtCharacterIndex:characterIndex range:range];
|
||||
}];
|
||||
|
||||
if (!linkTapped) {
|
||||
[self linkTextViewDidTap];
|
||||
}
|
||||
if (recognizer.state == UIGestureRecognizerStateBegan) {
|
||||
NSLog(@"touch down");
|
||||
} else if (recognizer.state == UIGestureRecognizerStateEnded) {
|
||||
// NSLog(@"touch up");
|
||||
NSLog(@"touch up %@", recognizer.isLongPress ? @"Y" : @"N");
|
||||
// CGPoint location = [recognizer locationInView:self];
|
||||
// location.x -= self.textContainerInset.left;
|
||||
// location.y -= self.textContainerInset.top;
|
||||
//
|
||||
// NSUInteger characterIndex = [self.layoutManager characterIndexForPoint:location inTextContainer:self.textContainer fractionOfDistanceBetweenInsertionPoints:NULL];
|
||||
// BOOL linkTapped = [self enumerateLinkRangesIncludingCharacterIndex:characterIndex usingBlock:^(NSRange range) {
|
||||
// [self didTapLinkAtCharacterIndex:characterIndex range:range];
|
||||
// }];
|
||||
//
|
||||
// if (!linkTapped) {
|
||||
// [self linkTextViewDidTap];
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user