Change Swift constraint on if statement - ios

Here is my tableview row/cell:
there are constraints set in place - the imageview is below the label and the button is below the imageview.
here is my code:
if(row == 1) {
imageview.hidden = false
} else {
imageview.hidden = true
//how can i change the button constraint from below imageview to below label?

Adding and removing constraints is really bad example for that. I'll make your UI more complex.
Best way of solving these auto-layout problems is adding two constraints. One from imageView to button and second from imageView to label.
Now after setting these constraints, you need to set their priority levels. So, let's say button will be below the imageView first. In this case, you need to set imageView to button constraint's priority to something like 750 or UILayoutPriorityDefaultHigh and label to button constraint's priority to 250 or UILayoutPriorityDefaultLow.
Let's start creating a custom UITableViewCell
class YourTableViewCell: UITableViewCell {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var label: UILabel!
#IBOutlet weak var button: UIButton!
#IBOutlet weak var buttonToLabelConstraint: NSLayoutConstraint!
#IBOutlet weak var buttonToImageViewConstraint: NSLayoutConstraint!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func shouldHideImageView(hidden: Bool) {
if(hidden == false) {
buttonToLabelConstraint.priority = UILayoutPriorityDefaultLow
buttonToImageViewConstraint.priority = UILayoutPriorityDefaultHigh
imageView.hidden = true
} else {
buttonToLabelConstraint.priority = UILayoutPriorityDefaultHigh
buttonToImageViewConstraint.priority = UILayoutPriorityDefaultLow
imageView.hidden = false
}
self.contentView.layoutIfNeeded()
}
}
After that, in your class where tableView is placed implement a logic like that:
if(row == 1) {
cell.shouldHideImageView(true)
} else {
cell.shouldHideImageView(false)
}
You should be all set.

You can try using a StackView, when you tell something to be hidden, the imageView the stack view will adjust the StackView as if the imageView was never a part of the view and it is an easy work around to not have to worry about constraints.

You can create IBOutlet on constraint and then just simply change the value like this:
buttonConstraint.constant = newValue
But i suggest you create for this a tableView. In this case you code and logic, i think, will be more accurate.

you could to this instead of hiding.
Make an outlet from the heights constraint of the imageview, call it constraint for now.
Set constraint.constant = 0 // effectively same as hiding.
Set constraint.constant = NON_ZERO_VALUE // effectively same as show.
hope it helps!

I see a couple of options. The first is a little easier to implement but a little less flexible if you decide to change your layout later.
Make the button's constraint to be below the label. Keep a reference to this constraint (you can connect it to your code via storyboard just like you do with the button itself, if you're using storyboard). When the imageView is visible, set myConstraint.constant += myImageView.frame.height. When the imageView is hidden, set myConstraint.constant -= myImageView.frame.height. Afterwards, call view.setNeedsLayout to update your constraints.
Make two constraints: one for below the image, and one for below the label ("constraintToImage" and "constraintToLabel"). Hook them both up to your controller like in option 1, and call view.addConstraint(constraintToImage) and view.removeConstraint(constraintToLabel) when the image becomes visible (and the opposite for when it's hidden). Again, call view.setNeedsLayout after.

Related

Add/ expand animation will cause unwanted UIScrollView scrolling

I notice that, if I perform add/ expand animation within an UIScrollView, it will cause unwanted scrolling behavior, when the UIScrollView fill with enough content to become scroll-able.
As you can see in the following animation, initially, the add/ expand animation works just fine.
When we have added enough item till the UIScrollView scrollable, whenever a new item is added, and UIScrollView will first perform scroll down, and then scroll up again!
My expectation is that, the UIScrollView should remain static, when add/ expand animation is performed.
Here's the code which performs add/ expand animation.
Add/ expand animation
#IBAction func add(_ sender: Any) {
let customView = CustomView.instanceFromNib()
customView.hide()
stackView.addArrangedSubview(customView)
// Clear off horizontal swipe in animation caused by addArrangedSubview
stackView.superview?.layoutIfNeeded()
customView.show()
// Perform expand animation.
UIView.animate(withDuration: 1) {
self.stackView.superview?.layoutIfNeeded()
}
}
Here's the constraint setup of the UIScrollView & added custom view item
Constraint setup
Custom view
class CustomView: UIView {
private var zeroHeightConstraint: NSLayoutConstraint!
#IBOutlet weak var borderView: UIView!
#IBOutlet weak var stackView: UIStackView!
override func awakeFromNib() {
super.awakeFromNib()
borderView.layer.cornerRadius = stackView.frame.height / 2
borderView.layer.masksToBounds = true
borderView.layer.borderWidth = 1
zeroHeightConstraint = self.safeAreaLayoutGuide.heightAnchor.constraint(equalToConstant: 0)
zeroHeightConstraint.isActive = false
}
func hide() {
zeroHeightConstraint.isActive = true
}
func show() {
zeroHeightConstraint.isActive = false
}
}
Here's the complete source code
https://github.com/yccheok/add-expand-animation-in-scroll-view
Do you have any idea why such problem occur, and we can fix such? Thanks.
Because of the way stack views arrange their subviews, animation can be problematic.
One approach that you may find works better is to embed the stack view in a "container" view.
That way, you can use the .isHidden property when adding an arranged subview, and allow the animation to update the "container" view:
The "add view" function now becomes (I added a Bool so we can skip the animation on the initial add in viewDidLoad()):
func addCustomView(_ animated: Bool) {
let customView = CustomView.instanceFromNib()
stackView.addArrangedSubview(customView)
customView.isHidden = true
if animated {
DispatchQueue.main.async {
UIView.animate(withDuration: 1) {
customView.isHidden = false
}
}
} else {
customView.isHidden = false
}
}
And we can get rid of all of the hide() / show() and zeroHeightConstraint in the custom view class:
class CustomView: UIView {
#IBOutlet weak var borderView: UIView!
#IBOutlet weak var stackView: UIStackView!
override func awakeFromNib() {
super.awakeFromNib()
borderView.layer.masksToBounds = true
borderView.layer.borderWidth = 1
}
override func layoutSubviews() {
super.layoutSubviews()
borderView.layer.cornerRadius = borderView.bounds.height * 0.5
}
}
Since it's a bit difficult to clearly show everything here, I forked your project with the changes: https://github.com/DonMag/add-expand-animation-in-scroll-view
Edit
Another "quirk" of animating a stack view shows up when adding the first arranged subview (also, when removing the last one).
One way to get around that is to add an empty view as the first subview.
So, for this example, in viewDidLoad() before adding an instance of CustomView:
let v = UIView()
stackView.addArrangedSubview(v)
This will make the first arranged subview a zero-height view (so it won't be visible).
Then, if you're implementing removing custom views, just make sure you don't remove that first, empty view.
If your stack view has .spacing = 0 noting else is needed.
If your stack view has a non-zero spacing, add another line:
let v = UIView()
stackView.addArrangedSubview(v)
stackView.setCustomSpacing(0, after: v)
I did a little research on this and the consensus was to update the isHidden and alpha properties when inserting a view with animations.
In CustomView:
func hide() {
alpha = 0.0
isHidden = true
zeroHeightConstraint.isActive = true
}
func show() {
alpha = 1.0
isHidden = false
zeroHeightConstraint.isActive = false
}
In your view controller:
#IBAction func add(_ sender: Any) {
let customView = CustomView.instanceFromNib()
customView.hide()
stackView.addArrangedSubview(customView)
self.stackView.layoutIfNeeded()
UIView.animate(withDuration: 00.5) {
customView.show()
self.stackView.layoutIfNeeded()
}
}
Also, the constraints in your storyboard aren't totally correct. You are seeing a red constraint error because autolayout doesn't know the height of your stackView. You can give it a fake height and make sure that "Remove at build time" is checked.
Also, get rid of your scrollView contentView height constraint defined as View.height >= Frame Layout Guide.height. Autolayout doesn't need to know the height, it just needs to know how subviews inside of the contentView stack up to define its vertical content size.
Everything else looks pretty good.

Hide and remove all space of nested UIView swift

I am trying to hide and remove the space of the MainUIView.I tried to make the MainUIView heightConstarint to 0 . But it is not hiding the views inside them.
I want to hide all the view and labels inside the MainUIView.
hope you understand my problem.Thank you in advance
Here is my code
#IBOutlet weak var heightConstarint:NSLayoutConstraint!
//#IBOutlet weak var viewhide: UIView!
override func viewDidLoad() {
super.viewDidLoad()
heightConstarint.constant = 0
//self.viewhide.setNeedsUpdateConstraints()
self.view.layoutIfNeeded()
}
Updating Constraints will Never work in
override func viewDidLoad(){}
If you want to change constraints programmatically then you must put your code in
override fun viewWillLayoutSubviews(){}
So your code will look like
override func viewWillLayoutSubviews() {
clipToBounds = true
heightConstarint.constant = 0
//self.viewhide.setNeedsUpdateConstraints()
self.view.layoutIfNeeded()
}
I don't know updating constraints will work or not in viewDidLoad. But there are another constraints for MainUIView.
I think you should remove top or bottom space.

Make a UIView relayout itself when dependent constraint changes

I have a UIImageView and UIButton, UIButton is aligned to top of UIImageView. Now in code I am changing the top of UIImageView but the UIButton is not updated accordingly. I tried SetNeedsLayout, LayoutIfNeeded on UIButton but nothing works.
override func viewDidLayoutSubviews() {
profileImageView.frame.origin.y = arcView.bounds.height/1.8
editProfileButton.layer.setNeedsDisplay()
}
I have set the constraints in Storyboard. I would really appreciate any help here.
First: Are you sure that all your constraints (for both the UIImageview and UIButton) are added right?
Second: when working the constraints, you should change the origin.y of the UIImageview also by by a constraint, by modifying its constant's value:
Instead of directly changing profileImageView.frame.origin.y, you should change the constant of the constraint that tells what's the imageview origin.y (if the first point is applied, this constraint must be exist...); Add this constraint to the viewController as an IBOutlet and change value of its constant property (take a look at the comments in the code snippet, it's a part of the answer):
class ViewController: UIViewController {
// let's assume that this is the #IBOutlet of the constraint, I called it 'imageViewTopMargin':
#IBOutlet weak var imageViewTopMargin: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
// it's not necessary -for sure- to do this the in the viewDidLoad method
// but I'm doing this for demo purposes:
// let's say that you want to push the imageView to the bottom with extra 40 points:
imageViewTopMargin.constant += 40
}
}
Hope this helped.

Dynamic UITextView mislocation behavior

I am trying to have a textview similar to iPhone messages, where the textview initially has a constraint (height <= 100) and the scrollEnabled = false
This is a link to the project:
https://github.com/akawther/TextView
The text view increases in height based on the content size as in the image on the left until it reaches the height of 100, then the scrollEnabled is set to true. It works perfectly until I click the "send" button on the lower right where the textView should become empty and go back to the original height and scrollEnabled becomes false. The middle image shows what happens when I click the button. When I start typing the textview moves down as you see in the last image on the right.
I want to be able to click the button and eliminate the behavior shown on the middle image, how can I fix this?
import UIKit
class ViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var bottomConstraint: NSLayoutConstraint!
#IBOutlet weak var messageTextView: UITextView!
#IBOutlet weak var parent: UIView!
let messageTextViewMaxHeight: CGFloat = 100
override func viewDidLoad() {
super.viewDidLoad()
self.messageTextView.delegate = self
}
#IBAction func Reset(sender: AnyObject) {
messageTextView.text = ""
messageTextView.frame.size.height = messageTextView.contentSize.height
messageTextView.scrollEnabled = false
self.parent.layoutIfNeeded()
}
func textViewDidChange(textView: UITextView) {
if textView.frame.size.height >= self.messageTextViewMaxHeight {
textView.scrollEnabled = true
} else {
textView.scrollEnabled = false
textView.frame.size.height = textView.contentSize.height
}
}
}
You can replicate my issue by following these steps in the github project:
1. keep typing words and pressing enters until you start seeing the scroll
2. Click the button you will see that the textview goes up in the blue
container. This is the issue I want to eliminate!
Try bellow code :-
#IBAction func Reset(sender: AnyObject) {
messageTextView.scrollEnabled = false
messageTextView.text = ""
messageTextView.frame.size.height = messageTextView.contentSize.height
parent.frame.size.height = 20
self.view.layoutIfNeeded()
}
func textViewDidChange(textView: UITextView) {
if textView.contentSize.height >= self.messageTextViewMaxHeight {
textView.scrollEnabled = true
} else {
textView.frame.size.height = textView.contentSize.height
textView.scrollEnabled = false
}
}
Your issue is that the UITextView has conflicting properties:
Place on the screen
Size
The size being constrained will cause an issue when you need a resizable TextView. Also, when the TextView is resized, its location is being changed in this case.
Alternate method to approach the issue:
Try setting constraints to its location in relation to the bottom of the screen. When the Keyboard appears, you should move the TextView up with it. Also setting constraints on the height of a resizable TextView is bad practice unless you are planning on forcing the user to scroll.
Hope this helps.
If you are using auto layout, you should be updating to constraint instead of updating the textView.frame.Try create a IBOutlet for your textView heightConstraint then set the updated height to it.
IBOutlet weak var textViewHeightConstraint: NSLayoutConstraint!
//calculate the height and update the constant
textViewHeightConstraint.constant = textView.contentSize.height

Chaning height of UIView

I'm trying to change the height and width of UIView using Swift and it's not working. Tried both codes below, still no progress.
self.contentView.frame.size.height = 100
self.contentView.bounds.size.height = 100
contentView is a subview of the main view.
I guess you are using constraints. And when you use constraints you should change not the frame but constraints itself. It should looks like this:
In storyboard, pick height constraint for your view and create outlet to controller:
And then change it's constant value:
#IBOutlet weak var heightConstraint: NSLayoutConstraint!
func changeHeight() {
heightConstraint.constant = 200
// uncomment to perform changes with animation
// UIView.animateWithDuration(0.3) { () -> Void in
self.view.layoutIfNeeded()
// }
}

Resources