SwiftUI视差动效切换视图/打开Sheet时卡顿问题求助
解决CoreMotion视差动效后台持续运行导致的卡顿问题
问题根源
当前实现中,MotionManager在初始化时就直接启动了设备运动更新,即便视图切换到listView或弹出Sheet后,原singleImage视图的运动更新仍在后台持续运行,占用主线程资源,最终引发界面卡顿。
解决方案
通过给MotionManager添加启停控制方法,并结合SwiftUI视图生命周期/场景状态监听,按需启动和停止运动更新,避免资源浪费。
1. 修改MotionManager类
新增startUpdates()和stopUpdates()方法,移除初始化时自动启动更新的逻辑:
import SwiftUI import CoreMotion class MotionManager: ObservableObject { @Published var pitch: Double = 0.0 @Published var roll: Double = 0.0 private var manager: CMMotionManager init() { self.manager = CMMotionManager() self.manager.deviceMotionUpdateInterval = 1/90 } // 启动运动更新 func startUpdates() { guard !manager.isDeviceMotionActive else { return } manager.startDeviceMotionUpdates(to: .main) { [weak self] motionData, error in guard let self = self, error == nil, let motionData = motionData else { if let error = error { print(error) } return } let newPitch = motionData.attitude.pitch let newRoll = motionData.attitude.roll if abs(newPitch - self.pitch) > 0.01 || abs(newRoll - self.roll) > 0.01 { self.pitch = newPitch self.roll = newRoll } } } // 停止运动更新 func stopUpdates() { guard manager.isDeviceMotionActive else { return } manager.stopDeviceMotionUpdates() } }
2. 控制PostView中的更新生命周期
在singleImage布局视图中,通过onAppear/onDisappear触发启停;同时监听场景状态,处理Sheet弹出时的更新暂停:
struct PostView: View { enum LayoutType { case singleImage, listView } var post: SomeTest var layoutType: LayoutType @Binding var isSheetPresented: Bool // 假设Sheet的展示状态由外部绑定 @Environment(\.scenePhase) private var scenePhase @StateObject private var motionManager = MotionManager() var body: some View { switch layoutType { case .singleImage: VStack { ZStack(alignment: .topLeading) { displayImageView(for: post) .modifier(ParallaxMotionModifier(manager: motionManager, magnitude: 15)) .drawingGroup() .frame(width: 370, height: 570) .scaleEffect(1.1) .cornerRadius(20) .clipped() .frame(width: 350, height: 550) .cornerRadius(20) .shadow(color: .black, radius: 6, x: 0, y: 0) } } .onAppear { motionManager.startUpdates() } .onDisappear { motionManager.stopUpdates() } // 监听Sheet展示状态,弹出时暂停更新 .onChange(of: isSheetPresented) { isPresented in isPresented ? motionManager.stopUpdates() : motionManager.startUpdates() } // 监听应用场景状态,后台时暂停更新 .onChange(of: scenePhase) { newPhase in if newPhase == .background { motionManager.stopUpdates() } else if newPhase == .active && layoutType == .singleImage { motionManager.startUpdates() } } case .listView: // 列表视图内容 Text("列表视图") } } func displayImageView(for post: SomeTest) -> some View { Image(post.imageName) .resizable() .scaledToFill() } }
关键优化点
- 仅在
singleImage视图可见时启动运动更新,视图消失时立即停止 - Sheet弹出时暂停更新,关闭后恢复,避免后台无效计算
- 监听应用场景状态,进入后台时自动停止更新,减少资源占用
内容的提问来源于stack exchange,提问作者xyz
相关产品推荐
相关产品推荐

