iOS: Expand cells when clicked on it - ios

I have a cell with a label and a button if I click the button the height of the cell should increase and an image should get visible.
I set up autolayout with a height constraint for the image that I change, when the button is clicked.
The constraint that I use on init is displayed correctly (e.g. if I set the constant to 0 or 200). But if I change the constant in the button action, nothing happens.
Here is the logic for the constraint
func toggleImageHeight(){
cellIsExpanded = !cellIsExpanded
self.imageHeightConstraint.constant = self.cellIsExpanded ? 200 : 0
UIView.animate(withDuration: 2) {
self.layoutIfNeeded()
}
}

Your code is not proper. please try this.
self.imageHeightConstraint.constant = self.cellIsExpanded ? 200 : 0
UIView.animate(withDuration: 2) {
self.layoutIfNeeded()
}
this will work.

Related

Animate the height of a UIScrollView based on it's content

My situation:
I have a horizontal ScrollView containing a StackView.
Inside this StackView there are some Views, that can be expanded/collapsed.
When I want to expand one of these Views, I first unhide some subViews in the View. After that I need to change the height of the ScrollView based on the new height of this View.
But this is not working...
I try this code:
UIView.animate(withDuration: 0.3) { [self] in
// Toggle hight of all subViews
stackView.arrangedSubviews.forEach { itemView in
guard let itemView = itemView as? MyView else { return }
itemView.toggleView()
}
// Now update the hight of the StackView
// But here the hight is always from the previous toggle
let height = self.stackView.arrangedSubviews.map {$0.frame.size.height}.max() ?? 0.0
print(height)
heightConstraint.constant = height
}
This code nicely animates, but always to the wrong height.
So the ScrollView animates to collapsed when it should be expanded and expanded when it should be collapsed.
Anyone with on idea how to solve this?
The problem is that, whatever you are doing here:
itemView.toggleView()
may have done something to change the height a view, but then you immediately call:
let height = self.stackView.arrangedSubviews.map {$0.frame.size.height}.max() ?? 0.0
before UIKit has updated the frames.
So, you can either track your own height property, or...
get the frame heights after the update - such as with:
DispatchQueue.main.async {
let height = self.stackView.arrangedSubviews.map {$0.frame.size.height}.max() ?? 0.0
print("h", height)
self.scrollHeightConstraint.constant = height
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
}

UILabel numberOfLines change with animation

I have a UILabel inside a UIStackView, and the stack view is inside another UIScrollView, I'm using auto layout. The label has single line (ie. numberOfLines equal to 1) and in a few cases I need to set it to multiline (ie. numberOfLines equal to 0) with an animation that expands it.
func expand() {
label.numberOfLines = 0
}
when I click expand:
messageView.expand()
UIView.animate(withDuration: 0.3) {
self.layoutIfNeeded()
}
However, when it expands, the label's frame isn't updated and I have to scroll (the scroll view) to make it fully visible. What could be wrong?
Thanks!
Have you changed the label's text?
Check if the autolayout is correct
Animation of UILabel doesn't perform within
UIView.animate(withDuration:).
This should work:
UIView.transition(with: label, duration: 0.5, options: .transitionCrossDissolve, animations: {
self.label.numberOfLines = 0
})
You can experiment with options and execution block.

Animate subview AutoLayout constraint changes

Numerous tutorials on animating AutoLayout constraints suggest to update constant property of a constraint and then call layoutIfNeeded() in animation block.
My situation is a bit tricky.
I have a view that houses 3 subviews. The height of this superview is not fixed - it is calculated as a sum of heights of its subviews.
On some event, I ask one of those 3 subviews to toggle its height (it changes between 0 and 30, i.e. I want to smoothly hide and show it).
The code is similar to this:
// In superview
subview1.isVisibleInContext = !subview1.isVisibleInContext
// In subview
class MySubview: UIView {
#IBOutlet var heightConstraint: NSLayoutConstraint!
var isVisibleInContext = false {
didSet {
updateHeight()
}
}
func toggleHeight() {
let newHeight = isVisibleInContext ? 30 : 0
layoutIfNeeded()
heightConstraint.constant = newHeight
UIView.animate(withDuration: 0.8) {
self.layoutIfNeeded()
}
}
}
Unfortunately, this does not work as I expect.
I can see the smooth change of the height of my subview, but the height of my superview is recalculated immediately after I set the new value for my subview height constraint.
I want to see the height of the superview gradually increasing/decreasing as on of its subviews grows or decreases.
Please someone point me in the right direction. Any help is appreciated.
Thanks a lot!
The animation block should be in the UIView that contains the 3 MySubviews. Inside the MySubview class you only update the height constraint's constant:
In Subview
func toggleHeight() {
let newHeight = isVisibleInContext ? 30 : 0
heightConstraint.constant = newHeight
}
Then in the UIView that contains the 3 MySubviews you animate the change:
In Superview
func toggleHeight(with subview: MySubview) {
subview.toggleHeight()
UIView.animate(withDuration: 0.8) {
self.view.layoutIfNeeded()
}
}
The first thing, that was incorrect in my approach was executing self.layoutIfNeeded(). Having investigated the issue I learned out that I had to execute it on the superivew, like this:
self.superview?.layoutIfNeeded()
That also didn't work out for a reason. The main issue in this case was that the view, which had 3 subviews inside was itself a subview of view. So to make it work I had to use the following code:
self.superview?.superview?.layoutIfNeeded()
Definitely not good that subview has to know the hierarchy of views, however it works.

How to make UILabel resize it's dimensions correctly when the view width changes?

I have a UILabel configured to automatically size for multi-lines depending on text length. The label is in a dynamically sizing UITableViewCell with constraints on all size.
The code moves UILabel origin to the right, thereby shrinking the UILabel width.
The label correctly resizes and wraps the text. In the process of positioning the origin back to it's original location, the height of the UILabel expands for 3 lines. But only 2 lines of text are required.
The reason the height expands to 3 lines is that in the process of returning the origin back to the original point with view animation, The text expands to 3 lines and reduces back to 2.
Is there a call or setting to make that will have the cell resize correctly? Or 2 is there a way to keep the cell from resizing when he origin changes. In particular. Is it possible to momentarily disable the UILabel multi-line option as the width shrinks and expands?
Here are screenshots showing the behavior.
Figure (1) Position of label in the UITableViewCell before changing its origin.
Figure (2) Origin of UILabel moves to the right and reduces the width of the label. Notice the second row has 2 lines and the label border tightly hugs the text.
Figure (3) Origin returns the position of the first image. Notice the UIlabel of the second row loosely hugs the text and does not return to it's original height.
#IBAction func editTable(_ sender: UIBarButtonItem) {
if isEditingDynamic {
let animate = UIViewPropertyAnimator(duration: 0.4, curve: .easeIn) {
for cell in self.tableView.visibleCells as! [CellResize] {
cell.lblDescription.frame.origin.x = 16
}
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
animate.addCompletion(){
(position:UIViewAnimatingPosition) in
for cell in self.tableView.visibleCells as! [CellResize] {
cell.leadingContraint.constant = 0
}
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
animate.startAnimation()
isEditingDynamic = false
} else {
let animate = UIViewPropertyAnimator(duration: 0.4, curve: .easeIn) {
for cell in self.tableView.visibleCells as! [CellResize] {
cell.lblDescription.frame.origin.x = 60
}
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
animate.addCompletion(){
(position:UIViewAnimatingPosition) in
for cell in self.tableView.visibleCells as! [CellResize] {
cell.leadingContraint.constant = 54
}
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
animate.startAnimation()
isEditingDynamic = true
}
}
Are you able to post some codes of how your table view cells and controller is set up? Typically, what I will do is I will create a single line UILabel and place it inside the cell's content view, constraining it Top, Left, Trailing & Leading with a height constraint >= current & numberOfLines = 0.
Then, in the tableview cell, I will set its estimatedHeight to be 100 and heightForRow to be UITableViewAutomaticDimension.
In this case, TableView will automatically handle the height of the cell based on the label's contents.

Increase uitablview height while run time to content entire content

I created an app with Swift 3 and Xcode 8.1. I have a UITableview and a UIView above it that shows and hides by clicking on a button in it. When the UIView appears, the last cell of UITableview does not show completely.
I use following code in button:
func filterShowHide ()
{
if !isShown
{
filterImage.image = UIImage(named: "ME-Filter-re")
self.filterView.isHidden = false
self.tableViewTop.constant = 0
// tableViewHeight.constant = tableViewHeight.constant * 1.5
isShown = true
}
else
{
filterImage.image = UIImage(named: "ME-Filter")
self.tableViewTop.constant = -(self.HeaderView.frame.height) + self.filterBTN.frame.size.height
self.filterView.isHidden = true
isShown = false
// tableViewHeight.constant = tableViewHeight.constant / 1.5
}
UIView.animate(withDuration: 0.25) {
self.view.layoutIfNeeded()
}
}
For more details here's the screenshot of:
Before clicking and After clicking
How can I show the last cell completely?
I think the problem is your tableview height is still the same and it gets pushed down as you update the top constraint.
You should set the table view bottom constraint to bottom of its super view or you can update tableView.contentInset.top with the values you are using for self.tableViewTop.constant

Resources