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

SwiftUI使用Preference Key实现等尺寸字母网格失败,求问题排查

问题分析与修复方案

你的代码存在4个核心问题,导致字母网格布局不符合预期:


1. PreferenceKey的reduce逻辑错误

当前WidthPreference的reduce方法仅取第一个非nil的宽度值,但我们需要的是所有单元格文本中的最大宽度来统一尺寸,这样才能保证所有方格大小一致。

修复代码:

struct WidthPreference: PreferenceKey {
    static let defaultValue: CGFloat = 0
    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
        value = max(value, nextValue())
    }
}

2. LetterView中frame与padding的顺序错误

你把padding放在frame外面,导致实际单元格尺寸是frame宽度 + padding,但我们测量的是Text的原始宽度(未包含padding),最终会让方格大小不一致。需要把padding移到frame内部。


3. 每个LetterView独立监听偏好,导致宽度不一致

每个LetterView单独监听WidthPreference,会出现部分单元格先拿到宽度就先渲染,后续单元格拿到的宽度可能不同。正确做法是在AlphabetGrid层级统一监听最大宽度,再传递给所有LetterView。


4. HStack未设置均匀分布

HStack默认根据内容宽度排列,没有让每个LetterView占满行内可用空间,需要给每个LetterView添加frame(maxWidth: .infinity)来保证每行单元格均匀分配宽度。


完整修复后的代码

1. PreferenceKey与View扩展修改

struct WidthPreference: PreferenceKey {
    static let defaultValue: CGFloat = 0
    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
        value = max(value, nextValue())
    }
}

extension View {
    func sizePreference() -> some View {
        background(GeometryReader { proxy in
            Color.clear
                .preference(key: WidthPreference.self, value: proxy.size.width)
        })
    }
}

2. AlphabetGrid修改(统一监听宽度并传递)

struct AlphabetGrid: View {
    @EnvironmentObject var theModel: MyModel
    @State private var maxCellWidth: CGFloat = 0
    
    var spacing: CGFloat = 8

    var body: some View {
        guard let allLetters = theModel.allLetters else { return EmptyView() }
        
        let theKeyboard = [
            allLetters.filter { $0.keyboardRow == 0 },
            allLetters.filter { $0.keyboardRow == 1 },
            allLetters.filter { $0.keyboardRow == 2 }
        ]
        
        VStack(spacing: spacing) {
            ForEach(theKeyboard, id: \.self) { keyboardRow in
                HStack(spacing: spacing) {
                    ForEach(keyboardRow, id: \.self) { keyboardLetter in
                        LetterView(
                            theIdx: keyboardLetter.letterStorePosition,
                            borderColour: .blue,
                            cellWidth: maxCellWidth,
                            spacing: spacing
                        )
                        .frame(maxWidth: .infinity)
                    }
                }
            }
        }
        .onPreferenceChange(WidthPreference.self) { newValue in
            maxCellWidth = newValue
        }
    }
}

3. LetterView修改(接收统一宽度,调整padding顺序)

struct LetterView: View {
    @EnvironmentObject var theModel: MyModel

    var theIdx: Int
    var borderColour: Color
    var cellWidth: CGFloat
    var spacing: CGFloat = 8

    var body: some View {
        Text(String(theModel.allLetters?[theIdx].letterStoreChar ?? "*"))
            .sizePreference()
            .frame(width: cellWidth, height: cellWidth, alignment: .center)
            .padding(spacing)
            .overlay(
                RoundedRectangle(cornerRadius: 8)
                    .stroke(borderColour, lineWidth: 1)
            )
    }
}

4. ContentView与MyModel代码保持不变


修改后,所有方格会基于文本的最大宽度设置统一尺寸,HStack会让每个单元格均匀分配行内空间,最终实现等尺寸字母网格效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 15:40:23