fatbobman/SwipeCell

Trigger action after animation completed?

an-erd opened this issue · 5 comments

How can I trigger the action after the animation has completed and not when the user taps the CellButton?

With the action there will appear an additional system image in a row view in my app, and this should happen as soon as the swipe has been reset and the complete row is shown again.

I'm using the code from your examples:

                //Configure button
                let button1 = SwipeCellButton(
                    buttonStyle: .titleAndImage,
                    title: "Mark",
                    systemImage: "bookmark",
                    titleColor: .white,
                    imageColor: .white,
                    view: nil,
                    backgroundColor: .orange,
                    action: { beacon.flag.toggle() },
                    feedback: true
                )
                let slot1 = SwipeCellSlot(slots: [button1])

                GroupBoxListEntry(...)

                    .swipeCell(cellPosition: .left, leftSlot: slot1, rightSlot: nil)

Looked in the code and added an .delay option to SwipeCellSlotStyle and implement in SwipeCellViewModifier1.swift using the DispatchQueue.main.asyncAfter. Please find the pull request #8

hi an-erd,
Thanks for your advice.
I did some changes in codes to archive the function you needed.

.onChange(of: status){ status in
            switch status {
            case .showLeftSlot:
                leftSlot?.showAction?()
            case .showRightSlot:
                rightSlot?.showAction?()
            case .showCell:
                break
            }
        }

public struct SwipeCellSlot {
    
    public let showAction: (() -> Void)?

code in Demo

struct DoSomethingWithoutPress:View{
    let button = SwipeCellButton(
        buttonStyle: .titleAndImage,
        title: "Mark",
        systemImage: "bookmark",
        titleColor: .white,
        imageColor: .white,
        view: nil,
        backgroundColor: .green,
        action: { },
        feedback: true
    )

    var slotLeft:SwipeCellSlot{
        SwipeCellSlot(slots: [button],showAction: {print("do something Left")})
    }

    var slotRight:SwipeCellSlot{
        SwipeCellSlot(slots: [button],showAction: {print("do something Right")})
    }


    var body: some View{
        HStack{
            Text("Do something without press")
        }
        .frame(maxWidth:.infinity,alignment: .center)
        .frame(height:100)
        .swipeCell(cellPosition: .both, leftSlot: slotLeft, rightSlot: slotRight)
    }
}

Another solution is using transformEnvironment

struct DemoShowStatus:View{

    let button = SwipeCellButton(
        buttonStyle: .titleAndImage,
        title: "Mark",
        systemImage: "bookmark",
        titleColor: .white,
        imageColor: .white,
        view: nil,
        backgroundColor: .green,
        action: { },
        feedback: true
    )

    var slot:SwipeCellSlot{
        SwipeCellSlot(slots: [button])
    }

    @State var status:CellStatus = .showCell

    var body: some View{
        HStack{
            Text("Cell Status:")
            Text(status.rawValue)
                .foregroundColor(.red)
                //get the cell status from Environment
                .transformEnvironment(\.cellStatus, transform: { cellStatus in
                    let temp = cellStatus
                    DispatchQueue.main.async {
                        if self.status != temp {
                        self.status = temp
                        switch self.status{
                        case .showRightSlot:
                            print("do right action")
                        case .showLeftSlot:
                            print("do left action")
                        case .showCell:
                            break
                        }
                        }
                    }
                })
        }
        .frame(maxWidth:.infinity,alignment: .center)
        .frame(height:100)
        .swipeCell(cellPosition: .both, leftSlot: slot, rightSlot: slot)
    }
}

Thanks for adding this code, this was in deed a function I was missing, too, and currently using a workaround I now can remove and switch to your updated code.

I need an additional function (that was what I tried to explain in the original post): for example, in the first row (swipe left) of the demo app (demo1) the user swipes left and the buttons are shown. When the user now presses "Mark", the action action: { bookmark.toggle() } is fired immediately when the user presses. For my code this gives an undefined behaviour because the animation is still ongoing and needs to be completed.

What I want to achieve is to fire the action as soon as the animation has been completed.

In the pull request #8 I just waited 0.5 sec (not elegant but works). It has some drawbacks, for example when using a destroy button to toggle a bool (.destroy), it's not working anymore (because using SwipeCellSlotStyle destructive and not delay).

So, an universal configurable function on firing the action as soon as the animation is completed back to the starting position and when using destroy and tap functionality would be great.

Can this be done with .transformEnvironment, too?

This is the issue I have, so executing .toggle() after 0.5 secs works:

RPReplay_Final1617350481.mov
struct ClickRestoreAndAction:View{

    @State var marked = false
    @State var clicked = false

    var body: some View{

        let button = SwipeCellButton(
            buttonStyle: .titleAndImage,
            title: "Mark",
            systemImage: "bookmark",
            titleColor: .white,
            imageColor: .white,
            view: nil,
            backgroundColor: .green,
            action: { clicked = true },
            feedback: true
        )

        let slotLeft = SwipeCellSlot(slots: [button])

        HStack{
            Text("Marked:")
            Image(systemName: marked ? "flag.fill" : "flag")
        }
        .onChange(of: clicked){ _ in
            if clicked {
                clicked = false
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.3){
                    withAnimation{
                        marked.toggle()
                    }
                }
            }
        }
        .frame(maxWidth:.infinity,alignment: .center)
        .frame(height:100)
        .swipeCell(cellPosition: .both, leftSlot: slotLeft, rightSlot: nil)
    }
}