是的,你可以不使用 GeometryReader
来实现这个功能。你只需要使每个关键视图可扩展即可。然后你的 HStack
和 VStack
就会自动处理剩下的部分。
Button
紧密包裹它的标签子视图,所以你需要使标签子视图可扩展。一个 Color
是一个会扩展以填充其给定空间的 View
,所以我们可以使用 Color.clear
作为按钮的标签,并在 Color
上叠加真正的 Text
标签。我认为我们应该为此定义一个 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
我已经定义了一个 EnvironmentKey
,这样我们就可以使用环境将操作回调从更高级别的键盘视图传递给所有按钮。
KeyPadButton
看起来像这样:
如果您不喜欢边框,请删除该修饰符。
请注意,我在 PreviewProvider
中手动设置了按钮的大小。由于此视图是可扩展的,默认情况下它会以设备大小预览,我们不需要看到一个巨大的按钮。
现在让我们定义一个 KeyPadRow
视图来布置一行按钮:
struct KeyPadRow: View {
var keys: [String]
var body: some View {
HStack {
ForEach(keys, id: \.self) { key in
KeyPadButton(key: key)
}
}
}
}
现在我们可以定义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
}
}
}
最后,让我们定义一个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
这是最终结果: