如何仅让VStack中的最后一个视图忽略键盘安全区域?
问题场景:键盘弹出时,VStack底部的BannerAd会被一并抬起;单独给BannerAd加.ignoresSafeArea(.keyboard)没效果;给VStack加该修饰符的话,ItemsView的焦点状态会异常,所有视图都被键盘顶起。
解决方案:用ZStack分离布局层级
核心思路是让ItemsView正常响应键盘安全区域,BannerAd脱离父布局的键盘影响,固定在屏幕底部。同时给ItemsView添加底部内边距,避免列表内容被BannerAd遮挡。
步骤1:获取BannerAd的高度(可选,若高度固定可跳过)
先定义一个PreferenceKey用来传递BannerAd的高度:
struct BannerHeightKey: PreferenceKey { static var defaultValue: CGFloat = 0 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = nextValue() } }
步骤2:修改BannerAd,传递高度信息
给BannerAd添加一个背景用来计算自身高度:
struct BannerAd: View { var body: some View { Text("Banner ad") .padding() .frame(maxWidth: .infinity) .background(Color.green) .background( GeometryReader { proxy in Color.clear .preference(key: BannerHeightKey.self, value: proxy.size.height) } ) } }
步骤3:重构ContentView布局
用ZStack将ItemsView和BannerAd分层,让BannerAd固定在底部并忽略键盘安全区域,同时给ItemsView设置底部内边距:
struct ContentView: View { @FocusState var focusedId: ObjectId? @State private var bannerHeight: CGFloat = 0 var body: some View { NavigationStack { ZStack(alignment: .bottom) { // 列表视图正常响应键盘,添加底部内边距避免被Banner遮挡 ItemsView(focusedId: $focusedId) .padding(.bottom, bannerHeight) // Banner固定在底部,忽略键盘安全区域 BannerAd() .ignoresSafeArea(.keyboard) .onPreferenceChange(BannerHeightKey.self) { bannerHeight = $0 } } } } }
简化方案(Banner高度固定时)
如果BannerAd的高度是固定值(比如50),可以直接省略PreferenceKey的步骤,给ItemsView加固定内边距:
struct ContentView: View { @FocusState var focusedId: ObjectId? var body: some View { NavigationStack { ZStack(alignment: .bottom) { ItemsView(focusedId: $focusedId) .padding(.bottom, 50) // 固定Banner高度 BannerAd() .ignoresSafeArea(.keyboard) } } } }
这样修改后,键盘弹出时,ItemsView会正常滚动以保证焦点TextField可见,而BannerAd会留在屏幕底部,不会被键盘抬起。
内容的提问来源于stack exchange,提问作者BPDev
相关产品推荐
相关产品推荐

