How to update UI at main queue in Swift? - swift

I want to create a splash screen application that I also have a Class named EXClass.
for some reasons the EXClass must initialize in main thread and update some UI.
please reference following swift code.
func applicationDidFinishLaunching(aNotification: NSNotification) {
self.windowController = ......
self.windowController.showWindow(self)
// In order to show window at the very first time, I create a main queue
dispatch_async(dispatch_get_main_queue()) {
self.ex = EXClass() // this class must init in main thread
}
}
If I init EXClass in dispatch_async(dispatch_get_main_queue()) that init class is ok but update UI is no effect. why?

Related

How to use applicationWillResignActive to call a function of a view controller

I am trying to call my menu view inside my view controller when the home button is pressed, or for that matter when the user gets a phone call, etc...
My goal is to call the function: toggleMenu() that is inside the QuestionViewController. Here's the code:
class QuestionViewController: UIViewController, MFMailComposeViewControllerDelegate {
///////////
func toggleMenu() {
// Show or hide menu
if menuStackView.isHidden == true {
// Show the menu
print("Showing Menu")
// Update labels
questionNumberMenuLabel.text = questionNumberLabel.text
endTimer()
menuStackView.isHidden = false
animateInMenu()
} else {
// Hide the menu
animateOutMenu()
}
}
I do believe I should utilize the following method found in the AppDelegate.swift file:
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
If I'm on the right track I need help calling the toggleMenu() function of the QuestionViewController class in this method. How do I do that?
Thanks so much!
Use the NotificationCenter and listen for UIApplicationWillResignActiveNotification. The system broadcasts that notification as well as calling your app delegate's applicationWillResignActive method (assuming you have one.)
Listen for notifications (using the method addObserver(forName:object:queue:using:) in your viewDidLoad. If you don't need to support iOS versions < 9.0, you don't need to worry about calling removeObserver - the system takes care of it for you.

View/Window won't update until #IBAction is complete

I have a button linked to an IBAction that takes around 5-10 seconds to complete.
While the IBAction function is running I want to print progress messages in my view/window.
The only message that prints is the last one, and it prints after the IBAction has completed (ie the button is not blue).
When I step through the function in the debugger, no messages are visible until I'm out of the function (ie button is not blue).
Any idea on how to update the window while the callback is being executed?
embed the IBAction logic inside a background queue:
DispatchQueue.global(qos: .background).async {
// background code
}
and anytime you need to print the progress in your view/window embed that part inside:
DispatchQueue.main.async {
// user interface code
}
generally you may want to try this:
DispatchQueue.global(qos: .background).async {
//background code
DispatchQueue.main.async {
//your main thread
}
}

Currently messing with Swift Reachability, should I do all my code in viewDidLoad or in viewDidAppear?

I'm currently trying to implement Reachability into my current project. I followed a tutorial on YouTube that worked but I'm unsure whether or not its the correct way of doing it. In the Reachability documentation (https://github.com/ashleymills/Reachability.swift) it shows two examples first one being 'Example - closures' where I assume it's done in the viewDidLoad?
//declare this property where it won't go out of scope relative to your listener
let reachability = Reachability()!
reachability.whenReachable = { reachability in
// this is called on a background thread, but UI updates must
// be on the main thread, like this:
DispatchQueue.main.async {
if reachability.isReachableViaWiFi() {
print("Reachable via WiFi")
} else {
print("Reachable via Cellular")
}
}
}
reachability.whenUnreachable = { reachability in
// this is called on a background thread, but UI updates must
// be on the main thread, like this:
DispatchQueue.main.async {
print("Not reachable")
}
}
do {
try reachability.startNotifier()
} catch {
print("Unable to start notifier")
}
and the last example was 'Example - notifications', this is where I get confused the creator says to do that all in viewDidAppear. Is there really a big difference if I just do everything inside viewDidLoad? Does it change the outcome of anything? It currently works fine but I'm not sure whether it's right, I don't want it affecting me in the future. Any help would be great! Thanks.
It depends on your needs.
If you want to use Reachability...
... dynamically only if this particular view is frontmost, startNotifier() in viewWillAppear and stopNotifier() in viewDidDisappear.
... in this particular view as long as the view is alive/loaded startNotifier() in viewDidLoad.
... globally in all views put the entire code in AppDelegate and post notifications.

Swift - performSegueWithIdentifier in thread

i've got this type of problem :
With my app i'm able to connect my iPhone with a BLE device so when i connect their together i'm transported into an another view.
Into this view i've to check always if i'm connected yet in a thread func.
If my connection is lost i've to call the performSegueWithIdentifier method.
But... When i do it, xCode give me back a bad report that says :
This application is modifying the autolayout engine from a background
thread, which can lead to engine corruption and weird crashes. This
will cause an exception in a future release.
This is my short code :
override func viewDidLoad(){
super.ViewDidLoad()
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0)) {
while(true){
if(serial.connectedPeripheral == nil){
self.GoBackToBLE()
}
}
}
}
I want to precise that i can't use the method dispatch_get_main_queue because if i use that i can't do anything else into the view for example if i have a button I want to be able to press it and if i use dispatch_get_main_queue method i can't.
any suggestions? thanks a lot
You shouldn't do anything related to UI in dispatch_get_global_queue
you can split the things to run into 2 parts, 1 is related to UI other one is doing network calls/calculations etc.
override func viewDidLoad(){
super.ViewDidLoad()
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_main_queue(), { () -> Void in
// do the ui stuff here
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), { () -> Void in
// do everything which is not ui
})
})
}

Updating the main View from a background thread - Swift

When I press a button I start a loop of calculations and need to be able to update the main view as it progresses. As this is a background thread the screen doesn't update until the call has completed. How do I achieve this in Swift? This is my current, simplified, approach which doesn't work.
#IBAction func calculatePower(sender: AnyObject) {
for day in 1...365 {
dispatch_async(dispatch_get_main_queue()) {
self.dayLabel.text = "\(day)"
}
}
}
I typically add some 0s onto the loop otherwise it completes too quickly going from just 1 to 365.
In Cocoa I would use a statement like:
[self performSelectorOnMainThread:#selector(updateDisplayEnergySunEquator:) withObject:[NSNumber numberWithDouble:distanceFromSun] waitUntilDone:YES];
Any ideas?
#IBAction functions are called in the main thread when the button is pressed. What happens is that your closures are queued on the main thread, but your main thread is busy working on your loop. Basically, you are doing asynchronous work within one thread.
To run the loop on another thread, wrap it in a dispatch_async and use another queue.
#IBAction func calculatePower(sender: AnyObject) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
for day in 1...365 {
dispatch_async(dispatch_get_main_queue()) {
self.dayLabel.text = "\(day)"
}
}
}
}