Displaying instances of view controllers within Xcode without disturbing the current hierarchy
A view controller can serve as a custom parent view controller of one or more child view controllers, displaying their views in any desired manner within its own view's interface.
You can instantiate a view controller in any desired manner. If you wish to pluck a view controller instance out of the storyboard (because you have already designed its view there), call:
https://developer.apple.com/documentation/uikit/uistoryboard/1616214-instantiateviewcontroller
Your view controller in the storyboard will need to have a storyboard ID string so that you can identify it.
After you've plucked a view controller from the storyboard in that way, to display its view in your interface, you must do the following dance:
The parent calls https://developer.apple.com/documentation/uikit/uiviewcontroller/1621394-addchild
The parent captures the view controller's
view
and sticks it into the interface as a subview of the parent's own view.The parent calls https://developer.apple.com/documentation/uikit/uiviewcontroller/1621405-didmove
The two view controllers now stand in a legal parent-child relationship and both view controllers will work properly. They can even refer to one another.
If you wish to remove the child view controller's view from the interface, you must reverse the dance:
The parent calls https://developer.apple.com/documentation/uikit/uiviewcontroller/1621381-willmove with a
nil
parameterThe parent removes the child view controller's view from the interface
The parent calls https://developer.apple.com/documentation/uikit/uiviewcontroller/1621425-removefromparent
Please read "Implementing a Container View Controller" on the main view controller docs page:
https://developer.apple.com/documentation/uikit/uiviewcontroller
Memory management ARC and view controllers
You are not getting a leak because you create a new controller and ARC will release this allocation for you.
But, it's better to create a @property
for your new view controller.
and modify your i.e. implementation like :
@property (nonatomic, strong) ViewController *myViewController;
if (!_myViewController)
self.myViewController = [[ViewController alloc] init];
self.myViewController.delegate = self;
[self presentViewController:_myViewController animated:YES completion:nil];
Here, you have a lazy property and you don't create a new one ViewController after the first creation.
But, you need to pass your delegate (or any property) outside your test.
Furthermore, if you use your first implementation and add this controller in a subview of the current controller without property, this will work but you will get a leak.
I got this experience with the code below :
RootViewController
- (void)viewDidLoad
{
[super viewDidLoad];
ViewController *myViewController = [[ViewController alloc] init];
[self.view addSubview:myViewController.view];
}
myViewController
will be add on the screen but released immediately without keeping any reference of the object, so if you add an action in 'ViewController`, your application will crash without explanation of XCode.
So, the correct way to write this without leak will be :
- (void)viewDidLoad
{
[super viewDidLoad];
if (!_myViewController)
self.myViewController = [[ViewController alloc] init];
[self.view addSubview:self.myViewController.view];
}
The answer is a bit longer and can be improved so don't hesitate !
Hope it's going to help some people.
Missing transition from first view controller to second view controller using navigation controller
In the storyboard, make sure there is a rootViewController segue from the navigation controller to the first view controller. Also, make sure the navigation controller is marked as the initial view controller.
In the code, change
self.nvc?.pushViewController...
To
self.navigationController?.pushViewController...
Label keeps getting reset when changing View Controllers
Solution
A simple solution would be to override a method named viewDidLoad in your code and set the text of your questionLabel there.
override func viewDidLoad() {
super.viewDidLoad()
// You can use any index here. With little effort you can even randomize this.
let exampleIndex = 0
questionLabel.text = questions[exampleIndex]
}
Explanation
The problem with your original code is that you haven't set the text of the questionLabel programmatically when the main view appears. By only setting the text in the nextQuestionButton IBaction, your text label will change only if the action is called (which is triggered by pressing the next button I assume). Thus, the text you set from the storyboard was shown.
For a quick review of app view life cycle you can refer to this link.
Attempt to present UIViewController on UIViewController whose view is not in the window hierarchy
Where are you calling this method from? 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.
My presumption is that the 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).
Caution
If you do make a call to presentViewController:animated:completion:
in the viewDidAppear:
you may run into an issue whereby the modal view controller is always being presented whenever the view controller's view appears (which makes sense!) and so the modal view controller being presented will never go away...
Maybe this isn't the best place to present the modal view controller, or perhaps some additional state needs to be kept which allows the presenting view controller to decide whether or not it should present the modal view controller immediately.
single function to dismiss all open view controllers
You can call :
self.view.window!.rootViewController?.dismiss(animated: false, completion: nil)
Should dismiss all view controllers above the root view controller.
displaying a ViewController from a non UI class
UINavigationController maintains a stack of view controllers. You can access this stack through the viewControllers property. To present your view controller, you can:
(a) have the navigation controller push the new view controller on to
the stack (pushViewController:animated:);(b) have the top view controller in the view controller stack present
the new view controller modally (presentViewController:animated:completion:), or;(c) add the new view controller to the view controller stack array
manually by assigning a new viewControllers array to the navigation
controller's viewControllers property (setViewControllers:).
Does a view controller get removed entirely when using a custom transition?
You said:
is my view controller still get removed after the transition finishes or is it still there but off the screen?
There are two completely separate issues here.
First, there is a question of the view controller hierarchy. When you present a new view controller, the old view controller is always kept in the view controller hierarchy so that when you dismiss back to it, it will still be there. However, when you dismiss, the dismissed view controller will be removed from the view controller hierarchy and (unless you do something unusual, like keeping your own strong reference to it somewhere) it will be deallocated.
Second, there is a separate question of the view hierarchy. When presenting, the UIPresentationController
dictates whether the presenting view controller's view remains in the view hierarchy or not. By default, it keeps it in the view hierarchy, but generally if doing a modal, full-screen "present", you'd specify a UIPresentationController
subclass that tells it to remove the presenting view controller's view when the transition is done.
For example, when doing a custom modal "present" transition where the presented view controller's view is opaque and covers the whole screen, then your UIViewControllerTransitioningDelegate
would not only supply the animation controllers, but also specify a presentation controller:
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return YourAnimationController(...)
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return YourAnimationController(...)
}
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return PresentationController(presentedViewController: presented, presenting: presenting)
}
And that presentation controller might be fairly minimal, only telling it to remove the presenter's view:
class PresentationController: UIPresentationController {
override var shouldRemovePresentersView: Bool { return true }
}
Related Topics
How to Properly Handle a Nil Uiapplication.Sharedapplication().Keywindow
Calculate New Coordinates with Starting Position and Distance
Subview Gesture Recognizer Not Being Called
Swift Bridging Header File Won't Work with Use_Frameworks
Swift: Obtain and Save the Updated Scnnode Over Time Using Projectpoint in Scenekit
How to Update Particular Value of Child in Firebase Db
Swift: Sort Array by Sort Descriptors
Drag Uibutton Without It Shifting to Center [Swift 3]
Firebase Connection Manager Should Return Only One Result
How to Make Uiscrollview Zoom in Only One Direction When Using Auto Layout
Updating the Status Bar Style Between View Controllers
Your Credentials Do Not Allow Access to This Resource Twitter API Error
Operation Went Isfinished=Yes Without Being Started by the Queue It Is In
How to Update a Swiftui View State from Outside (Uiviewcontroller for Example)
Google Signin Cocoapods Deprecated
Error After Importing Swift into Objective-C