SwiftUI: Menu inside list cell with onTapGesture triggers gesture
Alternatively, you can override Menu onTapGesture
:
struct ContentView: View {
var body: some View {
List {
HStack {
Text("Cell content")
Spacer()
Menu(content: {
Button(action: {}) {
Text("Menu Item 1")
Image(systemName: "command")
}
Button(action: {}) {
Text("Menu Item 2")
Image(systemName: "option")
}
Button(action: {}) {
Text("Menu Item 3")
Image(systemName: "shift")
}
}) {
Image(systemName: "ellipsis")
.imageScale(.large)
.padding()
}
.onTapGesture {} // override here
}
.contentShape(Rectangle())
.onTapGesture {
print("Cell tapped")
}
}
}
}
Also, there's no need to use .background(Color(.systemBackground))
to tap on spacers. This is not really flexible - what if you want change background color?
You can use contentShape
instead:
.contentShape(Rectangle())
List Item with Toggle subview and selection gesture
First, your [ItemDataModel]
array should be moved outside the body, so it's not recreated every time:
struct ListView: View {
@State var selected = Set<Int>()
let items = (1...10).map(ItemDataModel.init) // move outside the `body`
var body: some View {
VStack {
Text(String(describing: selected))
List(items) { item in
ListItemView(dataModel: item)
.onTapGesture { if !(selected.remove(item.id) != .none) { selected.insert(item.id) }}
}
}
}
}
Then, make sure that the Toggle
in your ListItemView
doesn't take all the space (that's the default behaviour) and attach onTapGesture
to override the parent's gesture:
struct ListItemView: View {
@ObservedObject var dataModel: ItemDataModel
var body: some View {
HStack {
Text(dataModel.title)
// Text("toggle") // if necessary add Toggle's label as `Text`
Spacer()
Toggle("", isOn: $dataModel.isOn) // use another initialiser
.fixedSize() // limit Toggle's width
.background(Color.red) // debug-only, to see the real frame
.onTapGesture {} // override tap gestures
}
.contentShape(Rectangle()) // make empty space *clickable*
}
}
SwiftUI using ForEach and onTapGesture to update selected element causing crash
Your problem is likely due to the view diffing algorithm having a different outcome if you have the text value present, since that would be a difference in the root view. Since you have two pieces of state that represent the sheet, and you are force unwrapping one of them, you're leaving yourself open to danger.
You should get rid of showingSheet
, then use the item binding of sheet instead. Make Cards
conform to Identifiable
, then rewrite the sheet modifier as:
.sheet(item: $selectedCard) { SpecificCardView(card: $0) }
This will also reset your selected card to nil when the sheet is dismissed.
Swiftui How can you use on Tap Gesture for entire row in a foreach loop
It only works for the highlighted text because the onTapGesture
is on the Text view.
1. Using a HStack()
I would embed the VStack
in a HStack
and add a Spacer()
to fill the entire area, then place the onTapGesture
to the HStack
, like this:
ForEach(model) { value in
HStack{
VStack(alignment: .leading) {
HStack {
Text(value.name)
.font(.system(size: 15))
.background(Color.red)
.padding(.trailing, 10.0)
}
HStack {
Text(value.statelong)
.foregroundColor(Color.gray)
.font(.system(size: 13))
Spacer()
}
}
Spacer()
}.contentShape(Rectangle())
.onTapGesture {
isOpen = true
defaults.setValue(value.name, forKey: "location")
}.fullScreenCover(isPresented: $isOpen, content: MainView.init)
}
2. Using a ZStack()
Or you could use a ZStack
to make a full view background and place the onTapGesture on the ZStack
, add .frame(maxWidth: .infinity)
to the ZStack
to fill the entire available width, like this:
ForEach(model) { value in
ZStack {
VStack(alignment: .leading) {
HStack {
Text(value.name)
.font(.system(size: 15))
.background(Color.red)
.padding(.trailing, 10.0)
}
HStack {
Text(value.statelong)
.foregroundColor(Color.gray)
.font(.system(size: 13))
Spacer()
}
}
Spacer()
}.contentShape(Rectangle())
.frame(maxWidth: .infinity)
.onTapGesture {
isOpen = true
defaults.setValue(value.name, forKey: "location")
}.fullScreenCover(isPresented: $isOpen, content: MainView.init)
}
Swift UI @State does get set on the first tap
In iOS 14, in order to get the .sheet
to be presented with up-to-date data, you must use sheet(item:)
instead of .sheet(isPresented:)
You can see another one of my answers here: https://stackoverflow.com/a/66190152/560942
and
SwiftUI: Switch .sheet on enum, does not work
In your specific case, it's hard to tell since I don't have your UserProfile
code, but I suggest that you:
- Get rid of your
showDetail
state variable - Make sure that
UserProfile
conforms toIdentifiable
- Change your
sheet(isPresented:)
to:
.sheet(item: $selectedListener) { listener in
ListenerDetailView(listener: listener)
}
Related Topics
No Designated Init for Skshapenode(Circleofradius: Radius)
Swiftui on MACos - Handle Single-Click and Double-Click at the Same Time
Swift: Can Not Use Array Filter in If Let Statement Condition
Split String by Components and Keep Components in Place
Scrolling Delegate in Tableview
Firestore Query Across Multiple Fields for Same Value
Difference Between Optional and Forced Unwrapping
Does Swift Have Short-Circuiting Higher-Order Functions Like Any or All
Swift 3 - How to Write Functions with No Initialisers Like the New Uicolors
Swift 2: Invalid Conversion from Throwing Function of Type to Non-Throwing Function
Swiftui Behavior of .Frame(Height: Nil)
Pattern Matching in a Swift for Loop
How to Detect the 2D Images Using Arkit and Realitykit
Value of Type 'Tags' Has No Member 'Lastused'
Swift: Dictionaries Inside Array
Programmatically Scroll Nsscrollview to the Right
Cannot Call Value of Non-Function Type 'Nshttpurlresponse' Alamofire Objectmapper