UIViewController lifecycle function loadview() frame question - ios

I have been mainly working with storyboard until I recently moving to only creating controller with code.
I followed our coding standard to create some subview in the loadView() function, inside which, the first line is super.loadView().
From what I observed, after I called super.loadView(), the frame of self.view is already set correctly with the viewController itself, which is exactly the frame of the viewController.
My more experience colleagues are saying this was 100% not working in the old days that it should give u CGRectZero instead and probably I should not rely on it.
I want to hear more suggestion from other people.
Here is my sample project setup:
1. create a simple proj
2. add a button in the first VC
3. create second VC by code, override loadView() function in second VC, call super.loadView() there and print self.view.bounds next line
4. self.present or use navigation controller from VC1 in the button action to present or push to VC2
5. it always give me correct frame of the second VC.
Please let me know.
----------- Edit -------------
To clarify my question, I know the lifecycle functions like viewDidLayoutSubviews or layoutSubViews to return the correct view. I am NOT asking these.
My question is why loadView() IS returning me the CORRECT frame now.

Unfortunatelly I cannot give any insights as to what is happening under the hood - what I can do though, is tell you that according to the documentation you shouldn’t be calling super.loadView() :
You can override this method in order to create your views manually. If you choose to do so, assign the root view of your view hierarchy to the view property. The views you create should be unique instances and should not be shared with any other view controller object. Your custom implementation of this method should not call super.
(Emphasis mine)

1) I have been mainly working with storyboard until I recently moving to only creating controller with code.
I followed our coding standard to create some subview in the loadView() function, inside which, the first line is super.loadView().
Since you are using xibs, use viewDidLoad over loadView because of If you use Interface Builder to create your views and initialize the view controller, you must not override this method. as stated in loadView in Apple Docs.
2) My more experience colleagues are saying this was 100% not working in the old days that it should give u CGRectZero instead and probably I should not rely on it.
I want to hear more suggestion from other people.
The proper frame for the viewController.view will be when viewWillLayoutSubviews is called. As for its subviews, viewDidLayoutSubviews is what you will need
view frame inside loadView and viewDidLoad are Nib frame size. Your colleagues are correct that it will not return a proper size. This is noticeable upon running an iPad app with viewcontroller presented modally, or run your app on different screen size as contrast to what the device is in xib
ALSO, this question is directly related to yours:
Why am I having to manually set my view's frame in viewDidLoad?

Related

Possible to perform segue without the use of StoryBoard?

I have been creating my Swift project completely programmatically thus far. I have created detailed Navigation Controllers and Table View Controllers without touching the StoryBoard (SB) once. I have come to a point where I would like to pass data from a TableViewCell click onto another ViewController, however, I believe that I need to use a segue and create/identify it inside of the SB. Since this application has become pretty complex over time, it has become quite difficult to mimic all of the view controllers inside of the SB, and creating any changes inside of the SB is not reflecting any changes inside of the Views on the simulator (I have paired the views inside of the SB to their respective class, yet nothing is working. Even the segue is not being recognized when the identifier matches up). Therefore, I have a couple of questions.
Is it possible to perform a segue programmatically? In other words, is it possible to pass data from one view controller to another without touching the story board?
Is there a simple guide or technique one can follow in order to mimic their code inside of the StoryBoard? With a simple application, it shouldn't be too difficult. However, is there any other way in order to portray the application in the StoryBoard if someone has been creating it completely programmatically?
Interface Builder (IB) is just a GUI for programmatic development. Anything you can do in IB you can definitely do programmatically but not everything you can do programmatically you can do in IB, because IB is just for interface building—and just some interface building, not all.
segue is just IB terminology. In iOS, you can only display a view controller three ways: push (via UINavigationController), present (via a presentation object vendor UIViewControllerTransitioningDelegate), or show (a more generic approach to displaying a view controller).
Since you're new to programmatic iOS development (the best kind, IMO), a quick 101:
class ProgrammaticViewController: UIViewController {
override func loadView() {
// do not call super.loadView()
// most IB developers don't even know this method exists
// because this is where IB does its work
// add all of your view objects here (scroll views,
// table views, buttons, everything)
setView()
addTableView()
...
}
override func viewDidLoad() {
super.viewDidLoad()
// do call super.viewDidLoad(), however
// do your post-view setup here, like adding observers
// or fetching data
// this method is called after the entire view
// has been loaded into memory so consider that
}
// other lifecycle events that come after viewDidLoad where you
// can perform last-second work include viewDidLayoutSubviews(),
// viewWillAppear(), viewDidAppear(), etc.
deinit {
// do any cleanup here like deactivating timers, removing
// observers, etc.
}
// MARK: Methods
func setView() {
// if you're creating your view controller programmatically,
// you must create the view controller's actual view property
// and it must be done before adding any subviews to the
// view controller's view
view = UIView()
view.frame = UIScreen.main.bounds
view.backgroundColor = UIColor.white
}
}
If you are using auto layout (and you probably should be), you don't always need to explicitly set the frame of the view with view.frame = UIScreen.main.bounds. If it's the root view controller of the app, it's not needed—the window owns that view controller and it will set the frame. If you've then put a UINavigationController in the root and you're using auto layout, none of the view controllers you push to ever need their frame set as well. Therefore, the only real time you need to explicitly set the view's frame using something like view.frame = UIScreen.main.bounds is when you present a view controller modally. This is because a modally-presented view controller is not owned by the window or a navigation controller; it exists temporarily inside a transient container view. In this case, you must set its frame.
Passing data forward programmatically is simpler than it is for IB developers. Just instantiate a view controller, inject one of its (non-private) properties with a value, and push, present, or show it. Here's what a UITableView delegate for didSelectRowAt may look like:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let p = indexPath.row
let detailViewController = SomeDetailViewController()
detailViewController.someProperty = searchResults[p]
navigationController?.pushViewController(detailViewController, animated: true)
}
And this is obviously doable because you've created that property in that view controller and not given it a private access modifier.
class SomeDetailViewController: UIViewController {
var dataObject: SomeType? // injectable
private var notInjectable: SomeType?
}
UPDATE: IB is clearly not the future of iOS development as far as Apple is concerned, with the introduction of SwiftUI, which is another way of programmatically developing iOS. Personally, I hate GUI programming and I'm happy to see it slowly phase out.
Is it possible to perform a segue programmatically?
Sure. UIStoryboardSegue has initializers that you can use to create a segue, and it has a perform() method that you can call to make the segue do it's thing. But segues are mostly a tool that makes it easier to transition between scenes in a storyboard, so there's little reason to want to do that if you're not using storyboards in the first place.
In other words, is it possible to pass data from one view controller to another without touching the story board?
Again, yes. Storyboards didn't even exist until iOS 5, and you can bet people were writing apps with transitions between view controllers well before that. The answer you've already gotten about how to do that is fine. More generally, though, view controllers are just objects, and all that one needs to send information to another is a valid reference to the destination controller.
However, is there any other way in order to portray the application in the StoryBoard if someone has been creating it completely programmatically?
If you mean that you want to switch over to using storyboards, then yes, there are some good ways to do that. If you mean that you want to somehow represent what your app is doing programmatically inside a storyboard but continue to do everything programmatically, then no, there's no way to do that (and really no point in doing it).
Transitioning to storyboards doesn't have to be an all or none kind of thing. You can model just one view controller in a storyboard and load it as needed using init(name:bundle:) (to initialize the storyboard itself) and instantiateViewController(withIdentifier:) (to load a particular view controller), and then just use that view controller the same way you do now. All the code that you use to set up that view controller and it's view hierarchy can then be removed, since you're now loading it from the storyboard instead. Once that's working, repeat the process for other view controllers, expanding your storyboard and excising the setup code as necessary.

What is viewDidLoad() for? [duplicate]

This question already has answers here:
Looking to understand the iOS UIViewController lifecycle
(11 answers)
iPhone SDK: what is the difference between loadView and viewDidLoad?
(8 answers)
Closed 5 years ago.
Can someone please help me understand what exactly viewDidLoad()does? I know it's called when the view controller is first loaded into memory. I addition, I am aware I can treat it as a main(). But I would like to know more about it. What does it reference too? UIView? It loads and treats all the buttons, labels, etc? Having a detailed and well explained overview would help!
In addition, I'm also confused about override. Does it add code to existent viewDidLoad()?
Thank you!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
P.S I am new here, feel free to leave any comments about my question formatting.
viewDidLoad is the method that is called once the MainView of a ViewController has been loaded. This is called after loadView is called.In the image you can see the MainView and other views within it. As soon as MainView has been loaded whatever will be included within the MainView you can get access to it (YES, all the buttons, labels, etc) in the ViewDidLoad method.
I'm also confused about override. Does it add code to existent viewDidLoad()?
As we know if subclass provides the specific implementation of the method that has been provided by one of its parent class, it is known as method overriding.
Here, the viewDidLoad in the superclass (UIViewController) is only an empty function. You need to Just override the function for your initial setup of the view once it has been loaded.
viewDidLoad is called when the ViewController has loaded its view hierarchy into memory. This is the point where you can perform your customized initialisation for your view controller.
For instance, if your view controller has a UILabel and you want to set a custom text to it, this is the point where you do that.

When to modify constranints so it will override storyboard setup?

The problem of mine is, that I setup the constraints in the storyboard. But then I want to adapt it before the view appears on screen.
So for example if I lay down 5 views in a line and I want to hide one of then on smaller phones, where do I put the code for this. I tried viewWillApper, but the storyboard setup loads after that method and override my setup.
I found viewDidLayoutSubviews but now Im wondering if this is the best approach. Because I think this gets called many times later but I want some method that is called only after constraints are modifyed by storybord setup. So just before the main view appears on screen.
Hope you'll guide me in the right direction.
The constraint will finish setting when viewDidLoad gets called. viewDidLayoutSubviews will be called by the system in proper time or when you change you orientation.

iOS Unable to simultaneously satisfy constraints in loadView

I'm getting the Unable to simultaneously satisfy constraints error.
I don't get the error when the viewcontroller establishes itself using viewDidLoad but it does get the error when using loadView...
Why is this happening?
I thought the only difference between loadView and viewDidLoad is that viewDidLoad occurs after loadView. At least, that seems to be the going explanation...
I don't know whether my answer would completely address your issue,but it might act as a starting point to resolve your issue.
There are quite a few points(you might be aware of) which needs to be noted before you use loadView:
loadView is a method that gets called when view is loading,viewDidLoad is method that will be executed after the
view is loaded.
loadView is recommended when you are willing to create the view programatically instead of setting in xib file,other wise there is
no point in using or calling loadView(It all depends on your
application requirement though).
Don't call super loadView if you initialise your view from story
board or xib file.
If you initialise your view from story board, do not call
[super loadView] and you must assign your rootView to self.view
property, if you call [super loadView] inside the method, you better never override this method and put your code in viewDidLoad method..
If you are using xib file to set up the view,do the modifications of objects set up in viewDidLoad and don't call loadView,if you are creating the view programatically,initialise the view in the loadView and do the additional set up in viewDidLoad.
The constraint error generally pops up in story board or xib file,when "Use AutoLayout" is selected,if you unselect it,the error disappears,since you are creating the view programatically in loadView,it is your responsibility to see to it that the auto layout option is disabled i.e. you need to make use of setTranslatesAutoresizingMaskIntoConstraints property by setting it to "NO".
You can also refer to some of related questions here and there which could well get you out of this issue.
Thanks and happy coding :)

How to instantiate a particular view controller with storyboard in iOS at early stage of loading?

When using tabs with storyboard in iOS 5, some of them may take quite a long time to initialize when switching to it (for example, a tab containing GLKViewController).
This happens because an amount of work in viewDidLoad method in this controller could be very big.
Is there a way to initialize particular view controller (and call it's viewDidLoad method) defined in the storyboard at early stage - when an application starts? Having done this, the delay should be eliminated.
Are you sure it's the view controller's instantiation and not the viewDidLoad method? The view controllers are probably all created when the storyboard is unpacked, but a view controller tries to delay loading its actual view object as long as possible; viewDidLoad isn't called until the view property of your UIViewController subclass is accessed.
So a way around this could be to manually access the view property:
__unused CGRect frame = [[tabBarController.viewControllers objectAtIndex:index] view].frame;
If the slowdown is, in fact, in the instantiation and the view controller isn't being created until you switch to that tab, then you'll have do force the view controller to be instantiated by accessing it programmatically, like in the above example.
Calling the frame of the vewcontroller or the .view property will most likely work,
but i dont advice you to mess up with the viewcontroller initializations and view settings
For the following reasons
changes you make will not be standard, they will be tricks and hacks that will later on get out of hand
changes that you make will not be carried with you easily to other projects you create
If i faced a problem like this i would create the GLKViewController separately for example in the app delegate and held it there, untill the viewDidLoad gets called in the viewController, then i would move this initilized GLKViewController to the viewController

Resources