SwiftUI如何不依赖UIKit编程模拟屏幕点击/滑动手势?
纯SwiftUI实现方案
完全可以不用依赖UIKit实现该需求,核心思路是复用你现有手势逻辑用到的状态变量,通过按钮触发状态变更并绑定匹配的动画即可,具体实现步骤如下:
- 首先梳理你现有卡片组件用到的核心状态:通常会包含
cardOffset(卡片偏移量,CGSize类型)、cardRotation(卡片旋转角度,Double类型)、cardOpacity(滑出时的透明度)这几个核心状态,手动拖拽时DragGesture的实时变更就是修改这几个值实现动效 - 给点赞/点踩按钮分别绑定点击事件,点击时先给状态变量赋值左右滑出的对应目标值:
- 点踩(左滑):设置
cardOffset = CGSize(width: -500, height: 0),同时给对应旋转角度赋值(比如-15度),透明度设为0 - 点赞(右滑):设置
cardOffset = CGSize(width: 500, height: 0),旋转角度设为15度,透明度设为0
- 点踩(左滑):设置
- 所有状态变更包裹在
withAnimation块中,动画时长和曲线和你手动滑动松手后的滑出动画保持一致,就能得到和手动滑动完全一致的效果 - 滑出动画结束后,执行你原有的卡片堆数据移除逻辑,把当前最上层卡片从数组中移除即可
核心代码示例
// 卡片核心状态 @State private var offset: CGSize = .zero @State private var rotation: Double = 0 @State private var currentCardIndex = 0 // 按钮点击事件 func swipeCard(isLike: Bool) { let targetOffset: CGFloat = isLike ? 500 : -500 let targetRotation: Double = isLike ? 15 : -15 // 动画参数和你手势滑出的参数保持一致 withAnimation(.easeOut(duration: 0.3)) { offset = CGSize(width: targetOffset, height: 0) rotation = targetRotation } // 动画结束后更新卡片堆 DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { offset = .zero rotation = 0 currentCardIndex += 1 } } // 卡片的修饰符用法和你原有拖拽逻辑保持一致 CardView() .offset(offset) .rotationEffect(.degrees(rotation)) .gesture( // 你原有的DragGesture逻辑不用改动 DragGesture() .onChanged { value in offset = value.translation rotation = Double(value.translation.width / 20) } .onEnded { value in // 原有松手判定逻辑,滑出或者回弹的动画参数和上面按钮的保持一致即可 if abs(value.translation.width) > 150 { swipeCard(isLike: value.translation.width > 0) } else { withAnimation(.easeOut(duration: 0.2)) { offset = .zero rotation = 0 } } } )
注意要保证按钮触发的动画参数(时长、动画曲线)和拖拽松手后滑出的动画参数完全一致,就能让两种操作的视觉效果没有任何差异,全程不需要调用任何UIKit相关的API。
内容的提问来源于stack exchange,提问作者ktom
相关产品推荐
相关产品推荐

