我尝试将Swift MVC转换为MVVM,但当我切换到详细信息页面时无法获得数据?

jqjz2hbq  于 2023-03-28  发布在  Swift
关注(0)|答案(2)|浏览(104)

我正在尝试从MVC创建MVVM,但是我不能通过prepare传输之前可以传输的信息。原因是什么?我如何修复?

MainViewController

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

DetailViewModel

class DetailViewModel {
    
    // MARK: - Properties
    
    var pokemonId: String
    let apiService: PokeAPIService
    
    var name: String?
    var abilities: [Ability]?
    var imageURL: URL?
    
    // MARK: - Initializers
    
    init(pokemonId: String, apiService: PokeAPIService) {
        self.pokemonId = pokemonId
        self.apiService = apiService
    }
    
    // MARK: - Public Methods
    
    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()
        }
    }
}

DetailViewController

var viewModel: DetailViewModel?

@IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var abilitiesNameLabel: UILabel!
    @IBOutlet weak var abilitiesNameLabel2: UILabel!
// MARK: - Private Methods
    
     func configureView() {
        viewModel?.fetchPokemonDetails(completion: { [weak self] in
            guard let self = self else { return }
            DispatchQueue.main.async {
                self.nameLabel.text = self.viewModel?.name
                self.abilitiesNameLabel.text = self.viewModel?.abilities?[0].ability?.name
                self.abilitiesNameLabel2.text = self.viewModel?.abilities?[1].ability?.name
                if let imageURL = self.viewModel?.imageURL {
                    self.loadImage(from: imageURL)
                }
            }
        })
    }
    
     func loadImage(from url: URL) {
        URLSession.shared.dataTask(with: url) { [weak self] (data, response, error) in
            guard let self = self, let data = data else { return }
            DispatchQueue.main.async {
                self.imageView.image = UIImage(data: data)
            }
        }.resume()
    }

当我切换回MVC时,我没有任何问题。我找不到我做错了什么,似乎我错过了一些小东西,但我不能弄清楚。

sf6xfgos

sf6xfgos1#

你的问题在这里

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

在这一步,detailVC.viewModelnil,设置为VM的一些属性没有任何结果。所以在这个方法中,你需要创建DetailViewModel的示例,并使用所有必需的数据将其设置为detailVC,如下所示:

detailVC.viewModel = DetailViewModel(id: id, name: name, url: url)
omqzjyyz

omqzjyyz2#

它不工作的原因是因为MVVM结构可能不正确。在MVVM中,您不需要使用prepare方法。相反,您可以使用Observable或合并模式。这些模式在值发生更改时通知View Controller。在View Controller中,您可以使用Binder将ViewModel的输出绑定到View的输入。这样,视图总是与最新的ViewModel数据保持同步。
你可以从这个链接学习MVVM(可观察模式):- [1]:https://www.youtube.com/watch?v=sLHVxnRS75w
你可以从这个链接学习MVVM(合并):-https://www.youtube.com/watch?v=30LapcVtyBQ
我本可以修改你的代码来使用MVVM设计模式,但我认为理解它是如何工作的对你来说很重要,这就是为什么我为你提供了这两个教程链接。
谢谢你!

相关问题