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

SwiftUI中Sheet内视图真实可用尺寸获取异常的技术问询

解决Sheet内视图尺寸获取偏差问题

问题根源

  1. 循环视图嵌套:原代码中Sheet直接嵌套ContentView,导致视图层级混乱,尺寸计算被干扰。
  2. PreferenceKey逻辑缺陷:原reduce方法直接覆盖尺寸值,若存在多个视图提交偏好时会丢失正确尺寸。
  3. Sheet布局未明确约束:未给Sheet内的视图添加填充父容器的约束,导致尺寸计算未匹配Sheet实际可用空间。

修正后的代码方案

import SwiftUI

struct ContentView: View {
    @State private var size: CGSize = .zero
    @State private var showSheet = false
    
    var body: some View {
        ZStack {
            Color.red
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .readSize { size in
                    self.size = size
                    print("主视图尺寸 = \(size)")
                }
                .onAppear {
                    Task { @MainActor in
                        try await Task.sleep(nanoseconds: NSEC_PER_SEC * 1)
                        withAnimation {
                            showSheet.toggle()
                        }
                    }
                }
                .sheet(isPresented: $showSheet) {
                    // 使用独立的Sheet视图,避免循环嵌套
                    NavigationStack {
                        SheetContentView()
                    }
                }
        }
    }
}

// Sheet专用视图
struct SheetContentView: View {
    @State private var sheetSize: CGSize = .zero
    
    var body: some View {
        Color.blue
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .readSize { size in
                sheetSize = size
                print("Sheet内视图尺寸 = \(size)")
            }
            .navigationTitle("Sheet示例")
            .navigationBarTitleDisplayMode(.inline)
    }
}

// 修正PreferenceKey的合并逻辑
struct ViewSizePreferenceKey: PreferenceKey {
    static var defaultValue: CGSize = .zero
    
    static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
        // 取最大尺寸,避免多视图提交时覆盖正确值
        let newSize = nextValue()
        value = CGSize(
            width: max(value.width, newSize.width),
            height: max(value.height, newSize.height)
        )
    }
}

extension View {
    func readSize(onChange: @escaping (CGSize) -> Void) -> some View {
        background(
            GeometryReader { geometryProxy in
                Color.clear
                    .preference(
                        key: ViewSizePreferenceKey.self,
                        value: geometryProxy.size
                    )
            }
        )
        .onPreferenceChange(ViewSizePreferenceKey.self, perform: onChange)
    }
}

关键改进点

  • 拆分视图层级:将Sheet内容拆分为独立的SheetContentView,避免循环嵌套导致的布局混乱。
  • 明确视图约束:给视图添加.frame(maxWidth: .infinity, maxHeight: .infinity),确保视图填充Sheet的实际可用空间,让尺寸计算更准确。
  • 优化PreferenceKey:修改reduce逻辑为取最大尺寸,防止多视图提交偏好时丢失正确的尺寸数据。
  • 适配Sheet导航栏:添加NavigationStack和导航标题,模拟真实场景下的Sheet布局,确保尺寸计算包含导航栏占用空间后的可用区域。

额外说明

如果需要获取Sheet的精确容器尺寸(包含拖拽指示器区域),可以结合UIKit的UISheetPresentationController获取,但上述SwiftUI原生方案已能满足绝大多数场景下的真实可用尺寸需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 01:25:05