7

Is there a way to swipe table rows to the left and to the right? I haven't found something for the new Framework SwiftUI so maybe there is no chance to use SwiftUI for this? I need to delete rows and use custom Swipes

1
  • Hi, did you find a way to do it? Commented Oct 7, 2019 at 12:43

2 Answers 2

3

It is possible to implement a delete action and the ability to reorder list items quite simply.

struct SwipeActionView: View {
    @State var items: [String] = ["One", "two", "three", "four"]

    var body: some View {
        NavigationView {
            List {
                ForEach(items.identified(by: \.self)) { item in
                    Text(item)
                }
                .onMove(perform: move)
                .onDelete(perform: delete)      
            }
            .navigationBarItems(trailing: EditButton())
        }
    }

    func delete(at offsets: IndexSet) {
        if let first = offsets.first {
            items.remove(at: first)
        }
    }

    func move(from source: IndexSet, to destination: Int) {
        // sort the indexes low to high
        let reversedSource = source.sorted()

        // then loop from the back to avoid reordering problems
        for index in reversedSource.reversed() {
            // for each item, remove it and insert it at the destination
            items.insert(items.remove(at: index), at: destination)
        }
    }
}

Edit: There is this article by apple that I cannot believe I didn't find previously. Composing SwiftUI Gestures. I haven't experimented with it yet, but the article seems to do a great job!

Sign up to request clarification or add additional context in comments.

Comments

0

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.

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.