Пока tvOS
поддерживает два способа создания ТВ-приложений, TVML и UIKit, и нет официальных упоминаний о том, как смешивать вещи, чтобы сделать TVML (это в основном XML). Пользовательский интерфейс с собственной частью счетчика для логика приложения и ввод-вывод (например, воспроизведение, потоковая передача, постоянство 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)")
}
}
где делегат должен соответствовать 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
, чтобы взять автомобиль методов с именованными параметрами и избегать похожих на java-скрипт верблюда, таких как callMyParam1Param2Param3 - Используйте
JSValue
как параметр, чтобы избавиться от обработчиков завершения. Используйте callWithArguments с внутренней стороны. Используйте javascript-функции на стороне JS; -
dispatch_async
для обработчиков завершения, возможно, вызвав setTimeout с задержкой 0 на стороне JavaScript, чтобы избежать зависания пользовательского интерфейса.
[ОБНОВЛЕНИЕ]
Я уточнил этот вопрос, чтобы быть более ясным. Я нахожу техническое решение для моста TVML
и UIKit
, чтобы
- Понять лучшую модель программирования с помощью
JavaScriptCode
- Имеем правильный мост от
JavaScriptCore
доObjectiveC
и viceversa - Получите лучшие результаты при вызове
JavaScriptCode
изObjective-C