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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 16:12:46