Как использовать Core Motion для вывода данных магнитометра с помощью SwiftUI?
У меня возникают проблемы с выводом данных для магнитометра, акселерометра и гироскопа с использованием Core Motion с SwiftUI. Я предполагаю, что моя проблема связана с startMagnetometerUpdates().
Я пробовал использовать исходный код, найденный здесь, при переполнении стека, а также на GitHub/google. Проблема в том, что весь найденный мной код использует UIKit вместо SwiftUI. Можно ли это реализовать без использования UIKit?
import CoreMotion
let motionManager = CMMotionManager()
var x = 0.0; var y = 0.0; var z = 0.0
func magnet() {
motionManager.magnetometerUpdateInterval = 1/60
motionManager.startMagnetometerUpdates()
if let magnetometerData = motionManager.magnetometerData {
x = magnetometerData.magneticField.x
y = magnetometerData.magneticField.y
z = magnetometerData.magneticField.z
}
}
struct Magnetometer: View {
var body: some View {
VStack {
Text("Magnetometer Data")
Text("X: \(x)")
Text("Y: \(y)")
Text("Z: \(z)")
}
}
}
struct Magnetometer_Previews: PreviewProvider {
static var previews: some View {
Magnetometer()
}
}
Выходные данные должны просто отображать значения x, y и z для датчика и обновляться с интервалом 1/60. Текущий вывод составляет 0,00000 для каждого значения, потому что я уже установил для каждой переменной значение 0.
2 ответа
У вас есть пара проблем с вашим кодом.
Ваша первая проблема заключается в том, что вам нужна привязка между данными вашей модели и представлением. Создав привязку, представление будет обновляться автоматически при изменении модели.
Вторая проблема заключается в том, что вы получаете доступ к данным магнитометра только один раз через motionManager.magnetometerData
вместо того, чтобы настраивать закрытие для отслеживания обновлений через startMagnetometerUpdates(to:withHandler:)
.
Вы можете использовать ObservableObject
из Combine
рамки и @ObservedObject
на ваш взгляд, чтобы создать соответствующую привязку.
Начните с создания класса для обертки вашего менеджера движений:
import Foundation
import Combine
import CoreMotion
class MotionManager: ObservableObject {
private var motionManager: CMMotionManager
@Published
var x: Double = 0.0
@Published
var y: Double = 0.0
@Published
var z: Double = 0.0
init() {
self.motionManager = CMMotionManager()
self.motionManager.magnetometerUpdateInterval = 1/60
self.motionManager.startMagnetometerUpdates(to: .main) { (magnetometerData, error) in
guard error == nil else {
print(error!)
return
}
if let magnetData = magnetometerData {
self.x = magnetData.magneticField.x
self.y = magnetData.magneticField.y
self.z = magnetData.magneticField.z
}
}
}
}
Этот класс соответствует ObservableObject
а также @Publish
es его три свойства, x,y и z.
Простое присвоение новых значений этим свойствам при закрытии обновления магнитометра приведет к тому, что издатель запустит и обновит всех наблюдателей.
Теперь, на ваш взгляд, вы можете объявить @ObservedObject
для вашего класса менеджера движения и привяжите свойства.
struct ContentView: View {
@ObservedObject
var motion: MotionManager
var body: some View {
VStack {
Text("Magnetometer Data")
Text("X: \(motion.x)")
Text("Y: \(motion.y)")
Text("Z: \(motion.z)")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(motion: MotionManager())
}
}
Обратите внимание, что вам нужно будет передать экземпляр MotionManager
в вашем SceneDelegate.swift
файл:
let contentView = ContentView(motion: MotionManager())
Вы можете использовать Combine для создания класса для предоставления данных:
import Combine
public class MagneicFieldProvider: NSObject, ObservableObject {
public let objectWillChange = PassthroughSubject<CMMagneticField,Never>()
public private(set) var magneticField: CMMagneticField = CMMagneticField() {
willSet {
objectWillChange.send(newValue)
}
}
deinit {
motionManager.stopMagnetometerUpdates()
}
private let motionManager: CMMotionManager
public override init(){
self.motionManager = CMMotionManager()
super.init()
motionManager.magnetometerUpdateInterval = 1/60
}
public func startUpdates() {
motionManager.startMagnetometerUpdates(to: OperationQueue.main) { this, that in
if let magneticField = self.motionManager.magnetometerData?.magneticField {
self.magneticField = magneticField
}
}
}
}
а затем используйте его в своем представлении:
struct MagnetometerView: View {
@ObservedObject var magnetometer = MagneicFieldProvider()
var body: some View {
VStack(alignment: .leading) {
Text("Magnetometer Data")
Text("X: \(magnetometer.magneticField.x)")
Text("Y: \(magnetometer.magneticField.y)")
Text("Z: \(magnetometer.magneticField.z)")
} .onAppear(perform: { self.magnetometer.startUpdates() })
}
}