Swiftui: Listitem Gestures

How to implement long press gesture on items within a list using SwiftUI?

I'm updating the solution here as it has some significant delays (more than what you would expect from a long press gesture) when long-pressed. So to mitigate this, you can use onLongPressGesture(minimumDuration:) to set a duration you are comfortable with.

See the example below

List {
ForEach(0..<100) { x in
Text("List number -\(x)")
.onTapGesture {}.onLongPressGesture(minimumDuration: 0.2) { // Setting the minimumDuration to ~0.2 reduces the delay
print("long press \(x)")
}
}
}

NOTE: It is important to have both .onTapGesture {}.onLongPressGesture{} one after the other. The above will not work otherwise.

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: 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())

SwiftUI .shadow blocks List scroll gesture for all but first List {}. Why?

Use compositingGroup() for solving issue!



import SwiftUI

struct ContentView: View {
var body: some View {
VStack {
ListView1()
ListView2()
}
.compositingGroup() // <<: Here!
.shadow(radius: 5)
}
}


Related Topics



Leave a reply



Submit