I have a UITextfield with an inputView of PickerView. I want to achieve that when users double tap on the UITextfield, the pickerView shows up modally, like default, and the focus of accessibility changes from the textfield to the pickerView. This is how I do it for now:
func textFieldDidBeginEditing(textField: UITextField) {
let dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(0.65 * Double(NSEC_PER_SEC)))
dispatch_after(dispatchTime, dispatch_get_main_queue(), {
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, self.quantityPicker)})
}
This delegate simply gets called when user double taps the textField and send a UIAccessibilityLayoutChangedNotification after a delay of 0.65 seconds to focus on pickerView.
Then questions are:
I used a delay here to wait for the pickerView pop-up animation to be done, but it will be much better if I can know that pickerView animation is done somewhere and put
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, self.quantityPicker)
there. So is there any delegate or other simple way to know that pickerView is ready to use?
When I double tap the textField, it will read the content of the textField first until it gets intercepted. Is there anyway to disable this to make it like: when I single tap the button, read accessibilityLabel, trait, hints etc, and when I double tap it, it reads nothing and just does its job.
You can use notification centre event UIKeyboardDidShowNotification. In the view controller hosting the UITextField add code to viewWillAppear method:
NSNotificationCenter.defaultCenter().addObserver(self,
selector: #selector(self.keyboardDidShow(_:)),
name: UIKeyboardDidShowNotification,
object: nil)
In the viewWillDisappear method add:
NSNotificationCenter.defaultCenter().removeObserver(self)
Then add a method keyboardDidShow:
func keyboardDidShow(notification:NSNotification) {
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, self.quantityPicker)
}
Of course if you have other input fields in your view controller you'll need to add some conditional code to keyboardDidShow.
Tested and working. Thanks for the question, I've been chasing the same issue.
If you present the view as part of a separate modal view controller, you can use the viewDidAppear method as the callback you are looking for.
-(void)viewDidAppear:(BOOL)animated {
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, outletOfViewYouWantFocused);
}
#Dale's answer in Swift 4:
viewWillAppear
NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidShow(notification:)), name: UIResponder.keyboardDidShowNotification, object: nil)
viewWillDisappear
NotificationCenter.default.removeObserver(self)
keyboardDidShow
#objc func keyboardDidShow(notification: Notification) {
UIAccessibility.post(notification: .layoutChanged, argument: self.quantityPicker)
}
Related
When a user taps on a button, I'd like the keyboard to pop up (which is easy), but I want a view that goes up along with it (sticking to the top of the keyboard). This view will be have a "send a message.." textfield. When the user pushes done, I want the keyboard to go away along with the view.
How do I make this view "stick" to the keyboard?
UITextFields have a property called inputAccessoryView
- Apple Documentation
- Relevant Stack Overflow Answer
This will pin whatever view you assign as that textfield's inputAccessoryView to the top of the keyboard.
Something important from the answer in that link to remember:
Note that the view you use should neither be in the view hierarchy elsewhere, nor should you add it to some superview, this is done for you.
go to your storyboard and add a view(lets call it topKeyboardView) at the bottom of your viewController. and give it the following constraints:
bottom space to bottom layout = 0
and then add the textfield*(i prefer using textView to make it change its height when the message gets too long...)*
and your button(send) on top of topKeyboardView.
lets code now..
go to your viewController.swift and add an IBOutlet to your textField and button and add this function:
//this is will tell if the keyboard hidden or not
func addKeyboardNotifications() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name:UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name:UIKeyboardWillHideNotification, object: nil)
}
// MARK:- Notification
func keyboardWillShow(notification: NSNotification) {
print("keyboard is up")
}
func keyboardWillHide(notification: NSNotification) {
print("keyboard is down")
}
in your viewDidLoad call the function:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
addKeyboardNotifications()
}
run it...
I have some code I call that changes the language in the viewWillAppear section of a viewcontroller inside a navigation controller.
When I hit the back button the language change doesn't take place even though I have code for it to in the viewWillAppear. The only time it switches is when I hit back all the way to the original screen and then start moving forward it changes. Is there any way to have the function in the viewWillAppear work?
Here is my code, I'm using a language changing pod:
//MARK: Language change
//used to change language text for imediate screens
func setText(){
locationsLabel.text = "Locations".localized()
languageLabel.text = "Languages".localized()
termsOfUseLabel.text = "Terms of Use".localized()
privacyPolicyLabel.text = "Privacy Policy".localized()
pushNotificationsLabel.text = "Push Notifications".localized()
contactUsLabel.text = "Contact Us".localized()
}
// Changes text to current language
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "setText", name: LCLLanguageChangeNotification, object: nil)
}
// Remove the LCLLanguageChangeNotification on viewWillDisappear
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self)
}
The viewWillAppear method is only adding a notification observer. The observer is removed in viewWillDisappear. This means that setText will only be called if the LCLLanguageChangeNotification notification is sent while the view is visible.
The update stops as soon as the view goes off-screen due to the navigation behaviour.
To ensure that the text is updated, you also need to call setText inside viewWillAppear:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
setText()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "setText", name: LCLLanguageChangeNotification, object: nil)
}
Implement navigationcontroller delegate methods
navigationController:willShowViewController:animated:
navigationController:didShowViewController:animated:
I have created this little class that solves this problem.
Just set it as a delegate of your navigation controller, and implement simple one or two methods in your view controller - that will get called when the view is about to be shown or has been shown via NavigationController
Here's the GIST showing the code
I am trying to implement a very basic chat feature into my app and I am using constraints to keep everything in the correct place. It is great except for when I need to actually type, and the problem that arises is that the keyboard covers the text field and I not only cannot see the textfield but I cannot dismiss it. Thank you for all help!
In summary,
Using a textfield with contraints at bottom of screen
keyboard shows up and covers it, and I cannot dismiss the keyboard
Just set observers for UIKeyboardWillShowNotification and UIKeyboardWillHideNotification.
Whenever, UIKeyboardWillShowNotification is triggered, move the UITextfield upwards equivalent to the keyboard height. Then, when the UIKeyboardWillHideNotification is triggered, move the keyboard back into place.
Dismiss keyboard by tapping anywhere
override func viewDidLoad()
{
super.viewDidLoad()
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "dismissKeyboard")
view.addGestureRecognizer(tap)}
func dismissKeyboard()
{
view.endEditing(true)
}
I have two views with a label on one of them. On the second view, there is a button. What I want to achieve here is to be able to press the button and it updates the label on the first view. How do I do that? I can't access the IBOutlet from the second view. Is there something that I have to do to the IBOutlet to make it public etc?
You can use NSNotificationCenter for that.
First of all in your viewDidLoad method add this code in your firstViewController class:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "refreshlbl:", name: "refresh", object: nil)
which will addObserver when your app loads.
And add this helper method in same viewController.
func refreshlbl(notification: NSNotification) {
lbl.text = "Updated by second View" //Update your label here.
}
After that in your secondViewController add this code when you dismiss your view:
#IBAction func back(sender: AnyObject) {
NSNotificationCenter.defaultCenter().postNotificationName("refresh", object: nil, userInfo: nil)
self.dismissViewControllerAnimated(true, completion: nil)
}
Now when you press back button from secondView then refreshlbl method will call from firstView.
use custom delegate method create a delegate in second view and access that view delegate function in first view. or use NSNotification or use NSUserdefaults
In my app I have a background task running that alerts the user when a certain event happens. What has to happen next depends on which view is currently on top. How can I determine this? I thought about writing some indicator into a plist file but that seems a bit clunky especially if there's a system way of doing it.
that's how it can be done in objective c
if([self.navigationController.topViewController isKindOfClass:[MYController Class]])
//then do something
I'm sure you cab find out the swift version easily
I can think of two possible ways:
First, you could use NSUserDefaults to set a Bool or String indicator. Assuming you're using Swift, try:
NSUserDefaults.standardUserDefaults().setObject("FirstViewOnTop", forKey:"WhatViewIsOnTop?")
NSUserDefaults.standardUserDefaults().synchronize()
When you want to retrieve the value, use:
let indicatorString = NSUserDefaults.standardUserDefaults().objectForKey("WhatViewIsOnTop?")
You could use an if/else statement from there.
The way I would go about doing it, though, is to subclass UIView and add a z property and add it to the init(). Then you can access it much more elegantly.
This sounds like the perfect opportunity to use NSNotificationCenter. Have the background task post a notification that your view controllers will listen for. When the active viewController gets the notification, it can handle it in the way that is appropriate for that viewController.
Send the Notification from the main queue since your task is running in the background:
dispatch_async(dispatch_get_main_queue()) {
NSNotificationCenter.defaultCenter().postNotificationName("SomethingWonderfulHappened", object: nil)
}
Receive the Notification: For each of your viewControllers that might want to act on the notification, override viewDidAppear to start listening for notifications, and override viewDidDisappear to stop listening for notifications:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "handleNotification:", name:"SomethingWonderfulHappened", object: nil)
}
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self)
}
Notification Handler: Implement one of these for each of your viewControllers. Have each one do what is appropriate for that viewController.
func handleNotification(notification: NSNotification) {
//Action to take when SomethingWonderfulHappened
}