Adding Subviews to custimize a keyboard

****** IMPORTANT*******
Inside the code segements, some code i had was causing the code blocks to mess up and cut out code, so please not that when i write hasPrefix:@(lessThen)UIKeyboard the (lessThen) should be replaced by the actual symbol. so like this hasPrefix:@<UIKeyboard.
***********************

*******************************************************
* There is a code example a few posts down that has all this code in it *
*******************************************************

This tutorial will show an example of how to add content to a UIKeyboard by adding a custom button. The following will show you how to add a decimal button to the numeric keyboard so you can enter decimal values without having to use a full keyboard. The key idea to understand here is that although the UIKeyboard is not directly available to us through the current iPhone SDK, we can still reference an instance of the keyboard using its base class.

UIKeyboard is a subclass of UIView. This is handy because it is very easy to add content to a view using code like below:

[myView addSubview:subview];

This is how we will add a button to our keyboard, by referencing the keyboard and adding a subview.

In my program I only had one keyboard and on text area to use the keyboard, so the following code worked fine for me. This example is not robust enough to handle multiple keyboard and multiple text areas but hopefully it gives you a good idea how things work.

STEP 1: Finding the UIKeyboard

The first thing we have to do is actually get a reference to the UIKeyboard. If your application uses a keyboard, you will find that they keyboard is not actually inside the UIWindow you create in your applicationDelegate, but in a separate UIWindow that is automatically generated. Each iPhone application you create has an array of windows. The example below shows you how to reference an item in your applications array of UIWindows.

The code above works fine if you know what window you want to reference. I am not 100% sure what the max number of windows an application can have is, but in general it is better to write smart code that can automatically find each window, instead of writing working code once and hoping that conditions never change. With that said lets take a look at the following code example. The code blow shows how to iterate through each window in our applications

So now that we are able to loop through all of our application windows, we are ready to starting looking for the UIKeyboard. As mentioned before, UIKeyboard is a subclass of UIView. This is helpful because each UIWindow has an array of its subviews. All we have to do now is loop through each subview of the current window. We can do this with a nested for loop. Below is an extended version of the code above that will show you how to iterate through all the subviews in a window.

Now we are at the point where we can look for the view that contains the keyboard. There are a couple of ways you can check the content/type of a UIView, in my code I do this by checking the prefix of the view description. Through debugging I found out that a UIKeyboard description starts with the text "<UIKeyboard". Knowing this it is very easy to figure out if we have found the right view, by checking the description of each view we find in the code above. We can check the description of the view by using the hasPrefix method. This method will return YES if the string we are comparing against has the same prefix (beginning) as the text we provide. The following code is the complete extension of the last 2 examples. It shows how we iterate though each view for each window in our application and how to check each view to see if it contains our keyboard. Note how you can use the description of a view to determine its contents.

So there you have it. Using a couple simple loops, we can reference our keyboard. Next, we need to add content.

STEP 2: Adding a custom button to the keyboard.

So now that we have a UIView reference of the keyboard. Adding a custom button is actually really simple. All we need to do is add a subview to the keyboard view. Below is the code that I use in my application to add a peroid to my numeric keypad keyboard. Keep in mind that we are referencing the keyboard directly, so position 0, 0 is not the top left of your screen, rather the top left of the keyboard. Remeber that the variable keyboard is the UIView we found in step1.

The code is just as simple as adding any other button. The button has 2 images, one for when its not clicked and one for when it is. When the button is pressed I want to update my textbox by appending a '.' which is show in the code below.

Now this was a little bit more difficult in my case then what I showed, that is because where I add the button, and where the buttons textarea target are in 2 different classes. But there are plenty of examples of calling methods from other classes on these forums so i wont get into that. If I can figure out how I will include the 2 images I used for this button in this tutorial so that anyone who wants to use them can.

STEP 3: Where does all this code go?

So here comes the point where I tell you that I am not 100% sure that what I am about to write is the best way to do things. There are most likely better places to add buttons to your text boxes, I just did it the following way. It will work fine, but you are more then welcome/encouraged to find a better solution.

Using notifications we can easily find out when a keyboard is about to be shown and attach a method to that event. Because a keyboard cannot be show without this method being called, I figured it would be a good place to reference the keyboard. This way we dont waste any time creating a custom keyboard if the user never uses a textarea.

I put all this code into the app delegate. I figured it was a good spot because it will live as long as my application does. Below is the first thing you need to do. As mention before, I wanted to listen for the notification of when the keyboard will be shown. The following line of code shows how to do this.

As seen above, i just told my application that each time the keyboard is shown, I want to call the method keyboardWillShow. This method is where all the code from step 1 and 2 is held. As mentioned this method sits inside of my app delegate. The follow code is what my appDelegate looks like. As you can see I have a function keyboardWillShow that handles events due the the code above. Each time the keyboard is to be shown we will add our button. The way i have done things, if you do not do this each time, the button will not persist. I hope this all makes sense.

If you have any questions let me know. This is probably the only tutorial I have ever written so I understand if it was confusing. Ive also included a screen shot of the keyboard and the images for the buttons you can use to add a decimal.

I subclassed UITextField and want to share this...
Make sure that you import the AudioToolBox framework!
And copy the sound file to your project.
The textField has to be of the type "NumberPad". Otherwise it will look weird.
This will also work with multiple textFields.

Attached is a quick little example project I made as per the request above.

I also included some code that moves the screen up and down as the keyboard is shown so that the textfield is never hidden.

The code is pretty much the same as the tutorial so its not super commented cause i wrote it quickly right now.

Let me know if there are any problems
Thanks

This was a great Example Project, but it didn't do exactly what I needed for my App. Once I figured it out, I went back an modified this project to include my own additions. I also made it a little more Interface Builder friendly.

Thanks keyboardCowboy and brygruver! You're awesome.
But I have this Question.
Does any one has solved the cursor position problem?
What I mean is, suppose you write "1234567890" and then you press "." and everything goes perfect. But how about inserting "." somewhere else rather than the tail?
for example in order to write "1234567.89" you will have to delete 8 and 9 and then write "." and then write "89" again.
I know this is something it has to be with UIKeyboard, which 3rd party programmers are not allowed to play with. But maybe there is a good idea someone could have... Any thoughts?:D

In case of UITextView I think can calculate the actual position by a function of the tapped possition, and current font sizes. But I don't know how accurate this method can be. I case of UITextField I am not sure whether I can do it.

In case of UITextView I think can calculate the actual position by a function of the tapped position, and current font sizes. But I don't know how accurate this method can be. I case of UITextField I am not sure whether I can do it.

Normally you don't need to know the cursor position ahead of the keypress. The cursor position becomes available to you when the key is pressed. So assuming you can generate a real keypress event instead of using the method from the example, do the usual thing...

Just implement a UITextFieldDelegate and in particular the method:

textField:shouldChangeCharactersInRange:replacementString:

The range corresponds to the current cursor position and the string corresponds to the pressed key. If you want to allow the keypress at the given cursor position, return YES. If you don't like it, just return NO. If you want to substitute something in place of the pressed key, use something like

Of course, since there's no publicly documented way to generate a "keypress" event then I guess you may still be stuck!

Oh wait, never mind... In just poking around, I see there's a handy method/property if you use a UITextView instead of a UITextField:

[myTextView selectedRange]

That may be helpful, if you can dress up the text view to look more like a text field, and add the clear button to it, and so forth.... I still don't understand why this method isn't available in a text field. It's obviously very handy.

I used brygruver blog method, and I have it working except it only adds the decimal point to the same textfield. If you have an interface with 4 textfields (textfield1, texfield 2...texfield4). How do you say in the
- (void)addDecimal:(NSNotification *)notification Method to append the '.' to the selected textfield??

This is great! Im having a problem though. Your method for making sure the next keyboard not have the decimal only works if you switch views. What if i have three text inputs on a page and only want one of them to have the decimal? How would you solve that?
Thanks!

This is great! Im having a problem though. Your method for making sure the next keyboard not have the decimal only works if you switch views. What if i have three text inputs on a page and only want one of them to have the decimal? How would you solve that?
Thanks!

I've updated the Example to solve this issue. Please check out my new site.

This method appears to be non-compliant and may now result in rejected applicaitons since it uses an undocumented feature to identify which UIView is the keyboard view.

We're talking about this now on the Apple developer forums - and one of the Apple employees seems to imply that this will result in a rejection.

Developers may wish to report a Bug in the Apple Bug Tracking system in order to elevate this issue and get an official resolution from Apple.

-t

I wrote that original tutorial a while ago, when I didn't know much about iphone development. It worked find for my needs and I though I would share, but it has probably become very outdated. I'm sure there has to be a better way to create custom keyboards these days an encourage anyone to submit an updated tutorial that isn't a hack job like the one I put together.

With that said hopefully the information on here has been helpful to some of you.

Apparently we were warned not to do this in the SDK 3.0 release notes, and since this "trick" relies on an undocumented call to find the name of the keyboard (walking the subviews until we find the one with the name we think it should be) it's going to - if it doesn't already - flag the code checker somehow...

The Apple recommendation was to submit a bug report.

The only other recommendation - from non-Apple employees in the forum - was to create a completely new, custom keypad and handle all the necessary events yourself. Not a great solution.

My App was just submitted with the Decimal point added to the keypad using this trick - so I guess I'll see if it gets through the gauntlet or not.

I've begun work on a totally custom keypad as a backup plan, but it's going to take some time to get it right and fully functional I'm afraid.

Apparently we were warned not to do this in the SDK 3.0 release notes, and since this "trick" relies on an undocumented call to find the name of the keyboard (walking the subviews until we find the one with the name we think it should be) it's going to - if it doesn't already - flag the code checker somehow...

The Apple recommendation was to submit a bug report.

The only other recommendation - from non-Apple employees in the forum - was to create a completely new, custom keypad and handle all the necessary events yourself. Not a great solution.

My App was just submitted with the Decimal point added to the keypad using this trick - so I guess I'll see if it gets through the gauntlet or not.

I've begun work on a totally custom keypad as a backup plan, but it's going to take some time to get it right and fully functional I'm afraid.

-t

Any update on this? I would like to implement a custom keyboard similar to what TipTap had done. Basically it's just a number pad with a clear, decimal point and a Done button which would work perfect for my Apps. It would be nice if within IB you could arrange for yourself a custom keyboard without having to roll everything from scratch.