没有触觉反馈

3
我的watchos应用依赖于后台的触觉反馈。我在互联网上搜索了很久,只知道如何将内容添加到info.plist中,但不想使用通知作为用户提示,因为这会导致界面过于繁琐。虽然我尝试过锻炼会话,但无法使其正常工作。请问如何让手腕下垂时也能正常使用触觉反馈?最简单的方法就足够了,而我对Swift并不是很熟悉,所以请尽可能详细地解释!以下是我的代码示例,请指导我如何在其中添加后台触觉反馈:

我的计时器:

 _ = Timer.scheduledTimer(timeInterval: 88, target: self, selector: "action", userInfo: nil, repeats: true)

我的行动:

 func action() {
    print("action")
    WKInterfaceDevice.current().play(.success)
     imageObject.setImageNamed("number2")     
}

这是我使用HKWorkout会话的内容,但我完全不知道正在发生什么。

  func workoutSession(_ workoutSession: HKWorkoutSession, didChangeTo toState: HKWorkoutSessionState, from fromState: HKWorkoutSessionState, date: Date) {
    switch toState {
    case .running:
        workoutDidStart(date)
    case .ended:
        workoutDidEnd(date)
    default:
        print("Unexpected state \(toState)")
    }
}

func workoutSession(_ workoutSession: HKWorkoutSession, didFailWithError error: Error) {
    // Do nothing for now
    print("Workout error")
}


func workoutDidStart(_ date : Date) {
    if let query = createHeartRateStreamingQuery(date) {
        self.currenQuery = query
        healthStore.execute(query)
    } else {
        label.setText("cannot start")
    }
}

func workoutDidEnd(_ date : Date) {
    healthStore.stop(self.currenQuery!)
    label.setText("---")
    session = nil
}

// MARK: - Actions
@IBAction func startBtnTapped() {
    if (self.workoutActive) {
        //finish the current workout
        self.workoutActive = false
        self.startStopButton.setTitle("Start")
        if let workout = self.session {
            healthStore.end(workout)
        }
    } else {
        //start a new workout
        self.workoutActive = true
        self.startStopButton.setTitle("Stop")
        _ = Timer.scheduledTimer(timeInterval: 5, target: self, selector: "firsts", userInfo: nil, repeats: false)


        startWorkout()
    }

}

func startWorkout() {

    // If we have already started the workout, then do nothing.
    if (session != nil) {
        return
    }

    // Configure the workout session.
    let workoutConfiguration = HKWorkoutConfiguration()
    workoutConfiguration.activityType = .crossTraining
    workoutConfiguration.locationType = .indoor

    do {
        session = try HKWorkoutSession(configuration: workoutConfiguration)
        session?.delegate = self
    } catch {
        fatalError("Unable to create the workout session!")
    }

    healthStore.start(self.session!)
}

func createHeartRateStreamingQuery(_ workoutStartDate: Date) -> HKQuery? {


    guard let quantityType = HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate) else { return nil }
    let datePredicate = HKQuery.predicateForSamples(withStart: workoutStartDate, end: nil, options: .strictEndDate )
    //let devicePredicate = HKQuery.predicateForObjects(from: [HKDevice.local()])
    let predicate = NSCompoundPredicate(andPredicateWithSubpredicates:[datePredicate])


    let heartRateQuery = HKAnchoredObjectQuery(type: quantityType, predicate: predicate, anchor: nil, limit: Int(HKObjectQueryNoLimit)) { (query, sampleObjects, deletedObjects, newAnchor, error) -> Void in
        //guard let newAnchor = newAnchor else {return}
        //self.anchor = newAnchor
        self.updateHeartRate(sampleObjects)
    }

    heartRateQuery.updateHandler = {(query, samples, deleteObjects, newAnchor, error) -> Void in
        //self.anchor = newAnchor!
        self.updateHeartRate(samples)
    }
    return heartRateQuery
}

func updateHeartRate(_ samples: [HKSample]?) {
    guard let heartRateSamples = samples as? [HKQuantitySample] else {return}

    DispatchQueue.main.async {
        guard let sample = heartRateSamples.first else{return}
        let value = sample.quantity.doubleValue(for: self.heartRateUnit)
        self.label.setText(String(UInt16(value)))

        // retrieve source from sample
        let name = sample.sourceRevision.source.name
        self.updateDeviceName(name)
        self.animateHeart()
    }
}

func updateDeviceName(_ deviceName: String) {
    deviceLabel.setText(deviceName)
}

func animateHeart() {
    self.animate(withDuration: 0.5) {
        self.heart.setWidth(60)
        self.heart.setHeight(90)
    }

    let when = DispatchTime.now() + Double(Int64(0.5 * double_t(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)

    DispatchQueue.global(qos: .default).async {
        DispatchQueue.main.asyncAfter(deadline: when) {
            self.animate(withDuration: 0.5, animations: {
                self.heart.setWidth(50)
                self.heart.setHeight(80)
            })            }


    }
}

我只需要Taptic引擎在后台震动,甚至不需要心率数据或任何其他东西。

2个回答

4
作为我之前回答的补充,我创建了一个示例项目,展示如何在后台触发振动:
要了解如何使用HKWorkoutSession的完整示例,请查看我在GitHub上的示例项目。该示例应用程序将每五秒触发一次振动,即使应用程序在后台运行也是如此。只有当应用程序使用包含HealthKit授权的配置文件签名时,HKWorkoutSession和示例才能正常工作。请确保将开发团队更改为您自己的团队以供所有三个可用目标使用。Xcode将尝试创建所需的配置文件。如果存在任何签名问题或者您使用通配符配置文件,则无法在后台运行。

非常感谢您抽出时间来创建并协助我。享受您的奖励吧!不过我有几个问题。在使用该应用程序时是否可以禁用心率传感器?如何结束一次锻炼?再次感谢您的帮助。 - Mr Big Problem
我有一个更重要的问题,希望你能回答。在这种情况下,使用私有和文件私有函数的意义是什么?如果它是一个私有函数,我该如何停止扩展委托中的计时器:func applicationWillResignActive()? - Mr Big Problem
这只是一个例子。方法不需要是“private”或“fileprivate”。 - naglerrr

2
这只能通过使用HKWorkoutSessions实现。苹果的watchOS应用程序编程指南清楚地指定了可用的后台模式,没有一个不需要HKWorkoutSession,也没有一个可以在后台触发触觉反馈。
使用HKWorkoutSession,您可以实现跟踪用户工作的应用程序。当HKWorkoutSession正在运行时,您的应用程序具有几个特权:

即使用户将手腕放下或与其他应用程序交互,该应用程序仍将在整个锻炼会话中运行。当用户抬起手腕时,应用程序重新出现,让用户快速轻松地检查他们的当前进度和表现。

该应用程序可以在后台继续访问Apple Watch传感器的数据,让您随时保持应用程序的最新状态。例如,跑步应用程序可以继续跟踪用户的心率,确保每当用户抬起手腕时显示最新的心率数据。

该应用程序可以在后台运行时使用音频或触觉反馈警报用户。

要利用HKWorkoutSession并提供触觉反馈,您需要将WKBackgroundModes键和UIBackgroundModes添加到WatchKit扩展的Info.plist中。
<key>WKBackgroundModes</key>
<array>
    <string>workout-processing</string>
</array>
<key>UIBackgroundModes</key>
<array>
    <string>audio</string>
</array>

这里有几个需要注意的问题:

  • 当另一个应用程序启动另一个WKWorkoutSession时,您的会话将被结束。
  • 如果您在后台过度使用资源,则您的应用程序可能会被watchOS挂起。
  • 您的应用程序可能会对填充配对iPhone上的“活动”应用程序中的活动环有贡献。
  • 根据您的应用程序类型,苹果可能会在发布到App Store时拒绝您的应用程序。
  • 需要HealthKit授权(您可以在Xcode的Capabilities视图中添加它)。

有关如何实现HKWorkoutSession的更详细指南,请查看API参考文档

有关如何使用HKWorkoutSession的完整示例,请查看我在GitHub上的示例项目。该示例应用程序将每五秒触发一次振动,即使应用程序在后台运行也是如此。 HKWorkoutSession和样例只能在应用程序由包含HealthKit授权的签名配置文件签名时工作。确保为所有三个可用目标更改开发团队。Xcode将尝试创建所需的签名配置文件。如果出现任何签名问题或者您使用了通配符签名配置文件,则在后台运行将不起作用。


我已经尝试过了,但无法使其正常工作。是否有人可以给我发送一个示例,其中包含仅在后台进行触觉反馈的工作流程? - Mr Big Problem
你的代码在哪里使用了 HKWorkoutSession?你的示例并没有展示出你是否尝试了我在答案中提到的方法。你有尝试使用 HKWorkoutSession 吗? - naglerrr
有什么想法吗,naglerrr? - Mr Big Problem
我在我的答案中更新了一个链接,链接指向我在GitHub上的完整示例项目。 - naglerrr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接