SwiftUI 使按钮在 HStack 中水平地彼此靠近

SwiftUI making buttons closer to each other horizontally in an HStack

我试着让键盘上的按钮在水平方向上靠得更近。首先我尝试调整按钮框架的宽度。但是我发现如果我减小边框的宽度,一些长宽的字符如“W”将无法正确显示。

然后我尝试将 HStack 的间距设置为负数,就像我在下面的代码中所做的那样。

但这会导致按钮相互重叠,可点击区域向左移动,这是不可接受的(可以通过设置背景颜色为蓝色来检查)。

有没有办法在不改变字体大小的情况下减小按钮距离?

struct KeyboardView: View {

    let KeyboardStack = [["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"],
                          ["A", "S", "D", "F", "G", "H", "J", "K", "L"],
                          ["Z", "X", "C", "V", "B", "N", "M"]]

    var body: some View {
        VStack(spacing: 9) {                      
            ForEach(KeyboardStack.indices) { row in
                let num = KeyboardStack[row].indices
                HStack(spacing: -12) {           
                    ForEach(num) { column in
                        Button(KeyboardStack[row][column]) {}
                        .frame(width: 12, height: 12,alignment: .center)
                        .padding()
//                        .background(Color.blue)
                        .font(.system(size: 15, weight: .regular, design: .default))
                        .foregroundColor(.white)
                        .buttonStyle(PlainButtonStyle())
                    }
                }
                .frame(width: 255, height: 19.5, alignment:.center)
            }
        }
        .frame(width: 445, height: 60, alignment:.center)
    }
}

第一个简单的事情就是去掉你的 padding() 修饰符,它会为每个按钮添加额外的填充。

我假设,鉴于这是一个键盘视图,您希望所有键的宽度都相同。您可以使用 PreferenceKey 来存储适合特定字母所需的最大宽度,然后将其用于每个 Button,使其仅根据需要变大:

struct KeyboardView: View {
    @State private var keyWidth : CGFloat = 0
    
    let KeyboardStack = [["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"],
                         ["A", "S", "D", "F", "G", "H", "J", "K", "L"],
                         ["Z", "X", "C", "V", "B", "N", "M"]]
    
    var body: some View {
        VStack(spacing: 9) {
            ForEach(KeyboardStack.indices) { row in
                let num = KeyboardStack[row].indices
                HStack(spacing: 0) {
                    ForEach(num) { column in
                        Button(action: {}) {
                            Text(KeyboardStack[row][column])
                                .font(.system(size: 15, weight: .regular, design: .default))
                                .foregroundColor(.white)
                                .buttonStyle(PlainButtonStyle())
                                .frame(width: keyWidth)
                                .background(GeometryReader {
                                    Color.clear.preference(key: KeyWidthKey.self,
                                                           value: [=10=].frame(in: .local).size.height)
                                })
                                .contentShape(Rectangle())
                        }
                    }
                }.onPreferenceChange(KeyWidthKey.self) { keyWidth = [=10=] }
                
            }
        }
    }
}

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

请注意,如果您更改字体大小,此解决方案将继续有效,因为它不依赖于每个键的硬编码 frame 大小。

这里有一个重构代码和方式给你:,

PS:还有很多空间可以做更多的重构,比如摆脱硬代码值,让按键和键盘大小根据屏幕大小动态变化等等支持其他语言或小写或大写单词,向按键列表添加声音可以继续下去。这是您开始或使用这种方式满足您需要的简单方法。 这就是为什么苹果有一个大团队只为键盘工作,这看起来很简单,但我们正在使用的 iOS 中的键盘比我们知道的要复杂得多它是我们应用程序中的一个应用程序,当我们需要时通过键盘输入内容。

struct KeyboardView: View {

    let keyBackgroundColor: Color
    let keyForegroundColor: Color
    let keyboardBackgroundColor: Color

    init(keyBackgroundColor: Color, keyForegroundColor: Color, keyboardBackgroundColor: Color) {
        self.keyBackgroundColor = keyBackgroundColor
        self.keyForegroundColor = keyForegroundColor
        self.keyboardBackgroundColor = keyboardBackgroundColor
    }
    
    init() {
        self.keyBackgroundColor = Color.gray
        self.keyForegroundColor = Color.primary
        self.keyboardBackgroundColor = Color.gray.opacity(0.5)
    }

    private let keyboard: [[String]] = [["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"],
                                        ["A", "S", "D", "F", "G", "H", "J", "K", "L"],
                                        ["Z", "X", "C", "V", "B", "N", "M"]]
    
    var body: some View {
        
        VStack(spacing: 5.0) {
            
            ForEach(keyboard.indices) { row in
                let num = keyboard[row].indices
                
                HStack(spacing: 5.0) {
                    
                    ForEach(num) { column in
                        
                        keyBackgroundColor.cornerRadius(2.0)
                            .frame(width: 24.0, height: 24.0)
                            .overlay( Text(keyboard[row][column])
                                        .font(.system(size: 15.0, weight: .regular, design: .default))
                                        .foregroundColor(keyForegroundColor)
                                        .fixedSize() )
                            .onTapGesture { print(keyboard[row][column]) }
                        
                    }
                }
                
            }
        }
        .padding(5.0)
        .background(keyboardBackgroundColor.cornerRadius(5.0))
        
    }
}

使用一些自定义代码的案例:

我建议使用 Button 作为包装视图,构建按钮的内容而不是直接使用字符串。您可以轻松控制其布局。

VStack(spacing: 4) {
    ForEach(keys.indices) { row in
        HStack(spacing: 3) {
            ForEach(keys[row].indices) { column in
                Button {
                    print("Tap \(keys[row][column])")
                } label: {
                    Text(keys[row][column])
                        .font(.system(size: fontSize))
                        .foregroundColor(.white)
                        .frame(minWidth: fontSize)
                        .padding(3)
                        .background(
                            RoundedRectangle(cornerRadius: 2)
                                .fill(.black)
                        )
                }
            }
        }
    }
}

结果