(iPhone) Как обрабатывать прикосновения к UITextView? - PullRequest
24 голосов
/ 05 марта 2009

Я пытаюсь обработать прикосновения к UITextView iPhone. Мне успешно удалось обработать касания и другие события касания, создав, например, подкласс UIImageViews и реализовав метод touchesBegan ... однако это, очевидно, не работает с UITextView: (

UITextView поддерживает взаимодействие с пользователем и поддерживает мультитач, просто чтобы быть уверенным ... нет никакой радости. Кто-нибудь справился с этим?

Ответы [ 7 ]

14 голосов
/ 08 мая 2009

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");
}
11 голосов
/ 11 августа 2011

Если вы хотите обрабатывать одно / двойное / тройное касание в UITextView, вы можете делегировать UIGestureRecongnizer и добавить распознаватели жестов в ваше текстовое представление.

Вот один и тот же код (в 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

7 голосов
/ 21 ноября 2011

Лучшее решение (без мошенничества и без использования какого-либо частного API: D)

Как объяснено ниже, добавление нового UITapGestureRecognizers в текстовое представление не дает ожидаемых результатов, методы-обработчики никогда не вызываются. Это потому, что 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 ...
}

Поступая таким образом, независимо от того, когда текстовое представление меняет свои распознаватели жестов, мы всегда будем ловить желаемый распознаватель жестов касания → Следовательно, наш метод-обработчик будет вызываться соответственно:)

Вывод: Если вы хотите добавить распознаватели жестов к UITextView, вам нужно проверить, что текстовое представление еще не имело.

  • Если у него его нет, просто делай как обычно. (Создайте свой распознаватель жестов, настройте его и добавьте в текстовое представление) и все готово!.
  • Если он имеет , то вам, вероятно, нужно сделать что-то подобное, как указано выше.



<ч />

Старый ответ

Я придумал этот ответ: мошенническим приватным методом , потому что в предыдущих ответах были недостатки, и они не работают, как ожидалось. Здесь вместо того, чтобы изменять поведение при нажатии UITextView, я просто перехватываю вызываемый метод и затем вызываю оригинальный метод.

Дополнительные пояснения

UITextView имеет кучу специализированных UIGestureRecognizers, каждый из них имеет target и action, но их target не сам UITextView, это объект передового класса UITextInteractionAssistant. (Этот помощник является @package иваром 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));
}
2 голосов
/ 06 марта 2009

Вам необходимо назначить 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;

1 голос
/ 26 августа 2010

Я использую текстовое представление как подпредставление большего представления. Мне нужно, чтобы пользователь мог прокручивать текстовое представление, но не редактировать его. Я хочу обнаружить одно нажатие на суперпредставление текстового представления, в том числе на само текстовое представление.

Конечно, я столкнулся с проблемой, заключающейся в том, что текстовое представление поглощает штрихи, которые на нем начинаются. Отключение взаимодействия с пользователем исправит это, но тогда пользователь не сможет прокрутить текстовое представление.

Мое решение состояло в том, чтобы сделать текстовое представление редактируемым и использовать метод делегата mustBeginEditing текстового представления, чтобы обнаружить касание в текстовом представлении. Я просто возвращаю НЕТ, тем самым предотвращая редактирование, но теперь я знаю, что текстовое представление (и, следовательно, суперпредставление) было использовано. Между этим методом и методом touchesEnded суперпредставления у меня есть то, что мне нужно.

Я знаю, что это не будет работать для людей, которые хотят получить доступ к фактическим касаниям, но если все, что вы хотите сделать, это обнаружить касание, этот подход работает!

0 голосов
/ 08 июля 2014

Вы также можете отправить Touch Down событие. Подключите это событие через Interface Builder.

enter image description here

Затем добавьте код в ваш обработчик событий

- (IBAction)onAppIDTap:(id)sender {
    //Your code
}
0 голосов
/ 26 ноября 2011

Как насчет создания UIScrollView и [scrollView addSubview: textview], которые позволяют прокручивать текстовое представление?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...