Reputation: 3180
I try to implement UITableView programmatically without use of xib or Storyboards. This is my code:
ViewController.swift
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let table: UITableViewController = MyTableViewController()
let tableView: UITableView = UITableView()
tableView.frame = CGRect(x: 10, y: 10, width: 100, height: 500)
tableView.dataSource = table
tableView.delegate = table
self.view.addSubview(tableView)
}
}
MyTableViewController.swift
import UIKit
class MyTableViewController: UITableViewController {
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
NSLog("sections")
return 2
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
NSLog("rows")
return 3
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
NSLog("get cell")
let cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: "Cell")
cell.textLabel!.text = "foo"
return cell
}
}
But when I run app, all I get is empty table. In log I see a few lines of sections
and rows
, but no get cell
. How can I fix this code to get table with 6 lines of foo
text?
Upvotes: 63
Views: 110924
Reputation: 9431
Simple solution
import UIKit
class CustomTableViewController: UICollectionViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath as IndexPath)
cell.textLabel!.text = "\(indexPath.row)"
return cell
}
}
Upvotes: 0
Reputation: 13527
Swift 4 compatible
Instead of adding a UITableView
to your UIViewController
, you should consider creating a UITableViewController
and avoid setting up delegates:
class YourTVC: TableViewController {
override func viewDidLoad() {
super.viewDidLoad()
// setup custom cells if you use them
tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "yourCell")
}
// MARK: tableView
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3 // set to value needed
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "yourCell", for: indexPath) as! CustomTableViewCell
cell.textLabel?.text = "Cell at row \(indexPath.row)"
return cell
}
}
Upvotes: 8
Reputation: 19
import UIKit
class ViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(TableCell.self, forCellReuseIdentifier: "cell")
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableCell
cell.nameLabel.text = "TableViewCell programtically"
cell.nameLabel.textAlignment = .center
cell.nameLabel.textColor = .white
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
}
Upvotes: 0
Reputation: 8986
Note: As you mentioned you just started programming in Swift
. I created a tableView programmatically. Copy
and paste
below code into your viewController
and run the project...
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
private let myArray: NSArray = ["First","Second","Third"]
private var myTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let barHeight: CGFloat = UIApplication.shared.statusBarFrame.size.height
let displayWidth: CGFloat = self.view.frame.width
let displayHeight: CGFloat = self.view.frame.height
myTableView = UITableView(frame: CGRect(x: 0, y: barHeight, width: displayWidth, height: displayHeight - barHeight))
myTableView.register(UITableViewCell.self, forCellReuseIdentifier: "MyCell")
myTableView.dataSource = self
myTableView.delegate = self
self.view.addSubview(myTableView)
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Num: \(indexPath.row)")
print("Value: \(myArray[indexPath.row])")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath as IndexPath)
cell.textLabel!.text = "\(myArray[indexPath.row])"
return cell
}
}
Output:
Upvotes: 129
Reputation: 311
I had a similar issue in that the data would not populate for my programmatic UITableView. This was because I was using a delegate/dataSource without a strong reference. Once I kept a reference to it (I had one class implementing both UITableViewDataSource and UITableViewDelegate), the data was populated.
Upvotes: 0
Reputation: 3817
Updated for Swift 3
Option 1:
import UIKit
//
// MARK :- TableViewController
//
class TableViewController: UITableViewController {
private let headerId = "headerId"
private let footerId = "footerId"
private let cellId = "cellId"
//
// MARK :- HEADER
//
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 150
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: headerId) as! CustomTableViewHeader
return header
}
//
// MARK :- FOOTER
//
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 150
}
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let footer = tableView.dequeueReusableHeaderFooterView(withIdentifier: footerId) as! CustomTableViewFooter
return footer
}
//
// MARK :- CELL
//
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 150
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! CustomTableCell
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
title = "TableView Demo"
view.backgroundColor = .white
setupTableView()
}
func setupTableView() {
tableView.backgroundColor = .lightGray
tableView.register(CustomTableViewHeader.self, forHeaderFooterViewReuseIdentifier: headerId)
tableView.register(CustomTableViewFooter.self, forHeaderFooterViewReuseIdentifier: footerId)
tableView.register(CustomTableCell.self, forCellReuseIdentifier: cellId)
}
}
//
// MARK :- HEADER
//
class CustomTableViewHeader: UITableViewHeaderFooterView {
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
contentView.backgroundColor = .orange
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
//
// MARK :- FOOTER
//
class CustomTableViewFooter: UITableViewHeaderFooterView {
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
contentView.backgroundColor = .green
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
//
// MARK :- CELL
//
class CustomTableCell: UITableViewCell {
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.backgroundColor = .white
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Option 2: replace above Option 1 TableViewController with this class
import UIKit
//
// MARK :- ViewController
//
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
private let headerId = "headerId"
private let footerId = "footerId"
private let cellId = "cellId"
lazy var tableView: UITableView = {
let tv = UITableView(frame: .zero, style: .plain)
tv.translatesAutoresizingMaskIntoConstraints = false
tv.backgroundColor = .lightGray
tv.delegate = self
tv.dataSource = self
tv.register(CustomTableViewHeader.self, forHeaderFooterViewReuseIdentifier: self.headerId)
tv.register(CustomTableViewFooter.self, forHeaderFooterViewReuseIdentifier: self.footerId)
tv.register(CustomTableCell.self, forCellReuseIdentifier: self.cellId)
return tv
}()
//
// MARK :- HEADER
//
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 150
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: headerId) as! CustomTableViewHeader
return header
}
//
// MARK :- FOOTER
//
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 150
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let footer = tableView.dequeueReusableHeaderFooterView(withIdentifier: footerId) as! CustomTableViewFooter
return footer
}
//
// MARK :- CELL
//
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 150
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! CustomTableCell
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
title = "TableView Demo"
view.backgroundColor = .white
view.addSubview(tableView)
setupAutoLayout()
}
func setupAutoLayout() {
tableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
tableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
}
Upvotes: 24
Reputation: 163
You don't need to make a separate class for UITableView. Just in your ViewController class implement protocols of UITableViewDelegate and UITableViewDataSource and then implement delegate methods. I think your code should be like
class ViewController: UIViewController , UITableViewDelegate , UITableViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
let table: UITableViewController = MyTableViewController()
let tableView: UITableView = UITableView()
tableView.frame = CGRect(x: 10, y: 10, width: 100, height: 500)
tableView.dataSource = table
tableView.delegate = table
self.view.addSubview(tableView)
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
NSLog("sections")
return 2
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
NSLog("rows")
return 3
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
NSLog("get cell")
let cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: "Cell")
cell.textLabel!.text = "foo"
return cell
}
}
Tell us more info or show logs if you still face issue.
Upvotes: 3
Reputation: 318814
It makes no sense that you are using a UITableViewController
as the data source and delegate for your view controller's table view. Your own view controller should be the table view's data source and delegate.
Since you seem to want a view controller with a table view that doesn't take up the entire view, move every thing to your view controller as follows:
ViewController.swift:
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let tableView: UITableView = UITableView()
tableView.frame = CGRect(x: 10, y: 10, width: 100, height: 500)
tableView.dataSource = self
tableView.delegate = self
self.view.addSubview(tableView)
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
NSLog("sections")
return 2
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
NSLog("rows")
return 3
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
NSLog("get cell")
let cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: "Cell")
cell.textLabel!.text = "foo"
return cell
}
}
Upvotes: 4