swift 单例对象或静态属性是否只是在接收方viewController未加载时在两个viewController之间发送数据的解决方案?

w8rqjzmb  于 2023-04-19  发布在  Swift
关注(0)|答案(1)|浏览(77)

我正在制作一个简单的演示程序。我想将选定的tableviewcell的产品数据发送到下一个ViewController(ProductDetailViewController)。
我认为直接访问另一个viewController的属性不值得考虑安全性,所以我制定了自定义协议并使用委托模式。
我的应用程序的rootViewController是ProductviewController,它是数据发送方对象。不幸的是,因为数据接收方对象“ProductDetailViewController”在ProductViewController.delegate?.didReceiveData(productList[indexPath.row])代码执行时尚未加载,所以什么都没有发生。
ProductDetailViewController至少被导航加载一次后,数据发送工作,但一开始不工作,所以成功了一半。
这是我密码
这是ProductViewController(数据发送器对象):

import UIKit

final class ProductViewController: UIViewController {

    static var delegate : ProductDetailViewDelegate?
    
    private let productTableView = UITableView()
    
    let productList : [Product] = [
        Product(name: "1907 Wall Set", cellImageName: "image-cell1", fullscreenImageName: "phone-fullscreen1"),
        Product(name: "1921 Dial Phone", cellImageName: "image-cell2", fullscreenImageName: "phone-fullscreen2"),
        Product(name: "1937 Desk Set", cellImageName: "image-cell3", fullscreenImageName: "phone-fullscreen3"),
        Product(name: "1984 Moto Portable", cellImageName: "image-cell4", fullscreenImageName: "phone-fullscreen4")
      ]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemGray6
        setTable()
        setUI()
    }
    
    func setTable() {
        productTableView.delegate = self
        productTableView.dataSource = self
        productTableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "Reused")
        productTableView.rowHeight = 94
        productTableView.backgroundColor = .systemGray6
        
        let header = UIView(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: 18))
        productTableView.tableHeaderView = header
    }
    
    func setUI() {
        productTableView.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(productTableView)
        
        NSLayoutConstraint.activate([
            productTableView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor),
            productTableView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor),
            productTableView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor),
            productTableView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor)
        ])
    }

}

extension ProductViewController : UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return productList.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Reused", for: indexPath) as! CustomTableViewCell
        cell.productName.text = productList[indexPath.row].name ?? "nonamed"
        cell.productImage.image = UIImage(named: productList[indexPath.row].cellImageName ?? "star")

        return cell
    }
    
    
}

extension ProductViewController : UITableViewDelegate{
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        
        let nextVC = ProductDetailViewController()
        
        ProductViewController.delegate?.didReceiveData(productList[indexPath.row])
        
        self.navigationController?.pushViewController(nextVC, animated: true)
    }

}

这是ProductDetailViewController(数据接收器对象)

import UIKit

protocol ProductDetailViewDelegate : AnyObject {
    
    func didReceiveData(_ product : Product?)
}

final class ProductDetailViewController: UIViewController {
     
    
    var currentProduct : Product?

    override func viewDidLoad() {
        super.viewDidLoad()
        ProductViewController.delegate = self
    }

}

extension ProductDetailViewController : ProductDetailViewDelegate {
    func didReceiveData(_ product: Product?) {
        currentProduct = product
    }
    
}
mwkjh3gx

mwkjh3gx1#

您使用了不应该使用的委托模式。切勿使用委托模式直接转发数据。您的ProductViewController需要在创建ProductDetailViewController时直接提供数据。由于ProductViewController直接创建并显示ProductDetailViewControllerProductViewController可以直接将数据交给ProductDetailViewController。不需要委托。不需要是静态的或单例的。
您的ProductDetailViewController类变为:

final class ProductDetailViewController: UIViewController {
    var currentProduct : Product?

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

删除ProductDetailViewDelegate协议和相应的ProductDetailViewController扩展。
然后ProductViewControllerdidSelectRowAt委托方法变为:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let nextVC = ProductDetailViewController()
        nextVC.currentProduct = productList[indexPath.row]
        
        self.navigationController?.pushViewController(nextVC, animated: true)
    }

同时删除ProductViewController中的静态delegate属性。现在您的代码更简单、更清晰。
UITableView为例,了解何时需要使用委托模式。许多不同的东西都可以创建表视图。表视图并不确切知道是什么创建了它。委托模式允许表视图在需要时请求其创建者(或任何其他被设置为其委托的对象)提供数据,并允许表视图通知其创建者有关事件的信息。
在您的例子中,ProductDetailViewController不需要向任何人请求数据,也不需要通知任何人任何事件。它的创建者可以在创建时将其初始数据传递给它,然后结束通信。
如果您稍后扩展ProductDetailViewController,使其确实需要一个委托模式来与其创建者进行通信,那么您需要修复您最初的尝试。

  • 恢复ProductDetailViewDelegate协议。
  • 将非静态delegate属性添加到ProductDetailViewController,而不是ProductViewController
  • ProductViewController添加实现ProductDetailViewDelegate的扩展。
  • ProductViewController创建ProductDetailViewController时,细节控制器的delegate属性设置为self
  • 如果需要,让ProductDetailViewDelegate调用其delegate上的委托方法来获取数据或通知它事件。

相关问题