Why UITableViewCell dynamic view constraint not working properly? - ios

I am trying to create UIButton inside the UITableViewCell using following way
UITableView configuration :
self.tableView.estimatedRowHeight = 1
self.tableView.rowHeight = UITableView.automaticDimension
self.tableView.delegate = self
self.tableView.dataSource = self
UITableViewCell:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let uiTableViewCell = UITableViewCell(style: .default, reuseIdentifier: "More");
uiTableViewCell.contentView.backgroundColor = .yellow
let moreBtn = UIButton(type: .system)
moreBtn.setTitle("More", for: .normal)
moreBtn.setTitleColor(.white, for: .normal)
moreBtn.backgroundColor = UIColor.init(hexString: "#2A1771")
moreBtn.translatesAutoresizingMaskIntoConstraints = false
uiTableViewCell.contentView.addSubview(moreBtn)
let leading = NSLayoutConstraint(item: moreBtn, attribute: .leading, relatedBy: .equal, toItem: uiTableViewCell.contentView, attribute: .leading, multiplier: 1, constant: 16)
let trailing = NSLayoutConstraint(item: moreBtn, attribute: .trailing, relatedBy: .equal, toItem: uiTableViewCell.contentView, attribute: .trailing, multiplier: 1, constant: 16)
let top = NSLayoutConstraint(item: moreBtn, attribute: .top, relatedBy: .equal, toItem: uiTableViewCell.contentView, attribute: .top, multiplier: 1, constant: 16)
let bottom = NSLayoutConstraint(item: moreBtn, attribute: .bottom, relatedBy: .equal, toItem: uiTableViewCell.contentView, attribute: .bottom, multiplier: 1, constant: 16)
let height = NSLayoutConstraint(item: moreBtn, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 52)
uiTableViewCell.contentView.addConstraints([leading,top,trailing,bottom,height])
NSLayoutConstraint.activate([leading,top,trailing,bottom,height])
return uiTableViewCell
}
See this following image.. Why more button going outside from contentView

Your constraints are saying the the button's trailing edge should be 16 more than the content view trailing edge (The constant is +16) and similarly for the bottom edge.
You can either swap the item and toItem properties in those constraints or use -16 as the constant.
You will also have a problem with adding the button in cellForRow as cells are reused when the table view scrolls, resulting in multiple buttons being added to a cell. You should add the button in the cell itself, either in the initialiser or in awakeFromNib

Related

Adding a uiview on a uiview in uitableviewcell

I am passing a view to UITableViewCell from cellForForAtIndex method. I want cell to resize based upon the content of UIView but cell's height is not changing. I have tried to add constraint from coding when I add custom view.
UITableViewCell method:
func addContainerView(view:UIView){
vwContainer.addSubview(view)
view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint(item: view, attribute: .leading, relatedBy: .equal, toItem: vwContainer, attribute: .leading, multiplier: 1.0, constant: 0.0).isActive = true
NSLayoutConstraint(item: view, attribute: .trailing, relatedBy: .equal, toItem: vwContainer, attribute: .trailing, multiplier: 1.0, constant: 0.0).isActive = true
NSLayoutConstraint(item: view, attribute: .top, relatedBy: .equal, toItem: vwContainer, attribute: .top, multiplier: 1.0, constant: 0.0).isActive = true
NSLayoutConstraint(item: view, attribute: .bottom, relatedBy: .equal, toItem: vwContainer, attribute: .bottom, multiplier: 1.0, constant: 0.0).isActive = true
}
Above code is called from the following code in CellForRowAtIndex:
if let rowObj = objData?.itemsArray?[indexPath.row] as? TableViewDataFormat, let cell = tableView.dequeueReusableCell(withIdentifier: TableViewCell, for: indexPath) as? HiltiTableViewCell {
if let container = rowObj.containerview{
cell.addContainerView(view: container)
cell.lblTitle.text = rowObj.cellTitle
}
return cell
}
The objective here is to create a custom uitableview in a custom framework where I can pass a UIView and cell should resize based upon the content of the view passed in.
Edit 1:
Tried to add content over contentview like following:
cell.contentView.addSubview(container)
but still getting an overlapped view as follows:
It should have bee 3 different cells, first one with uiview and other 2 with image inside

Broken constraints in the cell

I need to show some UIView in my cell, but size of this UIView (backView) is different for each cell. So I need to change the height of this cell also.
I'm trying to change height and width of this backView in runtime, but all that I'm getting is a bunch of Autolayout errors like that:
[LayoutConstraints] Unable to simultaneously satisfy constraints. Will attempt to recover by breaking constraint...
I created SimpleTextCell class for this cell, and its init looks like that:
self.backView = UIView()
self.backView.translatesAutoresizingMaskIntoConstraints = false
self.backView.backgroundColor = UIColor.yellow
self.backView.layer.cornerRadius = 8.0
self.contentView.addSubview(backView)
let constraintBackViewTop = NSLayoutConstraint(item: backView, attribute: .top, relatedBy: .equal, toItem: contentView, attribute: .topMargin, multiplier: 1.0, constant: -5)
let constraintBackViewBottom = NSLayoutConstraint(item: backView, attribute: .bottom, relatedBy: .equal, toItem: contentView, attribute: .bottomMargin, multiplier: 1.0, constant: 5)
let constraintBackViewLeft = NSLayoutConstraint(item: backView, attribute: .left, relatedBy: .equal, toItem: contentView, attribute: .leftMargin, multiplier: 1.0, constant: 0)
constraintBackHeight = NSLayoutConstraint(item: backView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: superHeight)
constraintBackWidth = NSLayoutConstraint(item: backView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: superWidth)
self.contentView.addConstraints([constraintBackViewTop, constraintBackViewBottom, constraintBackViewLeft, constraintBackHeight, constraintBackWidth])
superHeight and superWidth are just some default values here.
my cellForRowAt function is like that:
let cell = tableView.dequeueReusableCell(withIdentifier: "simpleTextCell", for: indexPath) as! SimpleTextCell
cell.selectionStyle = .none
cell.backgroundColor = UIColor.red
cell.constraintBackHeight.constant = 100
cell.constraintBackWidth.constant = 200
cell.updateConstraints()
And it doesn't work at all. I'm having errors (like that I've mentioned earlier), and although the height of my cell is kinda right, my backView is invisible because width of backView.frame is zero (I don't have a clue why) and the height of backView is 100 (the same). It seems that my new constraints are ignored.
I've tried to set priority for these constraints in cellForRowAt:
cell.constraintBackHeight.priority = UILayoutPriority.init(rawValue: 999)
The result is run-time error.
I've tried to overcome this error by something like this:
cell.constraintBackHeight.isActive = false
cell.constraintBackHeight.priority = UILayoutPriority.init(rawValue: 999)
cell.constraintBackHeight.isActive = true
but it doesn't work either.
How can I fix this?
The problem is here
constraintBackHeight = NSLayoutConstraint(item: backView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: superHeight)
constraintBackWidth = NSLayoutConstraint(item: backView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: superWidth)
you're setting 2 height constraints , it may be you just copied to make as a width and forget to change it to .width

Add NSLayoutConstraint for a view inside a UITableCellView

I would like to have a UIButton centred inside my UITableCellView. I've added the necessary code in the tableView cellForRowAt function but I get the error :
The view hierarchy is not prepared for the constraint: When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled. Break on
-[UIView(UIConstraintBasedLayout) _viewHierarchyUnpreparedForConstraint:] to debug.
My code is the following :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
cell = UITableViewCell(
style: .default, reuseIdentifier: Id.settingButtonCellIdentifier)
cell!.backgroundColor = UIColor.clear
cell!.preservesSuperviewLayoutMargins = false
cell!.separatorInset = UIEdgeInsets.zero
let button : UIButton = UIButton(type: UIButtonType.custom) as UIButton
button.backgroundColor = UIColor.red
button.setTitle("Click Me !", for: UIControlState.normal)
cell!.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.addConstraint(NSLayoutConstraint(item: button, attribute: .leading, relatedBy: .equal, toItem: cell!, attribute: .leading, multiplier: 1, constant: 10))
button.addConstraint(NSLayoutConstraint(item: button, attribute: .trailing, relatedBy: .equal, toItem: cell!, attribute: .trailing, multiplier: 1, constant: 10))
button.addConstraint(NSLayoutConstraint(item: button, attribute: .top, relatedBy: .equal, toItem: cell!, attribute: .top, multiplier: 1, constant: 10))
button.addConstraint(NSLayoutConstraint(item: button, attribute: .bottom, relatedBy: .equal, toItem: cell!, attribute: .bottom, multiplier: 1, constant: 10))
}
return cell!
}
Any help would be appreciated !
Try this
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
cell = UITableViewCell(
style: .default, reuseIdentifier: Id.settingButtonCellIdentifier)
cell!.backgroundColor = UIColor.clear
cell!.preservesSuperviewLayoutMargins = false
cell!.separatorInset = UIEdgeInsets.zero
let button : UIButton = UIButton(type: UIButtonType.custom) as UIButton
button.backgroundColor = UIColor.red
button.setTitle("Click Me !", for: UIControlState.normal)
button.translatesAutoresizingMaskIntoConstraints = false
cell!.contentView.addSubview(button)
cell.contentView.addConstraint(NSLayoutConstraint(item: button, attribute: .leading, relatedBy: .equal, toItem: cell.contentView, attribute: .leading, multiplier: 1, constant: 10))
cell.contentView.addConstraint(NSLayoutConstraint(item: button, attribute: .trailing, relatedBy: .equal, toItem: cell.contentView, attribute: .trailing, multiplier: 1, constant: 10))
cell.contentView.addConstraint(NSLayoutConstraint(item: button, attribute: .top, relatedBy: .equal, toItem: cell.contentView, attribute: .top, multiplier: 1, constant: 10))
cell.contentView.addConstraint(NSLayoutConstraint(item: button, attribute: .bottom, relatedBy: .equal, toItem: cell.contentView, attribute: .bottom, multiplier: 1, constant: 10))
}
return cell!
}

UITableViewCell: Adding constraints programmatically from a UITextView to Content View?

I'm facing an issue when trying to programmatically add constraints to the UITableViewCell's contentView.
In cellForRowAt, I'm programmatically creating a UITextView, then trying to apply constraints and add it to the cell's contentView.
However, Im getting the crash and error:
The view hierarchy is not prepared for the constraint: <NSLayoutConstraint:0x1700951d0 UITableViewCellContentView:0x12de3e150.leading == UITextView:0x12e891400.leading + 8 (inactive)>
When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled.
My following code:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
// Dequeue the cell to load data
let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
if indexPath.section == 1
{
let textView: UITextView = UITextView()
textView.textColor = UIColor.black
textView.translatesAutoresizingMaskIntoConstraints = false
cell.addSubview(textView)
let leadingConstraint = NSLayoutConstraint(item: cell.contentView, attribute: NSLayoutAttribute.leading, relatedBy: NSLayoutRelation.equal, toItem: textView, attribute: NSLayoutAttribute.leading, multiplier: 1.0, constant: 8.0)
let trailingConstraint = NSLayoutConstraint(item: cell.contentView, attribute: NSLayoutAttribute.trailing, relatedBy: NSLayoutRelation.equal, toItem: textView, attribute: NSLayoutAttribute.trailing, multiplier: 1.0, constant: -8.0)
cell.contentView.addConstraint(leadingConstraint)
cell.contentView.addConstraint(trailingConstraint)
let topConstraint = NSLayoutConstraint(item: cell.contentView, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem: textView, attribute: NSLayoutAttribute.top, multiplier: 1.0, constant: 0)
let bottomConstraint = NSLayoutConstraint(item: cell.contentView, attribute: NSLayoutAttribute.bottom, relatedBy: NSLayoutRelation.equal, toItem: textView, attribute: NSLayoutAttribute.bottom, multiplier: 1.0, constant: 0)
cell.contentView.addConstraint(topConstraint)
cell.contentView.addConstraint(bottomConstraint)
}
return cell
}
I do see a lot of questions regarding the mentioned error, but have not found one regarding this error when applied to a UITableViewCell.
Can someone point me in the right direction?
Thanks.
Let add textView to cell.contentView (NOT cell). So let change to cell.contentView.addSubview(textView) and it's working!

Place UILabel center of parent

I am trying to put UILable at the center of each cell of UICollectionView.
Here is my code:
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! CollectionViewCell
// Configure the cell
cell.backgroundColor = UIColor.brownColor()
let title = UILabel(frame: CGRectMake(cell.bounds.midX, cell.bounds.midY, cell.bounds.size.width, 20)) //
title.text = "ddd"
title.textColor = UIColor.whiteColor()
// title.center = cell.center
cell.contentView.addSubview(title)
// let xCenterConstraint = NSLayoutConstraint(item: title, attribute: .CenterX, relatedBy: .Equal, toItem: cell, attribute: .CenterX, multiplier: 1, constant: 0)
// let yCenterConstraint = NSLayoutConstraint(item: title, attribute: .CenterY, relatedBy: .Equal, toItem: cell, attribute: .CenterY, multiplier: 1, constant: 0)
// let leadingConstraint1 = NSLayoutConstraint(item: title, attribute: .Leading, relatedBy: .Equal, toItem: cell, attribute: .Leading, multiplier: 1, constant: 20)
// let trailingConstraint1 = NSLayoutConstraint(item: title, attribute: .Trailing, relatedBy: .Equal, toItem: cell, attribute: .Trailing, multiplier: 1, constant: -20)
// cell.addConstraints([xCenterConstraint, yCenterConstraint, leadingConstraint1, trailingConstraint1])
// title.centerXAnchor.constraintEqualToAnchor(cell.centerXAnchor).active = true
// title.centerYAnchor.constraintEqualToAnchor(cell.centerYAnchor).active = true
return cell
}
The output:
Seems I need to change my code, so what am I missing?
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! CollectionViewCell
// Configure the cell
cell.backgroundColor = UIColor.brownColor()
//Editing Starts
let title = UILabel(frame: CGRectMake(cell.bounds.midX -(cell.bounds.size.width/2) , cell.bounds.midY - 10, cell.bounds.size.width, 20))
//Editing Ends
//
title.text = "ddd"
title.textColor = UIColor.whiteColor()
// title.center = cell.center
cell.contentView.addSubview(title)
// let xCenterConstraint = NSLayoutConstraint(item: title, attribute: .CenterX, relatedBy: .Equal, toItem: cell, attribute: .CenterX, multiplier: 1, constant: 0)
// let yCenterConstraint = NSLayoutConstraint(item: title, attribute: .CenterY, relatedBy: .Equal, toItem: cell, attribute: .CenterY, multiplier: 1, constant: 0)
// let leadingConstraint1 = NSLayoutConstraint(item: title, attribute: .Leading, relatedBy: .Equal, toItem: cell, attribute: .Leading, multiplier: 1, constant: 20)
// let trailingConstraint1 = NSLayoutConstraint(item: title, attribute: .Trailing, relatedBy: .Equal, toItem: cell, attribute: .Trailing, multiplier: 1, constant: -20)
// cell.addConstraints([xCenterConstraint, yCenterConstraint, leadingConstraint1, trailingConstraint1])
// title.centerXAnchor.constraintEqualToAnchor(cell.centerXAnchor).active = true
// title.centerYAnchor.constraintEqualToAnchor(cell.centerYAnchor).active = true
return cell
}
Try following code it may helps you
let title = UILabel(frame: CGRectMake(0, 0, cell.bounds.size.width, 20))
title.textAlignment=.Center
title.text = "ddd"
title.textColor = UIColor.whiteColor()
title.center = cell.contentView.center
cell.contentView.addSubview(title)

Resources