I have application (UICatalog from Apple samples). I am using it with a framework called Lookback which is intended for screen recording.
I have interface defined as follows:
#interface AAPLSteppperViewController : UITableViewController
So it is in a straight way a subclass of UITableViewController. And it has implemented a methods as follows:
+ (NSString*)lookbackIdentifier {
return #"Profile Editor";
}
I wanted to investigate how lookbackIdentifier is being called and see something like that:
The question is: how to introduce such behavior as UITableViewController is a system class and I am not able to see the source of calls numbered 1 and 2 at the stack?
As I investigated framework docs, they recommend to implement always like that
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
...
}
As for me it looks like a change inside UIViewController - but how to achieve something like that?
I guess that it will include some playing with UIViewController but - how? I can't imagine how to override a single method of it without subclasssing.
I have only access to my AAPLSteppperViewController.
I would be grateful if somebody could give me a push in the right direction
As #dan pointed - swizzling is the right answer. I tried with instructions there: http://nshipster.com/method-swizzling/ and it gave me desired behaviour.
Thanks for help :)
Related
Sorry for a trivial (i suppose) question, but i have very small experience in programming for ios and in objective-c generally, so i can't figure by myself how to do some method in one place which will be working for all my view controllers (i don't want to multiply code by pasting this method in all my classes). It's a simple method which will connect to server in background thread and receive some data from it. This method is written i just need to figure how to make it work for all view controllers without multiplying code.
You have some choices:
Create a subclass of UIViewController and define all your ViewControllers subclasses of your custom class instead of UIViewController
Create a category of UIViewController with the implementation of the method
Create a class with your method declared as a class method:
E.g:
ConnectionManager.h
+ (NSArray *) getData;
ConnectionManager.m
+ (NSArray *) getData{
//method implementation
}
And you can call it from your view controllers like this:
[ConnectionManager getData]
A variant of this last option is to create a singleton object.
It seems very odd to do this "for all view controllers". Connecting to a network to get data would seem to be specific to each view controller to me.
Anyway, you could do something like this in a category.
#interface UIViewController (MyDataConnection)
- (void)thisIsYourMethodHere;
#end
and...
#implemnetation UIViewController (MyDataConnection)
- (void)thisIsYourMethodHere {
// do your networking stuff here.
}
#end
This means that in any view controller you can do...
[self thisIsYouMethodHere];
And it will run the code in the category.
I am currently using a GLKView connected to a GLKViewController in my iOS project to animate the background of my app which works fine. Now I introduced a UITableViewController for displaying some list. I also would like to animate the table view's background similar to the other view controllers. But therefore I need something like a GLKTableViewController, but this doesn't exist.
Somebody any ideas ?
Finally I did implement the most important UITableViewController functionalities myself. Basically it's very simple, just implement the following two protocols/interfaces:
UITableViewDelegate
UITableViewDataSource
... with their corresponding implementations in the *.m file.
Hope this helps also other people. Regards.
I'm in the situation where one of the viewControllers of my app is getting pretty big, specially since I've added a "Tutorial state" which adds a different implementation for many methods of this class that I control by checking
_tutorialEnabled?
So, my question is if this is a good use case for method swizzling, I could have this different implementations of these methods in a separate category and swizzle them when required, it might help me reduce the amount of code of the default implementation. Any comments or suggestions of other techniques are appreciated.
No, this is not what method swizzling was designed for.
Personally I would create a subclass of the view controller that manages tutorial related stuff. Then, depending on whether or not the tutorial is enabled, you instantiate either the tutorial controller or its superclass. This is what polymorphism was designed for: to avoid endless if/else/switches.
Why don't you subclass? Create a tutorial subclass of your view controller with all the needed logic. Present the tutorial in the real view controller's -viewDidAppear: using a full screen modal without animation. When the tutorial is over dismiss the model without animation.
if _tutorialEnabled != nil && _tutorialEnabled {
tutorialViewController = …
tutorialViewController.modalPresentationStyle = .FullScreen
presentViewController(tutorialViewController, animated: NO) {}
}
No, I wouldn't use method swizzling for this. It's a bit like using a sledgehammer to knock in a thumbtack.
Unlike others I also would not subclass a view controller, maintaining understandable flow around view lifecycle events is really important when you want to add other functionality later.
Instead I would use the strategy pattern for this. In your init you could do something like this:
if (tutorialEnabled) {
self.behaviour = [TutorialBehaviour new];
} else {
self.behaviour = [NormalBehaviour new];
}
Then when you need to do something that changes you just call a method on your behaviour eg.
- (void)viewDidLoad
{
...
[self.behaviour load]
...
}
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.
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/
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
}
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.
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.
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];
}
I have a function that needs to be called from several different places in my main view. Let's call it updateFunction.
I declare it as such:
- (void)updateFunction {
//updates some variables here
}
This happens immediately after #implementation MainViewController.
Now, I cannot figure out how to call it. [updateFunction]; is wrong, as it updateFunction();.
I know that this is stupid, but it's so basic that I don't think people are really writing about it. Can someone please just tell me how to call the function that I've written?
If you're calling it from a method in MainViewController you'll probably want to call:
[self updateFunction];
If you're calling it from outside MainViewController then in the owning object it would look something like:
MainViewController *mainViewController;
mainViewController = // set it somewhere;
[mainViewController updateFunction];
p.s. - I recommend you start with some Objective-C tutorials. Apple has a bunch on their website if you search for "sample code".
Use this code:
[self updateFunction];