Swift - UIView Animation speed up after first run - swift

I am displaying different animation for each case whereas it seem they are getting add together and speeding up. I tried using stopAnimating() as well as removeAllAnimations(). None of them are working.
switch currentDrawType {
case 0:
for i in 0..<imageNames.count{
images.append(UIImage(named: imageNames[i])!)
}
self.starImage.animationImages = images
self.starImage.animationDuration = 0.5
case 1:
for a in 0..<imageA.count{
images.append(UIImage(named: imageA[a])!)
}
self.starImage.animationImages = images
self.starImage.animationDuration = 7.0
case 2:
for b in 0..<imageB.count{
images.append(UIImage(named: imageB[b])!)
}
self.starImage.animationImages = images
self.starImage.animationDuration = 0.5
default:
break
}

Related

Custom Set of Steps in Slider

I have a #StateObject Slider. It is set to slide 1...5 step 1.
I need to have custom step [1,2,5,10,20] instead of step 1.
I have a setup where I have the slider work as an array index to my array of custom step. It is working. But It is jerky. Single slide registers slide value dozen times making my array to get picked up dozen times jerking the process. How can I have custom steps smooth.
I can adjust within the app but if Slider provides the exact number, logic of the app become much cleaner. Less translation, clear the logic.
Slider(value: $tSlider, in: 1...5, step: 1)
// How can I make above like below
Slider(value: $tSlider, in: 1...5, step: [1,2,5,10,20]
// What I have currently
#Published var tSteps:[Int] = [1,2,5,10,20]
#Published var tSlider: Double = 1
var tScale: Int { return tlSteps[Int(tlSlider)-1] }
Setting steps on Slider was a bad idea. Stepper was correct choice.
Stepper("Year Scale", onIncrement: {
switch tVM.tlScale {
case 1: self.tlScale = 2
case 2: self.tlScale = 5
case 5: self.tlScale = 10
case 10: self.tlScale = 20
default: self.tlScale = 20
},
onDecrement: {
switch tVM.tlScale {
case 20: self.tlScale = 10
case 10: self.tlScale = 5
case 5: self.tlScale = 2
case 2: self.tlScale = 1
default: self.tlScale = 1
}
})
referred to : post here and Skipjakk's answer. Strange Behavior of Stepper in SwiftUI

SKSpriteNode isn't responding when I try to hide it (Spoiler: SKAction timing issue)

OK, so I've been working on doing an RPG-style dialog box for a project, and while most of it is going smoothly, the one thing that's tripping me up right now is the little icon in the corner of the box to let you know there's more.
I tried to figure out how to get draw the shape, but not having any luck getting Core Graphics to draw triangles I decided to just use a PNG image of one instead. The code below shows everything relevant to how it's been set up and managed.
That being figured out, I'm now trying to get it to hide the marker when updating the box and show it again afterward. Here's what I've tried so far:
Method 1: Use .alpha = 0 to hide it from view during updates, restore with .alpha = 1
Method 2: Remove it from the node tree
Method 3: Place it behind the box background (located at .zPosition = -1)
The result has been consistent across all 3 methods: The triangle just stays in place, unresponsive when invoked.
class DialogBox: SKNode {
private var continueMarker = SKSpriteNode(imageNamed: "continueTriangle") // The triangle that shows in the lower-right to show there's more to read
init() {
/// Setup and placement. It appears in the proper position if I draw it and don't try to hide anything
continueMarker.size.width = 50
continueMarker.size.height = 25
continueMarker.position = CGPoint(x: ((width / 2) - (continueMarker.size.width * 0.9)), y: ((continueMarker.size.height * 0.9) - (height - margin)))
addChild(continueMarker)
}
func updateContent(forceAnimation: Bool = false) {
/// Determine what content to put into the box
hideContinueMarker()
/// Perform the content update in the box (which works as it should)
showContinueMarker()
}
func showContinueMarker() {
// continueMarker.alpha = 1 /// Method 1: Use .alpha to hide it from view during updates
// if (continueMarker.parent == nil) { // Method 2: Remove it from the tree
// addChild(continueMarker)
// }
continueMarker.zPosition = -2 /// Method 3: place it behind the box background (zPosition -1)
}
func hideContinueMarker() {
// continueMarker.alpha = 0 /// Method 1
// if (continueMarker.parent != nil) { /// Method 2
// continueMarker.removeFromParent()
// }
continueMarker.zPosition = 2 /// Method 3
}
}
OK, so while typing this one up I had some more ideas and ended up solving my own problem, so I figured I'd share the solution here, rather than pull a DenverCoder9 on everyone.
On the plus side, you get a look at a simple way to animate text in SpriteKit! Hooray!
In a final check to make sure I wasn't losing my mind, I added some print statements to showContinueMarker() and hideContinueMarker() and noticed that they always appeared simultaneously.
What's that mean? SKAction is likely at fault. Here's a look at the code for animating updates to the box:
private func animatedContentUpdate(contentBody: String, speaker: String? = nil) {
if let speaker = speaker {
// Update speaker box, if provided
speakerLabel.text = speaker
}
var updatedText = "" // Text shown so far
var actionSequence: [SKAction] = []
for char in contentBody {
updatedText += "\(char)"
dialogTextLabel.text = updatedText
// Set up a custom action to update the label with the new text
let updateLabelAction = SKAction.customAction(withDuration: animateUpdateSpeed.rawValue, actionBlock: { [weak self, updatedText] (node, elapsed) in
self?.dialogTextLabel.text = updatedText
})
// Queue up the action so we can run the batch afterward
actionSequence.append(updateLabelAction)
}
/// HERE'S THE FIX
// We needed to add another action to the end of the sequence so that showing the marker again didn't occur concurrent with the update sequence.
let showMarker = SKAction.customAction(withDuration: animateUpdateSpeed.rawValue, actionBlock: { [weak self] (node, elapsed) in
self?.showContinueMarker()
})
// Run the sequence
actionSequence.append(showMarker)
removeAction(forKey: "animatedUpdate") // Cancel any animated updates already in progress
run(SKAction.sequence(actionSequence), withKey: "animatedUpdate") // Start the update
}
In case you missed it in the big block there, here's the specific bit in isolation
let showMarker = SKAction.customAction(withDuration: animateUpdateSpeed.rawValue, actionBlock: { [weak self] (node, elapsed) in
self?.showContinueMarker()
})
Basically, we needed to add showing the triangle as an action at the end of the update sequence instead of just assuming it would occur after the update since the function was invoked at a later time.
And since all 3 methods work equally well now that the timing has been fixed, I've gone back to the .alpha = 0 method to keep it simple.

I'm new to programming, is there a shorter way of achieving the same result using something like a case or caseif?

I have the code below written in my program, but I was wondering if someone could help me structure in a more summarized way as using 'case' or 'caseif' from other languages.
var imagePressed: UIImageView
imagePressed = self.redImage
if color == 1 {
imagePressed = self.redImage
}
if color == 2 {
imagePressed = self.orangeImage
}
if color == 3 {
imagePressed = self.yellowImage
}
if color == 4 {
imagePressed = self.greenImage
}
imagePressed.alpha = 0.5
I would suggest to use Dictionary to avoid much code:
let images = [1 : redImage, 2 : orangeImage, 3: yellowImage, 4: greenImage]
if let image = images[color] {
imagePressed = image
}
Also consider replacing hardcoded numbers with a enum for color e.g.:
enum Color {
case red
case orange
...
}
Swift also provides the switch statement.
You could simply type
var imagePressed: UIImageView
switch color {
case 1: imagePressed = self.redImage
case 2: imagePressed = self.orangeImage
case 3: imagePressed = self.yellowImage
case 4: imagePressed = self.greenImage
default: // provide any default value
}
Note that Swift switch statement has no implicit fallthrough. That mean, if any case is matching, the code flow breaks there.
Use comma-separated conditions to match multiple cases.
I recommend to read the switch statement in their language guide:
https://docs.swift.org/swift-book/LanguageGuide/ControlFlow.html#ID129
Your syntax is quite inefficient anyway because the code checks always all conditions even if color is 1.
A better form is if - else if
var imagePressed : UIImageView
imagePressed = self.redImage
if color == 1 {
imagePressed = self.redImage
} else if color == 2 {
imagePressed = self.orangeImage
} else if color == 3 {
imagePressed = self.yellowImage
} else if color == 4 {
imagePressed = self.greenImage
}
imagePressed.alpha = 0.5
In this particular case I recommend to use a switch statement and drop the extra variable
switch color {
case 1: self.redImage.alpha = 0.5
case 2: self.orangeImage.alpha = 0.5
case 3: self.yellowImage.alpha = 0.5
case 4: self.greenImage.alpha = 0.5
default: break
}

Swift correct break multi-layer switch

I'm rewriting a code from java to swift and need to break a multi-layered switches right way. In Java it looks like :
block0 : switch (topSwithch) {
case one: {
switch (innerSwitch) {
case insideCase: {
if (something){
break block0;
}
// etc
So, I'm breaking one switch from another. How do I do it in swift ?
This is what it'd look like in Swift. I did this in a playground with hardcoded values for the variables:
let topSwitch = 1
let innerSwitch = 4
let one = 1
let insideCase = 4
let something = true
block0 : switch (topSwitch) { //first switch labeled "block0", switching on topSwitch
case one: //topSwitch is 1, one is 1
switch (innerSwitch) { // switching on innerSwitch
case insideCase: // both are 1
if (something){ // if true
break block0; // break from "block0"
}
default: break // else
}
default: break // else
}

Need to know about ENUM for contact in SpriteKit

I tried to make contact but it didn't work .
I create an enum:
enum object:UInt32{
case BB = 1
case TR = 2
case GAP = 3
}
Then I have 3 nodes:
balloon.physicsBody?.categoryBitMask = object.BB.rawValue
balloon.physicsBody?.contactTestBitMask = object.TR.rawValue
balloon.physicsBody?.collisionBitMask = object.GAP.rawValue
tree2.physicsBody?.categoryBitMask = object.TR.rawValue
tree2.physicsBody?.contactTestBitMask = object.BB.rawValue
gap.physicsBody?.categoryBitMask = object.GAP.rawValue
gap.physicsBody?.collisionBitMask = object.GAP.rawValue
gap.physicsBody?.contactTestBitMask = object.BB.rawValue
In DidBeginContact:
let result = contact.bodyA.categoryBitMask + contact.bodyB.categoryBitMask
switch result{
case object.BB.rawValue + object.TR.rawValue:
xoaBB(contact.bodyA.node as! SKSpriteNode, TR: contact.bodyB.node as! SKSpriteNode, toaDo: contact.contactPoint)
case object.BB.rawValue + object.GAP.rawValue:
score++
scorelabel.text = "\(score)"
default:
return
}
The problem is:
"case object.BB.rawValue + object.GAP.rawValue" didn't work
I want the ballon fly through the gap to score but it can't. Balloon just contact to gap and gets stuck there as shown here.
Can anyone help me, I really need "how to use Enum tutorial too"
As mentioned in my comment your bitmask-values doesn't quite make sense. How about something a little like this (I have not checked for typos):
enum MaskType : UInt32 {
case Balloon = 1
case Gap = 2
case Wall = 4
case None = 0
}
Then you need to decide what you are after. The following example does not register collisions, meaning you want have physical interactions happening between the objects.
balloon.physicsBody?.categoryBitMask = MaskType.Balloon.rawValue
balloon.physicsBody?.collisionBitMask = MaskType.None.rawValue
balloon.physicsBody?.contactTestBitMask = MaskType.Gap.rawValue | MaskType.Wall.rawValue
The balloon sprite will now register contacts with objects having either the .Gap or .Wall value as their categoryBitMask.
If you now setup your other spriteNode's physicsBodies appropriately you can do some nice things in the didBeginContact function. Perhaps something like this:
func didBeginContact(contact: SKPhysicsContact) {
let contactA = contact.bodyA
let contactB = contact.bodyB
func ballonHitType() -> Uint32 {
if contactA.categoryBitMask == MaskType.Baloon.rawValue {
return contactB.categoryBitMask
} else {
return contactA.categoryBitMask
}
}
// Then simply perform your wanted actions based on whatever ballonHitType you receive
}