UITableView self sizing stackView height Constraints issue - uitableview

I have a problem Self sizing Cell problem.
I want make layout like Feed Content with attached Image like twitter
Profile Image
-
Nickname Label
-
date Label
-
menuBarButton
Vertical - StackView
ㄴ
Content Label
ㄴ
FSPager CollectionView
Horizontal - StackView
Cell height is fine with image or none image cell, but debug console tells the broken Constraints
error is...
"<SnapKit.LayoutConstraint:0x281fab000#FeedCell.swift#207 UILabel:0x134433be0.top == UITableViewCellContentView:0x134437b90.top + 10.0>",
"<SnapKit.LayoutConstraint:0x281fab060#FeedCell.swift#208 UILabel:0x134433be0.height == 25.0>",
"<SnapKit.LayoutConstraint:0x281fab5a0#FeedCell.swift#236 UIStackView:0x1344370d0.top == UILabel:0x134433be0.bottom + 10.0>",
"<SnapKit.LayoutConstraint:0x281fab840#FeedCell.swift#243 UIStackView:0x134437a00.top == UIStackView:0x1344370d0.bottom + 10.0>",
"<SnapKit.LayoutConstraint:0x281fab960#FeedCell.swift#245 UIStackView:0x134437a00.bottom == UITableViewCellContentView:0x134437b90.bottom - 10.0>",
"<NSLayoutConstraint:0x2819478e0 'UISV-canvas-connection' UIStackView:0x1344370d0.top == UILabel:0x1344341a0.top (active)>",
"<NSLayoutConstraint:0x281947930 'UISV-canvas-connection' V:[UILabel:0x1344341a0]-(0)-| (active, names: '|':UIStackView:0x1344370d0 )>",
"<NSLayoutConstraint:0x2819777a0 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x134437b90.height == 44 (active)>"
and my Code is
profileImageView.snp.makeConstraints {
$0.leading.equalToSuperview().offset(10)
$0.top.equalTo(nicknameLabel)
$0.size.equalTo(50)
}
nicknameLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
nicknameLabel.snp.makeConstraints {
$0.top.equalToSuperview().offset(10)
$0.height.equalTo(25)
$0.leading.equalTo(profileImageView.snp.trailing).offset(10)
}
dateLabel.setContentHuggingPriority(.defaultLow, for: .horizontal)
dateLabel.snp.makeConstraints {
$0.centerY.equalTo(nicknameLabel)
$0.height.equalTo(25)
$0.leading.equalTo(nicknameLabel.snp.trailing)
}
menuBarButton.setContentHuggingPriority(.required, for: .horizontal)
menuBarButton.snp.makeConstraints {
$0.top.equalTo(nicknameLabel)
$0.leading.equalTo(dateLabel.snp.trailing).offset(10)
$0.trailing.equalToSuperview().inset(10)
}
imagePagerView.snp.makeConstraints {
$0.size.equalTo(250)
}
attachmentView.snp.makeConstraints {
$0.top.equalTo(nicknameLabel.snp.bottom).offset(10)
$0.leading.equalTo(nicknameLabel)
$0.trailing.equalTo(menuBarButton)
}
footerStackView.snp.makeConstraints {
$0.top.equalTo(attachmentView.snp.bottom).offset(10)
$0.leading.equalTo(nicknameLabel)
$0.bottom.equalToSuperview().inset(10)
}
and my tableView code is..
`
lazy var tableView = UITableView().then {
$0.register(FeedCell.self, forCellReuseIdentifier: FeedCell.identifier)
$0.refreshControl = refreshControl
$0.backgroundView = EmptyView(text: "피드가 존재하지 없습니다.")
$0.backgroundView?.isHidden = true
$0.backgroundColor = .white
$0.estimatedRowHeight = 300
$0.rowHeight = UITableView.automaticDimension
}
`
I read raywenderlich.com self sizing cell and WTFAutolayout is
enter link description here

Assuming your layout looks like you expect it to, this is a common issue with several UI elements - UIStackView being one of them.
Auto-layout is calculating the constraints, based on an expected cell height of 44.0. As it is modifying the elements layout, it throws errors / warnings.
Change this:
footerStackView.snp.makeConstraints {
$0.top.equalTo(attachmentView.snp.bottom).offset(10)
$0.leading.equalTo(nicknameLabel)
$0.bottom.equalToSuperview().inset(10)
}
to this:
footerStackView.snp.makeConstraints {
$0.top.equalTo(attachmentView.snp.bottom).offset(10)
$0.leading.equalTo(nicknameLabel)
$0.bottom.equalToSuperview().inset(10).priority(.required - 1)
}
and the messages should go away.

I changes the FSpagerView datasource's didSet code 1) and FSPagerView's parent Vertical StackView Constraints 2)
Add setNeedLayout() Before laytoutIfNeeded()
didSet {
pageControl.numberOfPages = attachments.count
if !attachments.isEmpty {
imagePagerView.isHidden = false
} else {
imagePagerView.isHidden = true
}
imagePagerView.reloadData()
setNeedsLayout() // <-- Add This line
layoutIfNeeded()
}
Change FSPagerView's parent Vertical StackView trailing Constraints
trailing.equalTo(imagePagerView)

Related

TableViewCell is constrained with Cartography and is not correct height

I am building a comments feature using a UITableView with dynamic cell heights. I am using the Cartography framework to set the constraints of the contents of each cell programmatically, as this table view is not setup within the storyboard.
I have a problem with the comment label overlapping cells below it.
Cells that have a short comment string are looking good, here's an example SS:
I have set tableView.estimatedRowHeight = 60, cells are clipsToBounds = false
and
func tableView(_: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
UITableView.automaticDimension
}
Here are the constraints that I have set using the Cartography framework for the different subviews of the Cell:
// User Image View
constrain(self, userImageView) {
$1.height == 36.0
$1.width == 36.0
$1.left == $0.left + 16.0
$1.top == $0.top + 12.0
$1.bottom == $0.bottom - 12.0 ~ UILayoutPriority(500)
}
// Comment Label
constrain(self, commentLabel, userImageView) {
$1.top == $2.top - 6.0
$1.right == $0.right - (18.0 + Geometry.likeButtonWidth)
$1.left == $2.right + Geometry.messageLabelLeftOffset
}
// Bottom view - ( comment_time / LikeCount / Reply )
constrain(self.contentView, messageLabel, bottomView, userImageView) { contentView, msgLabel, bottomView, userImageView in
bottomView.top == msgLabel.bottom
bottomView.right == msgLabel.rightMargin
bottomView.left == userImageView.right + Geometry.messageLabelLeftOffset
// contentView.bottom == bottomView.bottom // very tall cell is overlapping cells below with this line
// contentView.height == msgLabel.height + 20 // cell is twice as tall, leaving large empty gap, but not overlapping
}
The comment label has no bottom constraint set.
The bottonView has it's top set to the comment label's bottom, with no bottom constraint either. I figured this allows for the dynamic height to work.
Using neither of the commented out constraints above in bottomView's constrain, it is still overlapping. I understand the overlap is caused by clipsToBounds on the cell being set to false, so the cell's height is the problem.
This is how that looks:
How do I get the cells height to fit the content?
So I realized all constraints needed to use the cell's self.contentView instead of just self, I had previously tried this and it didn't render correctly but that was because not all vertical constraints were present as you could see above. So this is how it should have been:
// User Image View
constrain(self.contentView, userImageView) {
$1.height == 36.0
$1.width == 36.0
$1.left == $0.left + 16.0
$1.top == $0.top + 12.0
//$1.bottom == $0.bottom - 12.0 ~ UILayoutPriority(500) removed this
}
// Comment Label
constrain(self.contentView, commentLabel, userImageView, bottomView) {
$1.top == $2.top - 6.0
$1.right == $0.right - (18.0 + Geometry.likeButtonWidth)
$1.left == $2.right + Geometry.messageLabelLeftOffset
$1.bottom == $3.top
}
// Bottom view - ( comment_time / LikeCount / Reply )
constrain(self.contentView, messageLabel, bottomView, userImageView) { contentView, msgLabel, bottomView, userImageView in
bottomView.top == msgLabel.bottom
bottomView.right == msgLabel.rightMargin
bottomView.left == userImageView.right + Geometry.messageLabelLeftOffset
bottomView.bottom == contentView.bottom
}

Hiding element space without height constraint

This is a reuseable tableview cell. I want to hide 3. UILabel from the top, both the element and its space when it has no data.
I can't create a height constraint of it because I need to use it multiline depends on the text.
if release.post.isEmpty {
cell.label3.isHidden = true
} else {
cell.label3.isHidden = false
}
So how can I hide its space without a height constraint?
You can use the stack view and then hide the required objects i.e label or image. Remember to give the proper constraints to the stack view and select the required properties.
I fixed this by dynamically adding the constraint
cell.match.translatesAutoresizingMaskIntoConstraints = false
cell.match.constraints.forEach { (constraint) in
if constraint.firstAttribute == .height
{
constraint.constant = release.post.height(withConstrainedWidth: cell.match.frame.width, font: UIFont.preferredFont(forTextStyle: UIFont.TextStyle.subheadline)) + 15
}
}

UIStackView unsatisfiable autolayout constraints

I have a UIStackView defined in storyboard with the first button's height set to 70 and other one set to 45. I get this autolayout error:
[LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x280f614f0 UIButton:0x10641a120.height == 45 (active)>",
"<NSLayoutConstraint:0x280f60e60 UIButton:0x106418f80.height == 70 (active)>",
"<NSLayoutConstraint:0x280f604b0 'UISV-alignment' UIButton:0x10641a120.bottom == UIButton:0x106418f80.bottom (active)>",
"<NSLayoutConstraint:0x280f63cf0 'UISV-alignment' UIButton:0x10641a120.top == UIButton:0x106418f80.top (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x280f60e60 UIButton:0x106418f80.height == 70 (active)>
I understand the UIStackView is unable to accept different heights of UIButtons, is that correct and what is the way to have UIStackView accept different heights or widths of it's elements?
Something in your Stack View constraints is causing the problem.
Here is a valid layout:
With the Stack View properties:
The result before adding a third button via code:
And the result after adding a third button (height constraint of 60) via code:
No auto-layout warnings or errors.
The code (connected to Button 1), adds / removes Button 3 as an arranged subview of the stack view:
class TestViewController: UIViewController {
#IBOutlet var theStackView: UIStackView!
var thirdButton: UIButton = {
let b = UIButton()
b.translatesAutoresizingMaskIntoConstraints = false
b.setTitle("Button 3", for: .normal)
b.backgroundColor = .red
return b
}()
#IBAction func doAddThird(_ sender: Any) {
if theStackView.arrangedSubviews.count == 2 {
theStackView.addArrangedSubview(thirdButton)
} else {
if let v = theStackView.arrangedSubviews.last {
v.removeFromSuperview()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// finish initializing the third button
if let v = theStackView.arrangedSubviews.first as? UIButton {
thirdButton.titleLabel?.font = v.titleLabel?.font
}
NSLayoutConstraint.activate([
thirdButton.heightAnchor.constraint(equalToConstant: 60),
])
}
}
A Stackview will take on the dimensions of its components, if you don't give it height and width constraints. It looks like you are telling your stackView to be a particular height (either because you have a height constraint or two Y position constraints which imply a height). You cannot both tell the stackView to have a height and tell all of its arrangedSubviews to have a height unless those two values are exactly the same. So for instance if you tell the stack to be 150 high, and your buttons are 45 and 70 high then the one with the lowest content hugging priority loses and gets expanded to take up the extra 35 points of space that the stack view needs to be 150 points high.
Quick solutions:
Remove the hieght constraint on the stack view; it will now be as high as the sum of the high of its elements.
Add a blank UIView to the stack and give it content hugging of 1 and it will take up any extra space (this only works if your stack is bigger than its elements; if its too small you need to reduce the size of the arrangedSubviews instead).

Getting constraint error on updating the constraint in cellforRow

I have a UITableViewCell with collectionView inside it. constraint are pretty simple leading 0 , trailing 0 , top 10 and bottom 10 and height constraint of UICollection view is 160 . Connected an IBOutlet to height constraint. In cell for row I am updating the heigth constraint.
But getting error in debug
Code of cell for row is
func videoCell(for tableView:UITableView ,with indexPath:IndexPath , videos:[VideoCollectionViewCellConfigure] ) -> VideoTableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "VideoTableViewCell", for: indexPath) as! VideoTableViewCell
cell.videoConfigurators = videos
cell.collectionCellClickedBlock = self.videoCollectionCellClicked(_:_:)
if videos.count > 0 {
let video = videos[0]
cell.videoCollectionViewHeightConstraint.constant = video.height
}
cell.videoCollectionView.reloadData()
return cell
}
2018-04-16 12:42:10.815998+0530 CineBee[5280:74417] [LayoutConstraints]
Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or
constraints and fix it.
(
"<NSLayoutConstraint:0x6000006878a0 UICollectionView:0x7f86109c4c00.height
== 180 (active)>",
"<NSLayoutConstraint:0x604000885870 V:[UICollectionView:0x7f86109c4c00]-(10)-| (active, names: '|':UITableViewCellContentView:0x7f8615803ca0 )>",
"<NSLayoutConstraint:0x604000888de0 V:|-(10)-
[UICollectionView:0x7f86109c4c00] (active, names: '|':UITableViewCellContentView:0x7f8615803ca0 )>",
"<NSLayoutConstraint:0x60400088dde0 'UIView-Encapsulated-Layout-Height'
UITableViewCellContentView:0x7f8615803ca0.height == 180.5 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x6000006878a0
UICollectionView:0x7f86109c4c00.height == 180 (active)>
Try this
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
var defaultHeight = /*your default height*/
if videos.count > 0 {
let video = videos[0]
return video.height + 10 + 10 // video height + top padding + bottom padding
}
return defaultHeight
}
Also remove the collectionView height constraint from the Interface builder. Now you don't have to set constraint in the cellForItemAtIndexPath.
According to your error report, it seems like the case I was attempting to address in this SO question. If the conflicting constraints contain UIView-Encapsulated-Layout-Height it means the conflict arises during calculating dynamic height based on autolayout. Just lower one of the vertical constraints priority to 999 - if the code works, it's ok and you are good to go.

How to compare and change value of constrain programatically?

I have given constrains to UIImageView from xib. Now from code I can get all constrains of that image by code imageview.constraints . Now I want to change trailing , leading, top and bottom constrains constant values. I have loop through and my code is as below:
for constrain in imageview.constraints
{
if constrain.firstAttribute == NSLayoutAttribute.Trailing
{
constrain.constant = hPadding
}
if constrain.firstAttribute == NSLayoutAttribute.Leading
{
constrain.constant = hPadding
}
if constrain.firstAttribute == NSLayoutAttribute.Top
{
constrain.constant = vPadding
}
if constrain.firstAttribute == NSLayoutAttribute.Bottom
{
constrain.constant = vPadding
}
}
But non of the conditions are excited. Please help me to solve issue. What I am missing?

Resources