问题描述
我需要实现一个WatchKit强制触摸MenuItem
来调用saveWorkout()
方法,该方法位于不属于WKInterfaceController
子类的单独类中.
I need to implement a WatchKit force-touch MenuItem
to call a saveWorkout()
method that is located in a separate class that does not subclass WKInterfaceController
.
我意识到每个类至少需要一个指定的初始化器.我猜这是关键吗?
I realize that every class needs at least one designated initializer. I am guessing this is the key?
顺便说一句,我的" saveSession()已到达"使用sim时,print语句记录到控制台,但当我使用设备时,不会记录.即使使用设备,所有其他打印语句也会记录到控制台.有点奇怪.
Btw, my "saveSession() reached" print statement logs to the console when using the sim but not when I use a device. All other print statements log to the console even when using the device. A bit odd.
我的初始化尝试会引发各种错误,例如:
My attempts at initialization throw various errors such as:
2.调用中缺少参数'context'的参数
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...
推荐答案
致命错误是(或曾经)由以下行引起的:
The fatal error is (or was) caused by this line:
let wSM = WorkoutSessionManager()
该行创建WorkoutSessionManager的新实例,并在其上调用init()
.
That line creates a new instance of WorkoutSessionManager and calls init()
on it.
Swift为任何为其所有属性提供默认值并且不提供至少一个初始化器的结构或类提供一个称为init()
的默认初始化器.但是WorkoutSessionManager不提供healthStore
和workoutSession
属性的默认值(并且这些属性不是可选的),并且它提供了自己的名为init(context:)
的初始化程序,因此它没有默认初始化程序.
Swift provides a default initializer called init()
for any structure or class that provides default values for all of its properties and does not provide at least one initializer itself. But WorkoutSessionManager does not provide default values for the healthStore
and workoutSession
properties (and those properties are not optionals), and it provides its own initializer named init(context:)
, so it has no default initializer.
您需要使用指定的初始化程序init(context:)
(传递适当的WorkoutSessionContext实例)创建WorkoutSessionManager实例,或者为WorkoutSessionManager提供名为init()
的默认初始化程序.
You need to either create your instance of WorkoutSessionManager using the designated initializer init(context:)
(passing an appropriate instance of WorkoutSessionContext) or provide a default initializer for WorkoutSessionManager named init()
.
前者的确切方法取决于其余应用程序的实现以及DashboardController的表示.我假设您正在尝试重新创建 WWDC 2015 Session 203 .
The precise manner in which you should do the former depends on the implementation of the rest of your app and the presentation of your DashboardController. I assume you are trying to recreate the "Fit" app shown in WWDC 2015 Session 203.
在该演示中,初始控制器是ActivityInterfaceController的实例,并且该控制器负责呈现下一个接口(通过在情节提要中创建的脚本).您可以在ActivityInterfaceController类中看到以下代码:
In that demonstration, the initial controller is an instance of ActivityInterfaceController, and that controller is responsible for presenting the next interface (via segues created in the storyboard). You can see the following code in the 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实例创建并返回WorkoutSessionContext的新实例.该功能返回的上下文通过awakeWithContext
传递到相关界面的目标接口控制器.
The function above creates and returns a new instance of WorkoutSessionContext using an instance of HKHealthStore held by the initial controller. The context returned by that function is passed to the destination interface controller for the relevant segue through awakeWithContext
.
对于代码转换,您可以使用等效功能(例如,也会导致awakeWithContext
的pushControllerWithName(context:)
)传递上下文实例.
For transitions in code, you can pass a context instance using equivalent functions such as pushControllerWithName(context:)
which also lead to awakeWithContext
.
如果您的初始控制器与上述类似,则可以在DashboardController类的awakeWithContext
中访问传递的上下文,并使用它来配置WorkoutSessionManager的新实例:
If your initial controller is similar to the above, you can access the passed context in awakeWithContext
in your DashboardController class and use it to configure a new instance of 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的方式.
Creating an instance of WorkoutSessionManager in that way avoids calling the (non-existent) init()
initializer and permits reuse of the HKHealthStore instance. Whether that approach is open to you depends on the rest of your code and the way you are presenting your DashboardController.
请注意,您应该避免创建WorkoutSessionManager的多个实例.使用单个以提供在扩展程序中共享的WorkoutSessionManager的单个实例.
Note that you should avoid creating multiple instances of WorkoutSessionManager. Use a singleton to provide a single instance of WorkoutSessionManager that is shared across your extension.
这篇关于使用WatchKit强制触摸MenuItem调用外部函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!