@nativescript/core
Version:
A JavaScript library providing an easy to use api for interacting with iOS and Android platform APIs.
934 lines • 42.6 kB
JavaScript
// Requires
import { ViewCommon, isEnabledProperty, originXProperty, originYProperty, isUserInteractionEnabledProperty, testIDProperty } from './view-common';
import { hiddenProperty } from '../view-base';
import { Trace } from '../../../trace';
import { layout, iOSNativeHelper } from '../../../utils';
import { IOSHelper } from './view-helper';
import { ios as iosBackground } from '../../styling/background';
import { perspectiveProperty, visibilityProperty, opacityProperty, rotateProperty, rotateXProperty, rotateYProperty, scaleXProperty, scaleYProperty, translateXProperty, translateYProperty, zIndexProperty, backgroundInternalProperty } 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 { IOSPostAccessibilityNotificationType, isAccessibilityServiceEnabled, updateAccessibilityProperties } from '../../../accessibility';
import { CoreTypes } from '../../../core-types';
import { SharedTransition } from '../../transition/shared-transition';
export * from './view-common';
// helpers (these are okay re-exported here)
export * from './view-helper';
// This one can eventually be cleaned up but causes issues with a lot of ui-suite plugins in particular if not exported here
export * from '../properties';
const PFLAG_FORCE_LAYOUT = 1;
const PFLAG_MEASURED_DIMENSION_SET = 1 << 1;
const PFLAG_LAYOUT_REQUIRED = 1 << 2;
const majorVersion = iOSNativeHelper.MajorVersion;
export class View extends ViewCommon {
constructor() {
super(...arguments);
this._isLaidOut = false;
this._hasTransform = false;
this._hasPendingTransform = false;
this._privateFlags = PFLAG_LAYOUT_REQUIRED | PFLAG_FORCE_LAYOUT;
this._suspendCATransaction = false;
}
get isLayoutRequired() {
return (this._privateFlags & PFLAG_LAYOUT_REQUIRED) === PFLAG_LAYOUT_REQUIRED;
}
get isLayoutRequested() {
return (this._privateFlags & PFLAG_FORCE_LAYOUT) === PFLAG_FORCE_LAYOUT;
}
disposeNativeView() {
super.disposeNativeView();
this._cachedFrame = null;
this._isLaidOut = false;
this._hasTransform = false;
this._hasPendingTransform = false;
}
requestLayout() {
this._privateFlags |= PFLAG_FORCE_LAYOUT;
super.requestLayout();
const nativeView = this.nativeViewProtected;
if (nativeView && nativeView.setNeedsLayout) {
nativeView.setNeedsLayout();
}
if (this.viewController && this.viewController.view !== nativeView) {
this.viewController.view.setNeedsLayout();
}
}
measure(widthMeasureSpec, heightMeasureSpec) {
const measureSpecsChanged = this._setCurrentMeasureSpecs(widthMeasureSpec, heightMeasureSpec);
const forceLayout = (this._privateFlags & PFLAG_FORCE_LAYOUT) === PFLAG_FORCE_LAYOUT;
if (this.nativeViewProtected && (forceLayout || measureSpecsChanged)) {
// first clears the measured dimension flag
this._privateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
// measure ourselves, this should set the measured dimension flag back
this.onMeasure(widthMeasureSpec, heightMeasureSpec);
this._privateFlags |= PFLAG_LAYOUT_REQUIRED;
// flag not set, setMeasuredDimension() was not invoked, we trace
// the exception to warn the developer
if ((this._privateFlags & PFLAG_MEASURED_DIMENSION_SET) !== PFLAG_MEASURED_DIMENSION_SET) {
if (Trace.isEnabled()) {
Trace.write('onMeasure() did not set the measured dimension by calling setMeasuredDimension() ' + this, Trace.categories.Layout, Trace.messageType.error);
}
}
}
}
layout(left, top, right, bottom, setFrame = true) {
const { boundsChanged, sizeChanged } = this._setCurrentLayoutBounds(left, top, right, bottom);
if (setFrame) {
this.layoutNativeView(left, top, right, bottom);
}
const needsLayout = boundsChanged || (this._privateFlags & PFLAG_LAYOUT_REQUIRED) === PFLAG_LAYOUT_REQUIRED;
if (needsLayout) {
let position = { left, top, right, bottom };
if (this.nativeViewProtected && majorVersion > 10) {
// on iOS 11+ it is possible to have a changed layout frame due to safe area insets
// get the frame and adjust the position, so that onLayout works correctly
const frame = this.nativeViewProtected.frame;
position = IOSHelper.getPositionFromFrame(frame);
}
this.onLayout(position.left, position.top, position.right, position.bottom);
this._privateFlags &= ~PFLAG_LAYOUT_REQUIRED;
}
this.updateBackground(sizeChanged, needsLayout);
if (this._hasPendingTransform) {
this.updateNativeTransform();
this._hasPendingTransform = false;
}
this._privateFlags &= ~PFLAG_FORCE_LAYOUT;
}
updateBackground(sizeChanged, needsLayout) {
if (sizeChanged) {
this._onSizeChanged();
}
else if (this._nativeBackgroundState === 'invalid') {
const background = this.style.backgroundInternal;
this._redrawNativeBackground(background);
}
else {
// Update layers that don't belong to view's layer (e.g. shadow layers)
if (needsLayout) {
this.layoutOuterShadows();
}
}
}
layoutOuterShadows() {
const nativeView = this.nativeViewProtected;
if (nativeView?.outerShadowContainerLayer) {
CATransaction.setDisableActions(true);
nativeView.outerShadowContainerLayer.bounds = nativeView.bounds;
nativeView.outerShadowContainerLayer.position = nativeView.center;
CATransaction.setDisableActions(false);
}
}
setMeasuredDimension(measuredWidth, measuredHeight) {
super.setMeasuredDimension(measuredWidth, measuredHeight);
this._privateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
onMeasure(widthMeasureSpec, heightMeasureSpec) {
const view = this.nativeViewProtected;
const width = layout.getMeasureSpecSize(widthMeasureSpec);
const widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
const height = layout.getMeasureSpecSize(heightMeasureSpec);
const heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
let nativeWidth = 0;
let nativeHeight = 0;
if (view) {
const nativeSize = layout.measureNativeView(view, width, widthMode, height, heightMode);
nativeWidth = nativeSize.width;
nativeHeight = nativeSize.height;
}
const measureWidth = Math.max(nativeWidth, this.effectiveMinWidth);
const measureHeight = Math.max(nativeHeight, this.effectiveMinHeight);
const widthAndState = View.resolveSizeAndState(measureWidth, width, widthMode, 0);
const heightAndState = View.resolveSizeAndState(measureHeight, height, heightMode, 0);
this.setMeasuredDimension(widthAndState, heightAndState);
}
onLayout(left, top, right, bottom) {
//
}
_setNativeViewFrame(nativeView, frame) {
const oldFrame = this._cachedFrame || nativeView.frame;
if (!CGRectEqualToRect(oldFrame, frame)) {
if (Trace.isEnabled()) {
Trace.write(this + ' :_setNativeViewFrame: ' + JSON.stringify(IOSHelper.getPositionFromFrame(frame)), Trace.categories.Layout);
}
this._cachedFrame = frame;
let adjustedFrame = null;
let transform = null;
if (this._hasTransform) {
// Always set identity transform before setting frame;
transform = nativeView.layer.transform;
nativeView.layer.transform = CATransform3DIdentity;
nativeView.frame = frame;
}
else {
nativeView.frame = frame;
}
adjustedFrame = this.applySafeAreaInsets(frame);
if (adjustedFrame) {
nativeView.frame = adjustedFrame;
}
if (this._hasTransform) {
// re-apply the transform after the frame is adjusted
nativeView.layer.transform = transform;
}
const boundsOrigin = nativeView.bounds.origin;
const boundsFrame = adjustedFrame || frame;
nativeView.bounds = CGRectMake(boundsOrigin.x, boundsOrigin.y, boundsFrame.size.width, boundsFrame.size.height);
nativeView.layoutIfNeeded();
this._raiseLayoutChangedEvent();
this._isLaidOut = true;
}
else if (!this._isLaidOut) {
// Rects could be equal on the first layout and an event should be raised.
this._raiseLayoutChangedEvent();
// But make sure event is raised only once if rects are equal on the first layout as
// this method is called twice with equal rects in landscape mode (vs only once in portrait)
this._isLaidOut = true;
}
}
get isLayoutValid() {
if (this.nativeViewProtected) {
return this._isLayoutValid;
}
return false;
}
layoutNativeView(left, top, right, bottom) {
if (!this.nativeViewProtected) {
return;
}
const nativeView = this.nativeViewProtected;
const frame = IOSHelper.getFrameFromPosition({
left,
top,
right,
bottom,
});
this._setNativeViewFrame(nativeView, frame);
}
_layoutParent() {
if (this.nativeViewProtected) {
const frame = this.nativeViewProtected.frame;
const origin = frame.origin;
const size = frame.size;
const left = layout.toDevicePixels(origin.x);
const top = layout.toDevicePixels(origin.y);
const width = layout.toDevicePixels(size.width);
const height = layout.toDevicePixels(size.height);
this._setLayoutFlags(left, top, width + left, height + top);
}
super._layoutParent();
}
_setLayoutFlags(left, top, right, bottom) {
const width = right - left;
const height = bottom - top;
const widthSpec = layout.makeMeasureSpec(width, layout.EXACTLY);
const heightSpec = layout.makeMeasureSpec(height, layout.EXACTLY);
this._setCurrentMeasureSpecs(widthSpec, heightSpec);
this._privateFlags &= ~PFLAG_FORCE_LAYOUT;
this.setMeasuredDimension(width, height);
const { boundsChanged, sizeChanged } = this._setCurrentLayoutBounds(left, top, right, bottom);
this.updateBackground(sizeChanged, boundsChanged);
this._privateFlags &= ~PFLAG_LAYOUT_REQUIRED;
}
focus() {
if (this.ios) {
return this.ios.becomeFirstResponder();
}
return false;
}
applySafeAreaInsets(frame) {
if (majorVersion <= 10) {
return null;
}
if (this.iosIgnoreSafeArea) {
return frame;
}
if (!this.iosOverflowSafeArea || !this.iosOverflowSafeAreaEnabled) {
return IOSHelper.shrinkToSafeArea(this, frame);
}
else if (this.nativeViewProtected && this.nativeViewProtected.window) {
return IOSHelper.expandBeyondSafeArea(this, frame);
}
return null;
}
getSafeAreaInsets() {
const safeAreaInsets = this.nativeViewProtected && this.nativeViewProtected.safeAreaInsets;
const insets = { left: 0, top: 0, right: 0, bottom: 0 };
if (this.iosIgnoreSafeArea) {
return insets;
}
if (safeAreaInsets) {
insets.left = layout.round(layout.toDevicePixels(safeAreaInsets.left));
insets.top = layout.round(layout.toDevicePixels(safeAreaInsets.top));
insets.right = layout.round(layout.toDevicePixels(safeAreaInsets.right));
insets.bottom = layout.round(layout.toDevicePixels(safeAreaInsets.bottom));
}
return insets;
}
getLocationInWindow() {
if (!this.nativeViewProtected || !this.nativeViewProtected.window) {
return undefined;
}
const pointInWindow = this.nativeViewProtected.convertPointToView(this.nativeViewProtected.bounds.origin, null);
return {
x: pointInWindow.x,
y: pointInWindow.y,
};
}
getLocationOnScreen() {
if (!this.nativeViewProtected || !this.nativeViewProtected.window) {
return undefined;
}
const pointInWindow = this.nativeViewProtected.convertPointToView(this.nativeViewProtected.bounds.origin, null);
const pointOnScreen = this.nativeViewProtected.window.convertPointToWindow(pointInWindow, null);
return {
x: pointOnScreen.x,
y: pointOnScreen.y,
};
}
getLocationRelativeTo(otherView) {
if (!this.nativeViewProtected || !this.nativeViewProtected.window || !otherView.nativeViewProtected || !otherView.nativeViewProtected.window || this.nativeViewProtected.window !== otherView.nativeViewProtected.window) {
return undefined;
}
const myPointInWindow = this.nativeViewProtected.convertPointToView(this.nativeViewProtected.bounds.origin, null);
const otherPointInWindow = otherView.nativeViewProtected.convertPointToView(otherView.nativeViewProtected.bounds.origin, null);
return {
x: myPointInWindow.x - otherPointInWindow.x,
y: myPointInWindow.y - otherPointInWindow.y,
};
}
_onSizeChanged() {
const nativeView = this.nativeViewProtected;
if (!nativeView) {
return;
}
const background = this.style.backgroundInternal;
const backgroundDependsOnSize = (background.image && background.image !== 'none') || background.clipPath || !background.hasUniformBorder() || background.hasBorderRadius() || background.hasBoxShadow();
if (this._nativeBackgroundState === 'invalid' || (this._nativeBackgroundState === 'drawn' && backgroundDependsOnSize)) {
this._redrawNativeBackground(background);
}
}
updateNativeTransform() {
if (!this.isLayoutValid) {
this._hasPendingTransform = true;
return;
}
const scaleX = this.scaleX || 1e-6;
const scaleY = this.scaleY || 1e-6;
const perspective = this.perspective || 300;
const nativeView = this.nativeViewProtected;
let transform = new CATransform3D(CATransform3DIdentity);
// Only set perspective if there is 3D rotation
if (this.rotateX || this.rotateY) {
transform.m34 = -1 / perspective;
}
transform = CATransform3DTranslate(transform, this.translateX, this.translateY, 0);
transform = iOSNativeHelper.applyRotateTransform(transform, this.rotateX, this.rotateY, this.rotate);
transform = CATransform3DScale(transform, scaleX, scaleY, 1);
const needsTransform = !CATransform3DEqualToTransform(this.nativeViewProtected.layer.transform, transform) || (nativeView.outerShadowContainerLayer && !CATransform3DEqualToTransform(nativeView.outerShadowContainerLayer.transform, transform));
if (needsTransform) {
const updateSuspended = this._isPresentationLayerUpdateSuspended();
if (!updateSuspended) {
CATransaction.begin();
}
// Disable CALayer animatable property changes
CATransaction.setDisableActions(true);
this.nativeViewProtected.layer.transform = transform;
if (nativeView.outerShadowContainerLayer) {
nativeView.outerShadowContainerLayer.transform = transform;
}
this._hasTransform = this.nativeViewProtected && !CATransform3DEqualToTransform(this.nativeViewProtected.transform3D, CATransform3DIdentity);
CATransaction.setDisableActions(false);
if (!updateSuspended) {
CATransaction.commit();
}
}
}
updateOriginPoint(originX, originY) {
const nativeView = this.nativeViewProtected;
const newPoint = CGPointMake(originX, originY);
// Disable CALayer animatable property changes
CATransaction.setDisableActions(true);
nativeView.layer.anchorPoint = newPoint;
if (this._cachedFrame) {
this._setNativeViewFrame(nativeView, this._cachedFrame);
}
// Make sure new origin also applies to outer shadow layers
if (nativeView.outerShadowContainerLayer) {
// This is the new frame after view origin point update
const frame = nativeView.frame;
nativeView.outerShadowContainerLayer.anchorPoint = newPoint;
nativeView.outerShadowContainerLayer.position = CGPointMake(frame.origin.x + frame.size.width * originX, frame.origin.y + frame.size.height * originY);
}
CATransaction.setDisableActions(false);
}
// By default we update the view's presentation layer when setting backgroundColor and opacity properties.
// This is done by calling CATransaction begin and commit methods.
// This action should be disabled when updating those properties during an animation.
_suspendPresentationLayerUpdates() {
this._suspendCATransaction = true;
}
_resumePresentationLayerUpdates() {
this._suspendCATransaction = false;
}
_isPresentationLayerUpdateSuspended() {
return this._suspendCATransaction || this._suspendNativeUpdatesCount > 0;
}
_showNativeModalView(parent, options) {
const parentWithController = IOSHelper.getParentWithViewController(parent);
if (!parentWithController) {
Trace.write(`Could not find parent with viewController for ${parent} while showing modal view.`, Trace.categories.ViewHierarchy, Trace.messageType.error);
return;
}
const parentController = parentWithController.viewController;
if (parentController.presentedViewController) {
Trace.write('Parent is already presenting view controller. Close the current modal page before showing another one!', Trace.categories.ViewHierarchy, Trace.messageType.error);
return;
}
if (!parentController.view || !parentController.view.window) {
Trace.write('Parent page is not part of the window hierarchy.', Trace.categories.ViewHierarchy, Trace.messageType.error);
return;
}
this._setupAsRootView({});
super._showNativeModalView(parentWithController, options);
let controller = this.viewController;
if (!controller) {
const nativeView = this.ios || this.nativeViewProtected;
controller = IOSHelper.UILayoutViewController.initWithOwner(new WeakRef(this));
if (nativeView instanceof UIView) {
controller.view.addSubview(nativeView);
}
this.viewController = controller;
}
if (options.transition) {
controller.modalPresentationStyle = 4 /* UIModalPresentationStyle.Custom */;
if (options.transition.instance) {
this._transitioningDelegate = UIViewControllerTransitioningDelegateImpl.initWithOwner(new WeakRef(options.transition.instance));
controller.transitioningDelegate = this._transitioningDelegate;
this.transitionId = options.transition.instance.id;
const transitionState = SharedTransition.getState(options.transition.instance.id);
if (transitionState?.interactive?.dismiss) {
// interactive transitions via gestures
// TODO - these could be typed as: boolean | (view: View) => void
// to allow users to define their own custom gesture dismissals
options.transition.instance.setupInteractiveGesture(this._closeModalCallback.bind(this), this);
}
}
}
else if (options.fullscreen) {
controller.modalPresentationStyle = 0 /* UIModalPresentationStyle.FullScreen */;
}
else {
controller.modalPresentationStyle = 2 /* UIModalPresentationStyle.FormSheet */;
//check whether both height and width is provided and are positive numbers
// set it has prefered content size to the controller presenting the dialog
if (options.ios && options.ios.width > 0 && options.ios.height > 0) {
controller.preferredContentSize = CGSizeMake(options.ios.width, options.ios.height);
}
else {
//use CSS & attribute width & height if option is not provided
const handler = () => {
if (this.viewController) {
const w = (this.width || this.style.width);
const h = (this.height || this.style.height);
//TODO: only numeric value is supported, percentage value is not supported like Android
if (w > 0 && h > 0) {
this.viewController.preferredContentSize = CGSizeMake(w, h);
}
}
this.off(View.loadedEvent, handler);
};
this.on(View.loadedEvent, handler);
}
}
if (options.ios && options.ios.presentationStyle) {
const presentationStyle = options.ios.presentationStyle;
controller.modalPresentationStyle = presentationStyle;
if (presentationStyle === 7 /* UIModalPresentationStyle.Popover */) {
this._setupPopoverControllerDelegate(controller, parent);
}
}
const cancelable = options.cancelable !== undefined ? !!options.cancelable : true;
if (majorVersion >= 13) {
if (cancelable) {
// Listen for dismiss modal callback.
this._setupAdaptiveControllerDelegate(controller);
}
else {
// Prevent users from dismissing the modal.
controller.modalInPresentation = true;
}
}
this.horizontalAlignment = 'stretch';
this.verticalAlignment = 'stretch';
this._raiseShowingModallyEvent();
const animated = options.animated === undefined ? true : !!options.animated;
if (!this._modalAnimatedOptions) {
// track the user's animated options to use upon close as well
this._modalAnimatedOptions = [];
}
this._modalAnimatedOptions.push(animated);
// TODO: a11y
// controller.accessibilityViewIsModal = true;
// controller.accessibilityPerformEscape = () => {
// console.log('accessibilityPerformEscape!!')
// return true;
// }
parentController.presentViewControllerAnimatedCompletion(controller, animated, null);
const transitionCoordinator = parentController.transitionCoordinator;
if (transitionCoordinator) {
transitionCoordinator.animateAlongsideTransitionCompletion(null, () => this._raiseShownModallyEvent());
}
else {
// Apparently iOS 9+ stops all transitions and animations upon application suspend and transitionCoordinator becomes null here in this case.
// Since we are not waiting for any transition to complete, i.e. transitionCoordinator is null, we can directly raise our shownModally event.
// Take a look at https://github.com/NativeScript/NativeScript/issues/2173 for more info and a sample project.
this._raiseShownModallyEvent();
}
controller = null;
}
_hideNativeModalView(parent, whenClosedCallback) {
if (!parent || !parent.viewController) {
Trace.error('Trying to hide modal view but no parent with viewController specified.');
return;
}
// modal view has already been closed by UI, probably as a popover
if (!parent.viewController.presentedViewController) {
whenClosedCallback();
return;
}
const parentController = parent.viewController;
let animated = true;
if (this._modalAnimatedOptions?.length) {
animated = this._modalAnimatedOptions.slice(-1)[0];
}
parentController.dismissViewControllerAnimatedCompletion(animated, () => {
const transitionState = SharedTransition.getState(this.transitionId);
if (!transitionState?.interactiveCancelled) {
this._transitioningDelegate = null;
// this.off('pan', this._interactiveDismissGesture);
if (this._modalAnimatedOptions) {
this._modalAnimatedOptions.pop();
}
}
whenClosedCallback();
});
}
[isEnabledProperty.getDefault]() {
const nativeView = this.nativeViewProtected;
return nativeView instanceof UIControl ? nativeView.enabled : true;
}
[isEnabledProperty.setNative](value) {
const nativeView = this.nativeViewProtected;
if (nativeView instanceof UIControl) {
nativeView.enabled = value;
}
}
[originXProperty.getDefault]() {
return this.nativeViewProtected.layer.anchorPoint.x;
}
[originXProperty.setNative](value) {
this.updateOriginPoint(value, this.originY);
}
[originYProperty.getDefault]() {
return this.nativeViewProtected.layer.anchorPoint.y;
}
[originYProperty.setNative](value) {
this.updateOriginPoint(this.originX, value);
}
[testIDProperty.setNative](value) {
this.setAccessibilityIdentifier(this.nativeViewProtected, value);
}
setAccessibilityIdentifier(view, value) {
view.accessibilityIdentifier = value;
if (this.testID && this.testID !== value)
this.testID = value;
if (this.accessibilityIdentifier !== value)
this.accessibilityIdentifier = value;
}
[accessibilityEnabledProperty.setNative](value) {
this.nativeViewProtected.isAccessibilityElement = !!value;
updateAccessibilityProperties(this);
}
[accessibilityIdentifierProperty.getDefault]() {
return this.nativeViewProtected.accessibilityIdentifier;
}
[accessibilityIdentifierProperty.setNative](value) {
this.setAccessibilityIdentifier(this.nativeViewProtected, value);
}
[accessibilityRoleProperty.setNative](value) {
this.accessibilityRole = value;
updateAccessibilityProperties(this);
}
[accessibilityValueProperty.setNative](value) {
value = value == null ? null : `${value}`;
this.nativeViewProtected.accessibilityValue = value;
}
[accessibilityLabelProperty.setNative](value) {
value = value == null ? null : `${value}`;
// not sure if needed for Label:
// if ((<any>this).nativeTextViewProtected) {
// (<any>this).nativeTextViewProtected.accessibilityLabel = value;
// } else {
this.nativeViewProtected.accessibilityLabel = value;
// }
}
[accessibilityHintProperty.setNative](value) {
value = value == null ? null : `${value}`;
this.nativeViewProtected.accessibilityHint = value;
}
[accessibilityIgnoresInvertColorsProperty.setNative](value) {
this.nativeViewProtected.accessibilityIgnoresInvertColors = !!value;
}
[accessibilityLanguageProperty.setNative](value) {
value = value == null ? null : `${value}`;
this.nativeViewProtected.accessibilityLanguage = value;
}
[accessibilityHiddenProperty.setNative](value) {
this.nativeViewProtected.accessibilityElementsHidden = !!value;
updateAccessibilityProperties(this);
}
[accessibilityLiveRegionProperty.setNative]() {
updateAccessibilityProperties(this);
}
[accessibilityStateProperty.setNative](value) {
this.accessibilityState = value;
updateAccessibilityProperties(this);
}
[accessibilityMediaSessionProperty.setNative]() {
updateAccessibilityProperties(this);
}
[isUserInteractionEnabledProperty.getDefault]() {
return this.nativeViewProtected.userInteractionEnabled;
}
[isUserInteractionEnabledProperty.setNative](value) {
this.nativeViewProtected.userInteractionEnabled = value;
}
[hiddenProperty.getDefault]() {
return this.nativeViewProtected.hidden;
}
[hiddenProperty.setNative](value) {
this.nativeViewProtected.hidden = value;
}
[visibilityProperty.getDefault]() {
return this.nativeViewProtected.hidden ? CoreTypes.Visibility.collapse : CoreTypes.Visibility.visible;
}
[visibilityProperty.setNative](value) {
const nativeView = this.nativeViewProtected;
switch (value) {
case CoreTypes.Visibility.visible:
nativeView.hidden = false;
break;
case CoreTypes.Visibility.hidden:
case CoreTypes.Visibility.collapse:
nativeView.hidden = true;
break;
default:
throw new Error(`Invalid visibility value: ${value}. Valid values are: "${CoreTypes.Visibility.visible}", "${CoreTypes.Visibility.hidden}", "${CoreTypes.Visibility.collapse}".`);
}
// Apply visibility value to shadows as well
if (nativeView.outerShadowContainerLayer) {
nativeView.outerShadowContainerLayer.hidden = nativeView.hidden;
}
}
[opacityProperty.getDefault]() {
return this.nativeViewProtected.alpha;
}
[opacityProperty.setNative](value) {
const nativeView = this.nativeViewProtected;
const updateSuspended = this._isPresentationLayerUpdateSuspended();
if (!updateSuspended) {
CATransaction.begin();
}
// Disable CALayer animatable property changes
CATransaction.setDisableActions(true);
nativeView.alpha = value;
// Apply opacity value to shadows as well
if (nativeView.outerShadowContainerLayer) {
nativeView.outerShadowContainerLayer.opacity = value;
}
CATransaction.setDisableActions(false);
if (!updateSuspended) {
CATransaction.commit();
}
}
[rotateProperty.getDefault]() {
return 0;
}
[rotateProperty.setNative](value) {
this.updateNativeTransform();
}
[rotateXProperty.getDefault]() {
return 0;
}
[rotateXProperty.setNative](value) {
this.updateNativeTransform();
}
[rotateYProperty.getDefault]() {
return 0;
}
[rotateYProperty.setNative](value) {
this.updateNativeTransform();
}
[perspectiveProperty.getDefault]() {
return 300;
}
[perspectiveProperty.setNative](value) {
this.updateNativeTransform();
}
[scaleXProperty.getDefault]() {
return 1;
}
[scaleXProperty.setNative](value) {
this.updateNativeTransform();
}
[scaleYProperty.getDefault]() {
return 1;
}
[scaleYProperty.setNative](value) {
this.updateNativeTransform();
}
[translateXProperty.getDefault]() {
return 0;
}
[translateXProperty.setNative](value) {
this.updateNativeTransform();
}
[translateYProperty.getDefault]() {
return 0;
}
[translateYProperty.setNative](value) {
this.updateNativeTransform();
}
[zIndexProperty.getDefault]() {
return 0;
}
[zIndexProperty.setNative](value) {
this.nativeViewProtected.layer.zPosition = value;
}
[backgroundInternalProperty.getDefault]() {
return this.nativeViewProtected.backgroundColor;
}
[backgroundInternalProperty.setNative](value) {
this._nativeBackgroundState = 'invalid';
if (this.isLayoutValid) {
this._redrawNativeBackground(value);
}
}
sendAccessibilityEvent(options) {
if (!isAccessibilityServiceEnabled()) {
return;
}
if (!options.iosNotificationType) {
return;
}
let notification;
let args = this.nativeViewProtected;
if (typeof msg === 'string' && msg) {
args = msg;
}
switch (options.iosNotificationType) {
case IOSPostAccessibilityNotificationType.Announcement: {
notification = UIAccessibilityAnnouncementNotification;
break;
}
case IOSPostAccessibilityNotificationType.Layout: {
notification = UIAccessibilityLayoutChangedNotification;
break;
}
case IOSPostAccessibilityNotificationType.Screen: {
notification = UIAccessibilityScreenChangedNotification;
break;
}
default: {
return;
}
}
UIAccessibilityPostNotification(notification, args ?? null);
}
accessibilityAnnouncement(msg = this.accessibilityLabel) {
this.sendAccessibilityEvent({
iosNotificationType: IOSPostAccessibilityNotificationType.Announcement,
message: msg,
});
}
accessibilityScreenChanged() {
this.sendAccessibilityEvent({
iosNotificationType: IOSPostAccessibilityNotificationType.Screen,
});
}
_getCurrentLayoutBounds() {
const nativeView = this.nativeViewProtected;
if (nativeView && !this.isCollapsed) {
const frame = nativeView.frame;
const origin = frame.origin;
const size = frame.size;
return {
left: Math.round(layout.toDevicePixels(origin.x)),
top: Math.round(layout.toDevicePixels(origin.y)),
right: Math.round(layout.toDevicePixels(origin.x + size.width)),
bottom: Math.round(layout.toDevicePixels(origin.y + size.height)),
};
}
else {
return { left: 0, top: 0, right: 0, bottom: 0 };
}
}
_redrawNativeBackground(value) {
const updateSuspended = this._isPresentationLayerUpdateSuspended();
if (!updateSuspended) {
CATransaction.begin();
}
// Disable CALayer animatable property changes
CATransaction.setDisableActions(true);
const nativeView = this.nativeViewProtected;
if (nativeView) {
if (value instanceof UIColor) {
nativeView.backgroundColor = value;
}
else {
iosBackground.createBackgroundUIColor(this, (color) => {
nativeView.backgroundColor = color;
});
this._setNativeClipToBounds();
}
}
CATransaction.setDisableActions(false);
if (!updateSuspended) {
CATransaction.commit();
}
this._nativeBackgroundState = 'drawn';
}
_setNativeClipToBounds() {
const view = this.nativeViewProtected;
if (view) {
const backgroundInternal = this.style.backgroundInternal;
view.clipsToBounds = view instanceof UIScrollView || backgroundInternal.hasBorderWidth() || backgroundInternal.hasBorderRadius();
}
}
_setupPopoverControllerDelegate(controller, parent) {
const popoverPresentationController = controller.popoverPresentationController;
this._popoverPresentationDelegate = IOSHelper.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);
}
_setupAdaptiveControllerDelegate(controller) {
this._adaptivePresentationDelegate = IOSHelper.UIAdaptivePresentationControllerDelegateImp.initWithOwnerAndCallback(new WeakRef(this), this._closeModalCallback);
if (controller?.presentationController) {
controller.presentationController.delegate = this._adaptivePresentationDelegate;
}
}
}
__decorate([
profile,
__metadata("design:type", Function),
__metadata("design:paramtypes", [Number, Number, Number, Number, Object]),
__metadata("design:returntype", void 0)
], View.prototype, "layout", null);
__decorate([
profile,
__metadata("design:type", Function),
__metadata("design:paramtypes", [Number, Number]),
__metadata("design:returntype", void 0)
], View.prototype, "onMeasure", null);
View.prototype._nativeBackgroundState = 'unset';
var UIViewControllerTransitioningDelegateImpl = /** @class */ (function (_super) {
__extends(UIViewControllerTransitioningDelegateImpl, _super);
function UIViewControllerTransitioningDelegateImpl() {
return _super !== null && _super.apply(this, arguments) || this;
}
UIViewControllerTransitioningDelegateImpl.initWithOwner = function (owner) {
var delegate = UIViewControllerTransitioningDelegateImpl.new();
delegate.owner = owner;
return delegate;
};
UIViewControllerTransitioningDelegateImpl.prototype.animationControllerForDismissedController = function (dismissed) {
var _a;
var owner = (_a = this.owner) === null || _a === void 0 ? void 0 : _a.deref();
if (owner === null || owner === void 0 ? void 0 : owner.iosDismissedController) {
return owner.iosDismissedController(dismissed);
}
return null;
};
UIViewControllerTransitioningDelegateImpl.prototype.animationControllerForPresentedControllerPresentingControllerSourceController = function (presented, presenting, source) {
var _a;
var owner = (_a = this.owner) === null || _a === void 0 ? void 0 : _a.deref();
if (owner === null || owner === void 0 ? void 0 : owner.iosPresentedController) {
return owner.iosPresentedController(presented, presenting, source);
}
return null;
};
UIViewControllerTransitioningDelegateImpl.prototype.interactionControllerForDismissal = function (animator) {
var _a;
var owner = (_a = this.owner) === null || _a === void 0 ? void 0 : _a.deref();
if (owner === null || owner === void 0 ? void 0 : owner.iosInteractionDismiss) {
var transitionState = SharedTransition.getState(owner.id);
if (transitionState === null || transitionState === void 0 ? void 0 : transitionState.interactiveBegan) {
return owner.iosInteractionDismiss(animator);
}
}
return null;
};
UIViewControllerTransitioningDelegateImpl.prototype.interactionControllerForPresentation = function (animator) {
var _a;
var owner = (_a = this.owner) === null || _a === void 0 ? void 0 : _a.deref();
if (owner === null || owner === void 0 ? void 0 : owner.iosInteractionPresented) {
return owner.iosInteractionPresented(animator);
}
return null;
};
UIViewControllerTransitioningDelegateImpl.ObjCProtocols = [UIViewControllerTransitioningDelegate];
return UIViewControllerTransitioningDelegateImpl;
}(NSObject));
export class ContainerView extends View {
constructor() {
super();
this.iosOverflowSafeArea = true;
}
}
export class CustomLayoutView extends ContainerView {
createNativeView() {
return UIView.alloc().initWithFrame(UIScreen.mainScreen.bounds);
}
get ios() {
return this.nativeViewProtected;
}
onMeasure(widthMeasureSpec, heightMeasureSpec) {
// Don't call super because it will set MeasureDimension. This method must be overridden and calculate its measuredDimensions.
}
_addViewToNativeVisualTree(child, atIndex) {
super._addViewToNativeVisualTree(child, atIndex);
const parentNativeView = this.nativeViewProtected;
const childNativeView = child.nativeViewProtected;
if (parentNativeView && childNativeView) {
if (typeof atIndex !== 'number' || atIndex >= parentNativeView.subviews.count) {
parentNativeView.addSubview(childNativeView);
}
else {
parentNativeView.insertSubviewAtIndex(childNativeView, atIndex);
}
// Add outer shadow layer manually as it belongs to parent layer tree (this is needed for reusable views)
if (childNativeView.outerShadowContainerLayer && !childNativeView.outerShadowContainerLayer.superlayer) {
parentNativeView.layer.insertSublayerBelow(childNativeView.outerShadowContainerLayer, childNativeView.layer);
}
return true;
}
return false;
}
_removeViewFromNativeVisualTree(child) {
super._removeViewFromNativeVisualTree(child);
if (child.nativeViewProtected) {
const nativeView = child.nativeViewProtected;
// Remove outer shadow layer manually as it belongs to parent layer tree
if (nativeView.outerShadowContainerLayer) {
nativeView.outerShadowContainerLayer.removeFromSuperlayer();
}
nativeView.removeFromSuperview();
}
}
}
//# sourceMappingURL=index.ios.js.map