I have a simple UITableViewCell subclass in which I have a titleLabel property (the cell has more views, but for the sake of showing the issue, I will only do one label as it also breaks).
Here is my label code:
self.titleLabel = UILabel(frame: .zero)
self.titleLabel.numberOfLines = 0
self.titleLabel.font = UIFont.preferredFont(forTextStyle: .headline)
self.titleLabel.textColor = UIColor.white
self.titleLabel.adjustsFontSizeToFitWidth = false
self.titleLabel.textAlignment = .left
self.titleLabel.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(self.titleLabel)
self.titleLabel.topAnchor.constraint(equalTo: self.artworkImageView.topAnchor).isActive = true
self.titleLabel.leftAnchor.constraint(equalTo: self.artworkImageView.rightAnchor, constant: 10.0).isActive = true
self.titleLabel.rightAnchor.constraint(equalTo: self.contentView.rightAnchor, constant: -10.0).isActive = true
self.titleLabel.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor).isActive = true
I also set my UITableView up like this:
self.tableView.rowHeight = UITableView.automaticDimension
self.tableView.estimatedRowHeight = 50.0
However it keeps breaking constraints with an error like this:
"<NSLayoutConstraint:0x28211ce10 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x10859a4f0.height == 4.33333 (active)>"
There are more constraints, however this one says that my cell content view is only 4.3 of height, however I want it to grow as the label grows.
I also tried setting contentHuggingPriorities and the priority of the bottom anchor. I also compared it to code online or IB constraints I saw online and they all set 4 constraints: top, left, bottom, right.
I also tried leading and trailing instead of left and right - same result.
Any help appreciated
Here is my full AlbumTableViewCell:
class AlbumTableViewCell: UITableViewCell {
public private(set) var artworkImageView: UIImageView
public private(set) var titleLabel: UILabel
public private(set) var albumInfoLabel: UILabel
public private(set) var artistNameLabel: UILabel
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
self.artworkImageView = UIImageView(frame: .zero)
self.titleLabel = UILabel(frame: .zero)
self.albumInfoLabel = UILabel(frame: .zero)
self.artistNameLabel = UILabel(frame: .zero)
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.tintColor = UIColor.white
self.backgroundColor = UIColor.clear
self.contentView.backgroundColor = UIColor.barTintColor
self.contentView.layer.masksToBounds = false
self.contentView.layer.cornerRadius = 10.0
self.artworkImageView.layer.cornerRadius = 10.0
self.artworkImageView.layer.masksToBounds = true
self.artworkImageView.contentMode = .scaleAspectFit
self.artworkImageView.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(self.artworkImageView)
// image view
self.artworkImageView.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 5).isActive = true
self.artworkImageView.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 5).isActive = true
self.artworkImageView.widthAnchor.constraint(equalToConstant: 80).isActive = true
self.artworkImageView.heightAnchor.constraint(equalToConstant: 80).isActive = true
self.titleLabel = UILabel(frame: .zero)
self.titleLabel.numberOfLines = 2
self.titleLabel.font = UIFont.preferredFont(forTextStyle: .headline)
self.titleLabel.textColor = UIColor.white
self.titleLabel.adjustsFontSizeToFitWidth = false
self.titleLabel.textAlignment = .left
self.titleLabel.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(self.titleLabel)
// title
self.titleLabel.leadingAnchor.constraint(equalTo: self.artworkImageView.trailingAnchor, constant: 5.0).isActive = true
self.titleLabel.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 5.0).isActive = true
self.titleLabel.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -5.0).isActive = true
self.titleLabel.heightAnchor.constraint(equalToConstant: 35).isActive = true
self.albumInfoLabel.numberOfLines = 1
self.albumInfoLabel.font = UIFont.preferredFont(forTextStyle: .subheadline)
self.albumInfoLabel.textColor = UIColor.lightGray
self.albumInfoLabel.adjustsFontSizeToFitWidth = true
self.albumInfoLabel.textAlignment = .left
self.albumInfoLabel.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(self.albumInfoLabel)
// albumInfoLabel
self.albumInfoLabel.topAnchor.constraint(equalTo: self.titleLabel.bottomAnchor, constant: 5.0).isActive = true
self.albumInfoLabel.leadingAnchor.constraint(equalTo: self.titleLabel.leadingAnchor).isActive = true
self.albumInfoLabel.trailingAnchor.constraint(equalTo: self.titleLabel.trailingAnchor).isActive = true
self.albumInfoLabel.heightAnchor.constraint(equalToConstant: 35).isActive = true
self.artistNameLabel = UILabel(frame: .zero)
self.artistNameLabel.numberOfLines = 1
self.artistNameLabel.font = UIFont.preferredFont(forTextStyle: .subheadline)
self.artistNameLabel.textColor = UIColor.lightGray
self.artistNameLabel.adjustsFontSizeToFitWidth = true
self.artistNameLabel.textAlignment = .left
self.artistNameLabel.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(self.artistNameLabel)
// albumInfoLabel
self.artistNameLabel.topAnchor.constraint(equalTo: self.albumInfoLabel.bottomAnchor, constant: 5.0).isActive = true
self.artistNameLabel.leadingAnchor.constraint(equalTo: self.albumInfoLabel.leadingAnchor).isActive = true
self.artistNameLabel.trailingAnchor.constraint(equalTo: self.albumInfoLabel.trailingAnchor).isActive = true
self.artistNameLabel.heightAnchor.constraint(equalToConstant: 35).isActive = true
let selectedView: UIView = UIView(frame: .zero)
selectedView.backgroundColor = UIColor.gray
selectedView.layer.cornerRadius = 10.0
selectedView.layer.masksToBounds = false
self.selectedBackgroundView = selectedView
}
override func layoutSubviews() {
super.layoutSubviews()
let contentViewFrame = self.contentView.frame
let insetContentViewFrame = contentViewFrame.inset(by: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10))
self.contentView.frame = insetContentViewFrame
self.selectedBackgroundView?.frame = insetContentViewFrame
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
This code does not crash anymore but the cell does not autoresize (see image).. The light gray area is the content view
this code does not break any constrains anymore, but the cell also does not calculate the hight automatically. Here is my table view controller:
self.tableView.register(AlbumTableViewCell.self, forCellReuseIdentifier: "AlbumCell")
self.tableView.separatorStyle = .none
self.tableView.tableFooterView = UIView(frame: .zero)
self.tableView.rowHeight = UITableView.automaticDimension
self.tableView.estimatedRowHeight = 50.0
var titleLabel = UILabel()
contentView.addSubview(titleLabel)
titleLabel.textColor = UIColor(red:0.32, green:0.17, blue:0.12, alpha:1.0)
titleLabel.font = UIFont.boldSystemFont(ofSize: 16.0)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.topAnchor.constraint(equalTo: marginGuide.topAnchor).isActive = true
titleLabel.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor, constant: 8).isActive = true
titleLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor).isActive = true
titleLabel.leadingAnchor.constraint(equalTo: marginGuide.leadingAnchor, constant: 8).isActive = true
Try this.
To be honest i don't know exactly what's your issue but i know for sure that you have a bad time with cell constraint and you want a dynamic cell .
So let's say you have a cell with 4 views artWrokImageView , artNameLabel , artDescriptionLabel and the artistNameLabel
First you need to make sure these views most constraint from top and bottom the table cell , So when you call self.tableView.rowHeight = UITableView.automaticDimension it knows how to dynamically expand .
Second you need to tell the table to expand when ever view did appear
This is demo for the 4 views above .
Table View Controller :
class YourTableViewController : UITableViewController {
let customTableCellID = "customTableCellID";
override func viewDidLoad() {
super.viewDidLoad();
setupTable();
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tableView.estimatedRowHeight = 50;
self.tableView.rowHeight = UITableView.automaticDimension;
}
fileprivate func setupTable() {
tableView.register(YourCustomTableCell.self, forCellReuseIdentifier: customTableCellID);
}
}
extension YourTableViewController {
override func numberOfSections(in tableView: UITableView) -> Int {
return 1;
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1;
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: customTableCellID, for: indexPath) as! YourCustomTableCell
cell.artistNameLabel.text = "the real art";
cell.artworkImageView.image = UIImage(named: "mazen");
cell.artDescriptionLabel.text = "long long long long long long long long long long long long long long long long long long long long long long long long long description";
cell.artNameLabel.text = "someting"
return cell
}
}
Cell :
class YourCustomTableCell : UITableViewCell {
var artworkImageView : UIImageView = {
let imageView = UIImageView();
imageView.translatesAutoresizingMaskIntoConstraints = false;
return imageView;
}()
var artNameLabel : UILabel = {
let label = UILabel();
label.font = UIFont.boldSystemFont(ofSize: 20);
label.translatesAutoresizingMaskIntoConstraints = false;
return label
}()
var artDescriptionLabel : UILabel = {
let label = UILabel();
label.textColor = .darkGray;
label.numberOfLines = 0;
label.translatesAutoresizingMaskIntoConstraints = false;
return label;
}()
var artistNameLabel : UILabel = {
let label = UILabel();
label.textColor = .blue;
label.translatesAutoresizingMaskIntoConstraints = false;
return label;
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier);
setupCell();
}
fileprivate func setupCell() {
// add views
contentView.addSubview(artworkImageView);
contentView.addSubview(artNameLabel);
contentView.addSubview(artDescriptionLabel);
contentView.addSubview(artistNameLabel);
// layout views
// image view
artworkImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 5).isActive = true;
artworkImageView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5).isActive = true;
artworkImageView.widthAnchor.constraint(equalToConstant: 80).isActive = true;
artworkImageView.heightAnchor.constraint(equalToConstant: 80).isActive = true;
// art name
artNameLabel.leadingAnchor.constraint(equalTo: artworkImageView.trailingAnchor, constant: 5).isActive = true;
artNameLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5).isActive = true;
artNameLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -5).isActive = true;
artNameLabel.heightAnchor.constraint(equalToConstant: 35).isActive = true;
// descripion
artDescriptionLabel.leadingAnchor.constraint(equalTo: artworkImageView.trailingAnchor, constant: 5).isActive = true;
artDescriptionLabel.topAnchor.constraint(equalTo: artNameLabel.bottomAnchor, constant: 5).isActive = true;
artDescriptionLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -5).isActive = true;
// in art description label don't set the height anchors so it can expand
// artist name
artistNameLabel.leadingAnchor.constraint(equalTo: artworkImageView.trailingAnchor, constant: 5).isActive = true;
artistNameLabel.topAnchor.constraint(equalTo: artDescriptionLabel.bottomAnchor, constant: 5).isActive = true;
artistNameLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -5).isActive = true;
artistNameLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -5).isActive = true; // this constraint is requierd for dynamic cell
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
}
And if this answer not in your case please tell me .
Your tableviewcells don't know what their height is suppose to be. Which is fine.
You just have to give enough constraints so that it can figure it out. You're not doing that!
The major issue I currently see is that the artworkImageView is not constrained to top and bottom. Every vertical axiom of views needs to be constrained top to bottom.
Your first vertical axiom is just the image. It doesn't have a bottom constraint. Add that. So the tableviewcell knows how much it needs to resize itself. I strongly recommend you to see this moment of WWDC.
Also earlier in the same video at this moment it strongly recommend that you just dump your views in multiple stackviews and organize it that way. So that's also an alternative.
PS:
don't dump self. It just increases the line width with no added benefit.
Move all your non-layout related setup of your labels/images to their own instantiation. e.g.
lazy label : UILabel = {
let label = UILabel()
label.text = "John"
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
No need to mention frame : .zero. Just UILabel() implies the frame is zero.
It's always best to start your UI small then keep adding more elements to it. That way debugging your layout becomes easier/smaller.
Related
I am having an issue with my tableview, where the cells don't orient correctly before an image is cached, and only once I return back to a page and my image is cached do they orient correctly. Here is an example of what I am talking about, the first image is when I first go to the page, and I have a function which stores images in an imagecache, and the second picture is when I return to that page after navigating somewhere else, and the images are cached.
Incorrectly Formatted:
Correctly Formatted:
I know it is a very subtle difference
with the way it looks
but I'd love to get that figured out and understand why this is happening
My cells constant height is 85, and here is the relevant code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//Indexing part I left out dw about this
let cell = discountTableView.dequeueReusableCell(withIdentifier: self.discountCellId, for: indexPath) as! DiscountCell
cell.textLabel?.text = discount.retailerName
cell.detailTextLabel?.text = discount.matchedFirstName + " " + discount.matchedLastName
cell.profileImageView.image = UIImage.gif(name: "imageLoading")!
grabImageWithCache(discount.retailerImageLink) { (profilePic) in
cell.profileImageView.image = profilePic
}
//Adding second Image
cell.matchImageView.image = UIImage.gif(name: "imageLoading")!
grabImageWithCache(discount.matchedProfileImage) { (matchProfilepic) in
cell.matchImageView.image = matchProfilepic
}
//declare no selection style for cell (avoid gray highlight)
cell.selectionStyle = .none
//format the cell's curvature
cell.layer.cornerRadius = 38
return cell
}
**I am curious as to why the height of the cells seem to change when the image is cached, because the height is set to a constant number, so I have no idea why it is changing. When I print the height of the cells it says it is 85 both times as well...
UITableViewCell Class:
class DiscountCell: UITableViewCell {
override func layoutSubviews() {
super.layoutSubviews()
//vertical spacing between cells
let padding = UIEdgeInsets(top: 0, left: 0, bottom: 7, right: 0)
bounds = bounds.inset(by: padding)
textLabel?.frame = CGRect(x: 120, y: textLabel!.frame.origin.y-10, width: textLabel!.frame.width, height: textLabel!.frame.height)
detailTextLabel?.frame = CGRect(x: 120, y: detailTextLabel!.frame.origin.y, width: detailTextLabel!.frame.width, height: detailTextLabel!.frame.height)
//spacing between cells
let margins = UIEdgeInsets(top: 0, left: 0, bottom: 85, right: 0)
contentView.frame = contentView.frame.inset(by: margins)
}
//sets a placeholder imageview
let profileImageView: UIImageView = {
let imageView = UIImageView ()
imageView.image = UIImage(named: "failed")
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.layer.cornerRadius = 35
imageView.layer.masksToBounds = true
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.layer.borderWidth = 2
imageView.layer.borderColor = #colorLiteral(red: 0.9725490196, green: 0.9725490196, blue: 0.9725490196, alpha: 1)
return imageView
}()
//PlaceHolder imageview for match
let matchImageView:UIImageView = {
let imageView = UIImageView ()
imageView.image = UIImage(named: "failed")
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.layer.cornerRadius = 35
imageView.layer.masksToBounds = true
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
return imageView
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: .subtitle, reuseIdentifier: reuseIdentifier)
addSubview(matchImageView)
addSubview(profileImageView)
//Setting Profile Pic anchors
profileImageView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 5).isActive = true
profileImageView.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
profileImageView.widthAnchor.constraint(equalToConstant: 70).isActive = true
profileImageView.heightAnchor.constraint(equalToConstant: 70).isActive = true
//Setting Match Anchors
matchImageView.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
matchImageView.widthAnchor.constraint(equalToConstant: 70).isActive = true
matchImageView.heightAnchor.constraint(equalToConstant: 70).isActive = true
matchImageView.leftAnchor.constraint(equalTo: profileImageView.leftAnchor,constant: 35).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I believe I have narrowed in on the issue, I noticed that the spacing between the cells was inconsistent both times, so I think the issue is with these lines of code
//vertical spacing between cells
let padding = UIEdgeInsets(top: 0, left: 0, bottom: 7, right: 0)
bounds = bounds.inset(by: padding)
I am not sure what to do differently, as I watched tutorials saying to change the contentviews insets but when I do, it has no effect, and I see things saying iOS 11 changed the way you do this, but haven't been able to find how to actually go about fixing this.
Couple notes...
Not a good idea to modify a cell's bounds or its contentView. UIKit uses those for a lot of things (such as adjusting contents when implementing table editing).
Cell subviews should be added to the cell's contentView, not to the cell itself. Again, due to how table views rely on that hierarchy.
You can use UIView and UIImageView subclasses to handle the "rounding" for you. For example:
class RoundImageView: UIImageView {
override func layoutSubviews() {
layer.cornerRadius = bounds.size.height * 0.5
}
}
class RoundEndView: UIView {
override func layoutSubviews() {
layer.cornerRadius = bounds.size.height * 0.5
}
}
Here is a complete implementation:
class RoundImageView: UIImageView {
override func layoutSubviews() {
layer.cornerRadius = bounds.size.height * 0.5
}
}
class RoundEndView: UIView {
override func layoutSubviews() {
layer.cornerRadius = bounds.size.height * 0.5
}
}
class DiscountCell: UITableViewCell {
// "Holder" view... contains all other UI elements
// use RoundEndView so it handles the corner radius by itself
let holderView: RoundEndView = {
let v = RoundEndView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .white
return v
}()
//sets a placeholder imageview
// use RoundImageView so it handles the corner radius by itself
let profileImageView: RoundImageView = {
let imageView = RoundImageView()
imageView.image = UIImage(named: "failed")
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.layer.masksToBounds = true
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.layer.borderWidth = 2
imageView.layer.borderColor = #colorLiteral(red: 0.9725490196, green: 0.9725490196, blue: 0.9725490196, alpha: 1)
return imageView
}()
//PlaceHolder imageview for match
// use RoundImageView so it handles the corner radius by itself
let matchImageView: RoundImageView = {
let imageView = RoundImageView()
imageView.image = UIImage(named: "failed")
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.layer.masksToBounds = true
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
return imageView
}()
let mainLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.font = UIFont.systemFont(ofSize: 17.0, weight: .regular)
return v
}()
let subLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.font = UIFont.systemFont(ofSize: 14.0, weight: .regular)
return v
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: .subtitle, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
backgroundColor = .clear
contentView.addSubview(holderView)
holderView.addSubview(matchImageView)
holderView.addSubview(profileImageView)
holderView.addSubview(mainLabel)
holderView.addSubview(subLabel)
NSLayoutConstraint.activate([
// holder view constraints Top 5 pts from contentView Top
holderView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5.0),
// Leading 12 pts from contentView Leading
holderView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 12.0),
// Trailing 5 pts from contentView Trailing
holderView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -5.0),
// 5 pts from contentView Bottom (use lessThanOrEqualTo to avoid auto-layout warnings)
holderView.bottomAnchor.constraint(lessThanOrEqualTo: contentView.bottomAnchor, constant: -5.0),
//Setting Profile Pic anchors - Top and Leading 5 pts from Top and Leading of Holder view
profileImageView.topAnchor.constraint(equalTo: holderView.topAnchor, constant: 5),
profileImageView.leadingAnchor.constraint(equalTo: holderView.leadingAnchor, constant: 5),
// Botom 5 pts from contentView Bottom - this will determine the height of the Holder view
profileImageView.bottomAnchor.constraint(equalTo: holderView.bottomAnchor, constant: -5),
// width 70 pts, height equal to width (keeps it "round")
profileImageView.widthAnchor.constraint(equalToConstant: 70.0),
profileImageView.heightAnchor.constraint(equalTo: profileImageView.widthAnchor),
//Setting Match Pic Anchors - 35 pts from Profile Leading, centerY to Profile
matchImageView.leadingAnchor.constraint(equalTo: profileImageView.leadingAnchor, constant: 35),
matchImageView.centerYAnchor.constraint(equalTo: profileImageView.centerYAnchor, constant: 0.0),
// same width and height as Profile
matchImageView.widthAnchor.constraint(equalTo: profileImageView.widthAnchor),
matchImageView.heightAnchor.constraint(equalTo: profileImageView.heightAnchor),
//Setting Main Label Anchors - Top equal to Top of Match image
mainLabel.topAnchor.constraint(equalTo: matchImageView.topAnchor, constant: 0.0),
// Leading 12 pts from Match image Trailing
mainLabel.leadingAnchor.constraint(equalTo: matchImageView.trailingAnchor, constant: 12.0),
// prevent Trailing from going past holder view Trailing
mainLabel.trailingAnchor.constraint(equalTo: holderView.trailingAnchor, constant: -16.0),
//Setting Sub Label Anchors - Top 8 pts from Main label Bottom
subLabel.topAnchor.constraint(equalTo: mainLabel.bottomAnchor, constant: 8.0),
// Leading and Trailing equal to Main label Leading / Trailing
subLabel.leadingAnchor.constraint(equalTo: mainLabel.leadingAnchor, constant: 0.0),
subLabel.trailingAnchor.constraint(equalTo: mainLabel.trailingAnchor, constant: 0.0),
])
}
}
// example Discount object struct
struct Discount {
var retailerName: String = ""
var matchedFirstName: String = ""
var matchedLastName: String = ""
var matchedProfileImage: String = ""
var retailerImageLink: String = ""
}
class DiscountViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let discountTableView = UITableView()
let discountCellId = "dcID"
var myData: [Discount] = []
override func viewDidLoad() {
super.viewDidLoad()
// a little sample data
var d = Discount(retailerName: "Cup & Cone", matchedFirstName: "Kara", matchedLastName: "Tomlinson", matchedProfileImage: "pro1", retailerImageLink: "")
myData.append(d)
d = Discount(retailerName: "Cup & Cone", matchedFirstName: "Sophie", matchedLastName: "Turner", matchedProfileImage: "pro2", retailerImageLink: "")
myData.append(d)
d = Discount(retailerName: "Another Retailer", matchedFirstName: "WanaB3", matchedLastName: "Nerd", matchedProfileImage: "pro3", retailerImageLink: "")
myData.append(d)
discountTableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(discountTableView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
discountTableView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
discountTableView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
discountTableView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
discountTableView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
])
view.backgroundColor = UIColor(red: 1.0, green: 0.9, blue: 0.7, alpha: 1.0)
discountTableView.backgroundColor = UIColor(red: 0.66, green: 0.66, blue: 0.9, alpha: 1.0)
discountTableView.separatorStyle = .none
discountTableView.delegate = self
discountTableView.dataSource = self
discountTableView.register(DiscountCell.self, forCellReuseIdentifier: discountCellId)
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//Indexing part I left out dw about this
let discount = myData[indexPath.row]
let cell = discountTableView.dequeueReusableCell(withIdentifier: self.discountCellId, for: indexPath) as! DiscountCell
cell.mainLabel.text = discount.retailerName
cell.subLabel.text = discount.matchedFirstName + " " + discount.matchedLastName
// add first image
if let img = UIImage(named: "imageLoading") {
cell.profileImageView.image = img
}
// use your custom image funcs
//cell.profileImageView.image = UIImage.gif(name: "imageLoading")!
//grabImageWithCache(discount.retailerImageLink) { (profilePic) in
// cell.profileImageView.image = profilePic
//}
//Adding second Image
if let img = UIImage(named: discount.matchedProfileImage) {
cell.matchImageView.image = img
}
// use your custom image funcs
//cell.matchImageView.image = UIImage.gif(name: "imageLoading")!
//grabImageWithCache(discount.matchedProfileImage) { (matchProfilepic) in
// cell.matchImageView.image = matchProfilepic
//}
//declare no selection style for cell (avoid gray highlight)
cell.selectionStyle = .none
return cell
}
}
Results:
Your cells are a pill shape, which means you can achieve the same effect by doing cell.layer.cornerRadius = cell.frame.height / 2. Next you should use top and bottom constraints for your profileImageView so it forces the cell to have a certain padding instead of trying to create the padding yourself. The problem is that your cell height is changing a little bit when the images are cached, no idea what could be causing that.
I'm creating a simple app. I'm trying to make two screens look the same but I can't seem to get one of the table views to work. Instead, when the set up table view function is called, the error signal sigbart appears. I can't see why this is since on the other screen the table view works no problem and I've copied over the code.
let tableview: UITableView = {
let tv = UITableView()
tv.backgroundColor = UIColor.white
tv.translatesAutoresizingMaskIntoConstraints = false
return tv
}()
func setupTableView() {
tableview.delegate = self
tableview.dataSource = self
tableview.register(BunchCells.self, forCellReuseIdentifier: "cellId")
tableview.backgroundColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0)
view.addSubview(tableview)
NSLayoutConstraint.activate([
tableview.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 170),
tableview.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
tableview.rightAnchor.constraint(equalTo: self.view.rightAnchor),
tableview.leftAnchor.constraint(equalTo: self.view.leftAnchor)
])
}
class BunchCells: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
let cellView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.red
view.layer.cornerRadius = 10
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let dayLabel: UILabel = {
let label = UILabel()
label.text = "Day 1"
label.textColor = UIColor.white
label.font = UIFont.boldSystemFont(ofSize: 16)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
func setupView() {
addSubview(cellView)
NSLayoutConstraint.activate([
cellView.topAnchor.constraint(equalTo: self.topAnchor, constant: 20),
cellView.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -10),
cellView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 10),
cellView.bottomAnchor.constraint(equalTo: self.bottomAnchor)
])
dayLabel.heightAnchor.constraint(equalToConstant: 200).isActive = true
dayLabel.widthAnchor.constraint(equalToConstant: 200).isActive = true
dayLabel.centerYAnchor.constraint(equalTo: cellView.centerYAnchor).isActive = true
dayLabel.leftAnchor.constraint(equalTo: cellView.leftAnchor, constant: 20).isActive = true
}
}
I run your program and doesn't have any error, but I think this looks a little bit weird:
weak var tableView: UITableView!
let tableview: UITableView = {
let tv = UITableView()
tv.translatesAutoresizingMaskIntoConstraints = false
tv.separatorColor = UIColor.white
return tv
}()
Maybe cause of your error was what you accidentally invoke method on tableView which is always nil.? If it is not, give some hints how to reproduce your error).
I'm working on my first IOS app and have run into an issue. I have a quite elaborate programmatic autoloyout UI that responds to user interaction. When a keyboard is shown certain Views must be collapsed, others moved and others spawned into existence based on a few conditions.
Now in it's default state no autolayout errors occur. But once things start moving it all comes apart. A few of the issues have to do with images retaining their height, while their view's heigconstriant is set to 0. Now I do have .scaleToFill enabled.
I have looked into stackViews however since most of my Views are of a different size with different nested UI elements stackviews do now appear to solve my issues. But I would certainly like some input on that.
Now my questions is: How do I collapse UIView and UIImageviews dynamically and programatically?
Now I don't mind typing out a lot of constraints manually, as long as it works.
Here are the constraints of the Views in question(there are more)
func setUpLayout() {
// SuggestionCloud
suggestionCloud.setConstraints(
topAnchor: textView.bottomAnchor, topConstant: 0,
bottomAnchor: bottomMenu.topAnchor, bottomConstant: 0,
trailingAnchor: view.trailingAnchor, trailingConstant: -10,
leadingAnchor: view.leadingAnchor, leadingConstant: 10)
print("Suggestion View frame :\(suggestionCloud.frame)")
//WEIGHT_IMAGE_VIEW
weigtImageView.topAnchor.constraint(equalTo: view.topAnchor, constant: 30).isActive = true
weigtImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true
weigtImageView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true
weigtImageView.heightAnchor.constraint(equalToConstant: 150).isActive = true
weigtImageView.addSubview(weightLabel);
print("Weight Image View \(weigtImageView.frame)")
//WEIGHT_LABEL
weightLabel.trailingAnchor.constraint(equalTo: weigtImageView.trailingAnchor, constant: -30).isActive = true;
weightLabel.leadingAnchor.constraint(equalTo: weigtImageView.leadingAnchor, constant: 25).isActive = true;
weightLabel.heightAnchor.constraint(equalTo: weigtImageView.heightAnchor, multiplier: 1).isActive = true;
//TEXT_VIEW
textView.topAnchor.constraint(equalTo: weigtImageView.bottomAnchor).isActive = true;
textView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true
textView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true;
textView.heightAnchor.constraint(equalToConstant: 100).isActive = true;
textView.addSubview(nameTextField)
textView.addSubview(tagTextField)
textView.addSubview(setButtonView)
//TAG_CONTROLLER
tagController.heightAnchor.constraint(equalToConstant: 110).isActive = true;
tagController.topAnchor.constraint(equalTo: self.weigtImageView.bottomAnchor).isActive = true;
tagController.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant : 10).isActive = true
tagController.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -10).isActive = true
//SET_BUTTON_VIEW
setButtonView.topAnchor.constraint(equalTo: textView.topAnchor).isActive = true;
setButtonView.bottomAnchor.constraint(equalTo: textView.bottomAnchor).isActive = true;
setButtonView.trailingAnchor.constraint(equalTo: textView.trailingAnchor).isActive = true;
setButtonView.widthAnchor.constraint(equalToConstant: 110).isActive = true;
//NAME_TEXT_FIELD
nameTextField.trailingAnchor.constraint(equalTo: setButtonView.leadingAnchor, constant: -5).isActive = true
nameTextField.leadingAnchor.constraint(equalTo: textView.leadingAnchor, constant: 10).isActive = true
nameTextField.topAnchor.constraint(equalTo: textView.topAnchor, constant: 13).isActive = true
nameTextField.heightAnchor.constraint(equalToConstant: 31).isActive = true
nameTextField.layer.cornerRadius = 8
nameTextField.backgroundColor = .white;
//TAG_TEXT_FIELD
tagTextField.trailingAnchor.constraint(equalTo: setButtonView.leadingAnchor, constant: -5).isActive = true
tagTextField.leadingAnchor.constraint(equalTo: textView.leadingAnchor, constant: 10).isActive = true
tagTextField.bottomAnchor.constraint(equalTo: textView.bottomAnchor, constant: -13).isActive = true
tagTextField.heightAnchor.constraint(equalToConstant: 31).isActive = true
tagTextField.layer.cornerRadius = 8
tagTextField.backgroundColor = .white
here's the viewcontrollers setup:
class UIScaleControllerVew: UIViewController, UITextFieldDelegate, SuggenstionCloudDelegate {
let weigtImageView : UIImageView = {
var imageView = UIImageView(image: UIImage(named: "scaleVisorShadow"));
imageView.contentMode = .scaleToFill
imageView.translatesAutoresizingMaskIntoConstraints = false;
return imageView
}()
let weightLabel : UILabel = {
let label = UILabel()
label.text = "135 gr"
label.font = UIFont(name: "Avenir-Light", size: 50.0)
label.textAlignment = .right
label.translatesAutoresizingMaskIntoConstraints = false
return label
}();
let textView : UIView = {
var view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false;
return view;
}();
let setButtonView : UIImageView = {
var imageView = UIImageView(image: UIImage(named: "setButton"))
imageView.translatesAutoresizingMaskIntoConstraints = false;
return imageView;
}();
let nameTextField : UITextField = {
var textField = UITextField();
textField.tag = 2;
textField.translatesAutoresizingMaskIntoConstraints = false;
textField.addTarget(self, action: #selector(nameFieldEditingChanged(_:)), for: UIControl.Event.editingChanged)
return textField;
}();
let tagTextField : UITextField = {
var textField = UITextField();
textField.tag = 1;
textField.translatesAutoresizingMaskIntoConstraints = false;
textField.addTarget(self, action: #selector(textFieldEditingChanged(_:)), for: UIControl.Event.editingChanged)
return textField;
}();
let bottomMenu : UIView = {
var view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false;
return view;
}();
let saveButton : UIButton = {
let button = UIButton()
button.setImage(UIImage(named: "save"), for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false;
return button
}();
let microPhoneButton : UIButton = {
let button = UIButton()
button.setImage(UIImage(named: "microPhone"), for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false;
return button;
}();
let suggestionCloud : SuggenstionCloud = {
let cloud = SuggenstionCloud(image: UIImage(named: "suggestionCloud.png"))
cloud.translatesAutoresizingMaskIntoConstraints = false;
return cloud;
}();
let tagController : TagController = {
let tagController = TagController()
tagController.translatesAutoresizingMaskIntoConstraints = false
return tagController;
}()
let scaleModel = ScaleModel.init()
override func viewDidLoad() {
super.viewDidLoad()
print("UIScaleController_DidLoad")
tagTextField.delegate = self
nameTextField.delegate = self;
suggestionCloud.delegate = self;
view.backgroundColor = UIColor(hexString: "8ED7F5")
view.addSubview(weigtImageView)
view.addSubview(textView)
view.addSubview(bottomMenu);
view.addSubview(suggestionCloud)
view.addSubview(tagController)
tagController.isHidden = true;
setUpLayout()
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShowNotification(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHideNotification(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
}
var didSetUpSuggestionCloud = false
var didSetUpTagController = false
override func viewDidLayoutSubviews() {
guard !self.didSetUpTagController else {
return
}
guard !self.didSetUpSuggestionCloud else {
return
}
self.didSetUpSuggestionCloud = true
self.didSetUpTagController = true
};
and here's the problematic code:
#objc func keyboardWillShowNotification(notification: Notification ) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
// collapse and hide bottom view
bottomMenu.contentMode = .scaleToFill;
bottomMenu.heightAnchor.constraint(equalToConstant: 0).isActive = true;
bottomMenu.isHidden = true
// collapse and hide top view
weigtImageView.contentMode = .scaleToFill;
weigtImageView.heightAnchor.constraint(equalToConstant: 0).isActive = true;
weigtImageView.isHidden = true;
// spawn my tag view
tagController.topAnchor.constraint(equalTo: self.textView.bottomAnchor).isActive = true;
tagController.bottomAnchor.constraint(equalTo: suggestionCloud.topAnchor).isActive = true
tagController.isHidden = false;
// set textviews new constraints
textView.bottomAnchor.constraint(equalTo: tagController.topAnchor).isActive = true;
// set middleView's new constraints
suggestionCloud.topAnchor.constraint(equalTo: tagController.bottomAnchor).isActive = true;
suggestionCloud.bottomAnchor.constraint(equalTo: bottomMenu.topAnchor, constant: -keyboardSize.height).isActive = true
self.view.layoutIfNeeded()
}
}
Now there are so many unexpected things happening that i'm positive that my approach to this is just wrong conceptually.
Please let me know where I need to look for a solution.
Here are e few pictures of what is happening so far:
So when the keyboard is up:
The weightView is collapsed: suggestioncloud and text are moved up.
If a tag is added a new view called tagController needs to be places between the texView and the suggesitonCloud. Lastyl the keybaord needs to be collapsed again.
Ill add some sscreenshots
One thing that you could look at is duplicate constraints.
Every time you call weigtImageView.heightAnchor.constraint(equalToConstant: 0).isActive = true you are creating a new constraint. This does not automatically replace any previous height constraints that are active.
To replace a constraint, you need to keep a reference to it, deactivate it, and then activate a new one (and optionally assign it the the variable you use to keep a reference).
Stack views
Stack views could be helpful in your situation, because they automatically collapse views that have isHidden set to true. I think that as long as the direct subviews of the StackView have an intrinsic content size (e.g. correct internal constraints), they should be placed correctly by the StackView.
If you do not have a strong reference to your views to be released then it is enough to execute this:
if view2Breleased.superview != nil {
view2Breleased.removeFromSuperview()
}
The view will then disappear and be released from memory.
If you don't know what a strong reference is then for the time being, just try the code I wrote. The views will disappear anyway.
(Strong reference means you have assigned the view to a variable which survives the execution of the code view2Breleased.removeFromSuperview() and the exit from the function call where the code view2Breleased.removeFromSuperview() is.)
You can change the constant of the heightAnchor constraint like so:
import Foundation
import UIKit
class TestController : UIViewController {
var myViewHeightConstraint : NSLayoutConstraint!
let myView : UIControl = {
let newView = UIControl()
newView.translatesAutoresizingMaskIntoConstraints = false
newView.backgroundColor = .red
return newView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
self.myView.addTarget(self, action: #selector(viewClicked), for: .touchUpInside)
self.myViewHeightConstraint = myView.heightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.heightAnchor)
setup()
}
func setup(){
view.addSubview(myView)
myView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
myView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
myView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
self.myViewHeightConstraint.isActive = true
}
#objc func viewClicked() {
self.myViewHeightConstraint.constant = -self.myView.frame.size.height
}
}
In my example, I set the constant to minus the height of the view's frame height, which effectively collapses it.
class CustomTableViewCell: UITableViewCell {
let nameLbl: UILabel = UILabel()
let profileBtn: UIButton = UIButton()
let aboutLbl: UILabel = UILabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(profileBtn)
contentView.addSubview(nameLbl)
contentView.addSubview(aboutLbl)
nameLbl.translatesAutoresizingMaskIntoConstraints = false
profileBtn.translatesAutoresizingMaskIntoConstraints = false
aboutLbl.translatesAutoresizingMaskIntoConstraints = false
profileBtn.backgroundColor = UIColor.red
nameLbl.font = UIFont(name: "Arial", size: 16)
aboutLbl.font = UIFont(name: "Arial", size: 16)
profileBtn.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
profileBtn.leftAnchor.constraint(equalTo: leftAnchor, constant: 20).isActive = true
profileBtn.widthAnchor.constraint(equalToConstant: 40).isActive = true
profileBtn.heightAnchor.constraint(equalToConstant: 40).isActive = true
self.profileBtn.layer.masksToBounds = true
self.profileBtn.layer.cornerRadius = CGFloat(roundf(Float(self.profileBtn.frame.size.width/2.0)))
nameLbl.topAnchor.constraint(equalTo: topAnchor, constant: 30).isActive = true
nameLbl.leftAnchor.constraint(equalTo: leftAnchor, constant: 70).isActive = true
nameLbl.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20).isActive = true
aboutLbl.topAnchor.constraint(equalTo: nameLbl.bottomAnchor, constant: 10).isActive = true
aboutLbl.leftAnchor.constraint(equalTo: leftAnchor, constant: 70).isActive = true
aboutLbl.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
i want the profile button inside the cell to have a round design.but ebven setting the corener radius and marskstobounds to true i am getting a square button. what am i doing wrong any help is apreciated. thanks in advance.
You are calculating the corner radius when the profile button hasn't been laid out yet. This means the width of the profile button will be zero rendering the corner radius the same. Move the line that you set the corner radius to an overriding method of layoutSubviews – this will ensure the views and subsequent sizes have been laid out in order for you to set the appropriate corner radius.
override func layoutSubviews() {
super.layoutSubviews()
profileBtn.layer.cornerRadius = profileBtn.frame.width / 2
}
Here is my solution:
override func layoutSubviews() {
super.layoutSubviews()
self.makeItCircle()
}
func makeItCircle() {
self.yourbutton.layer.masksToBounds = true
self.yourbutton.layer.cornerRadius = CGFloat(roundf(Float(self.yourbutton.frame.size.width/2.0)))
}
self.imageView.layer.masksToBounds = true //- in main
When you initialise the cell, the button does not have any frame. So self.profileBtn.layer.cornerRadius = CGFloat(roundf(Float(self.profileBtn.frame.size.width/2.0))) results in cornerRadius to be 0.
Since you are giving 40 constant width and height to the button, you can simply do this:
self.profileBtn.layer.cornerRadius = 20.0
Also make sure to set the button to clip the bounds:
self.profileBtn.clipsToBounds = true
if have a simple messageView in my app. on the messageView is a input-container with a textView. The textView should resize depending on its content.
It works so far, but every time while wrapping into the next line the text "jumps" for the first character, but reposition with the second character. it looks like:
the most of my code. i assume that it has something to do with the scroll capabilities of the textView(?)
private let container: UIView = {
let view = UIView()
view.backgroundColor = UIColor.white
view.layer.cornerRadius = 20
view.layer.masksToBounds = true
view.layer.borderColor = UIColor(red:0.90, green:0.90, blue:0.90, alpha:1.0).cgColor
view.layer.borderWidth = 0.5
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
private lazy var inputTV: UITextView = {
let tv = UITextView()
tv.translatesAutoresizingMaskIntoConstraints = false
tv.font = UIFont(name: "OpenSans-Light", size: 16)
tv.backgroundColor = .red
tv.delegate = self
tv.textContainer.lineBreakMode = .byWordWrapping
return tv
}()
override internal init(frame: CGRect) {
super.init(frame: CGRect.zero)
translatesAutoresizingMaskIntoConstraints = false
addSubview(container)
container.addSubview(inputTV)
container.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -16).isActive = true
container.leftAnchor.constraint(equalTo: leftAnchor , constant: 16).isActive = true
container.rightAnchor.constraint(equalTo: rightAnchor, constant: -16).isActive = true
containerHeightAnchor = container.heightAnchor.constraint(equalToConstant: 40)
containerHeightAnchor?.isActive = true
inputTV.leftAnchor.constraint(equalTo: uploadButton.rightAnchor).isActive = true
inputTV.centerYAnchor.constraint(equalTo: container.centerYAnchor).isActive = true
inputTV.rightAnchor.constraint(equalTo: sendButton.leftAnchor, constant: -5).isActive = true
textViewHeightAnchor = inputTV.heightAnchor.constraint(equalTo: container.heightAnchor)
textViewHeightAnchor?.isActive = true
}
internal func textViewDidChange(_ textView: UITextView) {
let contentHeight = textView.contentSize.height
containerHeightAnchor?.constant = max(contentHeight, 40)
inputTV.frame.size.height = contentHeight
}
I hope somebody could help. regards
thanks to #DonMag i do remove the heightAnchor from textViewand it does work for me. The new code is as following:
disable scrolling on the textView (isScrollEnabled = false)
private lazy var inputTV: UITextView = {
let tv = UITextView()
tv.translatesAutoresizingMaskIntoConstraints = false
tv.font = UIFont(name: "OpenSans-Light", size: 16)
tv.delegate = self
tv.isScrollEnabled = false
return tv
}()
remove heightAnchor (no inputTV.heightAnchor resp. my textViewHeightAnchor)
override internal init(frame: CGRect) {
super.init(frame: CGRect.zero)
translatesAutoresizingMaskIntoConstraints = false
addSubview(container)
container.addSubview(inputTV)
...
inputTV.leftAnchor.constraint(equalTo: uploadButton.rightAnchor).isActive = true
inputTV.centerYAnchor.constraint(equalTo: container.centerYAnchor).isActive = true
inputTV.rightAnchor.constraint(equalTo: sendButton.leftAnchor, constant: -5).isActive = true
}