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

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

Related

Swift 5. How present or show ViewController on button touch? Without Storyboard (programmatically)

How can I show() or persent() VC on button touch? What code should I write?
First, using Storyboards
If you are working with storyboard. You should connect your button view storyboard.
#IBAction fileprivate func handlePresentingView(_ sender: UIButton) {
let vc = SecondVC()
present(vc, animated: true, completion: nil)
}
Second, programmatically
1: If you are working programmatically
in viewDidLoad add the following line.
mybutton.addTarget(self, action: #selector(handlePresentingVC(_:)), for: .touchUpInside)
2: Your action method
#objc func handlePresentingVC(_ sender: UIButton) {
let vc = SecondVC()
present(vc, animated: true, completion: nil)
}
In my example, I'm assuming that you don't have a storyboard file for
your SecondVC view controller.
If SecondVC is connected to a storyboard view controller, you will
need to change the instantiation of your secondVC object inside of
your button's action method.
1: Select your SecondVC's view controller in your storyboard.
2: Add a Storyboard ID to it.
3: Change your button's action method to the following.
#objc func handlePresentingVC(_ sender: UIButton) {
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let secondVc = storyboard.instantiateViewController(withIdentifier: "SecondVC") as! SecondVC
present(secondVc, animated: true, completion: nil)
}
In Swift 5 and iOS 13
The default modal presentation style is a card. This shows the previous view controller at the top and allows the user to swipe away the presented view controller.
To retain the old style you need to modify the view controller inside of your button's action method like this:
secondVc.modalPresentationStyle = .fullScreen
This is the same for both programmatically created and storyboard created controllers.

Popping or dismissing a ViewController

I have a Feedback View Controller that is accessed in 7 locations across 4 different screens.
One way it's presented in a navigationController via pushViewController. The other 6 times it's presented modally.
Here's the function that opens the Feedback VC's
struct Constants{
static func openFeedback(openFrom: UIViewController, nav:Bool) {
let fbStoryboard = UIStoryboard(name: "FeedbackViewController", bundle: nil)
let fbVC = fbStoryboard.instantiateViewController(withIdentifier: "FBSBID")
fbVC.modalPresentationStyle = .overFullScreen
fbVC.modalTransitionStyle = .crossDissolve
if nav {
openFrom.navigationController?.pushViewController(fbVC, animated: true)
} else {
openFrom.present(fbVC, animated: true, completion: nil)
}
}
}
The Feedback VC is called with either Constants.openFeedback(openFrom: self, nav: true) or Constants.openFeedback(openFrom: self, nav: false)
Opening the VC works just fine!
Here's my close button on the Feedback View Controller:
#IBAction func closeButtonPressed(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
Which works 6 out of the 7 times, when not in the navigation stack. When it's in the navigation stack, the close button does not do anything.
My question is, how do I close out of the Feedback View Controller, based on if it's in the navigationController stack or not?
You can simply check if view controller is embedded inside UINavigationController by checking if controller's navigationController is nil or not.
So if it is embedded you can use popViewController(animated:) on navigation controller to "dismiss" pushed controller
if let navCon = navigationController {
navCon.popViewController(animated: true)
} else {
dismiss(animated: true)
}

How to switch to other view controller programmatically in Swift 4?

I need to switch page(other view controller) using a button.
I have two controllers (viewController to HomeViewController)
I have a button created in viewController:
let acceptButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Accept", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 18)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
//button.sendAction(Selector(buttonAction), to: HomeViewController(), for: .touchUpInside)
return button
}()
and I have set a function and used a print statement to test the function:
#objc func buttonAction(sender: UIButton){
print("button pressed")
}
I executed the program and when I press the button it prints out "button pressed"
Then I added the following into the function:
let HomeViewController = ViewController(nibName: ViewController, bundle: nil)
self.presentViewController(HomeViewController, animated: true, completion: nil)
As a result, it does not switch to other viewController(HomeViewController).
Any idea on how should I make it works? Thank you
There are couple of ways to switch to another ViewController.
Navigation
Present
If you have created the controllers in storyboard then you have to first get the instance of storyboard in case of multiple storyboards.
In case of multiple storyboard you need to mention the name of the storyboard.
let sampleStoryBoard : UIStoryboard = UIStoryboard(name: "UserBoard", bundle:nil)
Then get the instance of the view controller you wish to switch.
* Make sure you set the correct Identifier in ViewController.
let homeView = sampleStoryBoard.instantiateViewController(withIdentifier: "HomeViewController") as! HomeViewController
if you wish to present the view controller:
self.present(homeView, animated: true, completion: nil)
if you wish to push the view controller:
self.navigationController?.pushViewController(homeView, animated: true)
if you want to Switch to another Controller which you created as Xib
let homeView = HomeViewController(nibName: "HomeViewController", bundle: nil)
If you want to push:
self.navigationController?.pushViewController(homeView, animated: true)
Present the Controller:
present(homeView, animated: true, completion: nil)
Other ways to Switch between Controllers.
Wiring up the Segues
Ctrl+Drag from the “View Controller” button, to somewhere in the second View Controller(HomeViewController). It can be anywhere in the main box of the second view controller. When you release, it will show you a box like the one below.
in this you don't need any code to switch, it will switch on click of a button.
or you can CTLR + Drag from View controller to other controller to create segue and write below code on click of button action and switch to another view controller.
performSegue(withIdentifier: "mySegueID", sender: nil)
make sure you set the correct identifier of segue.
Details of different segues
Show — When the View Controllers are in a UINavigationController, this pushes the next View Controller onto the navigation stack. This allows the user to click the back button to return to the previous screen through the back button on the top left of the Navigation Bar.
Present modally — This presents the next view controller in a modal fashion over the current View Controller. This one doesn’t need to be part of a UINavigationController or a UISplitViewController. It just shows the destination View Controller in front of the previous one. This is usually used when the user has to either Finish or Cancel .
Custom — Exactly what it sounds like. You can make your own segue style and transitions and use it here.
Switch to another viewController programmatically in Swift 4 -
Navigation process
first, you need to embed your view with Navigation Controller like -
For Navigation programmatically you can use below code -
But this code work with if you working with StoryBoard in your project.
First, you need to set view identifier in storyBoard
Push with StoryBoard
let homeView = self.storyboard?.instantiateViewController(withIdentifier: "HomeViewController") as! HomeViewController
self.navigationController?.pushViewController(homeView, animated: true)
Present with StoryBoard
let homeView = self.storyboard?.instantiateViewController(withIdentifier: "HomeViewController") as! HomeViewController
present(homeView, animated: true, completion: nil)
Push with XIB
let homeView = HomeViewController(nibName: "HomeViewController", bundle: nil)
self.navigationController?.pushViewController(homeView, animated: true)
Present with XIB
let homeView = HomeViewController(nibName: "HomeViewController", bundle: nil)
present(homeView, animated: true, completion: nil)
For avoiding any crashes you can also try this code -
guard let homeView = HomeViewController(nibName: "HomeViewController", bundle: nil) else {
print("Controller not available")
return
}
self.navigationController?.pushViewController(homeView, animated: true)
you can switch to a Controller in two ways
Present and Dismiss
Navigation Controller
-> First Method is simple
just need to write two lines as :
In case Controller in Another storyboard file
let newStoryBoard : UIStoryboard = UIStoryboard(name: "UserBoard", bundle:nil)
let VC = newStoryBoard.instantiateViewController(withIdentifier: "HomeViewController") as! HomeViewController
self.present(VC, animated: true, completion: nil)
Or if in same storyboard
let VC = self.storyboard?.instantiateViewController(withIdentifier: "HomeViewController") as! HomeViewController
self.present(VC, animated: true, completion: nil)
-> Second using navigation Controller
Just make use of a navigation controller and then push or pop controllers in navigation stack
let VC = self.storyboard?.instantiateViewController(withIdentifier: "HomeViewController") as! HomeViewController
self.navigationController?.pushViewController(VC, animated: true)
In your case You are providing bundle as Nil which is wrong
let HomeViewController = ViewController(nibName: ViewController, bundle: nil)
///Need to Provide Bundle detail
let HomeViewController = ViewController(nibName: ViewController, bundle: Bundle.main)
let HomeViewController = ViewController(nibName: "ViewController", bundle: Bundle.main)
self.presentViewController(HomeViewController, animated: true, completion: nil)

View Controllers not being presented correctly

When the function is done executing I want to go back to my ProfileTabViewController. I want to get sent back to the view controller keeping the ProfileTabViewControllers same navigation bar and UITabBarController from before. The way I have it now it gets sent to the ProfileTavViewController but I lose my navigation bar as well as the tab bar. How do I just send it back to the original ProfileTabViewController. The first image is the original ViewController and the second image is when it gets sent back.
#IBAction func updateAction(_ sender: Any) {
let newInterests = options.joined(separator: " , ")
if newInterests == ""{
showAlert(message: "Please select at least one interest.")
}else{
updateFunction()
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "ProfileTabViewController") as! UIViewController
self.navigationController?.present(vc, animated: true, completion: nil)
}
}
While Presenting a ViewController it will not show navigationbar beacause presentation depends on UIViewController not NavigationViewcontroller.
You have to use popViewController method to get navigation bar.
for controller in self.navigationController?.viewControllers {
if controller is ProfileViewController {
self.navigationController!.popToViewController(controller, animated: true)
break
}
}
//Use Pop

Add a "Done" button to dismiss a programmatically created UIViewController

I'm a swift newbie and made a demo app which has a UIViewController and UITableViewController in the Storyboard. The UIViewController has a Collection View and an embedded navigation controller. On tapping a collection view cell, the app segues to the UITableViewController. I can come back to my UIViewController from the UITableViewController as well. All this works well. Now, some of the rows in my UITableViewController have URLs. On tapping a URL, I programmatically create a webview inside a UIviewcontroller and get the URL to load in it. The issue is that I can't dismiss the webview and get back to my UITableViewController after this. This is the code I have in my UITableViewController class :
// When user taps a row, get the URL and load it in a webview
let mywebViewController = UIViewController()
let webView = UIWebView(frame: mywebViewController.view.bounds)
webView.loadRequest(NSURLRequest(URL: url)) // url from the row a user taps
mywebViewController.view = webView
self.navigationController!.presentViewController(mywebViewController, animated: true, completion: nil)
How can I add a Done button to this programmatically created UIviewcontroller to dismiss the webview and get back to my UITableViewController
You can wrap the mywebViewController in a UINavigationController then set the rightBarButtonItem to UIBarButtonItem(barButtonSystemItem: .Done, target: self, action: "dismiss") like this:
let navController = UINavigationController(rootViewController: mywebViewController)
mywebViewController.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Done, target: self, action: "dismiss")
self.navigationController!.presentViewController(navController, animated: true, completion: nil)
and add a dismiss function in your UITableViewController:
func dismiss() {
self.dismissViewControllerAnimated(true, completion: nil)
}
Add done button to your presented ViewController and make an action for it.
To dismiss ViewController try this :
self.dismissViewControllerAnimated(true, completion: nil)
You can do one thing.
As you are pushing your mywebViewController into the navigation controller, make the navigation bar visible.
By simply tapping on Back button in the navigation bar you can easily dismiss your mywebViewController.
Hope this helps you. :)
Add button in mywebViewController and in its #IBAction, call
self.dismissViewControllerAnimated(true, completion: nil)
Update for Swift 4 & 5
In the VC you're navigating from
navController.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(self.dismiss))
Have to declare your selector as an #objc func
#objc func dismiss(){
self.dismiss(animated: true)
}

Resources