I have a view controller that is a child view controller of my window's root view controller. That child view controller has a table view and when i select a row it tells the parent view controller to present a view controller modally. The modal view controller, however, never appears. I created a bare bones test view controller that just prints viewDidLoad and viewWillAppear. What I notice is that when I call parentVC.present(testVC, animated:true, completion:nil), viewDidLoad is run, but viewWillAppear is not. viewWillAppear is only then called when I interact with the UI in some way. Whether tapping, panning, scrolling or whatever.
I've spent hours trying to debug this. It doesn't seem like the main queue is blocked and I've reduced the problem to its bare bones. The modally presented view controller's viewWillAppear is simply not called until I interact with the UI again.
What could be causing this symptom?
In comments, you mention that you're instantiating your view controller with
let vc = TestVC()
where TestVC is presumably a (largely empty) UIViewController subclass.
A view controller needs a view created either via either storyboard scene (using instantiateViewController), a NIB or, in very rare cases, a view you create in loadView (which you shouldn’t be confused with viewDidLoad).
I’d suggest creating a storyboard scene (assuming you are using storyboards), give it a storyboard ID, and then use instantiateViewController:
let vc = storyboard.instantiateViewController(withIdentifier: "foo")
But just having a UIViewController subclass called TestVC and instantiating it with TestVC() won’t work.
In our discussion, you said you wanted to do this programmatically with no NIB nor storyboard. If so, use loadView. See https://stackoverflow.com/a/37964249/1271826 for an example.
Related
I just read an interesting article that introduced me to a new concept that I have never heard before, which is adding a ViewController as a child of another ViewController. The article uses the UIActivityIndicatorView as an example to demonstrate a common use for when to use the addChild method and this got me wondering why would you use that method instead of just presenting the view controller.
The part I don't fully understand is the benefit of using that specific method since both presenting and adding as a child could be modular in a way that they can be reusable.
When would you use addChild instead of just presenting it? When does that make sense?
addChild:
let parent = UIViewController()
let child = MyViewController()
parent.view.addSubview(child.view)
parent.addChild(child)
child.didMove(toParent: parent)
Present:
let storyboard : UIStoryboard = UIStoryboard(name: "StoryboardName", bundle:nil)
let myVC = storyboard.instantiateViewController(withIdentifier: "myViewController") as! MyViewController
self.present(myVC , animated: true, completion: nil)
The difference is that when you present a controller, it becomes modal, and when you add it as a child controller, it doesn't. The difference is in semantics and use cases.
Modal
As an example of modal window, imagine a window with an OK button in macOS that prevents you from interacting with the parent window until you close it.
Hence why there can be only one view controller presented by a particular controller: the presented controller becomes “main” for user interaction, and the user must close it to return to the parent window.
A typical example of a modal window is an alert.
Child
But a child view controller is a different story. There can be any amount of them on a particular VC.
The parent controller becomes sort of container for child view controllers. For example, there can be a dashboard with multiple panels, and each panel can be a separate controller. This allows to separate logic between different pieces of an application instead of cramming everything into a single controller, to implement user customization in easy way, etc.
A typical example of a parent-child controller interaction is UINavigationController: the navigation controller is the parent, and all view controllers pushed into it are its children. We don't make an uber controller with the whole application's logic, we separate it between different screens, and the navigation controller is only responsible for presentation.
It's very simple. addChild is when you want to embed the view controller in you parent view controller (you will also need to follow up with a view.addSubview(child.view)). present is when you want to, well, present it... think of it as a change of screens.
addChild:
present:
Please help me with this. I have created a simple project with two views as shown. I have attached the images for my storyboard and swift files. So, I read that viewdidload will be executed only once while loading the view into the memory. But, when I make a transition from secondview to the firstview the viewdidload is executing again and so is the print statement in the viewdidload method.
Someone please explain me this.
viewDidLoad is not called once for the Application. It is get called once for that viewController when the view holds memory and loaded.
So as many number of of time you push to the viewController, that many times it will call the viewDidLoad
viewDidLoad() — Called when the view controller’s content view (the top
of its view hierarchy) is created and loaded
viewWillAppear() — Intended for any operations that you want always to
occur before the view becomes visible.
For more info about this look at the link : https://developer.apple.com/library/content/referencelibrary/GettingStarted/DevelopiOSAppsSwift/Lesson4.html
So if the view is already in memory (Like your case), then no need to push again, only need to pop back by this code
self.navigationController?.popViewControllerAnimated(true)
You should not make transition from secondViewController to firstViewController for back. Pop the second view controller by this code to back:
self.navigationController?.popViewControllerAnimated(true)
When you make a transition it makes a new instance from your firstViewController but when you pop the second view controller it dismiss your secondViewController and shows your last viewed viewController again.
Or
in the case that you are not using navigationController you should use below code to dismiss your secondViewController
self.dismissViewControllerAnimated(true, completion: {});
The main point is that you should not use new transition for back.
The simplest way:
1.First embed your ViewController in NavigationController
2.Call to this (instead of create segue for backing)
navigationController?.popToRootViewController(animated: true)
viewDidLoad will be called only once
Apple's view controller programming guide states (emphasis added):
The view controller that calls the presentViewController:animated:completion: method may not be the one that actually performs the modal presentation. The presentation style determines how that view controller is to be presented, including the characteristics required of the presenting view controller. For example, a full-screen presentation must be initiated by a full-screen view controller. If the current presenting view controller is not suitable, UIKit walks the view controller hierarchy until it finds one that is. Upon completion of a modal presentation, UIKit updates the presentingViewController and presentedViewController properties of the affected view controllers.
I am attempting to implement a custom view controller presentation with a UIPresentationController subclass. When I present my view controller:
let sb = UIStoryboard.init(name:"Main", bundle:nil)
let presented = sb.instantiateViewController(withIdentifier:"PresentedTableViewController")
presented.transitioningDelegate = overlayTransitioningDelegate
presented.modalPresentationStyle = .custom
definesPresentationContext = true
present(presented, animated:true, completion:nil)
...the presentingViewController property refers to a view controller higher up the hierarchy than the one that initiated the presentation. This says to me that I have not satisfied whatever mysterious requirements that UIModalPresentationStyle.custom desires of its presenting view controller, and so it went looking elsewhere.
The thing is, I can't find these requirements documented anywhere, nor can I figure them out. I need a reference to the initiating view controller in my presentation controller. And, okay, I could sidestep the issue entirely by providing the reference myself, but I'd prefer to do things the right way rather than code around my ignorant mistakes.
Does anyone know what must be done for a view controller to qualify as a presenting view controller when using UIModalPresentationStyle.custom? Thanks!
I need a loading screen to show while some data is being downloaded from the server. I present a view controller with modal segue without animation. But I don't know how to dismiss the loading screen view controller since dismissViewController function can only be called from inside.
I should be able to dismiss the loading screen view controller from another view controller. Any suggestions?
One option is using NSNotificationCenter. You can post custom notification and listen it in loading view for closing it.
If you are getting the data in parent view then you can close the loading view from parent view also. You can call dismissViewController from parent view using presentingViewController property of UIViewController class.
In your case from parentView you can dismiss the child view using:
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
presentingViewController
The view controller that presented this view controller. (read-only)
Declaration
Swift
var presentingViewController: UIViewController? { get }
Objective-C
#property(nonatomic, readonly) UIViewController *presentingViewController
Discussion
When you present a view controller modally (either explicitly or
implicitly) using the presentViewController:animated:completion:
method, the view controller that was presented has this property set
to the view controller that presented it. If the view controller was
not presented modally, but one of its ancestors was, this property
contains the view controller that presented the ancestor. If neither
the current view controller or any of its ancestors were presented
modally, the value in this property is nil. Import Statement
import UIKit Availability
Available in iOS 5.0 and later.
Do you need to show another VC? You could just show a subview, making it visible=true when you start the app, and on the request success or failure callbacks hide it again (depending on the networking framework you are using)
I don't know were you start the request and were you know when it is done, if the loading screen knows when the download is finished you can do one of two things, or you pass the first VC as a delegate (defining a protocol) to the second VC (loading screen), and when you know on the loading screen that the download is finished you call a method on the delegate that will dismiss the loading screen,
Or you can use NSNotificationCenter, register for some kind of events on the first VC and when the process finishes on the loading screen you notify the first VC with this method, to dismiss the loading screen.
If you start the process on the first VC, and you know when it ends also on the first VC I would not understand your question, as you would just dismiss the loading screen and it would work.
Could you give more info? If you'd like I can post some code in order to help you in one of this approaches.
in the viewDidLoad method i have this code.
When the application is running its not go to the another viewController, its gives me an error:
Warning: Attempt to present <CompleteCountryViewController: 0x7fb971779be0> on <ViewController: 0x7fb97176f3e0> whose view is not in the window hierarchy!
What can i do, that when the application running its will go to another viewController?
You should not present a view controller in the viewDidLoad method of another controller because you cannot show a view controller (present modally or push) when a transition is already occurring (push, pop, present, dismiss).
My suggestion is that you move the code in your code sample to the viewDidAppear: method. At this point, you know for sure that the transition has completed.
You seem to have a slight misunderstanding of the lifecycle of UIViewController if you want to modally present a view controller inside the viewDidLoad of another one.
viewDidLoad gets called in one view controller after it has been instantiated and its view components have been loaded (thus the name). The view of that view controller is about to be displayed, so it doesn't make much sense to instantiate another view controller at this point and present it on the first one.
Let me give you an example with two view controller A and B.
You instantiate A and its viewDidLoad gets called. So, A is about to be displayed! What you are doing in your code now is to instantiate B at this very point and show it on A. iOS doesn't like that and will give you your error.
I had an issue where I was attempting to present a modal view controller within the viewDidLoad method. The solution for me was to move this call to the viewDidAppear: method.
View controller's view is not in the window's view hierarchy at the point that it has been loaded (when the viewDidLoad message is sent), but it is in the window hierarchy after it has been presented (when the viewDidAppear: message is sent).