Skip to content

Commit c5df258

Browse files
authored
feat(modal-view-ios): handle iOS 13 dismiss modal gesture (#8024)
* feat(modal-view): introduce cancelable property on ShowModalOptions * fix(modal-view): handle iOS 13 modal dismiss gesture * chore: address PR comments
1 parent 60ac4e7 commit c5df258

3 files changed

Lines changed: 67 additions & 9 deletions

File tree

nativescript-core/ui/core/view-base/view-base.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,15 @@ export interface ShowModalOptions {
8383
}
8484
android?: {
8585
/**
86+
* @deprecated Use ShowModalOptions.cancelable instead.
8687
* An optional parameter specifying whether the modal view can be dismissed when not in full-screen mode.
8788
*/
8889
cancelable?: boolean
8990
}
91+
/**
92+
* An optional parameter specifying whether the modal view can be dismissed when not in full-screen mode.
93+
*/
94+
cancelable?: boolean
9095
}
9196

9297
export abstract class ViewBase extends Observable {

nativescript-core/ui/core/view/view.android.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -640,12 +640,21 @@ export class View extends ViewCommon {
640640
args.putInt(DOMID, this._domId);
641641
df.setArguments(args);
642642

643+
let cancelable = true;
644+
645+
if (options.android && (<any>options).android.cancelable !== undefined) {
646+
cancelable = !!(<any>options).android.cancelable;
647+
console.log("ShowModalOptions.android.cancelable is deprecated. Use ShowModalOptions.cancelable instead.");
648+
}
649+
650+
cancelable = options.cancelable !== undefined ? !!options.cancelable : cancelable;
651+
643652
const dialogOptions: DialogOptions = {
644653
owner: this,
645654
fullscreen: !!options.fullscreen,
646655
animated: !!options.animated,
647656
stretched: !!options.stretched,
648-
cancelable: options.android ? !!options.android.cancelable : true,
657+
cancelable: cancelable,
649658
shownCallback: () => this._raiseShownModallyEvent(),
650659
dismissCallback: () => this.closeModal()
651660
};

nativescript-core/ui/core/view/view.ios.ts

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export class View extends ViewCommon {
3030
nativeViewProtected: UIView;
3131
viewController: UIViewController;
3232
private _popoverPresentationDelegate: ios.UIPopoverPresentationControllerDelegateImp;
33+
private _adaptivePresentationDelegate: ios.UIAdaptivePresentationControllerDelegateImp;
3334

3435
private _isLaidOut = false;
3536
private _hasTransfrom = false;
@@ -422,14 +423,19 @@ export class View extends ViewCommon {
422423
controller.modalPresentationStyle = presentationStyle;
423424

424425
if (presentationStyle === UIModalPresentationStyle.Popover) {
425-
const popoverPresentationController = controller.popoverPresentationController;
426-
this._popoverPresentationDelegate = ios.UIPopoverPresentationControllerDelegateImp.initWithOwnerAndCallback(new WeakRef(this), this._closeModalCallback);
427-
popoverPresentationController.delegate = this._popoverPresentationDelegate;
428-
const view = parent.nativeViewProtected;
429-
// Note: sourceView and sourceRect are needed to specify the anchor location for the popover.
430-
// Note: sourceView should be the button triggering the modal. If it the Page the popover might appear "behind" the page content
431-
popoverPresentationController.sourceView = view;
432-
popoverPresentationController.sourceRect = CGRectMake(0, 0, view.frame.size.width, view.frame.size.height);
426+
this._setupPopoverControllerDelegate(controller, parent);
427+
}
428+
}
429+
430+
const cancelable = options.cancelable !== undefined ? !!options.cancelable : true;
431+
432+
if (majorVersion >= 13) {
433+
if (cancelable) {
434+
// Listen for dismiss modal callback.
435+
this._setupAdaptiveControllerDelegate(controller);
436+
} else {
437+
// Prevent users from dismissing the modal.
438+
(<any>controller).modalInPresentation = true;
433439
}
434440
}
435441

@@ -644,6 +650,22 @@ export class View extends ViewCommon {
644650
backgroundInternal.hasBorderWidth() ||
645651
backgroundInternal.hasBorderRadius();
646652
}
653+
654+
private _setupPopoverControllerDelegate(controller: UIViewController, parent: View) {
655+
const popoverPresentationController = controller.popoverPresentationController;
656+
this._popoverPresentationDelegate = ios.UIPopoverPresentationControllerDelegateImp.initWithOwnerAndCallback(new WeakRef(this), this._closeModalCallback);
657+
popoverPresentationController.delegate = this._popoverPresentationDelegate;
658+
const view = parent.nativeViewProtected;
659+
// Note: sourceView and sourceRect are needed to specify the anchor location for the popover.
660+
// Note: sourceView should be the button triggering the modal. If it the Page the popover might appear "behind" the page content
661+
popoverPresentationController.sourceView = view;
662+
popoverPresentationController.sourceRect = CGRectMake(0, 0, view.frame.size.width, view.frame.size.height);
663+
}
664+
665+
private _setupAdaptiveControllerDelegate(controller: UIViewController) {
666+
this._adaptivePresentationDelegate = ios.UIAdaptivePresentationControllerDelegateImp.initWithOwnerAndCallback(new WeakRef(this), this._closeModalCallback);
667+
controller.presentationController.delegate = this._adaptivePresentationDelegate;
668+
}
647669
}
648670
View.prototype._nativeBackgroundState = "unset";
649671

@@ -1019,6 +1041,28 @@ export namespace ios {
10191041
}
10201042
}
10211043

1044+
export class UIAdaptivePresentationControllerDelegateImp extends NSObject implements UIAdaptivePresentationControllerDelegate {
1045+
public static ObjCProtocols = [UIAdaptivePresentationControllerDelegate];
1046+
1047+
private owner: WeakRef<View>;
1048+
private closedCallback: Function;
1049+
1050+
public static initWithOwnerAndCallback(owner: WeakRef<View>, whenClosedCallback: Function): UIAdaptivePresentationControllerDelegateImp {
1051+
const instance = <UIAdaptivePresentationControllerDelegateImp>super.new();
1052+
instance.owner = owner;
1053+
instance.closedCallback = whenClosedCallback;
1054+
1055+
return instance;
1056+
}
1057+
1058+
public presentationControllerDidDismiss(presentationController: UIPresentationController) {
1059+
const owner = this.owner.get();
1060+
if (owner && typeof this.closedCallback === "function") {
1061+
this.closedCallback();
1062+
}
1063+
}
1064+
}
1065+
10221066
export class UIPopoverPresentationControllerDelegateImp extends NSObject implements UIPopoverPresentationControllerDelegate {
10231067
public static ObjCProtocols = [UIPopoverPresentationControllerDelegate];
10241068

0 commit comments

Comments
 (0)