代码之家  ›  专栏  ›  技术社区  ›  Latenec

私人IBOutlets Swift

  •  4
  • Latenec  · 技术社区  · 7 年前

    我知道我们的IBOutlets应该是私有的,但例如,如果我在TableViewCell中有IBOutlets,我应该如何从另一个ViewController访问它们?下面是我为什么要问这种问题的例子:

    class BookTableViewCell: UITableViewCell {
    
        @IBOutlet weak private var bookTitle: UILabel!
    
    }
    

    如果我分配给IBOutlet,它应该是 私有的 ,在访问单元格属性时,我在另一个ViewController中出错: 由于“私有”保护级别,无法访问“bookTitle”

    3 回复  |  直到 7 年前
        1
  •  7
  •   Bryan    5 年前

    如果我正确理解你的问题,你是在假设 @IBOutlet 属性应标记为 private

    @IBOutlet var myLabel: UILabel!

    这很酷,你不需要在init上使用它,它只需要在访问它的值之前在某处等待设置就可以了。。。Interface builder将在ViewDidLoad之前为您处理初始化,因此在此之后标签不会为零。。。再次之前 AwakeFromNib 方法位于UITableViewCell子类中,当您尝试访问 bookTitle 标签属性,它将崩溃,因为它将为零。。。这是关于为什么这应该是私人的棘手部分。。。否则,当你知道风投是百分之百在现场分配,没有必要害羞,使一切私人。。。

    例如,当你在 prepare(for segue:) 方法,您不应该访问@IBOutlets。因为它们没有分配,即使分配了,它们也会被push/present/whatever函数中的一些内部调用覆盖。。。

    好的,很酷。。那么现在该怎么办?

    UITableViewCell 子类,您可以安全地访问IBOutlets(仅当您使用故事板并且单元格位于TABLEVIEW中时)

    改变他们的价值观。。。你看

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
    // We shouldn't return just some constructor with UITableViewCell, but who cares for this purposes...
     guard let cell = tableView.dequeueReusableCell(withIdentifier: "bookTableViewCell", for: indexPath) else { return UITableViewCell() }
    cell.bookTitle.text = "any given text" // This should work ok because of interface builder...
    }
    

    上述情况应该适用于MVC模式,而不是MVVM或其他模式,在这些模式中,您不将故事板与TableViewController一起使用,并且嵌入了太多单元格。。。(因为注册单元格,但这是另一篇文章…)

    我将给你一些提示,如何在不接触实际值的情况下设置单元格/视图控制器中的值,并使其安全。。。此外,良好实践(安全)是使iBouts成为100%安全的可选方案,但这不是必要的,老实说,解决这个问题的方法很奇怪:

    视图控制器:

    class SomeVC: UIViewController {
    
        // This solution should be effective when those labels could be marked weak too...
        // Always access weak variables NOT DIRECTLY but with safe unwrap...
        @IBOutlet var titleLabel: UILabel?
        @IBOutlet var subtitleLabel: UILabel?
    
        var myCustomTitle: String?
        var myCustomSubtitle: String?
    
        func setup(with dataSource: SomeVCDataSource ) {
    
            guard let titleLabel = titleLabel, let subtitleLabel = subtitleLabel else { return }
            // Now the values are safely unwrapped and nothing can crash...
            titleLabel.text = dataSource.title
            subtitleLabel.text = dataSource.subtitle
        }
    
        // WHen using prepare for segue, use this:
        override func viewDidLoad() {
    
            super.viewDidLoad()
            titleLabel.text = myCustomTitle
            subtitleLabel.text = myCustomSubtitle
    
        }
    
    }
    
    struct SomeVCDataSource {
    
        var title: String
        var subtitle: String
    }
    

    下一个问题可能是:

        override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    
        guard let destinationVC = segue.destination as? SomeVC else { return }
    
        let datasource = SomeVCDataSource(title: "Foo", subtitle: "Bar")
        // This sets up cool labels... but the labels are Nil before the segue occurs and even after that, so the guard in setup(with dataSource:) will fail and return...
        destinationVC.setup(with: datasource)
    
        // So instead of this you should set the properties myCustomTitle and myCustomSubtitle to values you want and then in viewDidLoad set the values
        destinationVC.myCustomTitle = "Foo"
        destinationVC.myCustomSubtitle = "Bar"
    }
    

        2
  •  5
  •   DanielH    7 年前

    你应该只暴露你需要的东西。

    例如,你可以 set get 只有 text

    class BookTableViewCell: UITableViewCell {
        @IBOutlet weak private var bookTitleLabel: UILabel!
    
        var bookTitle: String? {
            set {
                bookTitleLabel.text = newValue
            }
            get {
                return bookTitleLabel.text
            }
        }
    }
    

    然后,无论你在哪里需要:

    cell.bookTitle = "It"
    

    现在外部对象无法访问 bookTitleLabel 但是可以更改它的文本内容。

        3
  •  0
  •   erik_m_martens    7 年前

    func configure(with bookTitle: String) {
        bookTitle.text = bookTitle
    }
    

    编辑:当你更换手机并添加新的插座时,这样的功能对未来很有用。然后可以将参数添加到 configure 函数来处理这些问题。在使用该函数的地方,到处都会出现编译器错误,这使您可以在使用该函数的地方正确设置单元格。这对于在不同地方重用单元的大型项目很有帮助。