Here is my code:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
cloud.center.x -= view.bounds.width
cloud1.center.x -= view.bounds.width
cloud2.center.x -= view.bounds.width
cloud3.center.x -= view.bounds.width
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIView.animate(withDuration: 12) {
self.cloud.center.x += self.view.bounds.width*2
UIView.animate(withDuration: 12, delay: 9, options: [],
animations: {
self.cloud1.center.x += self.view.bounds.width*2
},
completion: nil
)
UIView.animate(withDuration: 12, delay: 18, options: [],
animations: {
self.cloud2.center.x += self.view.bounds.width*2
},
completion: nil
)
UIView.animate(withDuration: 12, delay: 22, options: [],
animations: {
self.cloud3.center.x += self.view.bounds.width*2
},
completion: nil
)
}
}
I have got a redo which resets everything in the page apart from my animation which just stays in one position. How can I reset the animation in order for it to re-play the animation again?
Your animation code moves your 4 cloud views to the right by self.view.bounds.width*2, by adding that amount to their center positions.
To undo that change, simply subtract the same amount.
Related
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)
}
}}
Title might be a bit ambiguous so i'll get straight to the point. Within my Xcode project I've implemented a simple animation in my initial ViewController, which functions fine, but after leaving the initial View, and moving to another scene, and then returning to the initial view, the animation fails to restart and I can't figure out why.
Here's the relevant code from my initial view
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
//cloud animation
let oldCenter = cloud.center
let newCenter = CGPoint(x: oldCenter.x - 800, y: oldCenter.y)
UIView.animate(withDuration: 30.0, delay: 0.0, options:
[.curveLinear, .repeat], animations: {
self.cloud.center = newCenter
}, completion: nil)
//second cloud animation
let oldCenter2 = cloud2.center
let newCenter2 = CGPoint(x: oldCenter2.x + 800, y: oldCenter2.y)
UIView.animate(withDuration: 15.0, delay: 0.0, options:
[.curveLinear, .repeat], animations: {
self.cloud2.center = newCenter2
}, completion: nil)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
}
You should reset positions of the clouds to their original centers in viewDidDisappear:
var originalCenter: CGPoint!
var originalCenter2: CGPoint!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
originalCenter = cloud.center
originalCenter2 = cloud2.center
...
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// restore positions
cloud.center = originalCenter
cloud2.center = originalCenter2
}
I have got this problem with breaking animations block in my app. I am animating an image inside UIScrollView (zooming-in, moving from left to right, zooming-out). Animation is being launched on tap with UITapGestureRecognizer.
The problem is that even though I can zoom-out the image when it is being animated, animations block continues and as a result I get the image off the iPhone screen.
I have tried to use removeAllAnimations() method on both scrollView.layer and view.layer but it seems not to work.
Here is my code:
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
setupGestureRecognizer()
}
func setupGestureRecognizer() {
let singleTap = UITapGestureRecognizer(target: self, action: #selector(self.handleSingleTap(recognizer:)))
singleTap.numberOfTapsRequired = 1
scrollView.addGestureRecognizer(singleTap)
}
func handleSingleTap(recognizer: UITapGestureRecognizer) {
animateRecipeImage()
}
// MARK: - Recipe Image Animation
func animateRecipeImage() {
let offset = CGPoint(x: 0, y: 0)
var middleOffset = CGPoint()
let endOffset = CGPoint(x: (imageView.bounds.width / 2), y: 0)
// To avoid animation in landscape mode we check for current device orientation
if (UIDeviceOrientationIsPortrait(UIDevice.current.orientation)) {
if (scrollView.zoomScale > scrollView.minimumZoomScale) {
scrollView.setZoomScale(scrollView.minimumZoomScale, animated: true)
// Remove all animations and update image contraints to fit the screen
self.scrollView.layer.removeAllAnimations()
self.view.layer.removeAllAnimations()
updateConstraintsForSize(size: view.bounds.size)
} else {
scrollView.setZoomScale(scrollView.maximumZoomScale, animated: true)
let screenWidth = screenSize.width
// Handle big screen sizes
if (screenWidth < self.imageView.bounds.width) {
middleOffset = CGPoint(x: ((self.imageView.bounds.width - screenWidth) * self.scrollView.zoomScale), y: 0)
} else {
middleOffset = CGPoint(x: (self.imageView.frame.width - screenWidth), y: 0)
}
// Start animating the image
UIView.animate(withDuration: 2, delay: 0.5, options: [.allowUserInteraction], animations: {
self.scrollView.setContentOffset(offset, animated: false)
}, completion: { _ in
UIView.animate(withDuration: 4, delay: 0.5, options: [.allowUserInteraction], animations: {
self.scrollView.setContentOffset(middleOffset, animated: false)
}, completion: { _ in
UIView.animate(withDuration: 1.0, delay: 0.2, usingSpringWithDamping: 0.4, initialSpringVelocity: 1.1, options: [.allowUserInteraction], animations: {
self.scrollView.setContentOffset(endOffset, animated: false)
}, completion: nil)
})
})
}
}
}
Any ideas how to break that animations block? The result I would like to achieve is break animation and zoom-out image.
I want to create a transition effect on my label so that it comes out from the left side of the view and centre itself in the middle of the screen. I am doing:
override func viewDidLoad() {
super.viewDidLoad()
label.center.x -= view.bounds.width
}
to place the label out of the view, but this is not working as the label is still present in the view. Please help.
Because label.center.x -= view.bounds.width will make it appear at the left of the view, so you can't see any effect on it.
You can use UIView.animateWithDuration to make animation effect:
UIView.animateWithDuration(0.5, delay: 0, options: [.CurveEaseOut], animations: {
label.center.x += view.bounds.width
self.view.layoutIfNeeded()
}, completion: nil)
And you should put them in your viewDidAppear instead of viewDidLoad, so everytime your view is resume, it will animate your label. Your code will look like this:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
label.center.x = view.center.x // Place it in the center x of the view.
label.center.x -= view.bounds.width // Place it on the left of the view with the width = the bounds'width of the view.
// animate it from the left to the right
UIView.animateWithDuration(0.5, delay: 0, options: [.CurveEaseOut], animations: {
label.center.x += view.bounds.width
self.view.layoutIfNeeded()
}, completion: nil)
}
This will make your label appear from the left to the right.
SWIFT 5
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
label.center.x = view.center.x // Place it in the center x of the view.
label.center.x -= view.bounds.width // Place it on the left of the view with the width = the bounds'width of the view.
// animate it from the left to the right
UIView.animate(withDuration: 1, delay: 0, options: [.curveLinear], animations: {
self.label.center.x += self.view.bounds.width
self.view.layoutIfNeeded()
}, completion: nil)
}
Here is a solution for Swift 3
UIView.animate(withDuration: 1.0) {
label = self.view.frame.size.width / 2
}
make sure that the label is initially positioned away from the screen.
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)
}