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
When to Use Dequeuereusablecellwithidentifier VS Dequeuereusablecellwithidentifier: Forindexpath
Getting a List of Files in a Directory With a Glob
How to Use Uivisualeffectview to Blur Image
How to Use Auto Layout to Move Other Views When a View Is Hidden
Calling a Phone Number in Swift
How to Compress/Resize Image on iOS Before Uploading to a Server
Interface Builder: What Are the Uiview's Layout iOS 6/7 Deltas For
How to Exclude Notes and Reminders Apps from the Uiactivityviewcontroller
Ios 6: How to Restrict Some Views to Portrait and Allow Others to Rotate
Looping a Video With Avfoundation Avplayer
How to Validate an E-Mail Address in Swift
How to Handle Background Audio Playing While iOS Device Is Locked or on Another Application
How to Make a Push Segue When a Uitableviewcell Is Selected
Difference Between Viewdidload and Viewdidappear
Picking Two Different Images in the Same View Controller Using Imagepickercontroller in Swift