I am implementing Admob Interstitial Admob Ad in my iOS app. I need to show ad between transition from a Second View Controller to a (Main) View Controller. I display Second View Controller as "Present Modally", "Over Current Context". It looks like popup window.
When I return from Second (popup) View Controller (close/hide it), I have an error: Attempt to present GADNFullScreenAdViewController on ViewController which is already presenting SecondViewController.
How to handle this situation in a right way?
#IBAction func afterShowingSecond(_ segue:UIStoryboardSegue) {
if let secondViewController = segue.source as? SecondViewController {
if (secondViewController.someObject != nil) {
if (interstitial.isReady && interstitialAdIntervalExpired && !doNotShowAds) {
interstitial.present(fromRootViewController: self)
return
}
// some code
}
}
}
you can use the function dismiss(animated:completion:)
if let presentingVC = self.presentingViewController {
self.dismiss(animated: true) {
let vc = YourViewController()
presentingVC.present(vc, animated: true, completion: nil)
}
}
Related
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)
Here i'm dismissing one VC and trying to navigate new VC , but navigation not working. Dismiss VC working fine.
#IBAction func onClickEmailBtn(_ sender: UIButton) {
//dismiss VC
self.dismiss(animated: false, completion: nil)
//Navigate to VC
DispatchQueue.main.async {
let cevc = self.storyboard?.instantiateViewController(withIdentifier: "CEVC") as! EmailViewController
self.navigationController?.pushViewController(cevc, animated: true)
}
}
Here I have one NavigationViewController called VC1, on that I'm presenting one VC called VC2. In this VC2 when I click button I want to navigate new VC called EmailVC.
try this code
#IBAction func onClickEmailBtn(_ sender: UIButton) {
if let controller = self.storyboard?.instantiateViewController(withIdentifier: "CEVC") as! EmailViewController {
self.dismiss(animated: false, completion: nil)
self.presentingViewController?.present(controller, animated: true, completion: nil)
}
}
You were trying to push a ViewVontroller from dismissing view controller,
and the answer which you have accepted is presenting a viewController from dismissing viewController. Although it may serve your purpose, but I am going to answer you original question
// get the object of presenting nanvigation controller(navigation controller with has presented this view controller)
let presentingNavigationController = presentingViewController?.navigationController
dismiss(animated: true) {
let cevc = self.storyboard?.instantiateViewController(withIdentifier: "CEVC") as! EmailViewController
// here you push your view controller with presentingNavigationController
presentingNavigationController?.pushViewController(cevc, animated: true)
}
Try to find out Navigation controller using below code and replace Viewcontroller with your Controller name.
let productVC = SeconViewViewController()
let parentVC = (parent!.presentingViewController as! ViewController).childViewControllers.first as! UINavigationController
self.navigationController?.dismiss(animated: true, completion: {
parentVC.pushViewController(productVC, animated: true)
})
How to present another view controller after dismiss from navigation controller in swift ?
I am using Navigation controller.
ViewController1.swift
func pushTo(viewController: UIViewController) {
let showLocalPwd = self.storyboard?.instantiateViewController(withIdentifier: "LocalPwdVC") as! LocalPwdVC
self.navigationController?.present(showLocalPwd, animated: true, completion: nil)
}
ViewController2.swift
#IBAction func btnVerify(_ sender: Any)
{
self.dismiss(animated: true, completion: {
let vc = self.storyboard.instantiateViewController(withIdentifier: "DataVC") as! DataVC
self.navigationController.pushViewController(vc, animated: true)
})
}
After dismissing the View Controller, it will not goes to next viewcontroller i.e. DataVC
If you want to present the view controller then create a protocol in your dismissViewController
protocol dismissViewController {
func presentCompletedViewController()
}
// Then create a delegate
var delegate = dismissViewController? = nil
// If you are using action to dismiss your ViewController then call delegate
#IBAction func YourButton(_ sender: Any) {
self.dismiss(animated: true) {
self.delegate!.presentCompletedViewController()
}
}
//And then call this in your main or homeViewController
class HomeViewController: UIViewController, dismissViewController {
func presentCompletedViewController(){
// enter code to present view controller
}
// And at last don't forget to call delegate
yourVc.delegate = self
you need to call this delegate in which you are presenting your dismissViewController
Let's say I have five view controllers and I want to go to the specific view controller
RootViewController ==> FirstViewController ==> SecondViewController ==> ThirdViewController ==> FourthViewController(Modally presented having a button)
and all other controllers I connected through push method.
My task is I want to go to the firstViewController from FourthViewController when button is clicked. Any help?
for controller in self.navigationController!.viewControllers as Array {
if controller.isKind(of: HomeViewController.self) {
self.navigationController!.popToViewController(controller, animated: true)
break
}
}
this is the code I have done.
Add delegate in FourthViewController:
self.dismiss(animated: true) {
self.delegate.popToFirstVC()
}
Add func popToFirstVC() in ThirdViewController.
Use popToViewController:
func popToFirstVC() {
if let firstViewController = self.navigationController?.viewControllers[1] {
self.navigationController?.popToViewController(firstViewController, animated: true)
}
}
or better
guard let viewControllers = self.navigationController?.viewControllers else {
return
}
for firstViewController in viewControllers {
if firstViewController is FirstViewController {
self.navigationController?.popToViewController(firstViewController, animated: true)
break
}
}
There is still such an option.
Add an Observer for this function and call where necessary.
But I would do it only in the most extreme cases.
func popToThisVC() {
if let topController = UIApplication.topViewController() {
topController.navigationController?.popToViewController(self, animated: true)
}
}
You mentioned connected, hence I reckon you used storyboards and segues, in that case why not create an unwind segue?
It's a bit hard to show you snippet of unwind segue here via text only but I think Unwind segue blog from medium has your answer
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)