diff --git a/.gitignore b/.gitignore index d92483fd2..76eff7cd8 100644 --- a/.gitignore +++ b/.gitignore @@ -51,4 +51,6 @@ apps/lib/ apps/hooks/ apps/node_modules/ -package-lock.json \ No newline at end of file +package-lock.json + +.nsbuildinfo \ No newline at end of file diff --git a/apps/app/ui-tests-app/list-view/main-page.ts b/apps/app/ui-tests-app/list-view/main-page.ts index 62a5e1649..eaae1317f 100644 --- a/apps/app/ui-tests-app/list-view/main-page.ts +++ b/apps/app/ui-tests-app/list-view/main-page.ts @@ -16,6 +16,7 @@ export function loadExamples() { examples.set("bindings", "list-view/listview-binding"); examples.set("listview-bg-separator-color", "list-view/listview-bg-separator-color"); examples.set("csslv", "list-view/csslv"); + examples.set("scrolling-and-sizing", "list-view/scrolling-and-sizing"); return examples; } diff --git a/apps/app/ui-tests-app/list-view/scrolling-and-sizing.css b/apps/app/ui-tests-app/list-view/scrolling-and-sizing.css new file mode 100644 index 000000000..93fbd189e --- /dev/null +++ b/apps/app/ui-tests-app/list-view/scrolling-and-sizing.css @@ -0,0 +1,47 @@ +.p-10 { + padding: 10 20 10 20; +} + +.m-b-10 { + margin-bottom: 10; +} + +.page { + background-color: #F2F2F2; +} + +TextView { + background-color: #FFF; +} + +.bordered { + border-width: 5; + border-color: green; +} + +.fixed-height { + height: 55; +} + +.border-radius { + border-radius: 15; +} + +.border-radius-nonuniform { + border-radius: 10 20 30 40; +} + +.bordered-nonuniform { + border-top-color: red; + border-right-color: green; + border-bottom-color: blue; + border-left-color: purple; + border-top-width: 5; + border-right-width: 10; + border-bottom-width: 15; + border-left-width: 20; +} + +.body { + font-size: 11; +} \ No newline at end of file diff --git a/apps/app/ui-tests-app/list-view/scrolling-and-sizing.ts b/apps/app/ui-tests-app/list-view/scrolling-and-sizing.ts new file mode 100644 index 000000000..243209898 --- /dev/null +++ b/apps/app/ui-tests-app/list-view/scrolling-and-sizing.ts @@ -0,0 +1,4 @@ +export function onNavigatingTo(args) { + const page = args.object; + page.bindingContext = ["The quick", "brown fox", "jumped over", "the", "lazy dog."]; +} \ No newline at end of file diff --git a/apps/app/ui-tests-app/list-view/scrolling-and-sizing.xml b/apps/app/ui-tests-app/list-view/scrolling-and-sizing.xml new file mode 100644 index 000000000..071bc44da --- /dev/null +++ b/apps/app/ui-tests-app/list-view/scrolling-and-sizing.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/app/ui-tests-app/main-page.ts b/apps/app/ui-tests-app/main-page.ts index 6658f10ba..b030a14d3 100644 --- a/apps/app/ui-tests-app/main-page.ts +++ b/apps/app/ui-tests-app/main-page.ts @@ -25,6 +25,7 @@ export function pageLoaded(args: EventData) { examples.set("modalview", "modal-view/modal-view"); examples.set("page", "page/main-page"); examples.set("perf", "perf/main-page"); + examples.set("scroll-view", "scroll-view/main-page"); examples.set("segStyle", "segmented-bar/all"); examples.set("search-bar", "search-bar/main-page"); examples.set("tab-view", "tab-view/main-page"); diff --git a/apps/app/ui-tests-app/scroll-view/main-page.ts b/apps/app/ui-tests-app/scroll-view/main-page.ts new file mode 100644 index 000000000..116a1d77d --- /dev/null +++ b/apps/app/ui-tests-app/scroll-view/main-page.ts @@ -0,0 +1,16 @@ +import { EventData } from "tns-core-modules/data/observable"; +import { SubMainPageViewModel } from "../sub-main-page-view-model"; +import { WrapLayout } from "tns-core-modules/ui/layouts/wrap-layout"; +import { Page } from "tns-core-modules/ui/page"; + +export function pageLoaded(args: EventData) { + const page = args.object; + const wrapLayout = page.getViewById("wrapLayoutWithExamples"); + page.bindingContext = new SubMainPageViewModel(wrapLayout, loadExamples()); +} + +export function loadExamples() { + const examples = new Map(); + examples.set("scrolling-and-sizing", "scroll-view/scrolling-and-sizing"); + return examples; +} \ No newline at end of file diff --git a/apps/app/ui-tests-app/scroll-view/main-page.xml b/apps/app/ui-tests-app/scroll-view/main-page.xml new file mode 100644 index 000000000..33306f0d0 --- /dev/null +++ b/apps/app/ui-tests-app/scroll-view/main-page.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/apps/app/ui-tests-app/scroll-view/scrolling-and-sizing.css b/apps/app/ui-tests-app/scroll-view/scrolling-and-sizing.css new file mode 100644 index 000000000..fa1a2c8f0 --- /dev/null +++ b/apps/app/ui-tests-app/scroll-view/scrolling-and-sizing.css @@ -0,0 +1,47 @@ +.p-10 { + padding: 10 20 10 20; +} + +.m-b-10 { + margin-bottom: 10; +} + +.page { + background-color: #F2F2F2; +} + +ScrollView { + background-color: #FFF; +} + +.bordered { + border-width: 5; + border-color: green; +} + +.fixed-height { + height: 55; +} + +.border-radius { + border-radius: 15; +} + +.border-radius-nonuniform { + border-radius: 10 20 30 40; +} + +.bordered-nonuniform { + border-top-color: red; + border-right-color: green; + border-bottom-color: blue; + border-left-color: purple; + border-top-width: 5; + border-right-width: 10; + border-bottom-width: 15; + border-left-width: 20; +} + +.body { + font-size: 11; +} \ No newline at end of file diff --git a/apps/app/ui-tests-app/scroll-view/scrolling-and-sizing.xml b/apps/app/ui-tests-app/scroll-view/scrolling-and-sizing.xml new file mode 100644 index 000000000..da25a263c --- /dev/null +++ b/apps/app/ui-tests-app/scroll-view/scrolling-and-sizing.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/app/ui-tests-app/text-view/main-page.ts b/apps/app/ui-tests-app/text-view/main-page.ts index 71372d5d9..3581cc0ba 100644 --- a/apps/app/ui-tests-app/text-view/main-page.ts +++ b/apps/app/ui-tests-app/text-view/main-page.ts @@ -14,5 +14,6 @@ export function loadExamples() { examples.set("text-view-border", "text-view/text-view-border"); examples.set("text-view-hint-color", "text-view/text-view-hint-color"); examples.set("hint-text-color", "text-view/hint-text-color"); + examples.set("scrolling-and-sizing", "text-view/scrolling-and-sizing"); return examples; } \ No newline at end of file diff --git a/apps/app/ui-tests-app/text-view/scrolling-and-sizing.css b/apps/app/ui-tests-app/text-view/scrolling-and-sizing.css new file mode 100644 index 000000000..93fbd189e --- /dev/null +++ b/apps/app/ui-tests-app/text-view/scrolling-and-sizing.css @@ -0,0 +1,47 @@ +.p-10 { + padding: 10 20 10 20; +} + +.m-b-10 { + margin-bottom: 10; +} + +.page { + background-color: #F2F2F2; +} + +TextView { + background-color: #FFF; +} + +.bordered { + border-width: 5; + border-color: green; +} + +.fixed-height { + height: 55; +} + +.border-radius { + border-radius: 15; +} + +.border-radius-nonuniform { + border-radius: 10 20 30 40; +} + +.bordered-nonuniform { + border-top-color: red; + border-right-color: green; + border-bottom-color: blue; + border-left-color: purple; + border-top-width: 5; + border-right-width: 10; + border-bottom-width: 15; + border-left-width: 20; +} + +.body { + font-size: 11; +} \ No newline at end of file diff --git a/apps/app/ui-tests-app/text-view/scrolling-and-sizing.xml b/apps/app/ui-tests-app/text-view/scrolling-and-sizing.xml new file mode 100644 index 000000000..f47be58b2 --- /dev/null +++ b/apps/app/ui-tests-app/text-view/scrolling-and-sizing.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tns-core-modules/ui/core/view/view.ios.ts b/tns-core-modules/ui/core/view/view.ios.ts index d1bee883b..ee84ee907 100644 --- a/tns-core-modules/ui/core/view/view.ios.ts +++ b/tns-core-modules/ui/core/view/view.ios.ts @@ -437,7 +437,10 @@ export class View extends ViewCommon { _setNativeClipToBounds() { let backgroundInternal = this.style.backgroundInternal; - this.nativeViewProtected.clipsToBounds = backgroundInternal.hasBorderWidth() || backgroundInternal.hasBorderRadius(); + this.nativeViewProtected.clipsToBounds = + this.nativeViewProtected instanceof UIScrollView || + backgroundInternal.hasBorderWidth() || + backgroundInternal.hasBorderRadius(); } } View.prototype._nativeBackgroundState = "unset"; diff --git a/tns-core-modules/ui/list-view/list-view.ios.ts b/tns-core-modules/ui/list-view/list-view.ios.ts index 20af23d7e..acf2e5e0e 100644 --- a/tns-core-modules/ui/list-view/list-view.ios.ts +++ b/tns-core-modules/ui/list-view/list-view.ios.ts @@ -1,4 +1,5 @@ -import { ItemEventData } from "."; +import { ScrollEventData } from "../scroll-view"; +import { ItemEventData } from "."; import { ListViewBase, View, KeyedTemplate, Length, Observable, Color, separatorColorProperty, itemTemplatesProperty, layout, EventData diff --git a/tns-core-modules/ui/scroll-view/scroll-view.ios.ts b/tns-core-modules/ui/scroll-view/scroll-view.ios.ts index 12961667e..f7c0f6465 100644 --- a/tns-core-modules/ui/scroll-view/scroll-view.ios.ts +++ b/tns-core-modules/ui/scroll-view/scroll-view.ios.ts @@ -17,7 +17,7 @@ class UIScrollViewDelegateImpl extends NSObject implements UIScrollViewDelegate if (owner) { owner.notify({ object: owner, - eventName: ScrollViewBase.scrollEvent, + eventName: "scroll", scrollX: owner.horizontalOffset, scrollY: owner.verticalOffset }); diff --git a/tns-core-modules/ui/styling/background.ios.ts b/tns-core-modules/ui/styling/background.ios.ts index baf32fa7d..f1a4de3c1 100644 --- a/tns-core-modules/ui/styling/background.ios.ts +++ b/tns-core-modules/ui/styling/background.ios.ts @@ -1,3 +1,5 @@ +import { ScrollEventData } from "../scroll-view"; + import { Background as BackgroundDefinition } from "./background"; import { View, Point } from "../core/view"; import { Color } from "../../color"; @@ -10,15 +12,15 @@ export * from "./background-common"; interface NativeView extends UIView { hasNonUniformBorder: boolean; - borderLayer: CAShapeLayer; + borderLayer: CALayer; hasBorderMask: boolean; borderOriginalMask: CALayer; - topBorderLayer: CAShapeLayer; - rightBorderLayer: CAShapeLayer; - bottomBorderLayer: CAShapeLayer; - leftBorderLayer: CAShapeLayer; + topBorderLayer: CALayer; + rightBorderLayer: CALayer; + bottomBorderLayer: CALayer; + leftBorderLayer: CALayer; } interface Rect { @@ -37,11 +39,15 @@ export module ios { const nativeView = view.nativeViewProtected; if (nativeView.hasNonUniformBorder) { + unsubscribeFromScrollNotifications(view); clearNonUniformBorders(nativeView); } - if (background.hasUniformBorderColor() && background.hasBorderRadius()) { + const hasNonUniformBorderWidths = background.hasBorderWidth() && !background.hasUniformBorder(); + const hasNonUniformBorderRadiuses = background.hasBorderRadius() && !background.hasUniformBorderRadius(); + if (background.hasUniformBorderColor() && (hasNonUniformBorderWidths || hasNonUniformBorderRadiuses)) { drawUniformColorNonUniformBorders(nativeView, background); + subscribeForScrollNotifications(view); } else if (background.hasUniformBorder()) { const layer = nativeView.layer; const borderColor = background.getUniformBorderColor(); @@ -52,6 +58,7 @@ export module ios { layer.cornerRadius = Math.min(Math.min(renderSize.width / 2, renderSize.height / 2), cornerRadius); } else { drawNoRadiusNonUniformBorders(nativeView, background); + subscribeForScrollNotifications(view); } // Clip-path should be called after borders are applied. @@ -69,6 +76,42 @@ export module ios { } } +function onScroll(this: void, args: ScrollEventData): void { + const view = args.object; + const nativeView = view.nativeViewProtected; + if (nativeView instanceof UIScrollView) { + adjustLayersForScrollView(nativeView); + } +} + +function adjustLayersForScrollView(nativeView: UIScrollView & NativeView) { + const layer = nativeView.borderLayer; + if (layer instanceof CALayer) { + // Compensates with transition for the background layers for scrolling in ScrollView based controls. + CATransaction.begin(); + CATransaction.setValueForKey(kCFBooleanTrue, kCATransactionDisableActions); + const offset = nativeView.contentOffset; + const transform = { a: 1, b: 0, c: 0, d: 1, tx: offset.x, ty: offset.y }; + layer.setAffineTransform(transform); + if (nativeView.layer.mask) { + nativeView.layer.mask.setAffineTransform(transform); + } + CATransaction.commit(); + } +} + +function unsubscribeFromScrollNotifications(view: View) { + if (view.nativeViewProtected instanceof UIScrollView) { + view.off("scroll", onScroll); + } +} +function subscribeForScrollNotifications(view: View) { + if (view.nativeViewProtected instanceof UIScrollView) { + view.on("scroll", onScroll); + adjustLayersForScrollView(view.nativeViewProtected); + } +} + function clearNonUniformBorders(nativeView: NativeView): void { if (nativeView.borderLayer) { nativeView.borderLayer.removeFromSuperlayer(); @@ -507,12 +550,15 @@ function drawUniformColorNonUniformBorders(nativeView: NativeView, background: B } function drawNoRadiusNonUniformBorders(nativeView: NativeView, background: BackgroundDefinition) { - const layer = nativeView.layer; - layer.borderColor = undefined; - layer.borderWidth = 0; - layer.cornerRadius = 0; + const borderLayer = CALayer.layer(); + nativeView.layer.addSublayer(borderLayer); + nativeView.borderLayer = borderLayer; - const layerBounds = layer.bounds; + borderLayer.borderColor = undefined; + borderLayer.borderWidth = 0; + borderLayer.cornerRadius = 0; + + const layerBounds = nativeView.layer.bounds; const layerOrigin = layerBounds.origin; const layerSize = layerBounds.size; @@ -555,7 +601,7 @@ function drawNoRadiusNonUniformBorders(nativeView: NativeView, background: Backg topBorderLayer.fillColor = background.borderTopColor.ios.CGColor; topBorderLayer.path = topBorderPath; - layer.addSublayer(topBorderLayer); + borderLayer.addSublayer(topBorderLayer); nativeView.topBorderLayer = topBorderLayer; hasNonUniformBorder = true; } @@ -573,7 +619,7 @@ function drawNoRadiusNonUniformBorders(nativeView: NativeView, background: Backg rightBorderLayer.fillColor = background.borderRightColor.ios.CGColor; rightBorderLayer.path = rightBorderPath; - layer.addSublayer(rightBorderLayer); + borderLayer.addSublayer(rightBorderLayer); nativeView.rightBorderLayer = rightBorderLayer; hasNonUniformBorder = true; } @@ -591,7 +637,7 @@ function drawNoRadiusNonUniformBorders(nativeView: NativeView, background: Backg bottomBorderLayer.fillColor = background.borderBottomColor.ios.CGColor; bottomBorderLayer.path = bottomBorderPath; - layer.addSublayer(bottomBorderLayer); + borderLayer.addSublayer(bottomBorderLayer); nativeView.bottomBorderLayer = bottomBorderLayer; hasNonUniformBorder = true; } @@ -609,7 +655,7 @@ function drawNoRadiusNonUniformBorders(nativeView: NativeView, background: Backg leftBorderLayer.fillColor = background.borderLeftColor.ios.CGColor; leftBorderLayer.path = leftBorderPath; - layer.addSublayer(leftBorderLayer); + borderLayer.addSublayer(leftBorderLayer); nativeView.leftBorderLayer = leftBorderLayer; hasNonUniformBorder = true; } diff --git a/tns-core-modules/ui/text-view/text-view.ios.ts b/tns-core-modules/ui/text-view/text-view.ios.ts index 9bd944fe0..dcf9b6960 100644 --- a/tns-core-modules/ui/text-view/text-view.ios.ts +++ b/tns-core-modules/ui/text-view/text-view.ios.ts @@ -1,4 +1,5 @@ -import { TextView as TextViewDefinition } from "."; +import { ScrollEventData } from "../scroll-view"; +import { TextView as TextViewDefinition } from "."; import { EditableTextBase, editableProperty, hintProperty, textProperty, colorProperty, placeholderColorProperty, borderTopWidthProperty, borderRightWidthProperty, borderBottomWidthProperty, borderLeftWidthProperty, @@ -9,8 +10,8 @@ import { profile } from "../../profiling"; export * from "../editable-text-base"; -class UITextViewDelegateImpl extends NSObject implements UITextViewDelegate { - public static ObjCProtocols = [UITextViewDelegate]; +class UITextViewDelegateImpl extends NSObject implements UIScrollViewDelegate, UITextViewDelegate { + public static ObjCProtocols = [UITextViewDelegate, UIScrollViewDelegate]; private _owner: WeakRef; @@ -76,6 +77,19 @@ class UITextViewDelegateImpl extends NSObject implements UITextViewDelegate { return true; } + + public scrollViewDidScroll(sv: UIScrollView): void { + const owner = this._owner.get(); + if (owner) { + const contentOffset = owner.nativeViewProtected.contentOffset; + owner.notify({ + object: owner, + eventName: "scroll", + scrollX: contentOffset.x, + scrollY: contentOffset.y + }); + } + } } export class TextView extends EditableTextBase implements TextViewDefinition {