iOS ViewController Life Cycle Best Practices [closed] - ios

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 7 years ago.
Improve this question
Does anyone know of a good tutorial that explains in depth the view controller life cycle. I have read the docs so please do not link me to them. I am just looking for a practical explanation of each function such as viewDidLoad and viewWillAppear, viewWillLayoutSubviews, etc, and when best to use them with examples in Swift. If there are no tutorials would anyone be willing to explain them here in their answer.

I show the code for you with using swift.
import UIKit
class LifeCycleViewController: UIViewController {
// MARK: -property
lazy var testBtn: UIButton! = {
var btn: UIButton = UIButton()
btn.backgroundColor = UIColor.redColor()
return btn
}()
// MARK: -life cycle
override func viewDidLoad() {
super.viewDidLoad()
println("View has loaded")
// set the superView backgroudColor
self.view.backgroundColor = UIColor.blueColor()
// add testBtn to the superView
self.view.addSubview(self.testBtn)
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
println("View will appear")
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
println("View has appeared")
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
println("View will disappear")
}
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
println("View has desappeared")
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
println("SubViews will layout")
// layout subViews
self.testBtn.frame = CGRectMake(100, 100, 100, 100)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
println("SubViews has layouted")
var testBtn_Width = self.testBtn.frame.width
println("testBtn's width is \(testBtn_Width)")
}
}
Here is the result:
View has loaded
View will appear
SubViews will layout
SubViews has layouted
testBtn's width is 100.0
SubViews will layout
SubViews has layouted
testBtn's width is 100.0
View has appeared
You can see the ViewController's life cycle clearly.

Not bad example on this image.

This is the way I see it.
ViewDidLoad - Called when you create the class and load from xib. Great for initial setup and one-time-only work.
ViewWillAppear - Called right before your view appears, good for hiding/showing fields or any operations that you want to happen every time before the view is visible. Because you might be going back and forth between views, this will be called every time your view is about to appear on the screen.
ViewDidAppear - Called after the view appears - great place to start an animations or the loading of external data from an API.
ViewWill/DidDisappear - Same idea as WillAppear.
ViewDidUnload/ViewDidDispose - In Objective C, this is where you do your clean-up and release of stuff, but this is handled automatically so not much you really need to do here.

Related

How to change colour of scrollbar when calling flashScrollIndicator() in Swift 3

I just discovered how to properly use flashScrollIndicator but I'm having trouble changing the colour of the scroller when it flashes. I want to change it to red. This is what I have for my function to flash the scroller when the viewTable loads up:
override func viewDidAppear(_ animated: Bool) {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.tableView.flashScrollIndicators()
}
I also was able to create a function that actually changes the scroller but only when the user actually scrolls, not when the scroller simply flashes. If its any help, this is the code for it:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let verticalIndicator = scrollView.subviews.last as? UIImageView
verticalIndicator?.backgroundColor = UIColor.red
}
Any help would be great! Thanks.
You just need to put those two pieces together. If you add the color-changing code to viewDidAppear, it should work just like it does in scrollViewDidScroll.
override func viewDidAppear(_ animated: Bool) {
let verticalIndicator = tableView.subviews.last as? UIImageView
verticalIndicator?.backgroundColor = UIColor.red
self.tableView.flashScrollIndicators()
}
While this is possible, I don't recommend it for a few reasons:
It's fragile. Since this isn't a readily available public API, there's no guarantee this will work in future iOS versions.
It's probably a bad design choice. Do you really want to point the user's attention to a differently colored scroll bar? Most users familiar with other apps are used to the default look of the scroll bars, so it communicates their position in the view without much extra attention. Also it doesn't look very nice when I just tried it.
The alternative solution is to simply set the scroll indicator style to one of the available options (default, white, or black).
tableView.indicatorStyle = .default

Swift App Xib - Second ViewController - Labels not showing for 30 seconds but Buttons etc are. How can I solve?

Swift App Xib - Second ViewController - Labels not showing for 30 seconds but Buttons and other controls showing immediately. What could this be / how can I solve this?
Xcode 7.3.1
Swift 2
This is happening on the device and in the simulator
---> Video of Issue <-----
I present from first viewController like this:
secondViewController.user = user
self.presentViewController(secondViewController, animated: true, completion: nil)
Second View Controller
override func viewDidLoad() {
super.viewDidLoad()
print("ViewController did load")
print("selected facility is: ", user?.selectedFacility)
// Do any additional setup after loading the view.
}
// required init(coder aDecoder: NSCoder) {
// super.init(nibName: "ListViewController", bundle: NSBundle.mainBundle())
// }
override func awakeFromNib() {
print("awake from nib")
}
override func viewWillAppear(animated: Bool) {
print("viewWillAppear")
}
override func viewDidAppear(animated: Bool) {
print("viewDidAppear")
}
override func viewDidDisappear(animated: Bool) {
print("viewDidDisappear")
}
UPDATE
If I set the text in viewDidLoad the label appears at the same time as everything else.
override func viewDidLoad() {
super.viewDidLoad()
print("ListViewController did load")
labelX.text = "heyVC"
Adding the outlet itself didn't change anything. It was actually programmatically setting the text that fixed it. I think this is a bug in IB. I will file with apple.
Bug number is 27029176
See if you have set some custom font of label and the font file could not be found by system(like if you have deleted the file). If system font is not set in Label , try setting system font and re-run to confirm.
Are you doing anything with NSURL / NSMutableURLRequest / dataTaskWithRequest?
I am experiencing exactly the same thing with the 30sec delay. When I am commenting out my code which is fetching data from an URL (with the above mentioned methods) the labels are showing instantly. When the code is executed, the labels are displayed with a delay. I guess the fetching somehow makes things asynchronous.
Not exactly a solution but maybe a starting point for further research. Please let me/us know if you have new ideas or the solution. Thanks. :-)

Swift: dispatch_async is effect is inconsistent for animation

I have a globalstate in my app. Depending on the state the GUI is different.
When I go from the start View A to View B I have globalstate 3
It should show an information screen, but it doesn't. BUT: When the View B has loaded only once and I jump from View C/D/E back to View B, then the code work perfectly. (You have to be in View A to get in View B.)
I use a lot dispatch_async(dispatch_get_main_queue.. that isn't good style, is it?
Why is my animation not loading at the beginning? What is good style? Thank you for answers and sorry for mistakes (english isn't my mothertongue)
override func viewDidLoad() {
super.viewDidLoad()
dispatch_async(dispatch_get_main_queue(), {
self.animateTheInformationViewWhenGlobalStateIsThree()
})
}
func animateTheInformationViewWhenGlobalStateIsThree() {
print("GLOGBALSTATE \(globalState)") //it is 3
if globalState == 3 {
setGlobalState(3)
dispatch_async(dispatch_get_main_queue(), {
GUITools.animateTheInformationView(self.tableView, animateBottomLayout: self.animationBottomConstraint, value: self.negativValue)
})
print("THE POSITIV VALUE THE NEGATIV")
}
//GUITools-Static-Class:
class func animateTheInformationView(tableView: UITableView, animateBottomLayout: NSLayoutConstraint, value: CGFloat) {
dispatch_async(dispatch_get_main_queue(), {
animateBottomLayout.constant += value
UIView.animateWithDuration(Constants.animationTime, animations: { () -> Void in
tableView.layoutIfNeeded()
},completion: {
(value: Bool) in
})
})
}
EDIT
With viewDidAppear it works. But the animation isn't a real animation. The tableView "jumps". So there is no sliding/animation.
I deleted all dispatch_async..
override func viewDidAppear(animated: Bool) {
self.animateTheInformationViewWhenGlobalStateIsSeven()
}
viewDidLoad() does not mean that your view is already visible. Since it's not visible yet you cannot apply animations to it.
viewDidLoad() is only meant to configure your view controller's view and set up your view hierarchy - i.e. to add subviews.
What you want to use is viewWillAppear() (or viewDidAppear()) to start your animation as soon as the view becomes (or became) visible.
Also all the dispatch_async calls are most likely unnecessary. You usually only need them when you are not on the main (= UI) thread. Simply remove them.

Adding View Programmatically from ViewController super class doesn't show in front

I have a BaseViewController that my UIViewControllers extend so i can have explicit functions that i dont need to rewrite. Something i would like would be a functions such as self.showSpinner() and the viewController would show the spinner
My Code looks like this
class BaseViewController: UIViewController {
var actvIndicator : UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
self.actvIndicator = UIActivityIndicatorView(activityIndicatorStyle: .WhiteLarge)
self.actvIndicator.color = UIColor.blackColor()
self.actvIndicator.backgroundColor = UIColor.blackColor()
self.actvIndicator.frame = CGRectMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2, 100, 100);
self.actvIndicator.center = self.view.center
self.actvIndicator .startAnimating()
self.view.addSubview(self.actvIndicator)
self.actvIndicator.bringSubviewToFront(self.view)
self.edgesForExtendedLayout = UIRectEdge.None
self.navigationController?.navigationBar.translucent = false
}
func showSpinner(){
self.actvIndicator.startAnimating()
}
func hideSpinner(){
self.actvIndicator.stopAnimating()
}
}
And my viewcontrollers looks like this
class MyProjectViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.showSpinner()
}
}
MyProjectViewController have UITableView that fills the entire screen. When i set tblProjects.alpha = 0 i can see the spinner. But i want it in the front.
i also tried self.view.bringSubviewToFront(self.actvIndicator)
What am i missing?
A couple quick notes before I get into what I think your problem is:
When you add a subview it is automatically added to the top layer, no need for the bringSubviewToFront: in viewDidLoad: (which is being used wrong anyway).
You should not set view frames in viewDidLoad: (e.g. centering a view). Frames are not setup yet, so you should move that to viewWillAppear: or some other variant.
Now your issue is most likely a view hierarchy problem (further confirmed by your comment) and thus can probably be fixed by pushing the spinner to the front every time you want it to be shown, like:
func showSpinner() {
self.view.bringSubviewToFront(self.actvIndicator)
self.actvIndicator.startAnimating()
}
The problem here stands on the fact that table view is draw after you are calling self.view.bringSubviewToFront(self.actvIndicator). A possible workaround for this is to call bringSubviewToFront when showing the spinner
func showSpinner(){
self.view.bringSubviewToFront(self.actvIndicator)
self.actvIndicator.startAnimating()
}

viewDidAppear & viewDidLoad Are Are Initializing Positions Too Late

I am looking to update a UIView thats in a storyboard (and instantiated from the storyboard) when it loads in the app.
I need to position a few icons in a dynamic way (that the interface builder doesn't let me do quite yet). However, if I put my code in viewDidAppear or viewDidLoad, it seems to be getting called too late.
Here is what happens the first few second or two when the view loads:
And then a bit later it goes to the right position (as the code was called).
My question is where do I need to initialize the positions of these objects so they dont snap over a second later. viewDidLoad and viewDidAppear are too late? Why!? :)
override func viewDidAppear(animated: Bool)
{
initView()
_gridLines = false
}
func initView()
{
_cameraFeed.initAfterLoad()
//center the buttons on half distance between middle button and screen edge
var middleDistance:CGFloat = _swapButton.frame.origin.x + _swapButton.frame.width/2
_linesButton.frame.origin.x = middleDistance/2 - _linesButton.frame.width/2
_flashButton.frame.origin.x = middleDistance + middleDistance/2 - _flashButton.frame.width/2
_selectPhotos.frame.origin.x = middleDistance/2 - _selectPhotos.frame.width/2
}
Swift and objc answers welcome!
Try putting the code setting the frame in viewWillAppear instead of viewDidAppear.
Also, you should call the super.
override func viewWillAppear(animated: Bool) {
initView()
_gridLines = false
super.viewWillAppear(animated)
}

Resources