I'm attempting to animate a UIDatePicker as I would animate a UIView, by changing the height constraint constant and animating it. But in UIDatePicker this doesn't work, everything around it animates, but the height instantly changes.
private func increasePicker() {
pickerHeight.constant = 200
UIView.animateWithDuration(0.2) {
self.view.layoutIfNeeded()
}
}
What's is the right way to do this? Thanks!
You can try this code.
private func increasePicker()
{
pickerHeight.constant = 200
UIView.animateWithDuration(0.2) {
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
}
}
Related
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.
I want to change my collectionView position when pressing a button. I wrapped the collectionView and Button in a View and used UIView animate to make it move up. It can move up successfully, but when I am scrolling collectionView it's return back to its original position. If I tried many times, it became normal and didn't move back again.
How could i fix this issue? Thank you for any advice.
func openPreview() {
if !isPreviewOpened {
UIView.animate(withDuration: 0.3) {
self.previewView.frame.origin.y -= self.previewView.frame.height
self.previewButton.frame.origin.y -= self.previewView.frame.height
}
isPreviewOpened = true
updateUI()
}
func closePreview() {
if isPreviewOpened {
UIView.animate(withDuration: 0.3) {
self.previewView.frame.origin.y += self.previewView.frame.height
self.previewButton.frame.origin.y += self.previewView.frame.height
}
isPreviewOpened = false
updateUI()
}
}
After asking others, provide my solution to this question, hope it can help someone in the future.
This issue was happened because that the frame I changed conflicting with the auto layout I set earlier in storyboard. According to the document, I have to set translatesAutoresizingMaskIntoConstraints to true to track the frames.
By default, the autoresizing mask on a view gives rise to constraints that fully determine
the view's position. This allows the auto layout system to track the frames of views whose
layout is controlled manually (through -setFrame:, for example).
When you elect to position the view using auto layout by adding your own constraints,
you must set this property to NO. IB will do this for you.
So this is the answer.
func openPreview() {
if !isPreviewOpened {
previewView.translatesAutoresizingMaskIntoConstraints = true
UIView.animate(withDuration: 0.3) {
self.previewView.frame.origin.y = self.view.bounds.maxY - self.previewView.frame.height
self.previewButton.frame.origin.y = self.view.bounds.maxY - self.previewView.frame.height - self.previewButton.frame.height
}
}
}
I have a following collection view. When the user scrolls the view up, I want the collection view to go all the way up. Similarly, when it is scrolled down, I want it to come back to its original position.
I have a uiview and imageView above the collectionview.
Now, If this was a tableview, I would have used header. How do i achieve this behavious. I am out of Ideas.
Thanks in advance
UICollectionView is the sub-class of UIScrollView so you can achieve what you want is just by the scrollview delegate method and the constraints of your UIView and UIImageView upon the UICollectionView.
Here I am going to give you one example to achieve this with scrollview delegate.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView == collectionView{
if scrollView.contentOffset.y == 0{
UIView.animate(withDuration: 0.5) { () -> Void in
self.constTopTagHeight.constant = 175 // Your UIView and imageview constraints adjust.
self.view.layoutIfNeeded()
}
}
else{
UIView.animate(withDuration: 0.5) { () -> Void in
self.constTopTagHeight.constant = 0 // Your UIView and imageview constraints adjust.
self.view.layoutIfNeeded()
}
}
}
}
Hope this help you.
I'm facing a problem : I have a very simple custom UIView which groups one UITextField and one UILabel.
The UILabel is on the UITextField, and I want that the UILabel go up to 40px when user begins to edit the UITextField.
I'm using constraints to set the textfield's frame and the label's frame.
So I just set the delegate in my custom view with UITextFieldDelegate protocol, and implement the delegate method : textFieldDidBeginEditing, as bellow :
I'm just animating the constraint (center Y) of my label in the delegate method.
func textFieldDidBeginEditing(textField: UITextField) {
self.centerVerticallyLabelConstraint.constant = -40
UIView.animateWithDuration(0.5) {
self.textFieldLabel.layoutIfNeeded()
}
}
What happens is that the UILabel goes up correctly, but it goes up instantly. It ignores the duration of 0.5 in my animation. I don't know why :
I'm not animating something else at the same time, I only have this animation.
I haven't disable animations for UIView.
The delegate is set to my UITextfield.
Any idea?
Try calling layoutIfNeeded on your view instead of textview.
UIView.animate(withDuration: 0.5) {
self.view.layoutIfNeeded()
}
Hope this helps.
need to call self.view.layoutIfNeeded() not self.textFieldLabel.layoutIfNeeded()
Thanks
Try Below code to execute:
func textFieldDidBeginEditing(textField: UITextField) {
UIView.animate(withDuration: 0.5) {
self.textFieldLabel.frame.origin.y -= 40
}
}
For me, the issue seems to be because I was already on the main thread & I called the animation block inside a GCD main.async call.
ie I had
DispatchQueue.main.async {
<# update the constraint s#>
self.animatedViewsParent.setNeedsLayout()
animatedView.setNeedsLayout()
UIView.animate(withDuration: 2.0) {
self.animatedViewsParent.layoutIfNeeded()
}
}
The view animates moves but does not animate gradually over 2.0 seconds. Removing the outer DispatchQueue.main.async {} solves the problem.
TL;DR: When UITableView contentSize is correct?
To update my table height constraint I am using the following function:
func adjustHeightOfTableview()
{
self.myTableView.layoutIfNeeded()
let newHeight = self.myTableView.contentSize.height
self.tableHeightConstraint.constant = newHeight
self.view.setNeedsUpdateConstraints()
}
I tried these 2 solutions:
Calling adjustHeightOfTableview in viewDidLayoutSubviews
override func viewDidLayoutSubviews()
{
super.viewDidLayoutSubviews()
self.adjustHeightOfTableview()
}
Result: The table did not appear at all on the screen. I guess it is because content size is 0 when viewDidLayoutSubviews called first time. Since table height is set to 0, no cell will be created and viewDidLayoutSubviews is not called again
Calling adjustHeightOfTableview after table reload (I have extention for that)
extension UITableView
{
func reloadData(completion: ()->()) {
UIView.animateWithDuration(0, animations: { self.reloadData() })
{ _ in completion() }
}
}
//in my class:
self.relatedTableView.reloadData()
{
self.adjustHeightOfTableview()
}
Result: table was to heigh. Wrong contentSize??
Bottom line: How and where it is best to update UITableView height NSLayoutConstraint?
Found it! The answer was here: Height adjusted UITableView using Auto Layout
The height constraint needed to be low priority and it worked.
I've updated the constraint both after reloading table and in viewDidLayoutSubviews as described in the question