UIView's not being added to contentView? - ios

I'm trying to add some views in code to the contentView of a table view cell. However nothing shows up. I get an empty cell. Below is the code of me adding subviews to the content view of a custom cell.
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupViews()
}
func setupViews() {
//feedback_title setup
feedback_title.text = Constants.IndividualStudiesPage.FEEDBACK_TITLE
feedback_title.numberOfLines = 0
feedback_title.lineBreakMode = .ByTruncatingTail
feedback_title.preferredMaxLayoutWidth = standard_height_width
//feedback_box setup
feedback_box.text = Constants.IndividualStudiesPage.DEFAULT_FEEDBACK
// TODO: Change to infinte (0) number of lines. Height constraint interferes with this
feedback_box.numberOfLines = 2
feedback_box.preferredMaxLayoutWidth = standard_height_width
contentView.addSubview(feedback_title)
contentView.addSubview(feedback_box)
}
Here's the interesting thing. When I add some dummy view like an empty UILabel to the content view in the storyboard, then everything shows up including the views I added in the code. Here's a picture to show you what I mean:
But when I don't add any dummy view then nothing shows up...Why and how can I fix this? Thanks in advance.
EDIT:
The feedback_cell and feedback_box are instantiated like so at the top of the class
class FeedbackCell: UITableViewCell {
var feedback_title : UILabel = UILabel.newAutoLayoutView()
var feedback_box : UILabel = UILabel.newAutoLayoutView()
newAutoLayoutView is a PureLayout function that essentially just instantiates the labels to empty views. I then update the AutoLayout constraints, also using PureLayout in the updateConstraints function:
override func updateConstraints() {
if !did_update_constraints {
//prevent labels from being compressed below intrinsic height but also from taking too much height
NSLayoutConstraint.autoSetPriority(UILayoutPriorityRequired){
self.feedback_title.autoSetContentHuggingPriorityForAxis(.Vertical)
self.feedback_title.autoSetContentCompressionResistancePriorityForAxis(.Vertical)
self.feedback_box.autoSetContentHuggingPriorityForAxis(.Vertical)
self.feedback_box.autoSetContentCompressionResistancePriorityForAxis(.Vertical)
}
//height constraints
feedback_title.autoSetDimension(.Height, toSize: standard_height_width)
//feedback_title constraints
feedback_title.autoPinEdgeToSuperviewMargin(.Leading)
feedback_title.autoPinEdgeToSuperviewMargin(.Trailing, relation: NSLayoutRelation.LessThanOrEqual)
feedback_title.autoPinEdgeToSuperviewMargin(.Top)
//feedback_box constraints
feedback_box.autoPinEdge(.Top, toEdge: .Bottom, ofView: feedback_title, withOffset: 10, relation: .GreaterThanOrEqual)
feedback_box.autoPinEdgeToSuperviewMargin(.Leading)
feedback_box.autoPinEdgeToSuperviewMargin(.Trailing, relation: NSLayoutRelation.LessThanOrEqual)
feedback_box.autoPinEdgeToSuperviewMargin(.Bottom)
did_update_constraints = true
}
super.updateConstraints()
View hierarchy debugger:
Tableview code:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
switch (indexPath.row) {
case 0:
return setupDescriptionCell(tableView, indexPath: indexPath)
case 1:
return setupFeedbackCell(tableView, indexPath: indexPath)
case 2:
return setupStatsCell(tableView, indexPath: indexPath)
case 3:
return setupSurveyCell(tableView, indexPath: indexPath)
default:
return setupDefaultCell(tableView, indexPath: indexPath)
}
}
func setupFeedbackCell(tableView : UITableView, indexPath : NSIndexPath)->UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cell_IDs[1], forIndexPath: indexPath) as! FeedbackCell
//in the future download feedback in view did load and adjust here
return cell
}

Related

Custom TableViewCells do not show up

So I am pretty new to iOS development. I try to create everything programmatically so my Storyboard is empty. I'm currently trying to get a TableView with custom cells. The TableView is running and looking fine when I use the standard UITableViewCell. I created a very simple class called "GameCell". Basically, I want to create a cell here with multiple labels and maybe some extra UIObjects in the future (imageView etc.). For some reason, the custom cells do not show up.
Game cell class:
class GameCell: UITableViewCell {
var mainTextLabel = UILabel()
var sideTextLabel = UILabel()
func setLabel() {
self.mainTextLabel.text = "FirstLabel"
self.sideTextLabel.text = "SecondLabel"
}
}
Here the additional necessary code to get the number of rows and return the cells to the TableView which I have in my ViewController. self.lastGamesCount is just an Int here and definitely not zero when I print it.
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.lastGamesCount
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellID) as! GameCell
In my viewDidLoad() I register the cells like this:
tableView.register(GameCell.self, forCellReuseIdentifier: cellID)
When I run everything the Build is successful I can see the navigation bar of my App and all but the TableView is empty. I go back to the normal UITableViewCell and the cells are showing up again. What am I missing here? Any help is appreciated.
Thanks!
The problem is you need to set constraints for these labels
var mainTextLabel = UILabel()
var sideTextLabel = UILabel()
after you add them to the cell
class GameCell: UITableViewCell {
let mainTextLabel = UILabel()
let sideTextLabel = UILabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setLabel()
}
func setLabel() {
self.mainTextLabel.translatesAutoresizingMaskIntoConstraints = false
self.sideTextLabel.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(mainTextLabel)
self.contentView.addSubview(sideTextLabel)
NSLayoutConstraint.activate([
mainTextLabel.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor),
mainTextLabel.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor),
mainTextLabel.topAnchor.constraint(equalTo: self.contentView.topAnchor,constant:20),
sideTextLabel.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor),
sideTextLabel.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor),
sideTextLabel.topAnchor.constraint(equalTo: self.mainTextLabel.bottomAnchor,constant:20),
sideTextLabel.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor,constant:-20)
])
self.mainTextLabel.text = "FirstLabel"
self.sideTextLabel.text = "SecondLabel"
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

Swift 3 incorrect initial UITableViewCell row height

I am trying to programmatically design the layout of my custom UITableView cells. However, I get some strange behavior: despite setting the rowHeight, the first four rows are the default height, and then the rest are what I specified. This impacts the design of my cell, because I programmatically lay out labels based in part on the row height.
I initialize the tableview as follows:
func gameTableInit() {
gameTableView = UITableView()
gameTableView.delegate = self
gameTableView.dataSource = self
let navFrame = self.navigationController?.view.frame
gameTableView.frame = CGRect(x: navFrame!.minX, y: navFrame!.maxY, width: self.view.frame.width, height: self.view.frame.height - navFrame!.height - (self.tabBarController?.view.frame.height)!)
gameTableView.rowHeight = gameTableView.frame.height/4 //HEIGHT OF TABLEVIEW CELL
gameTableView.register(GameCell.self, forCellReuseIdentifier: "cell")
self.view.addSubview(gameTableView)
}
This is my cellForRowAtIndexPath function:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! GameCell
print("height of cell: \(cell.frame.height)")
var game = Game()
game.awayTeam = "CLE"
game.homeTeam = "GSW"
cell.setUIFromGame(g: game)
return cell
}
The print statement prints:
height of cell: 44.0
height of cell: 44.0
height of cell: 44.0
height of cell: 44.0
After some scrolling, it then prints the expected:
height of cell: 166.75
...
I have created a custom tableview cell called GameCell
class GameCell: UITableViewCell {
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
override func layoutSubviews() {
super.layoutSubviews()
positionUIElements()
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//on left
var awayTeamAbbrev = UILabel()
var awayTeamLogo: UIImage!
//on right
var homeTeamAbbrev = UILabel()
var homeTeamLogo: UIImage!
func positionUIElements() {
//away team abbreviation is centered on upper left half of cell
awayTeamAbbrev.frame = CGRect(x: self.frame.minX, y: self.frame.minY, width: self.frame.midX, height: self.frame.height/4)
awayTeamAbbrev.textAlignment = .center
awayTeamAbbrev.adjustsFontSizeToFitWidth = true
//awayTeamLogo
//home team abbreviation is centered on upper right half of cell
homeTeamAbbrev.frame = CGRect(x: self.frame.midX, y: self.frame.minY, width: self.frame.midX, height: self.frame.height/4)
homeTeamAbbrev.textAlignment = .center
homeTeamAbbrev.adjustsFontSizeToFitWidth = true
//homeTeamLogo
self.contentView.addSubview(awayTeamAbbrev)
self.contentView.addSubview(homeTeamAbbrev)
}
func setUIFromGame(g: Game) {
awayTeamAbbrev.text = g.awayTeam!
homeTeamAbbrev.text = g.homeTeam!
}
}
I have tried many of the suggested answers online like calling layoutIfNeeded, but that didn't work anywhere I tried it.
While accepted answer can help in some cases I would like to share some other ways to fix this issue:
Check Content Hugging Priority of your cell's components (especially if you're using stack views inside cell)
In your cell add:
override func didMoveToSuperview() {
super.didMoveToSuperview()
layoutIfNeeded()
I found a working solution: In my GameCell class, I overrided layout subviews:
override func layoutSubviews() {
super.layoutSubviews()
self.contentView.layoutIfNeeded()
positionUIElements()
self.awayTeamAbbrev.preferredMaxLayoutWidth = self.awayTeamAbbrev.frame.size.width
self.homeTeamAbbrev.preferredMaxLayoutWidth = self.homeTeamAbbrev.frame.size.width
}
44 is defailt height for UITableViewCell in UITableView, try to override func
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension;
}
or return exact value, in your case 166.75
I had the same problem using UITableViewDiffableDataSource. The tableView set the correct cell height after I attempt to scroll manually the content.
The tableView was inside on an UIViewController. The dataSource was applied on the viewDidLoad method.
I reloaded the tableView content using tableView.reloadData() inside the viewWillAppear and the problem was solve.

swift - rendering table view is slow

I'm have a table view and I'm using the tableView.dequeueReusableCellWithIdentifier to reuse the cells but still tableView is very slow.
and by slow, I mean it takes about 500 milliseconds to put 9 of my views in the tableView. and it's tested on apple A7 X64 processor so it must be pretty slower on older processors.
the reason that it's slow is because there are a few sub views and constraints.
but I've seen more complex tableCells with better performance, so there must be something I can do.
like caching a cell or something else??
any ideas?
sample code
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
tableView.registerNib(UINib(nibName: "ChatCell", bundle: nil), forCellReuseIdentifier: "ChatCell")
let cell = tableView.dequeueReusableCellWithIdentifier("ChatCell") as! ChatCell
return cell
}
the reason that it's slow is because there are a few sub views and constraints.
Personally, I don't suggest you use constraints in cell, especially when there're many subviews, it'll cost much CPU time and lead the scrolling lag. Instead, you can calculate manually based on cell frame.
And for more suggestion, i suggest you take time to read this post: Simple Strategies for Smooth Animation on the iPhone.
The call to registerNib is normally done only once in viewDidLoad, not every time you are asked for a cell in cellForRowAtIndexPath. Not sure how slow that call is, but it might be the reason for your slow response.
I think you are using effects (like shadow or round corners or etc) or having heavy calculations on UI
Edit: Code Sample added
//Add in your init func
tblView.registerClass(MSCustomVerticalListCell.self, forCellReuseIdentifier: NSStringFromClass(MSCustomVerticalListCell))
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tblView.dequeueReusableCellWithIdentifier(NSStringFromClass(MSCustomVerticalListCell), forIndexPath: indexPath) as! MSCustomVerticalListCell
//add data binding
cell.item = dataSource[indexPath.row]
return cell
}
Your data binding class (Data Model):
class MSC_VCItem
{
var Title:String!
var Action:String!
var SubTitle:String!
var Icon:String!
init(title:String!,subTitle:String!,icon:String!,action:String!)
{
self.Title = title
self.SubTitle = subTitle
self.Icon = icon
self.Action = action
}
}
And Finally you custom table cell:
class MSCustomVerticalListCell : UITableViewCell {
let padding = 5
let imageWidth = 50
var customImageView: UIImageView!
var customTitleLabel: UILabel!
var customSubtitleLabel: UILabel!
var item: MSC_VCItem? {
didSet {
if let it = item {
customTitleLabel.text = it.Title
customSubtitleLabel.text = it.SubTitle
UIImage.loadFromCacheOrURL(it.Icon, callback: { (image: UIImage) -> () in
self.customImageView.image = image
})
setNeedsLayout()
}
}
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.backgroundColor = UIColor.clearColor()
customTitleLabel = UILabel(frame: CGRectZero)
self.addSubview(customTitleLabel)
customSubtitleLabel = UILabel(frame: CGRectZero)
contentView.addSubview(customSubtitleLabel)
customImageView = UIImageView(frame: CGRectZero)
customImageView.image = UIImage(named: "default")
contentView.addSubview(customImageView)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
//Write your UI here like bg color or text color
}
}

Change TableViewCell height depending on label.text?

How can I change the cell height to make the UILabel fit? I am not using Auto-Layout in my project.
Also, the TableViewCell text is set in the cellForRowAtIndexPath.
Code:
var commentsArray: [String] = []
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:TableViewCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! TableViewCell;
if self.commentsArray.count > indexPath.row{
cell.commentsText.text = commentsArray[commentsArray.count - 1 - indexPath.row]
cell.commentsText.font = UIFont.systemFontOfSize(15.0)
}
return cell
}
Any ideas?
The above comment of using heightForRowAtIndexPath is acceptable when not using auto layout, (though I highly recommend getting used it).
Calculating the height of a label when it's populated with a certain string can be done using the methods described in this post: How to calculate UILabel height dynamically?
1) You can use a method to set constraints and you can modify the height of the cell in this way:
(Example with a label called book_title in a custom UITableViewCell)
fune setupComponents(){
self.addSubview(book_title)
book_title.topAnchor.constraintEqualToAnchor(self.topAnchor, constant: 5).active = true
book_title.leftAnchor.constraintEqualToAnchor(self.leftAnchor, constant: 10).active = true
book_title.rightAnchor.constraintEqualToAnchor(self.rightAnchor).active = true
book_title.widthAnchor.constraintEqualToAnchor(self.widthAnchor, constant: -20).active = true
book_title.heightAnchor.constraintEqualToConstant(90).active = true
book_title.numberOfLines = 0
book_title.lineBreakMode = NSLineBreakMode.ByWordWrapping
}
2) You have to call this method inside these:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
setupComponents()
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: .Subtitle, reuseIdentifier: "CellId")
setupComponents()
}

Using auto layout in UITableviewCell

I am trying to use auto sizing UITableView cells in swift with snapKit! relevant flags set on the UITableView are as follows:
self.rowHeight = UITableViewAutomaticDimension
self.estimatedRowHeight = 70.0
I have a UITextField defined in my customUITableviewCell class like:
var uidTextField: UITextField = UITextField()
and the initial setup of the text field in my custom UITableViewCell looks like this:
self.contentView.addSubview(uidTextField)
uidTextField.attributedPlaceholder = NSAttributedString(string: "Woo Hoo", attributes: [NSForegroundColorAttributeName:UIColor.lightGrayColor()])
uidTextField.textAlignment = NSTextAlignment.Left
uidTextField.font = UIFont.systemFontOfSize(19)
uidTextField.returnKeyType = UIReturnKeyType.Done
uidTextField.autocorrectionType = UITextAutocorrectionType.No
uidTextField.delegate = self
uidTextField.addTarget(self, action: "uidFieldChanged", forControlEvents: UIControlEvents.EditingChanged)
uidTextField.snp_makeConstraints { make in
make.left.equalTo(self.contentView).offset(10)
make.right.equalTo(self.contentView)
make.top.equalTo(self.contentView).offset(10)
make.bottom.equalTo(self.contentView).offset(10)
}
when I run the code it shows up cut off and gives me an error in the console that reads:
Warning once only: Detected a case where constraints ambiguously
suggest a height of zero for a tableview cell's content view. We're
considering the collapse unintentional and using standard height
instead.
Is there something wrong with my autoLayout constraints or is this an issue with UIControls and autosizing of UITableView cells?
In SnapKit (and Masonry) you have to use negative values to add a padding to the right or bottom of a view. You are using offset(10) on your bottom constraint which causes the effect that the bottom 10pt of your text field will get cut off.
To fix this you have to give your bottom constraint a negative offset:
uidTextField.snp_makeConstraints { make in
make.left.equalTo(self.contentView).offset(10)
make.right.equalTo(self.contentView)
make.top.equalTo(self.contentView).offset(10)
make.bottom.equalTo(self.contentView).offset(-10)
}
Or you could get the same constraints by doing this:
uidTextField.snp_makeConstraints { make in
make.edges.equalTo(contentView).inset(UIEdgeInsetsMake(10, 10, 10, 0))
}
When you are using the inset() way you have to use positive values for right and bottom inset.
I don't understand why SnapKit uses negative values for bottom and right. I think that's counterintuitive and a bit confusing.
EDIT: This is a little example that is working fine (I hardcoded a tableView with 3 custom cells that include a UITextField):
ViewController:
import UIKit
import SnapKit
class ViewController: UIViewController {
let tableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(tableView)
tableView.dataSource = self
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 70
tableView.registerClass(CustomTableViewCell.self, forCellReuseIdentifier: "CustomCell")
tableView.snp_makeConstraints { (make) -> Void in
make.edges.equalTo(view)
}
}
}
extension ViewController: UITableViewDataSource {
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CustomCell", forIndexPath: indexPath)
return cell
}
}
CustomTableViewCell:
import UIKit
class CustomTableViewCell: UITableViewCell {
let textField = UITextField()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
textField.attributedPlaceholder = NSAttributedString(string: "Woo Hoo", attributes: [NSForegroundColorAttributeName:UIColor.lightGrayColor()])
textField.textAlignment = NSTextAlignment.Left
textField.font = UIFont.systemFontOfSize(19)
textField.returnKeyType = UIReturnKeyType.Done
textField.autocorrectionType = UITextAutocorrectionType.No
contentView.addSubview(textField)
textField.snp_makeConstraints { (make) -> Void in
make.edges.equalTo(contentView).inset(UIEdgeInsetsMake(10, 10, 10, 0))
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

Resources