Detecting Sheet Was Dismissed on iOS 13

Detecting sheet was dismissed on iOS 13

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?.

How do I detect if an actionSheet was dismissed?

You can use .onChange for action sheet presented state like on demo below

    @Binding var showActionSheet: Bool
var body: some View {

// some view here
.actionSheet(isPresented: $showActionSheet) {
// some ActionSheet here
}
.onChange(of: showActionSheet) { flag in
if !flag {
print("dismissed") // << here !!
}
}
}

iOS 13 Modals - Calling swipe dismissal programmatically

Mojtaba Hosseini, answer is something I was looking for.

Currently, I need to write a delegate function to let the presenting view know that the user dismissed the modal PLUS do the presentationControllerDidDismiss handler for swipe dismissals:

@IBAction func btnDismissTap(_ sender: Any) {
self.dismiss(animated: true, completion: {
self.delegate?.myModalViewDidDismiss()
})
}

I wanted to handle both of these the same way and Mojtaba's answer works for me. However, presentationControllerDidDismiss does not get invoked if you call it inside of the self.dismiss completion block, you need to call it before.

I adapted my code to use "presentationControllerWillDismiss" (for clarity) and simply called the delegate before I dismiss programmatically in my modals and it works great.

@IBAction func btnDismissTap(_ sender: Any) {
if let pvc = self.presentationController {
pvc.delegate?.presentationControllerWillDismiss?(pvc)
}
self.dismiss(animated: true, completion: nil)
}

Now, I no longer need to create delegate functions to handle modal dismissals in code and my swipe handler takes care of all scenarios.

FYI, what I'm "handling" is doing some UI clean up (de-selections, etc) on the presenting UI once the modal is dismissed.

iOS 13 Dismiss Modal Page Sheet with a button

If the originating VC is of type PresentingVC, and the modal of type PresentedVC, I'd use the below approach. Given the segue statement above I assume you're using storyboards, but if not replace the prepare(for segue:) with injecting the delegate value when you instantiate yourPresentedVC

For starters, set your PresentedVC up to hold a delegate by defining the delegate protocol and providing a delegate property.


protocol PresentedVCDelegate {
func presentedVCDidUpdateDatabase()
}

class PresentedVC {
var delegate: PresentedVCDelegate?

@IBAction buttontapped(_ sender: Any) {
//existing code to validate and save data to databse
delegate?. presentedVCDidUpdateDatabase()
dismiss(animated: true)
}
}

Update the PresentingVC so that it injects itself as the delegate when instantiating its child VC:

class PresentingVC {

//all the current code

// and amend preapre(for:) something like
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller.
if let presented = segue.destination as? PresentedVC {
presented.delegate = self
//anything else you do already
}
}
}

The extend it to support the protocol method

extension PresentingVC: PresentedVCDelegate {
func presentedVCDidUpdateDatabase() {
tableView.reloadData()
//any other work necessary after PresentedVC exits
}
}

Note: written from memory and not compiled, so may contain minor typos, but hopefully it's enough detail to get the concept across?

Picker data not updating when sheet is dismissed

The issue is with the ForEach signature you're using. It works only for constant data. If you want to use with changing data, you have to use something like:

ForEach(sources, id: \Source.name.hashValue) {
Text(verbatim: $0.name!)
}

Note that hashValue will not be unique for two entity objects with the same name. This is just an example

viewDidAppear issues in iOS 13

The default presentation style, which is the card style, will not trigger viewDidAppear when you dismiss the view controller.

You'll most likely have to switch to a different modalPresentationStyle that will trigger it. One of the presentation style's that does trigger it for example is .fullScreen

So for example, for viewControllerToPresent you'd want to set it's presentation style to

viewControllerToPresent.modalPresentationStyle = .fullScreen

before you present it

Detect when a presented view controller is dismissed

According to the docs, the presenting controller is responsible for the actual dismiss. When the presented controller dismisses itself, it will ask the presenter to do it for it. So if you override dismissViewControllerAnimated in your VC1 controller I believe it will get called when you hit cancel on VC2. Detect the dismiss and then call the super classes version which will do the actual dismiss.

As found from discussion this does not seem to work. Rather than rely on the underlying mechanism, instead of calling dismissViewControllerAnimated:completion on VC2 itself, call dismissViewControllerAnimated:completion on self.presentingViewController in VC2. This will then call your override directly.

A better approach altogether would be to have VC2 provide a block which is called when the modal controller has completed.

So in VC2, provide a block property say with the name onDoneBlock.

In VC1 you present as follows:

  • In VC1, create VC2

  • Set the done handler for VC2 as: VC2.onDoneBlock={[VC2 dismissViewControllerAnimated:YES completion:nil]};

  • Present the VC2 controller as normal using [self presentViewController:VC2 animated:YES completion:nil];

  • In VC2, in the cancel target action call self.onDoneBlock();

The result is VC2 tells whoever raises it that it is done. You can extend the onDoneBlock to have arguments which indicate if the modal comleted, cancelled, succeeded etc....



Related Topics



Leave a reply



Submit