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.
Related
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()
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.
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.
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()
I would like to implement a splash screen for my watch application. After a long check, i haven come to realize there is not a way as for phone and tablet such launchscreen.xib or launch image in info.plist for watch
To implement splash screen, i made a new starting interface, which will only appear when application starts. To do that
for life cycle i have checked this blog
after that in willActivate() func i have implemented
if first {
first = false
var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("update"), userInfo: nil, repeats: false)
}else{
first = true
self.popController()
}
and my update func
func update() {
self.pushControllerWithName("someListsController", context: nil)
}
my point is; i want this interface to show only when application starts, and when returning this interface , application to end.