Swift: whose view is not in the window hierarchy - ios

I know this is asked a lot but I've tried a lot of the other solutions on here and I can't seem to get it right.
So, I have a class that counts down, and at the end of the countdown a new view begins.
Here is the countdown class:
import Foundation
import UIKit
class CountdownController: UIViewController {
// MARK: Properties
#IBOutlet weak var countDownLabel: UILabel!
var count = 3
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("update"), userInfo: nil, repeats: true)
}
func update() {
if(count > 0) {
countDownLabel.text = String(count--)
}
else {
self.performSegueWithIdentifier("goestoMathTest", sender: self)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: Actions
}
and the error that comes up after the MathTestController shows is:
2016-05-26 23:43:48.579 TraderMathTestiOS[18654:951105] Warning: Attempt to
present <TraderMathTestiOS.MathTestController: 0x7fca824be7d0> on
<TraderMathTestiOS.CountdownController: 0x7fca824b9d70> whose view is not in
the window hierarchy!
**EDIT: So I tried another changing a few things and I think I narrowed down the issue. I changed the timer to be in viewDidLoad() and changed the repeats to 'false' and now MathTestController appears after 1 second with no warnings showing up. Here's the changed code:
override func viewDidLoad() {
super.viewDidLoad()
var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("update"), userInfo: nil, repeats: false)
}
func update() {
self.performSegueWithIdentifier("goestoMathTest", sender: self)
if(count > 0) {
countDownLabel.text = String(count--)
}
else {
self.performSegueWithIdentifier("goestoMathTest", sender: self)
}
}
I think the reason the error shows up is because the timer keeps repeating in the CountdownController even after MathTestController is called. Anybody know how to get my original functionality ( A timer that counts '3, 2, 1') without the error? Maybe I need to kill the timer somehow?

I fixed this finally. For anyone wondering, if your timer repeats you need to invalidate it if you're starting a new view. Here's the fixed code:
class CountdownController: UIViewController {
// MARK: Properties
#IBOutlet weak var countDownLabel: UILabel!
var timer = NSTimer()
var count = 3
override func viewDidLoad() {
super.viewDidLoad()
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("update"), userInfo: nil, repeats: true)
}
func update() {
if(count > 0) {
countDownLabel.text = String(count--)
}
else {
timer.invalidate()
self.performSegueWithIdentifier("goestoMathTest", sender: self)
}
}

Related

Using timers in swift

I am a complete beginner and am starting to learn the Swift programming language. I'm following a tutorial on Udemy and am having some problems with setting up a timer.
class ViewController: UIViewController {
#IBOutlet weak var label: UILabel!
var timer1 = Timer()
var counter = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
label.text = String(counter)
}
#IBAction func start(_ sender: Any) {
timer1 = Timer(timeInterval: 1, target: self, selector: #selector(tim), userInfo: nil, repeats: true)
}
#IBAction func pause(_ sender: Any) {
timer1.invalidate()
}
#IBAction func restart(_ sender: Any) {
timer1.invalidate()
counter = 0
label.text = String(counter)
}
#objc func tim() {
counter += 1
label.text = String(counter)
}
}
This is my code but the timer is not working. Please tell me where im going wrong.
You need to add timer to RunLoop
RunLoop.current.add(timer1, forMode: .commonModes)
Or use scheduledTimer
timer1 = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(tim), userInfo: nil, repeats: true)
Also in your restart function you need to do it again, because now you are just invalidating it and not starting again.

How to make an array show up on a label one by one

I am trying to make words in an array show up one at a time on a label, but with my code, the last word in the array shows up. What I want is for my label to display Hello [wait] (a certain amount of time preferably adjustable at some point) World [wait] Testing [wait] Array
Here's my code:
import UIKit
// Variables
let stringOfWords = "Hello World. Say Hello. Test Number One."
let stringOfWordsArray = stringOfWords.components(separatedBy: " ")
class ViewController: UIViewController {
// Outlets
#IBOutlet weak var labelWords: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
for word in stringOfWordsArray {
labelWords.text=(word)
}
}
}
I want to be able to have an adjuster for how fast the words show up and a start and stop button. If anyone can help me with that main part that would be great.
The simplest approach is to use Timer so you will be able to call start/stop it and adjust time (2 seconds in example below):
var timer: Timer?
var wordIndex = 0
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(update), userInfo: nil, repeats: true)
}
#objc func update() {
label.text = stringOfWordsArray[wordIndex]
if wordIndex < (stringOfWordsArray.count - 1) {
wordIndex += 1
}
else {
// start again ...
wordIndex = 0
}
}
#IBAction func startAction(_ sender: Any) {
timer = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(update), userInfo: nil, repeats: true)
}
#IBAction func stopAction(_ sender: Any) {
// remember to invalidate and nil it when you leave view
timer?.invalidate()
timer = nil;
}

Simple Timer in iOS

I'm trying to make a simple timer in iOS using Swift.
The program is working fine but whenever my START UIButton is pressed the function of timer starts and runs multiple time as much as the button is pressed.
I want to disable the START UIButton as soon as the timer function starts so that it does not run multiple times.
Please help me for the same.
This is my code of ViewController
import UIKit
class ViewController: UIViewController {
var time = 0.0
var timer = Timer()
#IBOutlet weak var lbl: UILabel!
#IBAction func start(_ sender: UIButton)
{
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(ViewController.action), userInfo: nil, repeats: true)
}
#IBAction func pause(_ sender: UIButton)
{
timer.invalidate()
}
#IBAction func reset(_ sender: UIButton)
{
timer.invalidate()
time = 0.0
lbl.text = ("0")
}
func action()
{
time += 0.1
lbl.text = String(time)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
To disable your button you can just set it's isUserInteractionEnabled property in start function:
#IBOutlet weak var startButton: UIButton!
#IBAction func start(_ sender: UIButton) {
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(ViewController.action), userInfo: nil, repeats: true)
startButton.isEnabled = false
}
Then set it back to true in pause and reset methods:
#IBAction func pause(_ sender: UIButton) {
timer.invalidate()
startButton.isEnabled = true
}
#IBAction func reset(_ sender: UIButton) {
timer.invalidate()
startButton.isEnabled = true
//the rest of your code
}
The most reliable way to ensure that the timer is only started and stopped once is writing two methods which check the current state (timer is not nil if it's currently running):
var timer : Timer?
func startTimer()
{
guard timer == nil else { return }
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(ViewController.action), userInfo: nil, repeats: true)
// disable start button
}
func stopTimer()
{
guard timer != nil else { return }
timer!.invalidate()
timer = nil
// enable start button
}
In the methods you can enable/disable the button(s) accordingly.

Unrecognized selector error when I tap a button

I'm having some problem with my app. The message Thread 1: Signal SIGABRT keeps popping up when I press a UIButton.
Here's my code:
import UIKit
class ViewController: UIViewController {
#IBOutlet var instructions: UILabel!
#IBOutlet var lockStatus: UIImageView!
#IBAction func hackButton(sender: AnyObject) {
let timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "update", userInfo: nil, repeats: false)
while(timer == 1){instructions.text = "loading"}
while(timer == 2){instructions.text = "loading."}
while(timer == 3) {instructions.text = "loading.."}
while(timer == 4){instructions.text = "loading..."}
while(timer == 5) {instructions.text = "hack successful!"
lockStatus.image = UIImage(named: "unlocked.png")
timer.invalidate()
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I checked the debugger, and it keeps saying that I sent an unrecognized selector sent to instance 0x7fa7baebc4c0. Can someone help me figure out what this means?
You are trying to use a method called "update" when you create your timer but your code (at least the portion you shared) does NOT have an update function.
#IBAction func hackButton(sender: AnyObject) {
let timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "update", userInfo: nil, repeats: false)
}
func update() { // do your updates here
}
That's because timer == 1 doesn't mean anything. The function update will be called by the timer and from there you can yourself keep a counter and increment it.

I can't control / understand the flow of code

in longPressed function, I want it to complete the execution of NSTimer written in initializeTimer() and then go to validate(). But it doesn't seem to happen. Can someone please make understand my mistake, as I'm unable to get it right ... Output is given after code ....
And i also tried using fire() but it executes only once and I need it to keep on repeating it upon users need...
Thank you ....
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var progressBar: UIProgressView!
var choice:Int = 0
var tim = NSTimer()
override func viewDidLoad() {
super.viewDidLoad()
println("In ViewDidLoad")
let longPress: UILongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: "longPressed:")
self.view.addGestureRecognizer(longPress)
}
func longPressed(recognizer: UILongPressGestureRecognizer){
if(recognizer.state == UIGestureRecognizerState.Began){
println("Long Pressed Begin")
initializeTimer()
validate(true, choice: choice)
}
}
func initializeTimer(){
println("In InitializeTimer")
progressBar.setProgress(0, animated: true)
tim = NSTimer.scheduledTimerWithTimeInterval(1, target:self, selector: "setProgress", userInfo: nil, repeats: true)
}
func validate(res: Bool, choice: Int){
println("In validate ")
}
func setProgress(){
println("In setProgress")
tim.invalidate()
}
}
Output :
In ViewDidLoad
Long Pressed Begin
In InitializeTimer
In validate
in Set Progress

Resources