In earlier versions of Xcode, when I create a new subclass of UIView the code below is automatically generated in the implementation file:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code...
}
return self;
}
I'm trying to reacquaint myself with it again and now it seems it's not automatically generated anymore. Is there a reason for that? Is this not the proper way of doing it anymore?
Thanks!
...now it seems it's not automatically generated anymore. Is there a reason for that? Is this not the proper way of doing it anymore?
The "generated" code really just comes from a template file, and all that has happened is that the template file changed at some point. -initWithFrame: and -iniWithCoder: are still both valid default initializers for UIView, and you should override one or both of them in your class if they're needed.
I don't know the actual reason behind the change to the template, but perhaps it was because there's nothing particularly special about the initializer methods compared to the various other methods in UIView that can be overridden. Nearly every UIView subclass needs to override -drawRect:, so it makes sense to include that in the template, but that's not true for -initWithFrame: or -initWithCoder:.
I do not think that there is a "hard" reason. Keep in mind that such autogenerated code is for convenience.
However, there might be two reasons:
A. You cannot say whether this is an useful initializer for your view subclass. That's guessing. Typically it is a good idea to give the initializer as much properties as possible.
B. If you have an initializer with more args, this is typically the designated initializer. In such a case the generated code is simply wrong, because -initWithFrame: becomes a secondary initializer and has to execute the designated initializer. It is no good idea to generate wrong code.
But this is guessing, too.
Related
I learned a very painful lesson about how to ensure your outlets are nil on a view controller's viewDidLoad, and that's by writing an initWith... method of your own.
-(id)initWithDoohickey:(SomeDoohickey*)doohickey
{
self = [super init];
if (self)
{
theDoohickey = doohickey;
}
return nil;
}
Now when you call this method, instead of initWithNibName, or simply init, your view outlets will be nil, and you'll run into all kinds of debugging nightmares.
So I see two solutions here...
(1) Call init or initWithNibName and let it do it's thing, and follow that with a classInQuestion configureWithDoohickey:(SomeDoohickey*)doohickey
or (2)
figure out how to properly write an initWith... method that actually loads the nib properly, while also allowing me to pass in whatever config variables I want in the same call.
I'd like to know how to do (2).
Simply replace your call to [super init]; with a call to [super initWithNibName:...];.
Obviously you need to pass in the correct arguments.
The problem you have is that you didn't properly chain the initializers. A subclass's initialization process must (eventually) call its superclass's designated initializer. The designated initializer ensures that all of a class's data is correctly set up.
If you don't call the superclass's DI, it's not able to set the properties that were defined in the superclass.
The simple init that you're using is not UIViewController's designated intializer; initWithNibName:bundle: is. As rmaddy said, that is the method you should use in your subclass's own designated intializer.
Initializers in the subclass can use each other, as long as one of them eventually passes up to the superclass's DI.
(As an aside, this relationship has been formalized in Swift. Initializer chaining is actually part of the language, rather than just a convention. This can be confusing, but interestingly, the way you wrote initWithDoohickey: would not have even compiled in Swift.)
Subclass of UIView
I have a subclass MyView of UIView.
This subclass has a #property UIView * realView.
What I want to do
Whenever a message is sent to MyView, I want to "forward it" to self.realView, excepted for few messages.
For instance, in the implementation of MyView, I would have this override:
- (void)setBackgroundColor:(UIColor *)color
{
[self.realView setBackgroundColor:color] ;
}
Instead of overriding explicitly all the methods, can I do it automatically, at the runtime?
Exceptions
For some methods, I want to have an explicit control. For instance:
- (void)setFrame:(CGRect)frame
{
/* do stuff */
[super setFrame:frame] ;
}
Instead of overriding explicitly all the methods, can I do it automatically, at the runtime?
You implement the -forwardInvocation: method to send any unrecognized messages to the other object. -forwardInvocation is called whenever an object doesn't implement the selector that's passed to it as a sort of second chance to handle a message. You can override it to send the message to another object (which is pretty much what NSProxy does), log the messages, etc.
As #cobbal points out below, -forwardInvocation will help you with methods not implemented in your superview, but it won't handle methods that are implemented int the superview because your MyView inherits implementations of those. For example, if you want to use a UIView as a proxy for a UIButton, all the methods specific to UIButton can be handled by -forwardInvocation:, but those defined by UIView cannot. In order to get a behavior other than the inherited method, you will of course need to override. In some situations you can get around that by deriving MyView from NSObject or UIResponder instead of from UIView, thus avoiding the inherited UIView implementations, but if MyView needs to be a real view you're stuck with overriding each method.
If you think about it, it's hard to imagine how your goal could be met without explicitly overriding each inherited method. You say that you only want to forward most messages, but how can the poor runtime tell which ones you do want to forward and which ones you don't? All it can do is look for a method for the given selector and call it if it finds one, or take some action (like calling -forwardInvocation:) if it doesn't.
Update: #robmayoff points out -forwardingTargetForSelector:, which didn't occur to me but is probably a better solution in your case. It still doesn't handle the situation where you need to redirect methods that you inherit from a superclass, though.
It's entirely possible.
First you need WZProtocolIntercepter. Then use the intercepter as the normal UIView:
WZProtocolInterceptor* fakeView = [[WZProtocolInterceptor alloc]
initWithInterceptedProtocol:#protocol(TheMethodsForTheMiddleManToHandle)];
fakeView.receiver = self.realView;
fakeView.middleMan = self;
[someViewController.view addSubview:fakeView];
Put the methods you want to control in TheMethodsForTheMiddleManToHandle:
#protocol TheMethodsForTheMiddleManToHandle
- (void)setFrame:(CGRect)frame;
#end
Recently I wrote some code where I tried to refer to an outlet on a UIViewController I'd just instantiated with [storyboard instantiateViewControllerWithIdentifier] and modify the subview that the outlet pointed to before presenting the ViewController. It didn't work because the ViewController's view hadn't loaded its subviews yet, including the one that my outlet referred to, so the property just gave me a null pointer.
After (with some struggle) tracking down the cause of my issue in the debugger, I Googled around and learned, through answers like this one, that I can cause the view to load its subviews without being displayed by calling the myViewController.view getter. After that, I can access my outlet without any problems.
It's a clear hack, though, and Xcode - quite rightly - doesn't like it, and angrily protests with this warning:
Property access result unused - getters should not be used for side effects
Is there a non-hacky alternative way to do this that doesn't involved abusing the .view getter? Alternatively, are there canonical/idiomatic patterns for this scenario involving something like dynamically adding a handler to be called as soon as the subviews are loaded?
Or is the standard solution just to replace myViewController.view with [myViewController view] to shut up Xcode's warning, and then live with the hack?
On iOS 9 or newer, one can use:
viewController.loadViewIfNeeded()
Docs: https://developer.apple.com/reference/uikit/uiviewcontroller/1621446-loadviewifneeded
I agree that forcing a view to load should be avoided but I ran into a case where it seemed the only reasonable solution to a problem (popping a UINavigationController containing a UISearchController that had yet to be invoked causes a nasty console says warning).
What I did was use new iOS9 API loadViewIfNeeded and for pre-iOS9 used viewController.view.alpha = 1.0. Of course a good comment above this code will prevent you (or someone else) removing this code later thinking it is unneeded.
The fact that Apple is now providing this API signals it can be needed from time to time.
Not sure how much cleaner this way, but it still works fine:
_ = vc.view
UPD: for your convenience, you can declare extension like below:
extension UIViewController {
func preloadView() {
let _ = view
}
}
You can read explaination by following URL: https://www.natashatherobot.com/ios-testing-view-controllers-swift/
merged Rudolph/Swany answers for pre ios9 deployment targets
if #available(iOS 9.0, *) {
loadViewIfNeeded()
}
else {
// _ = self.view works but some Swift compiler genius could optimize what seems like a noop out
// hence this perversion from this recipe http://stackoverflow.com/questions/17279604/clean-way-to-force-view-to-load-subviews-early
view.alpha = 1
}
If I understand you correctly, I think there's another fairly standard solution: move the outlet modification/configuration code into a viewDidLoad method (of the recently instantiated VC).
The topic is also discussed in this question.
It would require some restructuring, but it might give you a "cleaner" design in terms of MVC if your incoming VC handled its own configuration, and it would avoid the "You should never call this method directly" stricture on loadView.
You can call [myViewController loadView] to explicitly load the view, instead of abusing the .view getter. The .view getter actually calls loadView if necessary when called.
It's still not a very nice solution, since the UIView Documentation's section on loadView explicitly instructs that
You should never call this method directly
This question already has answers here:
Is it possible to make the -init method private in Objective-C?
(9 answers)
how to block a superclass method to be called to a subclass
(5 answers)
Closed 9 years ago.
Suppose you have a UIView subclass. You define an init method "myInitWithFrame: ... andWhatNot:...". You know you won't be using the init method inherited from UIView ever and your custom init method does some vital custom initialising so that you want to force client classes to never use the inherited initWithFrame method.
Is it possible to hide the standard initWithFrame method that was inherited from UIView?
Actually, you can get compile-time warnings about calling a method on a subclass. Use the __attribute((deprecated)) attribute. If you want people to use -initWithPizza: instead of -initWithFrame:, do this:
#interface MyView : UIView
- (id)initWithPizza:(MyPizza *)pizza;
#end
#interface MyView (Deprecations)
- (id)initWithFrame:(CGRect)frame __attribute((deprecated("Use initWithPizza: instead")));
#end
Putting the -initWithFrame: declaration in a separate category is necessary to avoid Xcode complaining that you declared the method in the header but didn't implement it. Since you're just inheriting it from the superclass, that's fine; you don't have to implement it at all. But if you want to implement it to throw an exception, or call through to -initWithPizza: with a default argument, that's fine.
Of course, this won't stop UIKit from calling -initWithFrame: if it was already going to do so. But if you can guarantee that won't happen, then you're fine.
Actually, you CAN restrict with a subclass. You can override whichever methods you want to block in your subclass's .h file. You can make initWithFrame unavailable by placing the following in your .h file.
- (id) initWithFrame:(CGRect) frame __attribute__((unavailable("message")));
This will make the initWithFrame: method unavailable to anyone using your subclass.
To keep other code form calling this method, you can further restrict by putting this in your .m file.
- (id) initWithFrame:(CGRect) frame
{
return nil;
}
No. You can't prevent the users of your subclass from calling the methods of a superclass. You could override them and throw an exception inside, but that would just produce a broken subclass.
Remember that inheritance works as an "is a" extension, that is, instances of your subclasses should behave normally in any context that doesn't know about this particular subclass but knows about its superclass. It's only in places that have explicit knowledge about your subclass that you can benefit from adding extra initialization and other methods.
For example, UIKit has no knowledge of your subclass. So if you want to make your UIView subclass available from a NIB, you need to use the initialization methods that will be called by the NIB loading system, namely initWithCoder:. You can simply call your own initialization methods inside initWithCoder:. But if there are any additional parameters you would like to pass to the init method, you'll have to provide a way to configure them after initialization.
Does it matter if I call the method of the super class first thing or at the end? For example
-(void)didReceiveMemoryWarning {
/* do a bunch of stuff */
[super didReceiveMemoryWarning];
}
versus
-(void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
/* do a bunch of stuff */
}
same question for other methods like viewWillAppear, willRotateToInterfaceOrientation etc.
I am looking for meaningful differences, not just stylistic, or philosophical (although those are welcome too).
Typical Cocoa convention:
If you are performing setup, call
super FIRST
If you are performing
teardown, call super LAST
So, initialization, viewDidLoad, etc.. fall under the first case. Memory warnings, viewDidUnload, and dealloc fall under the second case.
You should also design your classes to follow this convention. Any deviations should be specifically noted.
Related SO answer:
`[super viewDidLoad]` convention
To add:
The rationale for calling super first during setup, is that you want to make sure everything is in place before you extend functionality. The corollary, is that when you are deallocating, you don't want any superclass ivars your subclass depends on to be dealloced before you have a chance to handle them.
This makes sense in reference to UI updates as well as noted in the comments below.
It depends on functionality, you either want to do something after the super class did its thing or before.
For example if superclass holds some UI elements and you extend it and your class will hold some more UI elements. To get the size to fit your whole object you would probably call super class to calculate the size of its elements and then you add to that size the size of the elements that you added.
It would not make sense otherwise - super class is not aware of your elements so it would overwritten your calculation. Again, this depends on implementation.
There's a specific case where you need to call super method as last thing:
-(void)dealloc
{
...
[super dealloc];
}