Warning when presenting UIActivityViewController - ios

When i present an UIActivityController using the code below i get, it is presented but the console shows "Warning: Attempt to present <UIActivityViewController: 0x7f8788e7aed0> on <MyApp.CustomTableViewController: 0x7f8788e3db60> which is already presenting (null)".
#IBAction func shareImage(sender: AnyObject) {
let images: [UIImage] = [image.image!]
let activityViewController = UIActivityViewController(activityItems: images, applicationActivities: nil)
self.presentViewController(activityViewController, animated: true, completion: nil)
}
This func is called by an UILongPressGestureRecognizer. Note that i'm using storyboard with the following hierarchy:
TabBarController > (Relationship) > NavigationController > (Relationship) > TableViewController > (Show) > TableViewController > (Show) > ViewController.
The presentation happens on the last ViewController.
I'm quite sure it's about the hierarchy, which controller is currently presenting (and maybe how) and which controller is responsible for presenting the UIActivityViewController.
EDIT
UILongPressGestureRecognizer touch event is called multiple times which was causing the warning

It's hard to say from your question but is there some other view controller presented at the moment this happens? for example and action sheet or other?
In any case try this:
if self.presentedViewController != nil {
self.dismissViewControllerAnimated(false, completion: {
[unowned self] in
self.presentViewController(activityViewController, animated: true, completion: nil)
})
}else{
self.presentViewController(activityViewController, animated: true, completion: nil)
}

I get same error message, because the UILongPressGestureRecognizer call many times my selector. Now I call UIActivityViewController when UILongPressGestureRecognizer state is end.
#objc func longTouch(gestureRecognizer: UILongPressGestureRecognizer){
print("long touch")
if gestureRecognizer.state != .ended {
print("long touch not ended")
return
}
//open UIActivityViewController
}

Related

Navigate to ViewController and dissmiss VC

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)
})

Dismiss second ViewController from the third ViewController

I am trying to dismiss VC b from VC c where VC c is a popover and has a button for sign out but it is not working.
The structure of the flow is
VC a ----presents modally----> VC b ----presents a popover----> VC c
When the button in the popover is clicked the VC c and VC b must be dismissed so that (VC a)ViewWillAppear is called.
Try this:
You can dismiss your presentingViewController from child view controller as follow
self.presentingViewController?.dismiss(animated: true, completion: nil)
When you add a ViewController as childViewController
self.parent?.dismiss(animated: true, completion: nil)
If this view controller is a child of a containing view controller (e.g. a navigation controller or tab bar
controller,)
weak open var parent: UIViewController? { get }
The view controller that was presented by this view controller or its nearest ancestor.
open var presentedViewController: UIViewController? { get }
The view controller that presented this view controller (or its farthest ancestor.)
open var presentingViewController: UIViewController? { get }
If ViewControllers have hierarchy like
VC a ----presents as self.present(objects, animated: true, completion: nil) modally----> VC b ---- presents as self.present(objects, animated: true, completion: nil) popover----> VC c
And there are a button on VC c to move back to VC a then you can use:
self.presentingViewController?.presentingViewController?.dismiss(animated: true, completion: nil)
First of all try to dismiss VC b from itself, not presenting the VC c just to check if it works, using: self.dismiss(animated: true, completion: nil) or if VC b is embedded in a navigation controller, like this: self.navigationController?.dismiss(animated: true, completion: nil)
If the one from above works, I would suggest you implement the delegation protocol, where VC c will delegate to VC b the dismissal, whenever it should be done. You could also use a completion block for that, containing the dismiss code.
Hope this works
// Call inside View controller C
self.presentingViewController?.dismissViewControllerAnimated(false, completion: nil)
self.presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
`protocol ModalHandler {
func modalDismissed()
Class SecondViewController: UIViewController, ModalHandler {
func modalDismissed() {
self.dismiss(animated: false, completion: nil)
}
func open3rdController() {
let thirdVC = ThirdViewController(_ )
thirdVC.delegate = self
self.present(thirdVC, animated: true, completion: nil)
}
class ThirdViewController: UIViewController {
func dismiss() {
self.delegate.modalDismissed()
}
}
`
Whats about a Presenter or a Coordinator.
This instance will initialize all these VCs and also present them.
From there you can also dismiss them.

How to dismiss current view controller and parentviewcontroller?

I am presenting two view controller like so
self.presentViewController(choosePlace, animated: true, completion: nil)
self.presentViewController(shareController, animated: true, completion: nil)
and I want to dismiss both of them like this:
println("parent \(self.parentViewController)")
self.dismissViewControllerAnimated(true, completion: {
self.parentViewController?.dismissViewControllerAnimated(true, completion: nil)
})
self.parentViewController is always nil though. How can I dismiss two at the same time?
You can:
self.dismissViewControllerAnimated(true, completion: {
self.presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
})
So, make sure that you are presenting your view controllers in order:
parentViewController -> choosePlace -> shareController
(arrows indicate "self.presentViewController")
Swift 4
There where some issue I faced while trying Michael Voline's answer.
As the comment on the answer it did not work for me. it was because of the presentingViewController was nil.
So we need to set in a different property say presentingController but this will not work if you are setting it on the viewDidLoad
private var presentingController: UIViewController?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
presentingController = presentingViewController
}
then
dismiss(animated: false, completion: {
self.presentingController?.dismiss(animated: false)
})
I made the animation false so that the user will not see the UIof presenting view controller in the split seconds of dismissing.
You can reference the presenting view controller during the function 'viewWillDisappear' of your view controller (i.e ShareController dismisses choosePlace just before it leaves scope).
//place the below in shareController
override func viewWillDisappear(_ animated: Bool) {
self.presentingViewController?.dismiss(animated: false, completion: nil)
}
If Michael Voline's solution doesn't work , try the following code.
let customViewController = self.presentingViewController as? CustomViewController
self.dismiss(animated: true) {
customViewController?.dismiss(animated: true, completion: nil)
}

Dismissing a viewcontroller that is presented from UITabBarController in the app delegate

To achieve a similar effect to many photo apps I:
Set up my tab bar programmatically in the App delegate
Added a custom button on top of a tab bar item that I would like to use to launch the camera
(basically using this method: What's the best way of adding a custom centre button to a tab bar?)
Created a view controller that handles the camera functionality
I launch the view controller this way from the button and it works. However, I can't dismiss it effectively.
in didFinishLaunchingWithOptions :
var tabBC = UITabBarController()
//....
shutterButton.addTarget(self, action: "presentCamera:", forControlEvents: UIControlEvents.TouchUpInside)
tabBC.view.addSubview(shutterButton)
action attached:
func presentCamera(sender: AnyObject){
let composeSB = UIStoryboard(name: "Compose", bundle: nil) as UIStoryboard
let composeVC = composeSB.instantiateViewControllerWithIdentifier("composeVC") as ComposeViewController
self.window?.rootViewController!.presentViewController(composeVC, animated: false, completion: nil)
}
In ComposeViewController this method gets called but the view controller does not get dismissed. I would think this would dismiss the VC and I would see the views in the tabbarcontroller.
func cameraOverlayDidCancel() {
// I've tried:
dismissViewControllerAnimated(true, completion: nil)
//self.dismissViewControllerAnimated(true, completion: nil)
//self.view.parentViewController.dismissViewControllerAnimated(true, completion: nil)
}

How to dismiss ViewController in Swift?

I am trying to dismiss a ViewController in swift by calling dismissViewController in an IBAction
#IBAction func cancel(sender: AnyObject) {
self.dismissViewControllerAnimated(false, completion: nil)
println("cancel")
}
#IBAction func done(sender: AnyObject) {
self.dismissViewControllerAnimated(false, completion: nil)
println("done")
}
I could see the println message in console output but ViewController never gets dismissed. What could be the problem?
From you image it seems like you presented the ViewController using push
The dismissViewControllerAnimated is used to close ViewControllers that presented using modal
Swift 2
navigationController.popViewControllerAnimated(true)
Swift 4
navigationController?.popViewController(animated: true)
dismiss(animated: true, completion: nil)
I have a solution for your problem. Please try this code to dismiss the view controller if you present the view using modal:
Swift 3:
self.dismiss(animated: true, completion: nil)
OR
If you present the view using "push" segue
self.navigationController?.popViewController(animated: true)
if you do this i guess you might not get println message in console,
#IBAction func cancel(sender: AnyObject) {
if(self.presentingViewController){
self.dismissViewControllerAnimated(false, completion: nil)
println("cancel")
}
}
#IBAction func done(sender: AnyObject) {
if(self.presentingViewController){
self.dismissViewControllerAnimated(false, completion: nil)
println("done")
}
}
In Swift 3.0 to 4.0 it's as easy as typing this into your function:
self.dismiss(animated: true, completion: nil)
Or if you're in a navigation controller you can "pop" it:
self.navigationController?.popViewController(animated: true)
embed the View you want to dismiss in a NavigationController
add a BarButton with "Done" as Identifier
invoke the Assistant Editor with the Done button selected
create an IBAction for this button
add this line into the brackets:
self.dismissViewControllerAnimated(true, completion: nil)
Use:
self.dismiss(animated: true, completion: nil)
instead of:
self.navigationController.dismissViewControllerAnimated(true, completion: nil)
From Apple documentations:
The presenting view controller is responsible for dismissing the view controller it presented
Thus, it is a bad practise to just invoke the dismiss method from it self.
What you should do if you're presenting it modal is:
presentingViewController?.dismiss(animated: true, completion: nil)
If you presenting a controller without a Navigation Controller, you can call the following code from a method of the presented controller.
self.presentingViewController?.dismiss(animated: true, completion: nil)
If your ViewController is presented modally, optional presentingViewController will be not nil and the code will be executed.
Based on my experience, I add a method to dismiss me as extension to UIViewController:
extension UIViewController {
func dismissMe(animated: Bool, completion: (()->())?) {
var count = 0
if let c = self.navigationController?.viewControllers.count {
count = c
}
if count > 1 {
self.navigationController?.popViewController(animated: animated)
if let handler = completion {
handler()
}
} else {
dismiss(animated: animated, completion: completion)
}
}
}
Then I call this method to dismiss view controller in any UIViewController subclass. For example, in cancel action:
class MyViewController: UIViewController {
...
#IBAction func cancel(sender: AnyObject) {
dismissMe(animated: true, completion: nil)
}
...
}
Since you used push presented viewController, therefore, you can use
self.dismiss(animated: false, completion: nil)
Don't create any segue from Cancel or Done to other VC and only write this code your buttons #IBAction
#IBAction func cancel(sender: AnyObject) {
dismiss(animated: false, completion: nil)
}
So if you wanna dismiss your Viewcontroller use this. This code is written in button action to dismiss VC
#IBAction func cancel(sender: AnyObject) {
dismiss(animated: true, completion: nil)
}
Here is the one way to dismiss present view controller and move back to previous view controller. You can do this through Storyboard only.
Open Storyboard
Right click on Cancel button and drag it to previous view controller, where you want to move back to previous controller
Now release the right click and you can see some actions which performs on cancel button
Now choose "popover present" option from list
Now you can dismiss your current view by click on cancel button
Please try this, It's working with me.
Second Way - Use - navigationController.popViewControllerAnimated(true)
Best luck..
For reference, be aware that you might be dismissing the wrong view controller. For example, if you have an alert box or modal showing on top of another modal. (You could have a Twitter post alert showing on top of your current modal alert, for example). In this case, you need to call dismiss twice, or use an unwind segue.
Try this:
#IBAction func close() {
dismiss(animated: true, completion: nil)
}
If you using the present method in the parent VC then you should call this function, to dismiss the child VC use this
self.dismiss(animated: true, completion: nil)
if you calling child VC by using push method, to dismiss the child VC use this
self.navigationController?.popViewController(animated: true)
If you are presenting a ViewController modally, and want to go back to the root ViewController, take care to dismiss this modally presented ViewController before you go back to the root ViewController otherwise this ViewController will not be removed from Memory and cause Memory leaks.
In Swift 3.0
If you want to dismiss a presented view controller
self.dismiss(animated: true, completion: nil)
In Swift 4.1 and Xcode 9.4.1
If you use pushViewController to present new view controller, use this
self.navigationController?.popViewController(animated: false)
#IBAction func back(_ sender: Any) {
self.dismiss(animated: false, completion: nil)
}

Resources