I am working on a countdown timer for my app that starts counting down from 15. This is my code:
//
import UIKit
class ViewController2: UIViewController {
#IBOutlet weak var Timer: UILabel!
var countd = 15
//
//
override func viewDidLoad() {
super.viewDidLoad()
let time = NSTimer(timeInterval: 1.0, target: self, selector: "updateCounter", userInfo: nil, repeats: true)
Timer.text = String(countd)
NSRunLoop.mainRunLoop().addTimer(time, forMode: NSDefaultRunLoopMode)
}
func updateCounter(timer: NSTimer) {
Timer.text = String(countd)
if (countd > 0){
Timer.text = String(countd--)
Timer.text = String(countd)
}
}
// Do any additional setup after loading the view.
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
However, when I run it, as soon as I get to the part with the timer I see the label flash 15 for a second but then it immediately crashes with the SIGABRT error. What can I do?
Change
selector: "updateCounter"
to
selector: "updateCounter:"
Please note that in Swift 2.2 it will become difficult to make this mistake (the compiler will warn), and in Swift 3 it will become impossible (string literal selector syntax will become illegal). You might want to update to Swift 2.2 now if you don't understand string literal selector syntax.
Related
Hi this is my first app and I have been trying to figure out how to do this simple task but am getting overloaded with information and I have a feeling there may be an easier way to do it.
I am trying to build a random word generator that will select from an array of words at random time intervals. The time needs to change after the timer fires. The timer can keep running the whole time the app is on.
here is my code so far:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var label: UILabel!
#objc weak var paraTimer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
let randomNum = Int.random(in: 1..<5)
paraTimer = Timer.scheduledTimer(timeInterval: TimeInterval(randomNum), target: self, selector: #selector(runTimedCode), userInfo: nil, repeats: true)
}
/*
This invalidates my timer, not sure if I need to start a new timer now or if I can reset this?
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
paraTimer?.invalidate()
*/
#objc func runTimedCode() {
let array = ["Apple", "Banana", "Pear" ]
label.text = array.randomElement()
}
}
So the issue that I need to fix is it will select a random number from 1-5 and then not select a new number and just keep running every x seconds. Also should I invalidate the timer or leave that off?
Thank you. Like I said, this is my first time working on an app that doesn't just follow a straight tutorial.
To restart a timer with different intervals you have to recreate the timer.
First of all declare the timer strong and #objc is not needed
var paraTimer: Timer?
I recommended to create an extra method. Declare the timer non-repeating and use the block based API of Timer. The timer restarts itself.
func restartTimer() {
let randomNum = Int.random(in: 1..<5)
paraTimer = Timer.scheduledTimer(timeInterval: TimeInterval(randomNum), repeats: false) { [weak self] _ in
let array = ["Apple", "Banana", "Pear" ]
self?.label.text = array.randomElement()
self?.restartTimer()
}
}
Start the timer in viewWillAppear
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
restartTimer()
}
and stop it in viewDidDisappear
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
paraTimer?.invalidate()
paraTimer = nil
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I have yet another error, its the common swift error
Thread 1: signal SIGABRT
I know how to fix this error. Here is my recent question.
Can't resolve error: Thread 1: signal SIGABRT in swift XCODE
But now I am making a new project, and I got this error! So I tried to fix it, I thought I got it - but I didn't.. I have checked all my outlets and everything, But there is no trace of where this is coming from...
Here is my code:
import UIKit
class ViewController: UIViewController {
//Variables
var tempature:Int = 50
var battery:Int = 100
var monsterCount:Int = 0
//Outlets
#IBOutlet weak var BatteryLevel: UILabel!
#IBOutlet weak var TempDisplay: UILabel!
//The spy camera
#IBOutlet weak var monsterImageDisplay: UIImageView?
//The background image
#IBOutlet weak var backgroundImageShow: UIImageView!
//Functions
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
monsterImageDisplay?.isHidden = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//The interval to keep things going
var helloWorldTimer = Timer.scheduledTimer(timeInterval: 3.0, target: self, selector: Selector(("spawnMonstor")), userInfo: nil, repeats: true)
func spawnMonstor() {
monsterImageDisplay?.isHidden = false
monsterCount += 1
if monsterCount >= 3 {
let randomNumberBackgroundImage = arc4random_uniform(10)
if randomNumberBackgroundImage >= 5 {
backgroundImageShow.image = UIImage(named: "main-room-jumpTop")
} else if randomNumberBackgroundImage <= 5 {
backgroundImageShow.image = UIImage(named: "main-room-jumpBottom")
}
} else if monsterCount >= 5 {
let alert = UIAlertController(title: "GAME OVER", message: "Sorry! You died...", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Okay..", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
//The shock rooms button
#IBAction func ShockRooms(_ sender: Any) { monsterImageDisplay?.isHidden = true
}
}
Check that your UIImage names are the EXACT same names as the ones in your xcassets folder.
Instead of using the helloWorldTimer, write this at the top of your class, underneath your outlets:
var helloWorldTimer = Timer()
And then inside of your viewDidLoad, add:
helloWorldTimer = Timer.scheduledTimer(timeInterval: 3.0, target: self, selector: Selector(("spawnMonstor")), userInfo: nil, repeats: true)
Well, I'm not sure it is a real problem but it is unpleasant to see an error. The "thread 1 exc_bad_access (code=exc_i386_gpflt)" error appears with a probability of about 50% in simulator. But there is no error in real device.
I tried to use optionals, but without a noticeable result.
I have only WKWebView and a Button. The code is:
import UIKit
import WebKit
class ViewController: UIViewController {
#IBOutlet weak var WebView: WKWebView!
let all = ["kjhgjhkg","qqqqqqqqq", "wwwwwwwww", "lllllllll", "bbbbbbbb"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
WebView.loadHTMLString("<h1>The first title</h1>", baseURL: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func btnRefresh(_ sender: UIButton) {
let RandomNomber = Int(arc4random_uniform(UInt32(all.count)))
// let myString = all[RandomNomber]
// let myString2 = "<h1>" + myString + "</h1>"
WebView.loadHTMLString("<h1>" + all[RandomNomber] + "</h1>", baseURL: nil)
}
}
This is my first question on StackOverflow. I am trying to measure reaction time to stimuli and need multiple players and UIButtons for the players. The problem is if the two players in the code example are within 16 millisecond or less of each other both UIButton events come together and therefore the same time. I just did this in Android and had no problem.
I did try override of the touches_began() and the same problem. It seems as if the two UIButtons are being handled in the same interrupt as if they were a multi-touch which I have turned off for the two buttons.
I know the example is dumb but it does illustrate the 16 millisecond
issue. Just tap a bunch of times and see how many ties occur. I looked at the results of testing and I got an approximate 16 millisecond issue.
import UIKit
class ViewController: UIViewController {
weak var timer: Timer?
var startTime: Double = 0
var time: Double = 0
#IBOutlet weak var timeValueLabel1: UILabel!
#IBOutlet weak var timeValueLabel: UILabel!
#IBAction func stop1Button() {
let timeString = String(format: "%.3f", time)
timeValueLabel1.text = timeString
}
#IBAction func stopButton() {
let timeString = String(format: "%.3f", time)
timeValueLabel.text = timeString
}
override func viewDidAppear(_ animated: Bool) {
startTime = Date().timeIntervalSinceReferenceDate
timer = Timer.scheduledTimer(timeInterval: 0.001,
target: self,
selector: #selector(advanceTimer(timer:)),
userInfo: nil,
repeats: true)
}
override func viewWillDisappear(_ animated: Bool) {
timer?.invalidate()
}
func advanceTimer(timer: Timer) {
time = Date().timeIntervalSinceReferenceDate - startTime
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I'm trying to use an NSTimer to run a function at a certain interval, but every time it runs the program crashes with an exception:
+[Project.Class updateLabel]: unrecognized selector sent to class...
Terminating app due to uncaught exception "NSInvalidArgumentException".
The code is as follows:
class SecondViewController: UIViewController {
// MARK: Properties
override func viewDidLoad() {
super.viewDidLoad()
do {
try updateLabel()
try startTimer()
}
catch{
self.showAlert()
}
}
#IBOutlet weak var i2cLabel: UILabel!
// MARK: Actions
func startTimer() throws {
_ = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("updateLabel"), userInfo: nil, repeats: true)
}
func showAlert() {
}
func updateLabel() throws {
}
}
If I comment out the NSTimer line, the program compiles and runs just fine.
Note that updateLabel(), the selector function, takes no arguments so calling it as a selector without a colon suffix should work. This seems to be unique to this problem.
Try update your code to:
override func viewDidLoad() {
super.viewDidLoad()
do {
updateLabel()
try startTimer()
}
catch{
self.showAlert()
}
}
// MARK: Actions
func startTimer() throws {
_ = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("updateLabel"), userInfo: nil, repeats: true)
}
func showAlert() {
}
func updateLabel() {
}
It seems that: NSTimer don't know call a function throws. So it will crash.
I have tried to find some official or some article describe about it. But sad that I can't find now.
You can reference to this question: Reference
Hope this help!