24

我正在尝试处理 iPhone 的 UITextView 上的触摸。例如,我通过创建 UIImageViews 的子类并实现 touchesBegan 方法成功地处理了点击和其他触摸事件......但是这显然不适用于 UITextView :(

UITextView 启用了用户交互和多点触控,只是为了确保......没有快乐。有人设法处理这个吗?

4

7 回答 7

14

UITextView(UIScrollView 的子类)包含很多事件处理。它处理复制和粘贴以及数据检测器。也就是说,它可能是一个错误,它不会传递未处理的事件。

有一个简单的解决方案:您可以继承 UITextView 并在您自己的版本中实现您自己的 touchesEnded (和其他事件处理消息),您应该[super touchesBegan:touches withEvent:event];在每个触摸处理方法中调用。

#import "MyTextView.h"  //MyTextView:UITextView
@implementation MyTextView

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    NSLog(@"touchesBegan");
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
        [super touchesBegan:touches withEvent:event];
    NSLog(@"touchesMoved");
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
    NSLog(@"****touchesEnded");
    [self.nextResponder touchesEnded: touches withEvent:event]; 
    NSLog(@"****touchesEnded");
    [super touchesEnded:touches withEvent:event];
    NSLog(@"****touchesEnded");
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{
[super touches... etc]; 
NSLog(@"touchesCancelled");
}
于 2009-05-08T16:38:33.157 回答
11

如果您想在 UITextView 上处理单/双/三次点击,您可以委托 UIGestureRecongnizer 并在您的 textview 上添加手势识别器。

继承人相同的代码(在 viewDidLoad 中):

UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap)];

//modify this number to recognizer number of tap
[singleTap setNumberOfTapsRequired:1];
[self.textView addGestureRecognizer:singleTap];
[singleTap release];

-(void)handleSingleTap{
   //handle tap in here 
   NSLog(@"Single tap on view");
}

希望这有帮助:D

于 2011-08-11T05:02:49.880 回答
7

更好的解决方案 (无需调整任何内容或使用任何私有 API :D)

如下所述,将 new 添加UITapGestureRecognizers到 textview 并没有预期的结果,处理程序方法永远不会被调用。那是因为他们UITextView已经设置了一些点击手势识别器,我认为他们的委托不允许我的手势识别器正常工作,并且更改他们的委托可能会导致更糟糕的结果,我相信。

幸运的是,UITextView我已经设置了手势识别器,问题是它会根据视图的状态而变化(即:输入日语时的手势识别器集合与输入英语时以及不处于编辑模式时的手势识别器不同)。我通过在 UITextView 的子类中覆盖这些来解决这个问题:

- (void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
{
    [super addGestureRecognizer:gestureRecognizer];
    // Check the new gesture recognizer is the same kind as the one we want to implement
    // Note:
    // This works because `UITextTapRecognizer` is a subclass of `UITapGestureRecognizer`
    // and the text view has some `UITextTapRecognizer` added :)
    if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
        UITapGestureRecognizer *tgr = (UITapGestureRecognizer *)gestureRecognizer;
        if ([tgr numberOfTapsRequired] == 1 &&
            [tgr numberOfTouchesRequired] == 1) {
            // If found then add self to its targets/actions
            [tgr addTarget:self action:@selector(_handleOneFingerTap:)];
        }
    }
}
- (void)removeGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
{
    // Check the new gesture recognizer is the same kind as the one we want to implement
    // Read above note
    if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
        UITapGestureRecognizer *tgr = (UITapGestureRecognizer *)gestureRecognizer;
        if ([tgr numberOfTapsRequired] == 1 &&
            [tgr numberOfTouchesRequired] == 1) {
            // If found then remove self from its targets/actions
            [tgr removeTarget:self action:@selector(_handleOneFingerTap:)];
        }
    }
    [super removeGestureRecognizer:gestureRecognizer];
}

- (void)_handleOneFingerTap:(UITapGestureRecognizer *)tgr
{
    NSDictionary *userInfo = [NSDictionary dictionaryWithObject:tgr forKey:@"UITapGestureRecognizer"];
    [[NSNotificationCenter defaultCenter] postNotificationName:@"TextViewOneFingerTapNotification" object:self userInfo:userInfo];
    // Or I could have handled the action here directly ...
}

通过这种方式,无论 textview 何时更改其手势识别器,我们将始终捕获我们想要的点击手势识别器 → 因此,我们的处理程序方法将被相应地调用 :)

结论: 如果你想在 中添加手势识别器UITextView,你必须检查文本视图是否已经有它。

  • 如果没有它,只需按照常规方式进行即可。(创建你的手势识别器,设置它,然后将它添加到文本视图中)你就完成了!
  • 如果确实有,那么您可能需要执行与上述类似的操作。



旧答案

我通过调配私有方法得出了这个答案,因为以前的答案有缺点,而且它们没有按预期工作。这里,我并没有修改 的敲击行为,UITextView而是截取被调用的方法,然后调用原来的方法。

进一步说明

UITextView有一堆专门的UIGestureRecognizers,每个都有 atarget和 aaction但它们target不是它UITextView本身,它是 forward 类的一个对象UITextInteractionAssistant。(此助手是一个@packageivar,UITextView但前向定义在公共头文件中:UITextField.h)。

UITextTapRecognizer识别点击和调用oneFingerTap:UITextInteractionAssistant所以我们要拦截该调用:)

#import <objc/runtime.h>

// Prototype and declaration of method that is going be swizzled
// When called: self and sender are supposed to be UITextInteractionAssistant and UITextTapRecognizer objects respectively
void proxy_oneFingerTap(id self, SEL _cmd, id sender);
void proxy_oneFingerTap(id self, SEL _cmd, id sender){ 
    [[NSNotificationCenter defaultCenter] postNotificationName:@"TextViewOneFinderTap" object:self userInfo:nil];
    if ([self respondsToSelector:@selector(proxy_oneFingerTap:)]) {
        [self performSelector:@selector(proxy_oneFingerTap:) withObject:sender];
    }
}

...
// subclass of UITextView
// Add above method and swizzle it with.
- (void)doTrickForCatchingTaps
{
    Class class = [UITextInteractionAssistant class]; // or below line to avoid ugly warnings
    //Class class = NSClassFromString(@"UITextInteractionAssistant");
    SEL new_selector = @selector(proxy_oneFingerTap:);
    SEL orig_selector = @selector(oneFingerTap:);

    // Add method dynamically because UITextInteractionAssistant is a private class
    BOOL success = class_addMethod(class, new_selector, (IMP)proxy_oneFingerTap, "v@:@");
    if (success) {
        Method originalMethod = class_getInstanceMethod(class, orig_selector);
        Method newMethod = class_getInstanceMethod(class, new_selector);
        if ((originalMethod != nil) && (newMethod != nil)){
            method_exchangeImplementations(originalMethod, newMethod); // Method swizzle
        }
    }
}

//... And in the UIViewController, let's say

[textView doTrickForCatchingTaps];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textViewWasTapped:) name:@"TextViewOneFinderTap" object:nil];

- (void)textViewWasTapped:(NSNotification *)noti{
    NSLog(@"%@", NSStringFromSelector:@selector(_cmd));
}
于 2011-11-21T12:40:47.143 回答
2

您需要分配 UITextView instance.delegate = self (假设您要处理同一控制器中的事件)

并确保在界面中实现 UITextViewDelegate 协议...例如:

@interface myController : UIViewController <UITextViewDelegate>{
}

然后你可以实现以下任何一个


- (BOOL)textViewShouldBeginEditing:(UITextView *)textView;
- (BOOL)textViewShouldEndEditing:(UITextView *)textView;

- (void)textViewDidBeginEditing:(UITextView *)textView;
- (void)textViewDidEndEditing:(UITextView *)textView;

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
- (void)textViewDidChange:(UITextView *)textView;

- (void)textViewDidChangeSelection:(UITextView *)textView;

于 2009-03-06T05:37:38.693 回答
1

我正在使用 textview 作为更大视图的子视图。我需要用户能够滚动文本视图,但不能编辑它。我想检测对文本视图的超级视图的单击,包括文本视图本身。

当然,我遇到了 textview 吞噬了从它开始的触摸的问题。禁用用户交互将解决此问题,但随后用户将无法滚动文本视图。

我的解决方案是使 textview 可编辑并使用 textview 的 shouldBeginEditing 委托方法来检测 textview 中的点击。我只是返回 NO,从而阻止编辑,但现在我知道 textview(以及超级视图)已被点击。在这个方法和 superview 的 touchesEnded 方法之间,我有我需要的东西。

我知道这对想要获得实际触摸的人不起作用,但如果你想做的只是检测点击,这种方法有效!

于 2010-08-25T21:18:40.833 回答
0

制作一个 UIScrollView 以及[scrollView addSubview: textview]它可以滚动 textview 怎么样?

于 2011-11-25T21:57:54.383 回答
0

您也可以发送Touch Down事件。通过 Interface Builder 连接此事件。

在此处输入图像描述

然后在您的事件处理程序中添加代码

- (IBAction)onAppIDTap:(id)sender {
    //Your code
}
于 2014-07-08T00:26:12.063 回答