SwiftUI 点击手势识别器仅在影响状态更改时调用一次
SwiftUI tap gesture recogniser only called once when it effects a state change
我正在构建用于表单的自定义视图(多轮选择器)。为了保持表单紧凑,我只想在关联的行被点击时显示选择器。这应该很简单,但我遇到了一些意外行为。
如果与轻击手势识别器关联的闭包不影响视图的状态(例如,通过执行简单的打印语句),所有轻击都会按预期进行注册。当我尝试通过在闭包中切换一个布尔值来更新视图的状态时,点击手势只执行一次(显示选择器)。进一步的点击不会影响进一步的状态变化。向层次结构中的所有视图添加边框并未显示手势识别器的位置或尺寸有任何变化。
这应该是一件很容易实现的事情,但我已经浪费了很多时间来调试我的代码。当我发现自己做错了什么时,我无疑会踢自己一脚,但与此同时,如果能得到帮助,我将不胜感激。代码有点乱,因为它目前是概念验证。
//
// MultiWheelPickerView.swift
// AthenaVS
//
// Created by Adrian Ward on 02/02/2020.
// Copyright © 2020 AwardVetSciences. All rights reserved.
//
import SwiftUI
struct MultiWheelPickerView: View {
@State private var isWheelVisible = false
@State var selection = ["", ""]
private var prompt: String {
let currentValue = selection.reduce("", +)
return currentValue.count == 0 ? "Not set" : currentValue
}
private var title: String = "title".uppercased()
var body: some View {
VStack(alignment: .center) {
VStack(alignment: .leading) {
HStack {
Text("\(self.title)")
.font(.caption)
.bold()
Spacer()
Text("\(prompt)")
.foregroundColor(Color.gray)
}
.contentShape(Rectangle()) // ensures entire HStack is tapable
.onTapGesture {
self.isWheelVisible = !self.isWheelVisible
print("Tapped")
}
if isWheelVisible {
GeometryReader { geometry in
HStack {
ZStack {
Text("One")
Picker(selection: self.$selection[0], label: Text("one")) {
ForEach(0..<10) { row in
Text("\(row)")
.tag(String(row))
}
}
.labelsHidden() // label is misplaced otherwise
.pickerStyle(WheelPickerStyle())
.fixedSize(horizontal: true, vertical: true)
.frame(width: geometry.size.width / 2,
alignment: .trailing)
.clipped() // to enusre touch zone matches the text position
}
ZStack {
Text("Two")
Picker(selection: self.$selection[1], label: Text("two")) {
ForEach(10..<100) { row in
Text("\(row)")
.tag(String(row))
}
}
.labelsHidden() // label is misplaced otherwise
.pickerStyle(WheelPickerStyle())
.fixedSize(horizontal: true, vertical: true)
.frame(width: geometry.size.width / 2, alignment: .trailing)
.clipped() // to enusre touch zone matches the text position
}
}
}
.frame(height:100)
.clipped() // ensures that wheel isn't superimposed on next row
}
}
}
}
}
struct MultiPickerView_Previews: PreviewProvider {
static var previews: some View {
Group {
Form {
MultiWheelPickerView().previewDisplayName("As Form child")
}
MultiWheelPickerView().previewDisplayName("As View child")
}
}
}
当拾取器可见时,您的 GeometryReader 正在拦截点击。
你不能通过添加 .onTapGesture { print("Wheel tapped!") }
来测试它。
尝试在将 GeometryReader 的框架高度设置为 100 之前添加 .contentShape(Rectangle())
修饰符。它解决了我的问题。
我正在构建用于表单的自定义视图(多轮选择器)。为了保持表单紧凑,我只想在关联的行被点击时显示选择器。这应该很简单,但我遇到了一些意外行为。
如果与轻击手势识别器关联的闭包不影响视图的状态(例如,通过执行简单的打印语句),所有轻击都会按预期进行注册。当我尝试通过在闭包中切换一个布尔值来更新视图的状态时,点击手势只执行一次(显示选择器)。进一步的点击不会影响进一步的状态变化。向层次结构中的所有视图添加边框并未显示手势识别器的位置或尺寸有任何变化。
这应该是一件很容易实现的事情,但我已经浪费了很多时间来调试我的代码。当我发现自己做错了什么时,我无疑会踢自己一脚,但与此同时,如果能得到帮助,我将不胜感激。代码有点乱,因为它目前是概念验证。
//
// MultiWheelPickerView.swift
// AthenaVS
//
// Created by Adrian Ward on 02/02/2020.
// Copyright © 2020 AwardVetSciences. All rights reserved.
//
import SwiftUI
struct MultiWheelPickerView: View {
@State private var isWheelVisible = false
@State var selection = ["", ""]
private var prompt: String {
let currentValue = selection.reduce("", +)
return currentValue.count == 0 ? "Not set" : currentValue
}
private var title: String = "title".uppercased()
var body: some View {
VStack(alignment: .center) {
VStack(alignment: .leading) {
HStack {
Text("\(self.title)")
.font(.caption)
.bold()
Spacer()
Text("\(prompt)")
.foregroundColor(Color.gray)
}
.contentShape(Rectangle()) // ensures entire HStack is tapable
.onTapGesture {
self.isWheelVisible = !self.isWheelVisible
print("Tapped")
}
if isWheelVisible {
GeometryReader { geometry in
HStack {
ZStack {
Text("One")
Picker(selection: self.$selection[0], label: Text("one")) {
ForEach(0..<10) { row in
Text("\(row)")
.tag(String(row))
}
}
.labelsHidden() // label is misplaced otherwise
.pickerStyle(WheelPickerStyle())
.fixedSize(horizontal: true, vertical: true)
.frame(width: geometry.size.width / 2,
alignment: .trailing)
.clipped() // to enusre touch zone matches the text position
}
ZStack {
Text("Two")
Picker(selection: self.$selection[1], label: Text("two")) {
ForEach(10..<100) { row in
Text("\(row)")
.tag(String(row))
}
}
.labelsHidden() // label is misplaced otherwise
.pickerStyle(WheelPickerStyle())
.fixedSize(horizontal: true, vertical: true)
.frame(width: geometry.size.width / 2, alignment: .trailing)
.clipped() // to enusre touch zone matches the text position
}
}
}
.frame(height:100)
.clipped() // ensures that wheel isn't superimposed on next row
}
}
}
}
}
struct MultiPickerView_Previews: PreviewProvider {
static var previews: some View {
Group {
Form {
MultiWheelPickerView().previewDisplayName("As Form child")
}
MultiWheelPickerView().previewDisplayName("As View child")
}
}
}
当拾取器可见时,您的 GeometryReader 正在拦截点击。
你不能通过添加 .onTapGesture { print("Wheel tapped!") }
来测试它。
尝试在将 GeometryReader 的框架高度设置为 100 之前添加 .contentShape(Rectangle())
修饰符。它解决了我的问题。