diff --git a/application/application.d.ts b/application/application.d.ts index 0f247614c..24ee8b781 100644 --- a/application/application.d.ts +++ b/application/application.d.ts @@ -497,10 +497,15 @@ declare module "application" { rootController: UIViewController; /** - * The [UIApplication](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/index.html) object instance provided to the init of the module. + * The [UIApplication](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/index.html). */ nativeApp: UIApplication; + /** + * The [UIApplicationDelegate](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/index.html) class. + */ + delegate: typeof UIApplicationDelegate; + /** * Adds an observer to the default notification center for the specified notification. * For more information, please visit 'https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/#//apple_ref/occ/instm/NSNotificationCenter/addObserver:selector:name:object:' diff --git a/application/application.ios.ts b/application/application.ios.ts index fdd8cbf63..1743505a7 100644 --- a/application/application.ios.ts +++ b/application/application.ios.ts @@ -33,93 +33,6 @@ class Window extends UIWindow { } } -class TNSAppDelegate extends UIResponder implements UIApplicationDelegate { - - // An array of protocols to be implemented by the native class - public static ObjCProtocols = [UIApplicationDelegate]; - - public window: Window; - - applicationDidFinishLaunchingWithOptions(application: UIApplication, launchOptions: NSDictionary): boolean { - this.window = Window.alloc().initWithFrame(UIScreen.mainScreen().bounds); - this.window.backgroundColor = UIColor.whiteColor(); - - if (exports.onLaunch) { - exports.onLaunch(); - } - - exports.notify({ eventName: definition.launchEvent, object: this, ios: launchOptions }); - - var topFrame = frame.topmost(); - if (!topFrame) { - if (mainModule) { - topFrame = new frame.Frame(); - topFrame.navigate(mainModule); - } else { - // TODO: Throw an exception? - // throw new Error("A Frame must be used to navigate to a Page."); - return; - } - } - var app: IOSApplication = exports.ios; - setupOrientationListener(app); - - this.window.content = topFrame; - this.window.rootViewController = topFrame.ios.controller; - app.rootController = this.window.rootViewController; - this.window.makeKeyAndVisible(); - return true; - } - - applicationDidBecomeActive(application: UIApplication) { - if (exports.onResume) { - exports.onResume(); - } - - exports.notify({ eventName: definition.resumeEvent, object: this, ios: application }); - } - - applicationWillResignActive(application: UIApplication) { - // - } - - applicationDidEnterBackground(application: UIApplication) { - if (exports.onSuspend) { - exports.onSuspend(); - } - - exports.notify({ eventName: definition.suspendEvent, object: this, ios: application }); - } - - applicationWillEnterForeground(application: UIApplication) { - // - } - - applicationWillTerminate(application: UIApplication) { - if (exports.onExit) { - exports.onExit(); - } - - exports.notify({ eventName: definition.exitEvent, object: this, ios: application }); - } - - applicationDidReceiveMemoryWarning(application: UIApplication) { - if (exports.onLowMemory) { - exports.onLowMemory(); - } - - exports.notify({ eventName: definition.lowMemoryEvent, object: this, android: undefined, ios: application }); - } - - applicationOpenURLSourceApplicationAnnotation(application: UIApplication, url: NSURL, sourceApplication: string, annotation: any): boolean { - var dictionary = new NSMutableDictionary(); - dictionary.setObjectForKey(url, "TLKApplicationOpenURL"); - dictionary.setObjectForKey(application, "TLKApplication"); - NSNotificationCenter.defaultCenter().postNotificationNameObjectUserInfo("com.telerik.TLKApplicationOpenURL", null, dictionary); - return true; // or should we return false??? - } -} - class NotificationReceiver extends NSObject { private _onReceiveCallback: (notification: NSNotification) => void; @@ -142,14 +55,33 @@ class NotificationReceiver extends NSObject { } class IOSApplication implements definition.iOSApplication { - - public nativeApp: any; public rootController: any; + + private _delegate: typeof UIApplicationDelegate; private _registeredObservers = {}; + private _currentOrientation = UIDevice.currentDevice().orientation; + private _window: Window; constructor() { - // TODO: in iOS there is the singleton instance, while in Android such does not exist hence we pass it as argument - this.nativeApp = UIApplication.sharedApplication(); + this.addNotificationObserver(UIApplicationDidFinishLaunchingNotification, this.didFinishLaunchingWithOptions); + this.addNotificationObserver(UIApplicationDidBecomeActiveNotification, this.didBecomeActive); + this.addNotificationObserver(UIApplicationDidEnterBackgroundNotification, this.didEnterBackground); + this.addNotificationObserver(UIApplicationWillTerminateNotification, this.willTerminate); + this.addNotificationObserver(UIApplicationDidReceiveMemoryWarningNotification, this.didReceiveMemoryWarning); + this.addNotificationObserver(UIDeviceOrientationDidChangeNotification, this.orientationDidChange); + } + + get nativeApp(): UIApplication { + return UIApplication.sharedApplication(); + } + + get delegate(): typeof UIApplicationDelegate { + return this._delegate; + } + set delegate(value: typeof UIApplicationDelegate) { + if (this._delegate !== value) { + this._delegate = value; + } } public addNotificationObserver(notificationName: string, onReceiveCallback: (notification: NSNotification) => void) { @@ -164,19 +96,117 @@ class IOSApplication implements definition.iOSApplication { NSNotificationCenter.defaultCenter().removeObserverNameObject(observer, notificationName, null); } } + + private didFinishLaunchingWithOptions(notification: NSNotification) { + this._window = Window.alloc().initWithFrame(UIScreen.mainScreen().bounds); + this._window.backgroundColor = UIColor.whiteColor(); + + if (exports.onLaunch) { + exports.onLaunch(); + } + + exports.notify({ + eventName: definition.launchEvent, + object: this, + ios: notification.userInfo && notification.userInfo.objectForKey("UIApplicationLaunchOptionsLocalNotificationKey") || null + }); + + var topFrame = frame.topmost(); + if (!topFrame) { + if (mainModule) { + topFrame = new frame.Frame(); + topFrame.navigate(mainModule); + } else { + // TODO: Throw an exception? + // throw new Error("A Frame must be used to navigate to a Page."); + return; + } + } + + this._window.content = topFrame; + + this.rootController = this._window.rootViewController = topFrame.ios.controller; + + this._window.makeKeyAndVisible(); + } + + private didBecomeActive(notification: NSNotification) { + if (exports.onResume) { + exports.onResume(); + } + + exports.notify({ eventName: definition.resumeEvent, object: this, ios: UIApplication.sharedApplication() }); + } + + private didEnterBackground(notification: NSNotification) { + if (exports.onSuspend) { + exports.onSuspend(); + } + + exports.notify({ eventName: definition.suspendEvent, object: this, ios: UIApplication.sharedApplication() }); + } + + private willTerminate(notification: NSNotification) { + if (exports.onExit) { + exports.onExit(); + } + + exports.notify({ eventName: definition.exitEvent, object: this, ios: UIApplication.sharedApplication() }); + } + + private didReceiveMemoryWarning(notification: NSNotification) { + if (exports.onLowMemory) { + exports.onLowMemory(); + } + + exports.notify({ eventName: definition.lowMemoryEvent, object: this, android: undefined, ios: UIApplication.sharedApplication() }); + } + + private orientationDidChange(notification: NSNotification) { + var orientation = UIDevice.currentDevice().orientation; + + if (this._currentOrientation !== orientation) { + this._currentOrientation = orientation; + + var newValue; + switch (orientation) { + case UIDeviceOrientation.UIDeviceOrientationLandscapeRight: + case UIDeviceOrientation.UIDeviceOrientationLandscapeLeft: + newValue = enums.DeviceOrientation.landscape; + break; + case UIDeviceOrientation.UIDeviceOrientationPortrait: + case UIDeviceOrientation.UIDeviceOrientationPortraitUpsideDown: + newValue = enums.DeviceOrientation.portrait; + break; + default: + newValue = enums.DeviceOrientation.unknown; + break; + } + + exports.notify({ + eventName: definition.orientationChangedEvent, + ios: this, + newValue: newValue, + object: this + }); + } + } + } -// TODO: If we have nested require(application) calls we may enter unfinished module state, which will create two delegates, resulting in an exception -var app = new IOSApplication(); -exports.ios = app; +var iosApp = new IOSApplication(); +exports.ios = iosApp; exports.start = function () { + appModule.loadCss(); + try { // The "UIApplicationMain" enters a modal loop and the call will not return while the application is running. // This try-catch block here will catch JavaScript errors but no Objective C ones. // TODO: We need to implement better error handling for our native calls and to use the "error" parameter of the iOS APIs. - UIApplicationMain(0, null, null, "TNSAppDelegate"); + + UIApplicationMain(0, null, null, exports.ios && exports.ios.delegate ? NSStringFromClass(exports.ios.delegate) : NSStringFromClass(UIResponder)); } catch (error) { // At this point the main application loop is exited and no UI May be created. @@ -188,40 +218,4 @@ exports.start = function () { definition.notify({ eventName: definition.uncaughtErrorEvent, object: definition.ios, ios: error }); } -} - -var currentOrientation: number; -function setupOrientationListener(iosApp: IOSApplication) { - iosApp.addNotificationObserver(UIDeviceOrientationDidChangeNotification, onOreintationDidChange) - currentOrientation = UIDevice.currentDevice().orientation; -} - -function onOreintationDidChange(notification: NSNotification) { - var orientation = UIDevice.currentDevice().orientation; - - if (currentOrientation !== orientation) { - currentOrientation = orientation; - - var newValue; - switch (orientation) { - case UIDeviceOrientation.UIDeviceOrientationLandscapeRight: - case UIDeviceOrientation.UIDeviceOrientationLandscapeLeft: - newValue = enums.DeviceOrientation.landscape; - break; - case UIDeviceOrientation.UIDeviceOrientationPortrait: - case UIDeviceOrientation.UIDeviceOrientationPortraitUpsideDown: - newValue = enums.DeviceOrientation.portrait; - break; - default: - newValue = enums.DeviceOrientation.unknown; - break; - } - - exports.notify({ - eventName: definition.orientationChangedEvent, - ios: exports.ios, - newValue: newValue, - object: exports.ios, - }); - } -} +} \ No newline at end of file diff --git a/apps/orientation-demo/main-page.port.xml b/apps/orientation-demo/main-page.port.xml index 5ac9e933c..6f15011e1 100644 --- a/apps/orientation-demo/main-page.port.xml +++ b/apps/orientation-demo/main-page.port.xml @@ -1,5 +1,5 @@ - + diff --git a/apps/orientation-demo/main-page.ts b/apps/orientation-demo/main-page.ts index bd6b6529e..480372ca3 100644 --- a/apps/orientation-demo/main-page.ts +++ b/apps/orientation-demo/main-page.ts @@ -5,14 +5,14 @@ import pageModule = require("ui/page"); var vm = new observable.Observable(); function orientationChanged(data) { console.log("Orientation changed: " + data.newValue); - vm.set("oreintation", data.newValue); + vm.set("orientation", data.newValue); } export function onPageLoaded(args: observable.EventData) { var page = args.object; application.on(application.orientationChangedEvent, orientationChanged, page); page.bindingContext = vm; - vm.set("oreintation", "not changed"); + vm.set("orientation", "not changed"); } export function onPageUnloaded(args: observable.EventData) { diff --git a/apps/tests/app/app.ts b/apps/tests/app/app.ts index 9a5559ac4..bc1bc3cc5 100644 --- a/apps/tests/app/app.ts +++ b/apps/tests/app/app.ts @@ -1,5 +1,30 @@ import application = require("application"); +// Specify custom UIApplicationDelegate. +/* +class MyDelegate extends UIResponder implements UIApplicationDelegate { + public static ObjCProtocols = [UIApplicationDelegate]; + + applicationDidFinishLaunchingWithOptions(application: UIApplication, launchOptions: NSDictionary): boolean { + console.log("applicationWillFinishLaunchingWithOptions: " + launchOptions) + return true; + } + + applicationDidBecomeActive(application: UIApplication): void { + console.log("applicationDidBecomeActive: " + application) + } +} + +application.ios.delegate = MyDelegate; +*/ + +if (application.ios) { + // Observe application notifications. + application.ios.addNotificationObserver(UIApplicationDidFinishLaunchingNotification, (notification: NSNotification) => { + console.log("UIApplicationDidFinishLaunchingNotification: " + notification) + }); +} + application.mainModule = "app/mainPage"; // Common events for both Android and iOS. diff --git a/ios.d.ts b/ios.d.ts index 7cbc0fe37..f5fea120d 100644 --- a/ios.d.ts +++ b/ios.d.ts @@ -20267,7 +20267,7 @@ interface UIAlertViewDelegate { alertViewShouldEnableFirstOtherButton?(alertView: UIAlertView): boolean; } interface UIApplicationDelegate { - window: UIWindow; + window?: UIWindow; applicationDidFinishLaunching?(application: UIApplication): void; applicationWillFinishLaunchingWithOptions?(application: UIApplication, launchOptions: NSDictionary): boolean; applicationDidFinishLaunchingWithOptions?(application: UIApplication, launchOptions: NSDictionary): boolean;