For my intro page I have multiple images animations in sync with music to make a cool intro page. Problem is when I tap MENU and return to the app, all of the animations have completed. I have implemented a function to pause the music, yet need to do the same to the UIImage animations. FYI, it is the Mortal Kombat theme song synced with the characters it says in the song. I have not worked with animations before so looking at other solutions on SO and trying them has not worked.
// Mortal Kombat Good Guys
#IBOutlet weak var luiKangImage: UIImageView!
#IBOutlet weak var johnnyCageImage: UIImageView!
#IBOutlet weak var raidenImage: UIImageView!
#IBOutlet weak var kitanaImage: UIImageView!
#IBOutlet weak var sonyaImage: UIImageView!
#IBOutlet weak var nightWolfImage: UIImageView!
#IBOutlet weak var cassieCageImage: UIImageView!
#IBOutlet weak var kenshiTakahashiImage: UIImageView!
#IBOutlet weak var kungLaoImage: UIImageView!
#IBOutlet weak var jaxBriggsImage: UIImageView!
// Mortal Kombat Bad Guys
#IBOutlet weak var scorpionImage: UIImageView!
#IBOutlet weak var kanoImage: UIImageView!
#IBOutlet weak var reptileImage: UIImageView!
#IBOutlet weak var subZeroImage: UIImageView!
#IBOutlet weak var shangTsungImage: UIImageView!
#IBOutlet weak var goroImage: UIImageView!
#IBOutlet weak var shaoKhanImage: UIImageView!
#IBOutlet weak var tanyaImage: UIImageView!
#IBOutlet weak var quanChiImage: UIImageView!
#IBOutlet weak var mileenaImage: UIImageView!
var player: AVAudioPlayer?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//Set to execute function playSound
playSound()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
setInMotionImagePaths()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
setInMotionImageTimePaths()
}
func setInMotionImagePaths() {
// Mortal Kombat Good Guys Image Animation Paths
luiKangImage.center.x -= view.bounds.width
johnnyCageImage.center.y += view.bounds.width
raidenImage.center.y -= view.bounds.width
kitanaImage.center.x -= view.bounds.width
sonyaImage.center.x -= view.bounds.width
nightWolfImage.center.y += view.bounds.width
cassieCageImage.center.y -= view.bounds.width
kenshiTakahashiImage.center.y += view.bounds.width
kungLaoImage.center.x -= view.bounds.width
jaxBriggsImage.center.y -= view.bounds.width
// Mortal Kombat Bad Guys Image Animation Paths
scorpionImage.center.x += view.bounds.width
kanoImage.center.y += view.bounds.width
reptileImage.center.x += view.bounds.width
subZeroImage.center.x += view.bounds.width
shangTsungImage.center.y += view.bounds.width
goroImage.center.x += view.bounds.width
shaoKhanImage.center.y += view.bounds.width
tanyaImage.center.x += view.bounds.width
quanChiImage.center.y += view.bounds.width
mileenaImage.center.x += view.bounds.width
}
func setInMotionImageTimePaths() {
playSound()
// Mortal Kombat Good Guys Animation Time and Path
UIView.animate(withDuration: 5.0, delay: 0.6, options: [], animations: {
self.luiKangImage.center.x += self.view.bounds.width
}, completion: nil)
//DONT TOUCH TIME
UIView.animate(withDuration: 5.0, delay: 2.3, options: [], animations: {
self.johnnyCageImage.center.y -= self.view.bounds.width
}, completion: nil)
//DONT TOUCH TIME
UIView.animate(withDuration: 5.0, delay: 1.2, options: [], animations: {
self.raidenImage.center.y += self.view.bounds.width
}, completion: nil)
//DONT TOUCH TIME
UIView.animate(withDuration: 5.0, delay: 10.0, options: [], animations: {
self.kitanaImage.center.x += self.view.bounds.width
}, completion: nil)
UIView.animate(withDuration: 5.0, delay: 8.5, options: [], animations: {
self.sonyaImage.center.x += self.view.bounds.width
}, completion: nil)
//DONT TOUCH TIME
UIView.animate(withDuration: 5.0, delay: 11.0, options: [], animations: {
self.nightWolfImage.center.y -= self.view.bounds.width
}, completion: nil)
UIView.animate(withDuration: 5.0, delay: 12.0, options: [], animations: {
self.cassieCageImage.center.y += self.view.bounds.width
}, completion: nil)
UIView.animate(withDuration: 5.0, delay: 13.0, options: [], animations: {
self.kenshiTakahashiImage.center.y -= self.view.bounds.width
}, completion: nil)
UIView.animate(withDuration: 5.0, delay: 14.0, options: [], animations: {
self.kungLaoImage.center.x += self.view.bounds.width
}, completion: nil)
UIView.animate(withDuration: 5.0, delay: 15.0, options: [], animations: {
self.jaxBriggsImage.center.y += self.view.bounds.width
}, completion: nil)
// Mortal Kombat Bad Guys Animation Time and Path
UIView.animate(withDuration: 4.0, delay: 5.5, options: [], animations: {
self.scorpionImage.center.x -= self.view.bounds.width
}, completion: nil)
//DONT TOUCH TIME
UIView.animate(withDuration: 2.0, delay: 0.0, options: [], animations: {
self.kanoImage.center.y -= self.view.bounds.width
}, completion: nil)
//DONT TOUCH TIME
UIView.animate(withDuration: 5.0, delay: 11.0, options: [], animations: {
self.reptileImage.center.x -= self.view.bounds.width
}, completion: nil)
UIView.animate(withDuration: 4.0, delay: 7.0, options: [], animations: {
self.subZeroImage.center.x -= self.view.bounds.width
}, completion: nil)
//DONT TOUCH TIME
UIView.animate(withDuration: 5.0, delay: 10.3, options: [], animations: {
self.shangTsungImage.center.y -= self.view.bounds.width
}, completion: nil)
UIView.animate(withDuration: 5.0, delay: 11.5, options: [], animations: {
self.goroImage.center.x -= self.view.bounds.width
}, completion: nil)
UIView.animate(withDuration: 5.0, delay: 12.5, options: [], animations: {
self.shaoKhanImage.center.y -= self.view.bounds.width
}, completion: nil)
UIView.animate(withDuration: 5.0, delay: 13.5, options: [], animations: {
self.tanyaImage.center.x -= self.view.bounds.width
}, completion: nil)
UIView.animate(withDuration: 5.0, delay: 15.0, options: [], animations: {
self.quanChiImage.center.y -= self.view.bounds.width
}, completion: nil)
UIView.animate(withDuration: 5.0, delay: 14.5, options: [], animations: {
self.mileenaImage.center.x -= self.view.bounds.width
}, completion: nil)
}
func playSound() {
let url = Bundle.main.url(forResource: "MortalKombatSong", withExtension: "mp3")!
do {
player = try AVAudioPlayer(contentsOf: url)
guard let player = player else { return }
player.prepareToPlay()
player.play()
player.numberOfLoops = 0
} catch let error {
print(error.localizedDescription)
}
}
func stopSound() {
if player?.isPlaying ?? true {
player?.stop()
}
}
How I pause the music in App Delegate
import AVFoundation
var player: AVAudioPlayer?
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
player?.pause()
}
You could try out this answer. I don't believe it's possible to pause the actual view animations since they start as soon as they're passed to the animation block and you don't get any references to them
Related
I have created this animation for the first screen of the app(I'm not using Launchscreen) but when the animation ends I don't know how to pass to the next view controller. I've tired by segue but nothing :/
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var logoLSMini: UIImageView!
#IBOutlet weak var logoLSMini2: UIImageView!
#IBOutlet weak var logoLS: UIImageView!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.logoLSMini.alpha = 0.0
self.logoLSMini2.alpha = 0.0
self.logoLS.alpha = 0.0
self.logoLS.frame.origin.y = +100
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIView.animate(withDuration: 5, delay: 0.0, options: .curveEaseOut , animations: {
//FIRST EFFECT
self.logoLSMini.alpha = 1.0
self.logoLSMini.transform = CGAffineTransform(rotationAngle: 360)
self.logoLSMini.transform = CGAffineTransform(scaleX: 100, y: 100)
self.logoLS.alpha = 1.0
}, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
You simply need to perform the segue in the completion of UIView.animate. Just make sure you substitute whatever identifier you added to your segue in Storyboard for yourSegue.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIView.animate(withDuration: 5, delay: 0.0, options: .curveEaseOut , animations: {
//FIRST EFFECT
self.logoLSMini.alpha = 1.0
self.logoLSMini.transform = CGAffineTransform(rotationAngle: 360)
self.logoLSMini.transform = CGAffineTransform(scaleX: 100, y: 100)
self.logoLS.alpha = 1.0
}, completion: { _ in
self.performSegue(withIdentifier: "yourSegue", sender: nil)
})
}
Try to put the code of moving viewController in the completionHandler
UIView.animate(withDuration: 5, delay: 0.0, options: .curveEaseOut, animations: {
.....
}, completion: { (finished) in
if finished {
//Move to your next controller
DispatchQueue.main.async {
//Your code
}
}
})
I have UIImageView with moon image.
And moonImageContainerView is a superView for UIImageView
And I added swipe gesture to moonImageContainerView.
If swipe to up,
self.titleLabel.isHidden = false
UIView.animate(withDuration: 0.3, delay: 0.0, options: .curveEaseIn, animations: {
self.moonImageContainerView.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
self.moonImageContainerView.center.y = self.view.center.y
}, completion: nil)
you can see the result with gif
https://imgur.com/a/ftShkpO
And now, If I add just self.titleLabel.text = "MY TOPIA"
My animation is break.
self.titleLabel.text = "MY TOPIA" // Just added this line.
self.titleLabel.isHidden = false
UIView.animate(withDuration: 0.3, delay: 0.0, options: .curveEaseIn, animations: {
self.moonImageContainerView.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
self.moonImageContainerView.center.y = self.view.center.y
}, completion: nil)
You can see the result with gif
https://imgur.com/a/y57fPlA
Why does the moonImageContainerView disappear to the bottom?
How can I fix it?
Well, I use the rest setting and did not see your case.
import UIKit
class ImageViewController: UIViewController {
#IBOutlet var moonImageContainerView: UIView!
#IBOutlet var titleLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let pan = UISwipeGestureRecognizer.init(target: self, action: #selector(pp(_:)))
pan.direction = .up
moonImageContainerView.addGestureRecognizer(pan)
}
#objc func pp(_ pan: UISwipeGestureRecognizer){
self.titleLabel.text = "MY TOPIA"
self.titleLabel.isHidden = false
UIView.animate(withDuration: 0.3, delay: 0.0, options: .curveEaseIn, animations: {
self.moonImageContainerView.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
self.moonImageContainerView.center.y = self.view.center.y
}, completion: nil)
}
}
I have having a nightmare with a simple problem. On my app I have clouds moving in the background (currently from left to right).
However with the background they need to be right to left.
Eddited for the more of the page code below. Hopefully this will be easier to work out where I have gone wrong.
#IBOutlet var cloud1: UIImageView!
#IBOutlet var cloud2: UIImageView!
#IBOutlet var cloud3: UIImageView!
#IBOutlet var cloud4: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewWillAppear(_ animated: Bool) {
cloud1.alpha = 0.0
cloud2.alpha = 0.0
cloud3.alpha = 0.0
cloud4.alpha = 0.0
}
override func viewDidAppear(_ animated: Bool) {
UIView.animate(withDuration: 2.0, delay: 0.1,
options: [],
animations: {
self.cloud1.alpha = 1.0
}, completion: nil)
UIView.animate(withDuration: 2.0, delay: 0.1,
options: [],
animations: {
self.cloud2.alpha = 1.0
}, completion: nil)
UIView.animate(withDuration: 2.0, delay: 0.1,
options: [],
animations: {
self.cloud3.alpha = 1.0
}, completion: nil)
UIView.animate(withDuration: 2.0, delay: 0.1,
options: [],
animations: {
self.cloud4.alpha = 1.0
}, completion: nil)
animateTheClouds(cloud: cloud1)
animateTheClouds(cloud: cloud2)
animateTheClouds(cloud: cloud3)
animateTheClouds(cloud: cloud4)
}
func animateTheClouds(cloud : UIImageView) {
let cloudMovingSpeed = 60.0/view.frame.size.width
let duration = (view.frame.size.width - cloud.frame.origin.x) * cloudMovingSpeed
UIView.animate(withDuration: TimeInterval(duration), delay: 0.0, options: .curveLinear, animations: {
// Animate the origin to be off the left side of the screen.
cloud.frame.origin.x = cloud.frame.size.width
}, completion: {_ in
// Reset back to the right edge of the screen
cloud.frame.origin.x = -self.view.frame.size.width
self.animateTheClouds(cloud: cloud)
})
If you want to move them from right to left then you simply need to change the starting and ending x origin.
func animateTheClouds(cloud : UIImageView) {
let cloudMovingSpeed = 60.0/view.frame.size.width
let duration = (cloud.frame.origin.x + cloud.frame.size.width) * cloudMovingSpeed
UIView.animate(withDuration: TimeInterval(duration), delay: 0.0, options: .curveLinear, animations: {
// Animate the origin to be off the left side of the screen.
cloud.frame.origin.x = -cloud.frame.size.width
}, completion: {_ in
// Reset back to the right edge of the screen
cloud.frame.origin.x = self.view.frame.size.width
self.animateTheClouds(cloud: cloud)
})
Also make sure the initial x origin is set to self.view.frame.size.width.
Inside a UIViewController, I call a .xib file and present it over the current UIView.
// initiate the pop up ad view and display it
let popUpAdView = PopUpAdViewController(nibName: "PopUpAdView", bundle: nil)
popUpAdView.displayIntoSuperView(view)
There is a button inside that .xib file that should remove itself from the screen when it's touched. However it doesn't perform so.
What exactly am I missing?
class PopUpAdViewController: UIViewController {
#IBOutlet weak var popUpAdView: UIView!
#IBOutlet weak var closeButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// adjust the view size from the device's screen size
let screenSize = UIScreen.mainScreen().bounds
view.frame = CGRectMake(0, 64, screenSize.width, screenSize.height-64)
// cosmetically adjust the view itself
view.backgroundColor = UIColor.blackColor().colorWithAlphaComponent(0.6)
view.userInteractionEnabled = true
closeButton.userInteractionEnabled = true
// style the pop up ad view
popUpAdView.layer.cornerRadius = 5
popUpAdView.layer.shadowOpacity = 0.8
popUpAdView.layer.shadowOffset = CGSizeMake(0.0, 0.0)
closeButton.addTarget(self, action: #selector(removeOutOfSuperView), forControlEvents: .TouchUpInside)
}
func displayIntoSuperView(superView: UIView!) {
superView.addSubview(self.view)
// define the initial cosmetical values of the items
popUpAdView.transform = CGAffineTransformMakeScale(0.3, 0.3)
popUpAdView.alpha = 0.0
closeButton.alpha = 0.0
// animate...
UIView.animateWithDuration(0.8, delay: 0.0, options: .CurveEaseIn, animations: {
self.popUpAdView.alpha = 1.0
self.popUpAdView.transform = CGAffineTransformMakeScale(1.0, 1.0)
}) { (Bool) in
UIView.animateWithDuration(0.6, delay: 1.5, options: .CurveEaseIn, animations: {
self.closeButton.alpha = 1.0
}, completion: { (Bool) in
})
}
}
func removeOutOfSuperView() {
UIView.animateWithDuration(0.5, delay: 0.0, options: .CurveEaseIn, animations: {
self.closeButton.alpha = 0.0
self.popUpAdView.transform = CGAffineTransformMakeScale(0.1, 0.1)
}) { (finished) in
UIView.animateWithDuration(0.8, delay: 0.0, options: .CurveEaseIn, animations: {
self.view.alpha = 0.0
self.view.removeFromSuperview()
}, completion: nil)
}
}
#IBAction func closePopUpAdView(sender: AnyObject) {
print("Closing the pop up ad...")
removeOutOfSuperView()
}
}
Update
.xib structureL:
I have an IBOutlet Collection of buttons that I am trying to present on screen sequentially. They all start off screen fine, but as they animate in, I'd like each button to be presented on screen 0.05 seconds after the previous button. I can't figure out how to increment the delay in UIView.animateWithDuration. With the code below, they are all animating on screen at the same time.
//outlet collection
#IBOutlet var options: [UIButton]!
let increment = 0.25
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
for button in options {
button.center.y += view.bounds.height
}
}
override func viewDidLoad() {
super.viewDidLoad()
for button in options {
UIView.animateWithDuration(1.0, delay: increment, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.0, options: nil, animations: {
button.center.y -= self.view.bounds.height
self.increment + 0.05
}, completion: nil)
}
}
for button in options {
UIView.animateWithDuration(1.0, delay: increment, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.0, options: nil, animations: {
button.center.y -= self.view.bounds.height
}, completion: nil)
}
increment = increment + 0.05
}
Besides:
Change this
let increment = 0.25
To
var increment = 0.25
Increase the increment outside animation. Because animateWithDuration is an async method,it will return first. So,all your button have same delay.
#IBOutlet weak var button1: UIButton!
#IBOutlet weak var button2: UIButton!
#IBOutlet var options: [UIButton]!
let increment = 0.25
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
for button in options {
button.center.y += view.bounds.height
}
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
for button in options {
UIView.animateWithDuration(1.0, delay: increment, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.0, options: nil, animations: {
button.center.y -= self.view.bounds.height
}, completion: nil)
self.increment + 0.05
}
}
Also getting error "Cannot invoke '+=' with an argument list of type '(Double, FloatLiteralConvertible)'" when using += but it will take just +
Here is the way to achieve the required delay between animations:
var i! as UInt64;
i = 0
for button in options {
// your animation with delay
UIView.animateWithDuration(1.0, delay: (i *0.05), usingSpringWithDamping: 0.7, initialSpringVelocity: 0.0, options: nil, animations: {
button.center.y -= self.view.bounds.height
}, completion: nil)
})
++i
}