私人 IB 网点 Swift

Private IBOutlets Swift

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

class BookTableViewCell: UITableViewCell {

    @IBOutlet weak private var bookTitle: UILabel!

}

如果我分配给 IBOutlet 它应该是 private,我在访问单元格 属性 时在另一个 ViewController 中出错: 由于 'private' 保护级别

'bookTitle' 无法访问

我还没有遇到过让 IBOutlets 私有化变得普遍,至少对于单元格而言。如果你想这样做,请在你的单元格中提供一个非私有的配置方法,你可以将值传递给它,你想分配给你的网点。您的单元格中的函数可能如下所示:

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

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

您应该只公开您需要的内容。

例如,您可以 setget 只有单元格中的 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 但可以更改其文本内容。

我通常做的是接收数据对象并私下设置其所有出口功能的配置方法。

如果我对您的问题的理解正确,您是在假设 @IBOutlet 属性应该始终标记为 private……但事实并非如此。但也直接访问属性根本不安全。您会看到 ViewControllers、TableViewCells 和这些对象出于某种原因在可选的 IBOutlets 上使用隐式解包...您不需要在使用故事板或仅在代码中的某处使用它们时初始化 ViewController。 . 另一种方式 - 想象一下你正在以编程方式创建 VC 并且你正在将所有标签传递给初始化程序......它会让你大吃一惊......而不是这个,你在故事板中使用这个:

@IBOutlet var myLabel: UILabel!

这很酷,您不需要在 init 上拥有它,它会在那里等待在访问它的值之前在某个地方设置...Interface builder 将在 ViewDidLoad 之前为您处理初始化,所以在那之后标签不会为零...再次在 AwakeFromNib 方法进入 UITableViewCell 子类之前,当您尝试访问您的 bookTitle 标签 属性 时,它会崩溃,因为它将为零...这是关于为什么这应该是私有的棘手部分...否则当您知道 VC 在分配的场景中是 100% 时,无需害羞并将所有内容设为私有。 ..

例如,当您使用 prepare(for segue:) 方法工作时,您永远不应该访问 @IBOutlets。由于它们没有被分配,即使它们被分配了,它们也会被 push/present/ 任何函数中的一些内部调用覆盖...

好的,太好了..那么现在该怎么办呢?

当使用 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 或其他模式,在这些模式中,您不使用带表 ViewController 的情节提要和过多嵌入单元格...(因为注册单元格,但那是另一篇文章...)

我会给你一些建议,如何在不触及实际值的情况下设置 cell/ViewController 中的值并使其安全......另外一个好的做法(安全性)是使 IBOutlets 可选100% 安全,但这不是必需的,老实说,解决这个问题的方法很奇怪:

ViewControllers:

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"
}

你看,你不需要将你的 IBOutlets 设置为私有,因为你永远不知道你将如何使用它们快乐的编码和深度学习!