mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
feat(ios): enable safe area for stack and grid
This commit is contained in:
@@ -641,7 +641,7 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public abstract onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void;
|
public abstract onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void;
|
||||||
public abstract onLayout(left: number, top: number, right: number, bottom: number): void;
|
public abstract onLayout(left: number, top: number, right: number, bottom: number, insetLeft?: number, insetTop?: number): void;
|
||||||
public abstract layoutNativeView(left: number, top: number, right: number, bottom: number): void;
|
public abstract layoutNativeView(left: number, top: number, right: number, bottom: number): void;
|
||||||
|
|
||||||
public static resolveSizeAndState(size: number, specSize: number, specMode: number, childMeasuredState: number): number {
|
public static resolveSizeAndState(size: number, specSize: number, specMode: number, childMeasuredState: number): number {
|
||||||
@@ -879,6 +879,14 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getFullscreenArea(): any {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getSafeArea(): any {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
public getLocationInWindow(): Point {
|
public getLocationInWindow(): Point {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
14
tns-core-modules/ui/core/view/view.d.ts
vendored
14
tns-core-modules/ui/core/view/view.d.ts
vendored
@@ -404,7 +404,7 @@ export abstract class View extends ViewBase {
|
|||||||
* @param right Right position, relative to parent
|
* @param right Right position, relative to parent
|
||||||
* @param bottom Bottom position, relative to parent
|
* @param bottom Bottom position, relative to parent
|
||||||
*/
|
*/
|
||||||
public onLayout(left: number, top: number, right: number, bottom: number): void;
|
public onLayout(left: number, top: number, right: number, bottom: number, insetLeft?: number, insetTop?: number): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method must be called by onMeasure(int, int) to store the measured width and measured height. Failing to do so will trigger an exception at measurement time.
|
* This method must be called by onMeasure(int, int) to store the measured width and measured height. Failing to do so will trigger an exception at measurement time.
|
||||||
@@ -527,6 +527,16 @@ export abstract class View extends ViewBase {
|
|||||||
*/
|
*/
|
||||||
public createAnimation(options: AnimationDefinition): Animation;
|
public createAnimation(options: AnimationDefinition): Animation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the iOS safe area frame of the closest parent with UIViewController.
|
||||||
|
*/
|
||||||
|
public getSafeArea(): any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the iOS frame of the closest parent with UIViewController.
|
||||||
|
*/
|
||||||
|
public getFullscreenArea(): any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the location of this view in the window coordinate system.
|
* Returns the location of this view in the window coordinate system.
|
||||||
*/
|
*/
|
||||||
@@ -609,7 +619,7 @@ export abstract class View extends ViewBase {
|
|||||||
* Called by layout method to cache view bounds.
|
* Called by layout method to cache view bounds.
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_setCurrentLayoutBounds(left: number, top: number, right: number, bottom: number): void;
|
_setCurrentLayoutBounds(left: number, top: number, right: number, bottom: number): { boundsChanged: boolean, sizeChanged: boolean };
|
||||||
/**
|
/**
|
||||||
* Return view bounds.
|
* Return view bounds.
|
||||||
* @private
|
* @private
|
||||||
|
|||||||
@@ -90,7 +90,15 @@ export class View extends ViewCommon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (boundsChanged || (this._privateFlags & PFLAG_LAYOUT_REQUIRED) === PFLAG_LAYOUT_REQUIRED) {
|
if (boundsChanged || (this._privateFlags & PFLAG_LAYOUT_REQUIRED) === PFLAG_LAYOUT_REQUIRED) {
|
||||||
this.onLayout(left, top, right, bottom);
|
let insetLeft = 0;
|
||||||
|
let insetTop = 0;
|
||||||
|
|
||||||
|
if (this.nativeViewProtected.safeAreaInsets) {
|
||||||
|
insetLeft = layout.toDevicePixels(this.nativeViewProtected.safeAreaInsets.left);
|
||||||
|
insetTop = layout.toDevicePixels(this.nativeViewProtected.safeAreaInsets.top);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onLayout(left, top, right, bottom, insetLeft, insetTop);
|
||||||
this._privateFlags &= ~PFLAG_LAYOUT_REQUIRED;
|
this._privateFlags &= ~PFLAG_LAYOUT_REQUIRED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +146,7 @@ export class View extends ViewCommon {
|
|||||||
this.setMeasuredDimension(widthAndState, heightAndState);
|
this.setMeasuredDimension(widthAndState, heightAndState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onLayout(left: number, top: number, right: number, bottom: number): void {
|
public onLayout(left: number, top: number, right: number, bottom: number, insetLeft?: number, insetTop?: number): void {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,8 +167,30 @@ export class View extends ViewCommon {
|
|||||||
nativeView.frame = frame;
|
nativeView.frame = frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
const boundsOrigin = nativeView.bounds.origin;
|
if (nativeView.safeAreaInsets) {
|
||||||
nativeView.bounds = CGRectMake(boundsOrigin.x, boundsOrigin.y, frame.size.width, frame.size.height);
|
const leftInset = layout.toDevicePixels(nativeView.safeAreaInsets.left);
|
||||||
|
const topInset = layout.toDevicePixels(nativeView.safeAreaInsets.top);
|
||||||
|
|
||||||
|
const left = layout.toDevicePixels(frame.origin.x);
|
||||||
|
const top = layout.toDevicePixels(frame.origin.y);
|
||||||
|
const right = layout.toDevicePixels(frame.origin.x + frame.size.width);
|
||||||
|
const bottom = layout.toDevicePixels(frame.origin.y + frame.size.height);
|
||||||
|
if (leftInset || topInset) {
|
||||||
|
const frameNew = CGRectMake(layout.toDeviceIndependentPixels(left + leftInset), layout.toDeviceIndependentPixels(top + topInset), layout.toDeviceIndependentPixels(right - left), layout.toDeviceIndependentPixels(bottom - top));
|
||||||
|
nativeView.frame = frameNew;
|
||||||
|
const boundsOrigin = nativeView.bounds.origin;
|
||||||
|
nativeView.bounds = CGRectMake(boundsOrigin.x, boundsOrigin.y, frameNew.size.width, frameNew.size.height);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const boundsOrigin = nativeView.bounds.origin;
|
||||||
|
nativeView.bounds = CGRectMake(boundsOrigin.x, boundsOrigin.y, frame.size.width, frame.size.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const boundsOrigin = nativeView.bounds.origin;
|
||||||
|
nativeView.bounds = CGRectMake(boundsOrigin.x, boundsOrigin.y, frame.size.width, frame.size.height);
|
||||||
|
}
|
||||||
|
|
||||||
this._raiseLayoutChangedEvent();
|
this._raiseLayoutChangedEvent();
|
||||||
this._isLaidOut = true;
|
this._isLaidOut = true;
|
||||||
} else if (!this._isLaidOut) {
|
} else if (!this._isLaidOut) {
|
||||||
@@ -203,6 +233,16 @@ export class View extends ViewCommon {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getFullscreenArea(): any {
|
||||||
|
const parentWithController = ios.getParentWithViewController(this);
|
||||||
|
return parentWithController.viewController.view.frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getSafeArea(): any {
|
||||||
|
const parentWithController = ios.getParentWithViewController(this);
|
||||||
|
return parentWithController.viewController.view.safeAreaLayoutGuide.layoutFrame;
|
||||||
|
}
|
||||||
|
|
||||||
public getLocationInWindow(): Point {
|
public getLocationInWindow(): Point {
|
||||||
if (!this.nativeViewProtected || !this.nativeViewProtected.window) {
|
if (!this.nativeViewProtected || !this.nativeViewProtected.window) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@@ -688,34 +728,44 @@ export namespace ios {
|
|||||||
fullscreenSize.height -= (statusBarHeight + navBarHeight);
|
fullscreenSize.height -= (statusBarHeight + navBarHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
left = safeOrigin.x;
|
// left = safeOrigin.x;
|
||||||
width = safeAreaSize.width;
|
// width = safeAreaSize.width;
|
||||||
|
// top = safeOrigin.y;
|
||||||
|
// height = safeAreaSize.height;
|
||||||
|
|
||||||
if (hasChildControllers) {
|
left = fullscreenOrigin.x;
|
||||||
// If not inner most extend to fullscreen
|
width = fullscreenSize.width;
|
||||||
top = fullscreenOrigin.y;
|
top = fullscreenOrigin.y;
|
||||||
height = fullscreenSize.height;
|
height = fullscreenSize.height;
|
||||||
} else if (!scrollable) {
|
|
||||||
// If not scrollable dock under safe area
|
// if (hasChildControllers) {
|
||||||
top = safeOrigin.y;
|
// // If not inner most extend to fullscreen
|
||||||
height = safeAreaSize.height;
|
// top = fullscreenOrigin.y;
|
||||||
} else if (navBarHidden) {
|
// height = fullscreenSize.height;
|
||||||
// If scrollable but no navigation bar dock under safe area
|
// } else if (!scrollable) {
|
||||||
top = safeOrigin.y;
|
// // If not scrollable dock under safe area
|
||||||
height = navController ? (fullscreenSize.height - top) : safeAreaSize.height;
|
// top = safeOrigin.y;
|
||||||
} else {
|
// height = safeAreaSize.height;
|
||||||
// If scrollable and navigation bar extend to fullscreen
|
// } else if (navBarHidden) {
|
||||||
top = fullscreenOrigin.y;
|
// // If scrollable but no navigation bar dock under safe area
|
||||||
height = fullscreenSize.height;
|
// top = safeOrigin.y;
|
||||||
}
|
// height = navController ? (fullscreenSize.height - top) : safeAreaSize.height;
|
||||||
|
// } else {
|
||||||
|
// // If scrollable and navigation bar extend to fullscreen
|
||||||
|
// top = fullscreenOrigin.y;
|
||||||
|
// height = fullscreenSize.height;
|
||||||
|
// }
|
||||||
|
|
||||||
left = layout.toDevicePixels(left);
|
left = layout.toDevicePixels(left);
|
||||||
top = layout.toDevicePixels(top);
|
top = layout.toDevicePixels(top);
|
||||||
width = layout.toDevicePixels(width);
|
width = layout.toDevicePixels(width);
|
||||||
height = layout.toDevicePixels(height);
|
height = layout.toDevicePixels(height);
|
||||||
|
|
||||||
const widthSpec = layout.makeMeasureSpec(width, layout.EXACTLY);
|
const safeAreaWidth = layout.toDevicePixels(safeAreaSize.width);
|
||||||
const heightSpec = layout.makeMeasureSpec(height, layout.EXACTLY);
|
const safeAreaHeight = layout.toDevicePixels(safeAreaSize.height);
|
||||||
|
|
||||||
|
const widthSpec = layout.makeMeasureSpec(safeAreaWidth, layout.EXACTLY);
|
||||||
|
const heightSpec = layout.makeMeasureSpec(safeAreaHeight, layout.EXACTLY);
|
||||||
|
|
||||||
View.measureChild(null, owner, widthSpec, heightSpec);
|
View.measureChild(null, owner, widthSpec, heightSpec);
|
||||||
View.layoutChild(null, owner, left, top, width + left, height + top);
|
View.layoutChild(null, owner, left, top, width + left, height + top);
|
||||||
|
|||||||
@@ -156,11 +156,11 @@ export class GridLayout extends GridLayoutBase {
|
|||||||
this.setMeasuredDimension(widthSizeAndState, heightSizeAndState);
|
this.setMeasuredDimension(widthSizeAndState, heightSizeAndState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onLayout(left: number, top: number, right: number, bottom: number): void {
|
public onLayout(left: number, top: number, right: number, bottom: number, insetLeft?: number, insetTop?: number): void {
|
||||||
super.onLayout(left, top, right, bottom);
|
super.onLayout(left, top, right, bottom);
|
||||||
|
|
||||||
let paddingLeft = this.effectiveBorderLeftWidth + this.effectivePaddingLeft;
|
let paddingLeft = this.effectiveBorderLeftWidth + this.effectivePaddingLeft + insetLeft;
|
||||||
let paddingTop = this.effectiveBorderTopWidth + this.effectivePaddingTop;
|
let paddingTop = this.effectiveBorderTopWidth + this.effectivePaddingTop + insetTop;
|
||||||
|
|
||||||
this.columnOffsets.length = 0;
|
this.columnOffsets.length = 0;
|
||||||
this.rowOffsets.length = 0;
|
this.rowOffsets.length = 0;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
LayoutBaseCommon, clipToBoundsProperty, isPassThroughParentEnabledProperty, View
|
LayoutBaseCommon, clipToBoundsProperty, isPassThroughParentEnabledProperty, View, layout
|
||||||
} from "./layout-base-common";
|
} from "./layout-base-common";
|
||||||
|
|
||||||
export * from "./layout-base-common";
|
export * from "./layout-base-common";
|
||||||
@@ -30,6 +30,75 @@ export class LayoutBase extends LayoutBaseCommon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public _setNativeViewFrame(nativeView: UIView, frame: CGRect) {
|
||||||
|
// if (!CGRectEqualToRect(nativeView.frame, frame)) {
|
||||||
|
// if (traceEnabled()) {
|
||||||
|
// traceWrite(this + ", Native setFrame: = " + NSStringFromCGRect(frame), traceCategories.Layout);
|
||||||
|
// }
|
||||||
|
// this._cachedFrame = frame;
|
||||||
|
// if (this._hasTransfrom) {
|
||||||
|
// // Always set identity transform before setting frame;
|
||||||
|
// const transform = nativeView.transform;
|
||||||
|
// nativeView.transform = CGAffineTransformIdentity;
|
||||||
|
// nativeView.frame = frame;
|
||||||
|
// nativeView.transform = transform;
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// nativeView.frame = frame;
|
||||||
|
// }
|
||||||
|
|
||||||
|
nativeView.frame = frame;
|
||||||
|
|
||||||
|
const safeArea = this.getSafeArea();
|
||||||
|
const fullscreen = this.getFullscreenArea();
|
||||||
|
const locationOnScreen = this.getLocationOnScreen();
|
||||||
|
const onScreenLeft = layout.toDevicePixels(layout.round(locationOnScreen.x));
|
||||||
|
const onScreenTop = layout.toDevicePixels(layout.round(locationOnScreen.y));
|
||||||
|
|
||||||
|
let left = layout.toDevicePixels(frame.origin.x);
|
||||||
|
let top = layout.toDevicePixels(frame.origin.y);
|
||||||
|
let width = layout.toDevicePixels(frame.size.width);
|
||||||
|
let height = layout.toDevicePixels(frame.size.height);
|
||||||
|
|
||||||
|
let newLeft = left;
|
||||||
|
let newTop = top;
|
||||||
|
let newWidth = width;
|
||||||
|
let newHeight = height;
|
||||||
|
|
||||||
|
if (onScreenLeft <= layout.toDevicePixels(safeArea.origin.x)) {
|
||||||
|
newLeft = layout.toDevicePixels(fullscreen.origin.x);
|
||||||
|
newWidth = width + onScreenLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onScreenTop <= layout.toDevicePixels(safeArea.origin.y)) {
|
||||||
|
newTop = layout.toDevicePixels(fullscreen.origin.y);
|
||||||
|
newHeight = height + onScreenTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onScreenLeft + width >= layout.toDevicePixels(safeArea.origin.x) + layout.toDevicePixels(safeArea.size.width)) {
|
||||||
|
newWidth = newWidth + (layout.toDevicePixels(fullscreen.size.width) - (onScreenLeft + width));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onScreenTop + height >= layout.toDevicePixels(safeArea.origin.y) + layout.toDevicePixels(safeArea.size.height)) {
|
||||||
|
newHeight = newHeight + (layout.toDevicePixels(fullscreen.size.height) - (onScreenTop + height));
|
||||||
|
}
|
||||||
|
|
||||||
|
const frameNew = CGRectMake(layout.toDeviceIndependentPixels(newLeft), layout.toDeviceIndependentPixels(newTop), layout.toDeviceIndependentPixels(newWidth), layout.toDeviceIndependentPixels(newHeight));
|
||||||
|
nativeView.frame = frameNew;
|
||||||
|
|
||||||
|
// if (leftInset || topInset) {
|
||||||
|
// const frameNew = CGRectMake(layout.toDeviceIndependentPixels(left), layout.toDeviceIndependentPixels(top), layout.toDeviceIndependentPixels(right - left + leftInset), layout.toDeviceIndependentPixels(bottom - top + topInset));
|
||||||
|
// nativeView.frame = frameNew;
|
||||||
|
// const boundsOrigin = nativeView.bounds.origin;
|
||||||
|
// nativeView.bounds = CGRectMake(boundsOrigin.x, boundsOrigin.y, frameNew.size.width, frameNew.size.height);
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
const boundsOrigin = nativeView.bounds.origin;
|
||||||
|
nativeView.bounds = CGRectMake(boundsOrigin.x, boundsOrigin.y, frameNew.size.width, frameNew.size.height);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
[clipToBoundsProperty.getDefault](): boolean {
|
[clipToBoundsProperty.getDefault](): boolean {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,25 +81,26 @@ export class StackLayout extends StackLayoutBase {
|
|||||||
this.setMeasuredDimension(widthAndState, heightAndState);
|
this.setMeasuredDimension(widthAndState, heightAndState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onLayout(left: number, top: number, right: number, bottom: number): void {
|
public onLayout(left: number, top: number, right: number, bottom: number, insetLeft?: number, insetTop?: number): void {
|
||||||
super.onLayout(left, top, right, bottom);
|
super.onLayout(left, top, right, bottom);
|
||||||
if (this.orientation === "vertical") {
|
if (this.orientation === "vertical") {
|
||||||
this.layoutVertical(left, top, right, bottom);
|
this.layoutVertical(left, top, right, bottom, insetLeft, insetTop);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.layoutHorizontal(left, top, right, bottom);
|
this.layoutHorizontal(left, top, right, bottom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private layoutVertical(left: number, top: number, right: number, bottom: number): void {
|
private layoutVertical(left: number, top: number, right: number, bottom: number, insetLeft?: number, insetTop?: number): void {
|
||||||
const paddingLeft = this.effectiveBorderLeftWidth + this.effectivePaddingLeft;
|
const paddingLeft = this.effectiveBorderLeftWidth + this.effectivePaddingLeft + insetLeft;
|
||||||
const paddingTop = this.effectiveBorderTopWidth + this.effectivePaddingTop;
|
const paddingTop = this.effectiveBorderTopWidth + this.effectivePaddingTop + insetTop;
|
||||||
const paddingRight = this.effectiveBorderRightWidth + this.effectivePaddingRight;
|
const paddingRight = this.effectiveBorderRightWidth + this.effectivePaddingRight;
|
||||||
const paddingBottom = this.effectiveBorderBottomWidth + this.effectivePaddingBottom;
|
const paddingBottom = this.effectiveBorderBottomWidth + this.effectivePaddingBottom;
|
||||||
|
|
||||||
let childTop: number;
|
let childTop: number;
|
||||||
let childLeft: number = paddingLeft;
|
let childLeft: number = paddingLeft;
|
||||||
let childRight = right - left - paddingRight;
|
let childRight = right - left - paddingRight;
|
||||||
|
// let childRight = right - paddingRight;
|
||||||
|
|
||||||
switch (this.verticalAlignment) {
|
switch (this.verticalAlignment) {
|
||||||
case VerticalAlignment.MIDDLE:
|
case VerticalAlignment.MIDDLE:
|
||||||
|
|||||||
Reference in New Issue
Block a user