Stopping a loop mid-way using a button Swiftui [duplicate] - swift

I need to figure out how to stop a loop. I've tried a couple different kinds of loops, but the end result is the same - the stop button cannot be pressed while the loop is in progress. Here's what I have so far...
var stopButtonPressed = false
var numArray = [0,1,2,3,4,5,6,7,8,9]
for num in numArray{
print(num)
sleep(2)
if stopButtonPressed {
return
}
}
Obviously the button pressed sets stopButtonPressed to true, but again... the button can't be pressed while the loop is iterating. So what's a better way to do this?
EDIT
#dasblinkinlight
Ok, so I can follow the logic of what you're saying, but the implementation is still confusing. In your example, for the selector, you put "MyClass.doOneStepOfLoop" and that makes perfect sense. Only the timer is unwilling to accept a function as a parameter. So how can I have it do a step if it won't accept a function?
To simplify the problem, how about we just have it print("1") every two seconds? Based off your example, I would think that would look like this...
self.timer = NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: #selector(print("1")), userInfo: nil, repeats: true)
But this doesn't work and gives the error
Argument of '#selector' does not refer to an initializer or method
I looked through the Q&A you posted as well, and unfortunately I still don't get it. I see lots of pseudo code like "YourClass.doAStep" or "Class.buttonPressed" or "Class.test" in for the selector... could you demonstrate some actual implementation of this?

You misunderstand the way Cocoa applications handle events: the app will not respond to events until your method exits.
The proper way to do something every two seconds is to set up a timer, not to use a loop. Change the handler of stop button press to stop the timer:
// This goes into your viewWillAppear
self.timer = NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: #selector(MyClass.doOneStepOfYourLoop), userInfo: nil, repeats: true)
// This goes into your stopButtonPressed handler
self.timer.invalidate()

Related

Variables are not updated in timer completion block

Here is the scenario:
I want to scroll(or shake) a horizontal collectionView after a page showing up so user could see it scrolling...
I do it with no problem 3 sec after page pops up but I don't want it to do the dance when user reach the collectionView before 2 sec & scrolls it itself.
So here is my solution for it:
var collectionDidScroll = false
func scrollViewDidScroll(_ scrollView: UIScrollView) {
collectionDidScroll = true
}
override func awakeFromNib() {
super.awakeFromNib()
Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(showScrollTutorial), userInfo: nil, repeats: false)
}
#objc func showScrollTutorial() {
if !collectionDidScroll {
collectionView.shakeCells()
}
}
The Problem:
The "collectionDidScroll" is update just fine in class, but in timer completion its always false!
It means "collectionDidScroll" is not updating in completion & it has its launch time value.
Notice:
My class is a subclass of UICollectionViewCell
I even tried dispatchQueue with timer & timer with completion block but the results are the same
The comments above answered your question, but to help you tackle similar problems in the future yourself:
Whenever you have problems like this, you should try littering your code (especially scrollViewDidScroll and showScrollTutorial) with print-statements to see if they get called at all and which values they contain. Or you can set breakpoints!
Problem Solved!
I placed 'showScrollTutorial()' function contents inside a 'DispatchQueue' to force it to run in main thread, now it works like a charm!
Thanks for everyone.

Swift - Calling a method in a UIViewController from a separate UIView class

Okey, I have been struggling this for a long time now.. Cant get it to work, and I know there is an easy solution that I cannot find.
I have a UIViewController displaying the main app. When it launches there is two containerViews overlaid that displays a count down for the app to start. Countdown is done, and the "overlay" viewcontrollers are removed from superview.
BUT, I need to call the function to remove VC from superview and start the app in the main UIViewController. But I am not able to trigger this function from the separate "overlay" UIView..
If I make an instance of the UIViewController to trigger the startGame() function it works, but then all the labels return nil, and the app crashes.. I believe this is because the UIViewController is triggered twice?
A lot of explaining here.. I will try to show some outtakes in code:
Main UIViewController:
class DMStartVC: UIViewController {
.....
func startGame() {
self.view.viewWithTag(100)?.removeFromSuperview()
self.view.viewWithTag(101)?.removeFromSuperview()
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(DMStartVC.update), userInfo: nil, repeats: true)
UITimer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(DMStartVC.UIUpdate), userInfo: nil, repeats: true)
}
....
}
The UIView handling the "overlay" container views:
class countdown: UIView {
let dmStartVC = DMStartVC()
...
//when animation is done:
dmStartVC.startGame()
}
This causes crash because all labels in the UIViewController is nil.
How can I reach that .startGame() function?
Only way I have been able to solve this is adding a timer inside the DMStartVC to trigger .startGame()... But that is not a good solution as the timer has to trigger at the exact time the countdown ends.. and it never does.

NSTimer with drawing barChart in Background

I'm using an NSTimer in an iOS App in background, which is saving some data every 30 seconds in an array. The app shows the last 10 values (values of 5 minutes) in a linechart.
My problem is to use the function of saving data into the array every 30 seconds also in background, when the app isn't on screen. I've written a lot of themes about this, but I don't understand it.
My timer is the following:
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: (#selector(ViewController.counting)), userInfo: nil, repeats: true)
func counting() {
timerCounter += 1 //Int
if timerCounter%30==0 {
arrayOfValues.append(...) //Appending the array
reloadLineChart() // reload chart
}
}
Could anyone show me how to solve this? I know, there must be something with the background-methods in the ViewController, but don't now what to type in.
I think there must be a function, which is counting in background and a function that is reloading the chart when I'm back in the app.
I might understand that you don't want to declare the counter in app delegate, for whatever reason you might have,although I would recommend it. However you can call the functions I mentioned, from the same class in which you have defined the counter. You would need to call it like this:
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: "applicationWillResignActive:",
name: UIApplicationWillResignActiveNotification,
object: nil)
}
func applicationWillResignActive(notification: NSNotification) {
// stop your counter
}
Then do the same with the other function. Hope is clear.
In your app delegate you can use this method :
func applicationWillResignActive(application: UIApplication) {}
The code you add in this function will run right before your app goes in the background . Therefore you can write there the code to stop the counter.
Afterwards you need to use the following function to activate the counter again:
func applicationWillEnterForeground(application: UIApplication) {}
The code you write in this function will run when you come back to the app again. Hope it is clear enough.

reloadData in UITableView on uibutton click - swift

I've been playing around with the UITableView in swift.
Firstly, my goal is to append a cell with array data on click of the UIButton.
I've managed to do this with the following action
#IBAction func Option1Click(sender: UIButton) {
arrayOfQuestions.append(QuestionMark(Label:"Grocery List", option1:"yes", option2:"no")) }
But the issue is i cannot get to reload the tableView, because the tableView.reloadData() isn't inherited.
Secondly, i want to apply a timer. So that the appending of the cell would take a specific time interval.
I've managed to find a function on the forums
var timer = NSTimer.scheduledTimerWithTimeInterval(3, target: self,
selector: Selector("Option1Click"), userInfo: nil, repeats: true)
But i don't know where exactly to place this code to work.
Would appreciate any insights greatly!
Thank you.
You should get a reference to your tableview as an outlet and name it whatever you like.
After you can use the reference of the tableview and call the reload method
like this:
nameofyourreference.reloadData()

Detecting user inactivity in any VC

I need to be able to keep track of the last user activity (touch on screen). I found this question and answer here however this does not work for me in swift at all. idleTimer.release is not a thing and I just get an error. Also, I am assuming that you have to create an NSTimer since idleTimer is not a thing either unless you make it. I am also confused by the line
idleTimer = [[NSTimer scheduledTimerWithTimeInterval:maxIdleTime target:self selector:#selector(idleTimerExceeded) userInfo:nil repeats:NO] retain];
So, how can I accomplish this in swift? A bit frustrated!
Here's how I do it in my app. Create the timer to check every so often:
self.timer = NSTimer.scheduledTimerWithTimeInterval(
10, target: self, selector: "decrementScore", userInfo: nil, repeats: true)
Now we know that if decrementScore() is called, 10 seconds of idle time went by. If the user does something, we need to restart the timer:
func resetTimer() {
self.timer?.invalidate()
self.timer = NSTimer.scheduledTimerWithTimeInterval(
10, target: self, selector: "decrementScore", userInfo: nil, repeats: true)
}
For each view controller you want to track the last user activity of, create a property with either a NSDate or a NSTimeInterval.
Then, make liberal use of either NSResponder or UIResponder (you didn't specify whether or not you're doing this in iOS or MacOS) methods in your view controller subclass, which is what each application, window and view is built on top of.
UIResponder, for example, has methods for "touchesBegan:withEvent:", and you can reset your view controller's date property each time a touch happens in the view.