Switching between TVML and native views in a same app

Is there a way to launch an application by using the TVML framework to handle the list, collections, menus... and then, for a speficic action pushing a native custom view (OpenGL view for example), then going back to the TVML part when the back button clicked?


I supposed that will be based on some events from the js side, but I can't find out how the application can listening events from the js. I only see how to send data to the server by using evalueAppJavaScriptInContext...

Accepted Answer

You will need to use the appController:evaluateAppJavaScriptInContext: method of the TVApplicationControllerDelegate.


Make sure your AppDelegate has a property for your TVApplicationController:


// in your AppDelegate
@property (nonatomic, strong) TVApplicationController * appController;


-(void) appController:(TVApplicationController *)appController evaluateAppJavaScriptInContext:(JSContext *)jsContext {
   
    void (^customObjcBlock)() = ^void() {
        UIViewController * controller = ....;
        [_appController.navigationController pushViewController:controller animated:YES completion:^{
            // ...
         }];
    };
   
    [jsContext setObject:customObjcBlock forKeyedSubscript:@"callObjcMethod"];
}


Then in your javascript just call:


// in your js file
callObjcMethod();



The appController.navigationController acts just like you would expect it to in a regular iOS app. If your app starts up in javascript with a TVML layout, then you call the callObjcMethod(), your native VC will be pushed onto the nagivationController. Then all you have to do is pop the VC from the navigationController and you will be back to the TVML screen that is being backed by javacript.

Thanks for the example of hooking up the Javascript, but I'm still totally unable to get a native view controller to appear when pushed onto the TVApplicationController's navigation stack. (Incidentally, I get a compiler error with your code since the navigation controller doesn't like the 'completion' parameter.)


Anyway, if I remove the 'completion' argument then it compiles and the block is executed but nothing is visibly pushed onto the stack.


EDIT: It actually produces a runtime error:-

pushViewController:animated: called on <_TVAppNavigationController 0x15d01a800> while an existing transition or presentation is occurring; the navigation stack will not be updated.


I also tried pushing a native controller onto the stack straight from the app delegate like this:-


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // context with URL to js
    NSString *baseURL = @"http:/
    NSString *bootURL = [NSString stringWithFormat:@"%@js/application.js", baseURL];
    TVApplicationControllerContext *context = [[TVApplicationControllerContext alloc] init];
    context.javaScriptApplicationURL = [NSURL URLWithString:bootURL];
    context.launchOptions = @{@"BASEURL": baseURL};

    // create window and TVApplicationController
    UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.tvAppController = [[TVApplicationController alloc] initWithContext:context window:window delegate:self];

    // native controller to push
    UIViewController *controller = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"MyViewController"];

    // this presents modally but controller is blurred and behind TVML
    self.tvAppController.navigationController pushViewController:controller animated:YES];
    return YES;
}


This kind of works, in that it does not throw an error, but not properly since the native view is always blurred and behind the TVML view. I've tried pushing after the TVML has loaded, I've tried added delays, I've tried your way, I've tried everything I can think of...


I find it odd that I have to create a new UIWindow for the TVML to show up at all - I tried using the existing window but no joy. I'm having so much trouble with this I'm wondering if I'm fundamentally misunderstanding something about how TVApplicationControlller is supposed to work?

Switching between TVML and native views in a same app
 
 
Q