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

SwiftUI问题:在LazyVStack和ScrollView中使用AVPlayer时状态栏不显示

SwiftUI中LazyVStack+ScrollView搭配AVPlayer时状态栏消失的问题成因

问题复现代码

import SwiftUI
import AVKit

struct ViewOffsetKey: PreferenceKey {
    typealias Value = CGFloat
    static var defaultValue = CGFloat.zero
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value += nextValue()
    }
}

struct ContentView: View {
    @State var toolbarHeight: CGFloat = 64
    @State var freeScreenHeight: CGFloat = 0
    @State var toolbarOffset: CGFloat = 0
    @State var lastScrollOffset: CGFloat = 0
    @State var startChecking: Bool = false

    @State var player = AVPlayer()
    var videoUrl: String = "someExternal1:1ratioVideo"


    init() {
        UIScrollView.appearance().bounces = false
    }
    
    var body: some View {
             
        ZStack(alignment: .leading){

            VStack{
                
                HStack(spacing: 10){

                    Text("toolbar")

                    Spacer()

                }
                .padding(.horizontal, 12.0)
                .frame(height: toolbarHeight)
                .background(Color.green)
                .padding(.bottom, toolbarOffset)
                .offset(y: toolbarOffset)
                .zIndex(2)
                
                Spacer()
            }
            .clipped()
            .zIndex(2)
            

            ScrollView(.vertical, showsIndicators: false){

                LazyVStack(spacing: 0){
                    
                    VStack{

                    }
                    .frame(width: round(UIScreen.main.bounds.width / 1.5), height: toolbarHeight + 8)

                    ForEach(1..<5) { index in
                        VideoPlayer(player: player)
                            .onAppear{
                                startChecking = true
                            }
                            .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.width)
                                        .padding(.bottom, 10)
                        
                    }
                }
                .background(GeometryReader {
                    Color.clear.preference(key: ViewOffsetKey.self,
                        value: -$0.frame(in: .named("scroll")).origin.y)
                })
                .onPreferenceChange(ViewOffsetKey.self) {
                    
                    if(startChecking){
                        
                        if($0 > lastScrollOffset){

                            let howMuch = $0 - lastScrollOffset

                            if((toolbarOffset - howMuch) < -toolbarHeight){
                                toolbarOffset = -toolbarHeight
                            }else{
                                toolbarOffset -= howMuch
                            }

                        }else{

                            let howMuch = lastScrollOffset - $0

                            if((toolbarOffset + howMuch) > 0){
                                toolbarOffset = 0
                            }else{
                                toolbarOffset += howMuch
                            }
                        }
                    }

                    lastScrollOffset = $0
                }
            }
            .frame(height: freeScreenHeight)
        

        }
        .onAppear() {
                player = AVPlayer(url: URL(string: videoUrl)!)
        }
        .preferredColorScheme(.dark)
        .background(
            GeometryReader { proxy in

                Color.clear
                    .onAppear {
                         
                        freeScreenHeight = UIScreen.main.bounds.height - proxy.safeAreaInsets.top - proxy.safeAreaInsets.bottom
                    }
            }
        )
        .clipped()
}

问题成因分析

  • SafeArea布局计算错误:你通过freeScreenHeight将ScrollView的高度设为「屏幕总高度减去上下SafeArea」,但SwiftUI视图默认会自动适配SafeArea,这种强行压缩的布局会导致状态栏区域(属于顶部SafeArea)被ScrollView或上层视图覆盖,系统无法识别该区域需要显示状态栏内容。

  • 坐标系缺失导致滚动偏移计算异常:代码中使用.frame(in: .named("scroll"))获取滚动偏移,但ScrollView并未添加.coordinateSpace(name: "scroll")修饰符,这会导致滚动偏移计算错误,进而引发Toolbar的Offset动画异常,进一步破坏状态栏区域的布局逻辑。

  • AVPlayer的状态栏行为干扰:VideoPlayer封装的AVPlayer在加载时,会默认尝试优化播放区域,当检测到视图布局未明确保留状态栏空间时,会触发系统自动隐藏状态栏的逻辑,这是AVKit的默认行为之一。

  • ZStack层级与Clipping的副作用:顶层ZStack中包含Toolbar的VStack被设置了clipped()和高zIndex,结合ScrollView的错误高度设置,会导致状态栏区域被上层视图的Clipping操作截断,最终造成状态栏内容完全不可见。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 10:09:56