带有 onTapGesture 的分段选择器不响应点击
Segmented Picker with onTapGesture doesn't respond to taps
我尝试重新实现我正在使用的 SegmentedControlers,因为它们在 Xcode 11 beta 5 中已被弃用。花了一段时间,但我得到了我想要的外观。但是,当我用 onTapGesture() 替换 tapAction 时,选择器停止工作。
下面的代码显示了这个问题。注释掉 pickerStyle 会得到一个与 onTapGesture()
一起工作的轮式选择器
import SwiftUI
var oneSelected = false
struct ContentView: View {
@State var sel = 0
var body: some View {
VStack {
Picker("Test", selection: $sel) {
Text("A").tag(0)
Text("B").tag(1)
Text("C").tag(2)
}
.pickerStyle(SegmentedPickerStyle())
Picker("Test", selection: $sel) {
Text("A").tag(0)
Text("B").tag(1)
Text("C").tag(2)
}
.pickerStyle(SegmentedPickerStyle())
.onTapGesture(perform: {
oneSelected = (self.sel == 1)
})
Text("Selected: \(sel)")
}
}
}
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
#endif
我希望 Picker().pickerStyle(SegmentedPickerStyle()) 的工作方式与 SegmentedController() 相同。
您添加的 tapGesture
干扰了选择器的内置点击手势识别,这就是为什么您的 .onTapGesture
中的代码在点击选择器时运行,但选择器本身没有响应水龙头。对于您的情况,我建议采用不同的方法:将符合 ObservableObject
的视图模型传递到您的 ContentView
中,并让它包含一个 @Published
变量以供选择器选择。然后向该变量添加一个 属性 观察器,检查所选选项是否为 1。
例如:
class ViewModel: ObservableObject {
@Published var sel = 0 {
didSet {
oneSelected = oldValue == 1
}
}
var oneSelected = false
}
在 SceneDelegate.swift
中,或声明 ContentView
的任何地方:
ContentView().environmentObject(ViewModel())
在ContentView.swift
中:
@EnvironmentObject var env: ViewModel
var body: some View {
VStack {
Picker("Test", selection: $env.sel) {
Text("A").tag(0)
Text("B").tag(1)
Text("C").tag(2)
}
.pickerStyle(SegmentedPickerStyle())
Picker("Test", selection: $env.sel) {
Text("A").tag(0)
Text("B").tag(1)
Text("C").tag(2)
}
.pickerStyle(SegmentedPickerStyle())
Text("Selected: \(sel)")
}
}
注意:根据我的经验,在以前的测试版中向 SegmentedControl
添加 tapGesture 会导致 SegmentedControl
没有响应,所以我不是确定为什么它在以前的版本中对你有用。从 SwiftUI beta 5 开始,我认为没有办法为手势分配优先级。
编辑: 您可以使用 .highPriorityGesture()
使您的手势优先于视图中定义的手势,但您的手势具有更高的优先级会导致您的问题。但是,您可以使用 .simultaneousGesture()
,我认为这可以解决您的问题,但我不认为它在 SwiftUI Beta 5 中功能齐全。
class IndexManager: ObservableObject {
@Published var index = 0 {
didSet {
publisher.send(index)
}
}
let publisher = PassthroughSubject<Int, Never>()
}
struct SegmentedPickerView: View {
private let strings = ["a", "b", "c"]
@ObservedObject private var indexManager = IndexManager()
var body: some View {
Picker("", selection: $indexManager.index) {
ForEach(strings, id: \.self) {
Text([=10=]).tag(self.strings.firstIndex(of: [=10=])!)
}
}.pickerStyle(SegmentedPickerStyle())
.onReceive(indexManager.publisher) { int in
print("onReceive \(int)")
}
}
}
我能够在 onTapGesture
中使用以下条件实现此功能
@State private var profileSegmentIndex = 0
Picker(selection: self.$profileSegmentIndex, label: Text("Music")) {
Text("My Posts").tag(0)
Text("Favorites").tag(1)
}
.onTapGesture {
if self.profileSegmentIndex == 0 {
self.profileSegmentIndex = 1
} else {
self.profileSegmentIndex = 0
}
}
我尝试重新实现我正在使用的 SegmentedControlers,因为它们在 Xcode 11 beta 5 中已被弃用。花了一段时间,但我得到了我想要的外观。但是,当我用 onTapGesture() 替换 tapAction 时,选择器停止工作。
下面的代码显示了这个问题。注释掉 pickerStyle 会得到一个与 onTapGesture()
一起工作的轮式选择器import SwiftUI
var oneSelected = false
struct ContentView: View {
@State var sel = 0
var body: some View {
VStack {
Picker("Test", selection: $sel) {
Text("A").tag(0)
Text("B").tag(1)
Text("C").tag(2)
}
.pickerStyle(SegmentedPickerStyle())
Picker("Test", selection: $sel) {
Text("A").tag(0)
Text("B").tag(1)
Text("C").tag(2)
}
.pickerStyle(SegmentedPickerStyle())
.onTapGesture(perform: {
oneSelected = (self.sel == 1)
})
Text("Selected: \(sel)")
}
}
}
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
#endif
我希望 Picker().pickerStyle(SegmentedPickerStyle()) 的工作方式与 SegmentedController() 相同。
您添加的 tapGesture
干扰了选择器的内置点击手势识别,这就是为什么您的 .onTapGesture
中的代码在点击选择器时运行,但选择器本身没有响应水龙头。对于您的情况,我建议采用不同的方法:将符合 ObservableObject
的视图模型传递到您的 ContentView
中,并让它包含一个 @Published
变量以供选择器选择。然后向该变量添加一个 属性 观察器,检查所选选项是否为 1。
例如:
class ViewModel: ObservableObject {
@Published var sel = 0 {
didSet {
oneSelected = oldValue == 1
}
}
var oneSelected = false
}
在 SceneDelegate.swift
中,或声明 ContentView
的任何地方:
ContentView().environmentObject(ViewModel())
在ContentView.swift
中:
@EnvironmentObject var env: ViewModel
var body: some View {
VStack {
Picker("Test", selection: $env.sel) {
Text("A").tag(0)
Text("B").tag(1)
Text("C").tag(2)
}
.pickerStyle(SegmentedPickerStyle())
Picker("Test", selection: $env.sel) {
Text("A").tag(0)
Text("B").tag(1)
Text("C").tag(2)
}
.pickerStyle(SegmentedPickerStyle())
Text("Selected: \(sel)")
}
}
注意:根据我的经验,在以前的测试版中向 SegmentedControl
添加 tapGesture 会导致 SegmentedControl
没有响应,所以我不是确定为什么它在以前的版本中对你有用。从 SwiftUI beta 5 开始,我认为没有办法为手势分配优先级。
编辑: 您可以使用 .highPriorityGesture()
使您的手势优先于视图中定义的手势,但您的手势具有更高的优先级会导致您的问题。但是,您可以使用 .simultaneousGesture()
,我认为这可以解决您的问题,但我不认为它在 SwiftUI Beta 5 中功能齐全。
class IndexManager: ObservableObject {
@Published var index = 0 {
didSet {
publisher.send(index)
}
}
let publisher = PassthroughSubject<Int, Never>()
}
struct SegmentedPickerView: View {
private let strings = ["a", "b", "c"]
@ObservedObject private var indexManager = IndexManager()
var body: some View {
Picker("", selection: $indexManager.index) {
ForEach(strings, id: \.self) {
Text([=10=]).tag(self.strings.firstIndex(of: [=10=])!)
}
}.pickerStyle(SegmentedPickerStyle())
.onReceive(indexManager.publisher) { int in
print("onReceive \(int)")
}
}
}
我能够在 onTapGesture
中使用以下条件实现此功能@State private var profileSegmentIndex = 0
Picker(selection: self.$profileSegmentIndex, label: Text("Music")) {
Text("My Posts").tag(0)
Text("Favorites").tag(1)
}
.onTapGesture {
if self.profileSegmentIndex == 0 {
self.profileSegmentIndex = 1
} else {
self.profileSegmentIndex = 0
}
}