lor*_*isi 19 objective-c javascriptcore tvos
到目前为止,tvOS支持两种制作电视应用程序的方法,TVML和UIKit,并且没有正式提及如何混合使用TVML(基本上是XML)用户界面和应用程序逻辑的本机计数器部分和I/O(如播放,流媒体,iCloud持久性等).
那么,哪个是混合TVML和UIKit在新tvOS应用中的最佳解决方案?
在下面我尝试了一个解决方案,遵循从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)")
}
}
Run Code Online (Sandbox Code Playgroud)
代表必须遵守TVApplicationControllerDelegate:
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]?) {
}
}
Run Code Online (Sandbox Code Playgroud)
在这一点上,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');
}
Run Code Online (Sandbox Code Playgroud)
现在,假设你有一个非常复杂的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";
}
Run Code Online (Sandbox Code Playgroud)
你可以这样做
JSExportAs(boot,
- (void)boot:(JSValue *)status network:(JSValue*)network user:(JSValue*)c3 );
Run Code Online (Sandbox Code Playgroud)
在JS Counter部分的这一点,你不会做骆驼案:
objectwrapper.bootNetworkUser(statusChanged,networkChanged,userChanged)
Run Code Online (Sandbox Code Playgroud)
但你要做的是:
objectwrapper.boot(statusChanged,networkChanged,userChanged)
Run Code Online (Sandbox Code Playgroud)
最后,再看一下这个界面:
- (void)boot:(JSValue *)status network:(JSValue*)network user:(JSValue*)c3;
Run Code Online (Sandbox Code Playgroud)
传入的值JSValue*是一种在ObjC/Swift和之间传递完成处理程序的方法JavaScriptCore.在本机代码中,您可以使用参数进行调用:
dispatch_async(dispatch_get_main_queue(), ^{
NSNumber *state = [NSNumber numberWithInteger:status];
[networkChanged.context[@"setTimeout"]
callWithArguments:@[networkChanged, @0, state]];
});
Run Code Online (Sandbox Code Playgroud)
在我的发现中,我已经看到如果你不在主线程和异步上发送,MainThread将挂起.所以我将调用调用完成处理程序回调的javascript"setTimeout"调用.
所以我在这里使用的方法是:
JSExportAs采取与命名参数方法车,避免骆驼JavaScript的同行一样callMyParam1Param2Param3JSValue作为参数,以摆脱完成处理的.在本机端使用callWithArguments.在JS端使用javascript函数;dispatch_async 对于完成处理程序,可能在JavaScript端调用setTimeout 0延迟,以避免UI冻结.[更新]
我已经更新了这个问题,以便更清楚.我发现一个技术解决方案弥合TVML和UIKit以
JavaScriptCodeJavaScriptCore来ObjectiveC,反之亦然JavaScriptCode,从Objective-Cshi*_*hip 17
此WWDC视频介绍了如何在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
})
}
Run Code Online (Sandbox Code Playgroud)
以下是我从JavaScript到Swift的通信方式(它需要在Swift中进行一些设置):
//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")
}, completion: {(Bool) -> Void in
//evaluation block finished running
})
}
Run Code Online (Sandbox Code Playgroud)
[更新]对于那些想知道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
}
Run Code Online (Sandbox Code Playgroud)
小智 0
你提出了一个几乎可行的想法。显示本机视图后,目前还没有直接的方法将基于 TVML 的视图推送到导航堆栈上。我此时所做的是:
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.appController?.navigationController.popViewControllerAnimated(true)
dispatch_async(dispatch_get_main_queue()) {
tvmlContext!.evaluateScript("showTVMLView()")
}
Run Code Online (Sandbox Code Playgroud)
...然后在 JavaScript 方面:
function showTVMLView() {setTimeout(function(){_showTVMLView();}, 100);}
function _showTVMLView() {//push the next document onto the stack}
Run Code Online (Sandbox Code Playgroud)
这似乎是将执行从主线程转移到 JSVirtualMachine 线程并避免 UI 锁定的最干净的方法。请注意,我至少必须弹出当前的本机视图控制器,否则它会被发送一个致命的选择器。
| 归档时间: |
|
| 查看次数: |
4310 次 |
| 最近记录: |