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
相关产品推荐
相关产品推荐

