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.
Related
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")
}
I have added observers so that I can move textFields above keyboard.
Its working fine on all devices but keyboardWillAppear function is not called when running in simulator iPhone 4S (iOS 9).
This is happening when I "Connect Hardware Keyboard"
keyboardWillHide is being called instead of keyboardWillShow
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector:"keyboardWillAppear:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector:"keyboardWillDisappear:", name: UIKeyboardWillHideNotification, object: nil)
}
func keyboardWillAppear(notification: NSNotification){
// This is not called on simulator iPhone 4S(iOS 9.0)
}
func keyboardWillDisappear(notification: NSNotification){
// This is called
}
I really can't understand this behaviour. Can someone please explain the internal functionality which leads to this.
I'm using the the NSNotificationCenter.defaultCenter().postNotificationName function with the applicationDidEnterBackground function. So first I add these to AppDelegate.swift:
func applicationDidEnterBackground(application: UIApplication) {
println("applicationDidEnterBackground")
NSNotificationCenter.defaultCenter().postNotificationName("com.test.mytest", object: self)
}
And I add these to ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "myTestFunc", name: "com.test.mytest", object: nil)
}
func myTestFunc () {
println("CALLED")
}
And up to now, everything is working fine, the console print out the right thing when I enter background:
applicationDidEnterBackground
CALLED
But after I add a new view controller in Storyboard and connect both of them using any of Segue:
And now when I run the app, after I clicked two buttons and then back to the home page, the applicationDidEnterBackground is still calling once but the NSNotificationCenter is called twice:
applicationDidEnterBackground
CALLED
CALLED
So how can I solve this weird problem?
EDIT
I've also tried this, but still same result:
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().removeObserver(self, name: "com.test.mytest", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "myTestFunc", name: "com.test.mytest", object: nil)
}
Ah, I forget the viewDidLoad is loaded twice... I solved by:
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self, name: "com.test.mytest", object: nil)
}
I have experienced the same problem. In my case solution was enabling Background Modes in project capabilities tab.
I'm trying to create sliding menu similar to Code Menu in SpringApp here (see CodeViewController):
https://github.com/MengTo/Spring/blob/master/SpringApp
It works just fine as long as SpringViewController is Initial View Controller.
If I create another ViewController and make it initial then minimizeView/maximizeView is never getting called:
UIApplication.sharedApplication().sendAction("minimizeView:", to: nil, from: self, forEvent: nil)
In this method "to:" is set to nil to use the first responder. So it looks like when SpringViewController stops being initial view controller it's no longer first responder.
How do I fix it to make minimizeView and maximizeView actions defined in SpringViewController always work?
I found a workaround using notification center.
In SpringViewController add:
override func viewDidLoad() {
super.viewDidLoad()
// Add observer in notification center
// It receives notifications from menu
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: "minimizeView:",
name: "minimizeView",
object: nil)
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: "maximizeView:",
name: "maximizeView",
object: nil)
}
In OptionsViewController:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(true)
// skipped ...
// Post notification
NSNotificationCenter.defaultCenter().postNotificationName("minimizeView", object: nil)
}
#IBAction func closeButtonPressed(sender: AnyObject) {
// skipped...
// Post notification
NSNotificationCenter.defaultCenter().postNotificationName("maximizeView", object: nil)
// skipped...
}
This makes maximizeView and minimizeView work properly. I wonder if the approach with first responder is better.
You need this for view controllers to be able to receive actions:
- (BOOL)canBecomeFirstResponder {
return YES;
}
I'm trying to get the UITextViewTextDidChangeNotification to work. I'm new to using the NSNotificationCenter, so I'm having a hard time understanding what's going on exactly. I have a UITextView in a storyboard, and I've created an IBOutlet for it in my ViewController class and called it textView.
This is my viewDidLoad function:
override func viewDidLoad() {
super.viewDidLoad()
origin = self.view.frame.origin.y
if let field = textView{
field.placeholder = placeholder
field.layer.cornerRadius = 8
field.layer.borderWidth = 0.5
field.layer.borderColor = UIColor.grayColor().CGColor
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyPressed:"), name:UITextFieldTextDidChangeNotification, object: nil);
}
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name:UIKeyboardWillShowNotification, object: nil);
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name:UIKeyboardWillHideNotification, object: nil);
}
The keyboard notifications work great. To my understanding, they call a function with the same name as the selector. Is that correct? Or is there something more going on here? I made a function called keyPressed that took an NSNotification as a parameter, but that function never got called whereas when I engage the keyboard, the keyboardWillShow and keyboardWillHide functions are called. Can someone explain what's going on?
Alright, so I figured out a solution. I needed to post the notification in the textViewDidChange function. I did that using this:
NSNotificationCenter.defaultCenter().postNotificationName("keyPressed", object: nil)
Then I recognized the notification in my viewDidLoad function with this:
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyPressed:"), name:"keyPressed", object: nil);
Then I made this function under my viewDidLoad function:
func keyPressed(sender: NSNotification) {
}