4

I'm trying to category UIViewController to override viewWillAppear:. But getting this warning.

Category is implementing a method which also be implemented in primary class

@implementation UIViewController (ViewWillAppearCategory)

-(void)viewWillAppear:(BOOL)animated
{
    //.........
}

@end

I want to do some stuff during view appear in all screen, So I don't want to touch in all screen. that's why, go with category.

I may implement some method in sub class and I can call that method in all VC(all Screen). But I don't want this. It automatically invoke in view will appear call. Is this any idea to do this or did any mistake in above?

Note: This code will only appear in development phase for some testing purpose. So I'll remove this code when go with app store. So It should be easier task during removal, that is I won't touch all screen. I won't keep this code during submission to app store.

Mani
  • 17,549
  • 13
  • 79
  • 100

5 Answers5

11

In such cases you must try Method Swizzling, a very nice formed concept which allows you to change the implementation of an existing selector.

For more details and code please visit the link below.

http://nshipster.com/method-swizzling/

Alex Zavatone
  • 4,106
  • 36
  • 54
Harish Suthar
  • 711
  • 1
  • 4
  • 14
  • Is it invoke both implementation(`class_addMethod(...)`)? or Just replace first method and invoke only our own method? I want to invoke method which is available in category and also invoke default implementation which is in view controller. – Mani Apr 07 '14 at 12:58
  • While this works I think it's a terrible idea in the case presented, as it calls into question the validity of a great deal of testing that might have been done. Biting the bullet to create a subclass of UIViewController and changing many other classes to subclass that subclass seems well worth the modest effort, IMHO. – RobP Dec 12 '14 at 00:41
  • It's by far superior to any other solutions to such problems. I hope everyone learns to give wonderful answers like this more often, including me. It was a wonderful thing I learned today thanks to you. – M. Porooshani Nov 02 '15 at 07:46
  • @Mani so the category method and the default implementation will get invoked? – Shabarinath Pabba Jun 23 '16 at 17:00
  • @ShabarinathPabba Yes. Based on this, I've implemented the library. If you want sample, look at this link. https://github.com/npctech/Tattle-UI-iOS/blob/master/TattleControl/UIController%2BSnapShotButton.m – Mani Jun 24 '16 at 05:28
6

categories are for adding new methods, not overriding existing ones. Maybe make a subclass of UIViewController, say, MyUIViewController, with this code:

-(void) viewWillAppear:(BOOL) animated {
    // do your "category" stuff
}

then make all your other UIViewControllers subclasses of MyUIViewController with this code:

-(void) viewWillAppear:(BOOL) animated {
    [super viewWillAppear:animated];
    // rest of code for this class
}
RobP
  • 9,144
  • 3
  • 20
  • 33
  • Read my question fully, If I go with above, I should code in all screen. I don't want to do that. – Mani Apr 04 '14 at 07:56
  • I read your question. You can't change the behavior of all your view controllers at once without touching the code. The method above is the safest and least intrusive. – RobP Apr 04 '14 at 07:57
  • @Mani you're wrong. if you make a category then you get this behaviour on all your view controllers. by subclassing you'll get the desired behaviour on only one screen – Andrey Chernukha Apr 04 '14 at 07:57
  • you can but it is unsafe. subclassing can affect all his screens as long as he makes all those screens subclasses of a new parent class as I suggested. see http://stackoverflow.com/questions/5272451/overriding-methods-using-categories-in-objective-c – RobP Apr 04 '14 at 07:59
  • @AndreyChernukha Yes you are right. I'll get in all VC once I used that subclass, also If I don't want, I must rework on that. Am I right? – Mani Apr 04 '14 at 08:01
  • @RobP Thanks you. I've post answer. See this. If you've any suggestion, please say your suggestion too. – Mani Apr 04 '14 at 09:44
1

I understand the reasons why you want to have a simple solution to test something on all screens and remove it easily, however:

  • You can not call super in a category, and not calling [super viewWillAppear:] may have unexpected results depending on the class and its particular implementation.
  • Swizzling methods is a hack and as you'll remove it from your final version, your testing version becomes useless as it may behave very differently.

On the other hand creating a UIViewController superclass where you properly override viewWillAppear: is not that complicated:

  • The code will belong only to a single class. No need to repeat/maintain code for every "screen".
  • You only need to change the other controllers' superclass and Nibs or Storyboards references once.
  • You can keep the superclass for both testing and release and the behavior will be similar.
  • You can do so many more things in a superclass than in a category.

Ultimately it would be interesting to know what are you trying to achieve. You could probably achieve similar things by implementing a UINavigationControllerDelegate and keep track of controllers getting pushed and popped.


As for viewWillAppear documentation:

This method is called before the receiver’s view is about to be added to a view hierarchy and before any animations are configured for showing the view. You can override this method to perform custom tasks associated with displaying the view. For example, you might use this method to change the orientation or style of the status bar to coordinate with the orientation or style of the view being presented. If you override this method, you must call super at some point in your implementation.

Again, you can't do that from a category.

Rivera
  • 10,792
  • 3
  • 58
  • 102
  • Where did you find my code as `[super viewWillAppear:]`? Did you read my question fully and also my comments over this post? How do you tell that `The code will belong only to a single class`? If I use `UIViewController, UINavigationController, UITabbarController,UIPageViewController`? Then I go with all ? Isn't it? Anyway at least, I need to import that class in all file and also do this in all VC `UIViewController: BaseController"? this is bad idea in my case. – Mani Apr 08 '14 at 04:30
  • Your code **should** call `[super viewWillAppear:]` but you can't in a category. And while you're right about needing more than one superclass if you have something else than `UIViewController`s, it would help to know what do you actually want to achieve. – Rivera Apr 08 '14 at 04:48
  • I need `[super viewWillAppear:] `, once i've write code `viewWillAppear` in screen? I may leave `viewWillAppear` in my screen. Even I leave, `category` method will call. that's what I need. Oh man, I think you didn't understand my question. Just read swizzling concept, you'll understand my question. – Mani Apr 08 '14 at 05:12
0

What you want to achieve defeats the purpose of a Category. However, there's another way aside from subclassing UIViewController but you have to touch the viewWillAppear method for each controller.

//UIViewController+CustomCategory.h
@interface UIViewConctroller (CustomCategory)

- (void)performCustomization;

@end

//UIViewController+CustomCategory.m
@implementation UIViewController (CustomCategory)

- (void)performCustomization {
    // Do custom stuff…
}

@end

Then in each controller

//MYViewController.m
#import "UIViewController+CustomCategory.h"

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self performCustomization];
}
Alex Zavatone
  • 4,106
  • 36
  • 54
mownier
  • 1,719
  • 2
  • 13
  • 27
0

As you are saying the code is going to be executed only in debug mode. Then why do you worry about warnings let the warning come you continue your work when it comes to release you remove your Category.

If you don't even need to see the warning your go with your same answer like

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"

-(void)viewWillAppear:(BOOL)animated
{
    NSLog(@"I get callback here too");
}

#pragma clang diagnostic pop

But I would say to go for subclassing because removing the existing class is also not that hard in XCode tool.

Rajesh
  • 10,318
  • 16
  • 44
  • 64