Example of code for animation:
func startAnimation(index: Int) {
var jumpImages = ["Jump_1","Jump_2","Jump_3","Jump_4","Jump_5","Jump_6","Jump_7","Jump_8","Jump_9","Jump_10"]
if index == 1 {
jumpImages = ["Jump_11","Jump_21","Jump_31","Jump_41","Jump_51","Jump_61","Jump_71","Jump_81","Jump_91","Jump_101"]
}
var images = [UIImage]()
for image in jumpImages{
images.append(UIImage(named: image)!)
}
self.imageView.frame.origin.y = self.imageView.frame.origin.y + 100.0
self.imageView.animationImages = images
self.imageView.animationDuration = 2.0
self.imageView.animationRepeatCount = 1
self.imageView.startAnimating()
}
startAnimation(index: 0)
...
startAnimation(index: 1)
NOTE: Both startAnimation calls are in the main thread but not in the same run loop.
In my case I want to change jumpImages to another set of images. I should cancel the previous animation and start the new one but it looks like I set the last image in jumpImages sequence instead.
How to solve this issue?
Okay, I think I'm understanding your problem now -- from your statement, "[...] but it looks like I set the last image in jumpImages sequence instead," I think you mean that when you call startAnimation(index: 1), you are seeing just the last frame of the index 0 animations, and none of the index 1 animations.
Assuming that is right, your problem here going to be a race condition.
When you call startAnimation() then second time with index 1, the first animation may or may not still be in progress.
The solution will be to call self.imageView.stopAnimating() before you change all the images and start the new animation. The best practice would be to check the imageView.isAnimating flag before you call it. Something like this:
func startAnimation(index: Int) {
var jumpImages = ["Jump_1","Jump_2","Jump_3","Jump_4","Jump_5","Jump_6","Jump_7","Jump_8","Jump_9","Jump_10"]
if index == 1 {
jumpImages = ["Jump_11","Jump_21","Jump_31","Jump_41","Jump_51","Jump_61","Jump_71","Jump_81","Jump_91","Jump_101"]
}
var images = [UIImage]()
for image in jumpImages{
images.append(UIImage(named: image)!)
}
if self.imageView.isAnimating {
self.imageView.stopAnimating()
}
self.imageView.frame.origin.y = self.imageView.frame.origin.y + 100.0
self.imageView.animationImages = images
self.imageView.animationDuration = 2.0
self.imageView.animationRepeatCount = 1
self.imageView.startAnimating()
}
startAnimation(index: 0)
...
startAnimation(index: 1)
Also, since you're in a function and not within a closure, you can remove all those references to self., which makes things a bit shorter:
func startAnimation(index: Int) {
var jumpImages = ["Jump_1","Jump_2","Jump_3","Jump_4","Jump_5","Jump_6","Jump_7","Jump_8","Jump_9","Jump_10"]
if index == 1 {
jumpImages = ["Jump_11","Jump_21","Jump_31","Jump_41","Jump_51","Jump_61","Jump_71","Jump_81","Jump_91","Jump_101"]
}
var images = [UIImage]()
for image in jumpImages{
images.append(UIImage(named: image)!)
}
if imageView.isAnimating {
imageView.stopAnimating()
}
imageView.frame.origin.y = imageView.frame.origin.y + 100.0
imageView.animationImages = images
imageView.animationDuration = 2.0
imageView.animationRepeatCount = 1
imageView.startAnimating()
}
startAnimation(index: 0)
...
startAnimation(index: 1)
I am using .animationImages for UIImageViews in my project (it's a game). When the user presses a hint button I programmatically create a variable number of UIImageViews that will appear on screen and play the same image sequence once. I need to remove the UIImageViews from the view when the animation has stopped but I can't figure out how to do this.
I've been through the swift documentation for both .animationImages and .startAnimating but it's literally one paragraph and one line, neither of which is in relation to a completion or finish notifications etc. Any help will be much appreciated.
private func hideAnswerButtonsHint() {
// Clear all of the answer boxes that aren't hint locked
resetAnswerBoxes()
var imageArray = [UIImage]()
var remainingLetters = [String]()
for imageCount in 1...8 {
let imageName = "green_smoke_puff_0\(imageCount)"
let image = UIImage(named: imageName)!
imageArray.append(image)
}
for answerBox in answerBoxArray where answerBox.hintLock == false {
if let letter = answerBoxDict[answerBox.tag] {
remainingLetters.append(letter)
}
}
for answerButton in answerButtonArray where answerButton.hintLock == false {
if let index = remainingLetters.index(of: answerButton.titleText) {
answerButton.decompress()
remainingLetters.remove(at: index)
} else {
let frame = answerButton.superview?.convert(answerButton.frame.origin, to: nil)
let imageView = UIImageView()
imageView.animationImages = imageArray
imageView.animationDuration = 0.5
imageView.animationRepeatCount = 1
imageView.frame = CGRect(x: frame!.x, y: frame!.y, width: answerButton.frame.width, height: answerButton.frame.height)
view.addSubview(imageView)
imageView.startAnimating()
answerButton.compress()
answerButton.hintLock = true
}
}
}
UIImageView is an NSObject so you can always use KVO to watch its properties:
var observation: NSKeyValueObservation?
override func viewDidLoad() {
super.viewDidLoad()
observation = imageView.observe(\.isAnimating) { [weak self] imageView, change in
if let value = change.newValue,
value == true {
imageView.removeFromSuperview()
self?.observation = nil
}
}
}
I’m new in programming with Xcode and Swift and it seems that I do not understand the language correct. I’m trying to create a button which changes the image when clicked and an animated popup of the menu. All works fine as long as I don’t change the image. As soon as I add the toggling of the button-image, the animation doesn’t do what it should do.
I have the impression that some parameters are changed when I use ‘setImage’.
#IBAction func buttonMoreClicked(_ sender: UIButton) {
toggleButton(button: sender, imageOn: UIImage(named: "iconClose.png")!, imageOff: UIImage(named: "iconAdd.png")!)
if (boolFirstClickButtonMore == false) {
boolFirstClickButtonMore = true
self.centerAddEventButton = self.buttonAddEvent.center
self.centerAddGroceryButton = self.buttonAddGrocery.center
self.centerAddTasksButton = self.buttonAddTasks.center
self.righttopAddEventLabel = self.labelAddEvent.frame.origin
self.righttopAddGroceryLabel = self.labelAddGrocery.frame.origin
self.righttopAddTasksLabel = self.labelAddTasks.frame.origin
self.buttonAddEvent.center = self.buttonMore.center
self.buttonAddGrocery.center = sender.center
self.buttonAddTasks.center = sender.center
self.labelAddEvent.frame.origin = self.buttonMore.frame.origin
self.labelAddGrocery.frame.origin = self.buttonMore.frame.origin
self.labelAddTasks.frame.origin = self.buttonMore.frame.origin
}
// If button 'MORE' is set then hide menu buttons
if (boolSetMore) {
boolSetMore = false
sender.layer.backgroundColor = self.colorGreen.cgColor
UIView.animate(withDuration: 0.3, animations: {
self.buttonAddEvent.center = self.buttonMore.center
self.buttonAddGrocery.center = sender.center
self.buttonAddTasks.center = sender.center
self.labelAddEvent.frame.origin = self.buttonMore.frame.origin
self.labelAddGrocery.frame.origin = sender.frame.origin
self.labelAddTasks.frame.origin = sender.frame.origin
self.viewDisable.alpha = 0.0
self.buttonAddEvent.alpha = 0.0
self.buttonAddGrocery.alpha = 0.0
self.buttonAddTasks.alpha = 0.0
self.labelAddEvent.alpha = 0.0
self.labelAddGrocery.alpha = 0.0
self.labelAddTasks.alpha = 0.0
})
}
else {
boolSetMore = true
self.viewDisable.alpha = 0.75
self.buttonMore.layer.backgroundColor = self.colorDarkGreen.cgColor
UIView.animate(withDuration: 0.3, animations: {
self.buttonAddEvent.center = self.centerAddEventButton
self.buttonAddGrocery.center = self.centerAddGroceryButton
self.buttonAddTasks.center = self.centerAddTasksButton
self.labelAddEvent.frame.origin = self.righttopAddEventLabel
self.labelAddGrocery.frame.origin = self.righttopAddGroceryLabel
self.labelAddTasks.frame.origin = self.righttopAddTasksLabel
self.buttonAddEvent.alpha = 1.0
self.buttonAddGrocery.alpha = 1.0
self.buttonAddTasks.alpha = 1.0
self.labelAddEvent.alpha = 1.0
self.labelAddGrocery.alpha = 1.0
self.labelAddTasks.alpha = 1.0
})
}
}
func toggleButton(button: UIButton, imageOn: UIImage, imageOff: UIImage) {
if (button.currentImage == imageOn) {
button.setImage(imageOff, for: .normal )
}
else {
button.setImage(imageOn, for: .normal )
}
}
When clicking on the buttonMore, three buttons and labels appear from the buttonMore. When clicking again, the three buttons and labels should move back to the buttonMore and alpha is set 0 (animated). This works fine but when I add the toggling of the image, the positions of the three buttons changes and the animations disappears. Anybody an idea?
I'm trying to build up a animation library and I have this object I am referring to as a MedicalCard. It's a subclass of UIView. Nothing particularly fancy about it, there's a couple of properties:
var isRotated: Bool = false
var idx: Int = -1
let btnToggle = UIButton()
In my viewcontroller I create a bunch of these by looping through a dictionary that has some stored values for each card:
let arrCardData = [
["color":UIColor.lightGray, "title":"Card 1"],
["color":UIColor.black, "title":"Card 2"],
["color":UIColor.red, "title":"Card 3"],
["color":UIColor.green, "title":"Card 4"]
]
and then after the instantiation I store them in an array:
var idx = -1
var cardY = 50.0
let cardW = self.view.frame.size.width
for dictThisCard in arrCardData {
idx = idx + 1
let thisCard = MedicalCard()
thisCard.backgroundColor = dictThisCard["color"] as? UIColor
thisCard.frame = CGRect(x:0.0, y:cardY, width:Double(cardW), height:308.0)
thisCard.idx = idx
thisCard.buildCard()
arrCards.append(thisCard)
cardY = cardY + 100.0
self.view.addSubview(thisCard)
}
so far so good, I can add a print(thisCard.idx) and get that value in the log.
In the medical card class I have a button that starts the animation, it simply calls back to the viewcontroller through a notification which card's button was touched:
func toggleCard(_ myButton:UIButton) {
//call back to move cards
let noteName = Notification.Name("animateCards")
NotificationCenter.default.post(name:noteName, object:nil, userInfo:["idx":self.tag])
}
here's where I am falling off the rails, back in the viewcontroller I get the card idx from the notification and then retrieve that card from the array. I can log it and based on the frame and memory address it's the right card:
func animateCards(note:Notification) -> Void {
//get card from idx passed
let dictUserInfo = note.userInfo
let cardIDX = dictUserInfo!["idx"] as! Int
let thisCard = arrCards[cardIDX];
var angle: CGFloat = CGFloat.pi/2
print(thisCard)
print(arrCards[cardIDX])
}
here's the log:
<AnimationPlayground.MedicalCard: 0x7f91bdf0af10; frame = (0 50; 375 308); layer = <CALayer: 0x618000037360>>
<AnimationPlayground.MedicalCard: 0x7f91bdf0af10; frame = (0 50; 375 308); layer = <CALayer: 0x618000037360>>
but I can't see the properties here.
I am using Sprite Builder and created a scrollview to create selection of players list in stripe which can be scrolled horizontally.
PlayerListContainer.ccb:Scrollview Container
Taken a scroll view of width: 300, height: 320 with Horizontal scrolling enabled and Bounces enabled.
Select Content node as PlayerList.ccb (Thus needs to create another PlayerList.ccb class)
PlayerListContainer.swift - noting major but just call back functions are called.
PlayerList.ccb : Main listing
- Take CCNode with width, height as 300,320
PlayerList.swift
//
// PlayerList.swift
// tsb
//
// Created by Paresh Thakor on 27/12/15.
// Copyright (c) 2015 Apportable. All rights reserved.
//
import Foundation
//let Pi = CGFloat(M_PI)
//let degreesToRadians = CGFloat(Pi / 180)
//let radiansToDegrees = CGFloat(180 / Pi)
class PlayerList: CCNode {
//weak var __horizontalLayout: CCLayoutBox!
//weak var __container: CCScrollView!
weak var __node: CCNode!
var userDefaults = NSUserDefaults.standardUserDefaults()
override init!() {
super.init()
NSLog("init Player list")
}
func didLoadFromCCB() {
NSLog("Player list loaded")
__node.contentSizeType = CCSizeTypeNormalized
//__node.contentSizeInPoints = CGSizeMake(1000, 500)
__node.contentSize = CGSizeMake(3, 1)
// Add player children to select
for (var i = 0; i<20; i++) {
let playerBox = CCNodeColor(color: CCColor.redColor())
//playerBox.contentSize = CGSizeMake(50, 50)
let player = CCSprite(imageNamed: "ccbResources/playerBall.png")
playerBox.contentSize = CGSizeMake(player.contentSizeInPoints.width+20, player.contentSizeInPoints.height+20)
playerBox.position = ccp(CGFloat(5)+((playerBox.contentSizeInPoints.width+CGFloat(5)) * CGFloat(i)),5)
player.position = ccp(playerBox.contentSizeInPoints.width/2, playerBox.contentSizeInPoints.height/2)
playerBox.addChild(player)
__node.addChild(playerBox)
}
//self.contentSizeInPoints = CGSizeMake(20*50, 50)
self.userInteractionEnabled = true
self.color = CCColor(UIColor: UIColor.greenColor())
}
}
This works but the scrollview is not being displayed. this will load infinite boxes which we placed.
This scrollview does not scroll to right, etc. nothing happens.
Can some one tell me ?