【发布时间】:2016-01-09 22:59:49
【问题描述】:
到目前为止tvOS 支持两种制作电视应用程序的方法,TVML 和 UIKit,并且没有官方提到如何将东西混合以制作 TVML(基本上是 XML)用户界面与本机对应部分应用逻辑和 I/O(如播放、流式传输、iCloud 持久性等)。
那么,在新的tvOS 应用中混合TVML 和UIKit 的最佳解决方案是什么?
在下文中,我尝试了一个解决方案,该解决方案遵循从 Apple 论坛改编的代码 sn-ps 以及有关 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)")
}
}
委托必须符合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]?) {
}
}
此时的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";
}
你可以这样做
JSExportAs(boot,
- (void)boot:(JSValue *)status network:(JSValue*)network user:(JSValue*)c3 );
此时在JS Counter部分你不会做驼峰式的:
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]];
});
在我的发现中,我发现如果您不在主线程和异步上调度,MainThread 将挂起。所以我将调用调用完成处理程序回调的javascript“setTimeout”调用。
所以我在这里使用的方法是:
- 使用
JSExportAs获取带有命名参数的方法,并避免使用驼峰式javascript 对应物,例如callMyParam1Param2Param3 - 使用
JSValue作为参数来摆脱完成处理程序。在本机端使用 callWithArguments。在JS端使用javascript函数; -
dispatch_async用于完成处理程序,可能在 JavaScript 端调用 setTimeout 0-delayed,以避免 UI 冻结。
[更新]
为了更清楚,我已经更新了这个问题。我正在寻找一种桥接TVML 和UIKit 的技术解决方案,以便
- 通过
JavaScriptCode了解最佳编程模型 - 拥有从
JavaScriptCore到ObjectiveC和 反之亦然 - 从
Objective-C调用JavaScriptCode时表现最佳
【问题讨论】:
-
据我所知,这不是一个问题。如果您发现了一些想要分享的有用信息,ask and answer your own question。另外,我认为这个话题已经在 tvos 或 apple-tvos 的某个地方提出来了,所以你可能不需要问一个新问题,只需回答一个现有的问题。
-
@rickster如果我能找到这个问题的答案,我会回答的,但到目前为止还没有。没有关于
tvOS和TVML + UIKIT的具体问题,所以我不明白你的意思。是的,也许这个问题不清楚,我可以更好地说明。您的回答是“不具建设性的”,因为tvOS是一种全新的技术,对 Stackoverflow 知之甚少。当然这是我的观点,我很确定这个问题无论如何都是“建设性的”。 -
如果这个帖子不是试图提供信息,实际上是一个问题......不清楚你想问什么。也许您可以编辑以使问题更清楚。
-
好的@rickster 我明白你的意思。我的目标是这个。感谢您的帮助。
-
@rickster 我已经更新了这个问题,希望现在对我来说也更清楚了,再次感谢。
标签: objective-c tvos javascriptcore