SwiftUI中模糊半径为0导致状态栏塌陷的原因及解决方法
SwiftUI 应用从 inactive 回到 active 时状态栏塌陷问题排查与解决
背景
我实现了一个ViewModifier,当scenePhase为.inactive时模糊屏幕,当scenePhase为.background时锁定应用。整体功能正常,但存在一个严重问题:每当应用从屏幕模糊的.inactive状态回到.active状态时,状态栏会塌陷,屏幕内容会向上移入状态栏区域(见下图)。
注意:若将
.active状态的模糊半径从0改为0.00001,一切运行正常。

代码
struct AppLockViewModifier: ViewModifier { @Environment(\.scenePhase) private var scenePhase @ObservedObject private var appLockVM = AppLockViewModel.shared @State private var blurRadius: CGFloat = 16 func body(content: Content) -> some View { Group { if appLockVM.locked { AppLockView() } else { content } } .blur(radius: blurRadius) .onChange(of: scenePhase) { newValue in updateBlurRadius(newValue) } } private func updateBlurRadius(_ scenePhase: ScenePhase) { switch scenePhase { case .active : withAnimation { blurRadius = 0 } case .inactive: withAnimation { blurRadius = 16 } case .background: appLockVM.locked = true @unknown default: print("Unknown") } } }
问题成因
这是SwiftUI的blur修饰符在动画切换半径到0时的底层布局bug。当模糊半径从非0值通过动画变为0时,SwiftUI布局系统会错误计算视图的安全区域Insets,导致内容向上偏移侵入状态栏区域。
设置0.00001能正常工作的原因是:这个极小值让blur修饰符仍处于"激活"状态,布局系统不会触发那个错误的Insets计算逻辑。
解决方案
方式一:用极小值替代0(简单直接)
保留原有动画逻辑,将.active状态的模糊半径设为极小非0值(如0.0001),视觉上无模糊效果,同时规避布局bug:
case .active : withAnimation { blurRadius = 0.0001 }
方式二:条件性应用blur修饰符(更严谨)
不再给视图持续挂载blur,而是根据场景状态动态添加/移除修饰符,从根源避免0值模糊的布局问题:
struct AppLockViewModifier: ViewModifier { @Environment(\.scenePhase) private var scenePhase @ObservedObject private var appLockVM = AppLockViewModel.shared @State private var blurRadius: CGFloat = 16 func body(content: Content) -> some View { Group { if appLockVM.locked { AppLockView() } else { content } } .modifier(BlurModifier(radius: blurRadius)) .onChange(of: scenePhase) { newValue in updateBlurRadius(newValue) } } private func updateBlurRadius(_ scenePhase: ScenePhase) { switch scenePhase { case .active : withAnimation { blurRadius = 0 } case .inactive: withAnimation { blurRadius = 16 } case .background: appLockVM.locked = true @unknown default: print("Unknown") } } // 自定义条件模糊修饰符 private struct BlurModifier: ViewModifier { let radius: CGFloat func body(content: Content) -> some View { if radius > 0 { content.blur(radius: radius) } else { content } } } }
内容的提问来源于stack exchange,提问作者christophriepe
相关产品推荐
相关产品推荐

