Goal
I want to create a custom view that has a UITableView as a subview.
我想创建一个自定义视图,它有一个UITableView作为子视图。
The custom view creates the table view programmatically. To the outside world (i.e., the ViewController), though, the custom view itself would appear to be a table view.
自定义视图以编程方式创建表视图。到外面的世界去(例如但是,自定义视图本身看起来是一个表视图。
What I've tried
import UIKit
class CustomTableView: UIView {
// Do I make outlets?
//@IBOutlet var dataSource: UITableViewDataSource?
//@IBOutlet var delegate: UITableViewDelegate?
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect){
super.init(frame: frame)
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func layoutSubviews() {
super.layoutSubviews()
var tableView: UITableView!
tableView = UITableView(frame: self.bounds)
// I'm not sure how to set the delegate and dataSource
// tableView.dataSource = ???
// tableView.delegate = ???
self.addSubview(tableView)
}
}
After creating the UITableView programmatically and adding it as a subview to the custom view parent, I can't figure out how to get the custom view to act like it is the table view. That is, I don't know how to get the custom view to do the communication between the View Controller and the table view for the delegate
and dataSource
.
在以编程方式创建UITableView并将其作为子视图添加到自定义视图父视图之后,我无法弄清楚如何让自定义视图表现为表视图。也就是说,我不知道如何获取自定义视图来执行视图控制器和委托和数据源的表视图之间的通信。
What I've read
- Designing Your Data Source and Delegate
- 设计数据源和委托
- Create a static UITableView without Storyboards
- 创建一个没有故事板的静态UITableView。
These articles seemed good, but I got a little lost.
这些文章看起来不错,但我有点迷路了。
How do I make the custom view act like it's own table subview with regard to delegate and data source?
如何使自定义视图在委托和数据源方面表现为它自己的表子视图?
2 个解决方案
#1
1
It's not the most elegant, but jump out a level:
它并不是最优雅的,但它可以跳出一个层次:
class MyCustomViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
override func viewDidLoad() {
myCustomView = CustomTableView()
myCustomView.frame = ... /* layout programatically */
/* Alternatively to the above 2 lines,
lay out myCustomView in StoryBoard,
and capture it as an @IBOutlet. It will then be ready here
to muck with, well before it gets displayed and needs the data */
myCustomView.tableView.delegate = self
myCustomView.tableView.dataSource = self
}
/* Now implement all your dataSource and delegate methods */
}
* IMPORTANT *
*重要*
The key that you're missing is that tableView
must be a stored property of your custom view. It's important and needs to be promoted from a silly local! It should also be initialized in the awakeFromNib() function, even if you don't know the frame size. Then reset its frame at layout time.
您丢失的键是tableView必须是自定义视图的存储属性。这很重要,需要从一个愚蠢的地方提升!它也应该在awakeFromNib()函数中初始化,即使您不知道框架的大小。然后在布局时重置它的帧。
At a higher level, I don't know "What You're Really Trying To Do." It may not actually be the right implementation technique to be embedding a UITableView within a custom view; consider just laying out the UITableView in the main view. Then if you need decoration around it, lay those out as separate views in StoryBoard.
在更高的层次上,我不知道“你真正想做什么”。在自定义视图中嵌入UITableView实际上可能不是正确的实现技术;考虑在主视图中列出UITableView。如果你需要在它周围装饰,把它们作为故事板中的独立视图。
#2
1
The solution is to make the tableView
a property of the custom class (as @BaseZen suggested). Then provide properties and methods in the custom class to mimic and pass along the properties and methods needed from tableView
.
解决方案是使tableView成为自定义类的属性(如@BaseZen所建议的)。然后在自定义类中提供属性和方法,以模拟和传递tableView所需的属性和方法。
import UIKit
@IBDesignable class UICustomTableView: UIView {
private var myTableView: UITableView
required init(coder aDecoder: NSCoder) {
myTableView = UITableView()
super.init(coder: aDecoder)
}
override init(frame: CGRect){
myTableView = UITableView()
super.init(frame: frame)
}
override func awakeFromNib() {
super.awakeFromNib()
}
// TODO: @IBOutlet still can't be set in IB
@IBOutlet weak var delegate: UITableViewDelegate? {
get {
return myTableView.delegate
}
set {
myTableView.delegate = newValue
}
}
// TODO: @IBOutlet still can't be set in IB
@IBOutlet weak var dataSource: UITableViewDataSource? {
get {
return myTableView.dataSource
}
set {
myTableView.dataSource = newValue
}
}
func registerClass(cellClass: AnyClass?, forCellReuseIdentifier identifier: String) {
myTableView.registerClass(cellClass, forCellReuseIdentifier: identifier)
}
func dequeueReusableCellWithIdentifier(identifier: String) -> UITableViewCell? {
return myTableView.dequeueReusableCellWithIdentifier(identifier)
}
override func layoutSubviews() {
super.layoutSubviews()
// ...
// setup UITableView
myTableView.frame = self.bounds
self.addSubview(myTableView)
// ...
}
}
Then it can be used like a normal UITableView
:
然后它可以像普通的UITableView一样使用:
import UIKit
class TableViewDemoVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
@IBOutlet weak var customTableView: UICustomTableView!
var items: [String] = ["One", "Two", "Three"]
override func viewDidLoad() {
super.viewDidLoad()
// setup the table view from the IB reference
customTableView.delegate = self
customTableView.dataSource = self
customTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell = self.customTableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell!
cell.textLabel?.text = self.items[indexPath.row]
return cell
}
// ...
}
#1
1
It's not the most elegant, but jump out a level:
它并不是最优雅的,但它可以跳出一个层次:
class MyCustomViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
override func viewDidLoad() {
myCustomView = CustomTableView()
myCustomView.frame = ... /* layout programatically */
/* Alternatively to the above 2 lines,
lay out myCustomView in StoryBoard,
and capture it as an @IBOutlet. It will then be ready here
to muck with, well before it gets displayed and needs the data */
myCustomView.tableView.delegate = self
myCustomView.tableView.dataSource = self
}
/* Now implement all your dataSource and delegate methods */
}
* IMPORTANT *
*重要*
The key that you're missing is that tableView
must be a stored property of your custom view. It's important and needs to be promoted from a silly local! It should also be initialized in the awakeFromNib() function, even if you don't know the frame size. Then reset its frame at layout time.
您丢失的键是tableView必须是自定义视图的存储属性。这很重要,需要从一个愚蠢的地方提升!它也应该在awakeFromNib()函数中初始化,即使您不知道框架的大小。然后在布局时重置它的帧。
At a higher level, I don't know "What You're Really Trying To Do." It may not actually be the right implementation technique to be embedding a UITableView within a custom view; consider just laying out the UITableView in the main view. Then if you need decoration around it, lay those out as separate views in StoryBoard.
在更高的层次上,我不知道“你真正想做什么”。在自定义视图中嵌入UITableView实际上可能不是正确的实现技术;考虑在主视图中列出UITableView。如果你需要在它周围装饰,把它们作为故事板中的独立视图。
#2
1
The solution is to make the tableView
a property of the custom class (as @BaseZen suggested). Then provide properties and methods in the custom class to mimic and pass along the properties and methods needed from tableView
.
解决方案是使tableView成为自定义类的属性(如@BaseZen所建议的)。然后在自定义类中提供属性和方法,以模拟和传递tableView所需的属性和方法。
import UIKit
@IBDesignable class UICustomTableView: UIView {
private var myTableView: UITableView
required init(coder aDecoder: NSCoder) {
myTableView = UITableView()
super.init(coder: aDecoder)
}
override init(frame: CGRect){
myTableView = UITableView()
super.init(frame: frame)
}
override func awakeFromNib() {
super.awakeFromNib()
}
// TODO: @IBOutlet still can't be set in IB
@IBOutlet weak var delegate: UITableViewDelegate? {
get {
return myTableView.delegate
}
set {
myTableView.delegate = newValue
}
}
// TODO: @IBOutlet still can't be set in IB
@IBOutlet weak var dataSource: UITableViewDataSource? {
get {
return myTableView.dataSource
}
set {
myTableView.dataSource = newValue
}
}
func registerClass(cellClass: AnyClass?, forCellReuseIdentifier identifier: String) {
myTableView.registerClass(cellClass, forCellReuseIdentifier: identifier)
}
func dequeueReusableCellWithIdentifier(identifier: String) -> UITableViewCell? {
return myTableView.dequeueReusableCellWithIdentifier(identifier)
}
override func layoutSubviews() {
super.layoutSubviews()
// ...
// setup UITableView
myTableView.frame = self.bounds
self.addSubview(myTableView)
// ...
}
}
Then it can be used like a normal UITableView
:
然后它可以像普通的UITableView一样使用:
import UIKit
class TableViewDemoVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
@IBOutlet weak var customTableView: UICustomTableView!
var items: [String] = ["One", "Two", "Three"]
override func viewDidLoad() {
super.viewDidLoad()
// setup the table view from the IB reference
customTableView.delegate = self
customTableView.dataSource = self
customTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell = self.customTableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell!
cell.textLabel?.text = self.items[indexPath.row]
return cell
}
// ...
}