How do I connect custom keyboard to text field? - ios

I created a custom keyboard using buttons, but now I'm stuck with buttons that don't do anything.
How will I connect them to a text field?

First, you should be aware of the inputView property of the UITextField/UITextView classes. Basically you can have a custom view (your keyboard for example) and swap it with the default keyboard.
You can get some inspiration from this tutorial that shows how to create a custom keyboard - and how to connect it to your textfield/textview.
You can also read this answer for another way to connect your keyboard to a textfield/textview.

You can handle buttons touchUpInside Action like this to insert text in uitextfield
#IBAction func btnQuestionAction(_ sender: UIButton)
{
self.textDocumentProxy.insertText("?")
}
And to delete character using custom keyboard, You can use this:
#IBAction func btnBackspaceAction(_ sender: UIButton)
{
self.textDocumentProxy.deleteBackward()
}
Hope it will help you :)

Related

How to hook up "Pop Up Button" event handler on selection update?

I added a "Pop Up Button" from XCode Object Library, and wanted to hook it up with event handler that handles menu item selection update (for app's language selection).
The button is already created in *.xib file, and what kind of function should I create to hook up with button events?
I created a IBAction handler like this:
#IBOutlet weak var languageSettingButton: UIButton!
#IBAction func onLanguageSelected() {
// handling selection
// ...
}
But dragging the selector to this IBAction function did not work. What kind of selector function is it expecting? How do I hook it up?
My usage with two menu options regarding two app languages:
The Pop Up Button:
Update:
Followed #Charles Srstka's comment:
The IBAction func needs to have a sender param:
#IBAction func onLanguageSelected(_ sender: Any?) {
}
However the interesting thing is, we are able to hook up the entire button with the IBAction func, but not the menu item's selector, what should we do with the selector here? does it need to be hooked up with a function too?
Finally figured out the solution:
Based on Charles' comment, in view controller file, add a selection handler with sender param:
#IBAction func onSelectionUpdate(_ sender: Any?) {
// add logic here
}
In the pop up button's menu item's selector hook, drag it all the way to "File's owner", after dragging, a small menu will show available IBAction functions, choose the one we just created.

How to enable/disable navigation bar buttons [XCode] (swift)

I was wondering how I would be able to go about changing the status of buttons. I want to make it if one of two text fields has text in them then the button will become useable. I have currently turned off the button from the storyboard. The code I have to check if there is text inside of the text fields is as follows:
Disclaimer:
The code to check if the text field has any text in it works perfectly fine.
#IBAction func textFeildEditingChanged(_ textField: UITextField) {
if FirstName.hasText == true {
self.navigationItem.rightBarButtonItem?.isEnabled = true
print("First name isn't empty")
}
}
The current code that I have in there to set the button to enabled and disable doesn't work however the code to test if the text field has content does work. I just need to figure out how to disable and enable the button of a navigation item.
code that doesn't work is below:
self.navigationItem.rightBarButtonItem?.isEnabled = true
Any help would be greatly appreciated.
Edit: I am using a navigation controller, don't know if that's important or not.
If you are using the storyboard, just connect the outlet then disable it.
#IBOutlet var navigationItemButton: UIBarButtonItem!
then
navigationItemButton.isEnabled = FirstName.hasText

IOS Swift IBAction behaviour when combining actions

I have a custom keyboard extension which works as expected but I am coming across some odd behaviour which I can't explain. It is designed primarily for data input into Excel spreadsheets, so the fewer the keystrokes the better.
I have 2 IBActions.
Keypressed takes the value of the keypresses and inserts it into the current cell.
Returnpressed emulates the enter key which moves the cursor onto the next cell.
These work as described above, which is all good, but I am now trying to combine the actions, so that the user only has to press the first key and it inserts the text and then moves onto the next cell.
So when I simply extend the code in the Keypressed IBAction to include the code in the Returnpressed action, it simply inserts a carriage return into the text and stays in the same cell.
What am I missing please?
Here is a code snippet:
extension UIKeyInput{
func `return`() -> Void{
insertText("\n")
}
}
class KeyboardViewController: UIInputViewController, AVAudioPlayerDelegate {
#IBAction func KeyPressed(_ sender: Any) {
let string = (sender as AnyObject).titleLabel??.text
(textDocumentProxy as UIKeyInput).insertText("\(string!)")
**//THIS IS THE LINE THAT FIXED THIS FOR ME
textDocumentProxy.adjustTextPosition(byCharacterOffset: -1)**
self.EnterPressed(nil)
}
#IBAction func EnterPressed(_ sender: Any?) {
//default action for a return key press
textDocumentProxy.return()
}
I think you need to override the UITextInputDelegate textDidChange method (UIInputViewController implements UITextInputDelegate).It turns out that textDidChange is called when the text changes. And make the first responder to the next text field of your cell.
I managed to fudge this by determining what action s caused textDidChange to fire. It turns out that by simply adjusting the cursor portion, between inserting the text and firing the Return action works.
Not really sure how, but achieves what I want without the the user knowing it is a kludge and no overhead. I have changed the original code snippet to show the fix.

How to disable UIControlEventEditingChanged

I have a UIViewController with several UITextFields. When tap one text field, it should present the barcode scanning view controller. Once the scanning is completed, my barcode scanning viewcontroller is disappearing (used "dismissViewcontroller") and the scanned value should entered into the text field I tapped. This is working fine. I have set the delegate for each text field like this.
[field addTarget:metrixUIViewControllerIn action:#selector(executeScriptOnTextFieldChange:) forControlEvents:UIControlEventEditingChanged];
The problem is this :
Lets say I have set an alert to display inside this executeScriptOnTextFieldChange method. Once I tapped on the 1st text field, then the barcode scanner comes. Once I scanned barcode scanner closes and set the value for the first text field and fire the alert.Thats ok. But then if scanned by tapping the 2nd textfield and the string will set to that textfield and fire the alert related to 2nd textfield also fire the alert related to first textfield as well. I want to stop happening this. Is there any way to disable the delegate for one textfield? This happens because I am refreshing the view in the viewDidAppear. But I have to do that as well. Please help me.
UIControlEventEditingChanged for a textField can fire at many different events that are not even directly related to that textField, but related inderectly.
For instance, when your ViewController is presenting the barcodeScanner it may trigger a "resignFirstResponder" event on the textField. Also when the 2nd textField is tapped, cause the 2nd becomes first responder and the 1st suffers a "resignFirstResponder".
I suggest trying to use a UITapGestureRecognizer in your textField instead. Example:
Swift 4
#IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.textField.tag = 1
self.textField.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(fireTextField(_:))))
}
#objc func fireTextField(_ sender: UIGestureRecognizer){
let view = sender.view
guard view != nil else{
//Do nothing
return
}
let condition = view!.tag == 1
if condition{
//or do whatever other stuff you need
self.textField.becomeFirstResponder()
}else{
//Whatever for other textFields
}
}
This way, you could use the "tag" attribute to determine which textField is firing and so adjust "condition". You could also filter the flow with a switch using the "tag".
Not sure if any of this will really help as I would need more info about the flow you need to accomplish. Hope it does help!

How to connect UIButton to action using only interface builder

At the moment I am setting up my buttons for my keyboard with the following code:
func setupButtons() {
for subview in self.view.subviews {
if subview.isKindOfClass(UIButton) {
setupButton(subview as! UIButton)
}
}
}
func setupButton(btn: UIButton) {
btn.addTarget(self, action: #selector(KeyboardViewController.keyPressed(_:)), forControlEvents: .TouchUpInside)
}
But is there anyway I can skip this and all the target inside the layout builder so I can save a little bit of time looping through buttons on each keyboard view?
Sure, there are two ways to connect objects to Interface Builder, through outlets and actions.
class ViewController: UIViewController {
#IBOutlet weak var doStuffButton: UIButton!
#IBAction func doStuff(sender: UIButton) {
//do stuff
}
}
After adding that code look in interface builder and click on the view controller for this class.
Click on the connections inspector
Under outlets you will now see doStuffButton, however over the circle to the right of that, press control and click with your mouse, and drag it over to the button you want to connect it to and release. Setting outlets is NOT required for enabling actions. I just wanted to show this to you as well.
In the same pane you will also see received actions and the doStuff: method. Click and drag in the same way and release on the button. Select which type of event you want to process this action (normal is touch up inside).
Once you're all hooked up it should look like this:
There are many other variations of how to do this, but I thought this would get you a quick start.
If you're trying to ask how to do this without coding anything, just go into the assistant editor view of Xcode and Ctrl-drag from your button to the controllers class file. Then when the pop up displays, change outlet to action and give it a method name. This creates the IBAction method for you.
But in reality, the way you are doing it now with the for loop is far better. Especially if you have many buttons.

Resources