无法在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()) }
解决方案
核心问题
崩溃的原因有两个:
- SwiftUI的
@State属性必须在主线程更新,你当前在后台队列queue中直接修改myAttitude,触发线程冲突导致崩溃。 - 默认初始化的
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
相关产品推荐
相关产品推荐

