Swift - Custom textLabel in TableViewCell - ios

at the moment I have a UITableView where the user can add cells that have a textLabel inside of it.
That is how I set the label:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: WhishCell.reuseID, for: indexPath)
let currentWish = self.wishList[indexPath.row]
cell.textLabel?.text = currentWish.wishName
cell.backgroundColor = .clear
return cell
}
My question is, how I can custom textLabel (font, constraints,...). I tried creating a custom UILabel inside my WishCell class but I can not access it in cellforRowAt with cell.theLabel.
I hope you understand my problem, I am very grateful for every help :)
SOLVED
I just forgot the as! WhishCell in cellForRowAt. Thanks for all your help :)

You need to cast to you cell custom class name
let cell = tableView.dequeueReusableCell(withIdentifier: WhishCell.reuseID, for: indexPath) as! CellName
cell.lbl.text = ////
class CellName:UITableViewCell {
let lbl = UILabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
lbl.backgroundColor = UIColor.red
self.contentView.addSubview(lbl)
// set constraints here
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}

Dont force un-wrap it.. try using guard or if let
guard let cell = tableView.dequeueReusableCell(withIdentifier: WhishCell.reuseID, for: indexPath) as? WhishCell else{
return UITableViewCell()
}
cell.lbl.text = "my text"
let currentWish = self.wishList[indexPath.row]
cell.textLabel?.text = currentWish.wishName
cell.backgroundColor = .clear
return cell

Related

How to set tableview style to have subtitle programmatically while having dequeueReusableCell?

I want my tableView to have subtitle as well as being able to dequeue properly. I have referred to this link but it does not work for my code. What should I do?
My code is currently like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
//Calling tableview for a reusable cell here will always return a cell
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = developerArray[indexPath.row].developerName
cell.detailTextLabel?.text = developerArray[indexPath.row].developerHP
return cell
}
Swift 5
//Declare the variable cell Identifier
let reuseCellIdentifier = “cellIdentifier”;
//Implementation of cellForRowAt
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: reuseCellIdentifier)
if (!(cell != nil)) {
cell = UITableViewCell(style: .subtitle, reuseIdentifier: reuseCellIdentifier)
}
cell?.textLabel?.text = //Title text
cell?.detailTextLabel?.text = //Subtitle text
return cell!
}
You can create custom cell using nib by adding labels
For creating custom cell refer this link:
Custom UITableViewCell from nib in Swift
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:MyCustomCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! MyCustomCell
cell.title.text = yourTitleArray[indexPath.row]
cell.detailLbl.text = yourDetailArray[indexPath.row]
return cell
}
So first of all, did you create your table using storyboard or code?
Either way you need to make sure you set the datasource and delegate to self, provided the class they are in conforms to :
UITableViewDataSource
and
UITableViewDelegate
myTable?.delegate = self
myTable?.dataSource = self
Also make sure you register your cell
myTable?.register(myCell.self, forCellReuseIdentifier: "myCell")
And when declaring your cell, you need to force it as the type
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! myCell
Below is a working sample of tableview with a created cell. I hope this helps.
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
// Create the tableview object
var myTable:UITableView?
override func viewDidLoad() {
// Set the size and location of the tableview
myTable = UITableView(frame: CGRect(x: 0, y: 0, width: 300, height: 600))
// register your cell
myTable?.register(myCell.self, forCellReuseIdentifier: "myCell")
// set the background color of the table, note this wont make a difference unless the cell background is changed.
myTable?.backgroundColor = UIColor.clear
// set the datasource and delegate to self
myTable?.delegate = self
myTable?.dataSource = self
// This is jsut for style, wether there should be seperators or not, and if the user can select multiple lines
myTable?.separatorStyle = .none
myTable?.allowsMultipleSelection = true
// Add the table to your view
self.view.addSubview(myTable!)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// This is declaring how many rows you want in your table. I have 1 but you can do it according to the size of your array.
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// create the cell
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! myCell
// set the title text for this cell
cell.title.text = "HelloWorld"
// return the cell
return cell
}
}
and this is the class for the cell we referenced above.
class myCell: UITableViewCell {
var title = UILabel()
var detail = UILabel()
override func awakeFromNib() {
super.awakeFromNib()
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
title.font = UIFont.boldSystemFont(ofSize: 16)
title.textAlignment = .center
self.contentView.addSubview(title)
self.contentView.addSubview(detail)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func layoutSubviews() {
super.layoutSubviews()
title.frame = CGRect(x: self.contentView.frame.width / 2 - 200, y: 6, width: 400, height: 20)
detail.frame = CGRect(x: 150, y: 10, width: 280, height: 20)
}
}
Let me know if this helps. if you are doing it from story board let me know and I'll adjust.

detailTextLabel not showing even after setting style to subtitle

I have got this Swift code in my project
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: cellid, for: indexPath) as? UITableViewCell
if cell == nil {
cell = UITableViewCell(style: .subtitle, reuseIdentifier: cellid)
}
let user = users[indexPath.row]
cell!.textLabel?.text=user.Name
cell!.detailTextLabel?.text = user.Email
return cell!
}
Everything works fine except that detaiTextLabel is not showing.What is wrong with my code?
My problem was in
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
I should have set style to subtitle like this
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: .subtitle, reuseIdentifier: reuseIdentifier)
}
The problem is its not going inside if condition where you mentioned .subTitle. So I tried of removing if condition it and its working.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: cellid, for: indexPath) as? UITableViewCell
cell = UITableViewCell(style: .subtitle, reuseIdentifier: cellid)
let user = users[indexPath.row]
cell!.textLabel?.text=user.Name
cell!.detailTextLabel?.text = user.Email
return cell!
}
The text in detailTextLabel won't show up if your string is too long to fit inside the label's width. You have 2 options, reduce the size of your string or the font size.

Tableview cell subtitles

My tableview cell subtitles aren't showing when I use this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell:UITableViewCell?
if tableView.tag == 1 {
guard let latestCell = tableView.dequeueReusableCell(withIdentifier: "latestCell") else {
return UITableViewCell(style: .subtitle, reuseIdentifier: "latestCell")
}
latestCell.textLabel?.text = latest[indexPath.row]
latestCell.detailTextLabel?.text = latestSub[indexPath.row]
latestCell.accessoryType = .disclosureIndicator
return latestCell
}
}
But then if I use this:
else if tableView.tag == 2 {
let olderCell = UITableViewCell(style: UITableViewCellStyle.subtitle, reuseIdentifier: "olderCell")
olderCell.textLabel?.text = older[indexPath.row]
olderCell.detailTextLabel?.text = olderSub[indexPath.row]
olderCell.accessoryType = .disclosureIndicator
return olderCell
} else {
return cell!
}
}
The subtitles load perfectly, but after I close the app and reload the view, the app autoquits without giving a crash log or taking me to the debugging-tab.
I know that the arrays from which the data comes from are fine, and I think that I've set up everything right in the storyboard. A lot of similar questions have already been posted on the subject, but they all seem to come down to forgetting to set the cellStyle to .subtitle. Thanks in advance for any help I get!
BTW. My regular cell titles are working just like I want them to. No problem there.
EDIT:
I think the problem is that I can create a default-styled cell with no problem. But then when I try to set the style to .subtitle, it loads correctly the first time but when opening the second time, it crashes. Is there a way to use these both declarations together in a way that they don't eliminate each other out;?
guard let latestCell = tableView.dequeueReusableCell(withIdentifier: "latestCell") else {
return UITableViewCell(style: .subtitle, reuseIdentifier: "latestCell")
}
and:
let latestCell = UITableViewCell(style: UITableViewCellStyle.subtitle, reuseIdentifier: "latestCell")
Do it like this:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("latestCell") as UITableViewCell
cell.textLabel?.text = latest[indexPath.row]
cell.detailTextLabel?.text = latestSub[indexPath.row]
cell.accessoryType = .disclosureIndicator
return cell
}
Mark this as solution/upvote if this solved your problem.
Old but...
I think the problem was :
guard let latestCell = tableView.dequeueReusableCell(withIdentifier: "latestCell") else {
return UITableViewCell(style: .subtitle, reuseIdentifier: "latestCell")
}
will return an "empty" cell.
So.. just like :
var latestCell = tableView.dequeueReusableCell(withIdentifier: "latestCell")
if latestCell == nil {
latestCell = UITableViewCell(style: UITableViewCellStyle.subtitle, reuseIdentifier: "latestCell")
}
// your stuff
Ran into this question today as well. I assume the original poster found the answer, but for others that run into this thread in the future here is how I solved this. Note this link/thread explains addition methods of solving as well. How to Set UITableViewCellStyleSubtitle and dequeueReusableCell in Swift?
Environment: Swift 5.0 and Xcode Version 10.2.1.
Explanation: I solved this by subclassing UITabelViewCell and initializing it with the .subtitle type (see code below). Note the .subtitle in the super.init method.
Once you have the subclass don't forget to register CustomCell to your tableView and downcast as CustomCell in your tableView method cellForRowAt.
class CustomCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: .subtitle, reuseIdentifier: reuseIdentifier)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Here is the code for the cellForRowAt method, which has the titleLabel and the detailTextLabel(subtitle) properties. Note the downcast "as! CustomCell".
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) ->
UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for:
indexPath) as! CustomCell
cell.textLabel?.text = someArray[indexPath.row].title // Note someArray would have to be replaced with your array of strings.
cell.detailTextLabel?.text = someArray[indexPath.row].subtitle // Note someArray would have to be replaced with your array of strings.
return cell
}
Easiest solution:
Design both cells in Interface Builder directly in the table view(s), set the style and the accessory view to your desired values and add the identifiers.
Then the code can be reduced to.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch tableView.tag {
case 1:
let latestCell = tableView.dequeueReusableCell(withIdentifier: "latestCell" for: indexPath)
latestCell.textLabel!.text = latest[indexPath.row]
latestCell.detailTextLabel!.text = latestSub[indexPath.row]
return latestCell
case 2:
let olderCell = tableView.dequeueReusableCell(withIdentifier: "olderCell" for: indexPath)
olderCell.textLabel!.text = older[indexPath.row]
olderCell.detailTextLabel!.text = olderSub[indexPath.row]
return olderCell
default:
fatalError("That should never happen")
}
}
Since the cells are predefined to subtitle style, both textLabel and detailTextLabel are guaranteed to exist and can be safely unwrapped.
However what is the significant difference between the cells. From the given code you can actually use one cell (identifier cell). That can make the code still shorter:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell" for: indexPath)
if tableView.tag == 1 {
cell.textLabel!.text = latest[indexPath.row]
cell.detailTextLabel!.text = latestSub[indexPath.row]
} else {
cell.textLabel!.text = older[indexPath.row]
cell.detailTextLabel!.text = olderSub[indexPath.row]
}
return cell
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") //replace "Cell" with your identifier
cell.textLabel = yourTitleArray[indexPath.row]
cell.detailTextLabel = yourSubtitleArray[indexPath.row]
return cell!
}

Swift: can't configure custom table cell programmatically? Init not called?

Alright, I am trying to add content to my custom tableview cell programmatically as demonstrated in mob last question here - Swift: tableview cell content is added again and again with each reload? initializing all the content in func tableView() results in overlapping.
I have followed this question verbatim Swift. Proper initialization of UITableViewCell hierarchy And in my custom cell class (which I give a name to in my storyboard) I have:
class EventTableCellTableViewCell: UITableViewCell {
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) // the common code is executed in this super call
// code unique to CellOne goes here
print("INIT")
self.contentView.backgroundColor = UIColor.blackColor()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
And this init is not called because nothing is printed. The errors come in my func tableView() in my main VC. I originally had:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("eventCell", forIndexPath: indexPath) as! EventTableCellTableViewCell
// cell.eventTitle.text = names[indexPath.row]
// cell.eventDescription.text = descriptions[indexPath.row]
cell.contentView.clipsToBounds = false
//cell UIX
let eventTitleLabel = UILabel()
let dateLabel = UILabel()
let authorLabel = UILabel()
let locationLabel = UILabel()
let categoryView = UIImageView()
//then I add everything
But this didn't work so I looked at other posts and now have:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//var cell = tableView.dequeueReusableCellWithIdentifier("eventCell", forIndexPath: indexPath) as! EventTableCellTableViewCell
var cell = tableView.dequeueReusableCellWithIdentifier("eventCell", forIndexPath: indexPath) as! UITableViewCell
if (cell == nil) {
cell = EventTableCellTableViewCell.init(style: .Default, reuseIdentifier: "eventCell")
}
I have also tried doing it without the indexPath. Right now I get an error that cell cannot == nil, and not matter what I write init is not called.
How can I configure my cell programmatically?
If the cell is designed in the storyboard only init?(coder aDecoder: NSCoder) is called, init(style: style, reuseIdentifier: is never called.
And you have to set the class of the cell in Interface Builder to EventTableCellTableViewCell.

UITableViewCell not showing detailTextLabel.text - Swift

The detail (subtitle) text does not appear. The data are available, though, because when a println() call is added, it prints Optional("data") to the console with the expected data. In the storyboard, the UITableViewController is set to the proper class, the Table View Cell Style is set to 'Subtitle', and the reuse identifier is set to 'cell'. How can I get the subtitle information to display?
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell
dispatch_async(dispatch_get_main_queue(), { () -> Void in
cell.textLabel.text = self.myArray[indexPath.row]["title"] as? String
cell.detailTextLabel?.text = self.myArray[indexPath.row]["subtitle"] as? String
println(self.myArray[indexPath.row]["subtitle"] as? String)
// The expected data appear in the console, but not in the iOS simulator's table view cell.
})
return cell
}
Your code looks fine. Just goto the storyboard and select the cell of your tableview -> Now goto Attributes Inspector and choose the style to Subtitle.
Follow this according to the below screenshot.
Hope it helped..
Same issue here (from what I've read, perhaps a bug in iOS 8?), this is how we worked around it:
Delete the prototype cell from your storyboard
Remove this line:
var cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell
Replace with these lines of code:
let cellIdentifier = "Cell"
var cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? UITableViewCell
if cell == nil {
cell = UITableViewCell(style: UITableViewCellStyle.Value2, reuseIdentifier: cellIdentifier)
}
Update for Swift 3.1
let cellIdentifier = "Cell"
var cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)
if cell == nil {
cell = UITableViewCell(style: UITableViewCellStyle.value2, reuseIdentifier: cellIdentifier)
}
Update for Swift 4.2 - Simplified
let cell = UITableViewCell(style: UITableViewCell.CellStyle.value2, reuseIdentifier: "cellId")
Update for Swift 5 - Simplified
let cell = UITableViewCell(style: .value2, reuseIdentifier: "cellId")
If you still want to use prototype cell from your storyboard, select the TableViewcell style as Subtitle. it will work.
Try this it work for me (swift 5)
let cell = UITableViewCell(style: .value1, reuseIdentifier: "cellId")
cell.textLabel.text = "Déconnexion"
cell.imageView.image = UIImage(named: "imageName")
Objective c :
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"cellId"];
If you are setting the text to nil somewhere when you try to set it to a non-nil value the actual view that contains the text will be missing. This was introduced in iOS8. Try setting to an empty space #" " character instead.
See this: Subtitles of UITableViewCell won't update
If doing so programmatically without cells in interface builder this code works like a charm in Swift 2.0+
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
yourTableView.delegate = self
yourTableView.dataSource = self
yourTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "subtitleCell")
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return yourTableViewArray.count
}
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
let cell: UITableViewCell = yourTableView.dequeueReusableCellWithIdentifier("subtitleCell", forIndexPath: indexPath) as UITableViewCell
cell.textLabel?.text = "the text you want on main title"
cell.detailTextLabel?.text = "the text you want on subtitle"
return cell
}
For what it's worth: I had the problem of detail not appearing. That was because I had registered the tableView cell, which I should not have done as the cell prototype was defined directly in storyboard.
In Xcode11 and Swift5 , We have to do like below.
If we do it by checking the condition cell == nil and then creating UITableViewCell with cellStyle it is not working . Below solution is working for me .
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "Cell"
let cell = UITableViewCell(style: UITableViewCell.CellStyle.subtitle, reuseIdentifier: cellIdentifier)
cell?.textLabel?.text = "Title"
cell?.detailTextLabel?.text = "Sub-Title"
return cell!
}
Here is how it works for swift 5, to get a subtitle using detailtextlabel, using a UITableView object within a view controller, if you are missing any of these, it will not work and will probably crash.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource
In viewDidLoad:
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "subtitleCell")
Delegate Function:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Fetch a cell of the appropriate type.
let cell = UITableViewCell(style: .subtitle , reuseIdentifier: "subtitleCell")
// Configure the cell’s contents.
cell.textLabel!.text = "Main Cell Text"
cell.detailTextLabel?.text = "Detail Cell Text"
return cell
}
xcode 11
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
let cell = UITableViewCell(style: UITableViewCell.CellStyle.value1, reuseIdentifier: "reuseIdentifier")
cell.detailTextLabel?.text = "Detail text"
cell.textLabel?.text = "Label text"
// Configure the cell...
return cell
}
Some of the solutions above are not entirely correct. Since the cell should be reused, not re-created. You can change init method.
final class CustomViewCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: .value1, reuseIdentifier: reuseIdentifier)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Three properties will be deprecated in a future release: imageView textLabel and detailTextLabel
You can use UIListContentConfiguration to configure cell
dataSource = UITableViewDiffableDataSource(tableView: tableview, cellProvider: { tableview, indexPath, menu in
let cell = tableview.dequeueReusableCell(withIdentifier: self.profileCellIdentifier, for: indexPath)
var content = cell.defaultContentConfiguration()
content.text = menu.title
if indexPath.section == MenuSection.info.rawValue {
content.image = UIImage(systemName: "person.circle.fill")
content.imageProperties.tintColor = AppColor.secondary
}
if let subtitle = menu.subTitle {
content.secondaryText = subtitle
}
cell.contentConfiguration = content
return cell
})
Swift 5 with subtitle text, here no need to register your cell in viewDidLoad:
var cell = tableView.dequeueReusableCell(withIdentifier: "cell")
if cell == nil {
cell = UITableViewCell(style: UITableViewCell.CellStyle.subtitle, reuseIdentifier: "cell")
}
cell?.textLabel?.text = "title"
cell?.textLabel?.numberOfLines = 0
cell?.detailTextLabel?.text = "Lorem ipsum"
cell?.detailTextLabel?.numberOfLines = 0
return cell ?? UITableViewCell()

Resources