How to compare and change value of constrain programatically? - ios

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?

Related

UITableView self sizing stackView height Constraints issue

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)

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
}
}

Xamarin iOS - Fit scrollview's contents within the scrollview

This may be a fairly simple solution but none of the suggestions here work for me. I have a UIScrollView inside a UITableViewCell. I'm adding dynamic images from a list into the scrollview like this.
public void InitViews()
{
scrollViewThumbNails.ContentSize =
new SizeF((float)scrollViewThumbNails.Frame.Size.Width/listCount * listCount,
(float)scrollViewThumbNails.Frame.Size.Height);
for (int i = 0; i < listCount; i++)
{
var imageView = new UIImageView
{
Frame = new RectangleF((float)i * (float)scrollViewThumbNails.Frame.Size.Width / listCount, 0,
(float)scrollViewThumbNails.Frame.Size.Width / listCount, (float)scrollViewThumbNails.Frame.Size.Height),
UserInteractionEnabled = true,
ContentMode = UIViewContentMode.ScaleAspectFit
};
//call method to load the images
var index = i;
imageView.SetImage(
url: new NSUrl(allItems[i].AbsoluteUri),
placeholder: UIImage.FromFile("placeholder.png"),
completedBlock: (image, error, type, url) =>
{
//when download completes add it to the list
if (image != null)
{
allImages.Add(image);
scrollViewThumbNails.AddSubview(imageView);
}
});
}
I found a suggestion here which advices to set the content size of the scrollview, based on the total number of subviews in ViewDidAppear like this:
public override void ViewDidAppear (bool animated)
{
base.ViewDidAppear (animated);
CGRect contentRect = CGRect.Empty;
foreach(UIImageView view in scrollViewThumbNails.Subviews)
{
contentRect = CGRect.Union(contentRect,view.Frame);
}
scrollViewThumbNails.ContentSize = contentRect.Size;
}
The imageViews are still spaced out and do not start from the edge of the screen/scrollview as shown in my screen shot below. I would like for the first image to always be positioned at the origin of the scrollview and wouldn't want the spacing between each image. How can I adjust the scrollview based on the size of its contents?
Can someone show me what I'm missing? Thanks.
I found a solution here where I calculate the total height and width of all the views and assign that as the content size of the scroll view in ViewDidLayoutSubviews:
Solution
public override void ViewDidLayoutSubviews ()
{
base.ViewDidLayoutSubviews ();
try{
float scrollViewHeight = 0.0f;
float scrollViewWidth = 0.0f;
foreach(UIImageView view in scrollViewThumbnails.Subviews)
{
scrollViewWidth += (float)view.Frame.Size.Width;
scrollViewHeight += (float)view.Frame.Size.Height;
}
scrollViewThumbnails.ContentSize = new CGSize(scrollViewWidth, scrollViewHeight);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message+ex.StackTrace);
}
}

Updating "constant" on a NSLayoutConstraint fails with "Cannot assign to 'constant' in 'constraint'"

I've got the following swift code in my UIView subclass:
for constraint in self.constraints() {
if (constraint.firstItem as UIView == volumeOverlay && constraint.firstAttribute == NSLayoutAttribute.Top) ||
(constraint.secondItem as UIView == volumeOverlay && constraint.secondAttribute == NSLayoutAttribute.Top) {
NSLog ("Found constraint. Top is: \(constraint.constant)")
constraint.constant = 100
}
}
I'm getting a compile time error:
...TrackView.swift:25:37: Cannot assign to 'constant' in 'constraint'
The docs for NSLayoutConstraint say
Unlike the other properties, the constant may be modified after constraint creation. Setting the constant on an existing constraint performs much better than removing the constraint and adding a new one that's just like the old but for having a new constant.
So what am I doing wrong that I can't update the "constant" variable?
BTW, this is XCode 6.1.1 for iOS8
OK, i got it. I didn't realize that self.constraints() returns [AnyObject], so i had to convert the "constraint" to a NSLayoutConstraint.
There may be a more elegant way, but here's what I have now:
var y: CGFloat = // do some calculation....
for constraintObj in self.constraints() {
if constraintObj.isKindOfClass(NSLayoutConstraint) {
var constraint = constraintObj as NSLayoutConstraint
if (constraint.firstItem as UIView == volumeOverlay && constraint.firstAttribute == NSLayoutAttribute.Top) ||
(constraint.secondItem as UIView == volumeOverlay && constraint.secondAttribute == NSLayoutAttribute.Top) {
constraint.constant = y
break
}
}
}
Yay. it works.

Resources