如何在 Swift 中正确实现不同视图控制器之间的协议和委托?

Posted

技术标签:

【中文标题】如何在 Swift 中正确实现不同视图控制器之间的协议和委托?【英文标题】:How to properly implement protocols and delegates between different view controllers in Swift? 【发布时间】:2020-03-23 06:04:51 【问题描述】:

所以我是协议和委托的新手。但是,在了解了它之后,我尝试在我的项目中使用它。我不确定我是否做错了什么。但我无法更新标签。


import UIKit

class CountryVC: UIViewController 
    //MARK:-Properties   
    let deathLabel: UILabel = 
        let label = UILabel()
        label.textColor = .black
        label.textAlignment = .center
        label.text = "I am Death"
        return label
    ()
    let recoveredLabel: UILabel = 
        let label = UILabel()
        label.textColor = .black
        label.textAlignment = .center
        return label
    ()
    let infectedLabel: UILabel = 
        let label = UILabel()
        label.textColor = .black
        label.textAlignment = .center
        return label
    ()
    override func viewDidLoad() 
        super.viewDidLoad()
        view.backgroundColor = .white
        view.addSubview(deathsView)
        view.addSubview(infectedView)
        view.addSubview(recoveredView)

        deathsView.anchor(top: view.topAnchor, left: view.leftAnchor, right: view.rightAnchor, paddingTop: 100, paddingLeft: 20, paddingRight: 20, height: 100)
        deathsView.addSubview(deathLabel)
        deathLabel.anchor()
        deathLabel.centerXAnchor.constraint(equalTo: deathsView.centerXAnchor).isActive = true
        deathLabel.centerYAnchor.constraint(equalTo: deathsView.centerYAnchor).isActive = true

        infectedView.anchor(top: deathsView.bottomAnchor, left: view.leftAnchor, right: view.rightAnchor, paddingTop: 20, paddingLeft: 20, paddingRight: 20, height: 100)
        deathsView.addSubview(infectedLabel)
        infectedLabel.anchor()
        infectedLabel.centerXAnchor.constraint(equalTo: infectedView.centerXAnchor).isActive = true
        infectedLabel.centerYAnchor.constraint(equalTo: infectedView.centerYAnchor).isActive = true       
        recoveredView.anchor(top: infectedView.bottomAnchor, left: view.leftAnchor, right: view.rightAnchor, paddingTop: 20, paddingLeft: 20, paddingRight: 20, height: 100)
        recoveredView.addSubview(recoveredLabel)
        recoveredLabel.anchor()
        recoveredLabel.centerXAnchor.constraint(equalTo: recoveredView.centerXAnchor).isActive = true
        recoveredLabel.centerYAnchor.constraint(equalTo: recoveredView.centerYAnchor).isActive = true
    


extension CountryVC: UpdateCountry 
    func update(allCases: Int, recovered: Int, deaths: Int) 
        DispatchQueue.main.async 
            self.deathLabel.text = String(deaths)
            self.recoveredLabel.text = String(recovered)
            self.infectedLabel.text = String(allCases)
        
        print (String(deaths))
        print(String(recovered))
        print(String(allCases))
    


我可以看到数据被传递,因为我可以打印数据。我在我的另一个表视图控制器中调用了更新函数来获取这样的数据。


class CountryTableControllerTableViewController: UITableViewController 
    //MARK:- Properties
    var delegate: UpdateCountry!
    //MARK:- Initialization
    override func viewDidLoad() 
        super.viewDidLoad()
        delegate = CountryVC()
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "reuseIdentifier")
        self.navigationItem.title = "Select Your Country"
    
    // MARK: - Table view data source
    override func numberOfSections(in tableView: UITableView) -> Int  1 
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int  countries.count 
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
        cell.textLabel?.text = countries[indexPath.row]
        return cell
    
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 
        let urlCountry = "https://corona.lmao.ninja/countries/\(countries[indexPath.row])"
        let acturalUrl = urlCountry.lowercased()
        guard let url = URL(string: acturalUrl) else  return 
        let controler = CountryVC()
        controler.modalPresentationStyle = .fullScreen
        present(controler, animated: true, completion: nil)
        URLSession.shared.dataTask(with: url) (data, response, error) in
            guard let data = data else return
            do 
                let allCases = try JSONDecoder().decode(CountryCases.self, from: data)
                self.delegate.update(allCases: allCases.cases, recovered: allCases.recovered, deaths: allCases.deaths)  
             catch let jsonErr 
                print("Error serializing json", jsonErr)
            
        .resume()     
    

控制器显示数据,但 countryVC 中的标签未更新。

【问题讨论】:

您需要显示您的 CountryVC 类、您的 updateCountry 协议和 tableview 代码。确保设置其委托。请注意,以大写字母 UpdateCountry 开头的协议命名是 Swift 命名约定 请注意,您的代码是异步的。在完成处理程序中解码 json 之前,您应该向您展示 CountryVC 控制器。在调用 resume 后立即呈现它,它不会等待您的异步请求完成。 为什么DispatchQueue集合中标签的值 标签中的值在主线程中设置,因为更新 UI 元素总是在主队列中完成。 @LeoDabus 即使这样做了,我仍然看不到标签中的更新。 【参考方案1】:

首先按如下方式创建您的协议:

protocol CountryViewControllerDelegate: class 
    func updateLabels(_ controller: CountryViewController, country: String, allCases: Int, recovered: Int, deaths: Int)


然后在您的 CountryViewController 中:

class CountryViewController: UIViewController 
    weak var delegate: CountryViewControllerDelegate?
    // code ...


extension CountryViewController: CountryViewControllerDelegate 
    func updateLabels(_ controller: CountryViewController, country: String,  allCases: Int, recovered: Int, deaths: Int) 
        print("CountryViewController:", #function)
        DispatchQueue.main.async 

            // update your labels in the main thread 
            // self.deathLabel.text = String(deaths)
            // self.recoveredLabel.text = String(recovered)
            // self.infectedLabel.text = String(allCases)

            print("Country:", country)
            print("deaths:", deaths)
            print("recovered:", recovered)
            print("infected:", allCases)
        
    


终于在你的 CountryTableViewController 中:

class CountryTableViewController: UITableViewController, CountryViewControllerDelegate  

    weak var delegate: CountryViewControllerDelegate?
    // code ...

    override func viewDidLoad() 
        super.viewDidLoad()
        self.navigationItem.title = "Select Your Country"
        delegate = self
    

    // code ...

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 
        let country = countries[indexPath.row]
        let string = "https://corona.lmao.ninja/countries/\(country.lowercased())"
        guard let url = URL(string: link) else  return 
        let storyBoard = UIStoryboard(name: "Main", bundle: nil)
        let controller = storyBoard.instantiateViewController(withIdentifier: "CountryViewController") as! CountryViewController
        controller.delegate = self
        controller.modalPresentationStyle = .pageSheet
        controller.title = country
        let countryNavigationController = UINavigationController(rootViewController: controller)
        countryNavigationController.viewControllers = [controller]
        self.present(countryNavigationController, animated: true)

        URLSession.shared.dataTask(with: url)  data, response, error  in
            guard let data = data else  return 
            if let string = String(data: data, encoding: .utf8), string == "Country not found"  
                self.updateLabels(controller, country: country, allCases: 0, recovered: 0, deaths: 0)
                return
            
            do 
                let allCases = try JSONDecoder().decode(CountryCases.self, from: data)
                self.updateLabels(controller, country: country, allCases: allCases.cases, recovered: allCases.recovered, deaths: allCases.deaths)
             catch 
                print("JSON error:", error)
            
        .resume()
    
    func updateLabels(_ controller: CountryViewController, country: String, allCases: Int, recovered: Int, deaths: Int) 
        print("CountryTableViewController:", #function)
        controller.updateLabels(controller, country: country, allCases: allCases, recovered: recovered, deaths: deaths)
    

Sample Project

【讨论】:

以上是关于如何在 Swift 中正确实现不同视图控制器之间的协议和委托?的主要内容,如果未能解决你的问题,请参考以下文章

ios - 如何在 ios swift 中正确实现后台获取

在这种情况下,如何在 Flutter 中正确实现 FutureBuilder?

如何在 sql 数据库中正确实现商务数据关系?

如何在 Windows DLL 中正确实现和关闭休眠线程

如何在 Google App Engine 中正确实现多个模块的多个版本?

如何在python的类中正确实现辅助函数