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

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.

Related

scheduledTimer not stopping another ViewController in swift 5

I have set scheduledTimer in a view Controller. But when got to anther view controller, previous view controller timer is continue.Here is my code
var timer: Timer? = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(runTimedCode), userInfo: nil, repeats: false)
Here is function
#objc func runTimedCode() {
//Api Calling
}
when I go to anther viewController, previous Api calling is continue.
Please help me to scheduledTimer go another page/viewcontroller
It's likely that you may not be calling invalidate on the same thread as where you installed the timer. For example, if you installed in on the main thread, wrap invalidate in:
DispatchQueue.main.async(execute: {
self.timer.invalidate()
})
Reference: https://developer.apple.com/documentation/foundation/timer/1415405-invalidate
Timer holds strong reference to current view controller even if your app goes into background or inactive state it still active . Also whenever you push new controller timer is not stoped at all .
Even if you pop your controller it still don't stop timer at this moment . However timer would start with double speed if you initialise timer again . let's understand this problem .
Lets have 3 controllers
HomeScreenVC --> TimerControllerVC --> NewVC
When you initialise timer in TimerController . It create strong reference to your TimerController . Now push NewVC controller on some action method , timer is not stopped at this moment it is still running .
Same case happens with if you pop TimerControllerVC controller to HomeScreenVC . Now you again pushing TimerControllerVC and initialising fresh timer in some method . This time timer would work with 2x speed irrespective of new timer created timer . It creates cycle of recreation if you repeat this step .
Resolving timer issue , remember whenever you declare timer , you must invalidate it in viewwilldissappear method of UIViewcontroller .
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
timer.invalidate()
}
Invalidating timer in some other self declare methods will not helps because there are always some cases when your self declare is not called which will lead to some unexpected issue with timer .

How can I create a segue after a certain amount of seconds between view controllers without any buttons or swipe gestures?

At the entrance of my main storyboard, I have a loader animation from the Lottie library, after a few seconds I want the app to animate to another view controller which is my home page, How do I do this? I would prefer to not use a button or swipe gesture to have to go from the loader to the home page, but would I have do use the Timer class in some way, or would I need to create a segue using storyboard?
I already attempted using other questions similar to this on Stack Overflow, but I continuously get errors about performSegue() not having an identifier
2 ways: Segue or present(_ viewController:UIViewController, animated:Bool, completion: () -> Void)
So, in your 1st ViewController viewDidLoad method you can implement a Timer. Such as, Timer.scheduledTimer(withTimeInterval: TimeInterval, repeats: Bool, block: (Timer) -> Void) and set a time, set repeat to false and then the block, you can do whatever you want, such as present a new view controller. Example:
override func viewDidLoad() {
Timer.scheduledTime(withTimeInterval: 10.0, repeats: false) { (timer) in
self.present(ViewController2(), animated: true, completion: nil)
}
}

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.

Splash screen for watchkit in swift

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.

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.