I am wanting to create a form style TableView with a label on the left of the cell and TextField on the right.
However, I am having a difficult time finding information on TextField's inside custom TableViewCells. Is it even possible? I thought perhaps a xib file, but not much luck on finding answers there either.
Greatly appreciate some guidance.
import UIKit
class AddProfileViewController: UIViewController, UITableViewDataSource,
UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
let sectionTitles: [String] = ["PROFILES", "IDENTIFICATION", "EMERGENCY CONTACTS"]
let sectionImages: [UIImage] = [#imageLiteral(resourceName: "arrow"), #imageLiteral(resourceName: "arrow"), #imageLiteral(resourceName: "arrow")]
let s1Data: [String] = ["Barn Name", "Show Name", "Color", "Gender", "Breed", "Birth Date", "Heigh (Hh)"]
let s2Data: [String] = ["Markings", "Tattoo", "Branding", "Microchip ID", "Passport", "Registration", "Registration #"]
let s3Data: [String] = ["Contact Name", "Contact Phone", "Vet Name", "Vet Phone", "Farrier Name", "Farrier Phone"]
var sectionData: [Int: [String]] = [:]
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.setBackgroundImage(UIImage(),for: .default)
self.navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.navigationBar.isTranslucent = true
self.navigationItem.rightBarButtonItem = self.editButtonItem
sectionData = [0:s1Data, 1:s2Data, 2:s3Data]
// Do any additional setup after loading the view.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int)
-> Int {
return (sectionData[section]?.count)!
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 45
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView()
view.backgroundColor = UIColor.white
let label = UILabel()
label.text = sectionTitles[section]
label.frame = CGRect(x: 15, y: 5, width: 200, height: 35)
label.font = UIFont(name: "Avenir", size: 15)
label.textColor = UIColor.darkGray
view.addSubview(label)
let image = UIImageView(image: sectionImages[section])
image.frame = CGRect(x: 300, y: 8, width: 25, height: 25)
image.contentMode = .scaleAspectFit
view.addSubview(image)
return view
}
func numberOfSections(in tableView: UITableView) -> Int {
return sectionTitles.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
-> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "cell")
if cell == nil {
cell = UITableViewCell(style: .default, reuseIdentifier: "cell");
}
cell!.textLabel?.text = sectionData[indexPath.section]![indexPath.row]
cell!.textLabel?.font = UIFont(name: "Avenir", size: 13)
cell!.textLabel?.textColor = UIColor.lightGray
return cell!
}
}
It is perfectly possible. Try following this steps:
Add your UILabel and UITextField to your UITableViewCell (you can use NIB or Storyboard)
Make your TableViewController conform to UITextFieldDelegate
When you dequeue your cell, set your textfield’s delegate to your TableViewController
In didSelectRow make your textField become firstResponder (so you don’t have to tap precisely on the textField)
Don’t have a computer here to test, but let me know if this helps
Use protocols to finding the info and use stroyboard for creating layouts
Hope it will help!.
Related
Expanding and Collapsing is working fine tableview, Only facing issue while didselect row is tapped, I'm getting same index evry time after selecting a row. I'm getting the same out put, I want to pass the data to next view but output isn't working properly.
Here's My Details...
My OutPut
My Model
struct ItemList {
var name: String
var items: [String]
var collapsed: Bool
init(name: String, items: [String], collapsed: Bool = false) {
self.name = name
self.items = items
self.collapsed = collapsed
}
}
My ViewController Class
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var sections = [ItemList]()
var items: [ItemList] = [
ItemList(name: "Mac", items: ["MacBook", "MacBook Air"]),
ItemList(name: "iPad", items: ["iPad Pro", "iPad Air 2"]),
ItemList(name: "iPhone", items: ["iPhone 7", "iPhone 6"])
]
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
}
TableView Extension
extension ViewController:UITableViewDelegate,UITableViewDataSource{
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 60
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerHeading = UILabel(frame: CGRect(x: 5, y: 10, width: self.view.frame.width, height: 40))
let imageView = UIImageView(frame: CGRect(x: self.view.frame.width - 30, y: 20, width: 20, height: 20))
if items[section].collapsed{
imageView.image = UIImage(named: "collapse")
}else{
imageView.image = UIImage(named: "expand")
}
let headerView = UIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 60))
let tapGuesture = UITapGestureRecognizer(target: self, action: #selector(headerViewTapped))
tapGuesture.numberOfTapsRequired = 1
headerView.addGestureRecognizer(tapGuesture)
headerView.backgroundColor = UIColor.red
headerView.tag = section
headerHeading.text = items[section].name
headerHeading.textColor = .white
headerView.addSubview(headerHeading)
headerView.addSubview(imageView)
return headerView
}
func numberOfSections(in tableView: UITableView) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
let itms = items[section]
return !itms.collapsed ? 0 : itms.items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) ->
UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
cell.textLabel?.text = items[indexPath.section].items[indexPath.row]
return cell
}
#objc func headerViewTapped(tapped:UITapGestureRecognizer){
print(tapped.view?.tag)
if items[tapped.view!.tag].collapsed == true{
items[tapped.view!.tag].collapsed = false
}else{
items[tapped.view!.tag].collapsed = true
}
if let imView = tapped.view?.subviews[1] as? UIImageView{
if imView.isKind(of: UIImageView.self){
if items[tapped.view!.tag].collapsed{
imView.image = UIImage(named: "collapsed")
}else{
imView.image = UIImage(named: "expand")
}
}
}
tableView.reloadData()
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let row = items[indexPath.row]
print("IndexPath :- \(row.name)")
}
}
If you look at the way you are setting the text in your cells in cellForRowAt:
cell.textLabel?.text = items[indexPath.section].items[indexPath.row]
You are saying:
get the ItemList object for indexPath.section from items array
get the String from that object's items array of this indexPath.row
However, in your didSelectRowAt:
let row = items[indexPath.row]
print("IndexPath :- \(row.name)")
You are saying:
get the ItemList object for indexPath.row from items array
print the .name property of that object
So, change your didSelectRowAt code to match your cellForRowAt logic:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedString = items[indexPath.section].items[indexPath.row]
print("IndexPath :- \(selectedString)")
// or, for a little more clarity
let sectionObject = items[indexPath.section]
let rowItem = sectionObject.items[indexPath.row]
print("IndexPath :- \(indexPath) // Section :- \(sectionObject.name) // Item :- \(rowItem)")
}
booktable.frame = CGRect(x: 0, y: booktopview.bounds.height, width: screenWidth, height: screenHeight-booktopview.bounds.height-tabbarView.bounds.height)
booktable.register(UITableViewCell.self, forCellReuseIdentifier: "mycell")
booktable.dataSource = self
booktable.delegate = self
booktable.separatorColor = UIColor.lightGray
booktable.backgroundColor = UIColor.clear
booktable.separatorStyle = .singleLine
bookview.addSubview(booktable)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if(tableView == booktable)
{
let cell1 = booktable.dequeueReusableCell(withIdentifier: "mycell")
for object in (cell1?.contentView.subviews)!
{
object.removeFromSuperview();
}
let img :UIImageView = UIImageView()
let lbl : UILabel = UILabel()
img.frame = CGRect(x: 15, y: 15, width: 80, height: 130)
img.image = imgarray[indexPath.row]
img.layer.borderWidth = 1.0
img.layer.borderColor = UIColor.lightGray.cgColor
cell1?.contentView.addSubview(img)
imgheight = img.bounds.height
lbl.frame = CGRect(x: img.bounds.width + 40, y: (imgheight+40-80)/2, width: booktable.bounds.width-img.bounds.width + 40 - 100, height: 80)
lbl.text = imgname[indexPath.row]
lbl.numberOfLines = 0
lbl.textAlignment = .left
lbl.font = UIFont(name: "Arial", size: 23)
lbl.textColor = UIColor.black
cell1?.selectionStyle = .none
cell1?.contentView.addSubview(lbl)
return cell1!
}
The code shown above is for book table, which sometimes scrolls like normal and sometimes not scrolling at all. I am doing all the code programatically. I have tested this on both simulators and devices but still the problem exists. Any help is appreciated...
Create Custom UITableViewCell, let's say it is ListTableCell
class ListTableCell: UITableViewCell {
#IBOutlet weak var lblTemp: UILabel!
#IBOutlet weak var imgTemp: UIImage!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
I've created UITableViewCell with xib like this and bind IBOutlets
Let's say we have struct Model and array like this
struct Model {
let image : UIImage
let name: String
}
for i in 0...10 {
let model = Model(image: #imageLiteral(resourceName: "Cat03"), name: "Temp \(i)")
array.append(model)
}
Now on ViewController viewDidLoad() method,
tableView.register(UINib(nibName: "ListTableCell", bundle: nil), forCellReuseIdentifier: "ListTableCell")
Implement UITableViewDataSource methods like this,
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ListTableCell") as! ListTableCell
let model = array[indexPath.row]
cell.lblTemp.text = model.name
cell.imgTemp.image = model.image
return cell
}
FYI
For different tableviews, you can create different custom cell the same way and cellForRowAt indexPath and numberOfRowsInSection method will change appropriately.
Let me know in case of any queries.
UPDATE
Follow this and this to create CustomTableCell programmatically
I have done a UITableViewCell programmatically and it worked just fine with iOS 10. But after updating with iOS 11 and XCode 9, it behaves differently. The layout looks scrambled as below.
But if I tap on the cell then it rearranges and looks fine as below.
Here the code for UITableViewCell
import UIKit
import SnapKit
class AboutCell: UITableViewCell {
let roleLabel : UILabel = {
var tablelabel = UILabel()
tablelabel.font = UIFont (name: "Avenir Next Medium", size: 22)
tablelabel.textAlignment = .center
tablelabel.clipsToBounds = true
tablelabel.translatesAutoresizingMaskIntoConstraints = false
return tablelabel
}()
let nameLabel : UILabel = {
let tablelabel = UILabel()
tablelabel.font = UIFont (name: "Avenir Next Medium", size: 16)
tablelabel.textAlignment = .center
tablelabel.clipsToBounds = true
tablelabel.translatesAutoresizingMaskIntoConstraints = false
return tablelabel
}()
let webUrlLabel : UILabel = {
let tablelabel = UILabel()
tablelabel.font = UIFont (name: "Avenir Next Medium", size: 16)
tablelabel.textAlignment = .center
tablelabel.clipsToBounds = true
tablelabel.translatesAutoresizingMaskIntoConstraints = false
return tablelabel
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
}
override func layoutSubviews() {
super.layoutSubviews()
roleLabel.frame = CGRect(x: 0, y: 10, width: self.contentView.bounds.size.width-20, height: 25)
nameLabel.frame = CGRect(x: 0, y: roleLabel.frame.origin.y+25, width: self.bounds.size.width-20, height: 25)
webUrlLabel.frame = CGRect(x: 0, y: nameLabel.frame.origin.y+25, width: self.bounds.size.width-20, height: 25)
}
func setupViews(){
contentView.addSubview(roleLabel)
contentView.addSubview(nameLabel)
contentView.addSubview(webUrlLabel)
}
func setValuesForCell(contributor : Contributors){
roleLabel.text = contributor.contributorRole
nameLabel.text = contributor.contributorName
webUrlLabel.text = contributor.contributorWeb
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
and the extension I wrote for TableView delegate and datasource
extension AboutController : UITableViewDelegate, UITableViewDataSource{
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return contributorList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : AboutCell = tableView.dequeueReusableCell(withIdentifier: self.cellid, for: indexPath) as! AboutCell
cell.selectionStyle = .default
let contributor : Contributors = contributorList[indexPath.row]
cell.setValuesForCell(contributor: contributor)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath.row)
tableView.deselectRow(at: indexPath, animated: true)
}
}
and the ViewDidLoad method
override func viewDidLoad() {
super.viewDidLoad()
tableView.estimatedRowHeight = 100.0
tableView.rowHeight = 100
tableView.register(AboutCell.self, forCellReuseIdentifier: self.cellid)
view.addSubview(tableView)
tableView.snp.makeConstraints { (make) in
make.top.right.bottom.left.equalToSuperview()
}
let mayu = Contributors(contibutorRole: "Developer", contributorName: "J Mayooresan", contributorWeb: "http://jaymayu.com")
let janie = Contributors(contibutorRole: "Voice Artist", contributorName: "M Jananie", contributorWeb: "http://jaymayu.com")
let arjun = Contributors(contibutorRole: "Aathichudi Content", contributorName: "Arjunkumar", contributorWeb: "http://laymansite.com")
let artist = Contributors(contibutorRole: "Auvaiyar Art", contributorName: "Alvin", contributorWeb: "https://www.fiverr.com/alvincadiz18")
contributorList = [mayu, arjun, janie, artist]
tableView.delegate = self
tableView.dataSource = self
self.tableView.reloadData()
}
Since you're laying out your tableView using autolayout, you also need to ensure translatesAutoresizingMaskIntoConstraints is set to false.
SnapKit should be setting the tableView's translatesAutoresizingMaskIntoConstraints to false for you already.
Since you're laying out the cells manually (using frames in layoutSubviews). Setting the cells subview's translatesAutoresizingMaskIntoConstraints to false is likely not needed.
See Apple Docs here for translatesAutoresizingMaskIntoConstraints
I'm making a message that is shown when the tableView is empty and I've added a label in my subView and it appears to be pixelated. But when I add something in my tableView and delete it, then the message shown (UILabel) is perfectly fine. Can't figure out why.
Adding the label in my self.tableView.backgroundView = emptyLabel solves it but I want to add two labels so I add one in subView that makes it pixelated.
Here is my code:
class ReminderTableViewController: UITableViewController
{
#IBOutlet var myTableView: UITableView!
#IBOutlet weak var dateLabel: UIView!
#IBAction func back(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.separatorColor = UIColor.clear
tableView.separatorStyle = .none
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "details", sender: self)
}
public func buttonImageForEmptyStateView() -> UIImage? {
return UIImage.init(named: "Exclamation Mark Filled-100-2")
}
override func viewDidAppear(_ animated: Bool) {
tableView.reloadData()
self.reloadEmptyState(forTableView: self.tableView)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if list.count==0{
let emptyLabel=UILabel(frame: CGRect(x: 0,y: 0, width: self.view.bounds.width, height: self.view.bounds.height))
let emptyLabel2=UILabel(frame: CGRect(x: 0.0,y: 20.0, width: self.view.bounds.width, height: self.view.bounds.height))
self.view.addSubview(emptyLabel2)
self.view.addSubview(emptyLabel)
let emptyImage = UIImageView(image: UIImage(named: "Quote Right Filled-100 (3)"))
emptyImage.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(emptyImage)
emptyImage.alpha=0.1
NSLayoutConstraint.activate([
emptyImage.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
emptyImage.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
emptyImage.heightAnchor.constraint(equalToConstant: 90),
emptyImage.widthAnchor.constraint(equalToConstant: 90)
])
emptyLabel.text = "no present reminders"
emptyLabel.textColor=UIColor.darkGray
emptyLabel.font=UIFont(name: "HelveticaNeue-Light", size: 18)
emptyLabel.textAlignment = NSTextAlignment.center
emptyLabel2.text = "you add by going back to the homescreen"
emptyLabel2.textColor=UIColor.gray
emptyLabel2.font=UIFont(name: "HelveticaNeue-Light", size: 11)
emptyLabel2.textAlignment = NSTextAlignment.center
self.tableView.separatorStyle = UITableViewCellSeparatorStyle.none
return 0
}
else{
return (list.count)
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell=UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
cell.textLabel?.text=list[indexPath.row]
cell.contentView.backgroundColor = UIColor(red:0.89, green:0.89, blue:0.89, alpha:0.7)
cell.textLabel?.textColor=UIColor.black
cell.textLabel?.font=UIFont(name: "HelveticaNeue-Light", size: 16)
return (cell)
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle==UITableViewCellEditingStyle.delete{
list.remove(at: indexPath.row)
tableView.reloadData()
}
}
}
numberOfRowsInSection is call multiple times, so there is not good place to add subview because every time this function is call another UILabel's instance add to view hierarchy.
Remove adding labels in numberOfRowsInSection. Add class variables:
var emptyLabel: UILabel!
var emptyLabel2: UILabel!
var emptyImage: UIImageView!
then add the function
func createEmptyLabels() {
emptyLabel=UILabel(frame: CGRect(x: 0,y: 0, width: self.view.bounds.width, height: self.view.bounds.height))
emptyLabel2=UILabel(frame: CGRect(x: 0.0,y: 20.0, width: self.view.bounds.width, height: self.view.bounds.height))
self.view.addSubview(emptyLabel2)
self.view.addSubview(emptyLabel)
emptyImage = UIImageView(image: UIImage(named: "Quote Right Filled-100 (3)"))
emptyImage.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(emptyImage)
emptyImage.alpha=0.1
NSLayoutConstraint.activate([
emptyImage.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
emptyImage.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
emptyImage.heightAnchor.constraint(equalToConstant: 90),
emptyImage.widthAnchor.constraint(equalToConstant: 90)
])
emptyLabel.text = "no present reminders"
emptyLabel.textColor=UIColor.darkGray
emptyLabel.font=UIFont(name: "HelveticaNeue-Light", size: 18)
emptyLabel.textAlignment = NSTextAlignment.center
emptyLabel2.text = "you add by going back to the homescreen"
emptyLabel2.textColor=UIColor.gray
emptyLabel2.font=UIFont(name: "HelveticaNeue-Light", size: 11)
emptyLabel2.textAlignment = NSTextAlignment.center
emptyLabel.isHidden = true
emptyLabel2.isHidden = true
emptyImage.isHidden = true
}
call this function in viewDidLoad
and in numberOfRowsInSection:
let hideEmptyViews = (list.count != 0)
emptyLabel.isHidden = hideEmptyViews
emptyLabel2.isHidden = hideEmptyViews
emptyImage.isHidden = hideEmptyViews
You are adding the label in numberOfRowsInSection. The problem is that this method is called many times. So you are adding many copies of the label, piled on top of one another, and this makes the label look funny.
I set up a table view in storyboard and gave it a custom cell class:
Storyboard:
Cell class:
class CommentCell: UITableViewCell {
override func layoutSubviews() {
super.layoutSubviews()
textLabel?.frame = CGRect(x: 100, y: textLabel!.frame.origin.y - 2, width: textLabel!.frame.width, height: textLabel!.frame.height)
detailTextLabel?.frame = CGRect(x: 100, y: detailTextLabel!.frame.origin.y + 2, width: detailTextLabel!.frame.width, height: detailTextLabel!.frame.height)
}
let logoView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
imageView.layer.borderWidth = 1
imageView.layer.cornerRadius = 24
imageView.layer.masksToBounds = true
return imageView
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: .subtitle, reuseIdentifier: reuseIdentifier)
addSubview(logoView)
logoView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 8).isActive = true
logoView.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
logoView.widthAnchor.constraint(equalToConstant: 48).isActive = true
logoView.heightAnchor.constraint(equalToConstant: 48).isActive = true
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
And then in my view controller I gave it some dummy text to test it out, and the textLabel text shows up fine, but the detailTextLabel text and the imageView do not.
#IBOutlet weak var tableView: UITableView!
let commentors = ["Person One", "Person Two", "Person Three"]
let comments = ["Comment One", "Comment Two", "Comment Three"]
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CommentCell
cell.textLabel?.text = commentors[indexPath.row]
cell.detailTextLabel?.text = comments[indexPath.row]
DispatchQueue.main.async {
cell.logoView.image = UIImage(named: "testImage")
}
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return commentors.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 72
}
This is what I see when I run the app:
Anyone know why the textLabel is showing up but not the detailTextLabel or the image?
A table view cell with style custom has no implicit detailTextLabel and no imageView.
You have to implement the elements in the storyboard and connect them to appropriate IBOutlets.
#vadian's answer is correct, however, you do not have to add the other label manually.
I was facing the same issue and realised while looking at your question that the type of the cell in the Interface Builder is Custom and not subtitle. You initialise it correctly, but it seems like the style set in the IB has priority.
subtitle style makes sure that the detailTextLabel shows up and shows it already in the storyboard.
Hope this helps others.