Creating Custom Tableview Cells in Swift

creating custom tableview cells in swift

Thanks for all the different suggestions, but I finally figured it out. The custom class was set up correctly. All I needed to do, was in the storyboard where I choose the custom class: remove it, and select it again. It doesn't make much sense, but that ended up working for me.

How to create custom cells 100% programmatically in Swift?

I see several errors in your cell.

  1. Add subviews to contentView, not directly to cell:

    contentView.addSubview(cellView)
    cellView.addSubview(labelView)

    The same is necessary for constraints:

    NSLayoutConstraint.activate([
    cellView.topAnchor.constraint(equalTo: contentView.topAnchor),
    cellView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
    cellView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
    cellView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor)
    ])
  2. Views created in code need to set translatesAutoresizingMaskIntoConstraints = false,

    let cellView: UIView = {
    let view = UIView()
    view.backgroundColor = .systemRed
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
    }()

    let labelView: UILabel = {
    let label = UILabel()
    label.text = "Cell 1"
    label.translatesAutoresizingMaskIntoConstraints = false
    return label
    }()

    1. There are no constraints for your label.

How do I create a custom view in a custom table view cell?

The answer to you question is "Yes, you should put checkbox settings to another place. Particularly I would suggest you to move it to CustomTableViewCell class"

There is an opinion, that good Object-oriented software design is based on SOLID principles.

SOLID is an acronym. And S stands for Single responsibility principle.. Simply saying - one class should be responsible for one thing and this responsibility should be encapsulated by the class.

In your case ViewController takes responsibility for the Cells subview layouting and adjustments. Which breaks at least ~S~ in SOLID

P.S. To be honest I am a bit afraid to write this answer, because of often arguments about good software design.

Creating custom table View with Cell properties programmatically

In setUpUI() method of your RoomViewController class, set tableView's rowHeight to UITableView.automaticDimension and estimatedRowHeight to 100.

import UIKit

class RoomViewController: UIViewController {

private func setUpUI() {

view.backgroundColor = .white

title = "Room List "
view.addSubview(tableView)

tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableView.automaticDimension

// create constraints
tableView.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor).isActive = true
tableView.leftAnchor.constraint(equalTo:view.safeAreaLayoutGuide.leftAnchor).isActive = true
tableView.rightAnchor.constraint(equalTo:view.safeAreaLayoutGuide.rightAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true


// Creating constrain for Indecator
activityIndicator.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(activityIndicator)
activityIndicator.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
activityIndicator.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true

}
}

And add UIStackView in the RoomCellTableViewCell class as bellow:

import UIKit

class RoomCellTableViewCell: UITableViewCell {

private var stackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .vertical
stackView.distribution = .fill
stackView.alignment = .fill
stackView.spacing = 8
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)

addSubview(stackView)

NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: topAnchor),
stackView.bottomAnchor.constraint(equalTo: bottomAnchor),
stackView.leftAnchor.constraint(equalTo: leftAnchor),
stackView.rightAnchor.constraint(equalTo: rightAnchor)
])

stackView.addArrangedSubview(createdAtLabel)
stackView.addArrangedSubview(isOccupiedLabel)
stackView.addArrangedSubview(maxOccupancyLabel)
// stackView.addArrangedSubview(idLabel)
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

How to add a custom cell of uitableviewcell type as tableview header?

You should use the UITableViewDelagates and Datasource functions for the sections.

Try this:

func numberOfSections(in tableView: UITableView) -> Int {

return 1
}

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {

if let headerView = tableView.dequeueReusableCell(withIdentifier: "CustomCellHeader") as? CustomCellHeader {
cell.configure(tit: model.title)
cell.delegate = self
return headerView
}

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {

return 30
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {

return 110
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

return anyArray.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

if let cell = tableView.dequeueReusableCell(withIdentifier: "TableCell") as? TableCell {
cell.configureCell(with: model.data[indexPath.row])

return cell
}
}

You can add number of sections according to your choice and also can set the header's cell and the other's cell height separately.

Unable To Create a Custom Table View Cell programatically, Swift

I have come to know that without a NIB file, awakeFromNib won't be invoked, so the view wasn't being added.

By looking at this Stackoverflow question I modified the code to the below and it worked.

class AppointmentTVCell: UITableViewCell {

var caseId = UILabel()

required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}


override init(style: UITableViewCellStyle, reuseIdentifier: String!) {

super.init(style: style, reuseIdentifier: reuseIdentifier)
//Do your cell set up
caseId.frame = CGRect(x: 0, y: 50, width: 100, height: 30)
caseId.text = "Hello"
caseId.font = UIFont(name:"HelveticaNeue-Bold", size: 16)
contentView.addSubview(caseId)
}


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
}
}

Using custom XIB in table view cell

You can use 2 ways:

Create UITableViewCell (better)

1) Change UIView to UITableViewCell

class CustomTableViewCell: UITableViewCell { 

...

class var identifier: String {
return String(describing: self)
}
}

2) Register your cell

override func viewDidLoad() {
super.viewDidLoad()

self.tableView.registerNib(UINib(nibName: CustomTableViewCell.identifier, bundle: nil), forCellReuseIdentifier: CustomTableViewCell.identifier)
...
}

3) Use cellForRow(at:)

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

let cell = tableView.dequeueReusableCell(withIdentifier: CustomTableViewCell.identifier) as! CustomTableViewCell
cell.username.text = user.username

return cell
}

OR Add view as subview to cell (only in rare cases)

1) Add this to UserView

class UserView: UIView {

...

class func fromNib() -> UserView {
return UINib(nibName: String(describing: self), bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! UserView
}

}

2) Use cellForRow(at:)

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

let cellIdentifier = "UserTableViewCell"

guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? UserTableViewCell else {
fatalError("The dequeued cell is not an instance of UserTableViewCell.")
}

let userView = UserView.fromNib()
let user = users[indexPath.row]
userView.username.text = user.username

//Use frame size, but for me better to add 4 constraints
userView.frame = CGRect(x: 0, y: 0, width: cellWidth, height: cellHeight)

cell.contentView.addSubview(UserView)

return cell
}

Can you make a TableView in a custom cell?

You can absolutely put a UITableView inside UITableViewCells

Where to put .delegate = self depends how you created the cell.

If you created it programmatically then use override init

 override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
tableView = UITableView()
tableView.delegate = self
tableView.dataSource = self
}

If however your load the cell from nib or storyboard then use initWithCoder

-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
tableView = UITableView()
tableView.delegate = self
tableView.dataSource = self
}
return self;
}

Just a note, you are setting yourself up for a bumpy road. Its entirely possible nonetheless so good luck!

Insert Custom Cell into TableView

From my practice it is better to create custom type of cell. And add this type as a property to your PostViewModel. After that you can recognize what type of cell you should to dequeue. For example:

// Type of custom cell
enum PostsType {
case postCell
case loginCell
}

struct PostViewModel {
let type: PostsType
// another View model data
}

class ViewController: UITableViewController {

var posts: [PostViewModel] = [] {
didSet {
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
posts.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellType = posts[indexPath.row].type

switch cellType {
case .loginCell:
return tableView.dequeueReusableCell(withIdentifier: "LoginCell", for: indexPath)
case .postCell:
return tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexPath)
}
}
}


Related Topics



Leave a reply



Submit