I am creating a game, this function is used as a power up to shrink the sprite. I want this function to execute for 5 seconds, and attach this 5 second timer to a label as a count down.
After the function ends, return the sprite to its original size.
The function just shrinks the node by setting the scale.
func shrinkShip() { spaceShip.scale(to: CGSize(width: 60, height: 40)) }
Sticking with SpriteKit I would just do the following
func shrinkShip(duration: Double) {
let originalSize = spaceShip.size
spaceShip.scale(to: CGSize(width: 60, height: 40))
for x in 0..<Int(duration) {
//increment the timer every 1 second for the duration
self.run(SKAction.wait(forDuration: 1 * Double(x))) {
self.timerLabel.text = String(Int(duration) - x)
}
}
self.run(SKAction.wait(forDuration: duration)) {
//return the spaceship to full size
self.spaceShip.scale(to: originalSize)
//hide the timer
self.timerLabel.text = ""
self.timerLabel.isHidden = true
}
}
You can animate the view like this:
UIView.animate(withDuration: 5, animations: {
self.spaceShip.scale(to: CGSize(width: 60, height: 40))
},
completion: { _ in
UIView.animate(withDuration: 2) {
self.spaceShip.transform = .identity // Reset your view
}
})
You can try something like this
UIView.animate(withDuration: 5, animations: {
self.spaceShip.transform = CGAffineTransform(scaleX: 60, y: 40)
},
completion: { _ in
UIView.animate(withDuration: 2) {
self.spaceShip.transform = .identity // Reset your view
}
})
Related
I'm having a hard time with animations and PanGestureRecognizers, I want a view that slides by dragging up or dawn with your finger. It has 4 parts
1 - 2 the height, width and bottom constraints need to be changed
2 - 3 the height is the maximum height and you can slide it in or out
3 - 4 the card is at its maximum height and you can swipe it down
here is an image for a better understanding
thanks!
this is how I would do it.
var theView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
// First create your view.
theView = UIView(frame: CGRect(x: 40, y: self.view.bounds.height - 120, width: self.view.frame.width - 80, height: 100))
// set its background color
theView.backgroundColor = UIColor.black
// Create the round corners
theView.layer.cornerRadius = 20
// And add it to the view
self.view.addSubview(theView)
// Create the UIPanGestureRecognizer and assign the function to the selector
let gS = UIPanGestureRecognizer(target: self, action: #selector(growShink(_:)))
// Enable user interactions on the view
theView.isUserInteractionEnabled = true
// Add the gesture recognizer to the view
theView.addGestureRecognizer(gS)
}
// The control value is used to determine wether the user swiped up or down
var controlValue:CGFloat = 0
var animating = false
// The function called when the user swipes
#objc func growShink(_ sender:UIPanGestureRecognizer){
let translation = sender.location(in: theView)
// Check the control value to see if it has been set
if controlValue != 0 && !animating{
// if it has been set check that the control value is greater than the new value recieved by the pan gesture
if translation.x < controlValue{
animating = true
// If it is, change the values of the frame using animate
UIView.animate(withDuration: 0.5, animations: {
self.theView.frame = CGRect(x: 0, y: self.view.bounds.height - 300, width: self.view.frame.width, height: 300)
}, completion: {finished in
UIView.animate(withDuration: 1.0, animations: {
self.theView.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height)
}, completion: {finished in self.animating = false; self.controlValue = 0})})
}else if translation.x > controlValue{
animating = true
// else, change the values of the frame back.
UIView.animate(withDuration: 0.5, animations: {
self.theView.frame = CGRect(x: 0, y: self.view.bounds.height - 300, width: self.view.frame.width, height: 300)
}, completion: {finished in
UIView.animate(withDuration: 1.0, animations: {
self.theView.frame = CGRect(x: 40, y: self.view.bounds.height - 120, width: self.view.frame.width - 80, height: 100)
}, completion: {finished in self.animating = false; self.controlValue = 0})})
}
}
// set the control value to the value received from the pan gesture
controlValue = translation.x
}
}
You can adjust the width and height values however you want.
I have an image that I want to move 10 separate times in a row. Each time I want it to do a check, as notated by the print statement of checkForStuff. I noticed that the loop starts and it times out while the first of the 10 moves occurs. Any thoughts on how to chain animations, or do I need to manually write each chain in the sequential closure?
#objc func moveBox(sender: UIButton!) {
print("Started")
let imageToMove = UIImageView()
imageToMove.frame = CGRect(x: 50, y: 50, width: 50, height: 50)
imageToMove.backgroundColor = .red
self.view.addSubview(imageToMove)
var counter = 0
var altitude = 100
while counter <= 10 {
print("looping")
UIView.animate(withDuration: 0.5, animations: {
imageToMove.frame = CGRect(x: 50, y: altitude, width: 50, height: 50)
}, completion: { finished in
counter += 1
altitude += 50
print("Check For Stuff")
})
}
}
In short, in should move 50 pixels down, execute the closure, move 50 pixels down again. and again, and again.
Try Recursion as suggested by #matt
func animate(view: UIView, altitude: Int, numberOfTime: Int ) {
if numberOfTime == 0 { return }
UIView.animate(withDuration: 0.5, animations: {
view.frame = CGRect(x: 50, y: altitude, width: 50, height: 50)
}, completion: { finished in
self.animate(view: view, altitude: altitude + 50, numberOfTime: numberOfTime - 1)
})
}
Use this as
#objc func moveBox(sender: UIButton!) {
print("Started")
let imageToMove = UIImageView()
imageToMove.frame = CGRect(x: 50, y: 50, width: 50, height: 50)
imageToMove.backgroundColor = .red
self.view.addSubview(imageToMove)
animate(view: imageToMove, altitude: 100, numberOfTime: 10)
}
In my swift app I've create these two functions:
func presentAlertView() {
backgroundLogOutView.frame = UIScreen.main.bounds
backgroundLogOutView.backgroundColor = currentTheme.backgroundColor.withAlphaComponent(0.5)
logOutAlertView.frame.size = CGSize(width: 200, height: 200)
logOutAlertView.center = CGPoint(x: view.frame.width / 2, y: view.frame.height / 2 - (navigationController?.navigationBar.frame.size.height)!)
view.addSubview(backgroundLogOutView)
backgroundLogOutView.addSubview(logOutAlertView)
logOutAlertView.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
logOutAlertView.alpha = 0
UIView.animate(withDuration: 0.4, animations: {
self.logOutAlertView.alpha = 1
self.logOutAlertView.transform = CGAffineTransform.identity
})
}
#objc func removeAlertView() {
UIView.animate(withDuration: 0.4, animations: {
self.logOutAlertView.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
self.logOutAlertView.alpha = 0
self.backgroundLogOutView.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
self.backgroundLogOutView.alpha = 0
}) {(success: Bool) in
self.logOutAlertView.removeFromSuperview()
self.backgroundLogOutView.removeFromSuperview()
self.tableView.deselectRow(at: [0,4], animated: true)
}
}
By tapping a button the function presentAlertView() is invoked and the views appear then when the function removeAlertView() is invoked the views disapeear. And here all ok.
But when I tapped the button the second time the views don't appear!
Why this happened?
Thank you
self.backgroundLogOutView.alpha = 0
is present in removeAlertView().
self.backgroundLogOutView.alpha = 1
may be missing in presentAlertView().
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Set the image view in the starting location
moveobj.frame = CGRect(x: 100, y: 100, width: /* some width */,
height: /* some height */)
UIView.animate(withDuration: /* some duration */, animations: {
// Move image view to new location
self.moveobj.frame = CGRect(x: 300, y: 300, width: self.moveobj.frame.width, height: self.moveobj.frame.height)
}) { (finished) in
// Animation completed; hide the image view
self.moveobj.isHidden = true
}
}
After animation is completed the image is hidden but I would like to display again this after 5 second in original position. How do I this?
Well all you'd have to do is chain a new animation to the finish that does the opposite of the previous animation (move back to the original location and unhide) - Courtesy of #Rob's comment.
It'd just be:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Set the image view in the starting location
let originalFrame = CGRect(x: 100, y: 100, width: /* some width */,
height: /* some height */)
moveobj.frame = originalFrame
UIView.animate(withDuration: /* some duration */, animations: {
// Move image view to new location
self.moveobj.frame = CGRect(x: 300, y: 300, width: self.moveobj.frame.width, height: self.moveobj.frame.height)
}) { (finished) in
// Animation completed; hide the image view
self.moveobj.isHidden = true
// Next animation
DispatchQueue.main.asyncAfter(deadline: .now() + 5){
self.moveobj.frame = originalFrame
self.moveobj.isHidden = false
}
}
}
I have a view which I am animating in such a way that it expands and moves to a new set of coordinates in the View Controller. I am doing this using Keyframes:
UIView.animateKeyframesWithDuration(0.5, delay: 0, options: .CalculationModeCubic, animations: {
UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration: 0.5) {
//Moving main View
tappedView.frame = CGRectMake((self.screenSize.width/2)-250, (self.screenSize.height/2)-250, 500, 500)
}
}, completion: nil)
The problem is that once the animation is completed, the view returns to its original position and size.
Is there a way to prevent this from happening in Swift?
This is an issue if one is using auto layout. In which case, the final coordinates must be put in the completion handler for it to work.
Swift 2
UIView.animateKeyframesWithDuration(0.5, delay: 0, options: .CalculationModeCubic, animations: {
UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration: 0.5) {
// Moving main View
tappedView.frame = CGRectMake((self.screenSize.width/2)-250, (self.screenSize.height/2)-250, 500, 500)
}
}, completion: {(finished: Bool) -> Void in
tappedView.frame = CGRectMake((self.screenSize.width/2)-250, (self.screenSize.height/2)-250, 500, 500)
})
Swift 3, 4, 5
UIView.animateKeyframes(withDuration: 0.5, delay: 0, options: .calculationModeCubic, animations: {
UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.5) {
// Moving main View
tappedView.frame = CGRect(x: (self.screenSize.width/2)-250, y: (self.screenSize.height/2)-250, width: 500, height: 500)
}
}, completion: {(finished: Bool) -> Void in
tappedView.frame = CGRect(x: (self.screenSize.width/2)-250, y: (self.screenSize.height/2)-250, width: 500, height: 500)
})