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
This commit is contained in:
Alexander Djenkov
2019-11-20 10:00:54 +02:00
committed by GitHub
parent 60ac4e7a05
commit c5df258043
3 changed files with 67 additions and 9 deletions

View File

@ -83,10 +83,15 @@ export interface ShowModalOptions {
} }
android?: { android?: {
/** /**
* @deprecated Use ShowModalOptions.cancelable instead.
* An optional parameter specifying whether the modal view can be dismissed when not in full-screen mode. * An optional parameter specifying whether the modal view can be dismissed when not in full-screen mode.
*/ */
cancelable?: boolean cancelable?: boolean
} }
/**
* An optional parameter specifying whether the modal view can be dismissed when not in full-screen mode.
*/
cancelable?: boolean
} }
export abstract class ViewBase extends Observable { export abstract class ViewBase extends Observable {

View File

@ -640,12 +640,21 @@ export class View extends ViewCommon {
args.putInt(DOMID, this._domId); args.putInt(DOMID, this._domId);
df.setArguments(args); df.setArguments(args);
let cancelable = true;
if (options.android && (<any>options).android.cancelable !== undefined) {
cancelable = !!(<any>options).android.cancelable;
console.log("ShowModalOptions.android.cancelable is deprecated. Use ShowModalOptions.cancelable instead.");
}
cancelable = options.cancelable !== undefined ? !!options.cancelable : cancelable;
const dialogOptions: DialogOptions = { const dialogOptions: DialogOptions = {
owner: this, owner: this,
fullscreen: !!options.fullscreen, fullscreen: !!options.fullscreen,
animated: !!options.animated, animated: !!options.animated,
stretched: !!options.stretched, stretched: !!options.stretched,
cancelable: options.android ? !!options.android.cancelable : true, cancelable: cancelable,
shownCallback: () => this._raiseShownModallyEvent(), shownCallback: () => this._raiseShownModallyEvent(),
dismissCallback: () => this.closeModal() dismissCallback: () => this.closeModal()
}; };

View File

@ -30,6 +30,7 @@ export class View extends ViewCommon {
nativeViewProtected: UIView; nativeViewProtected: UIView;
viewController: UIViewController; viewController: UIViewController;
private _popoverPresentationDelegate: ios.UIPopoverPresentationControllerDelegateImp; private _popoverPresentationDelegate: ios.UIPopoverPresentationControllerDelegateImp;
private _adaptivePresentationDelegate: ios.UIAdaptivePresentationControllerDelegateImp;
private _isLaidOut = false; private _isLaidOut = false;
private _hasTransfrom = false; private _hasTransfrom = false;
@ -422,14 +423,19 @@ export class View extends ViewCommon {
controller.modalPresentationStyle = presentationStyle; controller.modalPresentationStyle = presentationStyle;
if (presentationStyle === UIModalPresentationStyle.Popover) { if (presentationStyle === UIModalPresentationStyle.Popover) {
const popoverPresentationController = controller.popoverPresentationController; this._setupPopoverControllerDelegate(controller, parent);
this._popoverPresentationDelegate = ios.UIPopoverPresentationControllerDelegateImp.initWithOwnerAndCallback(new WeakRef(this), this._closeModalCallback); }
popoverPresentationController.delegate = this._popoverPresentationDelegate; }
const view = parent.nativeViewProtected;
// Note: sourceView and sourceRect are needed to specify the anchor location for the popover. const cancelable = options.cancelable !== undefined ? !!options.cancelable : true;
// Note: sourceView should be the button triggering the modal. If it the Page the popover might appear "behind" the page content
popoverPresentationController.sourceView = view; if (majorVersion >= 13) {
popoverPresentationController.sourceRect = CGRectMake(0, 0, view.frame.size.width, view.frame.size.height); if (cancelable) {
// Listen for dismiss modal callback.
this._setupAdaptiveControllerDelegate(controller);
} else {
// Prevent users from dismissing the modal.
(<any>controller).modalInPresentation = true;
} }
} }
@ -644,6 +650,22 @@ export class View extends ViewCommon {
backgroundInternal.hasBorderWidth() || backgroundInternal.hasBorderWidth() ||
backgroundInternal.hasBorderRadius(); backgroundInternal.hasBorderRadius();
} }
private _setupPopoverControllerDelegate(controller: UIViewController, parent: View) {
const popoverPresentationController = controller.popoverPresentationController;
this._popoverPresentationDelegate = ios.UIPopoverPresentationControllerDelegateImp.initWithOwnerAndCallback(new WeakRef(this), this._closeModalCallback);
popoverPresentationController.delegate = this._popoverPresentationDelegate;
const view = parent.nativeViewProtected;
// Note: sourceView and sourceRect are needed to specify the anchor location for the popover.
// Note: sourceView should be the button triggering the modal. If it the Page the popover might appear "behind" the page content
popoverPresentationController.sourceView = view;
popoverPresentationController.sourceRect = CGRectMake(0, 0, view.frame.size.width, view.frame.size.height);
}
private _setupAdaptiveControllerDelegate(controller: UIViewController) {
this._adaptivePresentationDelegate = ios.UIAdaptivePresentationControllerDelegateImp.initWithOwnerAndCallback(new WeakRef(this), this._closeModalCallback);
controller.presentationController.delegate = this._adaptivePresentationDelegate;
}
} }
View.prototype._nativeBackgroundState = "unset"; View.prototype._nativeBackgroundState = "unset";
@ -1019,6 +1041,28 @@ export namespace ios {
} }
} }
export class UIAdaptivePresentationControllerDelegateImp extends NSObject implements UIAdaptivePresentationControllerDelegate {
public static ObjCProtocols = [UIAdaptivePresentationControllerDelegate];
private owner: WeakRef<View>;
private closedCallback: Function;
public static initWithOwnerAndCallback(owner: WeakRef<View>, whenClosedCallback: Function): UIAdaptivePresentationControllerDelegateImp {
const instance = <UIAdaptivePresentationControllerDelegateImp>super.new();
instance.owner = owner;
instance.closedCallback = whenClosedCallback;
return instance;
}
public presentationControllerDidDismiss(presentationController: UIPresentationController) {
const owner = this.owner.get();
if (owner && typeof this.closedCallback === "function") {
this.closedCallback();
}
}
}
export class UIPopoverPresentationControllerDelegateImp extends NSObject implements UIPopoverPresentationControllerDelegate { export class UIPopoverPresentationControllerDelegateImp extends NSObject implements UIPopoverPresentationControllerDelegate {
public static ObjCProtocols = [UIPopoverPresentationControllerDelegate]; public static ObjCProtocols = [UIPopoverPresentationControllerDelegate];