UIStackView content hugging is not working as expected in UITableViewCell - ios

I have two vertical StackView nested in horizontal StackView. For the first one:
titlesView.axis = .vertical
titlesView.distribution = .fill
titlesView.alignment = .leading
And the second one:
checkView.axis = .vertical
checkView.distribution = .fil
checkView.alignment = .center
and for holderStackView(root)
holderStackView.axis = .horizontal
holderStackView.alignment = .center
holderStackView.distribution = .fill
holderStackView.addArrangedSubview(titlesView)
holderStackView.addArrangedSubview(checkView)
titlesView.setContentHuggingPriority(.defaultLow, for: .horizontal)
checkView.setContentHuggingPriority(.defaultHigh, for: .horizontal)
I expect the titlesView must fill the StackView, but the checkView StackView fills the holderStackView stack view.
if I set alignment for both of them center or leading, then it works as expected and the first one grows.
Expectation:
Reality:
How can I fix this problem?
It happens only in the contentView of the UITableViewCell, see the minimal example below:
class ViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.separatorColor = UIColor(red:0.87, green:0.87, blue:0.87, alpha:1.00)
tableView.estimatedRowHeight = 150
tableView.backgroundColor = UIColor.clear
tableView.rowHeight = UITableViewAutomaticDimension
self.view.backgroundColor = UIColor(red:0.93, green:0.93, blue:0.93, alpha:1.00)
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 6
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return MedicationHomeCell()
}
}
class MedicationHomeCell: UITableViewCell {
let holderStackView = UIStackView()
let checkView = UIStackView()
let titlesView = UIStackView()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
layoutViews()
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
layoutViews()
}
func layoutViews() {
self.contentView.backgroundColor = .white
self.contentView.addSubview(holderStackView)
holderStackView.addArrangedSubview(titlesView)
holderStackView.addArrangedSubview(checkView)
titlesView.axis = .vertical
titlesView.distribution = .fill
titlesView.alignment = .leading
checkView.axis = .vertical
checkView.distribution = .fill
checkView.alignment = .center
holderStackView.axis = .horizontal
holderStackView.alignment = .center
holderStackView.distribution = .fill
titlesView.setContentHuggingPriority(.defaultLow, for: .horizontal)
checkView.setContentHuggingPriority(.defaultHigh, for: .horizontal)
holderStackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
holderStackView.leftAnchor.constraint(equalTo: self.contentView.leftAnchor),
holderStackView.rightAnchor.constraint(equalTo: self.contentView.rightAnchor),
holderStackView.topAnchor.constraint(equalTo: self.contentView.topAnchor),
holderStackView.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor)
])
for index in 0..<2 {
let label = UILabel()
label.text = "Label \(index)"
label.backgroundColor = .red
titlesView.addArrangedSubview(label)
}
for index in 0..<2 {
let label = UILabel()
label.text = "Text \(index)"
label.backgroundColor = .green
checkView.addArrangedSubview(label)
}
}
}

UIStackViews don't seem to respect the content hugging and resistance compression properties. Instead, set these properties on each arranged subview.
For nested stack views, you may need to drill down to their non-stack arranged subviews, and set the properties there.

Related

Ambiguous UILabel height in autoresizing UITableViewCell

I am having a hard time making my implementation of UITableView with automatic cell sizing. I am trying to use a UIStackView with 2 labels inside cell with automatic size.
It looks good overall but I am getting the following runtime issues:
I feel like the constraints I have should be enough for the use case here but I would like to get rid of the ambiguity here.
How that can be achieved? What is the common approach to take here?
I have prepared a sample project that simplifies my use case but still demonstrates the issue. Here is my controller:
class ViewController: UIViewController {
struct CellData {
let title: String
let subtitle: String
}
let table = UITableView()
let data = [
CellData(title: "Foo", subtitle: "Bar"),
CellData(title: "Baz", subtitle: "FooBar")
]
private let cellReuseID = "CellReuseID"
override func viewDidLoad() {
super.viewDidLoad()
table.register(Cell.self, forCellReuseIdentifier: cellReuseID)
table.dataSource = self
table.rowHeight = UITableView.automaticDimension
table.tableFooterView = UIView(frame: .zero)
table.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(table)
NSLayoutConstraint.activate([
table.leadingAnchor.constraint(equalTo: view.leadingAnchor),
table.trailingAnchor.constraint(equalTo: view.trailingAnchor),
table.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
table.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
])
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseID) as? Cell else {
fatalError()
}
cell.title = data[indexPath.row].title
cell.subtitle = data[indexPath.row].subtitle
return cell
}
}
And the cell it uses:
class Cell: UITableViewCell {
var title: String? {
didSet {
titleLabel.text = title
}
}
var subtitle: String? {
didSet {
subtitleLabel.text = subtitle
}
}
let titleLabel = UILabel()
let subtitleLabel = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupView() {
let stackView = UIStackView()
stackView.axis = .vertical
stackView.alignment = .top
stackView.distribution = .fill
stackView.spacing = 8
stackView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(stackView)
NSLayoutConstraint.activate([
stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
stackView.topAnchor.constraint(equalTo: contentView.topAnchor)
])
stackView.addArrangedSubview(titleLabel)
stackView.addArrangedSubview(subtitleLabel)
}
}
The reason for the Ambiguous Height is because both labels have the same Content Hugging Priority.
Auto-layout makes its "best guess" and you get the desired output -- but you'll still see the issues in Debug View Hierarchy.
To get rid of it, you can give one label a higher Hugging Priority.
For example (in setupView()):
titleLabel.setContentHuggingPriority(.defaultHigh + 1, for: .vertical)
subtitleLabel.setContentHuggingPriority(.defaultHigh, for: .vertical)

Is there any way to resize UIImageView inside UITableViewCell

I'm trying to add image inside of UITableViewCell with UIImageView, and really added "normally", but when i change the size of UIImageView. I get this error message:
[LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one
you don't want. Try this: (1) look at each constraint and try to
figure out which you don't expect; (2) find the code that added the
unwanted constraint or constraints and fix it. (
"<NSLayoutConstraint:0x600001a30b40 UIImageView:0x7fad5ed0fd90.height == 90 (active)>",
"<NSLayoutConstraint:0x600001a30b90 V:|-(0)-[UIStackView:0x7fad5ed10360] (active, names:
'|':Music.MusicsCell:0x7fad5ed0ec20'cellId' )>",
"<NSLayoutConstraint:0x600001a30d70 UIStackView:0x7fad5ed10360.bottom ==
Music.MusicsCell:0x7fad5ed0ec20'cellId'.bottom (active)>",
"<NSLayoutConstraint:0x600001a31270 'UISV-canvas-connection' UIStackView:0x7fad5ed10360.top == UIImageView:0x7fad5ed0fd90.top
(active)>",
"<NSLayoutConstraint:0x600001a312c0 'UISV-canvas-connection' V:[UIImageView:0x7fad5ed0fd90]-(0)-| (active, names:
'|':UIStackView:0x7fad5ed10360 )>",
"<NSLayoutConstraint:0x600001a314f0 'UIView-Encapsulated-Layout-Height'
Music.MusicsCell:0x7fad5ed0ec20'cellId'.height == 90.5 (active)>" )
Will attempt to recover by breaking constraint
but i've tried many things, like simple things, just image with background and size, example:
let imageView: UIImageView = UIImageView()
imageView.size(size: CGSize(width: 40, height: 40))
imageView.backgroundColor = .purple
imageView.layer.cornerRadius = 6
imageView.clipsToBounds = true
and I don't know what I have to disappear this warning message.
Follow the code below:
MusicsVC (UITableViewController):
class MusicsVC: UITableViewController {
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad();
tableView.register(MusicsCell.self, forCellReuseIdentifier: cellId)
}
}
extension MusicsVC {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5;
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! MusicsCell
return cell;
}
}
UITableViewCell:
class MusicsCell: UITableViewCell {
let tituloLabel: UILabel = .textLabel(text: "Teste", fontSize: 16);
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
let imageView: UIImageView = UIImageView()
imageView.size(size: CGSize(width: 60, height: 60))
imageView.layer.cornerRadius = 6
imageView.clipsToBounds = true
imageView.load(url: "https://studiosol-a.akamaihd.net/uploadfile/letras/fotos/d/7/4/2/d74238794c3024afbcba997e444fd2cb.jpg")
let stackView = UIStackView(arrangedSubviews: [
imageView,
tituloLabel
])
stackView.spacing = 12
addSubview(stackView)
stackView.inset(view: stackView, insets: UIEdgeInsets(top: 6, left: 12, bottom: 6, right: 12))
}
required init?(coder: NSCoder) {
fatalError()
}
}
I suppose do you want this, but your code is a little bit confused to know what you really you want... Declare your table view, and add constraints :
class ViewController: UIViewController {
let tableView = UITableView()
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
tableView.delegate = self
tableView.dataSource = self
tableView.register(MusicsCell.self, forCellReuseIdentifier: cellId)
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
tableView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
}
now delegate and datasource
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 72 // 60 image view height + 6x2 padding from top and bottom = 72
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! MusicsCell
return cell;
}
}
and this how your cell looks like:
class MusicsCell: UITableViewCell {
let tituloLabel: UILabel = {
let label = UILabel()
label.text = "Testo"
label.font = .systemFont(ofSize: 16, weight: .regular)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let myImageView: UIImageView = {
let iv = UIImageView()
iv.contentMode = .scaleAspectFill
iv.layer.cornerRadius = 6
iv.clipsToBounds = true
iv.translatesAutoresizingMaskIntoConstraints = false
return iv
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
guard let url = URL(string: "https://studiosol-a.akamaihd.net/uploadfile/letras/fotos/d/7/4/2/d74238794c3024afbcba997e444fd2cb.jpg") else {return}
myImageView.downloadImage(from: url) // this is my extension to load image from url, you can use yours
contentView.addSubview(myImageView)
myImageView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 6).isActive = true
myImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 12).isActive = true
myImageView.widthAnchor.constraint(equalToConstant: 60).isActive = true
myImageView.heightAnchor.constraint(equalToConstant: 60).isActive = true
contentView.addSubview(tituloLabel)
tituloLabel.topAnchor.constraint(equalTo: myImageView.topAnchor).isActive = true
tituloLabel.leadingAnchor.constraint(equalTo: myImageView.trailingAnchor, constant: 10).isActive = true
tituloLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
tituloLabel.bottomAnchor.constraint(equalTo: myImageView.bottomAnchor).isActive = true
}
required init?(coder: NSCoder) {
fatalError()
}
}
and this is the result

How to pressed a UIButton within a customized TableviewCell?

I have been looking all throughout SO on how to interact with a UIButton within a customized tableview cell. All of the answers I have seen are using IBOutlets, however I have not seen a way to do this fully programmatically. I am use to interacting with buttons via button.addTarget. Here are my two ViewControllers, one being the customized tableviewcell and the other being the ViewController.
Here is my customized. I tried using a protocol delegate route, however this has failed.
import UIKit
#objc protocol TableViewNew {
func onClickCell()
}
class NewMoveTableViewCell: UITableViewCell {
var cellDelegate: TableViewNew?
static let identifier = "NewTableViewCell"
private let myImageView: UIImageView = {
let imageView = UIImageView()
imageView.clipsToBounds = true
imageView.contentMode = .scaleAspectFill
imageView.layer.masksToBounds = true
imageView.backgroundColor = .purple
imageView.layer.cornerRadius = 80/2
return imageView
}()
private let myLabel : UILabel = {
let label = UILabel()
label.text = "test"
label.backgroundColor = .blue
label.textColor = .systemPink
label.adjustsFontSizeToFitWidth = true
label.textAlignment = .center
return label
}()
private let button: UIButton = {
let button = UIButton()
button.setTitle("Invite", for: .normal)
button.backgroundColor = .systemPink
button.layer.cornerRadius = 10
return button
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
addSubview(myImageView)
addSubview(myLabel)
addSubview(button)
setImageConstratins()
setTitleLabelConstraints()
setButton()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setImageConstratins() {
myImageView.translatesAutoresizingMaskIntoConstraints = false
myImageView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
myImageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12).isActive = true
myImageView.heightAnchor.constraint(equalToConstant: 80).isActive = true
myImageView.widthAnchor.constraint(equalToConstant: 80).isActive = true
}
func setTitleLabelConstraints() {
myLabel.translatesAutoresizingMaskIntoConstraints = false
myLabel.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
myLabel.leadingAnchor.constraint(equalTo: myImageView.trailingAnchor, constant: 5).isActive = true
myLabel.heightAnchor.constraint(equalToConstant: 80).isActive = true
//myLabel.trailingAnchor.constraint(equalTo: button.leadingAnchor, constant: -12).isActive = true
}
func setButton() {
button.translatesAutoresizingMaskIntoConstraints = false
button.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
button.leadingAnchor.constraint(equalTo: myLabel.trailingAnchor, constant: -5).isActive = true
button.heightAnchor.constraint(equalToConstant: 80).isActive = true
button.widthAnchor.constraint(equalToConstant: 150).isActive = true
button.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -12).isActive = true
}
public func configure(with name: String, label: String) {
myLabel.text = label
myImageView.image = UIImage(named: name)
}
override func prepareForReuse() {
super.prepareForReuse()
myLabel.text = nil
myImageView.image = nil
}
#objc func didTapButton(_ sender: Any) {
cellDelegate?.onClickCell()
}
}
Secondly, here is the ViewController that the TableView is within.
import UIKit
class NewMoveViewController: UIViewController {
private let tableView: UITableView = {
let tableView = UITableView()
tableView.rowHeight = 100
return tableView
}()
private var collectionView: UICollectionView?
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(tableView)
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.itemSize = CGSize(width: 50, height: 50)
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView?.register(NewMoveCollectionViewCell.self, forCellWithReuseIdentifier: NewMoveCollectionViewCell.identifier)
collectionView?.showsHorizontalScrollIndicator = false
title = "Add to Group"
tableView.register(NewMoveTableViewCell.self, forCellReuseIdentifier: NewMoveTableViewCell.identifier)
tableView.delegate = self
tableView.dataSource = self
collectionView?.backgroundColor = .systemBlue
collectionView?.dataSource = self
collectionView?.delegate = self
guard let myCollection = collectionView else {
return
}
view.addSubview(myCollection)
// Do any additional setup after loading the view.
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
collectionView?.frame = CGRect(x: 0, y: 100, width: view.frame.size.width, height: 50)
tableView.frame = CGRect(x: 0, y: 200, width: view.frame.size.width, height: view.frame.size.height)
}
}
extension NewMoveViewController : UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: NewMoveTableViewCell.identifier, for: indexPath) as! NewMoveTableViewCell
cell.cellDelegate = self
cell.configure(with: "", label: "test")
return cell
}
func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
return false
}
}
extension NewMoveViewController : UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NewMoveCollectionViewCell.identifier, for: indexPath) as! NewMoveCollectionViewCell
return cell
}
}
extension NewMoveViewController : TableViewNew {
func onClickCell() {
print("Pressed")
}
I conformed this ViewController to the protocol from the customized cell and put the function within the cell's cellForRowAt function. When I use this route, I run my app and everything comes up fine, however when I try to click on the customized tableviewcell within the viewcontroller, nothing happens. Any help would be greatly appreciated.
you need to add the action handler to your button:
button.addTarget(self, action: #selector(didTapButton(sender:)), for: .touchUpInside)

How to add subview to stackview in table view cell?

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.

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

Resources