How do I find and remove the view corresponding to the built-in iPhone keyboard?
In this project, the user enters input into a UITextField that's always the first responder.
In previous versions of iOS (2.1 --> 4, I believe) we added a listener for the
[[NSNotificationCenter defaultCenter]
addObserver: self
selector: #selector(keyboardWillShow:)
name: UIKeyboardWillShowNotification
object: nil
];
When the keyboard is about to show, we then remove it (and our textfield remains first responder).
- (void)keyboardWillShow:(NSNotification *)note {
UIWindow* tempWindow;
UIView* keyboard;
UIView* minusView = nil;
for(int c = 0; c < [[[UIApplication sharedApplication]
windows] count]; c ++) {
tempWindow = [[[UIApplication sharedApplication]
windows] objectAtIndex:c];
// Loop through all views in the current window
for(int i = 0; i < [tempWindow.subviews count]; i++) {
keyboard = [tempWindow.subviews objectAtIndex:i];
//the keyboard view description always starts with <UIKeyboard
if([[keyboard description] hasPrefix:#"<UIKeyboard"] == YES) {
minusView = keyboard;
}
}
}
[minusView removeFromSuperview];
}
This approach no longer works, because in iOS 5 there isn't a view of type UIKeyboard that can be found this way.
However, there is a view with the following description <UITextEffectsWindow: 0x693cca0; frame = (0 0; 320 480); hidden = YES; opaque = NO; layer = <UIWindowLayer: 0x693cdb0>> which I have tried to remove with the removeFromSuperview and setIsHidden methods.
Any tips on removing the keyboard and keeping first responder status?
The left screenshot below is the loading screen, which depicts the buttons layout as intended. The right screencap shows how the built-in keyboard obscures the calculator buttons.
You should not be removing the keyboard. The keyboard window is private and, as you have discovered, likely to change between releases.
The correct way to get the effect you want is to set your text field's inputView property to the view that contains your calculator buttons. Then iOS will take care of showing that view for you, instead of showing the default keyboard.
This is documented in “Custom Views for Data Input” in the Text, Web, and Editing Programming Guide for iOS.
Related
I hope to insert subview in front of displayed keyboard. I am using the following code:
[self.view bringSubviewToFront: myView];
but the subview does not display.
I am not exactly sure what you are looking for, but my best guess is you want to subview a "done"/"return" over the keypad.
You maybe able to do this by doing something like this (when the keyboard comes up)
UIWindow* tempWindow = [[[UIApplication sharedApplication] windows] objectAtIndex:1];
UIView* keyboard;
for(int i=0; i<[tempWindow.subviews count]; i++)
{
keyboard = [tempWindow.subviews objectAtIndex:i];
// keyboard view found; add the custom button to it
if([[keyboard description] hasPrefix:#"UIKeyboard"] == YES)
[keyboard addSubview:doneButton];
}
The bringSubviewToFront idea failed because it (the keyboard) is not a subview of your application.
Credit to Artyom from this question
Rather than using this for loop to find the correct window you can instead use:
UIWindow * window = [UIApplication sharedApplication].windows.lastObject;
[window addSubview:_menuView];
[window bringSubviewToFront:_menuView];
As long as you are adding it while the is keyboard active then the keyboard will always be the last view added and it reduces the code complexity greatly.
#Altaf, the prefix you mention in your code is not the good one. You should use:
if([[keyboard description] hasPrefix:#"<UIPeripheralHostView"] == YES)
See an example, with Touchpose classes to show touches on demo applications, that I modified to display the animation over the keyboard.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
iMessage Style Receding Keyboard in an iOS App
In the iOS5 Messages app, you can slide your finger down on the keyboard to bring it up and down. This is also done in the iA Writer app in the AppStore. How can I do this in code, that is, access and modify the Y position of the UIKeyboard?
Why reinvent the wheel? There are several open-source projects available that mimick the messages.app receding keyboard:
http://cocoacontrols.com/platforms/ios/controls/madismissivetextview
http://cocoacontrols.com/platforms/ios/controls/dakeyboardcontrol
http://cocoacontrols.com/platforms/ios/controls/imessagekeyboardeffect
To name a few.
There is no method to do this, but you may be able to modify the keyboard's frame directly like this:
UIWindow* tempWindow;
//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;
//Check each window in our application
for(int c = 0; c < [[[UIApplication sharedApplication] windows] count]; c ++)
{
//Get a reference of the current window
tempWindow = [[[UIApplication sharedApplication] windows] objectAtIndex:c];
//Get a reference of the current view
for(int i = 0; i < [tempWindow.subviews count]; i++)
{
keyboard = [tempWindow.subviews objectAtIndex:i];
if([[keyboard description] hasPrefix:#"(lessThen)UIKeyboard"] == YES)
{
//If we get to this point, then our UIView "keyboard" is referencing our keyboard.
}
}
}
I want to get a pointer reference to UIKeyboard *keyboard to the keyboard on screen so that I can add a transparent subview to it, covering it completely, to achieve the effect of disabling the UIKeyboard without hiding it.
In doing this, can I assume that there's only one UIKeyboard on the screen at a time? I.e., is it a singleton? Where's the method [UIKeyboard sharedInstance]. Brownie points if you implement that method via a category. Or, even more brownie points if you convince me why it's a bad idea to assume only one keyboard and give me a better solution.
Try this:
// my func
- (void) findKeyboard {
// Locate non-UIWindow.
UIWindow *keyboardWindow = nil;
for (UIWindow *testWindow in [[UIApplication sharedApplication] windows]) {
if (![[testWindow class] isEqual:[UIWindow class]]) {
keyboardWindow = testWindow;
break;
}
}
// Locate UIKeyboard.
UIView *foundKeyboard = nil;
for (UIView *possibleKeyboard in [keyboardWindow subviews]) {
// iOS 4 sticks the UIKeyboard inside a UIPeripheralHostView.
if ([[possibleKeyboard description] hasPrefix:#"<UIPeripheralHostView"]) {
possibleKeyboard = [[possibleKeyboard subviews] objectAtIndex:0];
}
if ([[possibleKeyboard description] hasPrefix:#"<UIKeyboard"]) {
foundKeyboard = possibleKeyboard;
break;
}
}
}
How about using -[UIApplication beginIgnoringInteractionEvents]?
Also, another trick to get the view containing the keyboard is to initialize a dummy view with CGRectZero and set it as the inputAccessoryView of your UITextField or UITextView. Then, get its superview. Still, such shenanigans is private/undocumented, but I've heard of apps doing that and getting accepted anyhow. I mean, how else would Instagram be able to make their comment keyboard interactive (dismiss on swipe) like the Messages keyboard?
I found that developerdoug's answer wasn't working on iOS 7, but by modifying things slightly I managed to get access to what I needed. Here's the code I used:
-(UIView*)findKeyboard
{
UIView *keyboard = nil;
for (UIWindow* window in [UIApplication sharedApplication].windows)
{
for (UIView *possibleKeyboard in window.subviews)
{
if ([[possibleKeyboard description] hasPrefix:#"<UIPeripheralHostView"])
{
keyboard = possibleKeyboard;
break;
}
}
}
return keyboard;
}
From what I could make out, in iOS 7 the keyboard is composed of a UIPeripheralHostView containing two subviews: a UIKBInputBackdropView (which provides the blur effect on whatever's underneath the keyboard) and a UIKeyboardAutomatic (which provides the character keys). Manipulating the UIPeripheralHostView seems to be equivalent to manipulating the entire keyboard.
Discaimer: I have no idea whether Apple will accept an app that uses this technique, nor whether it will still work in future SDKs.
Be aware, Apple has made it clear that applications which modify private view hierarchies without explicit approval beforehand will be rejected. Take a look in the Apple Developer Forums for various developers' experience on the issue.
If you're just trying to disable the keyboard (prevent it from receiving touches), you might try adding a transparent UIView that is the full size of the screen for the current orientation. If you add it as a subview of the main window, it might work. Apple hasn't made any public method of disabling the keyboard that I'm aware of - you might want to use one of your support incidents with Apple, maybe they will let you in on the solution.
For an app I am currently developing I am using a really quick and easy method:
Add this in the header file:
// Add in interface
UIWindow * _window;
// Add as property
#property (strong, nonatomic) IBOutlet UIView * _keyboard;
Then add this code in the bottom of the keyboardWillShow function:
-(void) keyboardWillShow: (NSNotification *) notification {
.... // other keyboard will show code //
_window = [UIApplication sharedApplication].windows.lastObject;
[NSTimer scheduledTimerWithTimeInterval:0.05
target:self
selector:#selector(allocateKeyboard)
userInfo:nil
repeats:NO];
}
This code look for when the keyboard is raised and then allocates the current window. I have then added a timer to allocate the keyboard as there were some issues when allocated immediately.
- (void)allocateKeyboard {
if (!_keyboard) {
if (_window.subviews.count) {
// The keyboard is always the 0th subview
_keyboard = _window.subviews[0];
}
}
}
We now have the keyboard allocated which gives you direct "access" to the keyboard as the question asks.
Hope this helps
Under iOS 8 it appears you have to jump down the chain more than in the past. The following works for me to get the keyboard, although with custom keyboards available and such I wouldn't rely on this working unless you're running in a controlled environment.
- (UIView *)findKeyboard {
for (UIWindow* window in [UIApplication sharedApplication].windows) {
UIView *inputSetContainer = [self viewWithPrefix:#"<UIInputSetContainerView" inView:window];
if (inputSetContainer) {
UIView *inputSetHost = [self viewWithPrefix:#"<UIInputSetHostView" inView:inputSetContainer];
if (inputSetHost) {
UIView *kbinputbackdrop = [self viewWithPrefix:#"<_UIKBCompatInput" inView:inputSetHost];
if (kbinputbackdrop) {
UIView *theKeyboard = [self viewWithPrefix:#"<UIKeyboard" inView:kbinputbackdrop];
return theKeyboard;
}
}
}
}
return nil;
}
- (UIView *)viewWithPrefix:(NSString *)prefix inView:(UIView *)view {
for (UIView *subview in view.subviews) {
if ([[subview description] hasPrefix:prefix]) {
return subview;
}
}
return nil;
}
I realize that this is the inverse of most posts, but I would like for the keyboard to remain up even if the 'keyboard down' button is pressed.
Specifically, I have a view with two UITextFields. With the following delegate method
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
return NO;
}
I am able to keep the keyboard up even if the user presses the Done button on the keyboard or taps anywhere else on the screen EXCEPT for that pesky keyboard down button on the bottom right of the keyboard.
I am using this view like a modal view (though the view is associated with a ViewController that gets pushed in a UINavigationController), so it really works best from a user perspective to keep the keyboard up all of the time. If anyone knows how to achieve this, please let me know! Thanks!
UPDATE Still no solution! When Done is pressed, it triggers textFieldShouldReturn, but when the Dismiss button is pressed, it triggers textFieldDidEndEditing. I cannot block the textField from ending editing or it never goes away. Somehow, I really want to have a method that detects the Dismiss button and ignores it. If you know a way, please enlighten me!
There IS a way to do this. Because UIKeyboard subclasses UIWindow, the only thing big enough to get in UIKeyboard's way is another UIWindow.
- (void)viewDidLoad {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(coverKey) name:UIKeyboardDidShowNotification object:nil];
[super viewDidLoad];
}
- (void)coverKey {
CGRect r = [[UIScreen mainScreen] bounds];
UIWindow *myWindow = [[UIWindow alloc] initWithFrame:CGRectMake(r.size.width - 50 , r.size.height - 50, 50, 50)];
[myWindow setBackgroundColor:[UIColor clearColor]];
[super.view addSubview:myWindow];
[myWindow makeKeyAndVisible];
}
This works on iPhone apps. Haven't tried it with iPad. You may need to adjust the size of myWindow. Also, I didn't do any mem management on myWindow. So, consider doing that, too.
I think I've found a good solution.
Add a BOOL as instance variable, let's call it shouldBeginCalledBeforeHand
Then implement the following methods:
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
shouldBeginCalledBeforeHand = YES;
return YES;
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
return shouldBeginCalledBeforeHand;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
shouldBeginCalledBeforeHand = NO;
}
As well as
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
return NO;
}
to prevent the keyboard from disappearing with the return button. The trick is, a focus switch from one textfield to another will trigger a textFieldShouldBeginEditing beforehand. If the dismiss keyboard button is pressed this doesn't happen. The flag is reset after a textfield has gotten focus.
Old not perfect solution
I can only think of a not perfect solution. Listen for the notification UIKeyboardDidHideNotification and make of the textfields first responder again. This will move the keyboard out of sight and back again. You could keep record of which textfield was the last firstResponder by listening for UIKeyboardWillHideNotification and put focus on it in the didHide.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardDidHide:)
name:UIKeyboardDidHideNotification
object:nil];
...
- (void)keyboardDidHide:(id)sender
{
[myTextField becomeFirstResponder];
}
For iOS 9/10 and Swift 3, use this to create a rect which overlaps the "Hide keyboard" - Button
override func viewDidLoad() {
NotificationCenter.default.addObserver(self, selector: #selector(coverKey), name: .UIKeyboardDidShow, object: nil)
}
func coverKey() {
if let keyboardWindow = UIApplication.shared.windows.last {
let r = UIScreen.main.bounds
let myWindow = UIWindow.init(frame: CGRect(x: r.size.width - 50 , y: r.size.height - 50, width: 50, height: 50))
myWindow.backgroundColor = UIColor.clear
myWindow.isHidden = false
keyboardWindow.addSubview(myWindow)
keyboardWindow.bringSubview(toFront: myWindow)
}
}
Notice that this adds a sub view to the keyboard window instead of the main window
Try adding a custom on top of the keyboard dismiss button so that the user won't be able to tab the dismiss button. I have used this method in one of my application.
- (void)addButtonToKeyboard {
// create custom button
UIButton *blockButton = [UIButton buttonWithType:UIButtonTypeCustom];
blockButton.frame = //set the frame here, I don't remember the exact frame
[blockButton setImage:[UIImage imageNamed:#"block_button.png"] forState:UIControlStateNormal];
// locate keyboard view
UIWindow *appWindows = [[[UIApplication sharedApplication] windows] objectAtIndex:1];
UIView *keyboard;
for (int i=0; i<[appWindows.subviews count]; i++) {
keyboard = [appWindows.subviews objectAtIndex:i];
// keyboard found, add the button
if ([[keyboard description] hasPrefix:#"<UIPeripheralHost"] == YES && [self.textField isFirstResponder]) {
[keyboard addSubview:doneButton];
}
}
}
Try this...
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField{
return NO;
}
You can use notification as mentioned by Nick Weaver.
If anyone has the app GymBuddy, then they will know what I am talking about. They seem to use the stock Number Pad keyboard but have added a "." button in the lower left as well as a bar across the top to switch to alpha characters. Does anyone know how to do this? Do I make a new view like the keyboard and pull it up and have the buttons correspond to the textField for input? I can't seem to find any information on customizing a keyboard or creating your own. Thanks
I have done this. Basically you add your own button as a subview of the UIKeyboard like this:
// This function is called each time the keyboard is going to be shown
- (void)keyboardWillShow:(NSNotification *)note {
// Just used to reference windows of our application while we iterate though them
UIWindow* tempWindow;
// 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;
// Check each window in our application
for(int c = 0; c < [[[UIApplication sharedApplication] windows] count]; c ++)
{
// Get a reference of the current window
tempWindow = [[[UIApplication sharedApplication] windows] objectAtIndex:c];
// Loop through all views in the current window
for(int i = 0; i < [tempWindow.subviews count]; i++)
{
// Get a reference to the current view
keyboard = [tempWindow.subviews objectAtIndex:i];
// From all the apps i have made, they keyboard view description always starts with <UIKeyboard so I did the following
if([[keyboard description] hasPrefix:#"<UIKeyboard"] == YES)
{
// Only add the Decimal Button if the Keyboard showing is a number pad. (Set Manually through a BOOL)
if (numberPadShowing && [keyboard viewWithTag:123] == nil) {
// Set the Button Type.
dot = [UIButton buttonWithType:UIButtonTypeCustom];
// Position the button - I found these numbers align fine (0, 0 = top left of keyboard)
dot.frame = CGRectMake(0, 163, 106, 53);
dot.tag = 123;
// Add images to our button so that it looks just like a native UI Element.
[dot setImage:[UIImage imageNamed:#"dotNormal.png"] forState:UIControlStateNormal];
[dot setImage:[UIImage imageNamed:#"dotHighlighted.png"] forState:UIControlStateHighlighted];
//Add the button to the keyboard
[keyboard addSubview:dot];
// When the decimal button is pressed, we send a message to ourself (the AppDelegate) which will then post a notification that will then append a decimal in the UITextField in the Appropriate View Controller.
[dot addTarget:self action:#selector(sendDecimal:) forControlEvents:UIControlEventTouchUpInside];
return;
}
else if (numberPadShowing && [keyboard viewWithTag:123])
{
[keyboard bringSubviewToFront:dot];
}
else if (!numberPadShowing)
{
for (UIView *v in [keyboard subviews]){
if ([v tag]==123)
[v removeFromSuperview];
}
}
}
}
}
}
- (void)sendDecimal:(id)sender {
// The decimal was pressed
}
Hope that's clear.
-Oscar
Check this post, this could be your answer:
UIKeyboardTypeNumberPad and the missing "return" key