swift: fix buttons animation - ios

When my app stat I want to show buttons animation. In storyboard my buttons have height = 50 and y = -50. I have this action in code.
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad()
{
super.viewDidLoad()
buttonAnimation()
}
func buttonAnimation()
{
self.buttonTopConstraint.constant += self.ButtonHeightConstraint.constant
UIView.animate(withDuration: 1.5, delay: 0.0, options: [], animations:
{
self.view.layoutIfNeeded()
}
)
}
Animation work well. But when app start my background imageView in storyboard and different imageView animated too. But I need to have only animation for button. How to fix it?

Don't call animation in viewDidLoad because at that time all views of your controller are going to set.
Use viewDidAppear to call animation method.
override func viewDidAppear()
{
buttonAnimation()
}

Use CGAffineTransform to animate your button. Remove y=-50 from storyboard and keep it at its desired place. Then call this function in viewWillAppear and viewDidAppear :
func animateButton() {
yourButton.transform = CGAffineTransform(translationX: yourButton.frame.origin.x, y: yourButton.frame.origin.y - 50)
UIView.animate(withDuration: 1.5, delay: 0.0, options: [], animations: {
self.yourButton.transform = .identity
}, completion: nil)
}

Related

Question about making UIView disappear from screen in Swift

I want to show the View for a few seconds and make it disappear from the screen.
If I wait 10 seconds on this screen
I want to make this view go off screen.
How can I animate the view slowly descending down?
code...
fileprivate func setupViewToAnimate(view: UIView) {
UIView.animate(withDuration: 2, delay: 10, options: .curveEaseInOut , animations: {
}) { _ in
}
}
You can use Timer to schedule the dismiss of your snackBar/notification view. it will be triggered after the completion of animation for showing your view. Please check the following code for the same.
private let keyWindow = UIApplication.shared.keyWindow!
let snackbarView = UIView()
fileprivate func setupViewToAnimate() {
let targetYPost = snackbarView.frame.origin.y
let dismissTimerLength = 3.0
snackbarView.frame.origin.y = keyWindow.frame.height
UIView.animate(withDuration: 0.4, animations: {
self.snackbarView.frame.origin.y = targetYPost
}, completion: {
_ in
Timer.scheduledTimer(timeInterval: TimeInterval(dismissTimerLength), target: self, selector: #selector(self.hideSnackbarView), userInfo: nil, repeats: false)
})
}
//After 3.4 sec snack bar will be hidden
#objc private func hideSnackbarView() {
UIView.animate(withDuration: 0.4, animations: {
self.snackbarView.frame.origin.y = self.keyWindow.frame.height
}, completion: {
_ in
self.snackbarView.isHidden = true
})
}
Output:-
I would suggest something like this:
private func setupViewToAnimate(view: UIView, bottomConstraint: NSLayoutConstraint) {
UIView.animate(withDuration: 2, delay: 10, options: .curveEaseInOut , animations: {
// Set the bottomConstraint to minus the height of the view so it gets entierly hidden
bottomConstraint.constant = -view.frame.height
// Tell the view to re-layout its subviews again so we have something to animate
self.view.layoutIfNeeded()
}) { _ in
// If the animation is completed, we can remove the view from the superview since we don't need it anymore
view.removeFromSuperview()
}
}

Keep animation when changing viewController

I'm trying to code an animation behind the profile picture, in order to encourage the user to click on it.
It's a circle which become bigger and smaller then.
override func layoutSubviews() {
super.layoutSubviews()
UIView.animate(withDuration: 1.0, delay: 0, options: [.repeat, .autoreverse], animations: {
self.bouncedView.transform = CGAffineTransform(scaleX:
1.3, y: 1.3)
}, completion: nil)
}
The problem is that, when I go to another viewController, the animation is stopped and the circle stays like you can the on the screen shot. Do you know how I could avoid this issue ?
Do a transform to .identity on ViewDidAppear. Something similar to below code:
class HomeController: UIViewController {
#IBOutlet weak var viewToAnimate: UIView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
self.viewToAnimate.transform = .identity
animateView()
}
func animateView(){
UIView.animate(withDuration: 1.0, delay: 0, options: [.repeat, .autoreverse], animations: {
self.viewToAnimate.transform = CGAffineTransform(scaleX:
1.3, y: 1.3)
}, completion: nil)
}
}
The problem as you might have guessed is that the UIView.animate is only called on the ViewDidLoad method and since we don't have access that code while returning to this ViewController from another, it is better to start the animation in the ViewWillAppear method.
If the same issue occurs when you switch between tabs, then please make a separate UIView subclass for the view that you want to animate and proceed as follows:
class AnimateView: UIView {
override func awakeFromNib() {
super.awakeFromNib()
}
override func willMove(toWindow newWindow: UIWindow?) {
super.willMove(toWindow: newWindow)
self.transform = .identity
animateView()
}
func animateView(){
UIView.animate(withDuration: 1.0, delay: 0, options: [.repeat, .autoreverse], animations: {
self.transform = CGAffineTransform(scaleX:
1.3, y: 1.3)
}, completion: nil)
}
}
Here, you have taken the animate function to the UIView object and whenever the view appears, the animation will be reset.
Well, I don't yet have a satisfying answer yet (at least that would satisfy me), but I would like to add at least some insight to which I was able to get by a bit of experimenting.
First of all, it seems that the animation gets ended when the viewController is not currently the one presented. In your case that means that the animation stops and finishes with the state, in which the view is 1.3 times bigger, and stops repeating. layoutSubviews gets called only when you present it the first time. At least the viewController.viewDidLayoutSubviews gets called only at the beginning and not when you go back (so layoutSubviews will not get executed when the view reappears) - so the animation won't get restarted.
I tried to move the animation to the viewDidAppear of a UIViewController - that did not work either, because stopping animation resulted in state in which the view was already scaled 1.3 times. Resetting the state to CGAffineTransform.identity before creating the animation did work.
Now as I said, this can serve you as a workaround on how to get it working, however, I think what you really are looking for is some hook that would tell your view (not viewController) that it got presented again. But the following minimal example can at least help others to take a look at it and not start from scratch.
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
let animator = UIViewPropertyAnimator(duration: 1, timingParameters: UICubicTimingParameters(animationCurve: .linear))
init(title: String) {
super.init(nibName: nil, bundle: nil)
self.title = title
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
let animatableView = UIView()
override func loadView() {
let view = UIView()
view.backgroundColor = .white
animatableView.frame = CGRect(x: 150, y: 400, width: 100, height: 100)
animatableView.backgroundColor = UIColor.magenta
view.addSubview(animatableView)
self.view = view
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.animatableView.transform = CGAffineTransform.identity
UIView.animate(withDuration: 1.0, delay: 0, options: [.repeat, .autoreverse], animations: {
self.animatableView.transform = CGAffineTransform(scaleX:
1.3, y: 1.3)
}, completion: nil)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
print("\(self.title) >> Lay out")
}
}
let tabBar = UITabBarController()
tabBar.viewControllers = [MyViewController(title: "first"), MyViewController(title: "second"), MyViewController(title: "third")]
tabBar.selectedIndex = 0
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = tabBar
EDIT
I added few lines to check if the self.animatableView.layer.animationKeys() contains any animations, it seems that switching tabs removes the animation from the view's layer - so you have to find a way to add the animation everytime the view reappears.
EDIT 2
So I would go with #SWAT's answer and use willMove(toWindow:) instead of layoutSubviews.

Why are the animated UI Labels in my code starting from within the view?

I'm a very new-to-code learner. I've been taking some online courses, and came across this project recently.
I basically copied the code as I saw it on the screen, as the project files weren't available to download.
The animation is supposed to bring the UILabels from outside the view and position them within the view.
What seems to happen however, is the labels are starting within the view screen and animate outward and beyond.
I've copied the project below. Any help is so very appreciated.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var helloWorld: UILabel!
#IBOutlet weak var secondLabel: UILabel!
#IBOutlet weak var hiddenLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
helloWorld.center.y -= view.bounds.height
secondLabel.center.y += view.bounds.height
hiddenLabel.alpha = 0.0
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
// Animate Hello World label
UIView.animateWithDuration(1.5, animations: {
self.helloWorld.center.y += self.view.bounds.height
}, completion: { finished in
self.secondAnimation()
})
// Animate background color change
UIView.animateWithDuration(2.0, delay: 1.5, options: [], animations: {
self.view.backgroundColor = UIColor.yellowColor()
}, completion:nil)
}
// Animate second Label
func secondAnimation() {
UIView.animateWithDuration(1.5, delay: 0.0, options: [], animations: { () -> Void in
self.secondLabel.center.y -= self.view.bounds.height
}, completion:nil)
}
func backgroundColor() {
UIView.animateWithDuration(2.5, animations: {
self.view.backgroundColor = UIColor.blackColor()
}, completion: nil)
UIView.animateWithDuration(1.0, delay: 1.5, options: [], animations: {
self.hiddenLabel.alpha = 1.0
}, completion:nil)
}
}
I would check hello world.center.y value in viewWillAppear, before you subtract the view.bounds.height. If center.y value is large and positive, then the subtraction might be setting the center.y value to be inside of the view. If this is the case, then you would want to change the subtraction in viewWillAppear to addition and then the addition in the viewDidAppear animation to subtraction.

Swift Completely restart the animation on button click

so what I wanna do is to restart the animation, when it's completed and I pressed the Restart button.
Here's what I've got right now.
It starts like that:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var fillView: UIView!
#IBAction func startAnimation(sender: AnyObject) {
self.animationBackground(fillView, animationTime: Float(15))
}
#IBAction func restartAnimation(sender: AnyObject) {
let layerRectangle = fillView.layer
restartLayer(layerRectangle)
}
func animationBackground(view:UIView,animationTime:Float){
UIView.animateWithDuration(NSTimeInterval(animationTime), delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations: {
view.transform = CGAffineTransformMakeTranslation(0.0, self.screenSize.height-67)
},completion:nil)
}
func restartLayer(layer:CALayer){
layer.beginTime = 0.0
layer.speed = 0.0
}
}
The restart function works only while animation is playing. But when it's completed that doesn't restart the animation from the beginning. Any tips on how to restart the animation on button click but only when it's already done?
I have already written above.. by the way:
func animationBackground(view:UIView,animationTime:Float){
self.fillView.transform = CGAffineTransformIdentity
UIView.animateWithDuration(NSTimeInterval(animationTime), delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations: {
view.transform = CGAffineTransformMakeTranslation(0.0, self.screenSize.height-67)
},completion:nil)
}
You have tranlsated the position and the frame (position). So once the animation has completed you have to restore the frame to its original position. You can restore the frame in completion block of the animation.
here the example:
UIView.animateWithDuration(1, animations: { () -> Void in
self.fillView.transform = CGAffineTransformMakeTranslation(0, 40)
}) { (end) -> Void in
self.fillView.transform = CGAffineTransformIdentity
}
You can you set transform to CGAffineTransformIdentity just before starting the animation.

how to animate an existing button in swift

I'd like to animate an existing UIButton in swift, the goal is to let get the attention of the user to this button when he taps the view controller. The animation is supposed to start from the top in the view controller and go to the initial position of the button.
I did the following scenario but it's not working:
import UIKit
class ViewController: UIViewController {
#IBOutlet var dismissButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
dismissButton = UIButton()
var tapGesture = UITapGestureRecognizer(target: self, action: Selector("handleTapGesture:"))
view.addGestureRecognizer(tapGesture)
}
func handleTapGesture(tapGesture: UITapGestureRecognizer) {
println("tap")
UIView.animateWithDuration(2.0, delay: 0.5, usingSpringWithDamping: 0.2, initialSpringVelocity: 0.0, options: nil, animations: {
self.dismissButton.center = CGPoint(x: 200, y: 90 + 200)
}, completion: nil)
}
}
Any suggestion?
Thank you!
Step 1: Take IBOutlet of your Top Constraint of button
Step 2: Update your Constraint's constant by using constraintName.constant property
Step 3: In animation block type self.view.layoutIfNeeded()
Here is an simple example: https://stackoverflow.com/a/26040569/3411787

Resources