Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I force objects of an array to conform to a specific protocol?

I want to allow for communication between different views.

I have two smaller views that sit on top of a bigger view, similar to iOS' video player, with some differences. When I tap the bigger view I want to toggle the views appearance, hide or unhide. I've got a protocol that all the views conform to. I want to add the views to another class I'll call HideViewsService, which has an NSArray property. Can I force the class that uses this HideViewsService class, and adds the views, to add only views that conform to this protocol? Or even just UIViews?

Also, I'd like to call a method on the views that I know all have it, but I don't know how to get by the compiler. Here is my -hideAllViews method:

-(void)hideAllViews
{
for(int i=0; i<self.viewArray.count; i++)
{
    id obj = [self.viewArray objectAtIndex:i];
    if([obj isKindOfClass:[UIView class]] == false)
    {
        return;
    }
    UIView *view = (UIView *)obj;
    if([view respondsToSelector:@selector(hide)])
    {
        [view hide]; // the compiler obviously doesn't like this
    }
}
}

Thanks!! I'm still learning, so please tell me if there is a better way (not just easier, but better).

like image 948
SirRupertIII Avatar asked Nov 05 '13 22:11

SirRupertIII


2 Answers

ObjC does not have generics. Sometimes it would be nice if it did, but it doesn't. But the above design has several problems.

First, if viewArray is supposed to contain only UIView objects, then it is a programming error for it to contain anything else. It is not ok to just return if you find that it doesn't. If you're going to check this here, it should be NSAssert(). Similarly, if everything in viewArray should be respond to hide, you shouldn't just skip it if it doesn't. That's the source of many subtle bugs.

The better solution is to control addition of objects at the point of adding them to HideViewsService (though there are really better ways to do it anyway; we'll get to that).

@protocol XYZHideableView <NSObject>
- (void)hide;
@end

@interface XYZHideViewsService
- (void)addHideableView:(id<XYZHideableView>)view;
- (void)removeHideableView:(id<XYZHideableView>)view;
@end

Now, you don't need to worry about whether viewArray contains things that respond to hide.

That said, I'd generally do this with NSNotificationCenter. Hideable views should observe a notification like XYZHideAllHideableViews. When they set it, they should hide themselves. Then you don't need a HideViewsService. You just just need a +[HideableView hideAllHideableViews] class method.

like image 96
Rob Napier Avatar answered Sep 20 '22 22:09

Rob Napier


If you want to compile without warnings you can just check if the object conforms to the protocol and then cast it to id<Protocol>:

if ([view conformsToProtocol:@protocol(Protocol)]) {
    id<Protocol> conformingView = (id<Protocol>)view;
    // or
    UIView<Protocol> *conformingView = (UIView<Protocol>*)view;
}
like image 35
Sebastian Avatar answered Sep 18 '22 22:09

Sebastian