Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/url/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何将TVML/JavaScriptCore连接到UIKit/Objective-C(Swift)?_Objective C_Tvos_Javascriptcore - Fatal编程技术网

如何将TVML/JavaScriptCore连接到UIKit/Objective-C(Swift)?

如何将TVML/JavaScriptCore连接到UIKit/Objective-C(Swift)?,objective-c,tvos,javascriptcore,Objective C,Tvos,Javascriptcore,到目前为止,tvOS支持两种制作电视应用程序的方法,TVML和UIKit,并且没有官方提到如何将TVML(基本上是XML)用户界面与应用程序逻辑和I/O(如播放、流媒体、iCloud持久性等)的本机计数器部分结合起来 那么,在新的tvOS应用程序中混合TVML和UIKit的最佳解决方案是什么 在下面的文章中,我尝试了一个解决方案,下面是从Apple论坛改编的代码片段,以及有关JavaScriptCore到ObjC/Swift绑定的相关问题。 这是Swift项目中的一个简单包装器类 import

到目前为止,
tvOS
支持两种制作电视应用程序的方法,TVML和UIKit,并且没有官方提到如何将TVML(基本上是XML)用户界面与应用程序逻辑和I/O(如播放、流媒体、iCloud持久性等)的本机计数器部分结合起来

那么,在新的
tvOS
应用程序中混合
TVML
UIKit
的最佳解决方案是什么

在下面的文章中,我尝试了一个解决方案,下面是从Apple论坛改编的代码片段,以及有关JavaScriptCore到ObjC/Swift绑定的相关问题。 这是Swift项目中的一个简单包装器类

import UIKit
import TVMLKit
@objc protocol MyJSClass : JSExport {
    func getItem(key:String) -> String?
    func setItem(key:String, data:String)
}
class MyClass: NSObject, MyJSClass {
    func getItem(key: String) -> String? {
        return "String value"
    }

    func setItem(key: String, data: String) {
        print("Set key:\(key) value:\(data)")
    }
}
其中,委托必须符合
tApplicationControllerDelegate

typealias TVApplicationDelegate = AppDelegate
extension TVApplicationDelegate : TVApplicationControllerDelegate {

    func appController(appController: TVApplicationController, evaluateAppJavaScriptInContext jsContext: JSContext) {
        let myClass: MyClass = MyClass();
        jsContext.setObject(myClass, forKeyedSubscript: "objectwrapper");
    }

    func appController(appController: TVApplicationController, didFailWithError error: NSError) {
        let title = "Error Launching Application"
        let message = error.localizedDescription
        let alertController = UIAlertController(title: title, message: message, preferredStyle:.Alert ) self.appController?.navigationController.presentViewController(alertController, animated: true, completion: { () -> Void in
            })
        }

    func appController(appController: TVApplicationController, didStopWithOptions options: [String : AnyObject]?) {
    }

    func appController(appController: TVApplicationController, didFinishLaunchingWithOptions options: [String : AnyObject]?) {
    }
}
在这一点上,javascript非常简单。查看带有命名参数的方法,您将需要更改javascript计数器部件方法名称:

   App.onLaunch = function(options) {
       var text = objectwrapper.getItem()
        // keep an eye here, the method name it changes when you have named parameters, you need camel case for parameters:      
       objectwrapper.setItemData("test", "value")
 }

App. onExit = function() {
        console.log('App finished');
    }
现在,假设您有一个非常复杂的js接口可以像导出一样导出

@protocol MXMJSProtocol<JSExport>
- (void)boot:(JSValue *)status network:(JSValue*)network user:(JSValue*)c3;
- (NSString*)getVersion;
@end
@interface MXMJSObject : NSObject<MXMJSProtocol>
@end
@implementation MXMJSObject
- (NSString*)getVersion {
  return @"0.0.1";
}
此时,在JS计数器部件中,您将不会执行驼峰情况:

objectwrapper.bootNetworkUser(statusChanged,networkChanged,userChanged)
但你要做的是:

objectwrapper.boot(statusChanged,networkChanged,userChanged)
最后,再次查看此界面:

- (void)boot:(JSValue *)status network:(JSValue*)network user:(JSValue*)c3;
传入的值JSValue*。是在
ObjC/Swift
JavaScriptCore
之间传递完成处理程序的一种方法。此时,在本机代码中,您可以使用参数执行所有调用:

dispatch_async(dispatch_get_main_queue(), ^{
                                           NSNumber *state  = [NSNumber numberWithInteger:status];
                                           [networkChanged.context[@"setTimeout"]
                                            callWithArguments:@[networkChanged, @0, state]];
                                       });
在我的发现中,我已经看到,如果不在主线程上进行调度和异步,主线程将挂起。因此,我将调用调用完成处理程序回调的javascript“setTimeout”调用

因此,我在这里使用的方法是:

  • 使用
    JSExportAs
    获取带有命名参数的方法的car,并避免使用callMyParam1Param2Param3等大小写javascript对应项
  • 使用
    JSValue
    作为参数来摆脱完成处理程序。在本机端使用callWithArguments。在JS端使用javascript函数
  • dispatch\u async
    用于完成处理程序,可能在JavaScript端调用setTimeout 0-delayed,以避免UI冻结
[更新] 为了更清楚,我更新了这个问题。我正在寻找一种桥接
TVML
UIKit
的技术解决方案,以便

  • 使用
    JavaScriptCode
  • 拥有从
    JavaScriptCore
    ObjectiveC
    的正确桥接,以及 维切维萨
  • Objective-C

你激发了一个想法,几乎奏效了。显示本机视图后,目前还没有直接的方法将基于TVML的视图推送到导航堆栈上。我现在所做的是:

let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.appController?.navigationController.popViewControllerAnimated(true)
dispatch_async(dispatch_get_main_queue()) {
    tvmlContext!.evaluateScript("showTVMLView()")
}
…然后在JavaScript方面:

function showTVMLView() {setTimeout(function(){_showTVMLView();}, 100);}
function _showTVMLView() {//push the next document onto the stack}
这似乎是将执行从主线程转移到JSVirtualMachine线程并避免UI锁定的最干净的方法。请注意,我必须至少弹出当前的本机视图控制器,否则它将被发送一个致命的选择器。

这解释了如何在JavaScript和Obj-C之间通信

以下是我从Swift到JavaScript的通信方式:

//when pushAlertInJS() is called, pushAlert(title, description) will be called in JavaScript.
func pushAlertInJS(){
    
    //allows us to access the javascript context
    appController!.evaluateInJavaScriptContext({(evaluation: JSContext) -> Void in
        
        //get a handle on the "pushAlert" method that you've implemented in JavaScript
        let pushAlert = evaluation.objectForKeyedSubscript("pushAlert")
        
        //Call your JavaScript method with an array of arguments
        pushAlert.callWithArguments(["Login Failed", "Incorrect Username or Password"])
        
        }, completion: {(Bool) -> Void in
        //evaluation block finished running
    })
}
以下是我如何从JavaScript与Swift通信(它需要在Swift中进行一些设置):

[UPDATE]对于那些想知道javascript端的“pushAlert”是什么样子的人,我将分享一个在application.js中实现的示例

var pushAlert = function(title, description){
   var alert = createAlert(title, description);
   alert.addEventListener("select", Presenter.load.bind(Presenter));
   navigationDocument.pushDocument(alert);
}


// This convenience funnction returns an alert template, which can be used to present errors to the user.

var createAlert = function(title, description) {  

   var alertString = `<?xml version="1.0" encoding="UTF-8" ?>
       <document>
         <alertTemplate>
           <title>${title}</title>
           <description>${description}</description>

         </alertTemplate>
       </document>`

   var parser = new DOMParser();

   var alertDoc = parser.parseFromString(alertString, "application/xml");

   return alertDoc
}
var pushAlert=功能(标题、说明){
var alert=createAlert(标题、说明);
alert.addEventListener(“选择”,Presenter.load.bind(Presenter));
导航文档。推送文档(警报);
}
//此便利功能返回一个警报模板,可用于向用户显示错误。
var createAlert=函数(标题、说明){
var警报字符串=`
${title}
${description}
`
var parser=新的DOMParser();
var-alertDoc=parser.parseFromString(alertString,“application/xml”);
返回alertDoc
}

据我所知,这不是一个问题。如果你发现了一些有用的信息想要分享。此外,我认为本已经或已经提出了这个话题,所以你可能不需要问一个新问题,只需要回答一个现有的问题。@Rickster如果我能找到这个问题的答案,我会回答的,但到目前为止还没有。关于
tvOS
TVML+UIKIT
没有具体问题,所以我不明白你的意思。是的,也许问题不清楚,我可以详细说明。您的回答是“不具建设性的”,因为
tvOS
是一种全新的技术,对Stackoverflow知之甚少。当然这是我的观点,我很确定这个问题是“建设性的”。如果这篇文章不是试图提供信息,实际上是一个问题。。。。不清楚你想问什么。也许你可以修改一下,让问题更清楚。好的@rickster我明白你的意思。我的目标就是这样。谢谢您的帮助。@rickster我已经更新了这个问题,希望现在我也能更清楚地了解这个问题,再次感谢您。因此,您在JavaScriptCore虚拟机中移动了
setTimeout
回调,而不是像上面那样从本机调用它。对,我认为这是一个很好的选择。为什么
evaluateScript
而不是
callWithArguments
?请您发布javascript代码如何查找“pushAlert”。也在你的presenter.js中?@ChrisBrasino如果你在application.js、presenter.js和resourceloader.js中使用苹果的目录示例,我会在application.js中声明“pushAlert”。@ChrisBrasino我已经更新了我的答案以包含js代码,这可能会对你有所帮助。@amok:我的答案解决了你的问题。pushAlert.callWithArguments([“登录失败
//call this method once after setting up your appController.
func createSwiftPrint(){

//allows us to access the javascript context
appController?.evaluateInJavaScriptContext({(evaluation: JSContext) -> Void in

    //this is the block that will be called when javascript calls swiftPrint(str)
    let swiftPrintBlock : @convention(block) (String) -> Void = {
        (str : String) -> Void in

        //prints the string passed in from javascript
        print(str)
    }

    //this creates a function in the javascript context called "swiftPrint". 
    //calling swiftPrint(str) in javascript will call the block we created above.
    evaluation.setObject(unsafeBitCast(swiftPrintBlock, AnyObject.self), forKeyedSubscript: "swiftPrint" as (NSCopying & NSObjectProtocol)?)
    }, completion: {(Bool) -> Void in
    //evaluation block finished running
})
}
var pushAlert = function(title, description){
   var alert = createAlert(title, description);
   alert.addEventListener("select", Presenter.load.bind(Presenter));
   navigationDocument.pushDocument(alert);
}


// This convenience funnction returns an alert template, which can be used to present errors to the user.

var createAlert = function(title, description) {  

   var alertString = `<?xml version="1.0" encoding="UTF-8" ?>
       <document>
         <alertTemplate>
           <title>${title}</title>
           <description>${description}</description>

         </alertTemplate>
       </document>`

   var parser = new DOMParser();

   var alertDoc = parser.parseFromString(alertString, "application/xml");

   return alertDoc
}