I have a viewcontroller that is presented as popover when the user clicks on an ImageView.
The problem is, I added a button to dismiss it but when I tap on it nothing happens.
The code I have is:
#IBAction func onCloseTapped(_ sender: Any) {
presentedViewController?.dismiss(animated: true, completion: nil)
}
I've also tried dismiss(animated: true, completion: nil) and other methods, but still nothing.
Can anyone tell me what I am doing wrong?
Edit: Posting a screenshot:
Edit 2: I'm presenting it from the storyboard. I've added a gesture recognizer on the image, then added segue from the storyboard that says present as popover, then anchor to the image.
it won't work because when you show as popover, the viewController doesn't have the navigationController. You have to create a delegate method and use the dismiss function on the viewController that make the call to the popover.
Here an exemple:
make the popover delegate, in the popover viewController:
protocol PopoverViewControllerDelegate: NSObjectProtocol {
func dismiss()
}
then you create a delegate variable and call when the button is tapped:
var delegate: PopoverViewControllerDelegate?
#IBAction func onCloseTapped(_ sender: Any) {
delegate?.dismiss()
}
Now in the viewController that call the popover you override the prepare for segue method to set the popover delegate:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "popover" {
if let vc = segue.destination as? PopoverViewController {
vc.delegate = self
}
}
}
Now you just need to use the delegate to dismiss your popover viewController:
extension ViewController: PopoverViewControllerDelegate {
func dismiss() {
navigationController?.dismiss(animated: true, completion: nil)
}
}
Don't forget to put the identifier for you segue, the identifier that we use is this = "popover"
Hope that help you.
Related
I am trying to make a segue to a table view controller when a button is tapped in my view controller programmatically. Here is my code:
#objc func editProfileButtonAction(sender: UIButton!) {
print("ButtonTapped")
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let identifier = segue.identifier {
if identifier == "EditProfile" {
var editProfileTableViewController = segue.destination as! EditProfileTableViewController
editProfileTableViewController = self
}
}
}
}
I really could use some help. I also need to make a segue to a collection view controller using a button in the same view controller.
Okay to clarify that. There is no way to create a segue programmatically. Segues are the arrows on storyboard linking from one to another VC. They are called with: performSegue. This calls the function prepare.
If you want to show a new VC when hitting a button (without segue), then you use the present(VC(), animated: true, completion: nil) } inside the button function. The VC is presented modally.
#objc func editProfileButtonAction(sender: UIButton!) {
print("editProfileButtonAction")
present(EditProfileTableViewController(), animated: true, completion: nil)
}
Make sure, that the segue in the Storyboard has exactly the identifier: "EditProfile". Normally I'm writing identifiers with lower letter in the beginning. You also need to prepare for Segue. For example set the delegate:
// Set ViewController class as the delegate of the EditProfileTableViewControllerDelegate protocol
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let identifier = segue.identifier {
if identifier == "EditProfile" {
let editProfileTableViewController = segue.destination as! EditProfileTableViewController
editProfileTableViewController = self
}
}
}
At one point in my coding time, I deleted all my storyboard because of too many error's that I could hardly solve. Now I'm doing it all programmatically. At first it was a bit hard to set up all the view's by myself but after all I'm very glad I'm not using storyboards anymore. For some stuff I need xib's, and for testing storyboard. Just if you're interested: the most iOS programmers are using storyboard, so it's okay if you go on with that.
The advantage of doing it all programmatically is that there are no segue's anymore. So just present, and on navigation VC's push, pop, ...
I got a little problem.
On my main view controller I got a bar button that opens a slide menu, which is a regular view controller using a slide in transition. The slide menu has a button to open another view controller. When the new view controller is opened, you have the option to cancel, which dismisses the current view controller. The problem is, that the user ends up in the menu view once again, instead of the main view controller. Would be very happy to know what I am doing wrong :)
func openSupport() {
guard let creditViewContoller = storyboard?.instantiateViewController(withIdentifier: "support") as? CreditViewController else { return }
present(creditViewContoller, animated: true)
}
#IBAction func buttonSupport(_ sender: UIButton) {
let menuView = MenuViewController()
menuView.dismiss(animated: true, completion: nil)
openSupport()
print("Tap on Support")
}
you can dismiss view controller simply by using
self.dismiss(animated: true, completion: nil)
Consider
#IBAction func buttonSupport(_ sender: UIButton) {
let menuView = MenuViewController() // (1)
menuView.dismiss(animated: true, completion: nil) // (2)
openSupport() // (3)
print("Tap on Support")
}
This:
Creates new MenuViewController but never presents it;
Calls dismiss on view controller that was never presented; and
Calls openSupport from this MenuViewController instance (which was never dismissed).
Bottom line, you want to let the main view controller that presented the menu do the presenting. So, the menu view controller should:
Define a protocol for it to inform the presenting view controller to transition to the next scene:
protocol MenuViewControllerDelegate: class {
func menu(_ menu: MenuViewController, present viewController: UIViewController)
}
And then the menu view controller can, when it’s done dismissing, tell its delegate what it should present:
class MenuViewController: UIViewController {
weak var delegate: MenuViewControllerDelegate?
#IBAction func didTapSupport(_ sender: Any) {
dismiss(animated: true) {
guard let controller = self.storyboard?.instantiateViewController(withIdentifier: "support") else { return }
self.delegate?.menu(self, present: controller)
}
}
#IBAction func didTapCancel(_ sender: Any) {
dismiss(animated: true)
}
}
Then the main view controller needs to
Make sure to set the delegate of the menu view controller:
class ViewController: UIViewController {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? MenuViewController {
destination.delegate = self
}
}
}
and
Make sure to present the view controller that the menu controller asked it to:
extension ViewController: MenuViewControllerDelegate {
func menu(_ menu: MenuViewController, present viewController: UIViewController) {
present(viewController, animated: true)
}
}
There are lots of different ways of achieving this, so don’t get lost in the details here. But the idea is to have some system by which the menu view controller can request whomever is to present the support view to do so, not try to do it itself.
If I have two ViewControllers one which contains a UITableView and another which updates data in the tableView. How would I reload the table data once I pop of the viewController and go back to the view with the tableView?
I already tried using viewDidAppear
You could use viewWillAppear just like Rajesh suggested:
override func viewWillAppear(_ animated: Bool) {
tableView.reloadData()
}
Or you could use a callback function to pass data and reload view controller 1's tableview.
In ViewController 2, define your callback function:
// Callback function
var callbackResult: ((data) -> ())?
And call it before going back to ViewController 1:
callbackResult?(data)
self.navigationController?.popViewController(animated: true)
In ViewController 1, use the callback function's closure to collect the result and reload your tableView. This can happen inside prepareForSegue, for example:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToViewController2" {
let destinationVC = segue.destination as! ViewController2
// Set any variable in ViewController2
destinationVC.callbackResult = { result in
// assign passing data etc..
self.tableView.reloadData()
}
}
}
You may try doing something like this:
class TableViewController: UITableViewController {
func showUpdatingViewController() {
let vc = UpdatingViewController()
vc.onUpdate = { [weak self] in
self?.tableView.reloadData()
}
navigationController?.pushViewController(vc, animated: true)
}
}
class UpdatingViewController: UIViewController {
var onUpdate: (() -> Void)?
func updatesFinished() {
onUpdate?()
dismiss(animated: true, completion: nil)
}
}
I was having similar issue and using viewWillAppear or viewDidAppear did not help me to reload the tableview.
I solved my situation by putting the reloadData() call within the unwindSegue
#IBAction func unwindToVCSetupDataScreen(_ unwindSegue: UIStoryboardSegue) {
/// Nothing is actually needed here
/// https://www.youtube.com/watch?v=WaSlHXNah7E #6:25
/// CTRL-Drag from Back button to the "exit" square at the very top of VC
tableView.reloadData()
}
I have a HomeViewController that is opening a WelcomeViewController using segue and is from type Sheet.
In WelcomeViewController the User is entering information and then closing(dismissing) it. After WelcomeViewController is closed I want to update HomeViewController by changing the String value of a TextField.
The Problem is, that ViewDidAppear in HomeViewController is not called after dismissing WelcomeViewController. I did not have this Problem before when developing for iOS.
How can I fix this? I tried to call it manually but got problems, because my IBOutlets were set to nil and I could not change the text in my TextField.
This is the code that invokes the segue:
self.performSegue(withIdentifier: "welcome", sender: self)
this is my viewDidAppear in HomeViewController:
override func viewDidAppear() {
super.viewDidAppear()
if (CoreDataAccess().countUsers() == 0) {
self.performSegue(withIdentifier: "welcome", sender: self)
} else {
//continue execution..
}
}
In WelcomeViewController I dismiss the ViewController using this code
dismiss(self)
This is how I solved it using a delegate.
Thanks to #ullstrm for the suggestion.
First I added this protocol to HomeViewController
protocol WelcomeDelegate {
func insertData(_ name: String, birthday: Date)
}
Then this prepare(for segue:)
override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
if let destinationViewController = segue.destinationController as? WelcomeViewController {
destinationViewController.delegate = self
}
}
And this extension at the bottom (I know it's not ok to call viewDidAppear manually , but it's working for now)
extension HomeViewController : WelcomeDelegate {
func insertData(_ name: String, birthday: Date) {
viewDidAppear()
}
}
Then in WelcomeViewController I added this variable
var delegate:WelcomeDelegate? = nil
And this line right before dismiss(self)
self.delegate?.insertData(tfName.stringValue, birthday: dpAge.dateValue)
Add your code to ViewWillAppear() instead of ViewDidAppear().
or you call a method in the HomeViewController when dismissing WelcomeViewcontroller
dismiss(animated: true) {
self.presentedViewController.myMethod()
}
I use performSegueWithIdentifier to navigate between views controllers
I have a tableView with multiple rows, when a row is actioned, i call a webViewController to play selected ressource with segue
performSegueWithIdentifier("Play", sender: nil)
Associate with :
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "Play") {
let detailVC = segue.destinationViewController as! WebViewController
detailVC.courseToPlay = self.courseToPlay
}
}
And, on WebViewController, i've a button to goBack on the list, whose i want to destroy instance of webViewController on goBack
I've not NavigationController in my storyBoard, so, this line doesn't work
navigationController.popViewControllerAnimated(true)
I use segue again to goBack :
performSegueWithIdentifier("closePlayer", sender: nil)
But each segue action instance a new view controller and they're never really free from memory, they seems to just be hiddens
eg : in Safari debugger i can see a lot of page and i can always interact with these in console
How to properly 'destroy' a view when scene change
Swift3:
self.dismiss(animated: true, completion: nil)
Hope it can help you.
Thanks
If it is a modal segue you can use
dismissViewControllerAnimated(true, completion: nil)
To dismiss the webViewController instead of another segue