Swift中如何传递视图参数?滑动手势移至ViewModel遇崩溃
解决滑动手势代码迁移至ViewModel的问题
首先得说,你的思路里有个核心问题——ViewModel不应该直接操作视图,这违背了MVVM的职责划分(ViewModel负责业务/逻辑处理,ViewController负责视图管理)。直接把视图实例传给ViewModel,很容易引发循环引用、内存泄漏,甚至因为视图生命周期问题导致崩溃(比如视图还没完成初始化就被ViewModel操作,或者视图已销毁但ViewModel还持有引用)。
下面给你一套符合MVVM规范的解决方案:
正确的实现思路
我们让ViewModel专注处理滑动手势的逻辑计算(比如判断滑动方向、计算偏移量、处理业务规则),然后通过回调闭包或者Combine发布者把处理结果通知给ViewController,由ViewController来负责实际的视图操作。
方案1:使用回调闭包(适合基础场景)
第一步:定义ViewModel
import UIKit class SwipeViewModel { // 定义回调闭包,用来传递滑动结果(比如方向、偏移量) var onSwipeHandled: ((UISwipeGestureRecognizer.Direction, CGPoint) -> Void)? func handleSwipe(_ gesture: UISwipeGestureRecognizer) { guard let direction = gesture.direction as? UISwipeGestureRecognizer.Direction, let translation = gesture.view?.translation(in: gesture.view?.superview) else { return } // 这里处理你的滑动逻辑:比如判断是否触发某个动作、计算业务相关的值 // ... 你的逻辑代码 ... // 把结果通过闭包传给ViewController onSwipeHandled?(direction, translation) } }
第二步:在ViewController中绑定ViewModel
class YourViewController: UIViewController { private let vm = SwipeViewModel() override func viewDidLoad() { super.viewDidLoad() setupSwipeGesture() bindViewModel() } private func setupSwipeGesture() { let swipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(_:))) swipeGesture.direction = .right // 根据你的需求设置方向 view.addGestureRecognizer(swipeGesture) } private func bindViewModel() { vm.onSwipeHandled = { [weak self] direction, translation in guard let self = self else { return } // 这里由ViewController负责操作视图,比如移动某个控件、更新UI switch direction { case .right: self.yourCustomView.transform = CGAffineTransform(translationX: translation.x, y: 0) // 其他方向的处理... default: break } } } @objc private func handleSwipe(_ gesture: UISwipeGestureRecognizer) { // 把手势事件交给ViewModel处理逻辑 vm.handleSwipe(gesture) } }
方案2:使用Combine(适合复杂的响应式场景)
如果你的项目用了Combine,可以用发布者来传递事件,代码更简洁:
ViewModel代码
import UIKit import Combine class SwipeViewModel { // 定义发布者,输出滑动信息 let swipeEvent = PassthroughSubject<(direction: UISwipeGestureRecognizer.Direction, translation: CGPoint), Never>() func handleSwipe(_ gesture: UISwipeGestureRecognizer) { guard let direction = gesture.direction as? UISwipeGestureRecognizer.Direction, let translation = gesture.view?.translation(in: gesture.view?.superview) else { return } // 处理逻辑... // 发送事件 swipeEvent.send((direction, translation)) } }
ViewController代码
class YourViewController: UIViewController { private let vm = SwipeViewModel() private var cancellables = Set<AnyCancellable>() override func viewDidLoad() { super.viewDidLoad() setupSwipeGesture() bindViewModel() } private func setupSwipeGesture() { let swipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(_:))) swipeGesture.direction = .right view.addGestureRecognizer(swipeGesture) } private func bindViewModel() { vm.swipeEvent .receive(on: DispatchQueue.main) .sink { [weak self] event in guard let self = self else { return } // 操作视图 switch event.direction { case .right: self.yourCustomView.transform = CGAffineTransform(translationX: event.translation.x, y: 0) default: break } } .store(in: &cancellables) } @objc private func handleSwipe(_ gesture: UISwipeGestureRecognizer) { vm.handleSwipe(gesture) } }
为什么你之前的方法会崩溃?
你尝试直接传递self.view给ViewModel,大概率是因为:
- 循环引用:ViewModel持有视图的强引用,同时ViewController持有ViewModel的强引用,导致两者都无法被释放,最终引发内存问题或崩溃。
- 视图生命周期不匹配:ViewModel可能在视图还未初始化完成、或者已经销毁后去操作它,导致野指针访问崩溃。
按照上面的方案,ViewModel完全不持有视图引用,只负责逻辑处理,ViewController负责视图操作,既符合MVVM的设计原则,也避免了崩溃问题。
内容的提问来源于stack exchange,提问作者tore
相关产品推荐
相关产品推荐

