You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无法在TextView中显示CMMotionManager.attitude值?SwiftUI技术求助

问题描述

我的应用依赖iPhone的握持姿态,需使用CoreMotion框架的.attitude属性,仅在用户配置应用时短时间读取设备姿态。尝试了拉取和推送两种运动数据获取方式,但均无法将CMAttitude值存储为可在闭包外访问的形式:

  • manager.startDeviceMotionUpdates闭包内的print语句可正常输出姿态数据
  • 但无法在闭包外访问.attitude值
  • 若尝试用TextView显示姿态数据,Xcode预览及真机运行的应用都会崩溃

原代码

import SwiftUI
import CoreMotion

let manager = CMMotionManager()
let queue = OperationQueue()

struct ContentView: View {
   
   @State var myAttitude: CMAttitude = CMAttitude()
   
   var body: some View {
      VStack {
         Image(systemName: "globe")
            .imageScale(.large)
            .foregroundStyle(.tint)
         Text("Hello, world!")
         Button(action: {
            
            guard manager.isDeviceMotionAvailable else { return }
            
            manager.startDeviceMotionUpdates(to: queue) { (motion, error) in
               guard let motion else {
                  return
               }
               
               let currentAttitude = motion.attitude
               myAttitude = currentAttitude
               print("🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢")
               print("Attitude-Pitch: \(myAttitude)")//  ←←← These Work Fine!
               print("Attitude-Pitch: \(myAttitude.pitch.formatted(.number.precision(.fractionLength(5))))")
               print("Attitude-Yaw: \(myAttitude.yaw.formatted(.number.precision(.fractionLength(5))))")
               print("Attitude-Roll: \(myAttitude.roll.formatted(.number.precision(.fractionLength(5))))")
               print("🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴")
            }
         }, label: {
            Text("Check Your Attitude")
               .foregroundStyle(.cyan)
         })
         Text("Attitude-Pitch: \(myAttitude)") //  ←←← App Crashes Here!
//         Text("Attitude-Pitch: \(myAttitude.pitch.formatted(.number.precision(.fractionLength(5))))")
//         Text("Attitude-Yaw: \(myAttitude.yaw.formatted(.number.precision(.fractionLength(5))))")
//         Text("Attitude-Roll: \(myAttitude.roll.formatted(.number.precision(.fractionLength(5))))")
      }
      .padding()
   }
}

#Preview {
   ContentView(myAttitude: CMAttitude())
}
解决方案

核心问题

崩溃的原因有两个:

  1. SwiftUI的@State属性必须在主线程更新,你当前在后台队列queue中直接修改myAttitude,触发线程冲突导致崩溃。
  2. 默认初始化的CMAttitude是无效状态,直接在Text中展示会引发未定义行为。

修复后代码

import SwiftUI
import CoreMotion

let manager = CMMotionManager()

struct ContentView: View {
    // 使用可选类型,避免无效初始值
    @State private var myAttitude: CMAttitude?
    
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text("Hello, world!")
            
            Button(action: {
                guard manager.isDeviceMotionAvailable else { return }
                
                // 设置更新间隔,控制数据获取频率
                manager.deviceMotionUpdateInterval = 0.1
                
                manager.startDeviceMotionUpdates(to: .global()) { motion, error in
                    guard let motion else { return }
                    
                    // 切换回主线程更新State,符合SwiftUI线程要求
                    DispatchQueue.main.async {
                        myAttitude = motion.attitude
                        print("🟢 姿态数据:\(motion.attitude)")
                    }
                    
                    // 短时间读取后停止更新(这里设置3秒后停止,可根据需求调整)
                    DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
                        manager.stopDeviceMotionUpdates()
                    }
                }
            }, label: {
                Text("Check Your Attitude")
                    .foregroundStyle(.cyan)
            })
            
            // 仅在有有效姿态数据时展示内容,避免空值问题
            if let attitude = myAttitude {
                Text("Pitch: \(attitude.pitch.formatted(.number.precision(.fractionLength(5))))")
                Text("Yaw: \(attitude.yaw.formatted(.number.precision(.fractionLength(5))))")
                Text("Roll: \(attitude.roll.formatted(.number.precision(.fractionLength(5))))")
            } else {
                Text("未读取到姿态数据")
            }
        }
        .padding()
    }
}

#Preview {
    ContentView()
}

关键改动说明

  • 将myAttitude改为可选类型CMAttitude?,避免无效初始值导致的展示异常
  • 更新@State时通过DispatchQueue.main.async切换到主线程,严格遵循SwiftUI的线程规则
  • 添加停止更新的逻辑,符合"短时间读取"的需求,避免不必要的资源消耗
  • 展示部分增加空值判断,仅在获取到有效数据时显示姿态信息

内容的提问来源于stack exchange,提问作者HirsuteJim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 06:41:01