How to detect the closing of the NofiticationCenter in Swift / Xcode? - ios

I have a UITableView and I want to to reload, when the NotificationCenter has been dismissed over the running app.
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.reloadData()
NSNotificationCenter.defaultCenter().addObserver(self, selector:"dismissedNC", name: UIApplicationWillEnterForegroundNotification, object: nil)
}
func dismissedNC() {
println("NC has been dismissed") // doesn't get printed
self.tableView.reloadData()
}
Any ideas why it doesn't work?

While the notification center was present, the app was never in the background, so it does not come to the foreground when the notification center is dismissed - and so there is no UIApplicationWillEnterForegroundNotification. The app is activated, not foregrounded. Watch for UIApplicationDidBecomeActiveNotification instead.

Related

View will appear is not firing when we dismiss notification settings overlay

I have a view controller when a button clicked I'm taking the user to app settings.
UIApplication.openApplicationSettings()
When I come back from appsettings to app then viewwillappear method is not firing.
or is there any other method that will let us know the appsettings is dismissed and user is seeing the screen right now.
You should be using app lifecycle events (SceneDelegate/AppDelegate), not view controller lifecycle events (viewDidLoad, viewDidAppear, etc). sceneDidBecomeActive(_:) should be fine for your purposes — for iOS 13+, you should be using SceneDelegate to listen to scene phases, like going to settings (becoming inactive) and then coming back (becoming active again).
/// SceneDelegate.swift
func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
/// your code here
}
If you want to listen to sceneDidBecomeActive directly in your view controller, try listening to the didActivateNotification notification.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver( /// add observer
self,
selector: #selector(activated),
name: UIScene.didActivateNotification,
object: nil
)
}
#objc func activated() {
print("View controller is back now")
}
}
Subscribe to following appdelegate events
applicationDidBecomeActive
or
applicationWillEnterForeground
In ViewDidLoad use below code snippet
NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)
and use below method
#objc func applicationDidBecomeActive(notification: NSNotification) {
updateTableUI()
}
Remove observer
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}

iOS viewWillAppear not being called when returning from background, even with UIModalPresentationStyle.FullScreen, in iOS 13+

Why isn't iOS calling viewWillAppear when our application is returning from the background, even when I've set UIModalPresentationStyle.FullScreen?
viewWillAppear is a function that responds to a view controller's state change. Background and foreground states are different; they are done at an app level.
You can still respond app state changes by using notifications:
override func viewDidAppear(_ animated: Bool) {
// ...
NotificationCenter.default.addObserver(self, selector: #selector(didReceiveForegroundNotification), name: UIApplication.willEnterForegroundNotification, object: nil)
}
#objc func didReceiveForegroundNotification() {
// app returned from the background
}
Your view controller will listen to events until it is deallocated or removed as an observer. If you don't want to execute code when the view controller has dissappeared, you can do it on viewDidDisappear:
override func viewDidDisappear(_ animated: Bool) {
// ..
NotificationCenter.default.removeObserver(self)
}

UITabBar Lifecycle's methods are not fired from background start

I've have a UITabBar controller as main controller, with 2 tabs. Each tab is a NavigatorViewController with a UIViewController embedded.
If I open the application from background after a previous cold launch, none of the ViewWillAppear (UITabBarController, UIViewController) is fired.
How can I call the lifecycle of UITabBarChildren when user come from backgroud? (IE: From a notification)
That is not in the life cycle because the state of controllers is not changing during background mode or other application events.
You should observe for applicationWillEnterForegroundNotification
class VC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Listen for application event somewhere early like `ViewDidLoad`
NotificationCenter.default.addObserver(self, selector: #selector(applicationWillEnterForegroundNotification), name: UIApplication.willEnterForegroundNotification, object: nil)
}
// Implement a function that you want to execute when event happen
#objc func applicationWillEnterForegroundNotification() {
// Do anything before application Enter Foreground
}
// Remove observer when the controller is going to remove to prevent further issues
deinit {
NotificationCenter.default.removeObserver(self)
}
}
When application comes from background non viewWillAppear/viewDidAppear is called for any active vc , you need to listen to app delegate like applicationWillEnterForegroundNotification
NotificationCenter.default.addObserver(self, selector: #selector(applicationWillEnterForegroundNotification), name: UIApplication.willEnterForegroundNotification, object: nil)
#objc func applicationWillEnterForegroundNotification(_ notification: NSNotification) {
print("To-Do")
}
You can add an observer to UIApplicationWillEnterForeground in your controllers.
Posted shortly before an app leaves the background state on its way to
becoming the active app.
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self,selector: #selector(self.appEnteredFromBackground(_:)),name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil)
}
#objc func appEnteredFromBackground(_ notification: NSNotification) {
print("From background")
}

Which View Controller is active when app comes to foreground?

Normally I can find out when a View Controller appears with
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
This won't be called though if the user presses the home button or for some other reason the app goes to the background and then returns to the foreground. To find out when the app comes to the foreground I can add an observer to the Notification Center.
class FirstViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterForeground), name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print("FirstViewController")
}
#objc func appWillEnterForeground() {
print("app in foreground")
}
}
However, my problem is that I have a tabbed app I want to know which View Controller is active when the app comes back into the foreground. The Notification center just sends a general message. Even though I an setting the notification observer in the first tab, it could be on any tab when the app goes into the background.
NSNotification.Name.UIApplicationWillEnterForeground
is a notification thrown by Notification Center. So obviously that is not related to any specific VC. What you can do rather is,
#objc func appWillEnterForeground() {
if self.viewIfLoaded?.window != nil {
// viewController is visible
}
}
Though notification of App entering foreground gets triggered to every viewController observing it, only the VC which is currently loaded and visible will have its code in if condition executed. That gives you a control to decide which VC is currently visible.
EDIT 1:
All that you want to figure out is the top ViewController in navigation stack of TabBarControllerwhen app comes to foreGround, you can add the observer for NSNotification.Name.UIApplicationWillEnterForeground only in UITabBarControllerand in
#objc func appWillEnterForeground() {
var vc : UIViewController = tabBarController.viewControllers![tabBarController.selectedIndex]
while vc.presentedViewController != nil || self.childViewControllers.count != 0 {
if vc.presentedViewController != nil {
vc = vc.presentedViewController!
}
else {
vc = vc.childViewControllers.last!
}
}
print("\(vc) should be the top most vc")
}
Use the Notification Observer and your appWillEnterForeground() or any event which gets fired from the observer in all view controllers under tab controller. So whichever the view controller you came back to will get your notification event get triggered in that particular VC. If you're looking for a centralized solution, this scattered gun approach may not work.

keyboardWillShow fire twice

I'm stuck with problem "keyboardWillShow" fires twice, but the "keyboardWillHide" called once.
Here is an example where I'm printing the keyboard sizes as soon as "keyboardWillShow" fires.
I've also put breakpoint in "viewDidLoad" and the observer registers only once.
I've added two elements "UITextField" and "UITextView" and for both it is the same behaviour.
I'm using iOS 9.2, swift lang., xcode 7
Below my ViewController
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardWillHideNotification, object: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
print("keyboardWillShow sizes: \(keyboardSize)")
}
}
func keyboardWillHide(notification: NSNotification) {
print("HideHideHide")
}
}
UPDATE
First time it fires once with sizes:
keyboardWillShow sizes: (0.0, 568.0, 320.0, 253.0)
for the rest it twice with different sizes:(second y position is changed also the height changed)
keyboardWillShow sizes: (0.0, 568.0, 320.0, 216.0)
keyboardWillShow sizes: (0.0, 352.0, 320.0, 216.0)
Probably you subscribe to more than one UIKeyboardWillShowNotification and forgot to unsubscribe from them.
Try to add observer in viewWillAppear and remove it in viewWillDisappear.
Issue is connected to simulator
On the real device it fires once as supposed to be.
Are you only entering this ViewController or are you navigating through several ViewControllers? Right now I can't see any code to unsubscribe from the notifications which means than once you enter this ViewController again it will subscribe again (providing its viewDidLoad method runs again). Weird that only one of them fires twice though. Good practice is to subscribe and unsubscribe in the respective opposite methods. If you subscribe in ViewDidLoad then unsubscribe in deinit. If you subscribe in viewWillAppear, unsubscribe in viewWillDisappear etc.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardWillHideNotification, object: nil)
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
Check so that deinit runs when leaving the ViewController.
I remove all added keyboards and leave only the system's , then the method will fire only one time.If add a new keyboard ,the method still fire two times. Maybe it's a system bug. System Keyboard
Are setting the Text Input Traits - Keyboard Type?
Example: If you set the Keyboard Type as "Number Pad", ideally it should call once, but it gets called twice. Please check that and be sure.
Resolution: You can maintain a bool to check if the keyboard is already up or not, and check its value while executing the selector code block.

Resources