使用 WatchKit 强制触摸 MenuItem 调用外部函数
Call external function using WatchKit force touch MenuItem
我需要实现一个 WatchKit force-touch MenuItem
来调用一个 saveWorkout()
方法,该方法位于一个单独的 class 中,该方法不 subclass WKInterfaceController
.
我意识到每个 class 至少需要一个指定的初始化器。我猜这是关键?
顺便说一句,我的“saveSession() reached”打印语句在使用 sim 时记录到控制台,但在我使用设备时却没有。即使在使用设备时,所有其他打印语句也会记录到控制台。有点奇怪。
我的初始化尝试抛出各种错误,例如:
1.fatal error: use of unimplemented initializer 'init()' for class 'DashboardController'
2.Missing argument for parameter 'context' in call
Dashboard.swift
class DashboardController: WKInterfaceController {
@IBOutlet var timerLabel: WKInterfaceTimer!
@IBOutlet weak var milesLabel: WKInterfaceLabel!
// var wSM: WorkoutSessionManager
//init(wSM: WorkoutSessionManager) {
// self.wSM = wSM
// super.init()
// }
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
addMenuItemWithItemIcon(.Accept, title: "Save", action: #selector(DashboardController.saveSession))
}
override func willActivate() {
super.willActivate()
print("Dashboard controller reached")
}
func saveSession() {
//wSM.saveWorkout()
print("saveSession() reached")
}
WorkoutSessionManager.swift
class WorkoutSessionContext {
let healthStore: HKHealthStore
let activityType: HKWorkoutActivityType
let locationType: HKWorkoutSessionLocationType
init(healthStore: HKHealthStore, activityType: HKWorkoutActivityType = .Other, locationType: HKWorkoutSessionLocationType = .Unknown) {
self.healthStore = healthStore
self.activityType = activityType
self.locationType = locationType
}
}
protocol WorkoutSessionManagerDelegate: class {
// ... protocol methods
}
class WorkoutSessionManager: NSObject, HKWorkoutSessionDelegate {
let healthStore: HKHealthStore
let workoutSession: HKWorkoutSession
init(context: WorkoutSessionContext) {
self.healthStore = context.healthStore
self.workoutSession = HKWorkoutSession(activityType: context.activityType, locationType: context.locationType)
self.currentActiveEnergyQuantity = HKQuantity(unit: self.energyUnit, doubleValue: 0.0)
self.currentDistanceQuantity = HKQuantity(unit: self.distanceUnit, doubleValue: 0.0)
super.init()
self.workoutSession.delegate = self
}
func saveWorkout() {
guard let startDate = self.workoutStartDate, endDate = self.workoutEndDate else {return}
// ...code...
致命错误是由(或曾经)由这一行引起的:
let wSM = WorkoutSessionManager()
该行创建了一个新的 WorkoutSessionManager 实例并对其调用 init()
。
Swift 为任何结构提供一个名为 init()
的默认初始化器,或 class 为其所有属性提供默认值并且不提供至少一个初始化器本身。但是 WorkoutSessionManager 没有为 healthStore
和 workoutSession
属性提供默认值(并且这些属性不是可选的),并且它提供了自己的名为 init(context:)
的初始化程序,因此它没有默认初始化程序。
您需要使用指定的初始化程序 init(context:)
(传递适当的 WorkoutSessionContext 实例)创建您的 WorkoutSessionManager 实例,或者为名为 init()
.
的 WorkoutSessionManager 提供默认初始化程序
您执行前者的确切方式取决于您的应用程序其余部分的实现和您的 DashboardController 的呈现。我假设您正在尝试重新创建 WWDC 2015 Session 203.
中显示的 "Fit" 应用程序
在该演示中,初始控制器是 ActivityInterfaceController 的一个实例,该控制器负责呈现下一个界面(通过在故事板中创建的 segues)。在ActivityInterfaceController中可以看到如下代码class:
override func contextForSegueWithIdentifier(segueIdentifier: String) -> AnyObject? {
let activityType: HKWorkoutActivityType
switch segueIdentifier {
case "Running":
activityType = .Running
case "Walking":
activityType = .Walking
case "Cycling":
activityType = .Cycling
default:
activityType = .Other
}
return WorkoutSessionContext(healthStore: self.healthStore, activityType: activityType)
}
上面的函数使用初始控制器持有的 HKHealthStore 实例创建并 returns 一个新的 WorkoutSessionContext 实例。该函数返回的上下文通过 awakeWithContext
.
传递给相关 segue 的目标接口控制器
对于代码中的转换,您可以使用等效函数(例如 pushControllerWithName(context:)
传递上下文实例,这也会导致 awakeWithContext
.
如果您的初始控制器与上述类似,您可以在 DashboardController class 中访问 awakeWithContext
中传递的上下文,并使用它来配置 WorkoutSessionManager 的新实例:
class DashboardController: WKInterfaceController
{
// ...
var wSM: WorkoutSessionManager?
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
if context is WorkoutSessionContext {
wSM = WorkoutSessionManager(context: context as! WorkoutSessionContext)
}
addMenuItemWithItemIcon(.Accept, title: "Save", action: #selector(DashboardController.saveSession))
}
// ...
}
以这种方式创建 WorkoutSessionManager 的实例可避免调用(不存在的)init()
初始化程序并允许重用 HKHealthStore 实例。该方法是否对您开放取决于您的其余代码以及您呈现 DashboardController 的方式。
请注意,您应该避免创建 WorkoutSessionManager 的多个实例。使用 singleton 提供在您的扩展中共享的单个 WorkoutSessionManager 实例。
我需要实现一个 WatchKit force-touch MenuItem
来调用一个 saveWorkout()
方法,该方法位于一个单独的 class 中,该方法不 subclass WKInterfaceController
.
我意识到每个 class 至少需要一个指定的初始化器。我猜这是关键?
顺便说一句,我的“saveSession() reached”打印语句在使用 sim 时记录到控制台,但在我使用设备时却没有。即使在使用设备时,所有其他打印语句也会记录到控制台。有点奇怪。
我的初始化尝试抛出各种错误,例如:
1.fatal error: use of unimplemented initializer 'init()' for class 'DashboardController'
2.Missing argument for parameter 'context' in call
Dashboard.swift
class DashboardController: WKInterfaceController {
@IBOutlet var timerLabel: WKInterfaceTimer!
@IBOutlet weak var milesLabel: WKInterfaceLabel!
// var wSM: WorkoutSessionManager
//init(wSM: WorkoutSessionManager) {
// self.wSM = wSM
// super.init()
// }
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
addMenuItemWithItemIcon(.Accept, title: "Save", action: #selector(DashboardController.saveSession))
}
override func willActivate() {
super.willActivate()
print("Dashboard controller reached")
}
func saveSession() {
//wSM.saveWorkout()
print("saveSession() reached")
}
WorkoutSessionManager.swift
class WorkoutSessionContext {
let healthStore: HKHealthStore
let activityType: HKWorkoutActivityType
let locationType: HKWorkoutSessionLocationType
init(healthStore: HKHealthStore, activityType: HKWorkoutActivityType = .Other, locationType: HKWorkoutSessionLocationType = .Unknown) {
self.healthStore = healthStore
self.activityType = activityType
self.locationType = locationType
}
}
protocol WorkoutSessionManagerDelegate: class {
// ... protocol methods
}
class WorkoutSessionManager: NSObject, HKWorkoutSessionDelegate {
let healthStore: HKHealthStore
let workoutSession: HKWorkoutSession
init(context: WorkoutSessionContext) {
self.healthStore = context.healthStore
self.workoutSession = HKWorkoutSession(activityType: context.activityType, locationType: context.locationType)
self.currentActiveEnergyQuantity = HKQuantity(unit: self.energyUnit, doubleValue: 0.0)
self.currentDistanceQuantity = HKQuantity(unit: self.distanceUnit, doubleValue: 0.0)
super.init()
self.workoutSession.delegate = self
}
func saveWorkout() {
guard let startDate = self.workoutStartDate, endDate = self.workoutEndDate else {return}
// ...code...
致命错误是由(或曾经)由这一行引起的:
let wSM = WorkoutSessionManager()
该行创建了一个新的 WorkoutSessionManager 实例并对其调用 init()
。
Swift 为任何结构提供一个名为 init()
的默认初始化器,或 class 为其所有属性提供默认值并且不提供至少一个初始化器本身。但是 WorkoutSessionManager 没有为 healthStore
和 workoutSession
属性提供默认值(并且这些属性不是可选的),并且它提供了自己的名为 init(context:)
的初始化程序,因此它没有默认初始化程序。
您需要使用指定的初始化程序 init(context:)
(传递适当的 WorkoutSessionContext 实例)创建您的 WorkoutSessionManager 实例,或者为名为 init()
.
您执行前者的确切方式取决于您的应用程序其余部分的实现和您的 DashboardController 的呈现。我假设您正在尝试重新创建 WWDC 2015 Session 203.
中显示的 "Fit" 应用程序在该演示中,初始控制器是 ActivityInterfaceController 的一个实例,该控制器负责呈现下一个界面(通过在故事板中创建的 segues)。在ActivityInterfaceController中可以看到如下代码class:
override func contextForSegueWithIdentifier(segueIdentifier: String) -> AnyObject? {
let activityType: HKWorkoutActivityType
switch segueIdentifier {
case "Running":
activityType = .Running
case "Walking":
activityType = .Walking
case "Cycling":
activityType = .Cycling
default:
activityType = .Other
}
return WorkoutSessionContext(healthStore: self.healthStore, activityType: activityType)
}
上面的函数使用初始控制器持有的 HKHealthStore 实例创建并 returns 一个新的 WorkoutSessionContext 实例。该函数返回的上下文通过 awakeWithContext
.
对于代码中的转换,您可以使用等效函数(例如 pushControllerWithName(context:)
传递上下文实例,这也会导致 awakeWithContext
.
如果您的初始控制器与上述类似,您可以在 DashboardController class 中访问 awakeWithContext
中传递的上下文,并使用它来配置 WorkoutSessionManager 的新实例:
class DashboardController: WKInterfaceController
{
// ...
var wSM: WorkoutSessionManager?
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
if context is WorkoutSessionContext {
wSM = WorkoutSessionManager(context: context as! WorkoutSessionContext)
}
addMenuItemWithItemIcon(.Accept, title: "Save", action: #selector(DashboardController.saveSession))
}
// ...
}
以这种方式创建 WorkoutSessionManager 的实例可避免调用(不存在的)init()
初始化程序并允许重用 HKHealthStore 实例。该方法是否对您开放取决于您的其余代码以及您呈现 DashboardController 的方式。
请注意,您应该避免创建 WorkoutSessionManager 的多个实例。使用 singleton 提供在您的扩展中共享的单个 WorkoutSessionManager 实例。