Reputation: 1184
I tried to reimplement the SegmentedControlers that I was using as they became deprecated in Xcode 11 beta 5. It took a while but I got the look I wanted. However when I replaced the tapAction with an onTapGesture() then the picker stopped working.
The code below shows the problem. Commenting out the pickerStyle gets a wheel picker which does work with 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
I expect that Picker().pickerStyle(SegmentedPickerStyle()) should work the same way as SegmentedController() did.
Upvotes: 9
Views: 6258
Reputation: 3154
import SwiftUI
import Combine
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($0).tag(strings.firstIndex(of: $0)!)
}
}
.pickerStyle(SegmentedPickerStyle())
.onReceive(indexManager.publisher) { int in
print("onReceive \(int)")
}
}
}
Upvotes: 3
Reputation: 364
.onTapGesture()
does not seem to work with a Picker with a .pickerStyle
attribute value of SegmentedPickerStyle()
.
An alternative that worked for me (taken from this answer is using the .onReceive()
call as such:
Picker("", selection: $quoteFrequencyIndex) {
ForEach(0..<frequencyOptions.count, id: \.self) { index in
Text(self.frequencyOptions[index])
.foregroundColor(colorPalettes[safe: colorPaletteIndex]?[1] ?? .white)
}
}
.pickerStyle(SegmentedPickerStyle())
.onReceive([self.quoteFrequencyIndex].publisher.first()) { _ in
WidgetCenter.shared.reloadTimelines(ofKind: "QuoteDropletWidget")
}
Abstracted to a general template, where you can put your own logic in the onReceive()
call, controlled by your selection
variable:
Picker(selection: ${variable}){}
.pickerStyle(SegmentedPickerStyle())
.onReceive([self.{variable}].publisher.first()) {}
Upvotes: 0
Reputation: 818
i was able to get this working with the following condition in a 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
}
}
Upvotes: 2
Reputation: 8096
The tapGesture
you added interferes with the picker's built in tap gesture recognizing, which is why the code in your .onTapGesture
runs when the picker is tapped, but the picker itself is not responding to taps. In your case, I suggest a different approach: pass a view model that conforms to ObservableObject
into your ContentView
, and have it contain an @Published
variable for the picker selection. Then add a property observer to that variable that checks if the selected option is 1.
For example:
class ViewModel: ObservableObject {
@Published var sel = 0 {
didSet {
oneSelected = oldValue == 1
}
}
var oneSelected = false
}
In SceneDelegate.swift
, or wherever ContentView
is declared:
ContentView().environmentObject(ViewModel())
In 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)")
}
}
Note: In my experience, adding a tapGesture to a SegmentedControl
in previous betas resulted in the SegmentedControl
being unresponsive, so I'm not sure why it was working for you in previous version. As of SwiftUI beta 5, I don't think there is a way to assign priority levels to gestures.
Edit: You can use .highPriorityGesture()
to make your gesture take precedence over gestures defined in the view, but your gesture having higher precedence is causing your problem. You can, however, use .simultaneousGesture()
, which I thought would be the solution to your problem, but I don't think it is fully functional as of SwiftUI Beta 5.
Upvotes: 6