You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,大概率是因为:

  1. 循环引用:ViewModel持有视图的强引用,同时ViewController持有ViewModel的强引用,导致两者都无法被释放,最终引发内存问题或崩溃。
  2. 视图生命周期不匹配:ViewModel可能在视图还未初始化完成、或者已经销毁后去操作它,导致野指针访问崩溃。

按照上面的方案,ViewModel完全不持有视图引用,只负责逻辑处理,ViewController负责视图操作,既符合MVVM的设计原则,也避免了崩溃问题。

内容的提问来源于stack exchange,提问作者tore

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:20:54