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

Swift MVC转MVVM后详情页数据传递失败问题求助

问题:MVVM架构下Segue跳转无法传递数据的原因与修复

核心原因

  1. viewModel未初始化:DetailViewController的viewModel是可选类型(var viewModel: DetailViewModel?),在prepare(for:sender:)中尝试通过detailVC.viewModel?.xxx赋值时,viewModel处于nil状态,可选链调用会直接跳过,导致赋值完全无效。
  2. ViewModel属性设计冲突:DetailViewModel的pokemonId是非可选存储属性,必须在初始化时赋值,但原代码试图在初始化后修改它,不符合类的初始化逻辑。

修复方案

1. 提前初始化ViewModel并直接赋值

在prepare(for:sender:)中创建DetailViewModel实例,直接赋值给detailVC.viewModel,而非依赖未初始化的可选属性:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "showDetail", let detailVC = segue.destination as? DetailViewController, let indexPath = tableView.indexPathForSelectedRow {
        let pokemonId = "\(indexPath.row + 1)"
        // 初始化ViewModel,传入必填参数
        let detailViewModel = DetailViewModel(pokemonId: pokemonId, apiService: PokeAPIService())
        // 直接赋值给detailVC的viewModel
        detailVC.viewModel = detailViewModel
        
        // 传递初始数据
        let pokemon = viewModel.pokemons[indexPath.row]
        detailViewModel.name = pokemon.name
        if let url = viewModel.getPokemonSpritesURL(for: indexPath.row) {
            detailViewModel.imageURL = url
        }
    }
}

2. 优化ViewModel初始化逻辑(可选)

如果希望在初始化时就传入所有初始数据,可以扩展ViewModel的初始化方法,避免后续零散赋值:

class DetailViewModel {
    var pokemonId: String
    let apiService: PokeAPIService
    
    var name: String?
    var abilities: [Ability]?
    var imageURL: URL?

    // 扩展初始化方法,支持传入初始数据
    init(pokemonId: String, apiService: PokeAPIService, name: String? = nil, imageURL: URL? = nil) {
        self.pokemonId = pokemonId
        self.apiService = apiService
        self.name = name
        self.imageURL = imageURL
    }

    // 原fetch方法保持不变
    func fetchPokemonDetails(completion: @escaping () -> Void) {
        apiService.getPokemon(by: pokemonId) { [weak self] response in
            guard let self = self else { return }
            self.name = response.name
            self.abilities = response.abilities
            if let urlString = response.sprites?.frontDefault {
                self.imageURL = URL(string: urlString)
            }
            completion()
        }
    }
}

对应prepare方法可以简化为:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "showDetail", let detailVC = segue.destination as? DetailViewController, let indexPath = tableView.indexPathForSelectedRow {
        let pokemonId = "\(indexPath.row + 1)"
        let pokemon = viewModel.pokemons[indexPath.row]
        
        let detailViewModel = DetailViewModel(
            pokemonId: pokemonId,
            apiService: PokeAPIService(),
            name: pokemon.name,
            imageURL: viewModel.getPokemonSpritesURL(for: indexPath.row)
        )
        detailVC.viewModel = detailViewModel
    }
}

3. 确保ViewController触发UI配置

检查DetailViewController的viewDidLoad中是否调用了configureView(),否则即使ViewModel初始化完成,也不会触发数据获取和UI更新:

override func viewDidLoad() {
    super.viewDidLoad()
    configureView()
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 12:43:21