Sigabrt swift Xcode (making a timer) - xcode

I'm trying to make a stopwatch, and I can't seem to understand why I am getting a sigabrt with my application. Sorry I didn't know what to include, so I included everything.
var timer = NSTimer()
var minutes: Int = 0
var seconds: Int = 0
var fractions: Int = 0
var startStopWatch: Bool = true
var StopwatchString: String = ""
#IBOutlet weak var display: UILabel!
#IBAction func start(sender: UIButton) {
if startStopWatch == true {
NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: Selector("updateStopwatch"), userInfo: nil, repeats: true)
startStopWatch = false
} else {
timer.invalidate()
startStopWatch = true
}
}
#IBAction func reset(sender: UIButton) {
}
override func viewDidLoad() {
super.viewDidLoad()
display.text = "00:00.00"
}
func updateStopWatch() {
fractions += 1
if fractions == 100 {
seconds += 1
fractions = 0
}
if seconds == 60 {
minutes += 1
seconds = 0
}
let fractionsString = fractions > 9 ? "\(fractions)" : "0\(fractions)"
let secondsString = seconds > 9 ? "\(seconds)" : "0\(seconds)"
let minutesString = minutes > 9 ? "\(minutes)" : "0\(minutes)"
StopwatchString = "\(minutesString):\(secondsString).\(fractionsString)"
display.text = StopwatchString
}

I cut and pasted your code into a project and when I tapped the start button the first line of the error message said:
-[My.ViewController updateStopwatch]: unrecognized selector sent to instance 0x7fe0034b3520
That tells you all you need to know. You send the updateStopwatch message to your view controller but it doesn't implement that method. I noticed though that your ViewController does implement an updateStopWatch method. Looks like a basic capitalization error to me.
Always, always, always, read at least the first line of the error message.

Here are some fixes:
+I changed the timer variable initially to nil - it will be saved in the start function.
+Your if statement in the start function looked like this:
if startStopWatch == true
But it should be like this:
if (startStopWatch)
Hope this helpes :)
var timer = nil
var minutes = 0
var seconds = 0
var fractions = 0
var startStopWatch = true
var StopwatchString = ""
#IBOutlet weak var display: UILabel!
#IBAction func start(sender: UIButton) {
if (startStopWatch == true) {
timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: Selector("updateStopwatch"), userInfo: nil, repeats: true)
startStopWatch = false
//I added one thing. If the start button is pushed the text will change to stop. You can change this if you like.
sender.setTitle("Stop", forState: .Normal)
//
} else {
timer.invalidate()
startStopWatch = true
}
}
#IBAction func reset(sender: UIButton) {
display.text! = "00:00.00"
}
override func viewDidLoad() {
super.viewDidLoad()
display.text! = "00:00.00"
}
func updateStopWatch() {
fractions++
if (fractions == 100) {
seconds++
fractions = 0
}
if (seconds == 60) {
minutes++
seconds = 0
}
var fractionsString = fractions > 9 ? "\(fractions)" : "0\(fractions)"
var secondsString = seconds > 9 ? "\(seconds)" : "0\(seconds)"
var minutesString = minutes > 9 ? "\(minutes)" : "0\(minutes)"
StopwatchString = "\(minutesString):\(secondsString).\(fractionsString)"
display.text! = StopwatchString
}

Related

Use of local variable 'updateTimer' before its declaration Egg timer

I just started learning programming and the instructor asked us to make an egg timer app.
I tried to run her example solution but XCode shows an issue with the code at #selector(updateTimer). The error says Use of local variable 'updateTimer' before its declaration.
This is the code:
class ViewController: UIViewController {
#IBOutlet weak var progress: UILabel!
let eggTimes = ["Soft" :3, "Medium":4, "Hard":6]
var secondsRemaining = 60
var timer = Timer()
#IBAction func hardnessSelected(_ sender: UIButton) {
timer.invalidate()
let hardness = sender.currentTitle!
secondsRemaining = eggTimes[hardness]!
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector:
#selector(updateTimer), userInfo: nil, repeats: true)
func updateTimer() {
if secondsRemaining > 0 {
print("\(secondsRemaining) seconds left to finish")
secondsRemaining -= 1
}
else {
timer.invalidate()
progress.text = "DONE"
}
}
}
}
The issue you're having is that the func updateTimer is declared under the #selector and as such isn't "available yet" to put it simply. What you probably wanted is to move the function outside the hardnessSelected as such:
class ViewController: UIViewController {
#IBOutlet weak var progress: UILabel!
let eggTimes = ["Soft" :3, "Medium":4, "Hard":6]
var secondsRemaining = 60
var timer = Timer()
#IBAction func hardnessSelected(_ sender: UIButton) {
self.timer.invalidate()
let hardness = sender.currentTitle!
self.secondsRemaining = self.eggTimes[hardness]!
self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.updateTimer), userInfo: nil, repeats: true)
}
#objc func updateTimer() {
if self.secondsRemaining > 0 {
print("\(self.secondsRemaining) seconds left to finish")
self.secondsRemaining -= 1
}
else {
self.timer.invalidate()
self.progress.text = "DONE"
}
}
}
You also need to add #objc in front of the function name to expose it to ObjC runtime. You can read more about that here.

How to make NSSpellChecker work with NSDocument?

I have a core data / document-driven macOS app, using Swift and I struggle on combining the out-of-the-box spell checking API with an NSDocument (NSPersistentDocument in my case)
It took me more time than it should take, but this is what I got, mostly guided by this great answer:
class VTDocument: NSPersistentDocument, NSChangeSpelling {
[...]
private let spellchecker = SpellChecker()
#IBAction func showGuessPanel(_ sender: Any?){
spellchecker.startSpellCheck(nodes: Array(db.nodes), tag: 0)
}
#IBAction #objc func changeSpelling(_ sender: Any?){
spellchecker.replace(with: "Test")
}
This is leading me to see the NSSpellChecker.spellingPanel, correctly showing the word to correct. However, the changeSpelling function should be "called" by the panel but is never called. The above spellChecker is a simple wrapper around the NSSpellChecker that keeps the status between function calls.
The SpellChecker class looks like this.
import Cocoa
class SpellChecker {
let checker = NSSpellChecker.shared
let count: UnsafeMutablePointer<Int> = UnsafeMutablePointer<Int>.allocate(capacity: 1)
var nodes = Array<Node> ()
var nodeNr = 0
var stringPos = 0
var range: NSRange = NSRange()
func startSpellCheck(nodes: [Node], tag: Int ) {
self.nodes = nodes
nodeNr = 0
stringPos = 0
continueChecking()
}
func continueChecking(){
if nodes.count == 0 {
return
}
if nodeNr >= nodes.count {
checker.updateSpellingPanel(withMisspelledWord: "")
checker.spellingPanel.orderFront(self)
return
}
if let nodeText = nodes[nodeNr].label {
range = checker.checkSpelling(of: nodeText, startingAt: stringPos, language: nil, wrap: false, inSpellDocumentWithTag: 0, wordCount: count)
if count.pointee > 0 {
stringPos = range.lowerBound
checker.updateSpellingPanel(withMisspelledWord: nodeText[range])
checker.spellingPanel.orderFront(self)
return
}
}
nodeNr = nodeNr + 1
continueChecking()
}
func replace(with: String){
if let nodeText = nodes[nodeNr].label {
let text = nodeText as NSString
text.replacingCharacters(in: range, with: with)
nodes[nodeNr].label = text as String
}
}
}

Swift Multiples App running error

I have running an issue with the Multiples App. I was just trying out and see if I can get the function right. However, this app can only run once, the addition itself is correct but when the game starts over, it will throw an error. Please help ~~
class ViewController: UIViewController {
#IBOutlet weak var AddBtn: UIButton!
#IBOutlet weak var TextInput: UITextField!
#IBOutlet weak var ResultLabel: UILabel!
var CurrentNum = 0
var SecondNum = 0
let MaxNum = 30
func currentNumUpdate(sum: Int, Mul: Int, nSum: Int){
ResultLabel.text = "\(sum) + \(Mul) = \(nSum)"
}
#IBAction func AddtionBtn(sender: UIButton) {
let TotalSum = CurrentNum + SecondNum
currentNumUpdate(CurrentNum, Mul: SecondNum, nSum: TotalSum)
CurrentNum += SecondNum
if (TextInput != nil && TextInput != ""){
SecondNum = Int(TextInput.text!)!
}
if GameOver(){
GameStartOver()
}
}
func GameOver() -> Bool{
if (CurrentNum > MaxNum){
return true
}else {
return false
}
}
func GameStartOver(){
ResultLabel.hidden = false
TextInput.text = ""
AddBtn.hidden = false
ResultLabel.text = "Again"
}
}

Why doesnt my timer countdown to 0 in Swift?

I have this timer and it starts at 3 and it should countdown to 0 but it stops at 2. I don't get why it doesn't go all they way down to 0. Can you please let me know what Im doing wrong with my code. Thank you!
class GameScene: SKScene, SKPhysicsContactDelegate {
var timerToStartGame = 3
var timerCountDownLabel: SKLabelNode! = SKLabelNode()
override func didMoveToView(view: SKView) {
timerCountDownLabel = SKLabelNode(fontNamed: "TimeBurner")
timerCountDownLabel.fontColor = UIColor.whiteColor()
timerCountDownLabel.zPosition = 40
timerCountDownLabel.fontSize = 60
timerCountDownLabel.position = CGPointMake(self.size.width / 2.4, self.size.height / 1.5)
self.addChild(timerCountDownLabel)
var clock = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: Selector("countdown"), userInfo: nil, repeats: true)
}
func countdown() {
timerCountDownLabel.text = String(timerToStartGame--)
if timerToStartGame == 0 {
doAction()
}
}
}
The problem occurs because you decrease it after displaying using -- after the var. Move it to the front and start from 4.
timerCountDownLabel.text = String(--timerToStartGame)

removeFromParent() does not remove a node, but makes it invisible

I have a restartButton that must appear when two bodies collide and when it happens for the first time, all goes great - bodies collide--> restartButton appears--> I restart level by touching restartButton.
It was a "good, right restart" And here problem starts...
After level been restarted, if I touch at center of the screen(where restartButton must appear when is called) game crashes, saying following:
"Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attemped to add a SKNode which already has a parent: name:'(null)' particleTexture: 'enemyPart.png' (100 x 100) position:{721.33929, 175.39999} accumulatedFrame:{{inf, inf}, {inf, inf}}'
** First throw call stack:
(0x2a0fa137 etc.)
libc++abi.dylib: terminating with uncaught exception of type NSException"
but restartButton is invisible and it couldn't even be there because no bodies have collided.
If after that "good restart" some enemy collide with player, restartButton appears for a moment and player, enemy1, enemy2, enemy3 are fadingOut from scene.
I would appreciate if someone can help
Here is code where you can see all that stuff:
import SpriteKit
import UIKit
let player = SKEmitterNode(fileNamed: "playerPart.sks")
let enemy1 = SKEmitterNode(fileNamed: "ePart.sks")
let enemy2 = SKEmitterNode(fileNamed: "ePart.sks")
let enemy3 = SKEmitterNode(fileNamed: "ePart.sks")
let restartButton = SKSpriteNode(imageNamed: "restartButton")
let playerCat: UInt32 = 0x1 << 0
let enemyCat: UInt32 = 0x1 << 1
class Level2: SKScene, SKPhysicsContactDelegate {
override func didMoveToView(view: SKView) {
physicsWorld.contactDelegate = self
initWorld()
movements()
player.physicsBody = SKPhysicsBody(circleOfRadius: 50)
player.position = CGPointMake(819.2 , 693.8)
player.zPosition = 1
player.physicsBody?.categoryBitMask = playerCat
player.physicsBody?.contactTestBitMask = enemyCat
player.targetNode = self
self.addChild(player)
enemy1.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(102, 102))
enemy1.position = CGPointMake(819.2, 175.4)
enemy1.zPosition = 1
enemy1.physicsBody?.affectedByGravity = false
enemy1.physicsBody?.dynamic = true
enemy1.physicsBody?.allowsRotation = false
enemy1.physicsBody?.categoryBitMask = enemyCat
enemy1.physicsBody?.contactTestBitMask = playerCat
enemy1.physicsBody?.collisionBitMask = 0x0
enemy1.targetNode = self
enemy1.particleBirthRate = 150
enemy1.particleLifetime = 10
enemy1.particleLifetimeRange = 20
enemy1.particlePositionRange = CGVectorMake(50, 60)
enemy1.emissionAngle = 0
enemy1.emissionAngleRange = 0
enemy1.particleSpeed = 0
enemy1.particleSpeedRange = 0
enemy2.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(102, 102))
enemy2.position = CGPointMake(614.4, 386.6)
enemy2.zPosition = 1
enemy2.physicsBody?.affectedByGravity = false
enemy2.physicsBody?.dynamic = true
enemy2.physicsBody?.allowsRotation = false
enemy2.physicsBody?.categoryBitMask = enemyCat
enemy2.physicsBody?.contactTestBitMask = playerCat
enemy2.physicsBody?.collisionBitMask = 0x0
enemy2.targetNode = self
enemy2.particleBirthRate = 150
enemy2.particleLifetime = 10
enemy2.particleLifetimeRange = 20
enemy2.particlePositionRange = CGVectorMake(50, 60)
enemy2.emissionAngle = 0
enemy2.emissionAngleRange = 0
enemy2.particleSpeed = 0
enemy2.particleSpeedRange = 0
enemy3.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(102, 102))
enemy3.position = CGPointMake(409.6, 181.8)
enemy3.zPosition = 1
enemy3.physicsBody?.affectedByGravity = false
enemy3.physicsBody?.dynamic = true
enemy3.physicsBody?.allowsRotation = false
enemy3.physicsBody?.categoryBitMask = enemyCat
enemy3.physicsBody?.contactTestBitMask = playerCat
enemy3.physicsBody?.collisionBitMask = 0x0
enemy3.targetNode = self
enemy3.particleBirthRate = 150
enemy3.particleLifetime = 10
enemy3.particleLifetimeRange = 20
enemy3.particlePositionRange = CGVectorMake(50, 60)
enemy3.emissionAngle = 0
enemy3.emissionAngleRange = 0
enemy3.particleSpeed = 0
enemy3.particleSpeedRange = 0
func initWorld() {
self.addChild(enemy1)
self.addChild(enemy2)
self.addChild(enemy3)
}
func movements() {
let move11 = SKAction.moveTo(CGPointMake(819.2, 386.6), duration: 1.5)
let move12 = SKAction.moveTo(CGPointMake(614.4, 386.6), duration: 1.5)
let move13 = SKAction.moveTo(CGPointMake(614.4, 175.4), duration: 1.5)
let move14 = SKAction.moveTo(CGPointMake(819.2, 175.4), duration: 1.5)
let enemy1m = SKAction.sequence([move11, move12, move13, move14])
let enemy1move = SKAction.repeatActionForever(enemy1m)
let move21 = SKAction.moveTo(CGPointMake(614.4, 591.4), duration: 1.5)
let move22 = SKAction.moveTo(CGPointMake(409.6, 591.4), duration: 1.5)
let move23 = SKAction.moveTo(CGPointMake(409.6, 386.6), duration: 1.5)
let move24 = SKAction.moveTo(CGPointMake(614.4, 386.6), duration: 1.5)
let enemy2m = SKAction.sequence([move21, move22, move23, move24])
let enemy2move = SKAction.repeatActionForever(enemy2m)
let move31 = SKAction.moveTo(CGPointMake(409.6, 386.6), duration: 1.5)
let move32 = SKAction.moveTo(CGPointMake(204.8, 386.6), duration: 1.5)
let move33 = SKAction.moveTo(CGPointMake(204.8, 181.8), duration: 1.5)
let move34 = SKAction.moveTo(CGPointMake(409.6, 181.8), duration: 1.5)
let enemy3m = SKAction.sequence([move31, move32, move33, move34])
let enemy3move = SKAction.repeatActionForever(enemy3m)
enemy1.runAction(enemy1move)
enemy2.runAction(enemy2move)
enemy3.runAction(enemy3move)
}
func didBeginContact(contact: SKPhysicsContact) {
let collision:UInt32 = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask)
if collision == (playerCat | enemyCat) {
self.removeAllActions()
self.runAction(SKAction.waitForDuration(0.1), completion: {
self.runAction(SKAction.waitForDuration(0.2), completion:
{self.removeAllActions()
self.removeChildrenInArray([enemy1, enemy2, enemy3, player])})
restartButton.size = CGSizeMake(200, 200)
restartButton.position = CGPointMake(512, 384)
restartButton.zPosition = 1
self.addChild(restartButton)
})
}
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if (restartButton .containsPoint(location)) {
restartButton.runAction(fadeAway)
restartButton.removeFromParent()
println(1)
self.runAction(SKAction.waitForDuration(1.5), completion: {
let repeatLevel = SKTransition.fadeWithDuration(2)
let level2 = Level2(fileNamed: "Level2")
self.view?.presentScene(level2, transition: repeatLevel)
})
}
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
Your restartButton is having its size set for the first time when your playerCat and enemyCat first collide, which is fine.
Since the restartButton now has a size. You can check if your touch is within the bounds of the restartButton.
if (restartButton .containsPoint(location)) {
Like you have done so, but at no point do you check wether the restartButton is added to the scene.
A quick fix could possibly be:
if (restartButton.parent != nil && restartButton .containsPoint(location)) {
If dont specifically need to check if it with the bounds of the node. You could directly check using this instead. Which will eliminate the need to check for a parent.
if (self.nodeAtPoint(location) == restartButton) {
Another thing i noticed, in your collision detection, you never check if it has already collided. So you might run the same code multiple times, where you just keep removing all actions and then adding a new one.
You could add a simple have variable to prevent redudancy
var detectionMade = false
and reset at
override func didMoveToView(view: SKView) {
physicsWorld.contactDelegate = self
initWorld()
movements()
detectionMade = false
...
and set to true when first colliding and checking
if collision == (playerCat | enemyCat) && !detectionMade {
detectionMade = true
...
#martinmeincke I've done it! By doing THIS:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if (restartButton .containsPoint(location) && restartButton.parent == nil) {
restartButton.runAction(fadeAway)
println(1)
}
}
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if (restartButton .containsPoint(location)) {
restartButton.removeFromParent()
self.runAction(SKAction.waitForDuration(1.5), completion: {
let repeatLevel = SKTransition.fadeWithDuration(2)
let level2 = Level2(fileNamed: "Level2")
self.view?.presentScene(level2, transition: repeatLevel)
})
}
}
}

Resources