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
}
}
}
Related
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
}
})
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)
}
I have the below code that sets up a vertical scrollview My question is that the views within the view controllers do not respond accurately to swipe gestures. For example in this scrollview, bottomVc contains swipe gestures for left and right swipes. When this view controller is isolated in another project it works flawlessly, but the swipe gestures don't work when it is within the scrollview below. I was wondering how I could fix this issue so that my viewconttollers can handle swipe gestures while being in the scrollview. Also FYI, the code that controls the other view controllers are in separate files along with the swipe gestures.
The code:
import UIKit
class VerticalScrollViewController: UIViewController, SnapContainerViewControllerDelegate {
var topVc: UIViewController!
var middleVc: UIViewController!
var bottomVc: UIViewController!
var scrollView: UIScrollView!
class func verticalScrollVcWith(middleVc: UIViewController,
topVc: UIViewController?=nil,
bottomVc: UIViewController?=nil) -> VerticalScrollViewController {
let middleScrollVc = VerticalScrollViewController()
middleScrollVc.topVc = topVc
middleScrollVc.middleVc = middleVc
middleScrollVc.bottomVc = bottomVc
return middleScrollVc
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view:
setupScrollView()
}
func setupScrollView() {
scrollView = UIScrollView()
scrollView.isPagingEnabled = true
scrollView.showsVerticalScrollIndicator = false
scrollView.bounces = false
let view = (
x: self.view.bounds.origin.x,
y: self.view.bounds.origin.y,
width: self.view.bounds.width,
height: self.view.bounds.height
)
scrollView.frame = CGRect(x: view.x, y: view.y, width: view.width, height: view.height)
self.view.addSubview(scrollView)
let scrollWidth: CGFloat = view.width
var scrollHeight: CGFloat
if topVc != nil && bottomVc != nil {
scrollHeight = 3 * view.height
middleVc.view.frame = CGRect(x: 0, y: 0, width: view.width, height: view.height)
topVc.view.frame = CGRect(x: 0, y: view.height, width: view.width, height: view.height)
bottomVc.view.frame = CGRect(x: 0, y: 2 * view.height, width: view.width, height: view.height)
addChildViewController(topVc)
addChildViewController(middleVc)
addChildViewController(bottomVc)
scrollView.addSubview(topVc.view)
scrollView.addSubview(middleVc.view)
scrollView.addSubview(bottomVc.view)
topVc.didMove(toParentViewController: self)
middleVc.didMove(toParentViewController: self)
bottomVc.didMove(toParentViewController: self)
//scrollView.contentOffset.y = middleVc.view.frame.origin.y
scrollView.contentOffset.y = topVc.view.frame.origin.y
} else if topVc == nil {
scrollHeight = 2 * view.height
middleVc.view.frame = CGRect(x: 0, y: 0, width: view.width, height: view.height)
bottomVc.view.frame = CGRect(x: 0, y: view.height, width: view.width, height: view.height)
addChildViewController(middleVc)
addChildViewController(bottomVc)
scrollView.addSubview(middleVc.view)
scrollView.addSubview(bottomVc.view)
middleVc.didMove(toParentViewController: self)
bottomVc.didMove(toParentViewController: self)
scrollView.contentOffset.y = 0
} else if bottomVc == nil {
scrollHeight = 2 * view.height
topVc.view.frame = CGRect(x: 0, y: 0, width: view.width, height: view.height)
middleVc.view.frame = CGRect(x: 0, y: view.height, width: view.width, height: view.height)
addChildViewController(topVc)
addChildViewController(middleVc)
scrollView.addSubview(topVc.view)
scrollView.addSubview(middleVc.view)
topVc.didMove(toParentViewController: self)
middleVc.didMove(toParentViewController: self)
scrollView.contentOffset.y = middleVc.view.frame.origin.y
} else {
scrollHeight = view.height
middleVc.view.frame = CGRect(x: 0, y: 0, width: view.width, height: view.height)
addChildViewController(middleVc)
scrollView.addSubview(middleVc.view)
middleVc.didMove(toParentViewController: self)
}
scrollView.contentSize = CGSize(width: scrollWidth, height: scrollHeight)
scrollView.delaysContentTouches = false
}
func outerScrollViewShouldScroll() -> Bool {
if scrollView.contentOffset.y < middleVc.view.frame.origin.y || scrollView.contentOffset.y > middleVc.view.frame.origin.y {
return false
} else {
return true
}
}
}
Try adding the UIGestureRecognizerDelegate protocol to your class, then adding this method to return true :
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
Should work, unless your controller setup is somehow preventing proper touch recognition..
I want an image of a red circle to move from the top of the screen to the bottom, as though it were a meteor traveling across the screen. It does move, but it moves in a millisecond with no animation. How do I make it move smoothly like a meteor with a tail?
class ViewController: UIViewController {
let meteorImage = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.meteorImage.backgroundColor = .clear
self.meteorImage.frame = CGRect(x: 50, y: 0, width: 50, height: 50)
self.meteorImage.isHidden = false
self.meteorImage.image = #imageLiteral(resourceName: "success")
view.addSubview(self.meteorImage)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func buttonPressed(_ sender: UIButton) {
let totalHeight = Int(view.frame.height)
var currentHeight = 0
while currentHeight < totalHeight {
self.meteorImage.backgroundColor = .clear
self.meteorImage.frame = CGRect(x: 50, y: currentHeight, width: 50, height: 50)
self.meteorImage.isHidden = false
self.meteorImage.image = #imageLiteral(resourceName: "success")
view.addSubview(self.meteorImage)
currentHeight += 1
}
}
}
Animations have to be done with an animate call.
This is a simple way to do an animation.
UIView.animate(withDuration: 0.1, animations: {
// Apply Changes to frame.
})
Consider also: instead of doing the while loop simply set the final location of the meteor's frame and set the duration to the amount of time you want it to take.
let newFrame= CGRect(x: 50,
y: currentHeight,
width: 50,
height: 50)
UIView.animate(withDuration: 3.0, animations: {
// Apply Changes to frame.
self.meteorImage.frame = newFrame
})
If you want to make your animation more complicated you can look into keyframe animations on a UIView.