Refreshing tableview data after dismiss() called on another tableview - swift

Setup: table view controller has button (Add) that pops up another view controller with a form. I'm using Realm to store data, so no need to pass data back. However, when I dismiss() the view controller, and return to the table view controller, I cannot get tableView.reloadData() to work.
I have tried viewWillAppear() and viewDidAppear() but neither appear to be in the call stack.
Any ideas where I need to put this?

You need a delegate
let second = ///
second.delegate = self
When you dismiss in 2nd vc
delegate?.refresh()

Probably your second controller is being displayed modally. According to Apple Developer Documentation:
If a view controller is presented by a view controller inside of a popover, this method is not invoked on the presenting view controller after the presented controller is dismissed.
You can solve your problem with a delegate:
protocol ControllerBDelegate {
func willDismiss()
}
class ControllerA {
func open() {
let vc = ControllerB()
vc.delegate = self
self.present(vc, animated: true)
}
}
extension ControllerA : ControllerBDelegate {
func willDismiss() {
self.tableView.reloadData()
}
}
class ControllerB {
weak var delegate: ControllerBDelegate?
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
delegate?.willDismiss()
}
}

Related

self.dismiss resets parent view controller's screen data

I am presenting a view controller inside the ParentViewController:
let item = getCurrentItem()
let presentViewController = viewController as! TestViewController
presentViewController.id = item.id
presentViewController.dismissController { // my custom delegate to inform the parent vc
self.dismiss(animated: true, completion: nil) // presenting view controller is closing then viewWillAppear will be triggered
}
self.present(presentViewController, animated: true, completion: nil)
But when dismiss is called, the ParentViewController's global arrays and variables are back to their initial values.
class ParentViewController {
// MARK: - Stored Properties
private let refreshControl = UIRefreshControl()
private var items = [String:[Item]]()
private var screenLoaded: Bool = false
// MARK: - Controller Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// after dismiss is called viewWillAppear gets hit but the "screenLoaded" is always false
if !screenLoaded {
screenLoaded = true
}
// items is initialised as well
}
private func openPopover() {
let item = getCurrentItem()
let presentViewController = viewController as! TestViewController
presentViewController.id = item.id
presentViewController.dismissController { // my custom delegate to inform the parent vc
self.dismiss(animated: true, completion: nil) // presenting view controller is closing then viewWillAppear will be triggered
}
self.present(presentViewController, animated: true, completion: nil)
}
}
How can I keep the parent view controller's original state after the dismiss is called?
After hours of digging Apple's documentations finally I found the following solution and it works for me. Hope it helps someone in the future.
[https://developer.apple.com/documentation/uikit/uimodalpresentationstyle/overcurrentcontext][1]
presentViewController.modalPresentationStyle = .overCurrentContext
Also, if there is a tabbar, the bottom of the child view controller will be under it if you set the presentation style to overCurrentContext. The workaround is to present the child view controller from the window's rootViewController.
sender.view.window?.rootViewController?.present(viewController, animated: true, completion: nil)
Now when the dismiss is called viewWillAppear is not triggered and I can keep the parent view controller's data and it's state.

How to dismiss a navigation controller?

I'm presenting a navigation controller (detailview) with showDetailViewController(). I want to dismiss it when a button is pressed. How can I dismiss this viewcontroller?
Code I've attempted:
//detailviewcontroller
#objc
func cancel(_ sender: Any) {
print("cancelPressed")
//self.navigationController?.popViewController(animated: true)
//self.navigationController?.dismiss(animated: true, completion: nil)
//self.dismiss(animated: true, completion: nil)
//splitViewController?.dismiss(animated: true, completion: nil)
//splitViewController?.navigationController?.popToRootViewController(animated: true)
//splitViewController?.navigationController?.popViewController(animated: true)
//splitViewController?.navigationController?.dismiss(animated: true, completion: nil)
//navigationController?.popViewController(animated: true)
//navigationController?.dismiss(animated: true, completion: nil)
//self.navigationController?.popToRootViewController(animated: true)
//self.navigationController?.viewControllers.remove(at: 1)
//self.navigationController?.viewControllers.remove(at: 0) - this one removes to blank view
//self.presentingViewController?.dismiss(animated: true, completion: nil)
}
I've tried multiple stackoverflow solutions:
Dismissing a navigation view controller
How to dismiss a view controller in a navigation controller, without dismissing the whole stack
ios swift - dismiss root view controller of navigation controller
Can't dismiss View Controller that's embedded in a Navigation Controller
How to dismiss a navigation controller presented from another navigation controller in iOS 10 and below?
Can't dismiss navigation controller in Swift
Can't dismiss navigation controller in Swift
Dismissing View Controller Doesn't Work While Using Navigation Controller
Dismiss current navigation controller when clicked tab bar
How to dismiss a certain view controller
How I'm presenting the detailviewcontroller:
//masterviewcontroller
// delegation for passing data between controllers
weak var passDelegate: PlaceSelectionDelegate?
func insertNewObject(_ sender: Any) {
if let detailViewController = passDelegate as? DetailViewController {
if let detailNavigationController = detailViewController.navigationController {
detailViewController.delegate = self
splitViewController?.showDetailViewController(detailNavigationController, sender: nil)
}
}
}
Expected results:
dismiss detail viewController on button press.
Actual results:
No dismiss.
Ok, I've been looking for a way of dealing with this showDetailViewController for a while now and although I'm not sure this answers your question, this is how I did it:
I have two controllers, let's call it FirstController and SecondController. They both inherit from UIViewController. When I click a certain navbar button in the FirstController, I use showDetailViewController to show SecondController as a sheet moving upwards, like the "calendars" button in the Calendar app.
In FirstController I implemented 2 functions: one to show the SecondController and one to dismiss it. I call the dismiss action from within the SecondController. It looks like this:
// FirstController
#objc func showSecondView() {
let vc = SecondController()
vc.delegate = self
let nav = UINavigationController(rootViewController:vc)
self.showDetailViewController(nav, sender: self)
}
func closeSecondView() {
self.dismiss(animated: true)
}
As you can see I wrapped my SecondController in a UINavigationController, so it shows the navbar with title and all. This is how the code looks like:
// SecondController
weak var delegate: FirstController!
override func viewDidLoad() {
super.viewDidLoad()
title = "Second"
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(closeMe))
}
#objc func closeMe() {
delegate.closeSecondView()
}
Notice how I made a property for the FirstController and I use it to dismiss the SecondController. When I presented SecondController I assigned self as delegate, which means I can now call the closeSecondView method from within it.
I am not sure whether it works out of the box with split-view. The documentation seems to suggest that whenever you use it, if the screen is big enough, it behaves differently because UISplitViewController overrides it. It also says there that all view controllers have this method.
Take a look: https://developer.apple.com/documentation/uikit/uiviewcontroller/1621432-showdetailviewcontroller
Give it a go, I hope this helps.
This should work if you simply want to dismiss the navigation controller
navigationController?.dismiss(animated: true, completion: nil)

How to call perform segue method from another class?

I want to perform segue from View B to View C and I am calling method in class A.
My segue method in Class B is-
//ViewController B
func nextViewAction() -> Void {
self.performSegueWithIdentifier("nextview", sender: self)
}
And I am calling it in Class A like this-
//ViewController A
#IBAction func sideMenuAction(sender: AnyObject) {
ViewClassB().nextViewAction()
}
But it crashing- Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Receiver () has no segue with identifier 'nextview''
Have you set the name nextview for the segue between Controller B and Controller C? You can check by clicking on the segue between the two controllers, and then checking the Identifier value in the Attributes Inspector.
What you can have is move to the third viewcontroller from first viewcontroller by skipping the second one use the code below..
class MyViewController: UIViewController, UINavigationControllerDelegate {
var viewControllerToInsertBelow : UIViewController?
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.delegate = self
}
func pushTwoViewControllers() {
if let viewController2 = self.storyboard?.instantiateViewControllerWithIdentifier("id1"),
let viewController3 = self.storyboard?.instantiateViewControllerWithIdentifier("id2") { //change this to your identifiers
self.viewControllerToInsertBelow = viewController2
self.navigationController?.pushViewController(viewController3, animated: true)
}
}
//MARK: - UINavigationControllerDelegate
func navigationController(navigationController: UINavigationController, didShowViewController viewController: UIViewController, animated: Bool) {
if let vc = viewControllerToInsertBelow {
viewControllerToInsertBelow = nil
let index = navigationController.viewControllers.indexOf(viewController)!
navigationController.viewControllers.insert(vc, atIndex: index)
}
}
}
Reference : SO Post
Don't need to do any other code just use
notification observer it will solve your problem.
Thank you
I think you cannot trigger segue action from AViewController because what
performSegue does is that :
Initiates the segue with the specified identifier from the current view controller's storyboard file.
Try implementing segue from A to C

Swift: Function Called when Back is pressed

I would like to perform some actions when a user presses the "Back" button on my Navigation Controller. Is there a Swift function that is called when this happens?
Try this (copied and pasted from manecosta)
Replacing the button to a custom one as suggested on another answer is possibly not a great idea as you will lose the default behavior and style.
One other option you have is to implement the viewWillDisappear method on the View Controller and check for a property named isMovingFromParentViewController. If that property is true, it means the View Controller is disappearing because it's being removed (popped).
Should look something like:
override func viewWillDisappear(animated : Bool) {
super.viewWillDisappear(animated)
if (self.isMovingFromParentViewController()){
// Your code...
}
}
Here is the link to the other question
override func viewWillDisappear(animated: Bool) {
// Do Your Lines of Code ...
}
Everytime when back button or Done is pressed or a view is popped out this function is called.. you need to override this..
Try to call this
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
if self.isMovingFromParent {
print("isMovingFromParent()") // dismiss for push viewController
}
if self.isBeingDismissed {
print("isBeingDismissed()") // dismiss for modal that doesn't has navigationController
}
if self.navigationController?.isBeingDismissed ?? false {
print("navigationController?.isBeingDismissed()") // dismiss for modal that has navigationController
}
}
Try this:
override func willMoveToParentViewController(parent: UIViewController?) {
if parent == nil {
// Back button Event handler
}
}
Swift 5
"viewWillDisappear" call Every time for forward or backward.
But if you add this check it's call only when back from controller.
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if self.isMovingFromParent {
print("Only back action")
}
}

Dismiss Popover after touch

I've created a popover inside my MainViewController when some button its touched using the UIPopoverPresentationController and set like it's delegate like it's showed in the WWDC 2014, in the following way :
MainViewController.swift
class MainViewController : UIViewController, UIPopoverPresentationControllerDelegate {
#IBAction func showPopover(sender: AnyObject) {
var popoverContent = self.storyboard?.instantiateViewControllerWithIdentifier("PopOverViewController") as UIViewController
popoverContent.modalPresentationStyle = UIModalPresentationStyle.Popover
var popover = popoverContent.popoverPresentationController
popoverContent.preferredContentSize = CGSizeMake(250, 419)
popover!.delegate = self
popover!.sourceView = self.view
popover!.sourceRect = CGRectMake(180,85,0,0)
self.presentViewController(popoverContent, animated: true, completion: nil)
}
}
The popover have a View inside it and when the View it's clicked with a Tap Gesture Recognizer I show LastViewController using a modal segue, the modal segue is created through the Interface Builder, not in code using an action to present the another LastViewController
Once the LastViewController is dismissed and I'm back in the MainViewController the popover remains open.
Inside the PopOverController I only have the default code nothing more.
LastViewController.swift
#IBAction func dismissVIew(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
}
The above code is used to dismiss the LastViewController once the button inside is touched.
Storyboard
How can I dismiss the popover once the another LastViewController it's visible, or before the another LastViewController should be opened?
Thanks in advance
I have already answer same problem over here.
There scenario is different but solution is same
You have to write code for dismiss presented view controller on completion of current view controller.
Write below code on your dismissVIew method of LastViewController.swift
var tmpController :UIViewController! = self.presentingViewController;
self.dismissViewControllerAnimated(false, completion: {()->Void in
println("done");
tmpController.dismissViewControllerAnimated(false, completion: nil);
});
Download link
In your button action on the FinalViewController, have you tried:
#IBAction func dismissMe() {
//this should tell the popover to tell the main view controller to dismiss it.
self.presentingViewController!.presentingViewController!.dismissViewControllerAnimated(false, completion: nil)
}
here is how I would do it.
I usually use lazy initialization for the PopoverViewController and it's ContentViewController
lazy var popoverVC: UIPopoverController = {
let vc = UIPopoverController(contentViewController: self.contentVC)
vc.delegate = self
return vc
}()
lazy var contentVC: UIViewController = {
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("ContentViewController") as UIViewController
vc.modalInPopover = true
return vc
}()
inside my contentViewController I hold a reference to the UIPopoverController.
var popoverVC: UIPopoverController!
then when I show the popover i just assign the popoverController to the contentViewController
#IBAction func showPopover(sender: UIButton) {
contentVC.popoverVC = self.popoverVC
let viewCenterRect = self.view.convertRect(self.view.bounds, toView: self.view)
popoverVC.presentPopoverFromRect(CGRectMake(CGRectGetMidX(viewCenterRect), CGRectGetMidY(viewCenterRect), 1, 1), inView: self.view, permittedArrowDirections: UIPopoverArrowDirection.allZeros, animated: true)
}
finally I dismiss the Popover programmatically inside an #IBAction
#IBAction func dismissPopover(sender: AnyObject) {
popoverVC.dismissPopoverAnimated(true)
}
Inside the viewcontroller you can override viewWillAppear()
Inside of this block dimiss it
override public func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
_viewToDismiss.removeFromSuperView()
}
But the above code assumes you have a reference to the PopOver object, which I don't think is good practice based on how you described the problem.
Rather, why not have the viewcontroller that created the PopOver be responsible for destroying it. Put this in the class that listens for the button touch (which I also assumes creates the PopOver as well)
- (void)viewWillDisappear:(BOOL)animated
{
_popOver.removeFromSuperView()
}
The popover have a View inside it and when the View it's clicked with a Tap Gesture Recognizer I show another ViewController using a modal segue.
As far as I understand from what you say, you should be able to call dismissViewControllerAnimated(_:completion:) from the action associated to your tap gesture recogniser. This will dismiss the popover you presented calling:
self.presentViewController(popoverContent, animated: true, completion: nil)
You can call this method on the popover view controller itself, depending on what is more convenient for you:
The presenting view controller is responsible for dismissing the view controller it presented. If you call this method on the presented view controller itself, it automatically forwards the message to the presenting view controller.
Based on #Jageen answer
Swift 4.2
let tmpController :UIViewController! = self.presentingViewController;
self.dismiss(animated: false, completion: {()->Void in
print("done");
tmpController.dismiss(animated: false, completion: nil);
});