9

Target is a modification with the following behavior:

enter image description here

(but only with 2 buttons - 1 on the left side, 1 on the right)

Behavior:

  • short swipe shows the buttons and gives the user the ability to click it.

  • strong long swipe presses button.

  • ability to use 2 finger gesture


Minimal reproducible example:

import SwiftUI

public extension View {
    func SwiperizeItem(closureL: @escaping () -> (), closureR: @escaping () -> ()) -> some View
    {
        self.modifier( SwiperizeItemModifier(closureL: closureL, closureR: closureR) )
    }
}

struct SwiperizeItemModifier: ViewModifier {
    @State var dragOffset = CGSize.zero
    
    @State var offset1Shown = CGSize(width: 100, height: 0)
    @State var offset1Click = CGSize(width: 250, height: 0)
    
    @State var offset2Shown = CGSize(width: -100, height: 0)
    @State var offset2Click = CGSize(width: -250, height: 0)
    
    @State var BackL = Color.green
    @State var BackR = Color.red
    @State var ForeColorL = Color.white
    @State var ForeColorR = Color.white
    
    @State var closureL: () -> Void
    @State var closureR: () -> Void
    
    func body(content: Content) -> some View {
        HStack{
            Button(action: { closureL() } ) {
                Text("Left")
                    .foregroundColor(ForeColorL)
                    
            }
            .background(BackL)
            .frame(maxWidth: dragOffset.width > 0 ? dragOffset.width : 0)
            .fixedSize()
            
            
            content
                //.padding([.leading, .trailing], 20)
                .animation(.spring())
                .offset(x: self.dragOffset.width)
                .gesture(DragGesture()
                            .onChanged(){
                                value in
                                self.dragOffset = value.translation
                            }
                            .onEnded(){
                                value in
                                if ( dragOffset.width > 0 ) {
                                    if ( dragOffset.width < offset1Shown.width) {
                                        self.dragOffset = .zero
                                    }
                                    else if ( dragOffset.width > offset1Shown.width &&  dragOffset.width < offset1Click.width ) {
                                        self.dragOffset = offset1Shown
                                    }
                                    else if ( dragOffset.width > offset1Click.width ) {
                                        self.dragOffset = .zero
                                        closureR()
                                    }
                                }
                                else {
                                    if ( dragOffset.width > offset2Shown.width) {
                                        self.dragOffset = .zero
                                    }
                                    else if ( dragOffset.width < offset2Shown.width && dragOffset.width > offset2Click.width ) {
                                        self.dragOffset = offset2Shown
                                    }
                                    else if ( dragOffset.width < offset2Click.width ) {
                                        self.dragOffset = .zero
                                        closureL()
                                    }
                                }
                            }
                )
        
        }
    }
}



// ____________________

struct GuestureItem_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            Text("Hello")
                .padding(.all, 30)
                .background( Color( NSColor.red ) )
                .SwiperizeItem(closureL: { print("click left") }, closureR: { print("click right") })
        }
    }
}

So... my problems are:

  1. to draw buttons like here using SwiftUI:

enter image description here

I think the solution may be related to the new release of SwiftUI components: LazyHGrid or OutlineGroup. https://developer.apple.com/videos/play/wwdc2020/10031

.onDelete() is not a solution for me because it's impossible to do 2 side buttons and impossible to edit "delete" text

  1. how to make 2 fingers swipe using swiftUI? (less important)
Faris
  • 358
  • 4
  • 10
Andrew_STOP_RU_WAR_IN_UA
  • 9,318
  • 5
  • 65
  • 101

3 Answers3

6

Unfortunately there isn't any native SwiftUI solution so far (as of SwiftUI 2 beta).


You can make your custom swipe actions using UIKit and wrap them in UIViewRepresentable.

Some solutions (you may have seen them already):


Or you can just use a library instead (at least until a native solution is developed).

Some libraries:


If you want to implement simultaneous swipe gesture you need to use UIViewRepresentable again:


Summing up

pawello2222
  • 46,897
  • 22
  • 145
  • 209
3

iOS 15+

In iOS 15 we can finally use native Swipe Actions:

func swipeActions<T>(edge: HorizontalEdge = .trailing, allowsFullSwipe: Bool = true, content: () -> T) -> some View where T : View

They can be attached to the ForEach container just like onMove or onDelete:

ForEach {
    // ...
}
.swipeActions(edge: .trailing) {
    Button {
        print("Editing...")
    } label: {
        Label("Edit", systemImage: "pencil")
    }
}
pawello2222
  • 46,897
  • 22
  • 145
  • 209
2

"How to implement swipe to delete?"

As long as you don't want to create custom UI for the delete button, you can take advantage of SwiftUI and use all of the built in features.

ForEach has a modifier called .onDelete, that gives you an IndexSet. This represents the rows that should be deleted when the user swipes. Now if we implement the necessary logic and wrap it in an animation block, everything will work as needed.

struct ContentView: View {
    @State var cars = ["Tesla Model 3", "BMW i3", "Roadster", "Cybertruck", "Agera Koenigsegg", "Rimac Concept One"]

    var body: some View {
        NavigationView {
            List {
                ForEach(cars, id: \.self) { car in
                    Text(car)
                }
                .onDelete { indexSet in
                    withAnimation {
                        cars.remove(atOffsets: indexSet)
                    }
                }
            }
            .navigationTitle("My Cars")
        }
    }
}

Note: .onDelete modifier is not available to use with List, can only be applied on ForEach.

Swipe to delete recording

"How to make a 2 fingers swipe gesture in SwiftUI?"

As of now SwiftUI does not have support for creating gestures for multiple fingers. The only solution is to use UIViewRepresentable in combination with UIPanGestureRecognizer. Then you can set the minimumNumberOfTouches to 2 fingers.

This post from Apple Developer Forum shows how you could achieve something similar for a simple 2 fingers tap gesture, but the idea and concept for swipe are very similar and already explained above.

Hope this helps!

Pondorasti
  • 607
  • 6
  • 14
  • What if I want to customize delete ui? – Neil Galiaskarov Jul 04 '20 at 13:38
  • 1
    Unfortunately, SwiftUI 2 / Xcode 12 beta 1 does not support custom UI for swipe to delete action. You are probably better off using UIKit, instead of implementing some hacky modifiers in SwiftUI. – Pondorasti Jul 04 '20 at 13:44
  • This question was created because of `.onDelete()` is not solution for me. Please, read the question once more: I need 2 buttons with custom text -- one is on the right side, one on the left. So this is not an answer on my question. – Andrew_STOP_RU_WAR_IN_UA Jul 06 '20 at 05:58
  • I am sorry for my poor understanding of the question. As an advise, better formatting of your question would probably attract more people to answer. Regarding the question, my suggestion is to just use UIKit and UIRepresentable, because SwiftUI does not have any support for what you are asking. – Pondorasti Jul 06 '20 at 15:35