UITableViewCell's content view height is not adjusting for the dynamic font (NSLayoutConstraints) - ios

I am having difficulties adjusting table view cells to automatically resize for dynamic type fonts. Note that I am using layout constraints in code, not in storyboard.
Here is my table view setup code:
tableView = UITableView(frame: .zero, style: .grouped)
tableView.register(ChatAnalyticsCell.self, forCellReuseIdentifier: ChatAnalyticsCell.reuseIdentifier)
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = UITableView.automaticDimension
Here are my 2 labels and image view setup in ChatAnalyticsCell class
private var iconImageView: UIImageView = {
let imageView = UIImageView(frame: CGRect(origin: .zero,
size: CGSize(width: 20, height: 20)))
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.tintColor = UIColor(red: 0.25, green: 0.3, blue: 0.72, alpha: 1)
return imageView
}()
private var segmentLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textColor = UIColor.gray
label.font = UIFont.preferredFont(forTextStyle: .footnote)
label.adjustsFontForContentSizeCategory = true
return label
}()
private var segmentDataLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textColor = UIColor(red: 0.13, green: 0.15, blue: 0.19, alpha: 0.9)
label.font = UIFont.preferredFont(forTextStyle: .body)
label.adjustsFontForContentSizeCategory = true
return label
}()
Here is the function where I add subviews and setup constraints, to resize properly for the dynamic-type font.
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupSubviews()
}
private func setupSubviews() {
addSubview(iconImageView)
iconImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 15.0).isActive = true
iconImageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
iconImageView.widthAnchor.constraint(equalToConstant: 20.0).isActive = true
iconImageView.heightAnchor.constraint(equalToConstant: 20.0).isActive = true
// Analytics segment label
addSubview(segmentLabel)
segmentLabel.leadingAnchor.constraint(equalTo: iconImageView.trailingAnchor, constant: 20).isActive = true
segmentLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20).isActive = true
segmentLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20).isActive = true
// Segment data label
addSubview(segmentDataLabel)
segmentDataLabel.leadingAnchor.constraint(equalTo: segmentLabel.leadingAnchor).isActive = true
segmentDataLabel.trailingAnchor.constraint(equalTo: segmentLabel.trailingAnchor).isActive = true
segmentDataLabel.topAnchor.constraint(equalTo: segmentLabel.bottomAnchor, constant: 5).isActive = true
segmentDataLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 20).isActive = true
contentView.heightAnchor.constraint(greaterThanOrEqualToConstant: 80).isActive = true
}
Note that when I run the code, there are no constraints breaking, but the layout is not properly updated. Please see the screenshot:

Add all the views to the contentView!
contentView.addSubview(yourView)
One more thing - you might want to change(why?) this constraint,
segmentDataLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 20).isActive = true
to,
segmentDataLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20).isActive = true
The rest of the constraints look ok to calculate the height of the contentView.

Related

How do I arrange the Profile Image View to pin next to the Username at the top in the arranged Stack View?

So I have a stack view and the profile image needs to go next to the the username and stay there. How do I do that in this arranged stack view without conflicts because I have tried to anchor it to the top. Like this but no results:
Image of what I am trying to achieve
But currently it keeps doing this:
What is currently happening
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(profileImageView)
contentView.addSubview(profileNameLabel)
contentView.addSubview(userHandel)
profileImageView.setContentHuggingPriority(.defaultHigh, for: .horizontal)
let innerPostStackView = UIStackView(arrangedSubviews: [profileNameLabel, userHandel, postTextLabel])
innerPostStackView.axis = .vertical
let postStackView = UIStackView(arrangedSubviews: [profileImageView, innerPostStackView])
postStackView.translatesAutoresizingMaskIntoConstraints = false
postStackView.alignment = .center
postStackView.spacing = 10
contentView.addSubview(postStackView)
NSLayoutConstraint.activate([
postStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10),
postStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -15),
postStackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10),
postTextLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -15)
])
This is what I Have tried with the stack views. I cannot get it to work the way I want it to look.
This how your cell looks like:
class MyCell: UITableViewCell {
let profileNameLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 0
label.textColor = .black
label.backgroundColor = .clear
label.font = .systemFont(ofSize: 20, weight: .bold)
label.text = "Minions"
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let userHandel: UILabel = {
let label = UILabel()
label.numberOfLines = 0
label.textColor = .systemBlue
label.backgroundColor = .clear
label.font = .systemFont(ofSize: 14, weight: .semibold)
label.text = "#Minions"
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let postTextLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 0
label.textColor = .black
label.backgroundColor = .clear
label.text = "Every Mac comes with a one-year limited warranty(opens in a new window) and up to 90 days of complimentary technical support(opens in a new window). AppleCare+ for Mac extends your coverage from your AppleCare+ purchase date and adds unlimited incidents of accidental damage protection, each subject to a service fee of $99 for screen damage or external enclosure damage, or $299 for other accidental damage, plus applicable tax. In addition, you’ll get 24/7 priority access to Apple experts via chat or phone. For complete details, see the terms(opens in a new window)."
return label
}()
let costant: CGFloat = 60
let profileImageView: UIImageView = {
let iv = UIImageView()
iv.backgroundColor = .darkGray.withAlphaComponent(0.2)
iv.contentMode = .scaleAspectFill
iv.clipsToBounds = true
iv.translatesAutoresizingMaskIntoConstraints = false
return iv
}()
let containerView: UIView = {
let v = UIView()
v.backgroundColor = .clear
return v
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
backgroundColor = .white
let image = UIImage(named: "minions")?.withRenderingMode(.alwaysOriginal)
profileImageView.image = image
profileImageView.widthAnchor.constraint(equalToConstant: costant).isActive = true // set here profileImageView wudth
profileImageView.layer.cornerRadius = costant / 2
contentView.backgroundColor = .white
containerView.addSubview(profileNameLabel)
profileNameLabel.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
profileNameLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
profileNameLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
profileNameLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true
containerView.addSubview(userHandel)
userHandel.topAnchor.constraint(equalTo: profileNameLabel.bottomAnchor).isActive = true
userHandel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
userHandel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
userHandel.heightAnchor.constraint(equalToConstant: 20).isActive = true
let totalUpStack = UIStackView(arrangedSubviews: [profileImageView, containerView])
totalUpStack.axis = .horizontal
totalUpStack.spacing = 6
totalUpStack.distribution = .fill
totalUpStack.translatesAutoresizingMaskIntoConstraints = false
totalUpStack.heightAnchor.constraint(equalToConstant: costant).isActive = true
let completeStack = UIStackView(arrangedSubviews: [totalUpStack, postTextLabel])
completeStack.axis = .vertical
completeStack.spacing = 6
completeStack.distribution = .fill
completeStack.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(completeStack)
completeStack.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10).isActive = true
completeStack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10).isActive = true
completeStack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10).isActive = true
completeStack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
This is the result:
I'm not sure if it will help you. You can ignore UIStackView and use auto-layout like this:
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(profileImageView)
contentView.addSubview(profileNameLabel)
contentView.addSubview(userHandel)
contentView.addSubview(postTextLabel)
// activate autolayout constraints:
NSLayoutConstraint.activate([
// profileImageView:
profileImageView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 16),
profileImageView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 16),
profileImageView.heightAnchor.constraint(equalToConstant: 52),
profileImageView.widthAnchor.constraint(equalToConstant: 52),
// profileNameLabel:
profileNameLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 16),
profileNameLabel.leftAnchor.constraint(equalTo: profileImageView.rightAnchor, constant: 8),
profileNameLabel.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -16),
// userHandel:
userHandel.topAnchor.constraint(equalTo: profileNameLabel.bottomAnchor, constant: 8),
userHandel.leftAnchor.constraint(equalTo: profileNameLabel.leftAnchor),
userHandel.rightAnchor.constraint(equalTo: profileNameLabel.rightAnchor),
// postTextLabel:
postTextLabel.topAnchor.constraint(equalTo: userHandel.bottomAnchor, constant: 8),
postTextLabel.leftAnchor.constraint(equalTo: userHandel.leftAnchor),
postTextLabel.rightAnchor.constraint(equalTo: userHandel.rightAnchor),
postTextLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -16)
])
}

Programmatically set constraints for uitableviewcell with uilabel and uiimageview

I am trying to build my tableview with custom UITableViewCell but facing some issues in coinstraints. Following is my custom UITablviewCell class:
class MovieCell: UITableViewCell {
static let identifier = "MovieCell"
let cellMargin = 15.0
private lazy var containerView: UIView = {
let view = UIView()
view.backgroundColor = .lightGray
view.layer.cornerRadius = 5
view.translatesAutoresizingMaskIntoConstraints = false
view.clipsToBounds = true
return view
}()
private let movieNameLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.movieAppBoldFont(size: 15)
label.numberOfLines = 0
return label
}()
private let movieImageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
return imageView
}()
var movieCellViewModel : MoviewCellViewModel = MoviewCellViewModel(image: nil, name: "") {
didSet {
movieNameLabel.text = movieCellViewModel.name
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
backgroundColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0)
layoutSubView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func layoutSubView() {
addSubview(containerView)
containerView.addSubview(movieNameLabel)
containerView.addSubview(movieImageView)
let marginGuide = containerView.layoutMarginsGuide
NSLayoutConstraint.activate([
containerView.topAnchor.constraint(equalTo: contentView.layoutMarginsGuide.topAnchor),
containerView.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor),
containerView.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor),
containerView.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor, constant: -cellMargin),
movieImageView.leadingAnchor.constraint(equalTo: marginGuide.leadingAnchor, constant: 5),
movieImageView.topAnchor.constraint(equalTo: marginGuide.topAnchor, constant: 5),
movieImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -5),
movieImageView.widthAnchor.constraint(equalToConstant: 50),
movieImageView.heightAnchor.constraint(equalToConstant: 50),
movieNameLabel.leadingAnchor.constraint(equalTo: marginGuide.trailingAnchor, constant: 5),
movieNameLabel.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor, constant: -5),
movieNameLabel.topAnchor.constraint(equalTo: marginGuide.topAnchor, constant: 5),
movieNameLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor, constant: -5)
])
}
}
I get the following result with the above code:
I am trying to create a UIView and add content to it. I am adding it because I want to show some empty space as a separator in tableview.
Couple issues...
Your first line in layoutSubView() is:
addSubview(containerView)
where is needs to be:
contentView.addSubview(containerView)
Second, you have movieImageView constrained to contentView:
movieImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -5),
where it should be constrained to marginGuide:
movieImageView.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor, constant: -5),
Third, you have movieNameLabel.leadingAnchor constrained to marginGuide.trailingAnchor:
movieNameLabel.leadingAnchor.constraint(equalTo: marginGuide.trailingAnchor, constant: 5),
where it should be constrained to movieImageView.trailingAnchor:
movieNameLabel.leadingAnchor.constraint(equalTo: movieImageView.trailingAnchor, constant: 5),
Making those changes will give you this (I set image view background to blue, and label background to cyan):
However, when you run the app, you'll see lots of Unable to simultaneously satisfy constraints. messages. This is common when using subviews with subviews in cells.
To get rid of the auto-layout complaints, we can give the containerView bottom anchor a less-than-required priority:
let bottomC = containerView.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor, constant: -cellMargin)
bottomC.priority = .required - 1
and then activate that in your constraints block:
//containerView.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor, constant: -cellMargin),
bottomC,
Here's your complete MovieCell class with those changes:
class MovieCell: UITableViewCell {
static let identifier = "MovieCell"
let cellMargin = 15.0
private lazy var containerView: UIView = {
let view = UIView()
view.backgroundColor = .lightGray
view.layer.cornerRadius = 5
view.translatesAutoresizingMaskIntoConstraints = false
view.clipsToBounds = true
return view
}()
private let movieNameLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = .systemFont(ofSize: 15, weight: .bold) // UIFont.movieAppBoldFont(size: 15)
label.numberOfLines = 0
return label
}()
private let movieImageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
return imageView
}()
var movieCellViewModel : MoviewCellViewModel = MoviewCellViewModel(image: nil, name: "") {
didSet {
movieNameLabel.text = movieCellViewModel.name
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
backgroundColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0)
layoutSubView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func layoutSubView() {
contentView.addSubview(containerView)
containerView.addSubview(movieNameLabel)
containerView.addSubview(movieImageView)
let marginGuide = containerView.layoutMarginsGuide
let bottomC = containerView.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor, constant: -cellMargin)
bottomC.priority = .required - 1
NSLayoutConstraint.activate([
containerView.topAnchor.constraint(equalTo: contentView.layoutMarginsGuide.topAnchor),
containerView.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor),
containerView.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor),
// containerView.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor, constant: -cellMargin),
bottomC,
movieImageView.leadingAnchor.constraint(equalTo: marginGuide.leadingAnchor, constant: 5),
movieImageView.topAnchor.constraint(equalTo: marginGuide.topAnchor, constant: 5),
// movieImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -5),
movieImageView.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor, constant: -5),
movieImageView.widthAnchor.constraint(equalToConstant: 50),
movieImageView.heightAnchor.constraint(equalToConstant: 50),
// movieNameLabel.leadingAnchor.constraint(equalTo: marginGuide.trailingAnchor, constant: 5),
movieNameLabel.leadingAnchor.constraint(equalTo: movieImageView.trailingAnchor, constant: 5),
movieNameLabel.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor, constant: -5),
movieNameLabel.topAnchor.constraint(equalTo: marginGuide.topAnchor, constant: 5),
movieNameLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor, constant: -5)
])
// during dev, so we can easily see the frames
movieImageView.backgroundColor = .systemBlue
movieNameLabel.backgroundColor = .cyan
}
}

How to create a UITextView programatically, inside UIView that is added programatically too?

I am trying to make a text acceptor view like Instagram and Snapchat.
I am trying to add a UITextview (programatically) inside a UIView thats also added programatically. Below is the code I am trying to use to add the UITextView, but it is not working when added with UIView code. Please help.
What I am trying to achieve is:
import UIKit
class TextViewViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let View1: UIView = {
let viewView = UIView()
viewView.translatesAutoresizingMaskIntoConstraints = false
viewView.contentMode = .scaleAspectFit
viewView.backgroundColor = .red
viewView.clipsToBounds = true
return viewView
}()
self.view.addSubview(View1)
View1.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true
View1.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
View1.topAnchor.constraint(equalTo: view.topAnchor, constant: 250).isActive = true
View1.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -250).isActive = true
let textView = UITextView(frame: CGRect(x: 20.0, y: 90.0, width: 250.0, height: 100.0))
textView.contentInsetAdjustmentBehavior = .automatic
textView.center = self.view.center
textView.textAlignment = NSTextAlignment.justified
textView.backgroundColor = UIColor.lightGray
// Use RGB colour
textView.backgroundColor = UIColor(red: 39/255, green: 53/255, blue: 182/255, alpha: 1)
// Update UITextView font size and colour
textView.font = UIFont.systemFont(ofSize: 20)
textView.textColor = UIColor.white
textView.font = UIFont(name: "Verdana", size: 17)
// Make UITextView web links clickable
textView.isSelectable = true
textView.isEditable = false
textView.dataDetectorTypes = UIDataDetectorTypes.link
// Make UITextView corners rounded
textView.layer.cornerRadius = 10
// Make UITextView Editable
textView.isEditable = true
View1.addSubview(textView)
//
// let textView: UITextView = {
// let tv = UITextView(frame: CGRect(x: 20.0, y: 90.0, width: 100.0, height: 50.0))
// tv.contentInsetAdjustmentBehavior = .automatic
// tv.center = self.view.center
// tv.textAlignment = NSTextAlignment.justified
// tv.textColor = UIColor.blue
// tv.backgroundColor = UIColor.lightGray
// return tv
// }()
//
// View1.addSubview(textView)
// textView.topAnchor.constraint(equalTo: View1.topAnchor, constant: 20).isActive = true
// textView.bottomAnchor.constraint(equalTo: View1.bottomAnchor, constant: 20).isActive = true
// textView.leadingAnchor.constraint(equalTo: View1.leadingAnchor, constant: 0).isActive = true
// textView.trailingAnchor.constraint(equalTo: View1.trailingAnchor, constant: 0).isActive = true
}
}
The issue is in the next line of code
textView.center = self.view.center
Get rid of it and you will see the textView in the view hierarchy

UIKit UIImageView with Offset UIImage

I'm currently adding a UIImageView to a Table Cell, and when the cell is populated with data, I'm adding a UIImage (if it exists) to the UIImageView, however, the UIImage is offset like this:
I'm using AutoLayout to setup the Cell, as follows:
import Foundation
import UIKit
class CatalogItemTableCell: UITableViewCell {
let itemImageView: UIImageView = {
let imgView = UIImageView()
imgView.translatesAutoresizingMaskIntoConstraints = false
imgView.contentMode = .scaleAspectFit
imgView.clipsToBounds = true
imgView.layer.borderColor = UIColor(named: "FontColor")?.cgColor
imgView.layer.borderWidth = 1.0
imgView.layer.cornerRadius = 4.0
imgView.layer.masksToBounds = true
return imgView
}()
let nameLabel: UILabel = {
let label = UILabel()
label.lineBreakMode = .byTruncatingTail
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.boldSystemFont(ofSize: 18)
return label
}()
let skuLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let descriptionLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.systemFont(ofSize: 10)
return label
}()
let qtyOnHand: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let priceLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let containerView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier);
containerView.addSubview(itemImageView)
containerView.addSubview(nameLabel)
containerView.addSubview(skuLabel)
containerView.addSubview(descriptionLabel)
containerView.addSubview(qtyOnHand)
containerView.addSubview(priceLabel)
self.contentView.addSubview(containerView)
NSLayoutConstraint.activate(
[
containerView.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 10.0),
containerView.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 10.0),
containerView.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: -10.0),
containerView.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -10.0),
]);
NSLayoutConstraint.activate(
[
itemImageView.topAnchor.constraint(equalTo: containerView.topAnchor),
itemImageView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
itemImageView.trailingAnchor.constraint(equalTo: nameLabel.leadingAnchor, constant: -10),
itemImageView.widthAnchor.constraint(equalToConstant: 80),
itemImageView.heightAnchor.constraint(equalToConstant: 80)
]);
NSLayoutConstraint.activate([
nameLabel.topAnchor.constraint(equalTo: containerView.topAnchor),
nameLabel.leadingAnchor.constraint(equalTo: itemImageView.trailingAnchor, constant: 10.0),
nameLabel.bottomAnchor.constraint(equalTo: skuLabel.topAnchor, constant: -10)
]);
NSLayoutConstraint.activate([
skuLabel.topAnchor.constraint(equalTo: nameLabel.bottomAnchor, constant: 10.0),
skuLabel.leadingAnchor.constraint(equalTo: nameLabel.leadingAnchor),
skuLabel.bottomAnchor.constraint(equalTo: descriptionLabel.topAnchor, constant: -10)
]);
NSLayoutConstraint.activate([
descriptionLabel.topAnchor.constraint(equalTo: skuLabel.bottomAnchor, constant: -10.0),
descriptionLabel.leadingAnchor.constraint(equalTo: skuLabel.leadingAnchor),
descriptionLabel.bottomAnchor.constraint(equalTo: qtyOnHand.topAnchor, constant: -10)
]);
NSLayoutConstraint.activate([
qtyOnHand.topAnchor.constraint(equalTo: descriptionLabel.bottomAnchor, constant: 10.0),
qtyOnHand.leadingAnchor.constraint(equalTo: descriptionLabel.leadingAnchor),
qtyOnHand.bottomAnchor.constraint(equalTo: containerView.bottomAnchor)
]);
NSLayoutConstraint.activate([
priceLabel.topAnchor.constraint(equalTo: containerView.topAnchor),
priceLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
]);
}
required init?(coder: NSCoder) {
super.init(coder: coder);
}
}
I'm also setting up the image using Alamoimagefire to scale it correctly, as I couldn't get scaling to work right by just adding the image to the UIImageView
let image = UIImage(contentsOfFile: fileUrl.path)
let size = CGSize(width: 80, height: 80)
let scaledImage = image!.af.imageAspectScaled(toFit: size)
cell.imageView?.image = scaledImage
Any help appreciated to help me understand what would cause this.
First, you shouldn't need to scale the image - using .contentMode = .scaleAspectFit on your image view should work properly.
The problem you are hitting, though, is you're setting the image on the wrong image view...
cell.imageView?.image = scaledImage
should be:
cell.itemImageView.image = scaledImage
but, again, no need to scale it first.

Vertical ScrollView not scrolling (No Storyboard)

I need help creating a Scroll View without Storyboards. Here is my code for setting up the Scroll View; I'm not setting a contentSize of the Scroll View because I'd like the scroll view content size to be dynamic, dependent on the amount of text in the TextView. What I did instead, is I tried adding a 'contentView' to the Scroll View and added all my UI elements into the contentView. Any help would be appreciated.
import Foundation
import UIKit
import UITextView_Placeholder
class ComposerVC: UIViewController {
private var scrollView: UIScrollView = {
let scrollView = UIScrollView(frame: UIScreen.main.bounds)
scrollView.translatesAutoresizingMaskIntoConstraints = false
return scrollView
}()
private var contentView: UIView = {
let content = UIView()
content.translatesAutoresizingMaskIntoConstraints = false
return content
}()
private var title: UITextView = {
let title = UITextView()
title.translatesAutoresizingMaskIntoConstraints = false
title.placeholder = "Untitled"
title.textColor = UIColor(hexString: "#50E3C2")
title.font = UIFont(name: "Rubik-BoldItalic", size: 32)
title.backgroundColor = .clear
title.isScrollEnabled = false
return title
}()
private var divider: UIView = {
let divider = UIView()
divider.translatesAutoresizingMaskIntoConstraints = false
divider.backgroundColor = UIColor(hexString: "#50E3C2")
return divider
}()
private var content: UITextView = {
let title = UITextView()
title.translatesAutoresizingMaskIntoConstraints = false
title.placeholder = "Begin writing here..."
title.textColor = .white
title.font = UIFont(name: "Avenir-Book", size: 15)
title.backgroundColor = .clear
title.isScrollEnabled = false
return title
}()
override func viewDidLoad() {
setupUI()
setupUIConstraints()
title.delegate = self
}
private func setupUI() {
view.backgroundColor = UIColor(hexString: "#131415")
view.addSubview(scrollView)
scrollView.addSubview(contentView)
contentView.addSubview(title)
contentView.addSubview(divider)
contentView.addSubview(content)
}
private func setupUIConstraints() {
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
contentView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
title.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 95).isActive = true
title.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 35).isActive = true
title.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -35).isActive = true
divider.topAnchor.constraint(equalTo: title.bottomAnchor, constant: 15).isActive = true
divider.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
divider.heightAnchor.constraint(equalToConstant: 1).isActive = true
divider.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.8).isActive = true
content.topAnchor.constraint(equalTo: divider.bottomAnchor, constant: 15).isActive = true
content.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 35).isActive = true
content.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -35).isActive = true
}
}
extension ComposerVC: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
let fixedWidth = textView.frame.size.width
let newSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
textView.frame.size = CGSize(width: max(newSize.width, fixedWidth), height: newSize.height)
}
}
A couple tips:
Don't use existing names for variables... with your code as-is, private var title: UITextView causes problems (title is already a view controller property).
Use var names that imply the object... e.g. titleTextView and contentTextView instead of title and content
During development - particularly when you're working on layout - give your UI elements contrasting background colors so you can easily see their frames at runtime.
When using code-created views, set .clipsToBounds = true ... if you don't see any subviews you've added, you know the frame / constraints are missing something.
I don't have your UITextView_Placeholder import, but that shouldn't affect anything here...
So, first, change your viewDidLoad() to this:
override func viewDidLoad() {
setupUI()
setupUIConstraints()
titleTextView.delegate = self
// contrasting colors during development
scrollView.backgroundColor = .red
titleTextView.backgroundColor = .yellow
contentTextView.backgroundColor = .green
divider.backgroundColor = .blue
contentView.backgroundColor = .cyan
}
When you run it, you should see (scroll view background is red, and this is without the placeholder stuff):
It looks correct, except there's no cyan-colored contentView.
Now, clip the subviews of contentView:
private var contentView: UIView = {
let content = UIView()
content.translatesAutoresizingMaskIntoConstraints = false
// add this line
content.clipsToBounds = true
return content
}()
The result:
Where did everything go? Well, we didn't see the cyan contentView and now we don't see any of its subviews ... If we use Debug View Hierarchy we can find out contentView has a Height of Zero.
Fix that by constraining the bottom of the second text view in setupUIConstraints() (I've renamed it to contentTextView instead of just content):
contentTextView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -95).isActive = true
and we get:
Now the Height of the cyan contentView is controlled by correctly setup constraints of its subviews.
As a side note: with constraints setup properly, and scrolling disabled for the text views, you do not need your:
extension ComposerVC: UITextViewDelegate {
//func textViewDidChange(_ textView: UITextView) {
//...
//}
}
The text view will automatically size itself to its text:
Here's the complete edited code:
class ComposerVC: UIViewController {
private var scrollView: UIScrollView = {
let scrollView = UIScrollView(frame: UIScreen.main.bounds)
scrollView.translatesAutoresizingMaskIntoConstraints = false
return scrollView
}()
private var contentView: UIView = {
let content = UIView()
content.translatesAutoresizingMaskIntoConstraints = false
// add this line so we know if the constraints are set correctly
content.clipsToBounds = true
return content
}()
private var titleTextView: UITextView = {
let title = UITextView()
title.translatesAutoresizingMaskIntoConstraints = false
// title.placeholder = "Untitled"
title.textColor = UIColor(hexString: "#50E3C2")
title.font = UIFont(name: "Rubik-BoldItalic", size: 32)
title.backgroundColor = .clear
title.isScrollEnabled = false
return title
}()
private var divider: UIView = {
let divider = UIView()
divider.translatesAutoresizingMaskIntoConstraints = false
divider.backgroundColor = UIColor(hexString: "#50E3C2")
return divider
}()
private var contentTextView: UITextView = {
let title = UITextView()
title.translatesAutoresizingMaskIntoConstraints = false
// title.placeholder = "Begin writing here..."
title.textColor = .white
title.font = UIFont(name: "Avenir-Book", size: 15)
title.backgroundColor = .clear
title.isScrollEnabled = false
return title
}()
override func viewDidLoad() {
setupUI()
setupUIConstraints()
titleTextView.delegate = self
// contrasting colors during development
scrollView.backgroundColor = .red
titleTextView.backgroundColor = .yellow
contentTextView.backgroundColor = .green
divider.backgroundColor = .blue
contentView.backgroundColor = .cyan
}
private func setupUI() {
view.backgroundColor = UIColor(hexString: "#131415")
view.addSubview(scrollView)
scrollView.addSubview(contentView)
contentView.addSubview(titleTextView)
contentView.addSubview(divider)
contentView.addSubview(contentTextView)
}
private func setupUIConstraints() {
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
contentView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
titleTextView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 95).isActive = true
titleTextView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 35).isActive = true
titleTextView.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -35).isActive = true
divider.topAnchor.constraint(equalTo: titleTextView.bottomAnchor, constant: 15).isActive = true
divider.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
divider.heightAnchor.constraint(equalToConstant: 1).isActive = true
divider.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.8).isActive = true
contentTextView.topAnchor.constraint(equalTo: divider.bottomAnchor, constant: 15).isActive = true
contentTextView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 35).isActive = true
contentTextView.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -35).isActive = true
contentTextView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -95).isActive = true
}
}
extension ComposerVC: UITextViewDelegate {
// func textViewDidChange(_ textView: UITextView) {
// let fixedWidth = textView.frame.size.width
// let newSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
// textView.frame.size = CGSize(width: max(newSize.width, fixedWidth), height: newSize.height)
// }
}
Assuming you are using iOS 11+, Your contentView should have its anchors constrained to the contentLayoutGuide of the scrollView. like this:
contentView
.leadingAnchor
.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor).isActive = true
contentView
.topAnchor
.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor).isActive = true
contentView
.trailingAnchor
.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor).isActive = true
contentView
.bottomAnchor
.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor).isActive = true
Also, its width should be constrained to the scrollView's frameLayoutGuide and not the view's width, like this:
contentView
.widthAnchor
.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor).isActive = true
That should make the scrollView detect the content size to fit.

Resources