I wanted the same and have now the following implementation.
The SwipeController checks when to execute a swipe action and performs the SwipeAction, for now you can add your swipe actions under the print lines in the executeAction function. But it is better make an abstract class from this.
Then in the SwipeLeftRightContainer struct we have most of the logic in the DragGesture. What it does is while your dragging its gonna change the offset and then make calls to the SwipeController to see if the threshold for swipe left or right are reached. Then when you finish the dragging it will come into the onEnded callback of the DragGesture. Here we will reset the offset and let the SwipeController decide to execute an action.
Keep in mind lot of the variables in the view are static for an iPhone X so you should change them to what fits best.
Also what this does is creating an action for left and right swipe, but you can adjust it to your own use ofcourse.
import SwiftUI
/** executeRight: checks if it should execute the swipeRight action
execute Left: checks if it should execute the swipeLeft action
submitThreshold: the threshold of the x offset when it should start executing the action
*/
class SwipeController {
var executeRight = false
var executeLeft = false
let submitThreshold: CGFloat = 200
func checkExecutionRight(offsetX: CGFloat) {
if offsetX > submitThreshold && self.executeRight == false {
Utils.HapticSuccess()
self.executeRight = true
} else if offsetX < submitThreshold {
self.executeRight = false
}
}
func checkExecutionLeft(offsetX: CGFloat) {
if offsetX < -submitThreshold && self.executeLeft == false {
Utils.HapticSuccess()
self.executeLeft = true
} else if offsetX > -submitThreshold {
self.executeLeft = false
}
}
func excuteAction() {
if executeRight {
print("executed right")
} else if executeLeft {
print("executed left")
}
self.executeLeft = false
self.executeRight = false
}
}
struct SwipeLeftRightContainer: View {
var swipeController: SwipeController = SwipeController()
@State var offsetX: CGFloat = 0
let maxWidth: CGFloat = 335
let maxHeight: CGFloat = 125
let swipeObjectsOffset: CGFloat = 350
let swipeObjectsWidth: CGFloat = 400
@State var rowAnimationOpacity: Double = 0
var body: some View {
ZStack {
Group {
HStack {
Text("Sample row")
Spacer()
}
}.padding(10)
.zIndex(1.0)
.frame(width: maxWidth, height: maxHeight)
.cornerRadius(5)
.background(RoundedRectangle(cornerRadius: 10).fill(Color.gray))
.padding(10)
.offset(x: offsetX)
.gesture(DragGesture(minimumDistance: 5).onChanged { gesture in
withAnimation(Animation.linear(duration: 0.1)) {
offsetX = gesture.translation.width
}
swipeController.checkExecutionLeft(offsetX: offsetX)
swipeController.checkExecutionRight(offsetX: offsetX)
}.onEnded { _ in
withAnimation(Animation.linear(duration: 0.1)) {
offsetX = 0
swipeController.prevLocX = 0
swipeController.prevLocXDiff = 0
self.swipeController.excuteAction()
}
})
Group {
ZStack {
Rectangle().fill(Color.red).frame(width: swipeObjectsWidth, height: maxHeight).opacity(opacityDelete)
Image(systemName: "multiply").font(Font.system(size: 34)).foregroundColor(Color.white).padding(.trailing, 150)
}
}.zIndex(0.9).offset(x: swipeObjectsOffset + offsetX)
Group {
ZStack {
Rectangle().fill(Color.green).frame(width: swipeObjectsWidth, height: maxHeight).opacity(opacityLike)
Image(systemName: "heart").font(Font.system(size: 34)).foregroundColor(Color.white).padding(.leading, 150)
}
}.zIndex(0.9).offset(x: -swipeObjectsOffset + offsetX)
}
}
var opacityDelete: Double {
if offsetX < 0 {
return Double(abs(offsetX) / 50)
}
return 0
}
var opacityLike: Double {
if offsetX > 0 {
return Double(offsetX / 50)
}
return 0
}
}
struct SwipeListView: View {
var body: some View {
ScrollView {
ForEach(0..<10) { index in
SwipeLeftRightContainer().listRowInsets(EdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10))
}
}
}
}
struct SwipeLeftRight_Previews: PreviewProvider {
static var previews: some View {
SwipeListView()
}
}
init
is part of something, but what? More, what exactly are you trying to do, and what do you mean by "that doesn't work for me"? Are you saying that theUIViewControllerRepresentable
doesn't work? That things inUIKit
won't work? Maybe that a swipe action is being intercepted by youronDelete
? Please, maybe with more details we can help you. – DagnydagotableView(_:trailingSwipeActionsConfigurationForRow:)
is aUITableViewDelegate
method, and SwiftUI doesn't let you set the delegate for theUITableView
it creates as an implementation detail, Max's attempt is unlikely to succeed. – Benedic