首页 > 解决方案 > 协议委托不调用

问题描述

我是 Swift UIKit 的新手,并尝试使用 VIPER 架构制作应用程序。我试图根据这个主题制作它,但我无法从视图中调用演示者函数。这是我的 ViewController 代码

class PokemonViewController: UIViewController {

override func viewDidLoad() {
    super.viewDidLoad()
    setupUI()
    
    presenter?.viewDidLoad()
}

@objc func refresh() {
    presenter?.refresh()
}

var presenter: ViewToPresenterPokemonsProtocol?

let reuseIdentifier = "cell"

lazy var collectionView: UICollectionView = {
    let collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: UICollectionViewLayout.init())
    collectionView.dataSource = self
    collectionView.delegate = self
    return collectionView
}()

lazy var refreshControl: UIRefreshControl = {
    let refreshControl = UIRefreshControl()
    refreshControl.attributedTitle = NSAttributedString(string: "Refreshing")
    refreshControl.addTarget(self, action: #selector(refresh), for: .valueChanged)
    return refreshControl
}()

}

extension PokemonViewController: PresenterToViewPokemonsProtocol {

func onFetchPokemonsSuccess() {
    self.collectionView.reloadData()
    self.refreshControl.endRefreshing()
}

func onFetchPokemonsFailure(error: String) {
    print("View receives the response from Presenter with error: \(error)")
    self.refreshControl.endRefreshing()
}
}

还有我的演示者代码

class PokemonPresenter : ViewToPresenterPokemonsProtocol {

weak var view: PresenterToViewPokemonsProtocol?
var interactor: PresenterToInteractorPokemonsProtocol?
var router: PresenterToRouterPokemonsProtocol?

var pokemons: [Pokemon]?

func viewDidLoad() {
    print("Presenter is being notified that View was loaded.")
    interactor?.loadPokemons()
}

func refresh() {
    print("Presenter is being notified that the View was refreshed.")
    interactor?.loadPokemons()
}
}

extension PokemonPresenter: InteractorToPresenterPokemonsProtocol {
func fetchPokemonsSuccess(pokemons: [Pokemon]) {
    print("Presenter receives the result from Interactor after it's done its job.")
    self.pokemons = pokemons
    view?.onFetchPokemonsSuccess()
}

func fetchPokemonsFailure(error: String) {
    print("Presenter receives the result from Interactor after it's done its job.")
    view?.onFetchPokemonsFailure(error: "Couldn't fetch pokemons: \(error)")
}

//    func getPokemonSuccess(_ pokemon: Pokemon) {
//        router?.pushToPokemonDetail(on: view!, with: pokemon)
//    }

func getPokemonFailure() {
    print("Couldn't retrieve pokemon by index")
}
}

所以我不知道当我调用演示者的 viewDidLoad 时我做错了什么。我的断点在视图调用演示者的 viewDidLoad 时触发,但预设的 viewDidLoad 中的断点不会触发。

标签: iosswift

解决方案


这是因为您的应用程序中有两个不同的 PokemonViewController 实例——一个是您在模块路由器中创建的,一个来自故事板。

为什么?您正在使用场景委托。因此,这就是窗口所在的位置,也就是故事板自动加载的位置。您需要防止这种情况并手动构建界面 - 在场景委托中,而不是在应用程序委托中。应用委托代码无用;它组装了一个包含 viper 模块的层次结构,但所有这些都被丢弃了,因为应用程序委托window不是界面的一部分。

解决方案是:

  • 编辑 Info.plist。在 Application Scene Manifest 中,向下钻取 Storyboard Name 并将其删除。

  • 重写场景委托的第一个方法,以便它完成您的应用程序委托所做的工作。

像这样:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    guard let scene = (scene as? UIWindowScene) else { return }
    window = UIWindow(windowScene: scene)
    window?.backgroundColor = .white
    window?.rootViewController = PokemonRouter.createModule()
    window?.makeKeyAndVisible()
}

推荐阅读