Is it possible to access/remove buttons from the keyboard on iPad?
I want the user to have access only to the number keyboard without possibility to switch between other types of keyboard. For this, i need to hide/disable "switch buttons" (buttons with label "ABC").
To access the keyboard i use:
UIWindow * tempWindow = [[[UIApplication sharedApplication] windows] objectAtIndex:1];
UIView* keyboard;
for(int i = 0; i < [tempWindow.subviews count]; i++)
{
keyboard = [tempWindow.subviews objectAtIndex:i];
if([[keyboard description] hasPrefix:#"<UIKeyboard"] == YES)
{
// access elements of keyboard
}
}
Also, the problem is that [keyboard.subviews count] = 0 , while the keyboard was found.
Each UITextField conforms to text input protocol called UITextInputTraits which declares property keyboardType. You can define the keyboard in interface builder or calling setKeyboardType: method on your text field. The keyboard types are defined in documentation. In your particular case you should use UIKeyboardTypeDecimalPad for number input.
Related
I'm displaying a UITabBar in my app, and am trying to assign accessibilityIdentifiers to the buttons. To accomplish this, I use the following lines in each of my view controller instantiations:
viewController.tabBarItem.accessibilityIdentifier = #"ViewControllerID";
These viewControllers all get added to the UITabBar like so:
NSMutableArray *tabBarItems = [NSMutableArray array];
for (NSInteger i=0; i<_viewControllers.count; i++) {
UIViewController *viewController = [_viewControllers objectAtIndex:i];
[viewController setCommonTabBarController:self];
[self addChildViewController:viewController];
if (i == 0) {
viewController.view.frame = self.currentTabView.bounds;
[self.currentTabView addSubview:viewController.view];
[self addConstraintsToSubView:viewController.view];
_selectedViewController = viewController;
_selectedIndex = 0;
}
[viewController didMoveToParentViewController:self];
[tabBarItems addObject:[viewController tabBarItem]];
}
[self.tabBar setItems:tabBarItems animated:animated];
So, what I think should be happening here, is we are grabbing the tabBarItem that has the accessibilityIdentifier set correctly (when I set breakpoints, the accessibilityIdentifier of each view controller is what I expect.) Then, when it is actually displayed, there is no accessibilityIdentifier.
Things I've noticed:
iOS is using UITabBarButton instead of UITabBarItem. I think that has something to do with it. When I print out the items array of the tab bar, each of the items has the correct accessibilityIdentifier, however, none of the UITabBarButton objects has the accessibilityIdentifier of the associated tab bar item.
Does anyone know why the accessibility identifier isn't, for lack of a better word, "carrying through" to the UITabBarButton object that iOS uses?
I ran into the same problem trying to add accessibility identifiers to UITabbar items for UITesting, I was able to do it using this workaround which is not perfectly sane but it works:
[self.tabBar setItems:tabBarItems animated:animated];
NSArray *identifiers = #[#"itemIdentifier1", #"itemIdentifier2", #"itemIdentifier3"];
int index = 0;
for (UIControl *control in controller.tabBar.subviews)
{
if ([control isKindOfClass:UIControl.class] && index < identifiers.count)
{
//This is actually the UITabBarButton
control.accessibilityIdentifier = identifiers[index];
index++;
}
}
The key is to assign the identifiers to the UITabbar subviews after the tabs have been added, which means the UITabBarButton objects have been created and ready to have an identifier set.
After recent iOS SDK 8.3 update, I'm unable to properly add subview to the native keyboard (UIInputSetHostView). Before the update I was using this solution (https://stackoverflow.com/a/26041697/1187451) and everything worked fine. However, at the moment this workaround doesn't work properly.. After adding a subview on top of the keyboard and trying to select the item, native keyboard's letters are being selected instead (Screenshot: http://grab.by/H3aY). If I got it right, apple has changed UIInputSetHostView's pointInside:withEvent: return value...
Basically, what I'm trying to do is something simmilar to facebook's messenger app, when user in the chat screen is able to select stickers that are displayed on top of the keyboard.
Maybe some of you know the way to do this?
The code which I'm using:
UIWindow* tempWindow = [[[UIApplication sharedApplication] windows] objectAtIndex:1];
UIView* keyboard;
for(int i=0; i<[tempWindow.subviews count]; i++)
{
keyboard = [tempWindow.subviews objectAtIndex:i];
if([[keyboard description] hasPrefix:#"<UIInputSetContainerView"] == YES)
{
for(int i = 0 ; i < [keyboard.subviews count] ; i++)
{
UIView* hostkeyboard = [keyboard.subviews objectAtIndex:i];
if([[hostkeyboard description] hasPrefix:#"<UIInputSetHost"] == YES)
{
[hostkeyboard addSubview:stickersView];
}
}
}
}
Thank you!
I'm trying to move my first responder on using tags in a tableview cell. I've set _txtFieldActive to pick up the active UITextFields tag. I can see this when I press the next button on the keyboard via NSLog. Now however I can't seem to figure out how to resignfirstresponder on that tag, and then move my first responder onto tag 102?? I get an error on the line of code trying to assign tag 102 to *tmp.
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
switch (textField.tag)
{
case 101:
//Do Nothing do not want to close keyboard but move on to next UITextField
if (_txtFieldActive.tag == 101)
{NSLog(#"Tag = 101");
UITextField *tmp = [textField.tag == 102];
[tmp becomeFirstResponder];
}
break;
case 102:
[textField resignFirstResponder];
}
return YES;
}
Many thanks all for your help in advance for any pointers.
Jon.
To get a reference to a view in the current view's hierarchy with a given tag, we need to call viewWithTag:.
if (_txtFieldActive.tag == 101) {
NSLog(#"Tag = 101");
UITextField *tmp = [self.view viewWithTag:102];
[tmp becomeFirstResponder];
}
Try that on for size.
If this is a UITableViewController subclass rather than a UIViewController subclass, you might need [self.tableView viewWithTag:102];, but self.view should work in either case.
I am new to Objective-C, and I am looking to limit a user from switching from the alphabet portion of a normal keyboard to the numeric/punctuation side. This being said, I would like to disable the button on the keyboard that does the switch. Maybe I'm putting in the wrong search parameters, but I'm finding little on Google and even less in my reference books. How would I go about actually disabling a button on an iOS keyboard? With alpha/numeric/punctuation characters this would be easily done by just ignoring the inputed characters that were entered, but the button that switches between keyboard portions does an action as opposed to returning a character.
Thank you for your help!
You actually can add and remove buttons of the default UIKeyboard
There's some recipes on the Internet like this: http://www.iphonedevsdk.com/forum/iphone-sdk-development/6573-howto-customize-uikeyboard.html and like this: http://www.iphonedevsdk.com/forum/iphone-sdk-development/6275-add-toolbar-top-keyboard.html
Those posts show you how to add a button, however the same principle can be used to remove.
Below I'll show you a compilation of one of the solutions:
//The UIWindow that contains the keyboard view -
//It some situations it will be better to actually
//iterate through each window to figure out where the keyboard is,
// but In my applications case
//I know that the second window has the keyboard so I just reference it directly
//
UIWindow* tempWindow = [[[UIApplication sharedApplication] windows]
// objectAtIndex:1];
//Because we cant get access to the UIKeyboard throught the SDK we will
// just use UIView.
//UIKeyboard is a subclass of UIView anyways
UIView* keyboard;
//Iterate though each view inside of the selected Window
for(int i = 0; i < [tempWindow.subviews count]; i++)
{
//Get a reference of the current view
keyboard = [tempWindow.subviews objectAtIndex:i];
//Check to see if the className of the view we have
//referenced is "UIKeyboard" if so then we found
//the keyboard view that we were looking for
if([[keyboard description] hasPrefix:#"<UIKeyboard"] == YES)
{
// Keyboard is now a UIView reference to the
// UIKeyboard we want. From here we can add a subview
// to th keyboard like a new button
//Do what ever you want to do to your keyboard here...
}
}
I implemented a neater solution. The trick is to place a disabled key image on top of the keyboard.
To do this
Run emulator (in 100% scale) and screen grab the asset you'd like (in my case, this was a disabled Done button at the bottom right end)
Place this image on top of the keyboard
Note that keyboard is placed in a separate UIWindow (since iOS5 I believe) and thus, you will need to do the following
+ (void) addSubviewToTop:(UIView *)view {
int count = [[[UIApplication sharedApplication] windows] count];
if(count <= 0) {
warn(#"addSubviewToTop failed to access application windows");
}
UIWindow *top_window = [[[UIApplication sharedApplication] windows] objectAtIndex:count-1];
[top_window addSubview:view];
[top_window bringSubviewToFront:view];
}
I'm not sure if the SDK has changed such that #ppaulojr's answer no longer works, or if I just have things set up weirdly on my system, but with the following tweaks I was able to get it to work!
The posts linked in #ppaulojr's answer are great (http://www.iphonedevsdk.com/forum/iphone-sdk-development/6573-howto-customize-uikeyboard.html and http://www.iphonedevsdk.com/forum/iphone-sdk-development/6275-add-toolbar-top-keyboard.html), and they helped me to get this to work.
Apparently the actual keyboard view is now embedded as a subview in some grander UIKeyboard view structure so a bit of recursion is involved. I got this to work:
-(void) findKeyboard {
NSArray* windows = [[UIApplication sharedApplication] windows];
for (int i = 0; i < [windows count]; i++) {
UIWindow* tempWindow = [[[UIApplication sharedApplication] windows]
objectAtIndex:i];
for(UIView *subView in [tempWindow subviews])
{
[self checkViews:subView];
}
}
}
-(void)checkViews:(UIView *)inView
{
for(UIView *keyboard in inView.subviews)
{
NSLog( #"ViewName: %#", [keyboard description] ); // Which view are we looking at
//Check to see if the className of the view we have
//referenced is "UIKeyboard" if so then we found
//the keyboard view that we were looking for
if([[keyboard description] hasPrefix:#"<UIKeyboard"] == YES)
{
// Keyboard is now a UIView reference to the
// UIKeyboard we want. From here we can add a subview
// to th keyboard like a new button
//Do what ever you want to do to your keyboard here...
break;
}
// Recurse if not found
[self checkViews:subView];
}
}
I also found that the best place to call this function is from -(void)textViewDidBeginEditing:(UITextView *)textView like so:
- (void)textViewDidBeginEditing:(UITextView *)textView {
NSLog(#"textViewDidBeginEditing");
[self findKeyboard];
}
This does the keyboard modifications as soon as the keyboard is added to the window, but before it actually shows up, so that the whole time it raises from the bottom, it will have been modified.
In my app I have a pretty large form to be filled out. The form itself is made with a table view and static cells mode. Each of the cells containing a label and a textfield. I wanted to add a toolbar above the keyboard to enable navigating between the text fields. I did that, however it is behaving very strange (at least I think).
I have no problems jumping to the next text field while jumping to the previous textfield is only possible if it is visible. If it is not visible the old textfield stays first responder, however the moment I scroll my table view and the intended text field becomes visible, it becomes the first responder (I do not click into it!). Well this is of course not the behavior that I wanted.
My question is: Is this kind of behavior normal? Could you somehow circumvent it?
If you want to see this kind of behavior yourself I have uploaded an Xcode project that illustrates the problem. You can download the zipped project here: download. The main parts of the code are explained below.
I set up a grouped table view with static cell. Each of them containing a label and a textfield. I created outlets for every textfield to gain access to them. In my viewDidLoad method I create the toolbar and its buttons, store the textfields in an array and set the controller to be the textfields delegate.
- (void)viewDidLoad
{
[super viewDidLoad];
// create the keyboard toolbar with navigation elements
self.keyboardToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 44)];
UIBarButtonItem *prevButton = [[UIBarButtonItem alloc] initWithTitle:#"Previous" style:UIBarButtonItemStyleBordered target:self action:#selector(prevClicked:)];
UIBarButtonItem *nextButton = [[UIBarButtonItem alloc] initWithTitle:#"Next" style:UIBarButtonItemStyleBordered target:self action:#selector(nextClicked:)];
[self.keyboardToolbar setItems:[NSArray arrayWithObjects:prevButton, nextButton, nil]];
// create the field chain
self.fields = [NSArray arrayWithObjects:self.aField, self.bField, self.cField, self.dField, self.eField, self.fField, nil];
// for scrolling and keeping track of currently active field
NSInteger max = [self.fields count];
for(NSInteger i = 0; i < max; i++) {
UITextField *curr = (UITextField *)[self.fields objectAtIndex:i];
curr.delegate = self;
}
}
The text field delegate methods store a reference of the active text field and prevent line breaks from being inserted.
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
textField.inputAccessoryView = self.keyboardToolbar;
return YES;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
// set the current active textfield
self.currentField = textField;
// scroll this textfield to top
UITableViewCell *cell = (UITableViewCell *)textField.superview.superview;
[self.tableView scrollToRowAtIndexPath:[self.tableView indexPathForCell:cell] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
self.currentField = nil;
return NO;
}
And finally there are the selectors for the toolbar buttons and the logic to get the next textfield.
- (void)moveResponderByStep:(NSInteger)step
{
// only move if a textfield is the current responder
if(![self.currentField isEditing]) {
return;
}
// where we are and where to move
NSInteger max = [self.fields count];
NSInteger moveToNumber;
for(NSInteger i = 0; i < max; i++) {
if(self.currentField == [self.fields objectAtIndex:i]) {
moveToNumber = i + step;
}
}
// stay in bounds
if(moveToNumber >= max || moveToNumber < 0) {
[self.currentField resignFirstResponder];
return;
}
// move on
UITextField *next = [self.fields objectAtIndex:moveToNumber];
[next becomeFirstResponder];
}
- (void)prevClicked:(id)sender
{
[self moveResponderByStep:-1];
}
- (void)nextClicked:(id)sender
{
[self moveResponderByStep:1];
}
Thank you!
Try checking first to see if the row is visible. Scroll the row to visible if it isn't. Then set the text field as first responder.