Несколько делегатов на один объект? - PullRequest
17 голосов
/ 25 января 2012

У меня есть UIScrollView, который мне нужен для подкласса, а внутри подкласса мне нужно присоединить UIScrollViewDelegate, чтобы я мог реализовать метод viewForZoomingInScrollView.

Тогда у меня есть UIViewController, где мне нужно создать экземпляр объекта этого подкласса UIScrollView, который я создал, и я также хотел бы сделать UIViewController UIScrollViewDelegate для этого объекта, чтобы я мог реализовать scrollViewDidZoom в этом UIViewController классе.

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

Ответы [ 5 ]

15 голосов
/ 20 февраля 2013

Иногда имеет смысл присоединить несколько делегатов к представлению с прокруткой. В этом случае вы можете создать простой разделитель делегирования:

// Public interface
@interface CCDelegateSplitter : NSObject

- (void) addDelegate: (id) delegate;
- (void) addDelegates: (NSArray*) delegates;

@end

// Private interface
@interface CCDelegateSplitter ()
@property(strong) NSMutableSet *delegates;
@end

@implementation CCDelegateSplitter

- (id) init
{
    self = [super init];
    _delegates = [NSMutableSet set];
    return self;
}

- (void) addDelegate: (id) delegate
{
    [_delegates addObject:delegate];
}

- (void) addDelegates: (NSArray*) delegates
{
    [_delegates addObjectsFromArray:delegates];
}

- (void) forwardInvocation: (NSInvocation*) invocation
{
    for (id delegate in _delegates) {
        [invocation invokeWithTarget:delegate];
    }
}

- (NSMethodSignature*) methodSignatureForSelector: (SEL) selector
{
    NSMethodSignature *our = [super methodSignatureForSelector:selector];
    NSMethodSignature *delegated = [(NSObject *)[_delegates anyObject] methodSignatureForSelector:selector];
    return our ? our : delegated;
}

- (BOOL) respondsToSelector: (SEL) selector
{
    return [[_delegates anyObject] respondsToSelector:selector];
}

@end

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

7 голосов
/ 25 января 2012

Вам не нужен объект с 2 делегатами.Вы хотите, чтобы ваш customScrollView отвечал за свои собственные функции UIScrollViewDelegate.

Чтобы ваш parentVC отвечал на методы делегата UIScrollView, вам также потребуется создать собственный делегат внутри вашего customScrollView.

В тот момент, когда вызывается функция UIScrollViewDelegate, вы также будете вызывать одну из ваших функций делегата из вашего пользовательского делегата.Таким образом, ваш parentVC будет реагировать в тот момент, когда вы захотите.

Это будет выглядеть примерно так:

CustomScrollView.h

@protocol CustomDelegate <NSObject>

//custom delegate methods
-(void)myCustomDelegateMethod;

@end

@interface CustomScrollView : UIScrollView <UIScrollViewDelegate>
{
    id<CustomDelegate> delegate
    //the rest of the stuff

CustomScrollView.m

-(void) viewForZoomingInScrollView
{
    [self.delegate myCustomDelegateMethod];
    //rest of viewForZoomingInScrollView code

ParentVC.h

@interface CustomScrollView : UIViewController <CustomDelegate>
{
    //stuff

ParentVC.m

-(void)makeCustomScrollView
{
     CustomScrollView *csv = [[CustomScrollView alloc] init];
     csv.delegate = self;
     //other stuff

}

-(void)myCustomDelegateMethod
{
   //respond to viewForZoomingInScrollView
}

Надеюсь, это полностью решит вашу проблему.Удачи.

3 голосов
/ 25 января 2012

Краткий ответ: нет.Делегаты, как правило, являются слабыми отношениями один-к-одному:

@property (nonatomic, weak /*or assign*/) id<MyViewDelegate> delegate;

Иногда вы увидите шаблон проектирования «слушатель», который является формой делегатов один-ко-многим:

- (void) addListener:(id<MyViewListener>)listener;
- (void) removeListener:(id<MyViewListener>)listener;

В вашем случае, в UIScrollView нет хорошей общедоступной точки переопределения, которая позволяет подклассам указывать viewForZoomingInScrollView.Я бы не стал делать UIScrollView своим собственным делегатом, если это возможно.Вы можете сделать UIViewController UIScrollViewDelegate и сделать так, чтобы он предоставлял viewForZooming.Или вы можете создать промежуточный подкласс представления, который использует UIScrollView, предоставляет viewForZooming и перенаправляет другие методы делегата вверх.

2 голосов
/ 25 января 2012

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

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

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

0 голосов
/ 22 декабря 2018

Вот еще одна потенциальная проблема с тем, что вы пытаетесь сделать ...

Допустим, у вас есть два экземпляра UIScrollView и один объект делегата. В объекте делегата вы переопределяете scrollViewDidScroll (UIScrollView *): метод протокола UIScrollViewDelegate.

Внутри метода вы хотите получить доступ к значению свойства contentOffset обоих представлений прокрутки, потому что, возможно, у вас есть два смежных представления коллекций, и вы пытаетесь получить индексный путь элемента в центре представление коллекции, чтобы получить значения свойств, связанных с этими двумя элементами (например, UIDatePicker).

В таком случае, чем вы отличаетесь между видами прокрутки? Свойство scrollView относится только к одному представлению прокрутки; но, даже если это относится к обоим, как вы получаете значение их соответствующих свойств contentOffset?

Теперь вы можете сказать: «Я могу создать IBOutlet для обоих и использовать их назначенные ссылки вместо свойства scrollView в методе делегата, такого как self.collectionViewFirst.contentOffset и self.collectionViewSecond.contentOffset, и игнорировать Свойство scrollView метода делегата.

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

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

...