I have a two view controllers, ViewControllerA and ViewControllerB, embedded in a UINavigationController. ViewControllerA has a UICollectionView that displays images.
When the camera icon located at index 0 and the UICollectionViewCell is selected, it should dismiss ViewControllerA and display ViewControllerB. And also, there is a ViewController that presents ViewControllerA modally
This is what I've done, but it only dismiss ViewControllerA and nothing happens
let controller = self.storyboard?.instantiateViewControllerWithIdentifier("cameraViewController") as! UINavigationController
self.dismissViewControllerAnimated(true, completion: { () -> Void in
self.presentingViewController?.presentViewController(controller, animated: true, completion: nil)
})
When view controller is dismissed, my guess is self is already deallocated so self.presentingViewController? becomes nil too. I would create a strong reference to presenting view controller right before dismissing action takes place:
let presentingViewController = self.presentingViewController
self.dismissViewControllerAnimated(true) { completed in
if completed {
presentingViewController?.presentViewController(controller, animated: true, completion: nil)
}
}
Also, make sure self.presentingViewController is not nil in the first place.
The reason nothing is happening is because self.presentingViewController? is nil.
If instead, you were using self.presentingViewController!, there would be an error stating Unexpectedly found nil while unwrapping optional value, which should always be avoided.
You are correct in using self.presentingViewController? instead of self.presentingViewController!, as it is much safer, but in order to fix this, you must make sure that self.presentingViewController? is not nil.
let presenting = self.presentingViewController
self.dismissViewControllerAnimated(true, completion: {() -> Void in
presenting?.presentViewController(controller, animated: true, completion: nil)
})
Related
Hello I am pretty annoyed with this:
Originally, I had many segues in my storyboard. Each button at bottom of tool bar would segue to various view controllers. As you can imagine, that is a lot of segues for 6 different items on toolbar. After segueing, each action would call self.dismiss and everything would be ok.
Recently, I wanted to clean up the storyboard.
I created the function:
extension UIViewController {
func segue(StoryboardID: String) {
let popOverVC = UIStoryboard(name: "Main", bundle:
nil).instantiateViewController(identifier: StoryboardID)
popOverVC.modalPresentationStyle = UIModalPresentationStyle.fullScreen
popOverVC.modalTransitionStyle = .crossDissolve
self.dismiss(animated: false, completion: nil)
print("dismissed")
self.present(popOverVC, animated: false, completion: nil)
print("presented")
}
}
What I am seeing is that the dismiss dismisses the new view controller from appearing. It essentially goes back to my first view controller presented upon launch. I want to dismiss all view controllers so that I don't keep piling up my views.
Thanks
The problem is that you present your popOverVC from a view controller that is being dismissed, so your popOverVC will never be shown as its parent is being dismissed.
Not sure how your architecture is exactly but you would either need to use the delegate pattern to tell the parent when to segue, for example:
self.dismiss(animated: true) {
self.delegate?.didDismiss(viewController: self)
}
Or to use some trick to get the top most view controller to present your popOverVC after the current view has been dismissed, ex:
self.dismiss(animated: true) {
var topController: UIViewController = UIApplication.shared.windows.filter{$0.isKeyWindow}.first!.rootViewController!
while (topController.presentedViewController != nil) {
topController = topController.presentedViewController!
}
topController.present(popOverVC, animated: true)
}
But I agree with #Paulw11, a UITabBarController seem like a much better option for what you're trying to do.
Let say I have the following VCs:
RootVC --> VC A --> VC B
I'm using present method to present view controller from RootVC to VC A then to VC B. Now I'm on VC B and I want to dismiss from VC B back to RootVC using
self.view.window!.rootViewController?.dismiss(animated: true, completion: nil)
it works but I still see VC A shows up during the dismiss process. Then, I try this method
self.presentationController?.presentedViewController.dismiss(animated: true, completion: nil)
It also works to dismiss back to root VC but I still see VC A in process.
My question is is there a way to not show VC A during the dismiss process? I already try animated: false but still get the same result. Thanks!
You need to make the change in the modalPresentationStyle to the .custom. The custom will allow you to view the presentingViewController view when the current visible controller's view is transparent.
Now when you want to go back to root view on the current presenting stack you need to call the method dismissToRootViewController(animated: completion:).
In the implementation of this method will allow all intermediate presenting view controller view to be transparent which will give you dismiss animation from VC c to RootVC.
extension UIViewController {
func dismissToRootViewController(animated: Bool, completion: (() -> Swift.Void)? = nil) {
var viewController = self.presentingViewController
while viewController?.presentingViewController != nil {
viewController?.view.alpha = 0.0
viewController = viewController?.presentingViewController
}
self.dismiss(animated: true) {
viewController?.dismiss(animated: false, completion: completion)
}
}
}
You can try to use this:
navigationController?.popToRootViewController(animated: false)
I have a problem with present UIViewController Modaly by above code
self.presentViewController(view, animated: true, completion: nil);
it doesn't work when another view present modaly such as UIAlert,its triggered by a socket packet in background and user may is performing another work and may another modal view already presented when the trigger happens.
You can not present two view controllers at the same time from the same source controller. Instead, try presenting the second view controller from the first one that was presented.
if let presented = self.presentedViewController {
presented.present(vcToPresent, animated: true, completion: nil)
}
else {
self.present(vcToPresent, animated: true, completion: nil)
}
I have a floating button to present a model view. I call presentViewController in AppDelegate
UIApplication.sharedApplication().keyWindow?.rootViewController!.getTopViewController().presentViewController(myViewController, animated: false, completion: nil)
When myViewController is presenting, I click my floating button again and my application will call myViewController one more time. So how to not present if it presenting?
What you can do is add a check on your button to check whether the present controller is presenting something which can be done like this
let controller = UIApplication.sharedApplication().keyWindow?.rootViewController!.getTopViewController()
if controller.presentedViewController == nil {
controller.presentViewController(myViewController, animated: false, completion: nil)
}
i'm trying to pass Data to a viewController. The problem is that its embedded in a navigationController and is presented modally. How can i pass to a viewController which is presented modally and is embedded in a navigation Controller
func offerNew(sender: UIBarButtonItem) {
let offerVC = self.storyboard?.instantiateViewControllerWithIdentifier("OfferViewController") as UINavigationController
self.presentViewController(offerVC, animated: true, completion: nil);
}
i've tried this
let offerVC = self.storyboard?.instantiateViewControllerWithIdentifier("offerNavigation") as UINavigationController
let targetVC = offerVC.topViewController as OfferViewController
self.presentViewController(targetVC, animated: true, completion: nil);
I'm assuming that offerVC is the navigationController in which your "target" view controller is embedded. If so, your target view controller can be accessed through the topViewController property of offerVC. So
let targetVC = offerVC.topViewController as TargetViewController
will give you a reference. You can then access the properties of your target view controller.
EDIT
But you should present OfferVC - it will display targetVC automatically.
self.presentViewController(offerVC, animated: true, completion: nil);
You could add a property to your OfferViewController, then assign it. Your view initialization code would be done in loadView, which won't be called until after presentViewController, so not having it during init shouldn't be a problem in most cases.