UIViewController subviews existance invariant - ios

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.

Related

drawRect depends on Views -> On Startup Custom View uses wrong frames

My view hierachy looks like this: (I use Autolayout)
ContentView
CustomView (implements drawRect)
Button
TableView
In the drawRect of the CustomView I draw a UIBezierPath from the top to the bottom through all CustomView.SuperView.subViews where the line stops if a Frame is in the way and begins to draw further till the next frame and so on.
On Startup when the DrawRect is called the Views are not finished with layouting I guess, because the frames I get from the views are not correct.
The Problem is solved if I call
customView.setNeedsLayout()
customView.layoutIfNeeded()
in the ViewDidAppear method. But this impacts performance cause it gets called twice.
What's the right way to do this?
Your approach is a violation of the principle tell, don't ask. You should be telling your view where to draw; it shouldn't be asking.
Inside CustomView:
Get rid of any calls to self.superview.
Add a property that represents the information you need to draw (like #property NSArray *framesToSkip)
In drawRect: look in your property for the information you need.
In your view controller:
In viewWillLayoutSubviews:, update your custom view's framesToSkip property with the appropriate views.
If needed, call setNeedsDisplay on your custom view.
A few general rules that, if broken, indicate you might be violating tell, don't ask:
Never access a view's superview.
Never access a view controller's parentViewController or presentingViewController.
Never import a view controller class unless it's your child / presented view controller.

Set View Controller Subview Loaded from Storyboard

I have a custom view controller I load from a Storyboard. When I try to set one of its subviews nothing happens (remains generic white view). What I don't understand is if I try to set VC.view it works fine. Why is this? Everything seems to be initialized after I load from the Storyboard. Where would I set the VS's subview?
Yes, this slightly confusing behaviour is how it is 'supposed' to work. When a view controller is loaded, its view is not - at least not until it is actually needed. See this doc for further info. Only when the view controller is presented, will it then load the view. As you have found, this is tiresome, because you often want to set some of the the subviews' properties before it is presented (say in a prepareForSegue or prior to pushViewController: or presentViewController:).
There is a work around - based on what you have observed. If you directly access the view property, the view controller will immediately load the view and all its subviews. So, if you want to set subview properties, just "touch" the view itself:
NSLog(#"View tag is %i", viewController.view.tag);
and you should then be able to access the subviews.
Alternatively, you could pass the relevant data in (non-UI) properties of your view controller, and then set up the subviews using that data during viewDidLoad or viewWillAppear.

UITextView and UILabel Subview Properties are nil

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!

IOS where to remove subviews programmatically in ViewController

I'm quite new to IOS so I'm sorry if my question is obvious.
I have set a ViewController's view in storyboard which contains other subviews.
In viewWillAppear I update these subviews depending on the object I passed to this ViewController. This object can have nil attributes and in this case I want to remove these subviews.
What is the right place to remove these subviews and is there a difference in terms of efficiency?
viewDidLoad
viewWillAppear
or viewWillLayoutSubviews ?
and will the constraints set to these removed objects also be removed?
Thx
The constraints will definitely be removed. However, it is possible to save the constraints in an array and add them back again in the future.
I would suggest making changes to the views( orientation, visibility, geometry ) in the viewWillLayoutSubviews method. You wouldn't want to do anything expensive in the ViewWillAppear method, because at that point the view is ready to be displayed to the user and it could impact how quickly the view appears to load for the user.
If you are using Storyboard and ARC do not worry about removing your views, conversely, if you are very interested to keep your memory under very tight control then do not use Storyboard and remove ARC.
What you refer to subviews are not subviews, the methods you listed are not UIView methods, and instead are UIViewController methods. However, if you have UIView objects that you are trying to remove, then those will also be handled for you. If you want more tight control, then declare them as public ivars, wrap them in #autoreleasepool {}, and set to nil in viewWillDisappear: or other method, or via delegate or notification pattern. It's relative to what you are doing and your conditions.

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.

Resources