feat: Pass NS app to the native app instead of presenting it over the root VC (#5967)

* feat: Pass NS app native controller to the native app instead of presenting it over the rootViewController

When NativeScript embedded app is created from the native one we check for whether the topmost UIViewController has NativeScriptEmbedder protocol (implemented in the iOS Runtime) method 'presentNativeScriptApp:'. If yes, we call it with the NS app viewcontroller as a parameter so the embedder has control over the NS app (where and how to present it etc.) For backwards compatibility we present the NS app on top of the topmost UIViewController as a fallback.

* style: Fix lint errors

* feat: Check for protocol instead of selector in embedding

I

* Check for rootController instead of topViewController to prevent crash if !rootController

* feat: Introduce NativeScriptEmbedder singleton

NativeScriptEmbedder is responsive for communication between the NS and the native iOS app. His delegate will implement methods which we can call from javascript such as "presentNativeScriptApp:".
This commit is contained in:
Teodor Dermendjiev
2018-06-27 16:48:11 +03:00
committed by GitHub
parent 67f9e060c6
commit 05c2460fc4
9 changed files with 109 additions and 3 deletions

1
.gitignore vendored
View File

@@ -5,6 +5,7 @@ libs
node_modules
package
platforms
!tns-core-modules/platforms
reports
tags

View File

@@ -131,16 +131,26 @@ class IOSApplication implements IOSApplicationDefinition {
// TODO: Expose Window module so that it can we styled from XML & CSS
this._window.backgroundColor = utils.ios.getter(UIColor, UIColor.whiteColor);
this.notifyAppStarted(notification);
}
public notifyAppStarted(notification?: NSNotification) {
const args: LaunchEventData = {
eventName: launchEvent,
object: this,
ios: notification.userInfo && notification.userInfo.objectForKey("UIApplicationLaunchOptionsLocalNotificationKey") || null
ios: notification && notification.userInfo && notification.userInfo.objectForKey("UIApplicationLaunchOptionsLocalNotificationKey") || null
};
notify(args);
notify(<LoadAppCSSEventData>{ eventName: "loadAppCss", object: <any>this, cssFile: getCssFileName() });
this.setWindowContent(args.root);
// this._window will be undefined when NS app is embedded in a native one
if (this._window) {
this.setWindowContent(args.root);
} else {
this._window = UIApplication.sharedApplication.delegate.window;
}
}
@profile
@@ -235,6 +245,7 @@ class IOSApplication implements IOSApplicationDefinition {
this._window.makeKeyAndVisible();
}
}
}
const iosApp = new IOSApplication();
@@ -296,7 +307,14 @@ export function start(entry?: string | NavigationEntry) {
if (rootController) {
const controller = getViewController(rootView);
rootView._setupAsRootView({});
rootController.presentViewControllerAnimatedCompletion(controller, true, null);
let embedderDelegate = NativeScriptEmbedder.sharedInstance().delegate;
if (embedderDelegate) {
embedderDelegate.performSelectorWithObject("presentNativeScriptApp:", controller);
} else {
let visibleVC = utils.ios.getVisibleViewController(rootController);
visibleVC.presentViewControllerAnimatedCompletion(controller, true, null);
}
iosApp.notifyAppStarted();
}
}
}

View File

@@ -0,0 +1 @@
## Platform specific native code

View File

@@ -0,0 +1,26 @@
//
// NativeScriptEmbedder.h
// NativeScript
//
// Created by Teodor Dermendzhiev on 6/19/18.
//
#include <UIKit/UIKit.h>
// When embedding NativeScript application embedder needs to conform to this protocol
// in order to have control over the NativeScript UIViewController
// otherwise NativeScript application is presented over the topmost UIViewController.
@protocol NativeScriptEmbedderDelegate
- (id)presentNativeScriptApp:(UIViewController*)vc;
@end
@interface NativeScriptEmbedder : NSObject
@property(nonatomic, retain, readonly) id<NativeScriptEmbedderDelegate> delegate;
+ (NativeScriptEmbedder *)sharedInstance;
- (void)setDelegate:(id <NativeScriptEmbedderDelegate>)aDelegate;
@end

View File

@@ -0,0 +1,19 @@
#import "NativeScriptEmbedder.h"
@implementation NativeScriptEmbedder
+ (NativeScriptEmbedder *)sharedInstance {
static NativeScriptEmbedder *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[NativeScriptEmbedder alloc] init];
});
return sharedInstance;
}
- (void)setDelegate:(id <NativeScriptEmbedderDelegate>)aDelegate {
_delegate = aDelegate;
}
@end

View File

@@ -0,0 +1,4 @@
module NativeScriptEmbedder {
header "NativeScriptEmbedder.h"
export *
}

View File

@@ -160,3 +160,14 @@ interface Array<T> {
//Dialogs
declare function alert(message?: any): void;
declare function confirm(message?: string): boolean;
// Embedding
declare interface NativeScriptEmbedderDelegate /* NSObject */ {
presentNativeScriptApp(any/* UIViewController*/): any;
performSelectorWithObject(string, any): any;
}
declare class NativeScriptEmbedder {
public static sharedInstance(): NativeScriptEmbedder;
public delegate: NativeScriptEmbedderDelegate;
}

View File

@@ -247,6 +247,13 @@ export module ios {
* iOS - this folder is read-only and contains the app and all its resources.
*/
export function getCurrentAppPath(): string;
/**
* Gets the currently visible(topmost) UIViewController.
* @param rootViewController The root UIViewController instance to start searching from (normally window.rootViewController).
* Returns the visible UIViewController.
*/
export function getVisibleViewController(rootViewController: any/* UIViewController*/ ): any/* UIViewController*/;
}
/**

View File

@@ -116,6 +116,25 @@ export module ios {
return NSString.stringWithString(NSString.pathWithComponents(<any>paths)).stringByStandardizingPath;
}
export function getVisibleViewController(rootViewController: UIViewController): UIViewController {
if (rootViewController.presentedViewController) {
return getVisibleViewController(rootViewController.presentedViewController);
}
if (rootViewController.isKindOfClass(UINavigationController.class())) {
return getVisibleViewController((<UINavigationController>rootViewController).visibleViewController);
}
if (rootViewController.isKindOfClass(UITabBarController.class())) {
let selectedTab = (<UITabBarController>rootViewController).selectedViewController;
return getVisibleViewController(<UITabBarController>rootViewController);
}
return rootViewController;
}
}
export function GC() {