我知道我们的IBOutlets应该是私有的,但是例如,如果我在TableViewCell中有IBOutlets,我应该如何从另一个ViewController访问它们?下面是我问这类问题的例子:
class BookTableViewCell: UITableViewCell {
@IBOutlet weak private var bookTitle: UILabel!
}如果我向IBOutlet分配它应该是专用,则在访问单元属性时,在另一个ViewController中出现了一个错误:“bookTitle”由于“私有”保护级别而不可访问
发布于 2017-11-16 00:07:27
如果我正确理解您的问题,您假设@IBOutlet属性应该一直标记为private .这不是真的。但是直接访问属性也是不安全的。您可以看到ViewControllers、TableViewCells和这些对象有理由在可选IBOutlets上使用隐式展开.在使用故事板或只是在代码中的某个地方使用它们时,您不需要插入ViewController .另一种方式--想象一下,你是以编程方式创建VC的,并且将所有标签传递给初始化程序.会打爆你的头..。取而代之的是,您可以在故事板中添加以下内容:
@IBOutlet var myLabel: UILabel!
这很酷,您不需要在init上安装它,它就在那里等待设置,然后访问它的值.接口生成器将在ViewDidLoad之前为您处理初始化,因此标签在此之后不会为零.同样,在AwakeFromNib方法进入UITableViewCell子类之前,当您尝试访问bookTitle label属性时,它会崩溃,因为它将为0.这就是为什么这应该是隐私的棘手部分..。否则,当你知道VC是100%的现场分配,就没有必要害羞,使一切私密.
例如,在使用prepare(for segue:)方法时,不应该访问@IBOutlet。由于它们没有被分配,即使它们被分配了,它们也会被推送/现在/任何函数中的一些内部调用覆盖.
好吧,这很酷..。那现在该怎么办?
使用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或其他模式,在这些模式中,您不会在tableViewControllers中使用情节提要,也不会过多地嵌入单元格。(因为注册了单元格,但这是另一篇文章.)
我将给您一些提示,如何在不触及实际值的情况下在单元格/视图控制器中设置值,并使其安全.此外,良好的实践(安全)是使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设置为私有,因为您永远不知道您将如何使用它们,如果您需要更多的例子或什么东西是不清楚的,问你想要的……祝你编码愉快,学习深入!
发布于 2017-11-15 14:42:40
你应该只揭露你想要的。
例如,您可以只在单元格中使用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,但能够更改它的文本内容。
我通常做的是配置方法,它接收数据对象,并私下设置它的所有插座特性。
发布于 2017-11-15 14:36:13
我还没见过让IBOutlets成为私有的东西,至少对于细胞来说是如此。如果要这样做,请在单元内提供一个非私有的配置方法,您可以将值传递给该方法,该方法要分配给您的插座。您的单元内的功能可能如下所示:
func configure(with bookTitle: String) {
bookTitle.text = bookTitle
}编辑:当您更改单元格并添加新的出口时,这样的函数可能对将来很有用。然后,可以向configure函数中添加参数以处理这些问题。在使用该函数的任何地方都会出现编译器错误,这使您可以在任何使用该函数的地方正确设置单元格。这对于在不同的地方重用单元格的大型项目是有帮助的。
https://stackoverflow.com/questions/47310069
复制相似问题