How to reload data in UITableView when popping view controller - swift

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

Related

Perform function on Dismiss of a View Controller

i am calling a GET(method) API on viewDidAppear function of a view controller. i am presenting a new view controller using navigation controller over my first view controller. on the second view controller i am calling an API of Post Method to add another entry into my previous screen Get method API. But when I dismiss the second View Controller the Get API data remains the same and when i again runs the code the data was updated on the first view controller. Can someone tell me that how to check on first view controller that my second view controller is dismissed so that i can call API there.
I got the solution for this. It didn't work by calling the API on viewDidAppear() or viewWIllAppear() . This will be done by using swift closures.
Below is the code:
class 1stViewController: UIViewController {
#IBAction func buttonTapped(_ sender: UIButton) {
guard let secondViewController = self.storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController else { return }
secondController.callbackClosure = { [weak self] in
print("call API")
}
self.navigationController?.pushViewController(secondController, animated: true)
}
}
On Second view Controller:
class SecondViewController: UIViewController {
var callbackClosure: ((Void) -> Void)?
override func viewWillDisappear(_ animated: Bool) {
callbackClosure?()
}
}

Change variable while dismissing modal controller

EDIT: I have decided to change the way my app works, so this problem is solved. Thanks to everyone who helped!
I have a modal controller where when I press a button it dismisses the view. What I want to do is change a variable in another view controller when I dismiss it, is that possible? Or, if this doesn't work, is there a way for me to access the changed variable of another swift file? I will add my code below:
class PopupViewController: UIViewController {
var event = ""
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func dismiss(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
#IBAction func event910(_ sender: Any) {
event = "storyTime"
dismiss(animated: true, completion: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! ViewController
vc.event = event
}
}
I want to pass the changed variable "event" to another view controller, how can I do this?
Delegate View Controller is as follows. : -
it is the place where you will send the data to the next swift file
protocol myprotocol {
func anyfunction(_ param1:String)
}
struct mystruct1 {
var delegate:myprotocol?
// where you want tot start the delegate / send the data to the next file
func anymethod(){
delegate.anyfunction(sendTheDataYouWant)
}
}
// it is here you will receive the data
class anyclass:UIViewController ,myprotocol {
let class1 = mystruct1()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
class1.delegate = self
}
func anyfunction(param1:String){
// here Save the data you want
// because this function will be triggered as delegate will be called
}
}
ps:- I reccomend you to read https://docs.swift.org/swift-book/LanguageGuide/Protocols.html
& apple docs

Swift: How to dismiss a ViewController programmatically?

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.

ViewDidAppear not called automatically

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

nothing happens on trying to dismiss popover viewcontroller Swift

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.