【问题标题】:How do I use Core Motion to output magnetometer data using SwiftUI?如何使用 Core Motion 使用 SwiftUI 输出磁力计数据?
【发布时间】:2020-02-10 11:44:43
【问题描述】:

我在使用 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。

【问题讨论】:

    标签: ios swift swiftui core-motion magnetometer


    【解决方案1】:

    您可以使用 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() })
        }
    }
    

    【讨论】:

      【解决方案2】:

      您的代码有几个问题。

      您的第一个问题是您需要模型数据和视图之间的绑定 - 通过创建绑定,视图将在模型更改时自动更新。

      第二个问题是您只能通过motionManager.magnetometerData 访问磁力计数据一次,而不是通过startMagnetometerUpdates(to:withHandler:) 设置闭包来监控更新。

      您可以使用Combine 框架中的ObservableObject 和视图中的@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@Publishes 它的三个属性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())
          }
      }
      

      请注意,您需要在SceneDelegate.swift 文件中传递MotionManager 的实例:

      let contentView = ContentView(motion: MotionManager())
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-04-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-08
        • 1970-01-01
        • 2011-05-25
        相关资源
        最近更新 更多