Updating auto layout after changing an object's frame size - swift

I am creating a view using auto layout and the result is this.
After the view loads, then I grab some text data and fill the UITextView's. The "About me" item can be multiple lines, so I resize that specific frame. Then, I get the following.
You see how the about me textview is covering the next field? How can I resize the auto layout with the new about me textview size? I searched and found some suggestions to use setNeedsLayout and layoutIfNeeded, but none worked.
I am setting up the auto layout like the following:
inputsContainerView.addSubview(ageInput)
ageInput.leftAnchor.constraint(equalTo: inputsContainerView.leftAnchor).isActive = true
ageInput.topAnchor.constraint(equalTo: inputsContainerView.topAnchor, constant: 10).isActive = true
ageInput.widthAnchor.constraint(equalTo: inputsContainerView.widthAnchor).isActive = true
ageInput.heightAnchor.constraint(equalToConstant: 60).isActive = true
inputsContainerView.addSubview(genderInput)
genderInput.leftAnchor.constraint(equalTo: inputsContainerView.leftAnchor).isActive = true
genderInput.topAnchor.constraint(equalTo: ageInput.bottomAnchor).isActive = true
genderInput.widthAnchor.constraint(equalTo: inputsContainerView.widthAnchor).isActive = true
genderInput.heightAnchor.constraint(equalToConstant: 60).isActive = true
inputsContainerView.addSubview(aboutInput)
aboutInput.leftAnchor.constraint(equalTo: inputsContainerView.leftAnchor).isActive = true
aboutInput.topAnchor.constraint(equalTo: genderInput.bottomAnchor).isActive = true
aboutInput.widthAnchor.constraint(equalTo: inputsContainerView.widthAnchor).isActive = true
aboutInput.heightAnchor.constraint(equalToConstant: 60).isActive = true
inputsContainerView.addSubview(memberSinceInput)
memberSinceInput.leftAnchor.constraint(equalTo: inputsContainerView.leftAnchor).isActive = true
memberSinceInput.topAnchor.constraint(equalTo: aboutInput.bottomAnchor).isActive = true
memberSinceInput.widthAnchor.constraint(equalTo: inputsContainerView.widthAnchor).isActive = true
memberSinceInput.heightAnchor.constraint(equalToConstant: 60).isActive = true
After the view loads, I fetch data and resize the about me textview's frame using the following function:
func resizeTextView(_ textView: UITextView) {
let fixedWidth = textView.frame.size.width
textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
let newSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
var newFrame = textView.frame
newFrame.size = CGSize(width: max(newSize.width, fixedWidth), height: newSize.height)
textView.frame = newFrame
}

If I were you, I'd use UITableView to create this form and add the label and UITextView inside the cells.
You could do a UITableViewCell like below where I set the height of label to 60 and auto layout it. UITextView also using the auto layout and fitting the cell's bottom.
import UIKit
class UserDetailCell: UITableViewCell {
var userDetailLabel : UILabel = {
var label = UILabel()
label.numberOfLines = 0
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor.lightGray
return label
}()
var userDetailTextView : UITextView = {
var tv = UITextView()
tv.translatesAutoresizingMaskIntoConstraints = false
tv.isScrollEnabled = false
return tv
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupUI()
}
func setupUI(){
addSubview(userDetailLabel)
NSLayoutConstraint.activate([
userDetailLabel.topAnchor.constraint(equalTo: topAnchor),
userDetailLabel.leftAnchor.constraint(equalTo: leftAnchor, constant: 8),
userDetailLabel.rightAnchor.constraint(equalTo: rightAnchor),
userDetailLabel.heightAnchor.constraint(equalToConstant: 60)
])
addSubview(userDetailTextView)
NSLayoutConstraint.activate([
userDetailTextView.topAnchor.constraint(equalTo: userDetailLabel.bottomAnchor),
userDetailTextView.leftAnchor.constraint(equalTo: leftAnchor, constant: 8),
userDetailTextView.rightAnchor.constraint(equalTo: rightAnchor),
userDetailTextView.bottomAnchor.constraint(equalTo: bottomAnchor)
])
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Then your UIViewController should be like below. I'm setting a delegate to UITextView inside the cellForRowAt indexPath method. Since I set the delegate, the textViewDidChange delegate method will be called. It is written inside an extension.
import UIKit
class UserDetailsController: UITableViewController {
let cellId = "cell"
var person = Person(myAge: 20, myGender: "Male", aboutMe: "Hello my name is jake waisee. What is your name? goayngeHello my name is jake waisee. What is your name? goayngeHello my name is jake waisee. What is your name? goayngeHello my name is jake waisee. What is your name? goaynge")
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UserDetailCell.self, forCellReuseIdentifier: cellId)
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 100
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 3
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! UserDetailCell
cell.userDetailTextView.delegate = self
cell.userDetailTextView.tag = indexPath.row
if indexPath.row == 0{
cell.userDetailLabel.text = "Age"
cell.userDetailTextView.text = "\(person.age)"
}else if indexPath.row == 1{
cell.userDetailLabel.text = "Gender"
cell.userDetailTextView.text = person.gender
}else if indexPath.row == 2{
cell.userDetailLabel.text = "About me"
cell.userDetailTextView.text = person.aboutMe
}
return cell
}
}
extension UserDetailsController: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
print(textView.text)
if textView.tag == 2 {
person.aboutMe = textView.text
}else if textView.tag == 0 {
person.age = Int(textView.text) ?? 0
}else if textView.tag == 1 {
person.gender = textView.text
}
//this will keep the textview growing as we type
tableView.beginUpdates()
tableView.endUpdates()
}
}
Hope this helps you out. Your UI should look something like below.

Related

Inside custom tableView cell: UILabel's background is beyond the text length (programmatically)

I create a tableView with custom cell, which contains two labels programmatically. And I cannot get left label's text aligned with its background. May need some help from your guys.
In order to narrow down the problem then I create a small project to do a few experiments:
Inside ViewController:
If with tableView.rowHeight = 40, get below result, which is not what I want. The left label's background is beyond the text length.
If comment out line tableView.rowHeight = 40, get below result, which is what I want but with a warning in console.
"[Warning] Warning once only: Detected a case where constraints ambiguously suggest a height of zero for a table view cell's content view. We're considering the collapse unintentional and using standard height instead."
Also try to use below statement, it gets the same display on screen with scenario 2. However, it has the same warning there as scenario 2.
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 40
ViewController
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var tableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
let uiView = UIView()
uiView.backgroundColor = .systemBackground
view = uiView
tableView.delegate = self
tableView.dataSource = self
tableView.register(CustomCell.self, forCellReuseIdentifier: "countryDetail")
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.rowHeight = 40
view.addSubview(tableView)
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
tableView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
])
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 8
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "countryDetail", for: indexPath) as? CustomCell else {
fatalError("Unable to dequeue CustomCell")
}
cell.name.text = "Country: "
cell.value.text = "US"
return cell
}
}
CustomCell
import UIKit
class CustomCell: UITableViewCell {
var name = UILabel()
var value = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: "countryDetail")
name.translatesAutoresizingMaskIntoConstraints = false
name.numberOfLines = 0
name.textAlignment = .left
name.layer.masksToBounds = true
name.layer.cornerRadius = 5
name.backgroundColor = .systemGreen
name.font = UIFont(name: "Helvetica Neue", size: 22)
contentView.addSubview(name)
value.translatesAutoresizingMaskIntoConstraints = false
value.numberOfLines = 0
value.textAlignment = .left
value.font = UIFont(name: "Helvetica Neue", size: 18)
contentView.addSubview(value)
NSLayoutConstraint.activate([
name.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10),
name.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
value.leadingAnchor.constraint(equalTo: name.trailingAnchor, constant: 10),
value.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
value.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10)
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Oh, it is a silly mistake. I should remove this line value.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10) from CustomCell to remove right label's trailing anchor constraint as I want the right label left aligned to the left one.
And this time using tableView.rowHeight = 40, the app works fine.

How to expand a UITableView inside a StackView?

I have programatically created a Stackview along with a UITableView inside it.
func setupStack() {
view.addSubview(stackView)
stackView.axis = .vertical
stackView.distribution = .fillEqually
stackView.spacing = 8
}
func fillStackView() {
yearTable.isHidden = true
yearTable.layer.cornerRadius = 10
allViews.append(yearTable) // adding the view to an array of views
// other views also created and added to stack
}
My problem is that when I fade in the tableView, it takes up as much space as the other views in the table which all have a height of 40. The tableView has a height of 150 but it never expands to that height. How do I get it to expand?
UITableView does not have an intrinsic height/width so when placed in a UIStackView without any height/width information, it will be provided a default size. i.e. if your tallest view in the stack is 60 in height, then the tableView will also be 60.
You could alter this by giving yearTable a height constraint before adding it to the UIStackView, like so:
func fillStackView() {
//...
yearTable.translatesAutoresizingMaskIntoConstraints = false
yearTable.heightAnchor.constraint(equalToConstant: 150).isActive = true
allViews.append(yearTable)
//...
}
But do note that if you do this then because of stackView.distribution = .fillEqually, all your other views in the UIStackView will also become 150.
(EXTRA) Playground Example:
import UIKit
import PlaygroundSupport
class ViewController: UIViewController {
lazy var stackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .vertical
stackView.distribution = .fillEqually
stackView.spacing = 8
stackView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(stackView)
stackView.widthAnchor.constraint(equalToConstant: 200).isActive = true
return stackView
}()
let datasource = [1,2,3,4,5,6,7,8,9,10]
override func viewDidLoad() {
super.viewDidLoad()
appendLabel(text: "Hello, World!", color: .lightGray)
appendLabel(text: "Lorem\nipsum\ndolor\nsit", color: .gray)
appendTableView()
}
func appendLabel(text: String, color: UIColor) {
let label = UILabel()
label.backgroundColor = color
label.numberOfLines = 0
label.text = text
stackView.addArrangedSubview(label)
}
func appendTableView() {
let tableView = UITableView()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
tableView.dataSource = self
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.heightAnchor.constraint(equalToConstant: 150).isActive = true
stackView.addArrangedSubview(tableView)
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return datasource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let item = datasource[indexPath.row]
cell.textLabel?.text = String(item)
return cell
}
}
let vc = ViewController()
vc.view.frame = CGRect(x: 0, y: 0, width: 320, height: 480)
PlaygroundPage.current.setLiveView(vc.view)
Comment tableView.heightAnchor.constraint(equalToConstant: 150).isActive = true to see the difference

Multiple UITableViewCell problem in displaying

I'm new in autolayout programmatically, and I have a problem with displaying 2 different cells in UITableView as below:
The first cell I want to show profiles picture and the username, then the menu options. But as screenshot the second section menu option is displaying behind the first section (user profile picture and username).
How to solve this problem?
UserTableViewCell:
class UserTableViewCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
layoutUI()
backgroundColor = .CustomGreen()
selectionStyle = .none
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
lazy var containerView: UIView = {
let containerView = UIView()
containerView.backgroundColor = .clear
containerView.translatesAutoresizingMaskIntoConstraints = false
return containerView
}()
lazy var userPhoto: UIImageView = {
let userPhoto = UIImageView()
userPhoto.translatesAutoresizingMaskIntoConstraints = false
userPhoto.layer.cornerRadius = userPhoto.frame.size.width / 2
userPhoto.clipsToBounds = true
return userPhoto
}()
lazy var username: UILabel = {
let username = UILabel()
username.textColor = .white
username.text = "Ahmed Abd Elaziz"
username.translatesAutoresizingMaskIntoConstraints = false
return username
}()
func setupContainerViewConstraints() {
NSLayoutConstraint.activate([
containerView.topAnchor.constraint(equalTo: topAnchor),
containerView.bottomAnchor.constraint(equalTo: bottomAnchor),
containerView.leadingAnchor.constraint(equalTo: leadingAnchor),
containerView.trailingAnchor.constraint(equalTo: trailingAnchor)
])
}
func setupUserPhotoConstraints() {
NSLayoutConstraint.activate([
userPhoto.centerXAnchor.constraint(equalTo: containerView.centerXAnchor),
userPhoto.widthAnchor.constraint(equalToConstant: frame.width / 3),
userPhoto.heightAnchor.constraint(equalToConstant: frame.width / 3)
])
}
func setupUsernameConstraints() {
NSLayoutConstraint.activate([
username.topAnchor.constraint(lessThanOrEqualTo: userPhoto.bottomAnchor, constant: 16),
username.centerXAnchor.constraint(equalTo: containerView.centerXAnchor),
])
}
func addSubviews() {
addSubview(containerView)
containerView.addSubview(userPhoto)
containerView.addSubview(username)
}
func layoutUI() {
addSubviews()
setupContainerViewConstraints()
setupUserPhotoConstraints()
setupUsernameConstraints()
}
}
SideMenuOptionTableViewCell:
class SideMenuOptionTableViewCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
layoutUI()
backgroundColor = .CustomGreen()
selectionStyle = .none
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
lazy var containerView: UIView = {
let containerView = UIView()
containerView.backgroundColor = .clear
containerView.translatesAutoresizingMaskIntoConstraints = false
return containerView
}()
lazy var viewTitle: UILabel = {
let viewTitle = UILabel()
viewTitle.font = UIFont(name: "AvenirNext-Regular", size: 20)
viewTitle.textColor = .white
viewTitle.numberOfLines = 0
viewTitle.translatesAutoresizingMaskIntoConstraints = false
return viewTitle
}()
func setupContainerViewConstraints() {
NSLayoutConstraint.activate([
containerView.topAnchor.constraint(equalTo: topAnchor),
containerView.bottomAnchor.constraint(equalTo: bottomAnchor),
containerView.leadingAnchor.constraint(equalTo: leadingAnchor),
containerView.trailingAnchor.constraint(equalTo: trailingAnchor)
])
}
func setupFoodTitle() {
NSLayoutConstraint.activate([
viewTitle.centerYAnchor.constraint(equalTo: containerView.centerYAnchor),
viewTitle.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 16)
])
}
func addSubview() {
addSubview(containerView)
containerView.addSubview(viewTitle)
}
func layoutUI() {
addSubview()
setupContainerViewConstraints()
setupFoodTitle()
}
}
SideMenuTableViewController:
class SideMenuTableViewController: UITableViewController {
let viewControllers = ["Controller One", "Logout"]
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UserTableViewCell.self, forCellReuseIdentifier: "UserTableViewCell")
tableView.register(SideMenuOptionTableViewCell.self, forCellReuseIdentifier: "SideMenuOptionTableViewCell")
tableView.reloadData()
tableView.backgroundColor = .CustomGreen()
tableView.separatorStyle = .none
tableView.rowHeight = UITableView.automaticDimension
// tableView.estimatedRowHeight = 100
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return section == 0 ? 1 : viewControllers.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "UserTableViewCell", for: indexPath) as! UserTableViewCell
cell.userPhoto.image = UIImage(named: "ahmed")
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "SideMenuOptionTableViewCell", for: indexPath) as! SideMenuOptionTableViewCell
cell.viewTitle.text = viewControllers[indexPath.row]
return cell
}
}
}
The problem here is you are adding constraints according to the cells height and all the views are being adjusted in the cell. As the space for the views is too small they are overlapping the views of the next cell. You can fix this problem by increasing the size of the content view of the cell.
Add this line in init of your UserTableViewCell
let minHeight = 70
let minHeightConstraint = contentView.heightAnchor.constraint(greaterThanOrEqualToConstant: minHeight)
minHeightConstraint.priority = UILayoutPriority(rawValue: 999)
minHeightConstraint.isActive = true

How can I match Custom View's frame with TableViewCell frame properly?

I have a TableView which contains image stretched into cell and I added a view for darkening the image. Since I want to draw this view only in the beginning and not every time I scroll in table I added my codes into awakeFromNib in TableViewCell.
TableViewCell.swift
#IBOutlet weak var equipmentImageView: UIImageView!
let darkFilter = UIView()
override func awakeFromNib() {
super.awakeFromNib()
darkFilter.backgroundColor = .black
darkFilter.layer.opacity = 0.6
darkFilter.frame = self.equipmentImageView.frame
equipmentImageView.addSubview(darkFilter)
}
My problem is that this view will not have the same width with the ImageView, so the effect I'm trying to implement takes half of the screen.
I followed this solution but the problem is that this solution will only apply after the cells recreate themselves by scrolling down and up again.
TableViewCell.swift
override func layoutSubviews() {
super.layoutSubviews()
darkFilter.backgroundColor = .black
darkFilter.layer.opacity = 0.6
darkFilter.frame = self.equipmentImageView.frame
equipmentImageView.addSubview(darkFilter)
}
How can I apply this solution before the cells are created and will not redraw when scrolling through it?
You could set the cell's .background color to .black, and then lower the alpha of the equipmentImageView and you'd achieve the same darkened effect without having to add a new UIView.
EDIT
To illustrate my point: here's a really sloppy and quick example I drew up in Playground with an image titled "Untitled.jpg" to prove fading the image with a black cell background works the same as adding a faded black layer on top of a cell with an image:
Picture of result:
code:
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
class CustomTableViewCell: UITableViewCell {
let myImageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
if let sample = Bundle.main.path(forResource: "Untitled", ofType: "jpg") {
let image = UIImage(contentsOfFile: sample)
imageView.image = image
}
imageView.clipsToBounds = true
return imageView
}()
let blackView: UIView = {
let myView = UIView()
myView.translatesAutoresizingMaskIntoConstraints = false
myView.backgroundColor = .black
return myView
}()
let title: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = .white
label.font = UIFont.systemFont(ofSize: 12)
return label
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
backgroundColor = .black
let views = [myImageView,
blackView,
title]
views.forEach {
contentView.addSubview($0)
NSLayoutConstraint.activate([
$0.heightAnchor.constraint(equalTo: contentView.heightAnchor),
$0.widthAnchor.constraint(equalTo: contentView.widthAnchor),
$0.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
$0.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
])
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setCellViews(indexPath: IndexPath) {
blackView.alpha = 0
if indexPath.row % 2 == 0 {
if indexPath.row % 4 == 0 {
blackView.alpha = 0.4
title.text = "blackLayer added and faded"
} else {
backgroundColor = .black
myImageView.alpha = 0.6
title.text = "backView faded"
}
} else {
title.text = "not faded at all"
}
}
}
private let reuseId = "cellId"
class MyViewController : UIViewController {
let tableView: UITableView = {
let tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.separatorStyle = .none
return tableView
}()
let data = ["one", "two", "three", "four", "five"]
override func viewDidLoad() {
super.viewDidLoad()
setTableView()
}
func setTableView() {
tableView.dataSource = self
tableView.delegate = self
tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: reuseId)
view.addSubview(tableView)
NSLayoutConstraint.activate([
tableView.heightAnchor.constraint(equalTo: view.heightAnchor),
tableView.widthAnchor.constraint(equalTo: view.widthAnchor),
tableView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
tableView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
])
}
}
extension MyViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: reuseId, for: indexPath) as! CustomTableViewCell
cell.setCellViews(indexPath: indexPath)
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
2 suggestions:
1) Since you are dealing with IBOutlets for the cell, you could also add the view on the IB on top of the image view, and then adding constrains to it to match up the image view size.
2) If you are aiming to adding it programmatically, you might need to setup its constraints based on the cell content view. For example:
darkFilter.backgroundColor = .black
darkFilter.layer.opacity = 0.6
contentView.addSubview(darkFilter)
darkFilter.translatesAutoresizingMaskIntoConstraints = false
darkFilter.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
darkFilter.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
darkFilter.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true
darkFilter.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true

Layout constraints for custom UITtableViewCell not working

I'm creating custom UITableViewCells with a UILabel in each but can't get the label to display anywhere else than the top left corner of the cell, looks like this.
Constraints don't seem to be applied yet they're being called (reaching breakpoint). I tried to replace the UILabel by an UIImageView and apply the same constraints but nothing appears (i.e. table view cells are blank).
What am I missing?
View for cells:
import UIKit
class myTableViewCell: UITableViewCell {
override init(style: UITableViewCellStyle, reuseIdentifier: String!) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
}
required init?(coder decoder: NSCoder) {
super.init(coder: decoder)
}
let label: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.systemFont(ofSize: 16, weight: UIFontWeightLight)
label.text = "Sample"
label.backgroundColor = UIColor.red
return label
}()
func setupViews () {
addSubview(label)
//add constraints
let marginsGuide = self.contentView.layoutMarginsGuide
label.leadingAnchor.constraint(equalTo: marginsGuide.leadingAnchor).isActive = true
label.trailingAnchor.constraint(equalTo: marginsGuide.trailingAnchor).isActive = true
label.topAnchor.constraint(equalTo: marginsGuide.topAnchor).isActive = true
label.bottomAnchor.constraint(equalTo: marginsGuide.bottomAnchor).isActive = true
}
}
View controller:
import UIKit
class myViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var myTableView: UITableView = UITableView()
var myArray = [Int]()
override func viewDidLoad() {
super.viewDidLoad()
//... do stuff incl. loading data into my myArray
let screenSize: CGRect = UIScreen.main.bounds
self.myTableView.frame = CGRect(x: 0, y: 0, width: screenSize.width, height: screenSize.height)
self.myTableView.delegate = self
self.myTableView.dataSource = self
self.myTableView.register(IngredientListTableViewCell.self, forCellReuseIdentifier: "cell")
self.view.addSubview(myTableView)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.myTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! myTableViewCell
return cell
}
}
Change this
addSubview(label)
to this
contentView.addSubview(label)