How to add subview to stackview in table view cell? - ios

I am in need to add the label as subview to UIStackView in table view cell.
I have created label as
let nameLabel=UILabel()
nameLabel.text=names[indexPath.row]
Where name is an array which is a type of String
My code is
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource
{
let names=["Amutha","Priya","Amuthapriya","Priyasri","Kavisha"]
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return names.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) ->
UITableViewCell
{
let cell=tableView.dequeueReusableCell(withIdentifier: "cell") as! ViewCell
for name in names
{
let nameLabel=UILabel()
nameLabel.text=name
cell.nameStackView!.addSubview(nameLabel)
}
return cell
}
}
Why I am getting a null pointer exception when I add a label to stackview?
Any help will be much appreciated.

Change
cell.nameStackView!.addSubview(nameLabel)
To
cell.nameStackView!.addArrangedSubview(nameLabel)

You can use below code to add UIStackView as per your need.
let titleLabel = UILabel()
let subtitleLabel = UILabel()
lazy var titleStackView: UIStackView = {
titleLabel.textAlignment = .center
titleLabel.text = "Good Morning"
titleLabel.textColor = UIColor.white
titleLabel.font = UIFont(name: "ProximaNova-Regular", size: 12.0)
subtitleLabel.textAlignment = .center
subtitleLabel.text = "--"
subtitleLabel.textColor = UIColor.white
subtitleLabel.heightAnchor.constraint(equalToConstant: 30.0).isActive = true
subtitleLabel.font = UIFont(name: "DublinSpurs", size: 20.0)
let stackView = UIStackView(arrangedSubviews: [subtitleLabel])
stackView.axis = .vertical/.horizontal
return stackView
}()
Also try replacing
for name in names
{
let nameLabel=UILabel()
nameLabel.text=name
cell.nameStackView!.addSubview(nameLabel)
}
with
let nameLabel=UILabel()
nameLabel.text = names[indexPath.row]
cell.nameStackView!.addSubview(nameLabel)

If you have nameStackView in storyboard or xib, make sure the IBOutlet is connected properly. If you have created the nameStackView programmatically make sure it is initialised.
And remove all existing labels from the stackview, else you'll have duplicate labels on each scroll
class ViewCell: UITableViewCell {
//Make sure IBOutlet is connected properly
#IBOutlet weak var nameStackView: UIStackView!
}
ViewController
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! ViewCell
for name in names
{
cell.nameStackView.arrangedSubviews.forEach { cell.nameStackView.removeArrangedSubview($0) }
let nameLabel=UILabel()
nameLabel.text=name
cell.nameStackView.addSubview(nameLabel)
}
return cell
}

Try below code
let nameLabel = UILabel()
cell.nameStackView.addArrangedSubview(nameLabel)

Try initializing the stackview and label in your ViewCell class.
class ViewCell: UITableViewCell {
let nameLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 14)
return label
}()
let nameStackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .vertical
return stackView
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super .init(style: style, reuseIdentifier: reuseIdentifier)
self.configureStackView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configureStackView()
{
addSubview(nameStackView)
nameStackView.translatesAutoresizingMaskIntoConstraints = false
nameStackView.topAnchor.constraint(equalTo: topAnchor).isActive = true
nameStackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
nameStackView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
nameStackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
nameStackView.addArrangedSubview(nameLabel)
nameLabel.translatesAutoresizingMaskIntoConstraints = false
nameLabel.topAnchor.constraint(equalTo: nameStackView.topAnchor,
constant:10).isActive = true
nameLabel.leadingAnchor.constraint(equalTo: nameStackView.leadingAnchor,
constant:10).isActive = true
nameLabel.trailingAnchor.constraint(equalTo: nameStackView.trailingAnchor,
constant:-10).isActive = true
nameLabel.bottomAnchor.constraint(equalTo: nameStackView.bottomAnchor,
constant:-10).isActive = true
}
}
This code is in case you didn't create your stackview in storyboard.
Now in your ViewController class, add the following code in your cellForRowAt method.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) ->
UITableViewCell
{
let cell=tableView.dequeueReusableCell(withIdentifier: "cell") as! ViewCell
cell.nameLabel.text = names[indexPath.row]
return cell
}
Hope, this solution works for you.

Related

Swift Table View Cell Value Failed to display vertically

I am new to swift . I am creating table view cell programatically with the sub title . I defined the two label and two function to get the value form API and display it into label control but the problem is the values of label are displayed into correct position . I want to display the title and then below the title is sub title values . Here is the screen shot .
Here is the code table view cell.
extension ViewController: UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewModel.rovers.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: StoryCell.identifier , for: indexPath) as? StoryCell
else{ return UITableViewCell()}
let row = indexPath.row
let title = viewModel.getTitle(by: row)
cell.configureCell(title:title)
let Id = viewModel.getId(by: row)
cell.configureCell(Id: Id)
return cell
}
}
Here is code for configure the label .
import UIKit
class StoryCell: UITableViewCell {
static let identifier = "StoryCell"
private lazy var storyTitleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textAlignment = .left
return label
}()
private lazy var storyIdLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textAlignment = .left
return label
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setUpUI()
setUpUIID()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configureCell(title: String) {
storyTitleLabel.text = "Status :\(title)"
}
func configureCell(Id: Int) {
storyIdLabel.text = "Id: \(String(Id))"
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
private func setUpUI() {
contentView.addSubview(storyTitleLabel)
// constraints
let safeArea = contentView.safeAreaLayoutGuide
storyTitleLabel.topAnchor.constraint(equalTo: safeArea.topAnchor).isActive = true
storyTitleLabel.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor).isActive = true
storyTitleLabel.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor).isActive = true
storyTitleLabel.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor).isActive = true
}
private func setUpUIID() {
contentView.addSubview(storyIdLabel)
// constraints
let safeArea = contentView.safeAreaLayoutGuide
storyIdLabel.topAnchor.constraint(equalTo: safeArea.topAnchor).isActive = true
storyIdLabel.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor).isActive = true
storyIdLabel.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor).isActive = true
storyIdLabel.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor).isActive = true
}
}
I made some changes in your code check below. Now it should be fine.
private func setUpUI() {
contentView.addSubview(storyTitleLabel)
// constraints
let safeArea = contentView.safeAreaLayoutGuide
storyTitleLabel.topAnchor.constraint(equalTo: safeArea.topAnchor).isActive = true
storyTitleLabel.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor).isActive = true
storyTitleLabel.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor).isActive = true
}
private func setUpUIID() {
contentView.addSubview(storyIdLabel)
let safeArea = contentView.safeAreaLayoutGuide
storyIdLabel.topAnchor.constraint(equalTo: storyTitleLabel.topAnchor).constant = 5
storyIdLabel.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor).isActive = true
storyIdLabel.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor).isActive = true
storyIdLabel.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor).isActive = true
}

Adding top and bottom constraints causes UILable to be squished

Programmatically I created a custom UITableViewCell and tried centering two UILabels vertically inside it. But the UILabel ended up being squished. Doing the same thing in Interface Builder with a prototype cell works well. What is wrong with my code?
The Custom view cell class
import UIKit
class TopViewCell: UITableViewCell {
let df: DateFormatter = {
let df = DateFormatter()
df.dateFormat = NSLocalizedString("DATE_WEEKDAY", comment: "show date and weekday")
return df
}()
var dateLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
var costLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
let margin = contentView.layoutMarginsGuide
contentView.addSubview(dateLabel)
dateLabel.leadingAnchor.constraint(equalTo: margin.leadingAnchor).isActive = true
dateLabel.topAnchor.constraint(equalTo: margin.topAnchor).isActive = true
dateLabel.bottomAnchor.constraint(equalTo: margin.bottomAnchor).isActive = true
contentView.addSubview(costLabel)
costLabel.trailingAnchor.constraint(equalTo: margin.trailingAnchor).isActive = true
costLabel.topAnchor.constraint(equalTo: dateLabel.topAnchor).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
dateLabel.text = df.string(from: Date())
costLabel.text = "total: five thousand"
}
}
The Custom UITableViewController class
import UIKit
class ItemViewController: UITableViewController {
var itemStore: ItemStore!
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(TopViewCell.self, forCellReuseIdentifier: "top_cell")
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return itemStore.allItems.count + 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell: UITableViewCell!
if indexPath.row == 0 {
cell = tableView.dequeueReusableCell(withIdentifier: "top_cell", for: indexPath)
} else {
cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = itemStore.allItems[indexPath.row - 1].name
cell.textLabel?.font = cell.textLabel!.font.withSize(30)
cell.detailTextLabel?.text = "$\(itemStore.allItems[indexPath.row - 1].valueInDolloar)"
}
return cell
}
}
Your TopViewCell is not auto-sizing correctly because you're setting the text in layoutSubviews(). Move those two lines to init and it will size properly:
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
let margin = contentView.layoutMarginsGuide
contentView.addSubview(dateLabel)
dateLabel.leadingAnchor.constraint(equalTo: margin.leadingAnchor).isActive = true
dateLabel.topAnchor.constraint(equalTo: margin.topAnchor).isActive = true
dateLabel.bottomAnchor.constraint(equalTo: margin.bottomAnchor).isActive = true
contentView.addSubview(costLabel)
costLabel.trailingAnchor.constraint(equalTo: margin.trailingAnchor).isActive = true
costLabel.topAnchor.constraint(equalTo: dateLabel.topAnchor).isActive = true
// set the text here
dateLabel.text = df.string(from: Date())
costLabel.text = "total: five thousand"
}
As a side note, you should specify the class when you use TopViewCell:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "top_cell", for: indexPath) as! TopViewCell
return cell
}
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = itemStore.allItems[indexPath.row - 1].name
cell.textLabel?.font = cell.textLabel!.font.withSize(30)
cell.detailTextLabel?.text = "$\(itemStore.allItems[indexPath.row - 1].valueInDolloar)"
return cell
}
As another side note... you can create two prototype cells in your Storyboard.

custom tableviewcell dynamic height not updating as per constraints (with programmatically autolayout)

I have a custom uitableviewcell in app, I am trying to update its length dynamically based on its subviews (labels) contents.
but it's not working.
find related code as below.
class TransactionTableViewCell: UITableViewCell{
private lazy var dateLabel: UILabel = {
let datelabel = UILabel()
datelabel.textColor = .label
datelabel.backgroundColor = .red
datelabel.lineBreakMode = .byTruncatingTail
datelabel.numberOfLines = 0
datelabel.translatesAutoresizingMaskIntoConstraints = false
return datelabel
}()
another method in same class:
func setupDefaultUI(){
self.contentView.addSubview(dateLabel)
}
override func awakeFromNib() {
super.awakeFromNib()
setupDefaultUI()
buildConstraints()
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupDefaultUI()
buildConstraints()
}
func buildConstraints(){
let marginGuide = self.contentView
NSLayoutConstraint.activate([
dateLabel.topAnchor.constraint(equalTo: marginGuide.topAnchor)
,
dateLabel.leadingAnchor.constraint(equalTo: marginGuide.leadingAnchor),
dateLabel.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor),
dateLabel.heightAnchor.constraint(greaterThanOrEqualToConstant: 1.0)
,
dateLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor)
])
}
and inside viewcontroller file:
private lazy var transactionTableView: UITableView = {
let tableview = UITableView.init()
tableview.backgroundView = nil
tableview.backgroundColor = .clear
tableview.rowHeight = UITableView.automaticDimension
tableview.estimatedRowHeight = 300
return tableview
}()
and in viewdidload:
transactionTableView.dataSource = Objdatasource
transactionTableView.delegate = Objdelegate
transactionTableView.register(TransactionTableViewCell.self, forCellReuseIdentifier: CellIdentifiers.transactionCell)
Remove this
dateLabel.heightAnchor.constraint(greaterThanOrEqualToConstant: 1.0)
In this tutorial (which I recently followed and worked for me), there is a function you are missing there:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}

How to create custom cells 100% programmatically in Swift?

I am trying to build a TableView programmatically, but I cannot get a basic standard label to display; all I see is basic empty cells. Here's my code:
TableView Cell:
class TableCell: UITableViewCell {
let cellView: UIView = {
let view = UIView()
view.backgroundColor = .systemRed
return view
}()
let labelView: UILabel = {
let label = UILabel()
label.text = "Cell 1"
return label
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setup()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setup() {
addSubview(cellView)
NSLayoutConstraint.activate([
cellView.topAnchor.constraint(equalTo: topAnchor),
cellView.bottomAnchor.constraint(equalTo: bottomAnchor),
cellView.leadingAnchor.constraint(equalTo: leadingAnchor),
cellView.trailingAnchor.constraint(equalTo: trailingAnchor)])
cellView.addSubview(labelView)
}
}
Data Source:
class TableDataSource: NSObject, UITableViewDataSource {
let cellID = "cell"
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath) as! TableCell
return cell
}
}
And this is the VC:
class TableViewController: UITableViewController {
let dataSource = TableDataSource()
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(TableCell.self, forCellReuseIdentifier: dataSource.cellID)
tableView.dataSource = dataSource
}
}
I am trying to keep the code as basic as possible for future references. I've set various breakpoints to see what could go wrong, but they all check out. Could it be the constraints that are wrong?
Any help is appreciated.
I see several errors in your cell.
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)
])
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
}()
There are no constraints for your label.
Your constraints don't work, because you need to change translatesAutoresizingMaskIntoConstraints for cellView in your setup():
func setup() {
addSubview(cellView)
cellView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
cellView.topAnchor.constraint(equalTo: topAnchor),
cellView.bottomAnchor.constraint(equalTo: bottomAnchor),
cellView.leadingAnchor.constraint(equalTo: leadingAnchor),
cellView.trailingAnchor.constraint(equalTo: trailingAnchor)])
cellView.addSubview(labelView)
}

How to set a label in a cell of tableview by code?

If I just want to set a label by code, I should write for example like this,
let label = UILabel()
label.frame = CGRect(x:10, y: 10, width:160, height:30)
label.text = "Test"
self.view.addSubview(label)
but if I want to set a label in a cell of tableView, how can I set it?
Thanks!
You should create you a custom class for you cell as such. This adds a label to a cell and uses anchoring system to set constraints on the label to fill the entire cell.
class CustomCell: UITableViewCell {
let label: UILabel = {
let n = UILabel()
n.textColor = UIColor.darkGray
n.textAlignment = .center
n.text = "Testing 123"
n.font = UIFont(name: "Montserrat", size: 30)
return n
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
label.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
label.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
label.topAnchor.constraint(equalTo: topAnchor).isActive = true
label.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
For the ViewController that uses this custom cell, you have to add the below cell registration like such, unless you're using storyboards/interface builder.
class ControllerUsesCell: UITableViewController {
let defaultCellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
tableView?.register(CustomCell.self, forCellWithReuseIdentifier: defaultCellId)
}
}
UITableviewCell subclass has textlabel property which you can use
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as UITableViewCell
cell.textLabel?.text = "My Text"
return cell
}
or alternatively you can use custom cell for more control
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomTableViewCell
let label = UILabel()
label.frame = CGRect(x: 10, y: 10, width: 160, height: 30)
label.text = "Test"
cell.contentView.addSubview(label)
return cell
}

Resources