mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-15 11:01:21 +08:00
iOS owner pattern changed to use WeakRef in order to prevent memory leaks.
Optimized list-view-tests. Added time per test.
This commit is contained in:
@ -29,12 +29,23 @@ export interface TestInfoEntry {
|
||||
testTimeout: number;
|
||||
}
|
||||
|
||||
export function time(): number {
|
||||
if (global.android) {
|
||||
return java.lang.System.nanoTime() / 1000000; // 1 ms = 1000000 ns
|
||||
}
|
||||
else {
|
||||
return CACurrentMediaTime() * 1000;
|
||||
}
|
||||
}
|
||||
|
||||
export var write = function write(message: string, type?: number) {
|
||||
//console.log(message);
|
||||
trace.write(message, trace.categories.Test, type);
|
||||
}
|
||||
|
||||
var runTest = function (testInfo: TestInfoEntry) {
|
||||
let start = time();
|
||||
let duration;
|
||||
try {
|
||||
if (testInfo.instance) {
|
||||
testInfo.testFunc.apply(testInfo.instance);
|
||||
@ -44,13 +55,15 @@ var runTest = function (testInfo: TestInfoEntry) {
|
||||
}
|
||||
|
||||
if (testInfo.isTest) {
|
||||
write("--- [" + testInfo.testName + "] OK", trace.messageType.info);
|
||||
duration = time() - start;
|
||||
write("--- [" + testInfo.testName + "] OK, duration: " + duration, trace.messageType.info);
|
||||
testInfo.isPassed = true;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
if (testInfo.isTest) {
|
||||
write("--- [" + testInfo.testName + "] FAILED: " + e.message, trace.messageType.error);
|
||||
duration = time() - start;
|
||||
write("--- [" + testInfo.testName + "] FAILED: " + e.message + ", duration: " + duration, trace.messageType.error);
|
||||
testInfo.isPassed = false;
|
||||
testInfo.errorMessage = e.message;
|
||||
}
|
||||
@ -78,7 +91,7 @@ function runAsync(testInfo: TestInfoEntry, recursiveIndex: number, testTimeout?:
|
||||
var error;
|
||||
var isDone = false;
|
||||
var handle;
|
||||
var testStartTime = new Date().getTime();
|
||||
var testStartTime = time();
|
||||
//write("--- [" + testInfo.testName + "] Started at: " + testStartTime, trace.messageType.info);
|
||||
var doneCallback = function (e: Error) {
|
||||
if (e) {
|
||||
@ -93,23 +106,25 @@ function runAsync(testInfo: TestInfoEntry, recursiveIndex: number, testTimeout?:
|
||||
testTimeout = defaultTimeout;
|
||||
}
|
||||
|
||||
let duration;
|
||||
var checkFinished = function () {
|
||||
duration = time() - testStartTime;
|
||||
if (isDone) {
|
||||
write("--- [" + testInfo.testName + "] OK", trace.messageType.info);
|
||||
write("--- [" + testInfo.testName + "] OK, duration: " + duration, trace.messageType.info);
|
||||
//write("--- [" + testInfo.testName + "] took: " + (new Date().getTime() - testStartTime), trace.messageType.info);
|
||||
testInfo.isPassed = true;
|
||||
runTests(testsQueue, recursiveIndex + 1);
|
||||
}
|
||||
else if (error) {
|
||||
write("--- [" + testInfo.testName + "] FAILED: " + error.message, trace.messageType.error);
|
||||
write("--- [" + testInfo.testName + "] FAILED: " + error.message + ", duration: " + duration, trace.messageType.error);
|
||||
//write("--- [" + testInfo.testName + "] took: " + (new Date().getTime() - testStartTime), trace.messageType.info);
|
||||
testInfo.errorMessage = error.message;
|
||||
runTests(testsQueue, recursiveIndex + 1);
|
||||
}
|
||||
else {
|
||||
var testEndTime = new Date().getTime();
|
||||
var testEndTime = time();
|
||||
if (testEndTime - testStartTime > testTimeout) {
|
||||
write("--- [" + testInfo.testName + "] TIMEOUT", trace.messageType.error);
|
||||
write("--- [" + testInfo.testName + "] TIMEOUT, duration: " + duration, trace.messageType.error);
|
||||
//write("--- [" + testInfo.testName + "] took: " + (testEndTime - testStartTime), trace.messageType.info);
|
||||
testInfo.errorMessage = "Test timeout.";
|
||||
runTests(testsQueue, recursiveIndex + 1);
|
||||
|
@ -128,24 +128,15 @@ function printRunTestStats() {
|
||||
}
|
||||
}
|
||||
|
||||
function time(): number {
|
||||
if (platform.device.os === platform.platformNames.android) {
|
||||
return java.lang.System.nanoTime() / 1000000; // 1 ms = 1000000 ns
|
||||
}
|
||||
else if (platform.device.os === platform.platformNames.ios) {
|
||||
return CACurrentMediaTime() * 1000;
|
||||
}
|
||||
}
|
||||
|
||||
function startLog(): void {
|
||||
let testsName: string = this.name;
|
||||
TKUnit.write("START " + testsName + " TESTS.", trace.messageType.info);
|
||||
this.start = time();
|
||||
this.start = TKUnit.time();
|
||||
}
|
||||
|
||||
function log(): void {
|
||||
let testsName: string = this.name;
|
||||
let duration = time() - this.start;
|
||||
let duration = TKUnit.time() - this.start;
|
||||
TKUnit.write(testsName + " COMPLETED for " + duration, trace.messageType.info);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import TKUnit = require("../../TKUnit");
|
||||
import testModule = require("../../ui-test");
|
||||
import TKUnit = require("../../TKUnit");
|
||||
import testRunner = require("../../testRunner");
|
||||
import app = require("application");
|
||||
import helper = require("../helper");
|
||||
@ -68,39 +69,32 @@ import labelModule = require("ui/label");
|
||||
|
||||
var ASYNC = 0.2;
|
||||
var FEW_ITEMS = [0, 1, 2];
|
||||
var MANY_ITEMS = [];
|
||||
var MANY_ITEMS = new Array<number>(100);
|
||||
for (var i = 0; i < 100; i++) {
|
||||
MANY_ITEMS[i] = i;
|
||||
MANY_ITEMS.push(i);
|
||||
}
|
||||
|
||||
export function test_default_TNS_values() {
|
||||
// <snippet module="ui/list-view" title="list-view">
|
||||
// ### Creating a ListView
|
||||
// ``` JavaScript
|
||||
var listView = new listViewModule.ListView();
|
||||
// ```
|
||||
// </snippet>
|
||||
export class ListViewTest extends testModule.UITest<listViewModule.ListView> {
|
||||
|
||||
public create(): listViewModule.ListView {
|
||||
return new listViewModule.ListView();
|
||||
}
|
||||
|
||||
public test_default_TNS_values() {
|
||||
// <snippet module="ui/list-view" title="list-view">
|
||||
// ### Creating a ListView
|
||||
// ``` JavaScript
|
||||
var listView = new listViewModule.ListView();
|
||||
// ```
|
||||
// </snippet>
|
||||
|
||||
function testAction(views: Array<viewModule.View>) {
|
||||
TKUnit.assertEqual(listView.isScrolling, false, "Default listView.isScrolling");
|
||||
TKUnit.assert(types.isUndefined(listView.items), "Default listView.items should be undefined");
|
||||
}
|
||||
|
||||
if (app.android) {
|
||||
TKUnit.assert(listView.android instanceof android.widget.ListView, "android property is android.widget.ListView");
|
||||
}
|
||||
else if (app.ios) {
|
||||
TKUnit.assert(listView.ios instanceof UITableView, "ios property is UITableView");
|
||||
}
|
||||
public test_set_items_to_array_loads_all_items(done) {
|
||||
var listView = this.testView;
|
||||
|
||||
};
|
||||
|
||||
helper.buildUIAndRunTest(listView, testAction);
|
||||
}
|
||||
|
||||
export function test_set_items_to_array_loads_all_items() {
|
||||
var listView = new listViewModule.ListView();
|
||||
|
||||
function testAction(views: Array<viewModule.View>) {
|
||||
var indexes = {};
|
||||
// <snippet module="ui/list-view" title="list-view">
|
||||
// ### Using ListView with Array
|
||||
@ -117,26 +111,35 @@ export function test_set_items_to_array_loads_all_items() {
|
||||
|
||||
//<hide>
|
||||
indexes[args.index] = true;
|
||||
if (args.index === (colors.length - 1)) {
|
||||
try {
|
||||
if (app.android) {
|
||||
TKUnit.assert(listView.android instanceof android.widget.ListView, "android property is android.widget.ListView");
|
||||
}
|
||||
else if (app.ios) {
|
||||
TKUnit.assert(listView.ios instanceof UITableView, "ios property is UITableView");
|
||||
}
|
||||
|
||||
TKUnit.assert(indexes[0], "itemLoading not called for index 0");
|
||||
TKUnit.assert(indexes[1], "itemLoading not called for index 1");
|
||||
TKUnit.assert(indexes[2], "itemLoading not called for index 2");
|
||||
done(null);
|
||||
}
|
||||
catch (e) {
|
||||
done(e);
|
||||
}
|
||||
}
|
||||
//</hide>
|
||||
});
|
||||
// ```
|
||||
// </snippet>
|
||||
}
|
||||
|
||||
TKUnit.wait(ASYNC);
|
||||
TKUnit.assert(indexes[0], "itemLoading not called for index 0");
|
||||
TKUnit.assert(indexes[1], "itemLoading not called for index 1");
|
||||
TKUnit.assert(indexes[2], "itemLoading not called for index 2");
|
||||
};
|
||||
public test_set_native_item_exposed() {
|
||||
let listView = this.testView;
|
||||
|
||||
helper.buildUIAndRunTest(listView, testAction);
|
||||
}
|
||||
|
||||
export function test_set_native_item_exposed() {
|
||||
var listView = new listViewModule.ListView();
|
||||
|
||||
function testAction(views: Array<viewModule.View>) {
|
||||
var indexes = {};
|
||||
var colors = ["red", "green", "blue"];
|
||||
let indexes = {};
|
||||
let colors = ["red", "green", "blue"];
|
||||
listView.items = colors;
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, function (args: listViewModule.ItemEventData) {
|
||||
if (platform.device.os === platform.platformNames.ios) {
|
||||
@ -146,7 +149,8 @@ export function test_set_native_item_exposed() {
|
||||
}
|
||||
});
|
||||
|
||||
TKUnit.wait(ASYNC);
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC);
|
||||
|
||||
for (var item in indexes) {
|
||||
if (platform.device.os === platform.platformNames.ios) {
|
||||
TKUnit.assert(indexes[item] instanceof UITableViewCell, "itemLoading not called for index " + item);
|
||||
@ -154,36 +158,27 @@ export function test_set_native_item_exposed() {
|
||||
TKUnit.assert(indexes[item] instanceof android.view.ViewGroup, "itemLoading not called for index " + item);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
helper.buildUIAndRunTest(listView, testAction);
|
||||
}
|
||||
|
||||
export function test_set_items_to_array_creates_native_views() {
|
||||
var listView = new listViewModule.ListView();
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, loadViewWithItemNumber);
|
||||
|
||||
function testAction(views: Array<viewModule.View>) {
|
||||
public test_set_items_to_array_creates_native_views() {
|
||||
var listView = this.testView;
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, this.loadViewWithItemNumber);
|
||||
listView.items = FEW_ITEMS;
|
||||
|
||||
TKUnit.wait(ASYNC);
|
||||
TKUnit.assertEqual(getNativeViewCount(listView), FEW_ITEMS.length, "Native views count.");
|
||||
};
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC);
|
||||
TKUnit.assertEqual(this.getNativeViewCount(listView), FEW_ITEMS.length, "Native views count.");
|
||||
}
|
||||
|
||||
helper.buildUIAndRunTest(listView, testAction);
|
||||
}
|
||||
public test_refresh_after_adding_items_to_array_loads_new_items() {
|
||||
|
||||
export function test_refresh_after_adding_items_to_array_loads_new_items() {
|
||||
var listView = this.testView;
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, this.loadViewWithItemNumber);
|
||||
|
||||
var listView = new listViewModule.ListView();
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, loadViewWithItemNumber);
|
||||
|
||||
function testAction(views: Array<viewModule.View>) {
|
||||
var colors = ["red", "green", "blue"];
|
||||
listView.items = colors;
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC);
|
||||
TKUnit.assertEqual(this.getNativeViewCount(listView), colors.length, "Native views count.");
|
||||
|
||||
TKUnit.wait(ASYNC);
|
||||
TKUnit.assertEqual(getNativeViewCount(listView), colors.length, "Native views count.");
|
||||
// <snippet module="ui/list-view" title="list-view">
|
||||
// > Note, that changing the array after the list view is shown will not update the UI.
|
||||
// You can force-update the UI using the refresh() method.
|
||||
@ -193,19 +188,14 @@ export function test_refresh_after_adding_items_to_array_loads_new_items() {
|
||||
listView.refresh();
|
||||
// ```
|
||||
// </snippet>
|
||||
TKUnit.wait(ASYNC);
|
||||
TKUnit.assertEqual(getNativeViewCount(listView), colors.length, "Native views count.");
|
||||
};
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC);
|
||||
TKUnit.assertEqual(this.getNativeViewCount(listView), colors.length, "Native views count.");
|
||||
}
|
||||
|
||||
helper.buildUIAndRunTest(listView, testAction);
|
||||
}
|
||||
|
||||
export function test_refresh_reloads_all_items() {
|
||||
var listView = new listViewModule.ListView();
|
||||
|
||||
function testAction(views: Array<viewModule.View>) {
|
||||
var indexes = {};
|
||||
var testStarted = false;
|
||||
public test_refresh_reloads_all_items() {
|
||||
let listView = this.testView;
|
||||
let indexes = {};
|
||||
let completed = false;
|
||||
listView.items = FEW_ITEMS;
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, function (args: listViewModule.ItemEventData) {
|
||||
if (!args.view) {
|
||||
@ -213,79 +203,66 @@ export function test_refresh_reloads_all_items() {
|
||||
}
|
||||
(<labelModule.Label>args.view).text = "item " + args.index;
|
||||
|
||||
if (testStarted) {
|
||||
indexes[args.index] = true;
|
||||
}
|
||||
indexes[args.index] = indexes[args.index] ? indexes[args.index] + 1 : 1;
|
||||
completed = args.index === (listView.items.length - 1);
|
||||
});
|
||||
|
||||
TKUnit.wait(ASYNC);
|
||||
testStarted = true;
|
||||
TKUnit.waitUntilReady(() => { return completed; }, ASYNC);
|
||||
TKUnit.assertEqual(indexes[0], 1, "itemLoading called more than once");
|
||||
TKUnit.assertEqual(indexes[1], 1, "itemLoading called more than once");
|
||||
TKUnit.assertEqual(indexes[2], 1, "itemLoading called more than once");
|
||||
|
||||
completed = false;
|
||||
listView.refresh();
|
||||
TKUnit.wait(ASYNC);
|
||||
|
||||
TKUnit.assert(indexes[0], "itemLoading not called for index 0");
|
||||
TKUnit.assert(indexes[1], "itemLoading not called for index 1");
|
||||
TKUnit.assert(indexes[2], "itemLoading not called for index 2");
|
||||
};
|
||||
TKUnit.waitUntilReady(() => { return completed; }, ASYNC);
|
||||
TKUnit.assertEqual(indexes[0], 2, "itemLoading not called for index 0");
|
||||
TKUnit.assertEqual(indexes[1], 2, "itemLoading not called for index 1");
|
||||
TKUnit.assertEqual(indexes[2], 2, "itemLoading not called for index 2");
|
||||
}
|
||||
|
||||
helper.buildUIAndRunTest(listView, testAction);
|
||||
}
|
||||
public test_set_itmes_to_null_clears_native_items() {
|
||||
var listView = this.testView;
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, this.loadViewWithItemNumber);
|
||||
|
||||
export function test_set_itmes_to_null_clears_native_items() {
|
||||
var listView = new listViewModule.ListView();
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, loadViewWithItemNumber);
|
||||
|
||||
function testAction(views: Array<viewModule.View>) {
|
||||
listView.items = FEW_ITEMS;
|
||||
TKUnit.wait(ASYNC);
|
||||
TKUnit.assertEqual(getNativeViewCount(listView), FEW_ITEMS.length, "Native views count.");
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC);
|
||||
TKUnit.assertEqual(this.getNativeViewCount(listView), FEW_ITEMS.length, "Native views count.");
|
||||
|
||||
listView.items = null;
|
||||
TKUnit.wait(ASYNC);
|
||||
TKUnit.assertEqual(getNativeViewCount(listView), 0, "Native views count.");
|
||||
};
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === 0; }, ASYNC);
|
||||
TKUnit.assertEqual(this.getNativeViewCount(listView), 0, "Native views count.");
|
||||
}
|
||||
|
||||
helper.buildUIAndRunTest(listView, testAction);
|
||||
}
|
||||
public test_set_itmes_to_undefiend_clears_native_items() {
|
||||
var listView = this.testView;
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, this.loadViewWithItemNumber);
|
||||
|
||||
export function test_set_itmes_to_undefiend_clears_native_items() {
|
||||
var listView = new listViewModule.ListView();
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, loadViewWithItemNumber);
|
||||
|
||||
function testAction(views: Array<viewModule.View>) {
|
||||
listView.items = FEW_ITEMS;
|
||||
TKUnit.wait(ASYNC);
|
||||
TKUnit.assertEqual(getNativeViewCount(listView), FEW_ITEMS.length, "Native views count.");
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC);
|
||||
TKUnit.assertEqual(this.getNativeViewCount(listView), FEW_ITEMS.length, "Native views count.");
|
||||
|
||||
listView.items = undefined;
|
||||
TKUnit.wait(ASYNC);
|
||||
TKUnit.assertEqual(getNativeViewCount(listView), 0, "Native views count.");
|
||||
};
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === 0; }, ASYNC);
|
||||
TKUnit.assertEqual(this.getNativeViewCount(listView), 0, "Native views count.");
|
||||
}
|
||||
|
||||
helper.buildUIAndRunTest(listView, testAction);
|
||||
}
|
||||
public test_set_itmes_to_different_source_loads_new_items() {
|
||||
var listView = this.testView;
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, this.loadViewWithItemNumber);
|
||||
|
||||
export function test_set_itmes_to_different_source_loads_new_items() {
|
||||
var listView = new listViewModule.ListView();
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, loadViewWithItemNumber);
|
||||
|
||||
function testAction(views: Array<viewModule.View>) {
|
||||
listView.items = [1, 2, 3];
|
||||
TKUnit.wait(ASYNC);
|
||||
TKUnit.assertEqual(getNativeViewCount(listView), 3, "Native views count.");
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC);
|
||||
TKUnit.assertEqual(this.getNativeViewCount(listView), 3, "Native views count.");
|
||||
|
||||
listView.items = ["a", "b", "c", "d"];
|
||||
TKUnit.wait(ASYNC);
|
||||
TKUnit.assertEqual(getNativeViewCount(listView), 4, "Native views count.");
|
||||
};
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC);
|
||||
TKUnit.assertEqual(this.getNativeViewCount(listView), 4, "Native views count.");
|
||||
}
|
||||
|
||||
helper.buildUIAndRunTest(listView, testAction);
|
||||
}
|
||||
public test_set_items_to_observable_array_loads_all_items() {
|
||||
var listView = this.testView;
|
||||
|
||||
export function test_set_items_to_observable_array_loads_all_items() {
|
||||
var listView = new listViewModule.ListView();
|
||||
|
||||
function testAction(views: Array<viewModule.View>) {
|
||||
var indexes = {};
|
||||
// <snippet module="ui/list-view" title="list-view">
|
||||
// ### Using ListView with ObservableArray
|
||||
@ -304,25 +281,21 @@ export function test_set_items_to_observable_array_loads_all_items() {
|
||||
// ```
|
||||
// </snippet>
|
||||
|
||||
TKUnit.wait(ASYNC);
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC);
|
||||
TKUnit.assert(indexes[0], "itemLoading not called for index 0");
|
||||
TKUnit.assert(indexes[1], "itemLoading not called for index 1");
|
||||
TKUnit.assert(indexes[2], "itemLoading not called for index 2");
|
||||
};
|
||||
}
|
||||
|
||||
helper.buildUIAndRunTest(listView, testAction);
|
||||
}
|
||||
public test_add_to_observable_array_refreshes_the_listview() {
|
||||
var listView = this.testView;
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, this.loadViewWithItemNumber);
|
||||
|
||||
export function test_add_to_observable_array_refreshes_the_listview() {
|
||||
var listView = new listViewModule.ListView();
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, loadViewWithItemNumber);
|
||||
|
||||
function testAction(views: Array<viewModule.View>) {
|
||||
var colors = new observableArray.ObservableArray(["red", "green", "blue"]);
|
||||
listView.items = colors;
|
||||
|
||||
TKUnit.wait(ASYNC);
|
||||
TKUnit.assertEqual(getNativeViewCount(listView), 3, "getNativeViewCount");
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC);
|
||||
TKUnit.assertEqual(this.getNativeViewCount(listView), 3, "getNativeViewCount");
|
||||
|
||||
// <snippet module="ui/list-view" title="list-view">
|
||||
// > When using ObservableArray the list view will be automatically updated when items are added or removed form the array.
|
||||
@ -331,60 +304,46 @@ export function test_add_to_observable_array_refreshes_the_listview() {
|
||||
//// The ListView will be updated automatically.
|
||||
// ```
|
||||
// </snippet>
|
||||
TKUnit.wait(ASYNC);
|
||||
TKUnit.assertEqual(getNativeViewCount(listView), 4, "getNativeViewCount");
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC);
|
||||
TKUnit.assertEqual(this.getNativeViewCount(listView), 4, "getNativeViewCount");
|
||||
}
|
||||
|
||||
};
|
||||
public test_remove_from_observable_array_refreshes_the_listview() {
|
||||
var listView = this.testView;
|
||||
var data = new observableArray.ObservableArray([1, 2, 3]);
|
||||
|
||||
helper.buildUIAndRunTest(listView, testAction);
|
||||
}
|
||||
|
||||
export function test_remove_from_observable_array_refreshes_the_listview() {
|
||||
var listView = new listViewModule.ListView();
|
||||
var data = new observableArray.ObservableArray([1, 2, 3]);
|
||||
|
||||
function testAction(views: Array<viewModule.View>) {
|
||||
listView.items = data;
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, loadViewWithItemNumber);
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, this.loadViewWithItemNumber);
|
||||
|
||||
TKUnit.wait(ASYNC);
|
||||
TKUnit.assertEqual(getNativeViewCount(listView), 3, "getNativeViewCount");
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC);
|
||||
TKUnit.assertEqual(this.getNativeViewCount(listView), 3, "getNativeViewCount");
|
||||
|
||||
data.pop();
|
||||
TKUnit.wait(ASYNC);
|
||||
TKUnit.assertEqual(getNativeViewCount(listView), 2, "getNativeViewCount");
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC);
|
||||
TKUnit.assertEqual(this.getNativeViewCount(listView), 2, "getNativeViewCount");
|
||||
}
|
||||
|
||||
};
|
||||
public test_splice_observable_array_refreshes_the_listview() {
|
||||
var listView = this.testView;
|
||||
var data = new observableArray.ObservableArray(["a", "b", "c"]);
|
||||
|
||||
helper.buildUIAndRunTest(listView, testAction);
|
||||
}
|
||||
|
||||
export function test_splice_observable_array_refreshes_the_listview() {
|
||||
var listView = new listViewModule.ListView();
|
||||
var data = new observableArray.ObservableArray(["a", "b", "c"]);
|
||||
|
||||
function testAction(views: Array<viewModule.View>) {
|
||||
listView.items = data;
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, loadViewWithItemNumber);
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, this.loadViewWithItemNumber);
|
||||
|
||||
TKUnit.wait(ASYNC);
|
||||
TKUnit.assertEqual(getNativeViewCount(listView), 3, "getNativeViewCount");
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC);
|
||||
TKUnit.assertEqual(this.getNativeViewCount(listView), 3, "getNativeViewCount");
|
||||
|
||||
// Remove the first 2 elements and add
|
||||
data.splice(0, 2, "d", "e", "f");
|
||||
TKUnit.wait(ASYNC);
|
||||
TKUnit.assertEqual(getNativeViewCount(listView), 4, "getNativeViewCount");
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC);
|
||||
TKUnit.assertEqual(this.getNativeViewCount(listView), 4, "getNativeViewCount");
|
||||
}
|
||||
|
||||
};
|
||||
public test_nativeTap_is_raised() {
|
||||
var listView = this.testView;
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, this.loadViewWithItemNumber);
|
||||
listView.items = FEW_ITEMS;
|
||||
|
||||
helper.buildUIAndRunTest(listView, testAction);
|
||||
}
|
||||
export function test_nativeTap_is_raised() {
|
||||
var listView = new listViewModule.ListView();
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, loadViewWithItemNumber);
|
||||
listView.items = FEW_ITEMS;
|
||||
|
||||
function testAction(views: Array<viewModule.View>) {
|
||||
var nativeTapRaised = false;
|
||||
var itemIndex = -1;
|
||||
/* tslint:disable:no-unused-variable */
|
||||
@ -405,23 +364,19 @@ export function test_nativeTap_is_raised() {
|
||||
// ```
|
||||
// </snippet>
|
||||
/* tslint:enable:no-unused-variable */
|
||||
TKUnit.wait(ASYNC);
|
||||
performNativeItemTap(listView, 1);
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC);
|
||||
this.performNativeItemTap(listView, 1);
|
||||
|
||||
TKUnit.assert(nativeTapRaised, "itemTap not raised.");
|
||||
TKUnit.assertEqual(itemIndex, 1, "tappedItemIndex");
|
||||
};
|
||||
}
|
||||
|
||||
helper.buildUIAndRunTest(listView, testAction);
|
||||
}
|
||||
public test_loadMoreItems_raised_when_showing_few_items() {
|
||||
var listView = this.testView;
|
||||
|
||||
export function test_loadMoreItems_raised_when_showing_few_items() {
|
||||
var listView = new listViewModule.ListView();
|
||||
|
||||
function testAction(views: Array<viewModule.View>) {
|
||||
var loadMoreItemsCount = 0;
|
||||
listView.items = FEW_ITEMS;
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, loadViewWithItemNumber);
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, this.loadViewWithItemNumber);
|
||||
// <snippet module="ui/list-view" title="list-view">
|
||||
// ### LoadMoreItems event
|
||||
// The event will be raised when the ListView is scrolled so that the last item is visible.
|
||||
@ -435,36 +390,35 @@ export function test_loadMoreItems_raised_when_showing_few_items() {
|
||||
});
|
||||
// ```
|
||||
// </snippet>
|
||||
TKUnit.wait(ASYNC);
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC);
|
||||
TKUnit.assertEqual(loadMoreItemsCount, 1, "loadMoreItemsCount");
|
||||
};
|
||||
}
|
||||
|
||||
helper.buildUIAndRunTest(listView, testAction);
|
||||
}
|
||||
public test_loadMoreItems_not_raised_when_showing_many_items() {
|
||||
var listView = this.testView;
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, this.loadViewWithItemNumber);
|
||||
|
||||
export function test_loadMoreItems_not_raised_when_showing_many_items() {
|
||||
var listView = new listViewModule.ListView();
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, loadViewWithItemNumber);
|
||||
|
||||
function testAction(views: Array<viewModule.View>) {
|
||||
var loadMoreItemsCount = 0;
|
||||
listView.items = MANY_ITEMS;
|
||||
listView.on(listViewModule.ListView.loadMoreItemsEvent, function (data: observable.EventData) {
|
||||
loadMoreItemsCount++;
|
||||
});
|
||||
|
||||
TKUnit.wait(ASYNC);
|
||||
// We can't use waitUntilReady because we don't know what to wait for.
|
||||
if (platform.device.os === platform.platformNames.android) {
|
||||
this.waitUntilTestElementLayoutIsValid();
|
||||
}
|
||||
else {
|
||||
TKUnit.wait(ASYNC);
|
||||
}
|
||||
|
||||
TKUnit.assertEqual(loadMoreItemsCount, 0, "loadMoreItemsCount");
|
||||
};
|
||||
}
|
||||
|
||||
helper.buildUIAndRunTest(listView, testAction);
|
||||
}
|
||||
public test_loadMoreItems_is_raised_when_scroll_to_last_item() {
|
||||
var listView = this.testView;
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, this.loadViewWithItemNumber);
|
||||
|
||||
export function test_loadMoreItems_is_raised_when_scroll_to_last_item() {
|
||||
var listView = new listViewModule.ListView();
|
||||
listView.on(listViewModule.ListView.itemLoadingEvent, loadViewWithItemNumber);
|
||||
|
||||
function testAction(views: Array<viewModule.View>) {
|
||||
var loadMoreItemsCount = 0;
|
||||
listView.items = MANY_ITEMS;
|
||||
listView.on(listViewModule.ListView.loadMoreItemsEvent, function (data: observable.EventData) {
|
||||
@ -473,89 +427,82 @@ export function test_loadMoreItems_is_raised_when_scroll_to_last_item() {
|
||||
|
||||
listView.scrollToIndex(MANY_ITEMS.length - 1);
|
||||
|
||||
TKUnit.wait(ASYNC);
|
||||
// We can't use waitUntilReady because we don't know what to wait for.
|
||||
if (platform.device.os === platform.platformNames.android) {
|
||||
this.waitUntilTestElementLayoutIsValid();
|
||||
}
|
||||
else {
|
||||
TKUnit.wait(ASYNC);
|
||||
}
|
||||
|
||||
TKUnit.assert(loadMoreItemsCount > 0, "loadMoreItemsCount");
|
||||
};
|
||||
}
|
||||
|
||||
helper.buildUIAndRunTest(listView, testAction);
|
||||
}
|
||||
public test_usingAppLevelConvertersInListViewItems() {
|
||||
var listView = this.testView;
|
||||
|
||||
export function test_usingAppLevelConvertersInListViewItems() {
|
||||
var listView = new listViewModule.ListView();
|
||||
var dateConverter = function (value, format) {
|
||||
var result = format;
|
||||
var day = value.getDate();
|
||||
result = result.replace("DD", day < 10 ? "0" + day : day);
|
||||
var month = value.getMonth() + 1;
|
||||
result = result.replace("MM", month < 10 ? "0" + month : month);
|
||||
result = result.replace("YYYY", value.getFullYear());
|
||||
return result;
|
||||
};
|
||||
|
||||
var dateConverter = function (value, format) {
|
||||
var result = format;
|
||||
var day = value.getDate();
|
||||
result = result.replace("DD", day < 10 ? "0" + day : day);
|
||||
var month = value.getMonth() + 1;
|
||||
result = result.replace("MM", month < 10 ? "0" + month : month);
|
||||
result = result.replace("YYYY", value.getFullYear());
|
||||
return result;
|
||||
};
|
||||
app.resources["dateConverter"] = dateConverter;
|
||||
|
||||
app.resources["dateConverter"] = dateConverter;
|
||||
var data = new observableArray.ObservableArray();
|
||||
data.push({ date: new Date(2020, 2, 7) });
|
||||
|
||||
var data = new observableArray.ObservableArray();
|
||||
data.push({ date: new Date(2020, 2, 7) });
|
||||
|
||||
function testAction(views: Array<viewModule.View>) {
|
||||
listView.itemTemplate = "<Label id=\"testLabel\" text=\"{{ date, date | dateConverter('DD.MM.YYYY') }}\" />";
|
||||
listView.items = data;
|
||||
|
||||
TKUnit.wait(ASYNC);
|
||||
var nativeElementText = getTextFromNativeElementAt(listView, 0);
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC);
|
||||
var nativeElementText = this.getTextFromNativeElementAt(listView, 0);
|
||||
|
||||
TKUnit.assertEqual(nativeElementText, "07.03.2020", "native element text");
|
||||
};
|
||||
}
|
||||
|
||||
helper.buildUIAndRunTest(listView, testAction);
|
||||
}
|
||||
public test_BindingListViewToASimpleArray() {
|
||||
var listView = this.testView;
|
||||
|
||||
export function test_BindingListViewToASimpleArray() {
|
||||
var listView = new listViewModule.ListView();
|
||||
|
||||
function testAction(views: Array<viewModule.View>) {
|
||||
listView.itemTemplate = "<Label id=\"testLabel\" text=\"{{ $value }}\" />";
|
||||
listView.items = [1, 2, 3];
|
||||
|
||||
TKUnit.wait(ASYNC);
|
||||
var firstNativeElementText = getTextFromNativeElementAt(listView, 0);
|
||||
var secondNativeElementText = getTextFromNativeElementAt(listView, 1);
|
||||
var thirdNativeElementText = getTextFromNativeElementAt(listView, 2);
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC);
|
||||
|
||||
var firstNativeElementText = this.getTextFromNativeElementAt(listView, 0);
|
||||
var secondNativeElementText = this.getTextFromNativeElementAt(listView, 1);
|
||||
var thirdNativeElementText = this.getTextFromNativeElementAt(listView, 2);
|
||||
|
||||
TKUnit.assertEqual(firstNativeElementText, "1", "first element text");
|
||||
TKUnit.assertEqual(secondNativeElementText, "2", "second element text");
|
||||
TKUnit.assertEqual(thirdNativeElementText, "3", "third element text");
|
||||
}
|
||||
|
||||
helper.buildUIAndRunTest(listView, testAction);
|
||||
}
|
||||
public test_BindingListViewToASimpleArrayWithExpression() {
|
||||
var listView = this.testView;
|
||||
|
||||
export function test_BindingListViewToASimpleArrayWithExpression() {
|
||||
var listView = new listViewModule.ListView();
|
||||
|
||||
function testAction(views: Array<viewModule.View>) {
|
||||
listView.itemTemplate = "<Label id=\"testLabel\" text=\"{{ $value, $value + ' some static text' }}\" />";
|
||||
listView.items = [1, 2, 3];
|
||||
|
||||
TKUnit.wait(ASYNC);
|
||||
var firstNativeElementText = getTextFromNativeElementAt(listView, 0);
|
||||
var secondNativeElementText = getTextFromNativeElementAt(listView, 1);
|
||||
var thirdNativeElementText = getTextFromNativeElementAt(listView, 2);
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC);
|
||||
|
||||
var firstNativeElementText = this.getTextFromNativeElementAt(listView, 0);
|
||||
var secondNativeElementText = this.getTextFromNativeElementAt(listView, 1);
|
||||
var thirdNativeElementText = this.getTextFromNativeElementAt(listView, 2);
|
||||
|
||||
TKUnit.assertEqual(firstNativeElementText, "1 some static text", "first element text");
|
||||
TKUnit.assertEqual(secondNativeElementText, "2 some static text", "second element text");
|
||||
TKUnit.assertEqual(thirdNativeElementText, "3 some static text", "third element text");
|
||||
}
|
||||
|
||||
helper.buildUIAndRunTest(listView, testAction);
|
||||
}
|
||||
public test_bindingToParentObject() {
|
||||
var listView = this.testView;
|
||||
var expectedValue = "parentTestValue";
|
||||
|
||||
export function test_bindingToParentObject() {
|
||||
var listView = new listViewModule.ListView();
|
||||
var expectedValue = "parentTestValue";
|
||||
|
||||
function testAction(views: Array<viewModule.View>) {
|
||||
var listViewModel = new observable.Observable();
|
||||
listViewModel.set("items", [1, 2, 3]);
|
||||
listViewModel.set("parentTestProp", expectedValue);
|
||||
@ -563,24 +510,21 @@ export function test_bindingToParentObject() {
|
||||
listView.bind({ sourceProperty: "items", targetProperty: "items" });
|
||||
listView.itemTemplate = "<Label id=\"testLabel\" text=\"{{ $parents[ListView].parentTestProp }}\" />";
|
||||
|
||||
TKUnit.wait(ASYNC);
|
||||
var firstNativeElementText = getTextFromNativeElementAt(listView, 0);
|
||||
var secondNativeElementText = getTextFromNativeElementAt(listView, 1);
|
||||
var thirdNativeElementText = getTextFromNativeElementAt(listView, 2);
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC);
|
||||
|
||||
var firstNativeElementText = this.getTextFromNativeElementAt(listView, 0);
|
||||
var secondNativeElementText = this.getTextFromNativeElementAt(listView, 1);
|
||||
var thirdNativeElementText = this.getTextFromNativeElementAt(listView, 2);
|
||||
|
||||
TKUnit.assertEqual(firstNativeElementText, expectedValue, "first element text");
|
||||
TKUnit.assertEqual(secondNativeElementText, expectedValue, "second element text");
|
||||
TKUnit.assertEqual(thirdNativeElementText, expectedValue, "third element text");
|
||||
}
|
||||
|
||||
helper.buildUIAndRunTest(listView, testAction);
|
||||
}
|
||||
public test_bindingToParentObjectWithSpacesInIndexer() {
|
||||
var listView = this.testView;
|
||||
var expectedValue = "parentTestValue";
|
||||
|
||||
export function test_bindingToParentObjectWithSpacesInIndexer() {
|
||||
var listView = new listViewModule.ListView();
|
||||
var expectedValue = "parentTestValue";
|
||||
|
||||
function testAction(views: Array<viewModule.View>) {
|
||||
var listViewModel = new observable.Observable();
|
||||
listViewModel.set("items", [1, 2, 3]);
|
||||
listViewModel.set("parentTestProp", expectedValue);
|
||||
@ -588,31 +532,27 @@ export function test_bindingToParentObjectWithSpacesInIndexer() {
|
||||
listView.bind({ sourceProperty: "items", targetProperty: "items" });
|
||||
listView.itemTemplate = "<Label id=\"testLabel\" text=\"{{ $parents[ ListView ].parentTestProp }}\" />";
|
||||
|
||||
TKUnit.wait(ASYNC);
|
||||
var firstNativeElementText = getTextFromNativeElementAt(listView, 0);
|
||||
var secondNativeElementText = getTextFromNativeElementAt(listView, 1);
|
||||
var thirdNativeElementText = getTextFromNativeElementAt(listView, 2);
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC);
|
||||
var firstNativeElementText = this.getTextFromNativeElementAt(listView, 0);
|
||||
var secondNativeElementText = this.getTextFromNativeElementAt(listView, 1);
|
||||
var thirdNativeElementText = this.getTextFromNativeElementAt(listView, 2);
|
||||
|
||||
TKUnit.assertEqual(firstNativeElementText, expectedValue, "first element text");
|
||||
TKUnit.assertEqual(secondNativeElementText, expectedValue, "second element text");
|
||||
TKUnit.assertEqual(thirdNativeElementText, expectedValue, "third element text");
|
||||
}
|
||||
|
||||
helper.buildUIAndRunTest(listView, testAction);
|
||||
}
|
||||
public test_ConverterIsCalledJustOnce_onAddingItemsToListView() {
|
||||
var listView = this.testView;
|
||||
var converterCalledCounter = 0;
|
||||
|
||||
export function test_ConverterIsCalledJustOnce_onAddingItemsToListView() {
|
||||
var listView = new listViewModule.ListView();
|
||||
var converterCalledCounter = 0;
|
||||
var testConverter = function (value) {
|
||||
converterCalledCounter++;
|
||||
return value;
|
||||
}
|
||||
|
||||
var testConverter = function (value) {
|
||||
converterCalledCounter++;
|
||||
return value;
|
||||
}
|
||||
app.resources["testConverter"] = testConverter;
|
||||
|
||||
app.resources["testConverter"] = testConverter;
|
||||
|
||||
function testAction(views: Array<viewModule.View>) {
|
||||
var listViewModel = new observable.Observable();
|
||||
listViewModel.set("items", [1, 2, 3]);
|
||||
listView.bindingContext = listViewModel;
|
||||
@ -620,7 +560,7 @@ export function test_ConverterIsCalledJustOnce_onAddingItemsToListView() {
|
||||
listView.bind({ sourceProperty: "items", targetProperty: "items" });
|
||||
listView.itemTemplate = "<Label id=\"testLabel\" text=\"{{ $value, $value | testConverter }}\" />";
|
||||
|
||||
TKUnit.wait(ASYNC);
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC);
|
||||
|
||||
if (utils.ios && utils.ios.MajorVersion < 8) {
|
||||
TKUnit.assertEqual(converterCalledCounter, listViewModel.get("items").length * 2, "Converter should be called once for every item.");
|
||||
@ -630,127 +570,163 @@ export function test_ConverterIsCalledJustOnce_onAddingItemsToListView() {
|
||||
}
|
||||
}
|
||||
|
||||
helper.buildUIAndRunTest(listView, testAction);
|
||||
}
|
||||
|
||||
export function test_no_memory_leak_when_items_is_regular_array(done) {
|
||||
var createFunc = function (): listViewModule.ListView {
|
||||
var listView = new listViewModule.ListView();
|
||||
listView.items = FEW_ITEMS;
|
||||
return listView;
|
||||
};
|
||||
|
||||
helper.buildUIWithWeakRefAndInteract(createFunc, (list) => {
|
||||
TKUnit.assert(list.isLoaded, "ListView should be loaded here");
|
||||
}, done);
|
||||
}
|
||||
|
||||
export function test_no_memory_leak_when_items_is_observable_array(done) {
|
||||
// Keep the reference to the observable array to test the weakEventListener
|
||||
var colors = new observableArray.ObservableArray(["red", "green", "blue"]);
|
||||
|
||||
var createFunc = function (): listViewModule.ListView {
|
||||
var listView = new listViewModule.ListView();
|
||||
listView.items = colors;
|
||||
return listView;
|
||||
};
|
||||
|
||||
helper.buildUIWithWeakRefAndInteract(createFunc, (list) => {
|
||||
TKUnit.assert(list.isLoaded, "ListView should be loaded here");
|
||||
}, done);
|
||||
}
|
||||
|
||||
function loadViewWithItemNumber(args: listViewModule.ItemEventData) {
|
||||
if (!args.view) {
|
||||
args.view = new labelModule.Label();
|
||||
public test_no_memory_leak_when_items_is_regular_array() {
|
||||
let weakRef = new WeakRef<listViewModule.ListView>(this.testView);
|
||||
weakRef.get().items = FEW_ITEMS;
|
||||
this.waitUntilTestElementIsLoaded();
|
||||
TKUnit.assertTrue(weakRef.get().isLoaded, "ListView should be loaded here");
|
||||
this.assertNoMemoryLeak(weakRef);
|
||||
}
|
||||
(<labelModule.Label>args.view).text = "item " + args.index;
|
||||
}
|
||||
|
||||
function getTextFromNativeElementAt(listView: listViewModule.ListView, index: number): string {
|
||||
if (listView.android) {
|
||||
var nativeElement = listView.android.getChildAt(index);
|
||||
if (nativeElement instanceof android.view.ViewGroup) {
|
||||
return (<android.widget.TextView>((<any>nativeElement).getChildAt(0))).getText() + "";
|
||||
public test_no_memory_leak_when_items_is_observable_array() {
|
||||
// Keep the reference to the observable array to test the weakEventListener
|
||||
var colors = new observableArray.ObservableArray(["red", "green", "blue"]);
|
||||
|
||||
let weakRef = new WeakRef<listViewModule.ListView>(this.testView);
|
||||
weakRef.get().items = colors;
|
||||
this.waitUntilTestElementIsLoaded();
|
||||
TKUnit.assertTrue(weakRef.get().isLoaded, "ListView should be loaded here");
|
||||
this.assertNoMemoryLeak(weakRef);
|
||||
}
|
||||
|
||||
private test_view_in_itemLoading_is_not_collected_prematurely() {
|
||||
let weakRef: WeakRef<labelModule.Label>;
|
||||
|
||||
let handler = function (args: listViewModule.ItemEventData) {
|
||||
let lbl = new labelModule.Label();
|
||||
lbl.text = args.index.toString();
|
||||
weakRef = new WeakRef(lbl);
|
||||
args.view = lbl;
|
||||
let listView: listViewModule.ListView = <listViewModule.ListView>args.object;
|
||||
listView.off("itemLoading", handler);
|
||||
};
|
||||
|
||||
this.testView.on("itemLoading", handler);
|
||||
this.testView.items = [1];
|
||||
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(this.testView) === this.testView.items.length; }, ASYNC);
|
||||
|
||||
if (platform.device.os === platform.platformNames.ios) {
|
||||
// Could cause GC on the next call.
|
||||
// NOTE: Don't replace this with forceGC();
|
||||
new ArrayBuffer(4 * 1024 * 1024);
|
||||
}
|
||||
return (<android.widget.TextView>nativeElement).getText() + "";
|
||||
utils.GC();
|
||||
|
||||
TKUnit.assert(weakRef.get(), weakRef.get() + " died prematurely!");
|
||||
}
|
||||
else if (listView.ios) {
|
||||
if (types.isFunction(listView.ios.visibleCells)) {
|
||||
return listView.ios.visibleCells()[index].contentView.subviews[0].text + "";
|
||||
|
||||
private assertNoMemoryLeak(weakRef: WeakRef<listViewModule.ListView>) {
|
||||
this.tearDown();
|
||||
TKUnit.waitUntilReady(() => {
|
||||
if (platform.device.os === platform.platformNames.ios) {
|
||||
// Could cause GC on the next call.
|
||||
// NOTE: Don't replace this with forceGC();
|
||||
new ArrayBuffer(4 * 1024 * 1024);
|
||||
}
|
||||
utils.GC();
|
||||
return !weakRef.get();
|
||||
});
|
||||
|
||||
TKUnit.assert(!weakRef.get(), weakRef.get() + " leaked!");
|
||||
}
|
||||
|
||||
private loadViewWithItemNumber(args: listViewModule.ItemEventData) {
|
||||
if (!args.view) {
|
||||
args.view = new labelModule.Label();
|
||||
}
|
||||
(<labelModule.Label>args.view).text = "item " + args.index;
|
||||
}
|
||||
|
||||
private getTextFromNativeElementAt(listView: listViewModule.ListView, index: number): string {
|
||||
if (listView.android) {
|
||||
var nativeElement = listView.android.getChildAt(index);
|
||||
if (nativeElement instanceof android.view.ViewGroup) {
|
||||
return (<android.widget.TextView>((<any>nativeElement).getChildAt(0))).getText() + "";
|
||||
}
|
||||
return (<android.widget.TextView>nativeElement).getText() + "";
|
||||
}
|
||||
else if (listView.ios) {
|
||||
if (types.isFunction(listView.ios.visibleCells)) {
|
||||
return listView.ios.visibleCells()[index].contentView.subviews[0].text + "";
|
||||
}
|
||||
else {
|
||||
return listView.ios.visibleCells[index].contentView.subviews[0].text + "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private getNativeViewCount(listView: listViewModule.ListView): number {
|
||||
if (listView.android) {
|
||||
return listView.android.getChildCount();
|
||||
}
|
||||
else if (listView.ios) {
|
||||
if (types.isFunction(listView.ios.visibleCells)) {
|
||||
return listView.ios.visibleCells().count;
|
||||
}
|
||||
else {
|
||||
return (<any>listView.ios.visibleCells).count;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return listView.ios.visibleCells[index].contentView.subviews[0].text + "";
|
||||
throw new Error("Cannot get native view count");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getNativeViewCount(listView: listViewModule.ListView): number {
|
||||
if (listView.android) {
|
||||
return listView.android.getChildCount();
|
||||
}
|
||||
else if (listView.ios) {
|
||||
if (types.isFunction(listView.ios.visibleCells)) {
|
||||
return listView.ios.visibleCells().count;
|
||||
private performNativeItemTap(listView: listViewModule.ListView, index: number): void {
|
||||
if (listView.android) {
|
||||
listView.android.performItemClick(listView.android.getChildAt(index), index, listView.android.getAdapter().getItemId(index));
|
||||
}
|
||||
else if (listView.ios) {
|
||||
// Calling selectRowAtIndexPathAnimatedScrollPosition will not tiger [Will|Did]SelectRowAtIndexPath callbacks.
|
||||
listView.ios.delegate.tableViewWillSelectRowAtIndexPath(listView.ios, NSIndexPath.indexPathForItemInSection(index, 0));
|
||||
}
|
||||
else {
|
||||
return (<any>listView.ios.visibleCells).count;
|
||||
throw new Error("Cannot perform native item tap");
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new Error("Cannot get native view count");
|
||||
}
|
||||
|
||||
//public test_LoadedUnloaded() {
|
||||
// var listView = this.testView;
|
||||
// var vm = {
|
||||
// loadedCount: 0,
|
||||
// unloadedCount: 0,
|
||||
// onViewLoaded: function onViewLoaded(args) {
|
||||
// this.loadedCount++;
|
||||
// console.log(args.object._domId + " LOADED");
|
||||
// },
|
||||
// onViewUnloaded: function onViewUnloaded(args) {
|
||||
// this.unloadedCount++;
|
||||
// console.log(args.object._domId + " UNLOADED");
|
||||
// }
|
||||
// };
|
||||
// listView.itemTemplate = "<Label text=\"{{ $value }}\" loaded=\"{{ onViewLoaded }}\" unloaded=\"{{ onViewUnloaded }}\"/>";
|
||||
// listView.bindingContext = vm;
|
||||
|
||||
// var count = 10;
|
||||
// var modifier = listView.ios ? 1 : 0; // iOS has one fake measure cell that raises loaded.
|
||||
// var generate = function (count: number): observableArray.ObservableArray<string> {
|
||||
// var items = new observableArray.ObservableArray<string>();
|
||||
// for (var i = 0; i < count; i++) {
|
||||
// items.push("Item " + i);
|
||||
// }
|
||||
// return items;
|
||||
// }
|
||||
|
||||
// function testAction(views: Array<viewModule.View>) {
|
||||
// listView.items = generate(count);
|
||||
// TKUnit.wait(ASYNC);
|
||||
// frame.topmost().navigate("pages/navigation/pageB");
|
||||
// TKUnit.wait(ASYNC);
|
||||
// frame.topmost().goBack();
|
||||
// TKUnit.assertEqual(vm.loadedCount, count + modifier, "Loaded Count");
|
||||
// TKUnit.assertEqual(vm.unloadedCount, count, "Unloaded Count");
|
||||
// }
|
||||
|
||||
// helper.buildUIAndRunTest(listView, testAction);
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
function performNativeItemTap(listView: listViewModule.ListView, index: number): void {
|
||||
if (listView.android) {
|
||||
listView.android.performItemClick(listView.android.getChildAt(index), index, listView.android.getAdapter().getItemId(index));
|
||||
}
|
||||
else if (listView.ios) {
|
||||
// Calling selectRowAtIndexPathAnimatedScrollPosition will not tiger [Will|Did]SelectRowAtIndexPath callbacks.
|
||||
listView.ios.delegate.tableViewWillSelectRowAtIndexPath(listView.ios, NSIndexPath.indexPathForItemInSection(index, 0));
|
||||
}
|
||||
else {
|
||||
throw new Error("Cannot perform native item tap");
|
||||
}
|
||||
}
|
||||
|
||||
//export function test_LoadedUnloaded() {
|
||||
// var listView = new listViewModule.ListView();
|
||||
// var vm = {
|
||||
// loadedCount: 0,
|
||||
// unloadedCount: 0,
|
||||
// onViewLoaded: function onViewLoaded(args) {
|
||||
// this.loadedCount++;
|
||||
// console.log(args.object._domId + " LOADED");
|
||||
// },
|
||||
// onViewUnloaded: function onViewUnloaded(args) {
|
||||
// this.unloadedCount++;
|
||||
// console.log(args.object._domId + " UNLOADED");
|
||||
// }
|
||||
// };
|
||||
// listView.itemTemplate = "<Label text=\"{{ $value }}\" loaded=\"{{ onViewLoaded }}\" unloaded=\"{{ onViewUnloaded }}\"/>";
|
||||
// listView.bindingContext = vm;
|
||||
|
||||
// var count = 10;
|
||||
// var modifier = listView.ios ? 1 : 0; // iOS has one fake measure cell that raises loaded.
|
||||
// var generate = function (count: number): observableArray.ObservableArray<string> {
|
||||
// var items = new observableArray.ObservableArray<string>();
|
||||
// for (var i = 0; i < count; i++) {
|
||||
// items.push("Item " + i);
|
||||
// }
|
||||
// return items;
|
||||
// }
|
||||
|
||||
// function testAction(views: Array<viewModule.View>) {
|
||||
// listView.items = generate(count);
|
||||
// TKUnit.wait(ASYNC);
|
||||
// frame.topmost().navigate("pages/navigation/pageB");
|
||||
// TKUnit.wait(ASYNC);
|
||||
// frame.topmost().goBack();
|
||||
// TKUnit.assertEqual(vm.loadedCount, count + modifier, "Loaded Count");
|
||||
// TKUnit.assertEqual(vm.unloadedCount, count, "Unloaded Count");
|
||||
// }
|
||||
|
||||
// helper.buildUIAndRunTest(listView, testAction);
|
||||
//}
|
||||
export function createTestCase(): ListViewTest {
|
||||
return new ListViewTest();
|
||||
}
|
@ -165,10 +165,10 @@ class ScrollLayoutTest extends testModule.UITest<scrollViewModule.ScrollView> {
|
||||
// No synchronous change.
|
||||
TKUnit.assertEqual(this.testView.verticalOffset, 0, "this.testView.verticalOffset");
|
||||
|
||||
TKUnit.waitUntilReady(() => { return TKUnit.areClose(this.testView.verticalOffset, 100, 0.1); });
|
||||
TKUnit.waitUntilReady(() => { return TKUnit.areClose(this.testView.verticalOffset, 100, 0.9); });
|
||||
|
||||
// The scrolling animation should be finished by now
|
||||
TKUnit.assertAreClose(this.testView.verticalOffset, 100, 0.1, "this.testView.verticalOffset");
|
||||
TKUnit.assertAreClose(this.testView.verticalOffset, 100, 0.9, "this.testView.verticalOffset");
|
||||
}
|
||||
|
||||
public test_scrollToHorizontalOffset_no_animation() {
|
||||
@ -206,10 +206,10 @@ class ScrollLayoutTest extends testModule.UITest<scrollViewModule.ScrollView> {
|
||||
// No synchronous change.
|
||||
TKUnit.assertEqual(this.testView.horizontalOffset, 0, "this.testView.horizontalOffset");
|
||||
|
||||
TKUnit.waitUntilReady(() => { return TKUnit.areClose(this.testView.horizontalOffset, 100, 0.1); });
|
||||
TKUnit.waitUntilReady(() => { return TKUnit.areClose(this.testView.horizontalOffset, 100, 0.9); });
|
||||
|
||||
// The scrolling animation should be finished by now
|
||||
TKUnit.assertAreClose(this.testView.horizontalOffset, 100, 0.1, "this.testView.horizontalOffset");
|
||||
TKUnit.assertAreClose(this.testView.horizontalOffset, 100, 0.9, "this.testView.horizontalOffset");
|
||||
}
|
||||
|
||||
public test_scrollView_persistsState_vertical() {
|
||||
|
@ -1,23 +1,24 @@
|
||||
import definition = require("fps-meter/fps-native");
|
||||
|
||||
class FrameHandlerImpl extends NSObject {
|
||||
static new(): FrameHandlerImpl {
|
||||
return <FrameHandlerImpl>super.new();
|
||||
}
|
||||
|
||||
private _owner: FPSCallback;
|
||||
private _owner: WeakRef<FPSCallback>;
|
||||
|
||||
public initWithOwner(owner: FPSCallback): FrameHandlerImpl {
|
||||
this._owner = owner;
|
||||
return this;
|
||||
public static initWithOwner(owner: WeakRef<FPSCallback>): FrameHandlerImpl {
|
||||
let handler = <FrameHandlerImpl>FrameHandlerImpl.new();
|
||||
handler._owner = owner;
|
||||
return handler;
|
||||
}
|
||||
|
||||
public handleFrame(sender: CADisplayLink): void {
|
||||
this._owner._handleFrame(sender);
|
||||
let owner = this._owner.get();
|
||||
if (owner) {
|
||||
owner._handleFrame(sender);
|
||||
}
|
||||
}
|
||||
|
||||
public static ObjCExposedMethods = {
|
||||
"handleFrame": { returns: interop.types.void, params: [ CADisplayLink ] }
|
||||
"handleFrame": { returns: interop.types.void, params: [CADisplayLink] }
|
||||
};
|
||||
}
|
||||
|
||||
@ -30,7 +31,7 @@ export class FPSCallback implements definition.FPSCallback {
|
||||
constructor(onFrame: (currentTimeMillis: number) => void) {
|
||||
this.onFrame = onFrame;
|
||||
|
||||
this.impl = FrameHandlerImpl.new().initWithOwner(this);
|
||||
this.impl = FrameHandlerImpl.initWithOwner(new WeakRef(this));
|
||||
|
||||
this.displayLink = CADisplayLink.displayLinkWithTargetSelector(this.impl, "handleFrame");
|
||||
this.displayLink.paused = true;
|
||||
|
@ -51,7 +51,7 @@ export class ActionBar extends common.ActionBar {
|
||||
// Set back button text
|
||||
if (previousController) {
|
||||
if (this.navigationButton) {
|
||||
var tapHandler = TapBarItemHandlerImpl.new().initWithOwner(this.navigationButton);
|
||||
var tapHandler = TapBarItemHandlerImpl.initWithOwner(new WeakRef(this.navigationButton));
|
||||
var barButtonItem = UIBarButtonItem.alloc().initWithTitleStyleTargetAction(this.navigationButton.text + "", UIBarButtonItemStyle.UIBarButtonItemStylePlain, tapHandler, "tap");
|
||||
previousController.navigationItem.backBarButtonItem = barButtonItem;
|
||||
}
|
||||
@ -112,7 +112,7 @@ export class ActionBar extends common.ActionBar {
|
||||
}
|
||||
|
||||
private createBarButtonItem(item: dts.ActionItem): UIBarButtonItem {
|
||||
var tapHandler = TapBarItemHandlerImpl.new().initWithOwner(item);
|
||||
var tapHandler = TapBarItemHandlerImpl.initWithOwner(new WeakRef(item));
|
||||
// associate handler with menuItem or it will get collected by JSC.
|
||||
(<any>item).handler = tapHandler;
|
||||
|
||||
@ -129,7 +129,7 @@ export class ActionBar extends common.ActionBar {
|
||||
|
||||
return barButtonItem;
|
||||
}
|
||||
|
||||
|
||||
public _onTitlePropertyChanged() {
|
||||
if (!this.page) {
|
||||
return;
|
||||
@ -141,7 +141,7 @@ export class ActionBar extends common.ActionBar {
|
||||
|
||||
private _navigationBarHeight: number = 0;
|
||||
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number) {
|
||||
|
||||
|
||||
let width = utils.layout.getMeasureSpecSize(widthMeasureSpec);
|
||||
let widthMode = utils.layout.getMeasureSpecMode(widthMeasureSpec);
|
||||
|
||||
@ -186,19 +186,19 @@ export class ActionBar extends common.ActionBar {
|
||||
}
|
||||
|
||||
class TapBarItemHandlerImpl extends NSObject {
|
||||
static new(): TapBarItemHandlerImpl {
|
||||
return <TapBarItemHandlerImpl>super.new();
|
||||
}
|
||||
private _owner: WeakRef<dts.ActionItemBase>;
|
||||
|
||||
private _owner: dts.ActionItemBase;
|
||||
|
||||
public initWithOwner(owner: dts.ActionItemBase): TapBarItemHandlerImpl {
|
||||
this._owner = owner;
|
||||
return this;
|
||||
public static initWithOwner(owner: WeakRef<dts.ActionItemBase>): TapBarItemHandlerImpl {
|
||||
let handler = <TapBarItemHandlerImpl>TapBarItemHandlerImpl.new();
|
||||
handler._owner = owner;
|
||||
return handler;
|
||||
}
|
||||
|
||||
public tap(args) {
|
||||
this._owner._raiseTap();
|
||||
let owner = this._owner.get();
|
||||
if (owner) {
|
||||
owner._raiseTap();
|
||||
}
|
||||
}
|
||||
|
||||
public static ObjCExposedMethods = {
|
||||
|
@ -2,19 +2,19 @@
|
||||
import stateChanged = require("ui/core/control-state-change");
|
||||
|
||||
class TapHandlerImpl extends NSObject {
|
||||
static new(): TapHandlerImpl {
|
||||
return <TapHandlerImpl>super.new();
|
||||
}
|
||||
private _owner: WeakRef<Button>;
|
||||
|
||||
private _owner: Button;
|
||||
|
||||
public initWithOwner(owner: Button): TapHandlerImpl {
|
||||
this._owner = owner;
|
||||
return this;
|
||||
public static initWithOwner(owner: WeakRef<Button>): TapHandlerImpl {
|
||||
let handler = <TapHandlerImpl>TapHandlerImpl.new();
|
||||
handler._owner = owner;
|
||||
return handler;
|
||||
}
|
||||
|
||||
public tap(args) {
|
||||
this._owner._emit(common.Button.tapEvent);
|
||||
let owner = this._owner.get();
|
||||
if (owner) {
|
||||
owner._emit(common.Button.tapEvent);
|
||||
}
|
||||
}
|
||||
|
||||
public static ObjCExposedMethods = {
|
||||
@ -33,7 +33,7 @@ export class Button extends common.Button {
|
||||
super();
|
||||
this._ios = UIButton.buttonWithType(UIButtonType.UIButtonTypeSystem);
|
||||
|
||||
this._tapHandler = TapHandlerImpl.new().initWithOwner(this);
|
||||
this._tapHandler = TapHandlerImpl.initWithOwner(new WeakRef(this));
|
||||
this._ios.addTargetActionForControlEvents(this._tapHandler, "tap", UIControlEvents.UIControlEventTouchUpInside);
|
||||
|
||||
this._stateChangedHandler = new stateChanged.ControlStateChangeListener(this._ios, (s: string) => {
|
||||
|
@ -71,7 +71,7 @@ export class DatePicker extends common.DatePicker {
|
||||
this._ios = new UIDatePicker();
|
||||
this._ios.datePickerMode = UIDatePickerMode.UIDatePickerModeDate;
|
||||
|
||||
this._changeHandler = UIDatePickerChangeHandlerImpl.new().initWithOwner(this);
|
||||
this._changeHandler = UIDatePickerChangeHandlerImpl.initWithOwner(new WeakRef(this));
|
||||
this._ios.addTargetActionForControlEvents(this._changeHandler, "valueChanged", UIControlEvents.UIControlEventValueChanged);
|
||||
}
|
||||
|
||||
@ -81,30 +81,33 @@ export class DatePicker extends common.DatePicker {
|
||||
}
|
||||
|
||||
class UIDatePickerChangeHandlerImpl extends NSObject {
|
||||
static new(): UIDatePickerChangeHandlerImpl {
|
||||
return <UIDatePickerChangeHandlerImpl>super.new();
|
||||
}
|
||||
|
||||
private _owner: DatePicker;
|
||||
private _owner: WeakRef<DatePicker>;
|
||||
|
||||
public initWithOwner(owner: DatePicker): UIDatePickerChangeHandlerImpl {
|
||||
this._owner = owner;
|
||||
return this;
|
||||
public static initWithOwner(owner: WeakRef<DatePicker>): UIDatePickerChangeHandlerImpl {
|
||||
let impl = <UIDatePickerChangeHandlerImpl>UIDatePickerChangeHandlerImpl.new();
|
||||
impl._owner = owner;
|
||||
return impl;
|
||||
}
|
||||
|
||||
public valueChanged(sender: UIDatePicker) {
|
||||
var comps = NSCalendar.currentCalendar().componentsFromDate(NSCalendarUnit.NSCalendarUnitYear | NSCalendarUnit.NSCalendarUnitMonth | NSCalendarUnit.NSCalendarUnitDay, sender.date);
|
||||
|
||||
if (comps.year !== this._owner.year) {
|
||||
this._owner._onPropertyChangedFromNative(common.DatePicker.yearProperty, comps.year);
|
||||
let owner = this._owner.get();
|
||||
if (!owner) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (comps.month !== this._owner.month) {
|
||||
this._owner._onPropertyChangedFromNative(common.DatePicker.monthProperty, comps.month);
|
||||
if (comps.year !== owner.year) {
|
||||
owner._onPropertyChangedFromNative(common.DatePicker.yearProperty, comps.year);
|
||||
}
|
||||
|
||||
if (comps.day !== this._owner.day) {
|
||||
this._owner._onPropertyChangedFromNative(common.DatePicker.dayProperty, comps.day);
|
||||
if (comps.month !== owner.month) {
|
||||
owner._onPropertyChangedFromNative(common.DatePicker.monthProperty, comps.month);
|
||||
}
|
||||
|
||||
if (comps.day !== owner.day) {
|
||||
owner._onPropertyChangedFromNative(common.DatePicker.dayProperty, comps.day);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -235,32 +235,42 @@ export class Frame extends frameCommon.Frame {
|
||||
class UINavigationControllerImpl extends UINavigationController implements UINavigationControllerDelegate {
|
||||
public static ObjCProtocols = [UINavigationControllerDelegate];
|
||||
|
||||
private _owner: Frame;
|
||||
private _owner: WeakRef<Frame>;
|
||||
|
||||
public static initWithOwner(owner: Frame): UINavigationControllerImpl {
|
||||
public static initWithOwner(owner: WeakRef<Frame>): UINavigationControllerImpl {
|
||||
var controller = <UINavigationControllerImpl>UINavigationControllerImpl.new();
|
||||
controller._owner = owner;
|
||||
return controller;
|
||||
}
|
||||
|
||||
get owner(): Frame {
|
||||
return this._owner;
|
||||
return this._owner.get();
|
||||
}
|
||||
|
||||
public viewDidLoad(): void {
|
||||
this._owner.onLoaded();
|
||||
let owner = this._owner.get();
|
||||
if (owner) {
|
||||
owner.onLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
public viewDidLayoutSubviews(): void {
|
||||
trace.write(this._owner + " viewDidLayoutSubviews, isLoaded = " + this._owner.isLoaded, trace.categories.ViewHierarchy);
|
||||
this._owner._updateLayout();
|
||||
let owner = this._owner.get();
|
||||
if (owner) {
|
||||
trace.write(this._owner + " viewDidLayoutSubviews, isLoaded = " + owner.isLoaded, trace.categories.ViewHierarchy);
|
||||
owner._updateLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public navigationControllerWillShowViewControllerAnimated(navigationController: UINavigationController, viewController: UIViewController, animated: boolean): void {
|
||||
// In this method we need to layout the new page otherwise page will be shown empty and update after that which is bad UX.
|
||||
var frame = this._owner;
|
||||
var newEntry: definition.BackstackEntry = viewController[ENTRY];
|
||||
var newPage = newEntry.resolvedPage;
|
||||
let frame = this._owner.get();
|
||||
if (!frame) {
|
||||
return;
|
||||
}
|
||||
|
||||
let newEntry: definition.BackstackEntry = viewController[ENTRY];
|
||||
let newPage = newEntry.resolvedPage;
|
||||
if (!newPage.parent) {
|
||||
if (!frame._currentEntry) {
|
||||
// First navigation
|
||||
@ -282,14 +292,18 @@ class UINavigationControllerImpl extends UINavigationController implements UINav
|
||||
}
|
||||
|
||||
public navigationControllerDidShowViewControllerAnimated(navigationController: UINavigationController, viewController: UIViewController, animated: boolean): void {
|
||||
var frame: Frame = this._owner;
|
||||
var backStack = frame.backStack;
|
||||
var currentEntry = backStack.length > 0 ? backStack[backStack.length - 1] : null;
|
||||
var newEntry: definition.BackstackEntry = viewController[ENTRY];
|
||||
let frame = this._owner.get();
|
||||
if (!frame) {
|
||||
return;
|
||||
}
|
||||
|
||||
let backStack = frame.backStack;
|
||||
let currentEntry = backStack.length > 0 ? backStack[backStack.length - 1] : null;
|
||||
let newEntry: definition.BackstackEntry = viewController[ENTRY];
|
||||
|
||||
// This code check if navigation happened through UI (e.g. back button or swipe gesture).
|
||||
// When calling goBack on frame isBack will be false.
|
||||
var isBack: boolean = currentEntry && newEntry === currentEntry;
|
||||
let isBack: boolean = currentEntry && newEntry === currentEntry;
|
||||
if (isBack) {
|
||||
try {
|
||||
frame._shouldSkipNativePop = true;
|
||||
@ -300,7 +314,7 @@ class UINavigationControllerImpl extends UINavigationController implements UINav
|
||||
}
|
||||
}
|
||||
|
||||
var page = frame.currentPage;
|
||||
let page = frame.currentPage;
|
||||
if (page && !navigationController.viewControllers.containsObject(page.ios)) {
|
||||
frame._removeView(page);
|
||||
}
|
||||
@ -308,7 +322,7 @@ class UINavigationControllerImpl extends UINavigationController implements UINav
|
||||
frame._navigateToEntry = null;
|
||||
frame._currentEntry = newEntry;
|
||||
|
||||
var newPage = newEntry.resolvedPage;
|
||||
let newPage = newEntry.resolvedPage;
|
||||
|
||||
// In iOS we intentionally delay the raising of the 'loaded' event so both platforms behave identically.
|
||||
// The loaded event must be raised AFTER the page is part of the windows hierarchy and
|
||||
@ -337,7 +351,7 @@ class iOSFrame implements definition.iOSFrame {
|
||||
private _navBarVisibility: string;
|
||||
|
||||
constructor(owner: Frame) {
|
||||
this._controller = UINavigationControllerImpl.initWithOwner(owner);
|
||||
this._controller = UINavigationControllerImpl.initWithOwner(new WeakRef(owner));
|
||||
this._controller.delegate = this._controller;
|
||||
this._controller.automaticallyAdjustsScrollViewInsets = false;
|
||||
//this.showNavigationBar = false;
|
||||
@ -356,8 +370,9 @@ class iOSFrame implements definition.iOSFrame {
|
||||
this._showNavigationBar = value;
|
||||
this._controller.navigationBarHidden = !value;
|
||||
|
||||
if (change) {
|
||||
this._controller.owner.requestLayout();
|
||||
let owner = this._controller.owner;
|
||||
if (owner && change) {
|
||||
owner.requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,28 +7,26 @@ import trace = require("trace");
|
||||
global.moduleMerge(common, exports);
|
||||
|
||||
class UIGestureRecognizerImpl extends NSObject {
|
||||
static new(): UIGestureRecognizerImpl {
|
||||
return <UIGestureRecognizerImpl>super.new();
|
||||
}
|
||||
|
||||
private _owner: GesturesObserver;
|
||||
private _owner: WeakRef<GesturesObserver>;
|
||||
private _type: any;
|
||||
private _callback: Function;
|
||||
private _context: any;
|
||||
|
||||
public initWithOwnerTypeCallback(owner: GesturesObserver, type: any, callback?: Function, thisArg?: any): UIGestureRecognizerImpl {
|
||||
this._owner = owner;
|
||||
this._type = type;
|
||||
public static initWithOwnerTypeCallback(owner: WeakRef<GesturesObserver>, type: any, callback?: Function, thisArg?: any): UIGestureRecognizerImpl {
|
||||
let handler = <UIGestureRecognizerImpl>UIGestureRecognizerImpl.new();
|
||||
handler._owner = owner;
|
||||
handler._type = type;
|
||||
|
||||
if (callback) {
|
||||
this._callback = callback;
|
||||
handler._callback = callback;
|
||||
}
|
||||
|
||||
if (thisArg) {
|
||||
this._context = thisArg;
|
||||
handler._context = thisArg;
|
||||
}
|
||||
|
||||
return this;
|
||||
return handler;
|
||||
}
|
||||
|
||||
public static ObjCExposedMethods = {
|
||||
@ -36,17 +34,18 @@ class UIGestureRecognizerImpl extends NSObject {
|
||||
};
|
||||
|
||||
public recognize(recognizer: UIGestureRecognizer): void {
|
||||
var callback = this._callback ? this._callback : this._owner.callback;
|
||||
var type = this._type;
|
||||
var target = this._owner.target;
|
||||
let owner = this._owner.get();
|
||||
let callback = this._callback ? this._callback : (owner ? owner.callback : null);
|
||||
let typeParam = this._type;
|
||||
let target = owner ? owner.target : undefined;
|
||||
|
||||
var args = {
|
||||
type: type,
|
||||
type: typeParam,
|
||||
view: target,
|
||||
ios: recognizer,
|
||||
android: undefined,
|
||||
object: target,
|
||||
eventName: definition.toString(type),
|
||||
eventName: definition.toString(typeParam),
|
||||
};
|
||||
|
||||
if (callback) {
|
||||
@ -209,7 +208,7 @@ export class GesturesObserver extends common.GesturesObserver {
|
||||
}
|
||||
|
||||
function _createUIGestureRecognizerTarget(owner: GesturesObserver, type: definition.GestureTypes, callback?: (args: definition.GestureEventData) => void, thisArg?: any): any {
|
||||
return UIGestureRecognizerImpl.new().initWithOwnerTypeCallback(owner, type, callback, thisArg);
|
||||
return UIGestureRecognizerImpl.initWithOwnerTypeCallback(new WeakRef(owner), type, callback, thisArg);
|
||||
}
|
||||
|
||||
interface RecognizerCache {
|
||||
|
@ -24,35 +24,44 @@ function onTextWrapPropertyChanged(data: dependencyObservable.PropertyChangeData
|
||||
global.moduleMerge(common, exports);
|
||||
|
||||
class UILabelImpl extends UILabel {
|
||||
static new(): UILabelImpl {
|
||||
return <UILabelImpl>super.new();
|
||||
}
|
||||
|
||||
private _owner: Label;
|
||||
private _owner: WeakRef<Label>;
|
||||
|
||||
public initWithOwner(owner: Label): UILabelImpl {
|
||||
this._owner = owner;
|
||||
return this;
|
||||
public static initWithOwner(owner: WeakRef<Label>): UILabelImpl {
|
||||
let labelImpl = <UILabelImpl>UILabelImpl.new();
|
||||
labelImpl._owner = owner;
|
||||
return labelImpl;
|
||||
}
|
||||
|
||||
public textRectForBoundsLimitedToNumberOfLines(bounds: CGRect, numberOfLines: number): CGRect {
|
||||
var rect = super.textRectForBoundsLimitedToNumberOfLines(bounds, numberOfLines);
|
||||
var textRect = CGRectMake(
|
||||
- (this._owner.borderWidth + this._owner.style.paddingLeft),
|
||||
- (this._owner.borderWidth + this._owner.style.paddingTop),
|
||||
rect.size.width + (this._owner.borderWidth + this._owner.style.paddingLeft + this._owner.style.paddingRight + this._owner.borderWidth),
|
||||
rect.size.height + (this._owner.borderWidth + this._owner.style.paddingTop + this._owner.style.paddingBottom + this._owner.borderWidth)
|
||||
let rect = super.textRectForBoundsLimitedToNumberOfLines(bounds, numberOfLines);
|
||||
let owner = this._owner.get();
|
||||
if (owner) {
|
||||
let size = rect.size;
|
||||
rect = CGRectMake(
|
||||
- (owner.borderWidth + owner.style.paddingLeft),
|
||||
- (owner.borderWidth + owner.style.paddingTop),
|
||||
size.width + (owner.borderWidth + owner.style.paddingLeft + owner.style.paddingRight + owner.borderWidth),
|
||||
size.height + (owner.borderWidth + owner.style.paddingTop + owner.style.paddingBottom + owner.borderWidth)
|
||||
);
|
||||
return textRect;
|
||||
}
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
public drawTextInRect(rect: CGRect): void {
|
||||
var textRect = CGRectMake(
|
||||
(this._owner.borderWidth + this._owner.style.paddingLeft),
|
||||
(this._owner.borderWidth + this._owner.style.paddingTop),
|
||||
rect.size.width - (this._owner.borderWidth + this._owner.style.paddingLeft + this._owner.style.paddingRight + this._owner.borderWidth),
|
||||
rect.size.height - (this._owner.borderWidth + this._owner.style.paddingTop + this._owner.style.paddingBottom + this._owner.borderWidth)
|
||||
);
|
||||
let owner = this._owner.get();
|
||||
let textRect: CGRect;
|
||||
let size = rect.size;
|
||||
if (owner) {
|
||||
textRect = CGRectMake((owner.borderWidth + owner.style.paddingLeft), (owner.borderWidth + owner.style.paddingTop),
|
||||
size.width - (owner.borderWidth + owner.style.paddingLeft + owner.style.paddingRight + owner.borderWidth),
|
||||
size.height - (owner.borderWidth + owner.style.paddingTop + owner.style.paddingBottom + owner.borderWidth));
|
||||
}
|
||||
else {
|
||||
textRect = CGRectMake(0, 0, size.width, size.height);
|
||||
}
|
||||
|
||||
super.drawTextInRect(textRect);
|
||||
}
|
||||
}
|
||||
@ -63,8 +72,7 @@ export class Label extends common.Label {
|
||||
constructor(options?: definition.Options) {
|
||||
super(options);
|
||||
|
||||
//this._ios = new UILabel();
|
||||
this._ios = UILabelImpl.new().initWithOwner(this);
|
||||
this._ios = UILabelImpl.initWithOwner(new WeakRef(this));
|
||||
this._ios.userInteractionEnabled = true;
|
||||
}
|
||||
|
||||
|
@ -13,13 +13,8 @@ export class ListPicker extends common.ListPicker {
|
||||
super();
|
||||
|
||||
this._ios = new UIPickerView();
|
||||
|
||||
var dataSource = ListPickerDataSource.new().initWithOwner(this);
|
||||
|
||||
this._dataSource = dataSource;
|
||||
this._ios.dataSource = this._dataSource;
|
||||
|
||||
this._delegate = ListPickerDelegateImpl.new().initWithOwner(this);
|
||||
this._ios.dataSource = this._dataSource = ListPickerDataSource.initWithOwner(new WeakRef(this));
|
||||
this._delegate = ListPickerDelegateImpl.initWithOwner(new WeakRef(this));
|
||||
}
|
||||
|
||||
public onLoaded() {
|
||||
@ -55,16 +50,13 @@ export class ListPicker extends common.ListPicker {
|
||||
|
||||
class ListPickerDataSource extends NSObject implements UIPickerViewDataSource {
|
||||
public static ObjCProtocols = [UIPickerViewDataSource];
|
||||
|
||||
private _owner: WeakRef<ListPicker>;
|
||||
|
||||
static new(): ListPickerDataSource {
|
||||
return <ListPickerDataSource>super.new();
|
||||
}
|
||||
|
||||
private _owner: ListPicker;
|
||||
|
||||
public initWithOwner(owner: ListPicker): ListPickerDataSource {
|
||||
this._owner = owner;
|
||||
return this;
|
||||
public static initWithOwner(owner: WeakRef<ListPicker>): ListPickerDataSource {
|
||||
let dataSource = <ListPickerDataSource>ListPickerDataSource.new();
|
||||
dataSource._owner = owner;
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
public numberOfComponentsInPickerView(pickerView: UIPickerView) {
|
||||
@ -72,35 +64,35 @@ class ListPickerDataSource extends NSObject implements UIPickerViewDataSource {
|
||||
}
|
||||
|
||||
public pickerViewNumberOfRowsInComponent(pickerView: UIPickerView, component: number) {
|
||||
return this._owner.items ? this._owner.items.length : 0;
|
||||
let owner = this._owner.get();
|
||||
return (owner && owner.items) ? owner.items.length : 0;
|
||||
}
|
||||
}
|
||||
|
||||
class ListPickerDelegateImpl extends NSObject implements UIPickerViewDelegate {
|
||||
public static ObjCProtocols = [UIPickerViewDelegate];
|
||||
|
||||
static new(): ListPickerDelegateImpl {
|
||||
return <ListPickerDelegateImpl>super.new();
|
||||
}
|
||||
private _owner: WeakRef<ListPicker>;
|
||||
|
||||
private _owner: ListPicker;
|
||||
|
||||
public initWithOwner(owner: ListPicker): ListPickerDelegateImpl {
|
||||
this._owner = owner;
|
||||
return this;
|
||||
public static initWithOwner(owner: WeakRef<ListPicker>): ListPickerDelegateImpl {
|
||||
let delegate = <ListPickerDelegateImpl>ListPickerDelegateImpl.new();
|
||||
delegate._owner = owner;
|
||||
return delegate;
|
||||
}
|
||||
|
||||
public pickerViewTitleForRowForComponent(pickerView: UIPickerView, row: number, component: number): string {
|
||||
if (this._owner) {
|
||||
return this._owner._getItemAsString(row);
|
||||
let owner = this._owner.get();
|
||||
if (owner) {
|
||||
return owner._getItemAsString(row);
|
||||
}
|
||||
|
||||
return row.toString();
|
||||
}
|
||||
|
||||
public pickerViewDidSelectRowInComponent(pickerView: UIPickerView, row: number, component: number): void {
|
||||
if (this._owner) {
|
||||
this._owner._onPropertyChangedFromNative(common.ListPicker.selectedIndexProperty, row);
|
||||
let owner = this._owner.get();
|
||||
if (owner) {
|
||||
owner._onPropertyChangedFromNative(common.ListPicker.selectedIndexProperty, row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,13 +18,25 @@ global.moduleMerge(common, exports);
|
||||
var infinity = utils.layout.makeMeasureSpec(0, utils.layout.UNSPECIFIED);
|
||||
|
||||
class ListViewCell extends UITableViewCell {
|
||||
static new(): ListViewCell {
|
||||
return <ListViewCell>super.new();
|
||||
public willMoveToSuperview(newSuperview: UIView): void {
|
||||
let parent: ListView = <ListView>(this.view ? this.view.parent : null);
|
||||
|
||||
// When inside ListView and there is no newSuperview this cell is
|
||||
// removed from native visual tree so we remove it from our tree too.
|
||||
if (parent && !newSuperview) {
|
||||
parent._removeContainer(this);
|
||||
}
|
||||
}
|
||||
|
||||
public get view(): view.View {
|
||||
return this.owner ? this.owner.get() : null
|
||||
}
|
||||
|
||||
public owner: WeakRef<view.View>;
|
||||
}
|
||||
|
||||
function notifyForItemAtIndex(listView: definition.ListView, cell: any, eventName: string, indexPath: NSIndexPath) {
|
||||
var args = <definition.ItemEventData>{ eventName: eventName, object: listView, index: indexPath.row, view: cell.view, ios: cell, android: undefined };
|
||||
function notifyForItemAtIndex(listView: definition.ListView, cell: any, view: view.View, eventName: string, indexPath: NSIndexPath) {
|
||||
let args = <definition.ItemEventData>{ eventName: eventName, object: listView, index: indexPath.row, view: view, ios: cell, android: undefined };
|
||||
listView.notify(args);
|
||||
return args;
|
||||
}
|
||||
@ -32,35 +44,35 @@ function notifyForItemAtIndex(listView: definition.ListView, cell: any, eventNam
|
||||
class DataSource extends NSObject implements UITableViewDataSource {
|
||||
public static ObjCProtocols = [UITableViewDataSource];
|
||||
|
||||
static new(): DataSource {
|
||||
return <DataSource>super.new();
|
||||
}
|
||||
private _owner: WeakRef<ListView>;
|
||||
|
||||
private _owner: ListView;
|
||||
|
||||
public initWithOwner(owner: ListView): DataSource {
|
||||
this._owner = owner;
|
||||
return this;
|
||||
public static initWithOwner(owner: WeakRef<ListView>): DataSource {
|
||||
let dataSource = <DataSource>DataSource.new();
|
||||
dataSource._owner = owner;
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
public tableViewNumberOfRowsInSection(tableView: UITableView, section: number) {
|
||||
return this._owner.items ? this._owner.items.length : 0;
|
||||
let owner = this._owner.get();
|
||||
return (owner && owner.items) ? owner.items.length : 0;
|
||||
}
|
||||
|
||||
public tableViewCellForRowAtIndexPath(tableView: UITableView, indexPath: NSIndexPath): UITableViewCell {
|
||||
// We call this method because ...ForIndexPath calls tableViewHeightForRowAtIndexPath immediately (before we can prepare and measure it).
|
||||
var cell = tableView.dequeueReusableCellWithIdentifier(CELLIDENTIFIER) || ListViewCell.new();
|
||||
this._owner._prepareCell(cell, indexPath);
|
||||
let cell = <ListViewCell>(tableView.dequeueReusableCellWithIdentifier(CELLIDENTIFIER) || ListViewCell.new());
|
||||
let owner = this._owner.get();
|
||||
if (owner) {
|
||||
owner._prepareCell(cell, indexPath);
|
||||
|
||||
var cellView: view.View = cell.view;
|
||||
if (cellView) {
|
||||
// Arrange cell views. We do it here instead of _layoutCell because _layoutCell is called
|
||||
// from 'tableViewHeightForRowAtIndexPath' method too (in iOS 7.1) and we don't want to arrange the fake cell.
|
||||
var width = utils.layout.getMeasureSpecSize(this._owner.widthMeasureSpec);
|
||||
var cellHeight = this._owner.getHeight(indexPath.row);
|
||||
view.View.layoutChild(this._owner, cellView, 0, 0, width, cellHeight);
|
||||
let cellView: view.View = cell.view;
|
||||
if (cellView) {
|
||||
// Arrange cell views. We do it here instead of _layoutCell because _layoutCell is called
|
||||
// from 'tableViewHeightForRowAtIndexPath' method too (in iOS 7.1) and we don't want to arrange the fake cell.
|
||||
let width = utils.layout.getMeasureSpecSize(owner.widthMeasureSpec);
|
||||
let cellHeight = owner.getHeight(indexPath.row);
|
||||
view.View.layoutChild(owner, cellView, 0, 0, width, cellHeight);
|
||||
}
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
@ -68,45 +80,52 @@ class DataSource extends NSObject implements UITableViewDataSource {
|
||||
class UITableViewDelegateImpl extends NSObject implements UITableViewDelegate {
|
||||
public static ObjCProtocols = [UITableViewDelegate];
|
||||
|
||||
static new(): UITableViewDelegateImpl {
|
||||
return <UITableViewDelegateImpl>super.new();
|
||||
}
|
||||
private _owner: WeakRef<ListView>;
|
||||
private _measureCell: ListViewCell;
|
||||
|
||||
private _owner: ListView;
|
||||
private _measureCell: UITableViewCell;
|
||||
|
||||
public initWithOwner(owner: ListView): UITableViewDelegateImpl {
|
||||
this._owner = owner;
|
||||
return this;
|
||||
public static initWithOwner(owner: WeakRef<ListView>): UITableViewDelegateImpl {
|
||||
let delegate = <UITableViewDelegateImpl>UITableViewDelegateImpl.new();
|
||||
delegate._owner = owner;
|
||||
return delegate;
|
||||
}
|
||||
|
||||
public tableViewWillDisplayCellForRowAtIndexPath(tableView: UITableView, cell: UITableViewCell, indexPath: NSIndexPath) {
|
||||
if (indexPath.row === this._owner.items.length - 1) {
|
||||
this._owner.notify(<observable.EventData>{ eventName: LOADMOREITEMS, object: this._owner });
|
||||
let owner = this._owner.get();
|
||||
if (owner && (indexPath.row === owner.items.length - 1)) {
|
||||
owner.notify(<observable.EventData>{ eventName: LOADMOREITEMS, object: owner });
|
||||
}
|
||||
}
|
||||
|
||||
public tableViewWillSelectRowAtIndexPath(tableView: UITableView, indexPath: NSIndexPath): NSIndexPath {
|
||||
var cell = tableView.cellForRowAtIndexPath(indexPath);
|
||||
notifyForItemAtIndex(this._owner, cell, ITEMTAP, indexPath);
|
||||
let cell = <ListViewCell>tableView.cellForRowAtIndexPath(indexPath);
|
||||
let owner = this._owner.get();
|
||||
if (owner) {
|
||||
notifyForItemAtIndex(owner, cell, cell.view, ITEMTAP, indexPath);
|
||||
}
|
||||
cell.highlighted = false;
|
||||
return indexPath;
|
||||
}
|
||||
|
||||
public tableViewHeightForRowAtIndexPath(tableView: UITableView, indexPath: NSIndexPath): number {
|
||||
var height = undefined;
|
||||
if (utils.ios.MajorVersion >= 8) {
|
||||
height = this._owner.getHeight(indexPath.row);
|
||||
let owner = this._owner.get();
|
||||
if (!owner) {
|
||||
return 44;
|
||||
}
|
||||
|
||||
let height = undefined;
|
||||
if (utils.ios.MajorVersion >= 8) {
|
||||
height = owner.getHeight(indexPath.row);
|
||||
}
|
||||
|
||||
if (utils.ios.MajorVersion < 8 || height === undefined) {
|
||||
// in iOS 7.1 (or iOS8+ after call to scrollToRowAtIndexPath:atScrollPosition:animated:) this method is called before tableViewCellForRowAtIndexPath so we need fake cell to measure its content.
|
||||
var cell = this._measureCell;
|
||||
let cell = this._measureCell;
|
||||
if (!cell) {
|
||||
this._measureCell = tableView.dequeueReusableCellWithIdentifier(CELLIDENTIFIER) || ListViewCell.new();
|
||||
cell = this._measureCell;
|
||||
}
|
||||
|
||||
height = this._owner._prepareCell(cell, indexPath);
|
||||
height = owner._prepareCell(cell, indexPath);
|
||||
}
|
||||
|
||||
return height;
|
||||
@ -134,7 +153,7 @@ export class ListView extends common.ListView {
|
||||
private _heights: Array<number>;
|
||||
private _preparingCell: boolean = false;
|
||||
private _isDataDirty: boolean = false;
|
||||
|
||||
private _map: Map<ListViewCell, view.View>;
|
||||
widthMeasureSpec: number = 0;
|
||||
|
||||
constructor() {
|
||||
@ -145,14 +164,10 @@ export class ListView extends common.ListView {
|
||||
this._ios.autoresizingMask = UIViewAutoresizing.UIViewAutoresizingNone;
|
||||
this._ios.estimatedRowHeight = DEFAULT_HEIGHT;
|
||||
|
||||
var dataSource = DataSource.new().initWithOwner(this);
|
||||
|
||||
this._dataSource = dataSource;
|
||||
this._ios.dataSource = this._dataSource;
|
||||
|
||||
this._delegate = UITableViewDelegateImpl.new().initWithOwner(this);
|
||||
|
||||
this._ios.dataSource = this._dataSource = DataSource.initWithOwner(new WeakRef(this));
|
||||
this._delegate = UITableViewDelegateImpl.initWithOwner(new WeakRef(this));
|
||||
this._heights = new Array<number>();
|
||||
this._map = new Map<ListViewCell, view.View>();
|
||||
}
|
||||
|
||||
public onLoaded() {
|
||||
@ -225,22 +240,34 @@ export class ListView extends common.ListView {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public _prepareCell(tableCell: UITableViewCell, indexPath: NSIndexPath): number {
|
||||
var cell: any = tableCell;
|
||||
var cellHeight: number;
|
||||
public _prepareCell(cell: ListViewCell, indexPath: NSIndexPath): number {
|
||||
let cellHeight: number;
|
||||
|
||||
try {
|
||||
this._preparingCell = true;
|
||||
if (!cell.view) {
|
||||
cell.view = this._getItemTemplateContent(indexPath.row);
|
||||
let view = cell.view;
|
||||
if (!view) {
|
||||
view = this._getItemTemplateContent(indexPath.row);
|
||||
}
|
||||
|
||||
var args = notifyForItemAtIndex(this, cell, ITEMLOADING, indexPath);
|
||||
var view = cell.view = args.view || this._getDefaultItemContent(indexPath.row);
|
||||
let args = notifyForItemAtIndex(this, cell, view, ITEMLOADING, indexPath);
|
||||
view = args.view || this._getDefaultItemContent(indexPath.row);
|
||||
|
||||
// If cell is reused be have old content - remove it first.
|
||||
if (!cell.view) {
|
||||
cell.owner = new WeakRef(view);
|
||||
}
|
||||
else if (cell.view !== view) {
|
||||
this._removeContainer(cell);
|
||||
(<UIView>cell.view._nativeView).removeFromSuperview();
|
||||
cell.owner = new WeakRef(view);
|
||||
}
|
||||
|
||||
this._prepareItem(view, indexPath.row);
|
||||
if (view && !view.parent && view.ios) {
|
||||
cell.contentView.addSubview(view.ios);
|
||||
this._map.set(cell, view);
|
||||
// We expect that views returned from itemLoading are new (e.g. not reused).
|
||||
if (view && !view.parent && view._nativeView) {
|
||||
cell.contentView.addSubview(view._nativeView);
|
||||
this._addView(view);
|
||||
}
|
||||
|
||||
@ -251,4 +278,9 @@ export class ListView extends common.ListView {
|
||||
}
|
||||
return cellHeight;
|
||||
}
|
||||
|
||||
public _removeContainer(cell: ListViewCell): void {
|
||||
this._removeView(cell.view)
|
||||
this._map.delete(cell);
|
||||
}
|
||||
}
|
||||
|
@ -11,35 +11,30 @@ import observable = require("data/observable");
|
||||
global.moduleMerge(pageCommon, exports);
|
||||
|
||||
class UIViewControllerImpl extends UIViewController {
|
||||
static new(): UIViewControllerImpl {
|
||||
return <UIViewControllerImpl>super.new();
|
||||
}
|
||||
|
||||
private _owner: Page;
|
||||
private _owner: WeakRef<Page>;
|
||||
|
||||
public initWithOwner(owner: Page): UIViewControllerImpl {
|
||||
this._owner = owner;
|
||||
this.automaticallyAdjustsScrollViewInsets = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public didRotateFromInterfaceOrientation(fromInterfaceOrientation: number) {
|
||||
trace.write(this._owner + " didRotateFromInterfaceOrientation(" + fromInterfaceOrientation + ")", trace.categories.ViewHierarchy);
|
||||
}
|
||||
|
||||
public viewDidLoad() {
|
||||
trace.write(this._owner + " viewDidLoad", trace.categories.ViewHierarchy);
|
||||
public static initWithOwner(owner: WeakRef<Page>): UIViewControllerImpl {
|
||||
let controller = <UIViewControllerImpl>UIViewControllerImpl.new();
|
||||
controller._owner = owner;
|
||||
controller.automaticallyAdjustsScrollViewInsets = false;
|
||||
return controller;
|
||||
}
|
||||
|
||||
public viewDidLayoutSubviews() {
|
||||
trace.write(this._owner + " viewDidLayoutSubviews, isLoaded = " + this._owner.isLoaded, trace.categories.ViewHierarchy);
|
||||
if (!this._owner.isLoaded) {
|
||||
let owner = this._owner.get();
|
||||
if (!owner) {
|
||||
return;
|
||||
}
|
||||
|
||||
trace.write(owner + " viewDidLayoutSubviews, isLoaded = " + owner.isLoaded, trace.categories.ViewHierarchy);
|
||||
if (!owner.isLoaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._owner._isModal) {
|
||||
if (owner._isModal) {
|
||||
let isTablet = device.deviceType === DeviceType.Tablet;
|
||||
let isFullScreen = !this._owner._UIModalPresentationFormSheet || !isTablet;
|
||||
let isFullScreen = !owner._UIModalPresentationFormSheet || !isTablet;
|
||||
let frame = isFullScreen ? UIScreen.mainScreen().bounds : this.view.frame;
|
||||
let origin = frame.origin;
|
||||
let size = frame.size;
|
||||
@ -62,7 +57,7 @@ class UIViewControllerImpl extends UIViewController {
|
||||
let bottom = height;
|
||||
let statusBarHeight = uiUtils.ios.getStatusBarHeight();
|
||||
let statusBarVisible = !UIApplication.sharedApplication().statusBarHidden;
|
||||
let backgroundSpanUnderStatusBar = this._owner.backgroundSpanUnderStatusBar;
|
||||
let backgroundSpanUnderStatusBar = owner.backgroundSpanUnderStatusBar;
|
||||
if (statusBarVisible && !backgroundSpanUnderStatusBar) {
|
||||
height -= statusBarHeight;
|
||||
}
|
||||
@ -70,9 +65,9 @@ class UIViewControllerImpl extends UIViewController {
|
||||
let widthSpec = utils.layout.makeMeasureSpec(width, mode);
|
||||
let heightSpec = utils.layout.makeMeasureSpec(height, mode);
|
||||
|
||||
View.measureChild(null, this._owner, widthSpec, heightSpec);
|
||||
View.measureChild(null, owner, widthSpec, heightSpec);
|
||||
let top = ((backgroundSpanUnderStatusBar && isFullScreen) || utils.ios.MajorVersion < 8 || !isFullScreen) ? 0 : statusBarHeight;
|
||||
View.layoutChild(null, this._owner, 0, top, width, bottom);
|
||||
View.layoutChild(null, owner, 0, top, width, bottom);
|
||||
|
||||
if (utils.ios.MajorVersion < 8) {
|
||||
if (!backgroundSpanUnderStatusBar && (!isTablet || isFullScreen)) {
|
||||
@ -88,31 +83,41 @@ class UIViewControllerImpl extends UIViewController {
|
||||
trace.write(this._owner + ", native frame = " + NSStringFromCGRect(this.view.frame), trace.categories.Layout);
|
||||
}
|
||||
else {
|
||||
this._owner._updateLayout();
|
||||
owner._updateLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public viewWillAppear() {
|
||||
trace.write(this._owner + " viewWillAppear", trace.categories.Navigation);
|
||||
this._owner._enableLoadedEvents = true;
|
||||
let owner = this._owner.get();
|
||||
if (!owner) {
|
||||
return;
|
||||
}
|
||||
|
||||
trace.write(owner + " viewWillAppear", trace.categories.Navigation);
|
||||
owner._enableLoadedEvents = true;
|
||||
|
||||
// In iOS we intentionally delay the raising of the 'loaded' event so both platforms behave identically.
|
||||
// The loaded event must be raised AFTER the page is part of the windows hierarchy and
|
||||
// frame.topmost().currentPage is set to the page instance.
|
||||
// https://github.com/NativeScript/NativeScript/issues/779
|
||||
if (!this._owner._isModal) {
|
||||
this._owner._delayLoadedEvent = true;
|
||||
if (!owner._isModal) {
|
||||
owner._delayLoadedEvent = true;
|
||||
}
|
||||
|
||||
this._owner.onLoaded();
|
||||
this._owner._enableLoadedEvents = false;
|
||||
owner.onLoaded();
|
||||
owner._enableLoadedEvents = false;
|
||||
}
|
||||
|
||||
public viewDidDisappear() {
|
||||
trace.write(this._owner + " viewDidDisappear", trace.categories.Navigation);
|
||||
this._owner._enableLoadedEvents = true;
|
||||
this._owner.onUnloaded();
|
||||
this._owner._enableLoadedEvents = false;
|
||||
let owner = this._owner.get();
|
||||
if (!owner) {
|
||||
return;
|
||||
}
|
||||
|
||||
trace.write(owner + " viewDidDisappear", trace.categories.Navigation);
|
||||
owner._enableLoadedEvents = true;
|
||||
owner.onUnloaded();
|
||||
owner._enableLoadedEvents = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,7 +130,7 @@ export class Page extends pageCommon.Page {
|
||||
|
||||
constructor(options?: definition.Options) {
|
||||
super(options);
|
||||
this._ios = UIViewControllerImpl.new().initWithOwner(this);
|
||||
this._ios = UIViewControllerImpl.initWithOwner(new WeakRef(this));
|
||||
}
|
||||
|
||||
public requestLayout(): void {
|
||||
|
@ -57,24 +57,26 @@ global.moduleMerge(common, exports);
|
||||
class UISearchBarDelegateImpl extends NSObject implements UISearchBarDelegate {
|
||||
public static ObjCProtocols = [UISearchBarDelegate];
|
||||
|
||||
static new(): UISearchBarDelegateImpl {
|
||||
return <UISearchBarDelegateImpl>super.new();
|
||||
}
|
||||
|
||||
private _owner: SearchBar;
|
||||
private _owner: WeakRef<SearchBar>;
|
||||
private _searchText: string;
|
||||
|
||||
public initWithOwner(owner: SearchBar): UISearchBarDelegateImpl {
|
||||
this._owner = owner;
|
||||
return this;
|
||||
public static initWithOwner(owner: WeakRef<SearchBar>): UISearchBarDelegateImpl {
|
||||
let delegate = <UISearchBarDelegateImpl>UISearchBarDelegateImpl.new();
|
||||
delegate._owner = owner;
|
||||
return delegate;
|
||||
}
|
||||
|
||||
public searchBarTextDidChange(searchBar: UISearchBar, searchText: string) {
|
||||
this._owner._onPropertyChangedFromNative(common.SearchBar.textProperty, searchText);
|
||||
let owner = this._owner.get();
|
||||
if (!owner) {
|
||||
return;
|
||||
}
|
||||
|
||||
owner._onPropertyChangedFromNative(common.SearchBar.textProperty, searchText);
|
||||
|
||||
// This code is needed since sometimes searchBarCancelButtonClicked is not called!
|
||||
if (searchText === "" && this._searchText !== searchText) {
|
||||
this._owner._emit(common.SearchBar.clearEvent);
|
||||
owner._emit(common.SearchBar.clearEvent);
|
||||
}
|
||||
|
||||
this._searchText = searchText;
|
||||
@ -82,12 +84,22 @@ class UISearchBarDelegateImpl extends NSObject implements UISearchBarDelegate {
|
||||
|
||||
public searchBarCancelButtonClicked(searchBar: UISearchBar) {
|
||||
searchBar.resignFirstResponder();
|
||||
this._owner._emit(common.SearchBar.clearEvent);
|
||||
let owner = this._owner.get();
|
||||
if (!owner) {
|
||||
return;
|
||||
}
|
||||
|
||||
owner._emit(common.SearchBar.clearEvent);
|
||||
}
|
||||
|
||||
public searchBarSearchButtonClicked(searchBar: UISearchBar) {
|
||||
searchBar.resignFirstResponder();
|
||||
this._owner._emit(common.SearchBar.submitEvent);
|
||||
let owner = this._owner.get();
|
||||
if (!owner) {
|
||||
return;
|
||||
}
|
||||
|
||||
owner._emit(common.SearchBar.submitEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,7 +113,7 @@ export class SearchBar extends common.SearchBar {
|
||||
|
||||
this._ios = new UISearchBar();
|
||||
|
||||
this._delegate = UISearchBarDelegateImpl.new().initWithOwner(this);
|
||||
this._delegate = UISearchBarDelegateImpl.initWithOwner(new WeakRef(this));
|
||||
}
|
||||
|
||||
public onLoaded() {
|
||||
@ -121,7 +133,7 @@ export class SearchBar extends common.SearchBar {
|
||||
|
||||
get ios(): UISearchBar {
|
||||
return this._ios;
|
||||
}
|
||||
}
|
||||
|
||||
private static findTextField(view: UIView) {
|
||||
for (let i = 0, l = view.subviews.count; i < l; i++) {
|
||||
|
@ -86,7 +86,7 @@ export class SegmentedBar extends common.SegmentedBar {
|
||||
super();
|
||||
this._ios = UISegmentedControl.new();
|
||||
|
||||
this._selectionHandler = SelectionHandlerImpl.new().initWithOwner(this);
|
||||
this._selectionHandler = SelectionHandlerImpl.initWithOwner(new WeakRef(this));
|
||||
this._ios.addTargetActionForControlEvents(this._selectionHandler, "selected", UIControlEvents.UIControlEventValueChanged);
|
||||
}
|
||||
|
||||
@ -96,22 +96,23 @@ export class SegmentedBar extends common.SegmentedBar {
|
||||
}
|
||||
|
||||
class SelectionHandlerImpl extends NSObject {
|
||||
static new(): SelectionHandlerImpl {
|
||||
return <SelectionHandlerImpl>super.new();
|
||||
}
|
||||
|
||||
private _owner: SegmentedBar;
|
||||
private _owner: WeakRef<SegmentedBar>;
|
||||
|
||||
public initWithOwner(owner: SegmentedBar): SelectionHandlerImpl {
|
||||
this._owner = owner;
|
||||
return this;
|
||||
public static initWithOwner(owner: WeakRef<SegmentedBar>): SelectionHandlerImpl {
|
||||
let handler = <SelectionHandlerImpl>SelectionHandlerImpl.new();
|
||||
handler._owner = owner;
|
||||
return handler;
|
||||
}
|
||||
|
||||
public selected(sender: UISegmentedControl) {
|
||||
this._owner.selectedIndex = sender.selectedSegmentIndex;
|
||||
let owner = this._owner.get();
|
||||
if (owner) {
|
||||
owner.selectedIndex = sender.selectedSegmentIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public static ObjCExposedMethods = {
|
||||
"selected": { returns: interop.types.void, params: [UISegmentedControl] }
|
||||
};
|
||||
}
|
||||
}
|
@ -25,19 +25,20 @@ function onMaxValuePropertyChanged(data: dependencyObservable.PropertyChangeData
|
||||
global.moduleMerge(common, exports);
|
||||
|
||||
class SliderChangeHandlerImpl extends NSObject {
|
||||
static new(): SliderChangeHandlerImpl {
|
||||
return <SliderChangeHandlerImpl>super.new();
|
||||
|
||||
private _owner: WeakRef<Slider>;
|
||||
|
||||
public static initWithOwner(owner: WeakRef<Slider>): SliderChangeHandlerImpl {
|
||||
let handler = <SliderChangeHandlerImpl>SliderChangeHandlerImpl.new();
|
||||
handler._owner = owner;
|
||||
return handler;
|
||||
}
|
||||
|
||||
private _owner: Slider;
|
||||
|
||||
public initWithOwner(owner: Slider): SliderChangeHandlerImpl {
|
||||
this._owner = owner;
|
||||
return this;
|
||||
}
|
||||
|
||||
public sliderValueChanged(sender: UISlider) {
|
||||
this._owner._onPropertyChangedFromNative(common.Slider.valueProperty, sender.value);
|
||||
let owner = this._owner.get();
|
||||
if (owner) {
|
||||
owner._onPropertyChangedFromNative(common.Slider.valueProperty, sender.value);
|
||||
}
|
||||
}
|
||||
|
||||
public static ObjCExposedMethods = {
|
||||
@ -57,7 +58,7 @@ export class Slider extends common.Slider {
|
||||
this._ios.minimumValue = 0;
|
||||
this._ios.maximumValue = this.maxValue;
|
||||
|
||||
this._changeHandler = SliderChangeHandlerImpl.new().initWithOwner(this);
|
||||
this._changeHandler = SliderChangeHandlerImpl.initWithOwner(new WeakRef(this));
|
||||
this._ios.addTargetActionForControlEvents(this._changeHandler, "sliderValueChanged", UIControlEvents.UIControlEventValueChanged);
|
||||
}
|
||||
|
||||
|
@ -13,19 +13,20 @@ function onCheckedPropertyChanged(data: dependencyObservable.PropertyChangeData)
|
||||
global.moduleMerge(common, exports);
|
||||
|
||||
class SwitchChangeHandlerImpl extends NSObject {
|
||||
static new(): SwitchChangeHandlerImpl {
|
||||
return <SwitchChangeHandlerImpl>super.new();
|
||||
}
|
||||
|
||||
private _owner: Switch;
|
||||
private _owner: WeakRef<Switch>;
|
||||
|
||||
public initWithOwner(owner: Switch): SwitchChangeHandlerImpl {
|
||||
this._owner = owner;
|
||||
return this;
|
||||
public static initWithOwner(owner: WeakRef<Switch>): SwitchChangeHandlerImpl {
|
||||
let handler = <SwitchChangeHandlerImpl>SwitchChangeHandlerImpl.new();
|
||||
handler._owner = owner;
|
||||
return handler;
|
||||
}
|
||||
|
||||
public valueChanged(sender: UISwitch) {
|
||||
this._owner._onPropertyChangedFromNative(common.Switch.checkedProperty, sender.on);
|
||||
let owner = this._owner.get();
|
||||
if (owner) {
|
||||
owner._onPropertyChangedFromNative(common.Switch.checkedProperty, sender.on);
|
||||
}
|
||||
}
|
||||
|
||||
public static ObjCExposedMethods = {
|
||||
@ -41,7 +42,7 @@ export class Switch extends common.Switch {
|
||||
super();
|
||||
this._ios = new UISwitch();
|
||||
|
||||
this._handler = SwitchChangeHandlerImpl.new().initWithOwner(this);
|
||||
this._handler = SwitchChangeHandlerImpl.initWithOwner(new WeakRef(this));
|
||||
this._ios.addTargetActionForControlEvents(this._handler, "valueChanged", UIControlEvents.UIControlEventValueChanged);
|
||||
}
|
||||
|
||||
|
@ -13,22 +13,21 @@ import color = require("color");
|
||||
global.moduleMerge(common, exports);
|
||||
|
||||
class UITabBarControllerImpl extends UITabBarController {
|
||||
static new(): UITabBarControllerImpl {
|
||||
return <UITabBarControllerImpl>super.new();
|
||||
}
|
||||
|
||||
private _owner: TabView;
|
||||
private _owner: WeakRef<TabView>;
|
||||
|
||||
public initWithOwner(owner: TabView): UITabBarControllerImpl {
|
||||
this._owner = owner;
|
||||
return this;
|
||||
public static initWithOwner(owner: WeakRef<TabView>): UITabBarControllerImpl {
|
||||
let handler = <UITabBarControllerImpl>UITabBarControllerImpl.new();
|
||||
handler._owner = owner;
|
||||
return handler;
|
||||
}
|
||||
|
||||
public viewDidLayoutSubviews(): void {
|
||||
trace.write("TabView.UITabBarControllerClass.viewDidLayoutSubviews();", trace.categories.Debug);
|
||||
super.viewDidLayoutSubviews();
|
||||
if (this._owner.isLoaded) {
|
||||
this._owner._updateLayout();
|
||||
let owner = this._owner.get();
|
||||
if (owner && owner.isLoaded) {
|
||||
owner._updateLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -36,42 +35,42 @@ class UITabBarControllerImpl extends UITabBarController {
|
||||
class UITabBarControllerDelegateImpl extends NSObject implements UITabBarControllerDelegate {
|
||||
public static ObjCProtocols = [UITabBarControllerDelegate];
|
||||
|
||||
static new(): UITabBarControllerDelegateImpl {
|
||||
return <UITabBarControllerDelegateImpl>super.new();
|
||||
}
|
||||
private _owner: WeakRef<TabView>;
|
||||
|
||||
private _owner: TabView;
|
||||
|
||||
public initWithOwner(owner: TabView): UITabBarControllerDelegateImpl {
|
||||
this._owner = owner;
|
||||
return this;
|
||||
public static initWithOwner(owner: WeakRef<TabView>): UITabBarControllerDelegateImpl {
|
||||
let delegate = <UITabBarControllerDelegateImpl>UITabBarControllerDelegateImpl.new();
|
||||
delegate._owner = owner;
|
||||
return delegate;
|
||||
}
|
||||
|
||||
public tabBarControllerDidSelectViewController(tabBarController: UITabBarController, viewController: UIViewController): void {
|
||||
trace.write("TabView.UITabBarControllerDelegateClass.tabBarControllerDidSelectViewController(" + tabBarController + ", " + viewController + ");", trace.categories.Debug);
|
||||
this._owner._onViewControllerShown(viewController);
|
||||
let owner = this._owner.get();
|
||||
if (owner) {
|
||||
owner._onViewControllerShown(viewController);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class UINavigationControllerDelegateImpl extends NSObject implements UINavigationControllerDelegate {
|
||||
public static ObjCProtocols = [UINavigationControllerDelegate];
|
||||
|
||||
static new(): UINavigationControllerDelegateImpl {
|
||||
return <UINavigationControllerDelegateImpl>super.new();
|
||||
}
|
||||
private _owner: WeakRef<TabView>;
|
||||
|
||||
private _owner: TabView;
|
||||
|
||||
public initWithOwner(owner: TabView): UINavigationControllerDelegateImpl {
|
||||
this._owner = owner;
|
||||
return this;
|
||||
public static initWithOwner(owner: WeakRef<TabView>): UINavigationControllerDelegateImpl {
|
||||
let delegate = <UINavigationControllerDelegateImpl>UINavigationControllerDelegateImpl.new();
|
||||
delegate._owner = owner;
|
||||
return delegate;
|
||||
}
|
||||
|
||||
navigationControllerDidShowViewControllerAnimated(navigationController: UINavigationController, viewController: UIViewController, animated: boolean): void {
|
||||
trace.write("TabView.UINavigationControllerDelegateClass.navigationControllerDidShowViewControllerAnimated(" + navigationController + ", " + viewController + ", " + animated + ");", trace.categories.Debug);
|
||||
// We don't need Edit button in More screen.
|
||||
navigationController.navigationBar.topItem.rightBarButtonItem = null;
|
||||
this._owner._onViewControllerShown(viewController);
|
||||
let owner = this._owner.get();
|
||||
if (owner) {
|
||||
owner._onViewControllerShown(viewController);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,11 +116,9 @@ export class TabView extends common.TabView {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this._ios = UITabBarControllerImpl.new().initWithOwner(this);
|
||||
|
||||
this._delegate = UITabBarControllerDelegateImpl.new().initWithOwner(this);
|
||||
|
||||
this._moreNavigationControllerDelegate = UINavigationControllerDelegateImpl.new().initWithOwner(this);
|
||||
this._ios = UITabBarControllerImpl.initWithOwner(new WeakRef(this));
|
||||
this._delegate = UITabBarControllerDelegateImpl.initWithOwner(new WeakRef(this));
|
||||
this._moreNavigationControllerDelegate = UINavigationControllerDelegateImpl.initWithOwner(new WeakRef(this));
|
||||
//This delegate is set on the last line of _addTabs method.
|
||||
}
|
||||
|
||||
|
@ -16,85 +16,99 @@ global.moduleMerge(common, exports);
|
||||
class UITextFieldDelegateImpl extends NSObject implements UITextFieldDelegate {
|
||||
public static ObjCProtocols = [UITextFieldDelegate];
|
||||
|
||||
static new(): UITextFieldDelegateImpl {
|
||||
return <UITextFieldDelegateImpl>super.new();
|
||||
private _owner: WeakRef<TextField>;
|
||||
private firstEdit: boolean;
|
||||
|
||||
public static initWithOwner(owner: WeakRef<TextField>): UITextFieldDelegateImpl {
|
||||
let delegate = <UITextFieldDelegateImpl>UITextFieldDelegateImpl.new();
|
||||
delegate._owner = owner;
|
||||
return delegate;
|
||||
}
|
||||
|
||||
private _owner: TextField;
|
||||
private firstEdit: boolean;
|
||||
|
||||
public initWithOwner(owner: TextField): UITextFieldDelegateImpl {
|
||||
this._owner = owner;
|
||||
return this;
|
||||
}
|
||||
|
||||
public textFieldShouldBeginEditing(textField: UITextField): boolean {
|
||||
this.firstEdit = true;
|
||||
return this._owner.editable;
|
||||
let owner = this._owner.get();
|
||||
if (owner) {
|
||||
return owner.editable;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public textFieldDidEndEditing(textField: UITextField) {
|
||||
if (this._owner.updateTextTrigger === enums.UpdateTextTrigger.focusLost) {
|
||||
this._owner._onPropertyChangedFromNative(textBase.TextBase.textProperty, textField.text);
|
||||
}
|
||||
let owner = this._owner.get();
|
||||
if (owner) {
|
||||
if (owner.updateTextTrigger === enums.UpdateTextTrigger.focusLost) {
|
||||
owner._onPropertyChangedFromNative(textBase.TextBase.textProperty, textField.text);
|
||||
}
|
||||
|
||||
this._owner.dismissSoftInput();
|
||||
owner.dismissSoftInput();
|
||||
}
|
||||
}
|
||||
|
||||
public textFieldShouldClear(textField: UITextField) {
|
||||
this.firstEdit = false;
|
||||
this._owner._onPropertyChangedFromNative(textBase.TextBase.textProperty, "");
|
||||
let owner = this._owner.get();
|
||||
if (owner) {
|
||||
owner._onPropertyChangedFromNative(textBase.TextBase.textProperty, "");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public textFieldShouldReturn(textField: UITextField): boolean {
|
||||
// Called when the user presses the return button.
|
||||
this._owner.dismissSoftInput();
|
||||
this._owner.notify({ eventName: TextField.returnPressEvent, object: this._owner });
|
||||
let owner = this._owner.get();
|
||||
if (owner) {
|
||||
owner.dismissSoftInput();
|
||||
owner.notify({ eventName: TextField.returnPressEvent, object: owner });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public textFieldShouldChangeCharactersInRangeReplacementString(textField: UITextField, range: NSRange, replacementString: string): boolean {
|
||||
if (this._owner.updateTextTrigger === enums.UpdateTextTrigger.textChanged) {
|
||||
if (textField.secureTextEntry && this.firstEdit) {
|
||||
this._owner._onPropertyChangedFromNative(textBase.TextBase.textProperty, replacementString);
|
||||
}
|
||||
else {
|
||||
var newText = NSString.alloc().initWithString(textField.text).stringByReplacingCharactersInRangeWithString(range, replacementString);
|
||||
this._owner._onPropertyChangedFromNative(textBase.TextBase.textProperty, newText);
|
||||
let owner = this._owner.get();
|
||||
if (owner) {
|
||||
if (owner.updateTextTrigger === enums.UpdateTextTrigger.textChanged) {
|
||||
if (textField.secureTextEntry && this.firstEdit) {
|
||||
owner._onPropertyChangedFromNative(textBase.TextBase.textProperty, replacementString);
|
||||
}
|
||||
else {
|
||||
let newText = NSString.alloc().initWithString(textField.text).stringByReplacingCharactersInRangeWithString(range, replacementString);
|
||||
owner._onPropertyChangedFromNative(textBase.TextBase.textProperty, newText);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.firstEdit = false;
|
||||
|
||||
this.firstEdit = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class UITextFieldImpl extends UITextField {
|
||||
static new(): UITextFieldImpl {
|
||||
return <UITextFieldImpl>super.new();
|
||||
}
|
||||
|
||||
private _owner: TextField;
|
||||
private _owner: WeakRef<TextField>;
|
||||
|
||||
public initWithOwner(owner: TextField): UITextFieldImpl {
|
||||
this._owner = owner;
|
||||
return this;
|
||||
public static initWithOwner(owner: WeakRef<TextField>): UITextFieldImpl {
|
||||
let handler = <UITextFieldImpl>UITextFieldImpl.new();
|
||||
handler._owner = owner;
|
||||
return handler;
|
||||
}
|
||||
|
||||
private _getTextRectForBounds(bounds: CGRect): CGRect {
|
||||
if (!this._owner) {
|
||||
let owner = this._owner ? this._owner.get() : null;
|
||||
|
||||
if (!owner) {
|
||||
return bounds;
|
||||
}
|
||||
|
||||
return CGRectMake(
|
||||
this._owner.borderWidth + this._owner.style.paddingLeft,
|
||||
this._owner.borderWidth + this._owner.style.paddingTop,
|
||||
bounds.size.width - (this._owner.borderWidth + this._owner.style.paddingLeft + this._owner.style.paddingRight + this._owner.borderWidth),
|
||||
bounds.size.height - (this._owner.borderWidth + this._owner.style.paddingTop + this._owner.style.paddingBottom + this._owner.borderWidth)
|
||||
);
|
||||
let size = bounds.size;
|
||||
return CGRectMake(owner.borderWidth + owner.style.paddingLeft, owner.borderWidth + owner.style.paddingTop,
|
||||
size.width - (owner.borderWidth + owner.style.paddingLeft + owner.style.paddingRight + owner.borderWidth),
|
||||
size.height - (owner.borderWidth + owner.style.paddingTop + owner.style.paddingBottom + owner.borderWidth)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public textRectForBounds(bounds: CGRect): CGRect {
|
||||
return this._getTextRectForBounds(bounds);
|
||||
}
|
||||
@ -111,8 +125,8 @@ export class TextField extends common.TextField {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this._ios = UITextFieldImpl.new().initWithOwner(this);
|
||||
this._delegate = UITextFieldDelegateImpl.new().initWithOwner(this);
|
||||
this._ios = UITextFieldImpl.initWithOwner(new WeakRef(this));
|
||||
this._delegate = UITextFieldDelegateImpl.initWithOwner(new WeakRef(this));
|
||||
}
|
||||
|
||||
public onLoaded() {
|
||||
|
@ -38,7 +38,7 @@ export class TimePicker extends common.TimePicker {
|
||||
this._ios = new UIDatePicker();
|
||||
this._ios.datePickerMode = UIDatePickerMode.UIDatePickerModeTime;
|
||||
|
||||
this._changeHandler = UITimePickerChangeHandlerImpl.new().initWithOwner(this);
|
||||
this._changeHandler = UITimePickerChangeHandlerImpl.initWithOwner(new WeakRef(this));
|
||||
this._ios.addTargetActionForControlEvents(this._changeHandler, "valueChanged", UIControlEvents.UIControlEventValueChanged);
|
||||
}
|
||||
|
||||
@ -48,30 +48,33 @@ export class TimePicker extends common.TimePicker {
|
||||
}
|
||||
|
||||
class UITimePickerChangeHandlerImpl extends NSObject {
|
||||
static new(): UITimePickerChangeHandlerImpl {
|
||||
return <UITimePickerChangeHandlerImpl>super.new();
|
||||
}
|
||||
|
||||
private _owner: TimePicker;
|
||||
private _owner: WeakRef<TimePicker>;
|
||||
|
||||
public initWithOwner(owner: TimePicker): UITimePickerChangeHandlerImpl {
|
||||
this._owner = owner;
|
||||
return this;
|
||||
public static initWithOwner(owner: WeakRef<TimePicker>): UITimePickerChangeHandlerImpl {
|
||||
let handler = <UITimePickerChangeHandlerImpl>UITimePickerChangeHandlerImpl.new();
|
||||
handler._owner = owner;
|
||||
return handler;
|
||||
}
|
||||
|
||||
public valueChanged(sender: UIDatePicker) {
|
||||
var comps = NSCalendar.currentCalendar().componentsFromDate(NSCalendarUnit.NSCalendarUnitHour | NSCalendarUnit.NSCalendarUnitMinute, sender.date);
|
||||
|
||||
if (comps.hour !== this._owner.hour) {
|
||||
this._owner._onPropertyChangedFromNative(common.TimePicker.hourProperty, comps.hour);
|
||||
let owner = this._owner.get();
|
||||
if (!owner) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (comps.minute !== this._owner.minute) {
|
||||
this._owner._onPropertyChangedFromNative(common.TimePicker.minuteProperty, comps.minute);
|
||||
var comps = NSCalendar.currentCalendar().componentsFromDate(NSCalendarUnit.NSCalendarUnitHour | NSCalendarUnit.NSCalendarUnitMinute, sender.date);
|
||||
|
||||
if (comps.hour !== owner.hour) {
|
||||
owner._onPropertyChangedFromNative(common.TimePicker.hourProperty, comps.hour);
|
||||
}
|
||||
|
||||
if (comps.minute !== owner.minute) {
|
||||
owner._onPropertyChangedFromNative(common.TimePicker.minuteProperty, comps.minute);
|
||||
}
|
||||
}
|
||||
|
||||
public static ObjCExposedMethods = {
|
||||
'valueChanged': { returns: interop.types.void, params: [UIDatePicker] }
|
||||
};
|
||||
}
|
||||
}
|
@ -6,21 +6,19 @@ global.moduleMerge(common, exports);
|
||||
class UIWebViewDelegateImpl extends NSObject implements UIWebViewDelegate {
|
||||
public static ObjCProtocols = [UIWebViewDelegate];
|
||||
|
||||
static new(): UIWebViewDelegateImpl {
|
||||
return <UIWebViewDelegateImpl>super.new();
|
||||
}
|
||||
private _owner: WeakRef<WebView>;
|
||||
|
||||
private _owner: WebView;
|
||||
|
||||
public initWithOwner(owner: WebView): UIWebViewDelegateImpl {
|
||||
this._owner = owner;
|
||||
return this;
|
||||
public static initWithOwner(owner: WeakRef<WebView>): UIWebViewDelegateImpl {
|
||||
let delegate = <UIWebViewDelegateImpl>UIWebViewDelegateImpl.new();
|
||||
delegate._owner = owner;
|
||||
return delegate;
|
||||
}
|
||||
|
||||
public webViewShouldStartLoadWithRequestNavigationType(webView: UIWebView, request: NSURLRequest, navigationType: number) {
|
||||
if (request.URL) {
|
||||
let owner = this._owner.get();
|
||||
if (owner && request.URL) {
|
||||
trace.write("UIWebViewDelegateClass.webViewShouldStartLoadWithRequestNavigationType(" + request.URL.absoluteString + ", " + navigationType + ")", trace.categories.Debug);
|
||||
this._owner._onLoadStarted(request.URL.absoluteString);
|
||||
owner._onLoadStarted(request.URL.absoluteString);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -32,17 +30,25 @@ class UIWebViewDelegateImpl extends NSObject implements UIWebViewDelegate {
|
||||
|
||||
public webViewDidFinishLoad(webView: UIWebView) {
|
||||
trace.write("UIWebViewDelegateClass.webViewDidFinishLoad(" + webView.request.URL + ")", trace.categories.Debug);
|
||||
this._owner._onLoadFinished(webView.request.URL.absoluteString);
|
||||
let owner = this._owner.get();
|
||||
if (owner) {
|
||||
owner._onLoadFinished(webView.request.URL.absoluteString);
|
||||
}
|
||||
}
|
||||
|
||||
public webViewDidFailLoadWithError(webView: UIWebView, error: NSError) {
|
||||
var url = this._owner.url;
|
||||
if (webView.request && webView.request.URL) {
|
||||
url = webView.request.URL.absoluteString;
|
||||
}
|
||||
let owner = this._owner.get();
|
||||
if (owner) {
|
||||
var url = owner.url;
|
||||
if (webView.request && webView.request.URL) {
|
||||
url = webView.request.URL.absoluteString;
|
||||
}
|
||||
|
||||
trace.write("UIWebViewDelegateClass.webViewDidFailLoadWithError(" + error.localizedDescription + ")", trace.categories.Debug);
|
||||
this._owner._onLoadFinished(url, error.localizedDescription);
|
||||
trace.write("UIWebViewDelegateClass.webViewDidFailLoadWithError(" + error.localizedDescription + ")", trace.categories.Debug);
|
||||
if (owner) {
|
||||
owner._onLoadFinished(url, error.localizedDescription);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,7 +60,7 @@ export class WebView extends common.WebView {
|
||||
super();
|
||||
|
||||
this._ios = new UIWebView();
|
||||
this._delegate = UIWebViewDelegateImpl.new().initWithOwner(this);
|
||||
this._delegate = UIWebViewDelegateImpl.initWithOwner(new WeakRef(this));
|
||||
}
|
||||
|
||||
public onLoaded() {
|
||||
|
Reference in New Issue
Block a user