Subclass implementing its own delegate protocol

Hey guys,


is it OK for a subclass to implement its own delegate protocol? I have a subclass of UIScrollView which needs to respond to events like scrollViewDidEndDecelerating and scrollViewDidScroll. Therefore, I had to set the delegate property to self and implement the delegate methods inside the subclass. Nevertheless, other classes should still be able to receive these events too, as if they were using the superclass. For example, I want my subclass to do something special when the inherited scrollview ends scrolling, though the external delegate still needs to be informed about the event. Instances of the subclass and the subclass itself use the same delegate object/property. Is this even possible and would it be a safe approach or should the delegate used within the subclass get its own, private property? I appreciate any help you can provide!


Cheers,


Alex 🙂

Accepted Answer

It's ok for a object to be its own delegate, but that's not the problem you're facing here. In this case, you will have (potentially) two delegates "competing" to provide the same delegate method(s), and you have to orchestrate what happens.


You could, for example (and other technical details permitting), override UIScrollView's 'delegate' property with your own, in the subclass. Initially, set the UIScrollView delegate to the subclass instance, then if some client of the scroll view provides a delegate too, your override will save that in a private property. When a delegate method is invoked, your object will get control first. When it's done what it needs to do, you will have to invoke the client's delegate.


This is a bit messy, because you're going to have to implement every possible delegate method, even the optional ones that you don't use. Also, when passing delegate method invocations on, you must be sure to check that the client's delegate object implements the optional ones before you invoke them.


Alternatively, if you control the behavior of the clients of the UIScrollView, you could instead define an alternate delegate property for clients to use, and keep the original delegate property for your subclass to use (override it and make it private, to make it unavailable to clients). But if you take that approach, I think you'll also have to declare an alternate delegate protocol, too, so there's still some mess.

Hey,


first of all thanks a lot for your reply! I've decided to go ahead with kind of the second approach you named but regrettably faced some problems. I defined an alternate delegate property for my subclass to use and kept the original one for clients to use, so that the 'external', public property accessed by clients is still named 'delegate' (makes it easier for other devs). Please have a look at my code:

The .h file:

@protocol APScrollViewDelegate <UIScrollViewDelegate>
@required
- (void)pageDidChangeToValue:(NSInteger)pageNumber;
@end


@interface APScrollView : UIScrollView <UIScrollViewDelegate>
- (instancetype)initWithFrame:(CGRect)frame andPages:(NSArray <UIView *> *)pages;
@property (assign, nonatomic) id <APScrollViewDelegate> delegate;
@end

The .m file:

@interface APScrollView ()
@property (assign, nonatomic) id <UIScrollViewDelegate> internalDelegate;
@property (strong, nonatomic) UIPageControl *pageControl;
@end

@implementation APScrollView


@dynamic delegate;

- (instancetype)initWithFrame:(CGRect)frame andPages:(NSMutableArray <UIView *> *)pages {
    if (self = [super initWithFrame:frame]) {
        self.internalDelegate = self;
        [...]
    }
  return self;
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    [...]
    [self.delegate pageDidChangeToValue:selectedPage];
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    [...]
}

@end

The UIScrollViewDelegate methods (internalDelegate property) just won't get called, what's wrong with my code? Do I have to override the setDelegate: method to initialize the internalDelegate property properly? Thanks in advance, I really appreciate any help you can provide!


Kind regards,


Alex

It isn't going to work. When you provide a subclass implementation of the "delegate" property, it overrides the superclass implementation. That means when the code in UIScrollView retrieves its delegate, it will execute your -[APScrollView delegate] getter, and get your APScrollViewDelegate object, not the "internal" one.


If you want to follow this approach, you have to give your alternate delegate property a different name.

Thanks for the nice explanation! Is there a possibility to make the delegate property of the superclass private respectively inaccessible to clients?


Kind regards,


Alex

No, I don't think there's anything you can do in Obj-C.


If you can keep the entire view hierarchy private (at compile time — obviously anything that goes looking for views at run time can pretty easily find them), you might be able to implement a wrapper object that mediates between the view hierarchy and clients, exposing only the behavior you want, but it's nowhere near obvious that this will actually be feasible for you.


I'd say it'd be more effective just to define the alternate delegate under a different property name, and move on to the next thing.

Subclass implementing its own delegate protocol
 
 
Q