mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-16 03:31:45 +08:00

iOS layout positioning now respects native properties like automaticallyAdjustsScrollViewInsets, edgesForExtendedLayout, extendedLayoutIncludesOpaqueBars, navigationBar.translucent, tabBar.translucent Removed frame-tests.ios.ts - those tests are now invalid Added new layout tests inside page-tests.ios.ts Commented few asserts in scroll-view-tests View now expose ios namespace with layoutView method and UILayoutViewController used by page, tab-view and application module ViewBase now expose viewController property that should be set from all widgets that are using viewcontrollers internally (like Page, Frame, TabView) ViewBase now sets ios property to either the view returned from createNativeView or to nativeViewProptected fragment.transitions now use animation/transition start to add fragments to waitingQueue. Before we did it manually in navigate/goBack. This way we can reuse the fragment.transition when calling showDialog. Also when animation/transition ends we check the animation/transition to see if this fragment should be set as current. Frame expose new loadViewFromEntry method (to load a view from URI) Frame navigation happens once frame is loaded Frame now supports Page as a child in XML Fixed GridLayout row, rowSpan, column, columnSpan properties type Fixed bug in GridLayout where add/remove of columns/rows won't update the internal state of the grid (backport from android when GridLayout is recycled) ListView will no longer invalidate layout when cell is removed Fixed bug in ScrollView ios where effectiveMinWidth/Height was multiplied to density (it is already on device pixels so no need to multiply) TabView android now calls loaded only on the selected child (not all) Core refactoring
158 lines
5.9 KiB
TypeScript
158 lines
5.9 KiB
TypeScript
import { ScrollEventData } from ".";
|
|
import { View, layout, ScrollViewBase, scrollBarIndicatorVisibleProperty } from "./scroll-view-common";
|
|
|
|
export * from "./scroll-view-common";
|
|
|
|
class UIScrollViewDelegateImpl extends NSObject implements UIScrollViewDelegate {
|
|
private _owner: WeakRef<ScrollView>;
|
|
|
|
public static initWithOwner(owner: WeakRef<ScrollView>): UIScrollViewDelegateImpl {
|
|
let impl = <UIScrollViewDelegateImpl>UIScrollViewDelegateImpl.new();
|
|
impl._owner = owner;
|
|
return impl;
|
|
}
|
|
|
|
public scrollViewDidScroll(sv: UIScrollView): void {
|
|
let owner = this._owner.get();
|
|
if (owner) {
|
|
owner.notify(<ScrollEventData>{
|
|
object: owner,
|
|
eventName: "scroll",
|
|
scrollX: owner.horizontalOffset,
|
|
scrollY: owner.verticalOffset
|
|
});
|
|
}
|
|
}
|
|
|
|
public static ObjCProtocols = [UIScrollViewDelegate];
|
|
}
|
|
|
|
export class ScrollView extends ScrollViewBase {
|
|
public nativeViewProtected: UIScrollView;
|
|
private _contentMeasuredWidth: number = 0;
|
|
private _contentMeasuredHeight: number = 0;
|
|
private _delegate: UIScrollViewDelegateImpl;
|
|
|
|
constructor() {
|
|
super();
|
|
this.nativeViewProtected = UIScrollView.new();
|
|
this._setNativeClipToBounds();
|
|
}
|
|
|
|
_setNativeClipToBounds() {
|
|
// Always set clipsToBounds for scroll-view
|
|
this.nativeViewProtected.clipsToBounds = true;
|
|
}
|
|
|
|
protected attachNative() {
|
|
this._delegate = UIScrollViewDelegateImpl.initWithOwner(new WeakRef(this));
|
|
this.nativeViewProtected.delegate = this._delegate;
|
|
}
|
|
|
|
protected dettachNative() {
|
|
this.nativeViewProtected.delegate = null;
|
|
}
|
|
|
|
protected updateScrollBarVisibility(value) {
|
|
if (this.orientation === "horizontal") {
|
|
this.nativeViewProtected.showsHorizontalScrollIndicator = value;
|
|
} else {
|
|
this.nativeViewProtected.showsVerticalScrollIndicator = value;
|
|
}
|
|
}
|
|
|
|
get horizontalOffset(): number {
|
|
return this.nativeViewProtected.contentOffset.x;
|
|
}
|
|
|
|
get verticalOffset(): number {
|
|
return this.nativeViewProtected.contentOffset.y;
|
|
}
|
|
|
|
get scrollableWidth(): number {
|
|
if (this.orientation !== "horizontal") {
|
|
return 0;
|
|
}
|
|
|
|
return Math.max(0, this.nativeViewProtected.contentSize.width - this.nativeViewProtected.bounds.size.width);
|
|
}
|
|
|
|
get scrollableHeight(): number {
|
|
if (this.orientation !== "vertical") {
|
|
return 0;
|
|
}
|
|
|
|
return Math.max(0, this.nativeViewProtected.contentSize.height - this.nativeViewProtected.bounds.size.height);
|
|
}
|
|
|
|
[scrollBarIndicatorVisibleProperty.getDefault](): boolean {
|
|
return true;
|
|
}
|
|
[scrollBarIndicatorVisibleProperty.setNative](value: boolean) {
|
|
this.updateScrollBarVisibility(value);
|
|
}
|
|
|
|
public scrollToVerticalOffset(value: number, animated: boolean) {
|
|
if (this.orientation === "vertical") {
|
|
const bounds = this.nativeViewProtected.bounds.size;
|
|
this.nativeViewProtected.scrollRectToVisibleAnimated(CGRectMake(0, value, bounds.width, bounds.height), animated);
|
|
}
|
|
}
|
|
|
|
public scrollToHorizontalOffset(value: number, animated: boolean) {
|
|
if (this.orientation === "horizontal") {
|
|
const bounds = this.nativeViewProtected.bounds.size;
|
|
this.nativeViewProtected.scrollRectToVisibleAnimated(CGRectMake(value, 0, bounds.width, bounds.height), animated);
|
|
}
|
|
}
|
|
|
|
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
|
|
// Don't call measure because it will measure content twice.
|
|
const width = layout.getMeasureSpecSize(widthMeasureSpec);
|
|
const widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
|
|
|
|
const height = layout.getMeasureSpecSize(heightMeasureSpec);
|
|
const heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
|
|
|
|
const child = this.layoutView;
|
|
this._contentMeasuredWidth = this.effectiveMinWidth;
|
|
this._contentMeasuredHeight = this.effectiveMinHeight;
|
|
if (child) {
|
|
let childSize: { measuredWidth: number; measuredHeight: number };
|
|
if (this.orientation === "vertical") {
|
|
childSize = View.measureChild(this, child, widthMeasureSpec, layout.makeMeasureSpec(0, layout.UNSPECIFIED));
|
|
} else {
|
|
childSize = View.measureChild(this, child, layout.makeMeasureSpec(0, layout.UNSPECIFIED), heightMeasureSpec);
|
|
}
|
|
|
|
const w = layout.toDeviceIndependentPixels(childSize.measuredWidth);
|
|
const h = layout.toDeviceIndependentPixels(childSize.measuredHeight);
|
|
this.nativeViewProtected.contentSize = CGSizeMake(w, h);
|
|
|
|
this._contentMeasuredWidth = Math.max(childSize.measuredWidth, this.effectiveMinWidth);
|
|
this._contentMeasuredHeight = Math.max(childSize.measuredHeight, this.effectiveMinHeight);
|
|
}
|
|
|
|
const widthAndState = View.resolveSizeAndState(this._contentMeasuredWidth, width, widthMode, 0);
|
|
const heightAndState = View.resolveSizeAndState(this._contentMeasuredHeight, height, heightMode, 0);
|
|
|
|
this.setMeasuredDimension(widthAndState, heightAndState);
|
|
}
|
|
|
|
public onLayout(left: number, top: number, right: number, bottom: number): void {
|
|
const width = (right - left);
|
|
const height = (bottom - top);
|
|
|
|
if (this.orientation === "horizontal") {
|
|
View.layoutChild(this, this.layoutView, 0, 0, Math.max(this._contentMeasuredWidth, width), height);
|
|
} else {
|
|
View.layoutChild(this, this.layoutView, 0, 0, width, Math.max(this._contentMeasuredHeight, height));
|
|
}
|
|
}
|
|
|
|
public _onOrientationChanged() {
|
|
this.updateScrollBarVisibility(this.scrollBarIndicatorVisible);
|
|
}
|
|
}
|
|
|
|
ScrollView.prototype.recycleNativeView = "auto"; |