Custom UITableViewCell programmatically using Swift
Solution 1
Let's make a few assumptions:
You have an iOS8 project with a Storyboard
that contains a single UITableViewController
. Its tableView
has a unique prototype UITableViewCell
with custom style and identifier: "cell".
The UITableViewController
will be linked to Class TableViewController
, the cell
will be linked to Class CustomTableViewCell
.
You will then be able to set the following code (updated for Swift 2):
CustomTableViewCell.swift:
import UIKit
class CustomTableViewCell: UITableViewCell {
let imgUser = UIImageView()
let labUserName = UILabel()
let labMessage = UILabel()
let labTime = UILabel()
override func awakeFromNib() {
super.awakeFromNib()
imgUser.backgroundColor = UIColor.blueColor()
imgUser.translatesAutoresizingMaskIntoConstraints = false
labUserName.translatesAutoresizingMaskIntoConstraints = false
labMessage.translatesAutoresizingMaskIntoConstraints = false
labTime.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(imgUser)
contentView.addSubview(labUserName)
contentView.addSubview(labMessage)
contentView.addSubview(labTime)
let viewsDict = [
"image": imgUser,
"username": labUserName,
"message": labMessage,
"labTime": labTime,
]
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[image(10)]", options: [], metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[labTime]-|", options: [], metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[username]-[message]-|", options: [], metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[username]-[image(10)]-|", options: [], metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[message]-[labTime]-|", options: [], metrics: nil, views: viewsDict))
}
}
TableViewController.swift:
import UIKit
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
//Auto-set the UITableViewCells height (requires iOS8+)
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 44
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 100
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CustomTableViewCell
cell.labUserName.text = "Name"
cell.labMessage.text = "Message \(indexPath.row)"
cell.labTime.text = NSDateFormatter.localizedStringFromDate(NSDate(), dateStyle: .ShortStyle, timeStyle: .ShortStyle)
return cell
}
}
You will expect a display like this (iPhone landscape):
Solution 2
This is the update for swift 3 of the answer Imanou Petit.
CustomTableViewCell.swift:
import Foundation
import UIKit
class CustomTableViewCell: UITableViewCell {
let imgUser = UIImageView()
let labUerName = UILabel()
let labMessage = UILabel()
let labTime = UILabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
imgUser.backgroundColor = UIColor.blue
imgUser.translatesAutoresizingMaskIntoConstraints = false
labUerName.translatesAutoresizingMaskIntoConstraints = false
labMessage.translatesAutoresizingMaskIntoConstraints = false
labTime.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(imgUser)
contentView.addSubview(labUerName)
contentView.addSubview(labMessage)
contentView.addSubview(labTime)
let viewsDict = [
"image" : imgUser,
"username" : labUerName,
"message" : labMessage,
"labTime" : labTime,
] as [String : Any]
contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-[image(10)]", options: [], metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[labTime]-|", options: [], metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-[username]-[message]-|", options: [], metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[username]-[image(10)]-|", options: [], metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[message]-[labTime]-|", options: [], metrics: nil, views: viewsDict))
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Settigns.swift:
import Foundation
import UIKit
class Settings: UIViewController, UITableViewDelegate, UITableViewDataSource {
private var myTableView: UITableView!
private let sections: NSArray = ["fruit", "vegitable"] //Profile network audio Codecs
private let fruit: NSArray = ["apple", "orange", "banana", "strawberry", "lemon"]
private let vegitable: NSArray = ["carrots", "avocado", "potato", "onion"]
override func viewDidLoad() {
super.viewDidLoad()
// get width and height of View
let barHeight: CGFloat = UIApplication.shared.statusBarFrame.size.height
let navigationBarHeight: CGFloat = self.navigationController!.navigationBar.frame.size.height
let displayWidth: CGFloat = self.view.frame.width
let displayHeight: CGFloat = self.view.frame.height
myTableView = UITableView(frame: CGRect(x: 0, y: barHeight+navigationBarHeight, width: displayWidth, height: displayHeight - (barHeight+navigationBarHeight)))
myTableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "cell") // register cell name
myTableView.dataSource = self
myTableView.delegate = self
//Auto-set the UITableViewCells height (requires iOS8+)
myTableView.rowHeight = UITableViewAutomaticDimension
myTableView.estimatedRowHeight = 44
self.view.addSubview(myTableView)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// return the number of sections
func numberOfSections(in tableView: UITableView) -> Int{
return sections.count
}
// return the title of sections
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section] as? String
}
// called when the cell is selected.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Num: \(indexPath.row)")
if indexPath.section == 0 {
print("Value: \(fruit[indexPath.row])")
} else if indexPath.section == 1 {
print("Value: \(vegitable[indexPath.row])")
}
}
// return the number of cells each section.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return fruit.count
} else if section == 1 {
return vegitable.count
} else {
return 0
}
}
// return cells
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTableViewCell
if indexPath.section == 0 {
cell.labUerName.text = "\(fruit[indexPath.row])"
cell.labMessage.text = "Message \(indexPath.row)"
cell.labTime.text = DateFormatter.localizedString(from: NSDate() as Date, dateStyle: .short, timeStyle: .short)
} else if indexPath.section == 1 {
cell.labUerName.text = "\(vegitable[indexPath.row])"
cell.labMessage.text = "Message \(indexPath.row)"
cell.labTime.text = DateFormatter.localizedString(from: NSDate() as Date, dateStyle: .short, timeStyle: .short)
}
return cell
}
}
Solution 3
In Swift 5.
The custom UITableViewCell:
import UIKit
class CourseCell: UITableViewCell {
let courseName = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
// Set any attributes of your UI components here.
courseName.translatesAutoresizingMaskIntoConstraints = false
courseName.font = UIFont.systemFont(ofSize: 20)
// Add the UI components
contentView.addSubview(courseName)
NSLayoutConstraint.activate([
courseName.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
courseName.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20),
courseName.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20),
courseName.heightAnchor.constraint(equalToConstant: 50)
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The UITableViewController:
import UIKit
class CourseTableViewController: UITableViewController {
private var data: [Int] = [1]
override func viewDidLoad() {
super.viewDidLoad()
// You must register the cell with a reuse identifier
tableView.register(CourseCell.self, forCellReuseIdentifier: "courseCell")
// Change the row height if you want
tableView.rowHeight = 150
// This will remove any empty cells that are below your data filled cells
tableView.tableFooterView = UIView()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "courseCell", for: indexPath) as! CourseCell
cell.courseName.text = "Course name"
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
}
Related videos on Youtube
shay te
Updated on September 01, 2020Comments
-
shay te over 3 years
Hey all I am trying to create a custom
UITableViewCell
, but I see nothing on the simulator. Can you help me please.I can see the label only if I
var labUserName = UILabel(frame: CGRectMake(0.0, 0.0, 130, 30));
but it overlaps the cell. I don't understand, Auto Layout should know the preferred size/minimum size of each cell?
Thanks
import Foundation import UIKit class TableCellMessages: UITableViewCell { var imgUser = UIImageView(); var labUserName = UILabel(); var labMessage = UILabel(); var labTime = UILabel(); override init(style: UITableViewCellStyle, reuseIdentifier: String) { super.init(style: style, reuseIdentifier: reuseIdentifier) imgUser.layer.cornerRadius = imgUser.frame.size.width / 2; imgUser.clipsToBounds = true; contentView.addSubview(imgUser) contentView.addSubview(labUserName) contentView.addSubview(labMessage) contentView.addSubview(labTime) //Set layout var viewsDict = Dictionary <String, UIView>() viewsDict["image"] = imgUser; viewsDict["username"] = labUserName; viewsDict["message"] = labMessage; viewsDict["time"] = labTime; //Image //contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[image(100)]-'", options: nil, metrics: nil, views: viewsDict)); //contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[image(100)]-|", options: nil, metrics: nil, views: viewsDict)); contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[username]-[message]-|", options: nil, metrics: nil, views: viewsDict)); contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[username]-|", options: nil, metrics: nil, views: viewsDict)); contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[message]-|", options: nil, metrics: nil, views: viewsDict)); } required init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
-
Austen Chongpison over 9 yearsIf you're seeing overlapping images in your cells then the cell height is probably not being set properly. See: stackoverflow.com/questions/494562/…
-
shay te over 9 yearsi see overlap only when i am using CGRectMake, but i don't want to use CGRectMake, i want to to be automatically,
-
shay te over 9 yearsi want the auto layout to control the sizing, this is the one thing i don't understand.
-
shay te over 9 yearsany help please, dose the labels inside should have their own size without me have to tell them what is the size ?
-
anoop4real over 4 yearsI know this thread is old, for me constraints worked better when "translatesAutoresizingMaskIntoConstraints = false" on all the UI elements like labels, views etc
-
rustyMagnet over 3 yearsI read a lot of SO articles about this. But this article was precise and clean: programmingwithswift.com/…
-
-
Esqarrouth over 9 yearsit seem like this is executing awakefromnib before you give it the parameters like cell.labUerName.text = "Name"
-
Esqarrouth over 9 yearsthis code is first calling awakefromnib than passing variables into the cell class. because of this i am getting a crash, what should be done?
-
Jamie Birch almost 7 yearsTo remove the dependency on Interface Builder for registering the forCellReuseIdentifier, add the following line to your UITableViewController in loadView() or ViewDidLoad():
tableView.register(CustomTableViewCell.classForCoder(), forCellReuseIdentifier: "cell")
. -
Jonathan Cabrera over 5 yearsJust wondering... why did you decide to use
init(style:)
instead ofawakeFromNib()
which Imanou Petit used? Is there a difference? -
Tiago Mendes over 5 yearsImanou Petit was assuming that the user was using Interface Builder. When I used his answer, I made the code totally independent of any storyboard/xib = nib, so in my app the awakeFromNib() never is called. By the way I also had to add this line: myTableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "cell")
-
CristianMoisei about 5 yearsHey @TiagoMendes quick question, I created a custom cell using the storyboard and I am trying to change its background colour in the cellForRowAtIndexPath method via an outlet, but it doesn't seem to be working. Properties like .isHidden work fine this way. Is this the correct way of doing this? Am I missing something?
-
Tofu Warrior over 3 yearsThis was the fix for me in Swift5 still