How to make View Controller Animated When called in App Delegate - ios

I am dismissing the login view controller from the AppDelegate.swift file. but the view controller just vanishes off the screen when i run the below code, how can i have the view controller slide down.
var currentUser = PFUser.currentUser()
if currentUser != nil {
var dashVC = mainSB.instantiateViewControllerWithIdentifier("dashView") as! itsTimeDashVC
window!.rootViewController = dashVC
window!.makeKeyAndVisible()
}

Updating root view with animation, May be that should work. I do not have tried it :
UIView.transitionFromView(self.window.rootViewController.view, toView: viewController.view, duration:0.6, options: UIViewAnimationOptions.TransitionFlipFromRight, completion: nil), completion: { (fininshed: Bool) -> () in
self.window.rootViewController = viewController
})

Related

How to detect a modal view is visible globally in SwiftUI

In SwiftUI, there're couple of ways to present a modal view like .popover.
My background is I would like to present a UIKit modal view somewhere else rather than under the current view page with
private func presentGlobally(animated: Bool, completion: (() -> Void)?) {
var rootViewController = UIApplication.shared.rootViewController
while true {
if let presented = rootViewController?.presentedViewController {
rootViewController = presented
} else if let navigationController = rootViewController as? UINavigationController {
rootViewController = navigationController.visibleViewController
} else if let tabBarController = rootViewController as? UITabBarController {
rootViewController = tabBarController.selectedViewController
} else {
break
}
}
UIApplication.shared.rootViewController?.present(self, animated: animated, completion: completion)
}
The above approach does not work because SwiftUI gave an error of
`` [Presentation] Attempt to present <BuyersCircle_iOS.GlobalModalUIKit: 0x15b82f000> on <TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentGS1_GS1_GS1_GS1_V16BuyersCircle_iOS11ContentViewGVS_30_EnvironmentKeyWritingModifierGSqCS2_11UserService___GS4_GSqCS2_11GlobalModal___GS4_GSqCS2_9CartModel___GS4_GSqCS2_19ReachabilityMonitor___GS4_GSqCS2_19PartialSheetManager___: 0x158715600> (from
So I'm thinking
If I can get the current SwiftUI modal view controller, so I can present the UIKit modal based on it. I don't know how to make it work.
In you algorithm you're calculating rootViewController but then presenting self to UIApplication.shared.rootViewController, not to the found rootViewController
Replace
UIApplication.shared.rootViewController?.present(self, animated: animated, completion: completion)
With
rootViewController?.present(self, animated: animated, completion: completion)

View controller retaining another when changing rootViewController

I've found a memory leak in an app where the following is done:
Imagine two view controllers, each of which calls a function in the appDelegate similar to this:
func switchRootViewController() {
let vc = getTheOtherViewController()
self.window?.rootViewController = vc
}
So far this is working well - the other VC is shown and the one that called the function is deallocated.
But when you present a third view controller from first or second VC via:
present(ThirdViewController(), animated: true)
And then call the above function in the appDelegate from ThirdVC (to show viewController one or two by making it the rootViewController), this ThirdVC and the VC that presented it do not get deallocated.
Any idea why that is?
Can post sample project if needed. VC's are instantiated from storyboard if that makes any difference.
You are messing up the view hierarchy here. You should dismiss all the presented view controllers of the current rootViewController before switching to the new one. This is the only solution I've found to work!
Your switchRootViewController method should be like below,
func switchRootViewController() {
if var topController = UIApplication.shared.keyWindow?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
topController.dismiss(animated: true, completion: {
let vc = getTheOtherViewController()
self.window?.rootViewController = vc
})
}
}
If you there are multiple view controllers presented,
func switchRootViewController() {
self.view.window!.rootViewController?.dismiss(animated: true, completion: {
let vc = getTheOtherViewController()
self.window?.rootViewController = vc
})
}

How to correctly dismiss a UINavigationController that's presented as a modal?

In my TabBarViewController, I create a UINavigationController and present it as a modal.
var navController = UINavigationController()
let messageVC = self.storyboard?.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController
self.presentViewController(self.navController, animated: false, completion: nil)
self.navController.pushViewController(messageVC, animated: false)
Inside my MessageViewController, this is how I want to dismiss it:
func swipedRightAndUserWantsToDismiss(){
if self == self.navigationController?.viewControllers[0] {
self.dismissViewControllerAnimated(true, completion: nil) //doesn't deinit
}else{
self.navigationController?.popViewControllerAnimated(true) //deinits correctly
}
}
deinit{
print("Deinit MessagesViewController")
}
The problem is that when I get to the root View Controller and try to dismiss both the child and the UINavigationController, my MessagesViewController deinit does not get called. Something's holding on to it -- most likely UINavigationController
Your controller hierarchy looks like this:
UITabViewController
|
| presents
|
UINavigationController
|
| contains view controllers
|
[root, MessagesViewController]
Now, if you are inside MessagesViewController, then its navigationController is the one that is being presented and that's the one you should be dismissing but calling dismiss on MessagesViewController should work too.
However, the problem is that dismissing the navigation controller won't remove its view controllers. It seems you are holding to your navigation controller (since you are presenting it using self.navController) so the state will become
UITabViewController
|
| self.navController holds a reference to
|
UINavigationController
|
| contains view controllers
|
[root, MessagesViewController]
To properly destroy MessagesViewController you will have to either let go of the navController or you will have to pop to root (thus removing MessagesViewController from view hierarchy).
The typical solution would be not to save a reference to navController at all. You could always create a new UINavigationController when presenting.
Another solution is using a delegate - instead of dismissing from inside MessagesViewController, let it call back to the presenter, which would call
self.navController.dismiss(animated: true) {
self.navController = nil
}
Try this
func swipedRightAndUserWantsToDismiss(){
self.navigationController.dismissViewControllerAnimated(false, completion:nil);
}
You can use the following to correctly dismiss a UINavigationController that's presented as a modal in Swift 4:
self.navigationController?.popViewController(animated: true)
if you want to just present a viewcontroller, then directly you can present that viewcontroller and no need to take a navigation controller for that particular viewcontroller.
But when we need to navigate from that presented view controller then we need to take a view controller as a root view of navigation controller. So that we can navigate from that presented view controller.
let messageVC = self.storyboard?.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController
let MynavController = UINavigationController(rootViewController: messageVC)
self.presentViewController(MynavController, animated: true, completion: nil)
and from that presented view controller, you can push to another view controller and also pop from another view controller.
And from presented view controller, here messageVC, we have to dismiss that as
func swipedRightAndUserWantsToDismiss() {
self.dismiss(animated: true, completion: nil)
}
which will dismiss messageVC successfully and come back to origin viewcontroller from where we have presented messageVC.
This is the right flow to perform presentViewController with navigation controller, to continue the navigation between the view controllers.
And for more if you are not sure that messageVC is presented or pushed, then you can check it by this answer.
And the swift version to check that is
func isModal() -> Bool {
if((self.presentingViewController) != nil) {
return true
}
if(self.presentingViewController?.presentedViewController == self) {
return true
}
if(self.navigationController?.presentingViewController?.presentedViewController == self.navigationController) {
return true
}
if((self.tabBarController?.presentingViewController?.isKindOfClass(UITabBarController)) != nil) {
return true
}
return false
}
So our final action to dismiss is like
func swipedRightAndUserWantsToDismiss() {
if self.isModal() == true {
self.dismiss(animated: true, completion: nil)
}
else {
self.navigationController?.popViewControllerAnimated(true)
}
}
No need to have member for navController. Use following code to present your MessagesViewController.
let messageVC = self.storyboard?.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController
let pesentingNavigationController = UINavigationController(rootViewController: messageVC)
self.presentViewController(pesentingNavigationController, animated: true, completion: nil)
Your dismiss view controller code will be
func swipedRightAndUserWantsToDismiss() {
self.navigationController.dismiss(animated: true, completion: nil)
}
I suggest you use the other initializer for your UINavigationController:
let messageVC = self.storyboard?.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController
let navController = UINavigationController(rootViewController: messageVC)
self.presentViewController(self.navController, animated: true, completion: nil)
To dimiss, simply do
func swipedRightAndUserWantsToDismiss() {
self.navigationController.dismissViewControllerAnimated(true, completion: nil)
}
This is how I solve the problem in Objective C.
You can call dismissViewControllerAnimated:NO on your self.navigationController itself.
Objective C
[self.navigationController dismissViewControllerAnimated:NO completion:nil];
Swift
self.navigationController.dismissViewControllerAnimated(false, completion: nil)
In Swift 3 this is achieved with:
self.navigationController?.dismiss(animated: true, completion: nil)

Dismissing a custom viewcontroller

I'm currently using a third party called "Form".
In my UIButton, I implement a method which initializes a custom view controller which is a subclass of FormViewController. I initialize FormViewController embedded in navigation controller.
In my FormViewController class, I tried below two methods but none of them did not work.
self.navigationController?.popViewControllerAnimated(true)
self.dismissViewControllerAnimated(true, completion: nil)
Codes for pressing a UIButton:
#IBAction func part1Pressed(sender: AnyObject) {
if let JSON = NSJSONSerialization.JSONObjectWithContentsOfFile("data.JSON") as? [String : AnyObject] {
let initialValues = [
"last_name" : "Nordman"]
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewContainerVC = storyboard.instantiateViewControllerWithIdentifier("viewContainerVC")
let sampleController = Part1_VC(JSON: JSON, initialValues: initialValues)
let rootViewController = UINavigationController(rootViewController: sampleController)
rootViewController.view.tintColor = UIColor(hex: "5182AF")
rootViewController.navigationBarHidden = false
FORMDefaultStyle.applyStyle()
FORMSeparatorView.appearance().setSeparatorColor(UIColor.clearColor())
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = rootViewController
self.window?.makeKeyAndVisible()
}
}
Thank you!
You are trying to dismiss the view controller when it wasn't presented in the first place.
Instead set the root view controller of the navigation controller to something else (e.g. the screen you want shown after you dismiss the form) and present your form view controller modally:
self.presentViewController(formVc, animated: true, completion: nil)
You can then dismiss the view controller as you intended when you are finished with the form.

UINavigationController shows RootViewController before showing topViewController

I'm trying to show view controller which is 2nd view controller in a navigation view controller. What I want to show RegistraionViewController if join is tapped. Based on that. I push registrationViewController to the navigation Controller where LoginViewController is rootView Controller. Now the issue is when Join is Tapped. It Should show registrationView Controller but it shows Login screen and after a delay of about half second. It shows registration screen. My Code is as follows:
if let navC = self.selectedViewController as? UINavigationController
{
if let vc = navC.viewControllers.first as? LoginViewController
{
if let registerVC = self.storyboard?.instantiateViewControllerWithIdentifier("RegistrationViewControllerIdentifier") as? RegistrationViewController
{
var view = navC.view
vc.navigationController?.pushViewController(registerVC, animated: false)
}
}
}
I have also tried by changing the transition of views and giving animation time as 0. But no luck. Which is :
if let registerVC = self.storyboard?.instantiateViewControllerWithIdentifier("RegistrationViewControllerIdentifier") as? RegistrationViewController
{
UIView.animateWithDuration(0.0, animations: {
if let view = self.navigationController?.view
{
self.navigationController?.pushViewController(registerVC, animated: false)
UIView.setAnimationTransition(UIViewAnimationTransition.None, forView:view , cache: false)
}
}, completion: {
(value: Bool) in
})
}

Resources