Interesting change in Modal View Behavior

I have a couple of iOS apps that use the modal view implemented with declares following the example in the documentation, e.g.

[code]Var v As New ModalView

// Display ModalView modally
Declare Sub presentViewController Lib “UIKit” _
Selector “presentViewController:animated:completion:” _
(parentView As Ptr, viewControllerToPresent As Ptr, animated As Boolean, completion As Ptr)

presentViewController(Self.Handle, v.Handle, True, Nil)[/code]

As the documentation says, a button is needed to close a modal view … until now (2019r3 is the first time I noticed the change). In apps compiled with r3 (and running in iOS 13), the user can now just swipe downwards on the view to dismiss it. No more button tap necessary…

I like this new functionality though it does create some interesting new decisions for the programmer who put all of the closing code into a button in previous version. I hope that the view is actually getting closed with this new downward swipe and not just left in limbo off-screen using up memory.

Didn’t see this in the release notes (though I may have missed it). Any gotchas one should be aware of?

[quote=468190:@Richard Allmendinger]I have a couple of iOS apps that use the modal view implemented with declares following the example in the documentation, e.g.

[code]Var v As New ModalView

// Display ModalView modally
Declare Sub presentViewController Lib “UIKit” _
Selector “presentViewController:animated:completion:” _
(parentView As Ptr, viewControllerToPresent As Ptr, animated As Boolean, completion As Ptr)

presentViewController(Self.Handle, v.Handle, True, Nil)[/code]

As the documentation says, a button is needed to close a modal view … until now (2019r3 is the first time I noticed the change). In apps compiled with r3 (and running in iOS 13), the user can now just swipe downwards on the view to dismiss it. No more button tap necessary…

I like this new functionality though it does create some interesting new decisions for the programmer who put all of the closing code into a button in previous version. I hope that the view is actually getting closed with this new downward swipe and not just left in limbo off-screen using up memory.

Didn’t see this in the release notes (though I may have missed it). Any gotchas one should be aware of?[/quote]
This must be a new “feature” of the iOS 13 SDK (which we needed to fully support dark mode).

Does the Close event still fire in this scenario?

If I use my already existent close button, everything works as it did before. I haven’t upgraded to Xcode 11 yet and won’t until I get all my apps updated and through the App Store. The behavior does not happen in the Xcode 10 simulator so right now I only see it when I upload the app to a device running iOS 13. Thus I can’t right now monitor the close event to see whether it fires or not.

Can Xcode 10 and 11 exist side by side with one just toggling the command line tools as in the past?

Okay, a bit more experimentation. The Deactivate event fires but the Close event does not appear to. But here is the interesting part: the Close event also does not fire when the modal view is closed with code rather than a swipe. I’ve put some code in the Close event, set a breakpoint on the first line of the Close event, run it in the Xcode 10 simulator, closed the modal view with a button, and the breakpoint is never reached…

The Close event never fires when dismissing a modal view on iOS.

self.close does not work on a modal view.
Instead, this must be called:

[code]Declare Sub dismissViewController_ Lib “UIKit.framework” _
Selector “dismissViewControllerAnimated:completion:” _
(viewController As Ptr, animated As Boolean, completion As Ptr)

dismissViewController_(self.ViewControllerHandle, True, Nil)
[/code]

Hm. We’ve documented that it’ll fire when the destructor is called, so it’s either a bug or the modal views are leaking.

@Richard Allmendinger
Please file a bug report about the Close event not firing.

[quote=468376:@Jeremie Leroy]self.close does not work on a modal view.
Instead, this must be called:[/quote]

Thanks Jeremie. In fact, my code uses the code that you cite to close the modal view, not self.close. And when I use dismissViewController I do not see the close even firing reliably. Every so often, the message box that I set up to tell me when the close event fires will popup two or three views later… This is also true when using a swipe gesture to close the view in iOS 13. But most of the time, it doesn’t show up at all and the break points I set in the close event are never triggered.

If users can swipe to dismiss a view and we don’t get an event for it, I suspect this is going to lead to some issues. Will someone please create a Feedback report?

<https://xojo.com/issue/58675>

The deactivate event fires reliably on swipe (or when closing using a declare) so one can put closing code there.

The other thing that appears to happen in iOS 13 is that, because the modal view now appears in front of the calling view, the calling view does not appear to deactivate or close so, when you return to the calling view, the Activate event does not fire and you have to update the calling view manually with any changes that you made in the modal view.

So I think I figured out why the close event isn’t firing.

When you call the dismissViewController: selector on your modal view, it doesn’t actually remove it from the rootViewController, the objects are not destroyed and thus the destructor is not called (which means they leak). You’ll need to do that in the completion handler of the dismissViewController call.

To prevent the user from swiping to dismiss, you should be able to set the controller’s obj-c modalInPresentation property to true before showing the view, passing it the iOSView.ViewControllerHandle.

https://developer.apple.com/documentation/uikit/uiviewcontroller/3229894-modalinpresentation?language=objc

DON’T
The deactivate event will also be called if the user switches to another app, if a phone call arrives, if a notification is read and so on.

This would seem to mean that all Xojo iOS apps with modal views are leaking, not just those running under iOS 13. How would one do that when using the standard declare (i.e., what Jeremie cited above) to close the view?

[quote=468440:@Jeremie Leroy]DON’T
The deactivate event will also be called if the user switches to another app, if a phone call arrives, if a notification is read and so on.[/quote]

Yes, I understand the implications of using deactivate. However, the modal views are on-screen for a relatively short period of time while the user makes a selection so the chances of an external event deactivating it are reduced (though they are certainly still present). For my app, the consequences of an external interruption are minimal but of course other apps may be different.

If the user can now swipe down to dismiss a modal view, short of disabling the swipe to dismiss option that Greg just mentioned and which I’ve not yet tried, one has to deal with it somehow.

Still confusing to me is that, apparently, in iOS 13 showing a modal view does not deactivate the calling view so that code in the activate event of the calling view is not triggered when the modal view is dismissed.

Declare Sub removeFromParentViewController Lib "UIKit" selector "removeFromParentViewController" (obj As ptr) removeFromParentViewController(Self.Handle)

Not sure if this is also new but I did not want to open another thread for it. When I show a modal view with the declares from the example project, the code after that still runs. It doesn’t stop and waits until the modal view is closed. Is this a normal behavior?

It’s not a bug. Showing views on iOS is asynchronous. What’s modal is that the user can’t do anything else until they dismiss the view.

Didn’t want to open a separate thread for that so I’m asking here: I have a view (not a modal one) with two buttons on it. Button1 shows ModalView1, Button2 shows ModalView2.

This is what I do:

  1. I push Button1, ModalView1 is being shown.
  2. I close ModalView1 with another button on that view.
  3. I push Button2 from the non-modal view, ModalView2 won’t show. Nothing happens. Button1 still works.

If I close ModalView1 (step 2 above) by swiping down instead pushing the button on that view, step 3 works fine.

What’s the code to close ModalView1?

// Close the Modal view to return to the calling view
Declare Sub dismissViewController Lib "UIKit" _
Selector "dismissViewControllerAnimated:completion:" _
(parentView As Ptr, animated As Boolean, completion As Ptr)

dismissViewController(Self.Handle, True, Nil)