Прохождение цепочки респондента для прохождения пользовательских событий. Это неправильно? - PullRequest
18 голосов
/ 05 октября 2010

Согласно документации iOS, цепочка респондента используется для передачи сенсорных событий «вверх по цепочке».Он также используется для действий, генерируемых элементами управления.Хорошо.

Что я действительно хотел бы сделать, так это отправить пользовательское событие "вверх по цепочке".Первый респондент, который поднимет на событие, будет обрабатывать его.Это похоже на довольно распространенную модель, но я не могу найти хорошего объяснения того, как сделать это «способом iOS / Какао».

Поскольку цепочка респондента - это именно то, что мне нужно, я придумалрешение, подобное этому:

// some event happened in my view that 
// I want to turn into a custom event and pass it "up":

UIResponder *responder = [self nextResponder];

while (responder) {

   if ([responder conformsToProtocol:@protocol(ItemSelectedDelegate)]) {
       [responder itemSelected:someItem];
       break;
   } 

   responder = [responder nextResponder];
}

Это прекрасно работает, но у меня есть ощущение, что должны быть другие способы справиться с этим.Ходить по цепочке вручную таким способом не очень ... приятно.

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

Каков наилучший способ справиться с этим в iOS (и в этом отношении с Какао)?

РЕДАКТИРОВАТЬ :

Что я хочу сделать?

У меня есть контроллер представления, который имеет представление, которое имеет подпредставления и т. Д. Некоторые из подпредставлений имеют определенный тип, который показывает элементиз базы данных.Когда пользователь касается этого представления, на контроллер должен быть отправлен сигнал для перехода на страницу сведений об этом элементе.

Представление, которое обрабатывает касание, находится на несколько уровней ниже основного представления в иерархии представлений.Я должен сообщить контроллеру (или в некоторых случаях конкретному подпредставлению «вверх по цепочке»), что элемент был выбран.

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

Ответы [ 3 ]

14 голосов
/ 06 октября 2010

У UIApplication есть метод для этой цели , как и его двоюродный брат Какао . Вы можете заменить весь этот код в вашем вопросе одним сообщением.

12 голосов
/ 05 октября 2010

Ты довольно близко. Что было бы более стандартным, это что-то вроде этого:

@implementation NSResponder (MyViewController)
- (void)itemSelected:(id)someItem
{
    [[self nextResponder] itemSelected:someItem];
}
@end

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

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

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

Решение Питера работает, если вы уверены, что первый респондент установлен правильно.Если вы хотите больше контроля над тем, какой объект уведомляется о событиях, вы должны использовать targetForAction: withSender: .

Это позволяет вам указать первое представление, которое должно иметь возможность обрабатывать событиеи оттуда он будет сканировать цепочку респондента, пока не найдет объект, который может обработать сообщение.

Вот полностью документированная функция, которую вы можете использовать:

@interface ABCResponderChainHelper
/*!
 Sends an action message identified by selector to a specified target's responder chain.
 @param action 
    A selector identifying an action method. See the remarks for information on the permitted selector forms.
 @param target 
    The object to receive the action message. If @p target cannot invoke the action, it passes the request up the responder chain.
 @param sender
    The object that is sending the action message.
 @param userInfo
    The user info for the action. This parameter may be @c nil.
 @return
    @c YES if a responder object handled the action message, @c NO if no object in the responder chain handled the message.
 @remarks
    This method pushes two parameters when calling the target. This design enables the action selector to be one of the following:
 @code
 - (void)action
 - (void)action:(id)sender
 - (void)action:(id)sender userInfo:(id)userInfo@endcode
*/
+ (BOOL)sendResponderChainAction:(SEL)action to:(UIResponder *)target from:(id)sender withUserInfo:(id)userInfo;
@end

Реализация:

@implementation ABCResponderChainHelper
+ (BOOL)sendResponderChainAction:(SEL)action to:(UIResponder *)target from:(id)sender withUserInfo:(id)userInfo {
    target = [target targetForAction:action withSender:sender];
    if (!target) {
        return NO;
    }

    NSMethodSignature *signature = [target methodSignatureForSelector:action];
    const NSInteger hiddenArgumentCount = 2; // self and _cmd
    NSInteger argumentCount = [signature numberOfArguments] - hiddenArgumentCount;
    switch (argumentCount) {
        case 0:
            SuppressPerformSelectorLeakWarning([target performSelector:action]);
            break;
        case 1:
            SuppressPerformSelectorLeakWarning([target performSelector:action withObject:sender]);
            break;
        case 2:
            SuppressPerformSelectorLeakWarning([target performSelector:action withObject:sender withObject:userInfo]);
            break;
        default:
            NSAssert(NO, @"Invalid number of arguments.");
            break;
    }

    return YES;
}
@end

Примечание: здесь используется макрос SuppressPerformSelectorLeakWarning .

Это прекрасно работает в UITableView.Представьте, что в ячейке есть распознаватель жестов, и вам нужно сообщить контролеру представления о выполненном действии.Вместо того, чтобы создавать делегата для ячейки и затем пересылать сообщение делегату, вместо этого можно использовать цепочку респондента.

Пример использования (отправитель):

// in table view cell class:
- (void)longPressGesture:(UILongPressGestureRecognizer *)recognizer {
    // ...
    [ABCResponderChainHelper sendResponderChainAction:@selector(longPressCell:) to:self from:self withUserInfo:nil];
}

Здесьself относится к самой ячейке. респондент цепочка гарантирует, что метод сначала отправляется в UITableViewCell, затем UITableView и, в конечном итоге, в UIViewController.

Пример использования(получатель):

#pragma mark - Custom Table View Cell Responder Chain Messages
- (void)longPressCell:(UITableViewCell *)sender {
    // handle the long press
}

Если вы попытались сделать то же самое с sendAction: to: from: forEvent: , у вас есть две проблемы:

  • Для того, чтобы он работал с цепочкой респондента, вы должны передать nil параметру to, после чего он запустит обмен сообщениями с первого респондента , а не с объектом вашеговыбор.Управление первым респондентом может быть громоздким.С помощью этого метода вы просто указываете, с какого объекта начинать.
  • Вы не можете легко передать второй аргумент с произвольными данными.Вам потребуется создать подкласс UIEvent, добавить свойство, такое как userInfo, и передать его в аргументе события;неуклюжее решение в этом случае.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...