I have a button and a collection view (horizontal) inside keyboard input view. Auto Layout is used. The button is hidden by default using Leading Constraint set to -50. When user start using collection view and contentOffset.x of collection view is greater than 80, The button will show. The code is working fine but animation is not working.
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if self.collectionView.contentOffset.x > 80 {
UIView.animate(withDuration: 1, delay: 0, options: .curveEaseIn, animations: {
self.sideButtonLeadingConstraint.constant = 0
self.view.layoutIfNeeded()
}, completion: nil)
} else {
UIView.animate(withDuration: 1, delay: 0, options: .curveEaseIn, animations: {
self.sideButtonLeadingConstraint.constant = -50
self.view.layoutIfNeeded()
}, completion: nil)
}
}
}
First, you shouldn't change you constraints inside animations block. Second, scrollViewDidScroll method is called a large number of times, and you should set some limitations for calling animation code inside it. Try something like this:
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let needsShow = collectionView.contentOffset.x > 80 && sideButtonLeadingConstraint.constant != 0
let needsHide = collectionView.contentOffset.x <= 80 && sideButtonLeadingConstraint.constant != -50
if needsShow {
sideButtonLeadingConstraint.constant = 0
} else if needsHide {
sideButtonLeadingConstraint.constant = -50
}
if needsShow || needsHide {
UIView.animate(withDuration: 1, delay: 0, options: .curveEaseIn, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
}
}
Try as follows by updating the constant outside the animation block. It will do the update with animation effect.
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if self.collectionView.contentOffset.x > 80 {
self.sideButtonLeadingConstraint.constant = 0
UIView.animate(withDuration: 1, delay: 0, options: .curveEaseIn, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
else {
self.sideButtonLeadingConstraint.constant = -50
UIView.animate(withDuration: 1, delay: 0, options: .curveEaseIn, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
}}
Related
I have button on view, while the view is animating the button is disabled for some reason
Here's the code below , I have allowUserInteraction in options but it doesn't do anything
UIView.animate(withDuration: 0.5, delay: 0, options: .allowUserInteraction) {
view.frame.origin.y = 36
} completion: { (_) in
UIView.animate(withDuration: 0.5, delay: 2, options: .allowUserInteraction) {
view.frame.origin.y = -100
} completion: { (_) in
print("completed")
}
}
place the button outside of the view you are animating.
I have a UITextView, that is displaying text
When the text is not very large(i'm speaking about 100-200 words), the app is working just fine
If it has EXTREMLY large texts(around 10000 words), it keeps crashing
The code
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
check(scrollView)
let scrollPos = textView.contentOffset.y
if dragging { return }
if isAppearanceOpened { return }
if scrollPos > 0 {
UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseInOut, animations: {
if scrollPos <= self.contentOffset {
self.topView.alpha = 1
self.pageView.alpha = 1
self.topViewHeight.constant = 70
} else {
self.topView.alpha = 0
self.pageView.alpha = 0
self.topViewHeight.constant = 0
}
self.view.layoutIfNeeded()
}) { (_) in
if !decelerate { self.recheckEditorPosition() }
}
} else {
UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseInOut, animations: {
self.topView.alpha = 1
self.pageView.alpha = 1
self.topViewHeight.constant = 70
}) { (_) in
if !decelerate { self.recheckEditorPosition() }
}
}
}
The error
EXC_BAD_ACCESS (code=2, address=0x16d2a7ef0)
Console is clear, so i'm kind of confused
If i'm removing self.view.layoutIfNeedee(), the app works just fine, but the animation is absent
So, i got a result out of my research
The crash was caused by the textView contentInset.
When the left and right insets are 0, it's working well on large texts, and my guess is that the calculation of the textView alignements, fonts, and so on are quite time consuming, so the compiler just gave a error.
I tried to animate isHidden, it seems working ok, but if I mistakenly animate isHidden=false 5 times by setting yes to true 5 times, then sometimes I should animate isHidden=true 2 or more time to make my UIView visible!
Am I missing something?
if (yes)
{
UIView.animate(withDuration: 0.3, delay:0, animations: {
myLabel.isHidden=false
}
}
else
{
UIView.animate(withDuration: 0.3, delay:0, animations: {
myLabel.isHidden=true
}
}
You should not animate a view's "isHidden" parameter. You should animate its alpha.
if (yes)
{
UIView.animate(withDuration: 0.3, delay:0, animations: {
myLabel.alpha=1.0
}
}
else
{
UIView.animate(withDuration: 0.3, delay:0, animations: {
myLabel.alpha=0.0
}
}
-- UPDATE --
If you want to make the view hidden after the animation you can use this:
myLabel.isHidden=false
UIView.animateWithDuration(0.3, delay: 0.0, options: .CurveEaseOut, animations: {
myLabel.alpha=1.0
}, completion: { finished in
})
UIView.animateWithDuration(0.3, delay: 0.0, options: .CurveEaseOut, animations: {
myLabel.alpha=0.0
}, completion: { finished in
myLabel.isHidden=true
})
I think problem is that you are using linear animation on Bool type which has only 2 values (false = 0, true = 1) and any other values between that (it's pulse).
Try this:
if (yes)
{
myLabel.alpha = 0
myLabel.isHidden = false
UIView.animate(withDuration: 0.3, animations: {
myLabel.alpha = 1
})
}
else
{
UIView.animate(withDuration: 0.3, animations: {
myLabel.alpha = 0
}, completion: { (status) in
myLabel.isHidden = true
})
}
I am trying to toggle the visibility of a UILabel based on a Tap Gesture on an UIImageView. The code that performs the toggling is as follows:
func imageTapped(img: UIImageView) {
print(photoTitle.hidden)
if (photoTitle.hidden) {
UIView.animateWithDuration(0.5, delay: 0, options: UIViewAnimationOptions.CurveEaseInOut, animations: {
self.photoTitle.alpha = 1
}, completion: nil)
}
else {
UIView.animateWithDuration(0.5, delay: 0, options: UIViewAnimationOptions.CurveEaseInOut, animations: {
self.photoTitle.alpha = 0
}, completion: nil)
}
self.photoTitle.hidden = !self.photoTitle.hidden
}
The issue with this is that it seems to ignore the animation on the second tap i.e. to hide the UILabel again. It just becomes invisible instead of animating gradually. In the viewdDidLoad(), I initialize the photoTitle.hidden = true to be invisible initially.
Any glaring mistakes?
You need to move self.photoTitle.hidden = true into the completion block of your else condition
hidden doesn't work on this animation, you can instead of alpha
Just try to change the function like this
Swift 2
func imageTapped(img: UIImageView) {
print(photoTitle.hidden)
UIView.animateWithDuration(0.5, delay: 0, options: UIViewAnimationOptions.CurveEaseInOut, animations: {
self.photoTitle.alpha = self.photoTitle.alpha < 0.5 ? 1.0 : 0.0
}, completion: nil)
}
Swift 3, 4, 5
func imageTapped(img: UIImageView) {
print(photoTitle.hidden)
UIView.animate(withDuration: 0.5, delay: 0, options: UIView.AnimationOptions.curveEaseInOut, animations: {
self.photoTitle.alpha = self.photoTitle.alpha < 0.5 ? 1.0 : 0.0
}, completion: nil)
}
I have a collection view with images.I want to perform scrolling on button press . i have searched on google I found out only to set contentoffset but I want to scroll with animations(scroll should bounce if content is past the scrolling area).I have added a image to show you what exactly i have to do, in picture there is a collection view and little arrows on both sides are buttons.
#IBAction func moveScrollLeft(sender: UIButton) {
UIView.animateWithDuration(0.2, delay: 0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
self.imageCollectionView.contentOffset.x -= 50
}, completion: nil)
print(imageCollectionView.contentOffset.x)
}
#IBAction func moveScrollRight(sender: UIButton) {
UIView.animateWithDuration(0.2, delay: 0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
self.imageCollectionView.contentOffset.x += 50
}, completion: nil)
print(imageCollectionView.contentOffset.x)
}
Use UICollectionview's scrollToItemAtIndexPath(_:atScrollPosition:animated:)
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UICollectionView_class/#//apple_ref/occ/instm/UICollectionView/scrollToItemAtIndexPath:atScrollPosition:animated:
For example:
let indexPath = NSIndexPath(forRow: 3, inSection: 0)
imageCollectionView.scrollToItemAtIndexPath(indexPath, atScrollPosition: .CenteredHorizontally, animated: true)
Please check this code i have modify your code:
#IBAction func moveScrollLeft(sender: UIButton) {
self.imageCollectionView.contentOffset.x -= 50
UIView.animateWithDuration(0.2, delay: 0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
}, completion: nil)
self.view.layoutIfNeeded()
print(imageCollectionView.contentOffset.x)
}
#IBAction func moveScrollRight(sender: UIButton) {
self.imageCollectionView.contentOffset.x += 50
UIView.animateWithDuration(0.2, delay: 0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
print(imageCollectionView.contentOffset.x)
}