UserInteraction is disabled on animating view - ios

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.

Related

Animation completion block goes haywire after dismissing view

I have a detail viewcontroller that contains a repeating carousel with an animating image view like so:
func animateCarousel1(){
UIView.animate(withDuration: 1, delay: 3, options: .curveEaseInOut, animations:
//image animation
},completion: { (_) -> Void in
print("animation 1 complete")
self.animateCarousel2()
})
}
func animateCarousel2(){
UIView.animate(withDuration: 1, delay: 3, options: .curveEaseInOut, animations:
//image animation
},completion: { (_) -> Void in
print("animation 2 complete")
self.animateCarousel1()
})
}
Upon popping this view and returning to the parent view, I see that in the debug console, the functions are continuing to be called in the background, repeatedly and simultaneously, infinitely.
CPU usage also jumps to 90% in the simulator as well.
Is there some sort of deinit I need to do before popping the viewcontroller? I can't seem to wrap my head around this one.
The issue is that completion blocks are called even if the animation finishes. So there are a few possible solutions:
Check the finished parameter of the completion handlers:
func animateCarousel1() {
UIView.animate(withDuration: 1, delay: 3, options: .curveEaseInOut, animations: {
//image animation
}, completion: { finished in
print("animation 1 complete")
if finished { self.animateCarousel2() }
})
}
func animateCarousel2() {
UIView.animate(withDuration: 1, delay: 3, options: .curveEaseInOut, animations: {
//image animation
}, completion: { finished in
print("animation 2 complete")
if finished { self.animateCarousel1() }
})
}
Use a different animation technique that doesn't require the circular references between these animation routines, e.g.:
func animateCarousel() {
UIView.animateKeyframes(withDuration: 8, delay: 3, options: .repeat, animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.125) {
// animation 1
}
UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.125) {
// animation 2
}
}, completion: nil)
}
It's possible this solve the problem you can see this post link here
Use on deinit() controller method or viewDidDissapear(), try the following code
deinit {
print("deinit \(NSStringFromClass(self.classForCoder).components(separatedBy: ".").last ?? "")") // print viewcontroller deallocated
self.view_for_remove_animations.layer.removeAllAnimations() // Forze delete animations
}
OR
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
print("viewDidDisappear \(NSStringFromClass(self.classForCoder).components(separatedBy: ".").last ?? "")") // print viewcontroller deallocated
self.view_for_remove_animations.layer.removeAllAnimations() // Forze delete animations
}
Try it, and send feedback
The reason is in animation block, you are using strong reference of self which is why when you are poping this viewcontroller its reference count is still not 0, because of which ARC is not able to dealloc the reference of this view controller.
There is a concept of [weak self]. You can modify your code like below and then after poping you won't see those method calls in your debug console. Reason: weak self will not increase the reference count and ARC will be able to remove the reference of the object
func startAnimation() {
UIView.animate(withDuration: 0.4, animations: {[weak self] in
self?.animateView.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
print("startAnimation")
}, completion: {[weak self]
finished in
self?.startAnimation2()
})
}
func startAnimation2() {
UIView.animate(withDuration: 0.4, animations: {[weak self] in
self?.animateView.frame = CGRect(x: 100, y: 0, width: 100, height: 100)
print("startAnimation2")
}, completion: {[weak self]
finished in
self?.startAnimation()
})
}

How to do a sequence animation on a textview (swift3)

My code below is a button that when hit applies animation. Right now there are two animations. I would like to do a sequence the animations meaning have the 2nd animation not start until the first animation has completed.
var speed: CGFloat = 5.3 // speed in seconds
#IBAction func press(_ sender: Any) {
self.theTextView.resignFirstResponder()
UIView.animate(withDuration: TimeInterval(speed), animations: {
////1st action[
self.theTextView.contentOffset = .zero
self.theTextView.setContentOffset(.zero, animated: true)]
/////2nd action[
self.theTextView.contentOffset = CGPoint(x: 0, y: self.theTextView.contentSize.height)]
}, completion: nil)
}}
The easiest way of doing that would be using the completion block of the animate(withDuration:animations:completion:) method. The completion block is executed when the animation sequence ends. In your case, it would look like this :
UIView.animate(withDuration: 6.0, animations: {
// First animation goes here
self.theTextView.contentOffset = CGPoint.zero
}, completion: { (finished) in
// This completion block is called when the first animation is done.
// Make sure the animation was not interrupted/cancelled :
guard finished else {
return
}
UIView.animate(withDuration: 1.0, animations: {
// And the second animation goes here
self.theTextView.contentOffset = CGPoint(x: 0, y: self.theTextView.contentSize.height)
})
})
There is also convenient way to make sequence of concurrent animations by using animateKeyframes:
UIView.animateKeyframes(withDuration: 1, delay: 0, options: .calculationModeCubic, animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.3, animations: {
self.someView.alpha = 0.5
self.view.layoutIfNeeded()
})
//second animation
UIView.addKeyframe(withRelativeStartTime: 0.4, relativeDuration: 0.3, animations: {
self.someView.alpha = 1
self.view.layoutIfNeeded()
})
}, completion: { (finish) in
// Do something on animation complete
})

UIView.animate not animating bool value perfectly

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

iOS Label Visibility toggle not animating

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

how to Scroll(horizontally) in collection view on button press?

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

Resources