首页 > 解决方案 > 如何在 SwiftUI 中以编程方式在屏幕的某些部分模拟点击/滑动手势?

问题描述

我有一个 SwiftUI 应用程序,其行为类似于 Tinder(使用本指南构建- 或查看源代码)。您只需左右滑动一堆卡片。我有一个“竖起大拇指”和“竖起大拇指”按钮作为刷卡的替代品,但是当它们被按下时,我希望它用户刷卡,就好像他们自己刷卡一样。

在没有 UIKit 的 SwiftUI 中有什么方法可以做到这一点?

标签: iosswiftuiswipegesturetinder

解决方案


If your thumbs up and down buttons were part of the CardView then it would be quite simple to achieve: just repeat whatever code executes on gesture inside a button action. Something along these lines:

                   // CardView.swift

                   Button (action: {
                        // hardcoded for simplicity
                        self.translation = .init(width: 100, height: 0)
                        self.swipeStatus = .like
                        // a hacky way to "wait" for animation to complete
                        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3, execute: {
                            self.onRemove(self.user)
                        })
                    }) {
                        Image(systemName: "hand.thumbsup")
                            .foregroundColor(.green)
                    }

But, if your buttons reside outside of the CardView, I think you'll have to re-architect your views a little bit. I would start by converting swipeStatus from @State into a @Binding, so its value can be passed down from the parent view. Then you will need to declare a state variable in your ContentView, which you can then tie together with swipeStatus binding. Something like:

                                    // ContentView.swift

                                    @State var currentSwipeStatus: LikeDislike = .none
                                    ....
                                    CardView(
                                        swipeStatus: self.users.last == user
                                        ? $currentSwipeStatus
                                        : .constant(.none),
                                         user: user,
                                         onRemove: { removedUser in
                                        self.users.removeAll { $0.id == removedUser.id}
                                        self.swipeStatus = .none
                                    })

Within your card view you'll need to react to the bound value changing. I just appended onChange to the main VStack:

.onChange(of: swipeStatus) { newState in
                if newState == .like {
                        self.translation = .init(width: 100, height: 0)
                        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3, execute: {
                            self.onRemove(self.user)
                        })
                }
            }

I feel like there's a more elegant way of achieving this. I'm not a big fan of the onRemove callback, asyncAfter hack, and the fact that they render all cards at once. But at least that's a start.


推荐阅读