I've been working with Swift and iOS for a number of months now. I am familiar with many of the ways things are done but I'm not good enough that I can just write things up without looking. I've appreciated Stack Overflow in the past for providing quick answers to get me back on track with topics I've gotten rusty on (for example, AsyncTask Android example).
iOS's UITableView is in this category for me. I've done them a few times, but I forget what the details are. I couldn't find another question on StackOverflow that just asks for a basic example and I'm looking for something shorter than many of the tutorials that are online (although this one is very good).
I am providing an answer below for my future reference and yours.
The example below is an adaptation and simplification of a longer post from We ❤ Swift. This is what it will look like:
Create a New Project
It can be just the usual Single View Application.
Add the Code
Replace the ViewController.swift code with the following:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// Data model: These strings will be the data for the table view cells
let animals: [String] = ["Horse", "Cow", "Camel", "Sheep", "Goat"]
// cell reuse id (cells that scroll out of view can be reused)
let cellReuseIdentifier = "cell"
// don't forget to hook this up from the storyboard
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Register the table view cell class and its reuse id
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier)
// (optional) include this line if you want to remove the extra empty cell divider lines
// self.tableView.tableFooterView = UIView()
// This view controller itself will provide the delegate methods and row data for the table view.
tableView.delegate = self
tableView.dataSource = self
}
// number of rows in table view
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.animals.count
}
// create a cell for each table view row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// create a new cell if needed or reuse an old one
let cell:UITableViewCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as UITableViewCell!
// set the text from the data model
cell.textLabel?.text = self.animals[indexPath.row]
return cell
}
// method to run when table view cell is tapped
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped cell number \(indexPath.row).")
}
}
Read the in-code comments to see what is happening. The highlights are
The view controller adopts the UITableViewDelegate and UITableViewDataSource protocols.
The numberOfRowsInSection method determines how many rows there will be in the table view.
The cellForRowAtIndexPath method sets up each row.
The didSelectRowAtIndexPath method is called every time a row is tapped.
Add a Table View to the Storyboard
Drag a UITableView onto your View Controller. Use auto layout to pin the four sides.
Hook up the Outlets
Control drag from the Table View in IB to the tableView outlet in the code.
Finished
That's all. You should be able run your app now.
This answer was tested with Xcode 9 and Swift 4
Variations
Row Deletion
You only have to add a single method to the basic project above if you want to enable users to delete rows. See this basic example to learn how.
Row Spacing
If you would like to have spacing between your rows, see this supplemental example.
Custom cells
The default layout for the table view cells may not be what you need. Check out this example to help get you started making your own custom cells.
Dynamic Cell Height
Sometimes you don't want every cell to be the same height. Starting with iOS 8 it is easy to automatically set the height depending on the cell content. See this example for everything you need to get you started.
Further Reading
iOS & Swift Tutorial: UITableViewController
iOS Table View Tutorial Using Swift
For completeness sake, and for those that do not wish to use the Interface Builder, here's a way of creating the same table as in Suragch's answer entirely programatically - albeit with a different size and position.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var tableView: UITableView = UITableView()
let animals = ["Horse", "Cow", "Camel", "Sheep", "Goat"]
let cellReuseIdentifier = "cell"
override func viewDidLoad() {
super.viewDidLoad()
tableView.frame = CGRectMake(0, 50, 320, 200)
tableView.delegate = self
tableView.dataSource = self
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier)
self.view.addSubview(tableView)
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return animals.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:UITableViewCell = tableView.dequeueReusableCellWithIdentifier(cellReuseIdentifier) as UITableViewCell!
cell.textLabel?.text = animals[indexPath.row]
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("You tapped cell number \(indexPath.row).")
}
}
Make sure you have remembered to import UIKit.
In Swift 4.1 and Xcode 9.4.1
Add UITableViewDataSource, UITableViewDelegate delegated to your class.
Create table view variable and array.
In viewDidLoad create table view.
Call table view delegates
Call table view delegate functions based on your requirement.
import UIKit
// 1
class yourViewController: UIViewController , UITableViewDataSource, UITableViewDelegate {
// 2
var yourTableView:UITableView = UITableView()
let myArray = ["row 1", "row 2", "row 3", "row 4"]
override func viewDidLoad() {
super.viewDidLoad()
// 3
yourTableView.frame = CGRect(x: 10, y: 10, width: view.frame.width-20, height: view.frame.height-200)
self.view.addSubview(yourTableView)
// 4
yourTableView.dataSource = self
yourTableView.delegate = self
}
// 5
// MARK - UITableView Delegates
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell : UITableViewCell? = tableView.dequeueReusableCell(withIdentifier: "cell")
if cell == nil {
cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
}
if self. myArray.count > 0 {
cell?.textLabel!.text = self. myArray[indexPath.row]
}
cell?.textLabel?.numberOfLines = 0
return cell!
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50.0
}
If you are using storyboard, no need for Step 3.
But you need to create IBOutlet for your table view before Step 4.
SWIFT 5
If you only want a tableView on your screen then you can implement UITableViewController to your ViewController and do like this to show a simple tableViewController with a label in it.
Swift file
class ToDoListViewController: UITableViewController {
let array = ["GAFDGSG","VSBFFSB","BFBFB"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
array.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ToDoItemCell", for: indexPath)
cell.textLabel?.text = array[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath)
}
}
And in storyboard create a UITableViewController with mentioning the identifier like this
MainStoryboard
Result
Here is the Swift 4 version.
import Foundation
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource
{
var tableView: UITableView = UITableView()
let animals = ["Horse", "Cow", "Camel", "Sheep", "Goat"]
let cellReuseIdentifier = "cell"
override func viewDidLoad()
{
super.viewDidLoad()
tableView.frame = CGRect(x: 0, y: 50, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height)
tableView.delegate = self
tableView.dataSource = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier)
self.view.addSubview(tableView)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return animals.count
}
internal func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell:UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as UITableViewCell!
cell.textLabel?.text = animals[indexPath.row]
return cell
}
private func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: IndexPath)
{
print("You tapped cell number \(indexPath.row).")
}
}
// UITableViewCell set Identify "Cell"
// UITableView Name is tableReport
UIViewController,UITableViewDelegate,UITableViewDataSource,UINavigationControllerDelegate, UIImagePickerControllerDelegate {
#IBOutlet weak var tableReport: UITableView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableReport.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = "Report Name"
return cell;
}
}
Related
I wanted to give a go at swift, and looked at several tutorials. I tried to implement a TableView.
Here is my code :
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var items: [String] = ["lol1", "lol2", "lol3"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1;
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:UITableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "cell")!
cell.textLabel?.text = self.items[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
print("You selected cell #\(items[indexPath.row])!")
}
}
MY IBOutlet is connect to the tableview I inserted in the storyboard.
When I run it, I have a TableView, but it's missing contents.
From what I gathered through some (more or less outdated) tutorials, I shouldn't have anything more to do, what am I missing ?
Where are you set dataSource and Deleagte methods of TableView?
use this code
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.tableView.dataSource = self
self.tableView.delegate = self
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
2 possible reasons:
If the cell is designed as prototype cell you must not register the cell.
dataSource and delegate of the table view must be connected to the controller in Interface Builder or set in code.
There are two views in my project: in the first one users input two words and those words are saved in a dictionary as the key and the value. The code in short:
var words = [Dictionary<String, String>]()//<-declared outside the class
words.append([input.text: input2.text] as! [String : String])
Then in the second view, I want to display all the saved words in table cell like so:
And I have found some code for doing that, but it works only for a local dict:
class TableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// Data model: These strings will be the data for the table view cells
let animals: [String] = ["Horse", "Cow", "Camel", "Sheep", "Goat"]
// cell reuse id (cells that scroll out of view can be reused)
let cellReuseIdentifier = "cell"
// don't forget to hook this up from the storyboard
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Register the table view cell class and its reuse id
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier)
// (optional) include this line if you want to remove the extra empty cell divider lines
// self.tableView.tableFooterView = UIView()
// This view controller itself will provide the delegate methods and row data for the table view.
tableView.delegate = self
tableView.dataSource = self
}
// number of rows in table view
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.animals.count
}
// create a cell for each table view row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// create a new cell if needed or reuse an old one
let cell:UITableViewCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as UITableViewCell!
// set the text from the data model
cell.textLabel?.text = self.animals[indexPath.row]
return cell
}
// method to run when table view cell is tapped
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped cell number \(indexPath.row).")
}
}
I have edited the code above like so:
var wordIndex1 = words.count - 1
class TableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var word = words[wordIndex1] as? Dictionary<String, String>
let cellReuseIdentifier = "cell"
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier)
tableView.delegate = self
tableView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (self.word?.count)!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:UITableViewCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as UITableViewCell!
cell.textLabel?.text = self.word?.keys.first
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped cell number \(indexPath.row).")
}
}
But it only displays the last added value whereas in the first code it creates a new cell for each new value.
What did I do wrong?
You access array index = number of items you need
var wordIndex1 = words.count - 1
or in short
if let lastWord = words.last {
////
}
note array indexing starts with 0 and to numberOfElemnts - 1
Also it's better to have a model
struct Item {
let leftInp.rightInp:String
}
then create the array like
var arr = [Item]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return words.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:UITableViewCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as UITableViewCell!
let dic = words[indexPath.row]
cell.textLabel?.text = dic.keys.first
return cell
}
Trying to play with Table in the XCode Playgrounds to understand how it works, because there a lot of stuff connected to the UITableView and a few tutorials describe these relations and inheritance under the scenes (like UIView -> UIScrollView -> UITableView -> UITableViewCell) and how it manages that inheritance, connections and delegations. Normally with an ordinary tutor, you're given with snippet of code and the instructions how to establish delegate connections in XCode and the right methods to call. Result - you memorize the steps but still don't understand the mechanism of the Table. Below is code snippet that I try to adopt for XCode Playgrounds to draw a simple table. The problem - I can't properly register programmatically reuse identifier for Table cell nether from class or outside it. The nature of error
reason: 'unable to dequeue a cell with identifier Cell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'.
Please, help and point out what is wrong and why.
class TableViewController: UITableViewController {
let tableData = ["Matthew", "Mark", "Luke", "John"]
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as UITableViewCell
cell.textLabel?.text = tableData[indexPath.row]
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.dataSource = self
//Did this part as comments to try to do this outside the class
// self.view.frame = CGRect(x: 0, y: 0, width: 320, height: 480)
// self.tableView = UITableView(frame:self.view.frame)
// self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
self.view.addSubview(self.tableView)
}
}
let frame = CGRect(x: 0, y: 0, width: 320, height: 480)
let tableView = UITableView(frame: frame, style: .plain)
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
PlaygroundPage.current.liveView = TableViewController()
In a UIViewController contaning a UITableView, programmatically registering of Custom UITableViewCell Subclasses to an instance of UITableView is implemented by:
- (void)registerClass:(nullable Class)cellClass forCellReuseIdentifier:(NSString *)identifier
This is registeringClass should be done before setting up table's dataSource.
After long time playing with different solutions of building tables from various tutorials I finally have caught some difference in use between a UITableViewController and a UIViewController for displaying a UITableView. In my example from my question, some lines of code are just redundant. That's happened because I tried to implement the approach of building a table within the UIViewController, but I used the UITableViewController instead and additionally tried to set the dataSource and all view properties, but as I understand now - the UITableViewController already does all this stuff for me (correct me here if I wrong). The functioning snippet of code is below:
class TableViewController: UITableViewController {
let tableData = ["Matthew", "Mark", "Luke", "John"]
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as UITableViewCell
cell.textLabel?.text = tableData[indexPath.row]
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}
}
PlaygroundPage.current.liveView = TableViewController()
I am new to iOS dev and basically I'm trying to populate a TableView with String values from an array.
However when I run the app, blank rows show up and no text values are shown. Have I coded this correctly?
import UIKit
class SelectIssueController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var issuesTableView: UITableView!
var issues = [Issue]()
override func viewDidLoad() {
super.viewDidLoad()
self.issues = ["Test1", "Test2", "Test3"]
self.issuesTableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}
override var preferredStatusBarStyle: UIStatusBarStyle{
return UIStatusBarStyle.lightContent
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.issues.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.issuesTableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as UITableViewCell
cell.textLabel?.text = issues[indexPath.row]
//Even if I manually set a value, the rows are still blank
//cell.textLabel?.text = "Hello World"
return cell
}
}
You can set Table view data source and delegate in two ways.
1. Click Cntrl+drag from tableView to view controller. See below figure
Create the outlet of your tableView and assign its datasource and delegate in ViewDidLoad.
In your example you already have an outlet to issuesTableView, so you would write:
issuesTableView.dataSource = self
issuesTableView.delegate = self
Thanks:)
I am wondering why is my tableview not populating? I have a tableview inside my view controller and I have tried to follow the guide however, nothing seems to show up on my tableview.
here's my code
#IBOutlet weak var weatherTableView: UITableView!
var items: [String] = ["Sunny", "Cloudy", "Rainy"]
override func viewDidLoad() {
super.viewDidLoad()
self.weatherTableView.register(UITableViewCell.self, forCellReuseIdentifier: "WeatherCell")
// Do any additional setup after loading the view.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:UITableViewCell = self.weatherTableView.dequeueReusableCell(withIdentifier: "WeatherCell")! as UITableViewCell
cell.textLabel?.text = self.items[indexPath.row]
return cell
}
am I missing something here?
Thanks for your help in advance
Make sure you are assigning tableview delegate and datasource throw storyboard or add below line into your viewDidLoad() of your viewcontroller (UITableViewDelegate, UITableViewDataSource)
weatherTableView.delegate = self;
weatherTableView.dataSource = self