可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I am using storyboard instantiateViewControllerWithIdentifier:
and I'm noticing that all the IBOutlets
I have wired up are still nil. However, the IBActions
I have wired up work. The view and controller are linked (i.e controller.view is not nil), and if I show the view it displays what I am expecting.
What am I missing?
Here's my setup:
- I've got a View Controller defined in my storyboard. I have given it an identifier which is the same identifier I use when invoking
instantiateViewControllerWithIdentifier:
- I've set up the view's owner by clicking on the View Controller (just under First Responder) and under the Identity Inspector set the Custom Class to be the same name as the class I want to wire the view to.
- Then I open up Assistant editor, and control dragged the UI elements to create the
IBOutlets
and IBActions
.
回答1:
The view seems to be initialized properly only after it is accessed first. The problem goes away when calling
[self presentViewController:vc animated:NO completion:nil];
or more simply
[vc view];
回答2:
[Use me as bad example]
Maybe not a good idea after all, it works but it violates the regular loading making the app unstable ^_^.
I'll leave the answer here in case someone else want to know if you do it.
I was having the same issue, however the custom component I designed isn't loaded via presentViewController (load in overlay to the previous view)
You can simply call
myViewController.loadView()
//Swift
[myViewController loadView]
//Obj-C
回答3:
Outlets are established with Key Value Coding, you could put this into your view controller subclass:
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath {
[super setValue:value forKeyPath:keyPath];
}
- (void)setValue:(id)value forKey:(NSString *)key {
[super setValue:value forKeyPath:key];
}
And put a breakpoint on the two calls to super.
In the debugger, I'd try:
Comparing 'self' to the view controller you think you're applying the outlets to. Perhaps your storyboard has a second view controller you're not expecting. When you said you 'set up an owner' I thought that was strange terminology. Did you drag out one of the cube objects to represent your view controller in addition to the representation that's already there?
Compare [self valueForKey:key] or [self valueForKeyPath:keyPath] or just [self outletName] with the passed in value after the call through to super. Maybe you have setter that isn't doing what you intend. I've seen one mistake like this come up a couple of times where people have an outlet for an object with a name like "firstName" and then an action with the selector "setFirstName:". That confuses key value coding, and you end up with setFirstName: getting called during nib loading with intent of establishing the outlet, but the setter for the outlet is implemented as an action method, so the outlet is never set.
回答4:
Setting the frame for the instantiated ViewController seems to set the IBOutlets.
ex:
DemoViewController *demoVC = [[self storyboard] instantiateViewControllerWithIdentifier:@"demoVC"];
demoVC.frame = self.view.frame; //or CGRectMake(0, 0, 1015, 604);
This would initialize the IBOutlets of DemoViewController like Label, TextField, Button, Table etc,.
Hope this helps.
回答5:
If the object you're linking to is a top-level entity in the scene, like an ArrayController, the reference needs to be strong (not weak!) since it's not retained by the view hierarchy. A weak reference can cause a problem like what you describe.
Look here for more information: Should IBOutlets be strong or weak under ARC?
回答6:
People gave solution of how to get the view loaded (some of which you should never use (loadView()
) so here is a way to check whether your view is already loaded
I ran into the problem because I had a property with a didSet
observer to update the UI which obviously doesn't work if the outlets are not yet set, so here is some example code how to work around it:
var name: String? {
didSet {
updateNameLabel()
}
}
override func viewDidLoad() {
super.viewDidLoad()
updateNameLabel()
}
func updateRoomLabel() {
guard isViewLoaded() else {
return
}
[...]
}
So now when you display the outlets get updated but also every time you update the property