feat(ios): page transition handling with more flexibility

This commit is contained in:
Nathan Walker
2022-09-06 06:56:16 -07:00
parent 671603ab4b
commit 81d977e7c8
13 changed files with 33 additions and 61 deletions

View File

@@ -1,4 +1,5 @@
import type { Page } from '../ui/page';
import type { ViewBase } from '../ui/core/view-base';
import type { View } from '../ui/core/view';
import type { AndroidAccessibilityEvent } from './accessibility-types';

View File

@@ -1,4 +1,5 @@
import * as Application from '../application';
import type { ViewBase } from '../ui/core/view-base';
import type { View } from '../ui/core/view';
import { notifyAccessibilityFocusState } from './accessibility-common';
import { AccessibilityLiveRegion, AccessibilityRole, AccessibilityState, AccessibilityTrait } from './accessibility-types';
@@ -120,7 +121,7 @@ function ensureNativeClasses() {
});
}
export function setupAccessibleView(view: View): void {
export function setupAccessibleView(view: Partial<ViewBase>): void {
const uiView = view.nativeViewProtected as UIView;
if (!uiView) {
return;

View File

@@ -1,10 +1,8 @@
// Definitions.
import { AlignSelf, FlexGrow, FlexShrink, FlexWrapBefore, Order } from '../../layouts/flexbox-layout';
import { Page } from '../../page';
// Types.
import { CoreTypes } from '../../../core-types';
import { Property, CssProperty, CssAnimationProperty, InheritedProperty, clearInheritedProperties, propagateInheritableProperties, propagateInheritableCssProperties, initNativeView } from '../properties';
import { setupAccessibleView } from '../../../accessibility';
import { CSSUtils } from '../../../css/system-classes';
import { Source } from '../../../utils/debug';
import { Binding, BindingOptions } from '../bindable';
@@ -442,6 +440,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
return true;
});
setupAccessibleView(this);
this._emit('loaded');
}

View File

@@ -21,7 +21,7 @@ import { AndroidActivityBackPressedEventData, android as androidApp } from '../.
import { Device } from '../../../platform';
import lazy from '../../../utils/lazy';
import { accessibilityEnabledProperty, accessibilityHiddenProperty, accessibilityHintProperty, accessibilityIdentifierProperty, accessibilityLabelProperty, accessibilityLanguageProperty, accessibilityLiveRegionProperty, accessibilityMediaSessionProperty, accessibilityRoleProperty, accessibilityStateProperty, accessibilityValueProperty } from '../../../accessibility/accessibility-properties';
import { AccessibilityLiveRegion, AccessibilityRole, AndroidAccessibilityEvent, setupAccessibleView, isAccessibilityServiceEnabled, sendAccessibilityEvent, updateAccessibilityProperties, updateContentDescription, AccessibilityState } from '../../../accessibility';
import { AccessibilityLiveRegion, AccessibilityRole, AndroidAccessibilityEvent, isAccessibilityServiceEnabled, sendAccessibilityEvent, updateAccessibilityProperties, updateContentDescription, AccessibilityState } from '../../../accessibility';
import * as Utils from '../../../utils';
import { SDK_VERSION } from '../../../utils/constants';
import { CSSShadow } from '../../styling/css-shadow';
@@ -320,20 +320,6 @@ export class View extends ViewCommon {
nativeViewProtected: android.view.View;
constructor() {
super();
const weakRef = new WeakRef(this);
const handler = () => {
const owner = weakRef.get();
if (owner) {
setupAccessibleView(owner);
owner.off(View.loadedEvent, handler);
}
};
this.on(View.loadedEvent, handler);
}
// TODO: Implement unobserve that detach the touchListener.
_observe(type: GestureTypes, callback: (args: GestureEventData) => void, thisArg?: any): void {
super._observe(type, callback, thisArg);

View File

@@ -11,7 +11,7 @@ import { ios as iosBackground, Background } from '../../styling/background';
import { perspectiveProperty, visibilityProperty, opacityProperty, rotateProperty, rotateXProperty, rotateYProperty, scaleXProperty, scaleYProperty, translateXProperty, translateYProperty, zIndexProperty, backgroundInternalProperty, clipPathProperty } from '../../styling/style-properties';
import { profile } from '../../../profiling';
import { accessibilityEnabledProperty, accessibilityHiddenProperty, accessibilityHintProperty, accessibilityIdentifierProperty, accessibilityLabelProperty, accessibilityLanguageProperty, accessibilityLiveRegionProperty, accessibilityMediaSessionProperty, accessibilityRoleProperty, accessibilityStateProperty, accessibilityValueProperty, accessibilityIgnoresInvertColorsProperty } from '../../../accessibility/accessibility-properties';
import { setupAccessibleView, IOSPostAccessibilityNotificationType, isAccessibilityServiceEnabled, updateAccessibilityProperties, AccessibilityEventOptions, AccessibilityRole, AccessibilityState } from '../../../accessibility';
import { IOSPostAccessibilityNotificationType, isAccessibilityServiceEnabled, updateAccessibilityProperties, AccessibilityEventOptions, AccessibilityRole, AccessibilityState } from '../../../accessibility';
import { CoreTypes } from '../../../core-types';
export * from './view-common';
@@ -58,12 +58,6 @@ export class View extends ViewCommon implements ViewDefinition {
return (this._privateFlags & PFLAG_FORCE_LAYOUT) === PFLAG_FORCE_LAYOUT;
}
constructor() {
super();
this.once(View.loadedEvent, () => setupAccessibleView(this));
}
disposeNativeView() {
super.disposeNativeView();

View File

@@ -5,18 +5,6 @@ import { FadeTransition } from '../transition/fade-transition';
import { Trace } from '../../trace';
namespace UIViewControllerAnimatedTransitioningMethods {
const methodSignature = NSMethodSignature.signatureWithObjCTypes('v@:c');
const invocation = NSInvocation.invocationWithMethodSignature(methodSignature);
invocation.selector = 'completeTransition:';
export function completeTransition(didComplete: boolean) {
const didCompleteReference = new interop.Reference(interop.types.bool, didComplete);
invocation.setArgumentAtIndex(didCompleteReference, 2);
invocation.invokeWithTarget(this);
}
}
@NativeClass
class AnimatedTransitioning extends NSObject implements UIViewControllerAnimatedTransitioning {
public static ObjCProtocols = [UIViewControllerAnimatedTransitioning];
@@ -37,9 +25,7 @@ class AnimatedTransitioning extends NSObject implements UIViewControllerAnimated
return impl;
}
public animateTransition(transitionContext: any): void {
const containerView = transitionContext.valueForKey('containerView');
const completion = UIViewControllerAnimatedTransitioningMethods.completeTransition.bind(transitionContext);
public animateTransition(transitionContext: UIViewControllerContextTransitioning): void {
switch (this._operation) {
case UINavigationControllerOperation.Push:
this._transitionType = 'push';
@@ -55,7 +41,7 @@ class AnimatedTransitioning extends NSObject implements UIViewControllerAnimated
if (Trace.isEnabled()) {
Trace.write(`START ${this._transition} ${this._transitionType}`, Trace.categories.Transition);
}
this._transition.animateIOSTransition(containerView, this._fromVC.view, this._toVC.view, this._operation, completion);
this._transition.animateIOSTransition(transitionContext, this._fromVC, this._toVC, this._operation);
}
public transitionDuration(transitionContext: UIViewControllerContextTransitioning): number {
@@ -85,7 +71,7 @@ export function _createIOSAnimatedTransitioning(navigationTransition: Navigation
} else if (navigationTransition.name) {
const name = navigationTransition.name.toLowerCase();
if (name.indexOf('slide') === 0) {
const direction = name.substr('slide'.length) || 'left'; //Extract the direction from the string
const direction = name.substring('slide'.length) || 'left'; //Extract the direction from the string
transition = new SlideTransition(direction, navigationTransition.duration, nativeCurve);
} else if (name === 'fade') {
transition = new FadeTransition(navigationTransition.duration, nativeCurve);

View File

@@ -170,17 +170,18 @@ export class ScrollView extends ScrollViewBase {
}
public onLayout(left: number, top: number, right: number, bottom: number): void {
if (!this.nativeViewProtected) {
return;
}
const insets = this.getSafeAreaInsets();
let width = right - left - insets.right - insets.left;
let height = bottom - top - insets.bottom - insets.top;
const nativeView = this.nativeViewProtected;
if (majorVersion > 10) {
// Disable automatic adjustment of scroll view insets
// Consider exposing this as property with all 4 modes
// https://developer.apple.com/documentation/uikit/uiscrollview/contentinsetadjustmentbehavior
nativeView.contentInsetAdjustmentBehavior = 2;
this.nativeViewProtected.contentInsetAdjustmentBehavior = 2;
}
let scrollWidth = width + insets.left + insets.right;
@@ -193,7 +194,7 @@ export class ScrollView extends ScrollViewBase {
height = Math.max(this._contentMeasuredHeight, height);
}
nativeView.contentSize = CGSizeMake(layout.toDeviceIndependentPixels(scrollWidth), layout.toDeviceIndependentPixels(scrollHeight));
this.nativeViewProtected.contentSize = CGSizeMake(layout.toDeviceIndependentPixels(scrollWidth), layout.toDeviceIndependentPixels(scrollHeight));
View.layoutChild(this, this.layoutView, insets.left, insets.top, insets.left + width, insets.top + height);
}

View File

@@ -1,8 +1,10 @@
import { Transition } from '.';
export class FadeTransition extends Transition {
public animateIOSTransition(containerView: UIView, fromView: UIView, toView: UIView, operation: UINavigationControllerOperation, completion: (finished: boolean) => void): void {
public animateIOSTransition(transitionContext: UIViewControllerContextTransitioning, fromViewCtrl: UIViewController, toViewCtrl: UIViewController, operation: UINavigationControllerOperation): void {
const toView = toViewCtrl.view;
const originalToViewAlpha = toView.alpha;
const fromView = fromViewCtrl.view;
const originalFromViewAlpha = fromView.alpha;
toView.alpha = 0.0;
@@ -10,10 +12,10 @@ export class FadeTransition extends Transition {
switch (operation) {
case UINavigationControllerOperation.Push:
containerView.insertSubviewAboveSubview(toView, fromView);
transitionContext.containerView.insertSubviewAboveSubview(toView, fromView);
break;
case UINavigationControllerOperation.Pop:
containerView.insertSubviewBelowSubview(toView, fromView);
transitionContext.containerView.insertSubviewBelowSubview(toView, fromView);
break;
}
@@ -29,7 +31,7 @@ export class FadeTransition extends Transition {
(finished: boolean) => {
toView.alpha = originalToViewAlpha;
fromView.alpha = originalFromViewAlpha;
completion(finished);
transitionContext.completeTransition(finished);
}
);
}

View File

@@ -16,7 +16,7 @@ export class Transition {
private _interpolator: android.view.animation.Interpolator;
private _id: number;
constructor(duration: number, curve: any) {
constructor(duration: number, curve?: any) {
this._duration = duration;
this._interpolator = curve ? _resolveAnimationCurve(curve) : _defaultInterpolator();
this._id = transitionId++;
@@ -30,7 +30,7 @@ export class Transition {
return this._interpolator;
}
public animateIOSTransition(containerView: any, fromView: any, toView: any, operation: any, completion: (finished: boolean) => void): void {
public animateIOSTransition(transitionContext: any, fromViewCtrl: any, toViewCtrl: any, operation: any): void {
throw new Error('Abstract method call');
}

View File

@@ -1,9 +1,9 @@
export class Transition {
static AndroidTransitionType: { enter: string; exit: string; popEnter: string; popExit: string };
constructor(duration: number, nativeCurve: any);
constructor(duration: number, nativeCurve?: any /* UIViewAnimationCurve | string | CubicBezierAnimationCurve | android.view.animation.Interpolator | android.view.animation.LinearInterpolator */);
public getDuration(): number;
public getCurve(): any;
public animateIOSTransition(containerView: any, fromView: any, toView: any, operation: any, completion: (finished: boolean) => void): void;
public animateIOSTransition(transitionContext: any /*UIViewControllerContextTransitioning */, fromViewCtrl: any /* UIViewController */, toViewCtrl: any /* UIViewController */, operation: any /* UINavigationControllerOperation */): void;
public createAndroidAnimator(transitionType: string): any;
public toString(): string;
}

View File

@@ -19,7 +19,7 @@ export class Transition {
return this._curve;
}
public animateIOSTransition(containerView: UIView, fromView: UIView, toView: UIView, operation: UINavigationControllerOperation, completion: (finished: boolean) => void): void {
public animateIOSTransition(transitionContext: UIViewControllerContextTransitioning, fromViewCtrl: UIViewController, toViewCtrl: UIViewController, operation: UINavigationControllerOperation): void {
throw new Error('Abstract method call');
}

View File

@@ -14,8 +14,10 @@ export class SlideTransition extends Transition {
this._direction = direction;
}
public animateIOSTransition(containerView: UIView, fromView: UIView, toView: UIView, operation: UINavigationControllerOperation, completion: (finished: boolean) => void): void {
public animateIOSTransition(transitionContext: UIViewControllerContextTransitioning, fromViewCtrl: UIViewController, toViewCtrl: UIViewController, operation: UINavigationControllerOperation): void {
const toView = toViewCtrl.view;
const originalToViewTransform = toView.transform;
const fromView = fromViewCtrl.view;
const originalFromViewTransform = fromView.transform;
let fromViewEndTransform: CGAffineTransform;
@@ -46,10 +48,10 @@ export class SlideTransition extends Transition {
switch (operation) {
case UINavigationControllerOperation.Push:
containerView.insertSubviewAboveSubview(toView, fromView);
transitionContext.containerView.insertSubviewAboveSubview(toView, fromView);
break;
case UINavigationControllerOperation.Pop:
containerView.insertSubviewBelowSubview(toView, fromView);
transitionContext.containerView.insertSubviewBelowSubview(toView, fromView);
break;
}
@@ -65,7 +67,7 @@ export class SlideTransition extends Transition {
(finished: boolean) => {
toView.transform = originalToViewTransform;
fromView.transform = originalFromViewTransform;
completion(finished);
transitionContext.completeTransition(finished);
}
);
}

View File

@@ -2606,7 +2606,7 @@ export interface TraceWriter {
export class Transition {
constructor(duration: number, nativeCurve: any);
// (undocumented)
public animateIOSTransition(containerView: any, fromView: any, toView: any, operation: any, completion: (finished: boolean) => void): void;
public animateIOSTransition(transitionContext: UIViewControllerContextTransitioning, fromViewCtrl: UIViewController, toViewCtrl: UIViewController, operation: UINavigationControllerOperation): void;
// (undocumented)
public createAndroidAnimator(transitionType: string): any;
// (undocumented)