SwiftUI:数字键盘布局

kee*_*n3d 3 swift swiftui

我正在 Swift UI 中构建自定义数字键盘。我希望按钮排列在填充屏幕的网格中。到目前为止,我发现这样做的唯一方法是使用,GeometryReader但这对于这个简单的任务来说似乎有点矫枉过正。有没有更好的方法来写这个?

GeometryReader { geometry in
    HStack(spacing: 0) {
        ForEach([a, b, c]) { n in
            Button(action: { self.add(n) }) { Text("\(n)") }
                .frame(width: geometry.size.width/3)
        }
    }
}.frame(height: 80)
Run Code Online (Sandbox Code Playgroud)

这导致了这个,这就是我想要的样子。只是好奇是否有没有GeometryReader.

在此处输入图片说明

rob*_*off 13

是的,您无需GeometryReader. 您只需要使每个键视图可扩展。然后你HStackVStack会照顾休息。

AButton紧紧包裹其标签子视图,因此您需要使标签子视图可展开。AColor是 a View,它会扩展以填充它给定的任何空间,所以让我们Color.clear用作按钮的标签,并将实际Text标签覆盖在Color. 我认为我们应该KeyPadButton View为此定义一个:

struct KeyPadButton: View {
    var key: String

    var body: some View {
        Button(action: { self.action(self.key) }) {
            Color.clear
                .overlay(RoundedRectangle(cornerRadius: 12)
                    .stroke(Color.accentColor))
                .overlay(Text(key))
        }
    }

    enum ActionKey: EnvironmentKey {
        static var defaultValue: (String) -> Void { { _ in } }
    }

    @Environment(\.keyPadButtonAction) var action: (String) -> Void
}

extension EnvironmentValues {
    var keyPadButtonAction: (String) -> Void {
        get { self[KeyPadButton.ActionKey.self] }
        set { self[KeyPadButton.ActionKey.self] = newValue }
    }
}

#if DEBUG
struct KeyPadButton_Previews: PreviewProvider {
    static var previews: some View {
        KeyPadButton(key: "8")
            .padding()
            .frame(width: 80, height: 80)
            .previewLayout(.sizeThatFits)
    }
}
#endif
Run Code Online (Sandbox Code Playgroud)

我已经定义了一个,EnvironmentKey以便我们可以使用环境将动作回调从更高级别的键盘视图传递到所有按钮。

KeyPadButton 看起来像这样:

键盘按钮预览

如果您不喜欢边框,只需删除该修饰符。

请注意,我已经手动设置了PreviewProvider. 由于此视图是可展开的,因此默认情况下它会以设备大小进行预览,我们不需要看到一个巨大的按钮。

现在让我们定义一个KeyPadRow视图来布置一行按钮:

struct KeyPadRow: View {
    var keys: [String]

    var body: some View {
        HStack {
            ForEach(keys, id: \.self) { key in
                KeyPadButton(key: key)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们可以定义KeyPad视图来布置整个键盘并提供按钮的操作:

struct KeyPad: View {
    @Binding var string: String

    var body: some View {
        VStack {
            KeyPadRow(keys: ["1", "2", "3"])
            KeyPadRow(keys: ["4", "5", "6"])
            KeyPadRow(keys: ["7", "8", "9"])
            KeyPadRow(keys: [".", "0", "?"])
        }.environment(\.keyPadButtonAction, self.keyWasPressed(_:))
    }

    private func keyWasPressed(_ key: String) {
        switch key {
        case "." where string.contains("."): break
        case "." where string == "0": string += key
        case "?":
            string.removeLast()
            if string.isEmpty { string = "0" }
        case _ where string == "0": string = key
        default: string += key
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,让我们定义一个ContentView显示键盘上方的字符串:

struct ContentView : View {
    var body: some View {
        VStack {
            HStack {
                Spacer()
                Text(string)
            }.padding([.leading, .trailing])
            Divider()
            KeyPad(string: $string)
        }
        .font(.largeTitle)
            .padding()
    }

    @State private var string = "0"

}


#if DEBUG
struct ContentView_Previews : PreviewProvider {
    static var previews: some View {
        Group {
            ContentView()
        }
    }
}
#endif
Run Code Online (Sandbox Code Playgroud)

这是最终结果:

演示