UITextView and UILabel Subview Properties are nil - ios

In one of my view controllers, I've got the following structure:
+-View
+-Scroll View 1
+-UIView
+-Scroll View 2
...and I'm adding three subviews into Scroll View 2, one of which contains a series of UITextViews and UILabels as a form-like structure.
The problem I'm seeing is that when I try to access the properties that refer to those controls from the view controller level, they are coming up nil. However, if I set a breakpoint in awakeFromNib inside the UIView that contains the UITextFields and UILabels, the properties are valid... it's almost like the reference disappears.
I can clearly see that they exist, and if I do the following in the debug console after setting a breakpoint, I definitely get the view graph output to the console:
po [self.profileEditView recursiveDescription]
Does this sound familiar to anyone, and if so, how can I remedy it?
One thing I'm thinking of doing is just not trying to access those properties directly, since the goal is to get the view controller to be the delegate of those UITextView controls so it can respond to becoming the first responder by adjusting Scroll View 1's position when each text view gets focus. It's just more of a hassle, but if that's what I have to do I'll go that route.
I've just never seen this happen before so I thought it was pretty strange. There's definitely a lot going on in this view controller.
Additional Details:
The View Controller is defined in a Storyboard.
The UIView subviews added at runtime have XIBs that I load programatically.

I discovered what the problem is... I was inadvertently instantiating another subview as a subview inside initWithFrame (don't ask me why I was doing that... I think I was under the impression that I needed to do that to instantiate the XIB at that point), so the properties on the nested subview were valid but the properties of the top-level view (the one contained in Scroll View 2) were all nil.
I had a five-minute conversation with a developer in NZ and he asked about what I was doing there and it became readily apparent that what I needed to do was instantiate the view and its NIB in the view controller with:
ProfileEditView *view = [[[NSBundle mainBundle] loadNibNamed:#"ProfileEditView" owner:nil options:nil] objectAtIndex:0];
...instead of instantiating it with initWithFrame.
Thanks for the help, however!

Related

Update UI from another controller

I would like to update my UILabel on click the button of ContainerView contains table ViewController. When I try to do this UILabel's outlets reference shows nil value exception. I am using Swift3 with Xcode8
Most probably the problem you are seeing is due to the fact that the view that owns this label on another view controller is still not loaded.
This happens often, basically because views owned by a view controller are instantiated in a lazy manner, this means that they are loaded only when required.
To fix that before setting the value on the label, just preload the view by doing something like.
_ = another_viewcontroller_instance.view
In this way you are forcing the destination view controller to load the view and creating all the necessary connection on the xib.
Even if this fix works, this is not a good way to deal with this kind of pattern (sending info from a VC to another), but since you didn't gave us any further detail this is the only solution I have.

UIViewController subviews existance invariant

At what point in the UIViewController lifecycle is the subview property of self.view guaranteed to be populated with all the correct views? Note: I don't care about if they're laid out or not, just that they exist in the subview array. WWDC videos say that loadView, viewDidLoad, and init all don't come with that guarantee but viewWillLayoutSubviews is also late in the game.
The task I'm trying to perform in this instance is localization. In a common view controller class, I want to loop through all the subviews, see if they have a custom attribute set that identifies what localized string key is attached to that view, and then recurse through all that views subviews until the bottom of the view hierarchy is reached. Layout isn't important in this instance, just that the subviews are populated in the view controller.
At what point in the UIViewController lifecycle is the subview property of self.view guaranteed to be populated with all the correct views
The earliest point implemented by most apps is viewDidLoad. At that point you are guaranteed that self.view exists along with all the subviews from the storyboard, and that any outlets hooked up to this view controller from the storyboard have been populated.
I don't care about if they're laid out or not, just that they exist in the subview array
Exactly so. self.view and its nib-loaded subviews exist at this point, but their layout has not yet taken place and their frame is not necessarily correct. You don't care, so viewDidLoad is fine for your purposes.

iOS VIewController for UIScrollViews content

I have a UIScrollView in my app and I am adding some custom views from xib to it so you can horizontally scroll (tabbing) in ScrollView to change which one is shown. For now this works but I have a problem with connecting views to controllers.
I don't know how to choose structure of ViewControllers (how many controllers should I use, use nested controllers,...).
I have a rootView and its controller. In this rootView there is a ScrollView and this ScrollView contains some custom views (subviews) loaded from xib (using loadNibNamed method).
My question is should I use the same ViewController as for rootView also for these subviews in ScrollView? Problem is that the ViewControllers view property is already bind to the rootView (super view in rootView) so when I bind this view property also to subviews an error is occurred. Also if I create new controller for these subviews an error is occurred as well.
When I am loading subviews to the ScrollView with loadNibNamed method in ViewController of rootView, owner of these subviews is ViewController (owner argument of loadNibNamed method is set to self).
Can you tell me please, how should I solve this? What controller should I use for subviews, should I create new one or should I use existing one. Or should I use some nested controller? I am newbie in iOS development so I have a chaos in using ViewControllers right now...
If there isn't much code that is relative to controlling the sub views you could use just the root view controller. i.e A single controller for a single scene would be a good MVC approach.
If you are using it this way , don't change the view property of view controller as this messes it up for the root view - controller setup. If you just need a reference to this views you already have it with the return value of loadNibNamed. Also if you are setting the owner to self then create additional instance variable to hold the sub views(and not the view property) so that you can specify the owner from the xib itself and connect the references appropriately.
However if you have substantial business logic to be written regarding the sub views then its fine to create separate view-controllers(a single class would be fine if all the subviews behave the more or less same way if you are getting what i mean) for it. In the xib for the subviews, you can specify this class as the owner and when using loadNibNamed: you should create an object of the subviewcontroller class and specify this as the owner. This way you can modularize the whole thing.

Custom views with Storyboard

In complex screens (View Controllers) I used to separate the whole thing in smaller pieces (I call them widgets). These widgets consist basically of a MyWidget.h and a MyWidget.m file as well as a MyWidget.xib file, where the root element is a UIView and the MyWidget class is the File Owner of the UIView. In the init of this widget I do a loadNibNamed.
In my View Controller I then do a [[MyWidget alloc] init], which I add to View's Controller main view as a sub view. This, so far, works perfectly.
I'm now wondering, how to do the same with storyboard, because I cannot really start to drag in a UIView somewhere, I always have to start with an UIViewController, which I don't want to.
If there is no possible way doing this with a Storyboard, can I simply do it the old way, by using the Storyboard for my main screens and segues, and use a separate .xib file to define custom views?
Putting the widget/view in a separate .xib file works, and is appropriate especially if you might want to reference that same view from multiple View Controllers.
However, sometimes you do want to see the additional view/widget within the same storyboard, and it is possible. Here's how you do it:
Select your view controller in IB (click on the black bar below the view), then drag a UIView from the Object Library into the black bar:
When a view is in the black bar, it's instantiated like any other view in IB but just isn't added to your view hierarchy until you do so in code. Change the view's class to match your own subclass if necessary:
You can hook it up to your view controller like you would hook up any other view:
The added view shows up in your Document Outline and you can hook up actions and references there too:
Now, the problem that remains is that you can't actually see the view no matter how many times you try to click or double click, which would defeat the whole purpose of putting it in the same storyboard. Fortunately there are two workarounds that I know of.
The first workaround is to drag the view from the black bar back into your view controller's view, edit it, then drag it back into the black bar once you're done. This is troublesome but reliable.
The other workaround is more finicky, but I prefer it because it lets me see all my views at the same time:
Drag a UITableView from the Object Library into your newly added view.
Then drag a UITableViewCell into that UITableView.
Once you do that, your view pops out magically by the side, but you have a UITableView that you don't want. You can either resize that to 0x0, or you can delete it and your UIView will (usually) still stay visible.
Occasionally the secondary view will become hidden again in IB. You can repeat the above steps if you deleted the UITableView, or if the UITableView is still in the hierarchy you just need to click on the UITableViewCell and the view will appear again.
The second method works for UIViews but not so well for UIToolbars and is impossible for UIButtons, so the cleanest solution I've found when you need to include lots of different subviews is to attach a single secondary UIView to your view controller as a container that never gets shown, put all your secondary views in there, and use the UITableViewCell trick to make everything visible. I resize my dummy UITableView to 0x0 to make that invisible. Here's a screenshot of how it all looks like together:
If you're just looking to make your view controllers else-where(and not in your story-board), then there's a pretty simple way to accomplish this:
1) Create your CustomViewControllers(abcdController in the code I tried) with their individual xibs as usual.
2) Add a UIViewController(or whatever was the superclass of your CustomViewController) to the story-board.
3) Set the CustomClass to CustomViewController instead of UIViewController as shown here:
4) Finally, in your viewDidLoad, load the custom xib and you're done.
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSBundle mainBundle] loadNibNamed:#"abcdController" owner:self options:nil];
// Do any additional setup after loading the view from its nib.
}
I think you can do something like this to get instance of specific viewcontroller from Storyboard and use view on top of it.
ex:
MyViewController* myViewController = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"myViewController"];
UIView* view = myViewController.view; //Get the view from your StoryBoard.
Hope this helps
Thanks
Vijay

Custom Sized Nib File Controllers and Interface Builder

I've recently been introduced to development for iOS by a friend and have begun to experiment with the interface builder and view controllers. One thing I'm finding is that when using a nib in conjunction with a view controller, your view controllers ivars are quickly polluted with views you may never actually reference. I would like to modularize the key components in my main nib into several different views. I have two questions regarding this:
How can I create a nib file for a custom sized view (one that doesn't fill the entire screen)?
How can I add the newly modularized to my main nib (all of the classes I would create for the components would be view controllers not views)?
Assuming I were to alloc and init the view controllers and add them to the main view programmatically, how could I position the custom sized views since I have already called initWithNibName:bundle:. I can't call initWithFrame: right?
Answers would be much appreciated!
Thanks,
PhpMyCoder
EDIT
I've discovered the answer to my first question. It seems that in the attributes inspector of your nib file you must disable the status bar (change it to unspecified) to enable the editing of the height and width parameters in the size inspector. However, I still am unsure of how to add these custom nibs and their view controllers to another nib without coding them in with initWithFrame: and addSubview:. Any ideas on adding a view controller's view to a nib in IB?
EDIT 2
Added question 3 (or 2 depending on how you think about it).
EDIT 3
I seem to be hastily asking questions today. A simple call to setFrame: will deal with sizing and positioning (and you can even append it on to your init function initWithNibName:bundle:frame:). Still not sure how to add the view (created by a nib) from a view controller to another nib in Interface Builder. If this is possible, I'd love to hear how.
Remember to not get ViewControllers and Views confused.
They are both in a hierarchy, and each ViewController has/controls a "main" view (which likely has bunches of subviews).
You don't add a view to a nib. A nib is a mechanism to help you assemble views. (A NeXT Interface Builder file, if we delve into nomenclature.)
This is how you load a nib:
[[NSBundle mainBundle] loadNibNamed:#"foo" owner:self options:nil];
Normally, you give a nib name to a controller and it does it for you, but you can do this to. There are complex and tricky ways to access the content. The following is the standard way to do it.
The owner you pass in must be the type declared as the owner in that nib file. It should have some of its outlets connected to objects in the nib file. After you load the nib file, they'll just "be there". If you called this twice, it would overwrite the first ones and replace them with the second ones. (mostly harmless, definitely useless)
So, typically, you wired it up to view. Now you have a view that's floating around in memory and not connected to the view hierarchy of the application. Time to do that. You must take view and figure out where it belongs in the pre-existing hierarchy and call [someOtherView addSubview:self.view] no it, and it will appear. Yes, if you want to explicitly place/size it, you will need to do that. Note that view.frame is in the superview's coordinate system.

Resources