Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS 13 viewWillAppear not getting called [duplicate]

Before iOS 13, presented view controllers used to cover the entire screen. And, when dismissed, the parent view controller viewDidAppear function were executed.

Now iOS 13 will present view controllers as a sheet as default, which means the card will partially cover the underlying view controller, which means that viewDidAppear will not be called, because the parent view controller has never actually disappeared.

Is there a way to detect that the presented view controller sheet was dismissed? Some other function I can override in the parent view controller rather than using some sort of delegate?

like image 961
Marcos Tanaka Avatar asked Jun 12 '19 19:06

Marcos Tanaka


People also ask

Why viewWillAppear is not called?

The Simple Answer. The technical reason for when viewWillAppear gets called is simple. Notifies the view controller that its view is about to be added to a view hierarchy. It can't be any view hierarchy — it has to be the one with a UIWindow at the root (not necessarily the visible window).

Does viewWillAppear get called before viewDidLoad?

viewWillAppear(_:)Always called after viewDidLoad (for obvious reasons, if you think about it), and just before the view appears on the screen to the user, viewWillAppear is called.

How many times is viewWillAppear called?

If both these methods are called, what's the difference between the two? viewDidLoad() is only called once, when the view is loaded from a . storyboard file. viewWillAppear(_:) is called every time the view appears.

Can viewDidAppear be called multiple times?

viewDidAppear callback can be called multiple times in a single presentation.


Video Answer


5 Answers

Is there a way to detect that the presented view controller sheet was dismissed?

Yes.

Some other function I can override in the parent view controller rather than using some sort of delegate?

No. "Some sort of delegate" is how you do it. Make yourself the presentation controller's delegate and override presentationControllerDidDismiss(_:).

https://developer.apple.com/documentation/uikit/uiadaptivepresentationcontrollerdelegate/3229889-presentationcontrollerdiddismiss


The lack of a general runtime-generated event informing you that a presented view controller, whether fullscreen or not, has been dismissed, is indeed troublesome; but it's not a new issue, because there have always been non-fullscreen presented view controllers. It's just that now (in iOS 13) there are more of them! I devote a separate question-and-answer to this topic elsewhere: Unified UIViewController "became frontmost" detection?.

like image 66
matt Avatar answered Oct 06 '22 05:10

matt


Here's a code example of a parent view-controller which is notified when the child view-controller it presents as a sheet (i.e., in the default iOS 13 manner) is dismissed:

public final class Parent: UIViewController, UIAdaptivePresentationControllerDelegate
{
  // This is assuming that the segue is a storyboard segue; 
  // if you're manually presenting, just set the delegate there.
  public override func prepare(for segue: UIStoryboardSegue, sender: Any?)
  {
    if segue.identifier == "mySegue" {
      segue.destination.presentationController?.delegate = self;
    }
  }

  public func presentationControllerDidDismiss(
    _ presentationController: UIPresentationController)
  {
    // Only called when the sheet is dismissed by DRAGGING.
    // You'll need something extra if you call .dismiss() on the child.
    // (I found that overriding dismiss in the child and calling
    // presentationController.delegate?.presentationControllerDidDismiss
    // works well).
  }
}

Jerland2's answer is confused, since (a) the original questioner wanted to get a function call when the sheet is dismissed (whereas he implemented presentationControllerDidAttemptToDismiss, which is called when the user tries and fails to dismiss the sheet), and (b) setting isModalInPresentation is entirely orthogonal and in fact will make the presented sheet undismissable (which is the opposite of what OP wants).

like image 44
SuddenMoustache Avatar answered Oct 06 '22 05:10

SuddenMoustache


Another option to get back viewWillAppear and viewDidAppear is set

let vc = UIViewController()
vc.modalPresentationStyle = .fullScreen

this option cover full screen and after dismiss, calls above methods

like image 43
PiterPan Avatar answered Oct 06 '22 04:10

PiterPan


For future readers here is a more complete answer with implementation:

  1. In the root view controllers prepare for segue add the following (Assuming your modal has a nav controller)
    // Modal Dismiss iOS 13
    modalNavController.presentationController?.delegate = modalVc
  1. In the modal view controller add the following delegate + method
// MARK: - iOS 13 Modal (Swipe to Dismiss)

extension ModalViewController: UIAdaptivePresentationControllerDelegate {
    func presentationControllerDidAttemptToDismiss(_ presentationController: UIPresentationController) {


        print("slide to dismiss stopped")
        self.dismiss(animated: true, completion: nil)
    }
}
  1. Ensure in the modal View Controller that the following property is true in order for the delegate method to be called
    self.isModalInPresentation = true
  1. Profit
like image 29
Jerland2 Avatar answered Oct 06 '22 04:10

Jerland2


Swift

General Solution to call viewWillAppear in iOS13

class ViewController: UIViewController {

        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            print("viewWillAppear")
        }

        //Show new viewController
        @IBAction func show(_ sender: Any) {
            let newViewController = NewViewController()
            //set delegate of UIAdaptivePresentationControllerDelegate to self
            newViewController.presentationController?.delegate = self
            present(newViewController, animated: true, completion: nil)
        }
    }

    extension UIViewController: UIAdaptivePresentationControllerDelegate {
        public func presentationControllerDidDismiss( _ presentationController: UIPresentationController) {
            if #available(iOS 13, *) {
                //Call viewWillAppear only in iOS 13
                viewWillAppear(true)
            }
        }
    }
like image 23
dimohamdy Avatar answered Oct 06 '22 04:10

dimohamdy