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
How to Update Swift Dependencies in Xcode
Swift - Lazy Var VS. Let When Creating Views Programmatically (Saving Memory)
How to Override Layerclass in Swift
Why Does Filters in Swift Iterate the Collection Twice
Uisplitviewcontroller in Portrait on iPhone Always Show Master and Detail in iOS 8
How to Make a Local Module with the Swift Package Manager
How to Rearrange Views in Swiftui Zstack by Dragging
Difference Between String Interpolation and String Concatenation
Nsbutton with Round Corners and Background Color
Executefetchrequest Doesn't Return the Nsmanagedobject Subclass
Class Level or Struct Level Method in Swift Like Static Method in Java
What Language Is Swift Written In
How to Create a Cgsize in Swift
Synchronize Properties in Swift 3 Using Gcd
How to Avoid Migration in Realmswift