I am writing a universal app that will be used primarily at night. I will need to display a keyboard but do not want the light colors of the keyboard to blind the user and/or spoil their night vision. I do not want to have to go through the trouble to creating a custom keyboard so I thought a solution might be to place a UIView over the keyboard and give it a black background color with an alpha of 0.5 or something however, I can not figure out how to get a UIView to cover the keyboard. Does anyone know how to do this? Does Apple allow this?
The keyboard is found as a subview of a new window that is added when it appears. Finding it is a little hacky and fragile (will need checking at new iOS versions, as it has changed before) but it does work and it is allowed (I do exactly this for a night mode in an app that is on the app store).
UIWindow* tempWindow = [[[UIApplication sharedApplication] windows] objectAtIndex:1]; // This assumes you aren't adding any new windows yourself
for(UIView *keyboard in tempWindow.subviews)
{
if([[keyboard description] hasPrefix:#"<UIPeripheralHost"] == YES) // This was different in an earlier version of iOS, and may well change again in the future!
{
[keyboard addSubview:maskView];
break;
}
}
This is done inside the method that responds to the UIKeyboardDidShowNotification object. I've not tried it on the iPad, this is iPhone code only.
The mask view is, as you say, just a plain view with a black background and some transparency. You can also use the alert keyboard style which gives a black space in between the keys.
This method does not prevent the little key flashes (the larger keys that pop up when you tap a key) from being at full brightness, unfortunately.
try applying the required changes on inputView property of UITextFiled/UITextArea (the one being used).
Related
I'm relatively new to iOS SDK, and I'm experiencing a very bizarre issue regarding the device keyboard location and orientation for an app I'm working on. The problem is that if the keyboard is open while the user multi-tasks or the app goes into background, after the user comes back to the app, the keyboard will be be displaced (with UIKeyboardWillChangeFrameNotification being raised), but in an incorrect orientation and location.
Sometimes the keyboard shows up completely off the screen too, which is completely undesired behaviour.
My questions are:
What is the position and orientation of the keyboard dependant on? How is it controlled by iOS?
Is there a way to detect when the keyboard is being displayed off-screen regardless of the device type and screen size? I'm thinking it would be doable by tracking UIKeyboardWillChangeFrameNotification or UIKeyboardWillShowNotification.
How would I reset/set the location and orientation of the keyboard prior to displaying it? Is this even possible?
From the documentation:
Use the keys described in “Keyboard Notification User Info Keys” to get the location and size of the keyboard from the userInfo dictionary.
Keys used to get values from the user information dictionary of keyboard notifications:
NSString * const UIKeyboardFrameBeginUserInfoKey;
NSString * const UIKeyboardFrameEndUserInfoKey;
NSString * const UIKeyboardAnimationDurationUserInfoKey;
NSString * const UIKeyboardAnimationCurveUserInfoKey;
1.) Keyboard is a UIWindow, the position is dependent on the Application's Main Window.
2.) What you could do is, upon the one of the notifications UIKeyboardWillShowNotification or UIKeyboardWillChangeFrameNotification method firing, loop through the windows subviews to locate the Keyboard. In one of my applications I needed to add a subview to the keyboard. For your case you can get the frame by doing this:
//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 UIPeripheral throught the SDK we will just use UIView.
//UIPeripheral 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];
//Assuming this is for 4.0+, In 3.0 you would use "<UIKeyboard"
if([[keyboard description] hasPrefix:#"<UIPeripheral"] == YES) {
//Keyboard is now a UIView reference to the UIPeripheral we want
NSLog(#"Keyboard Frame: %#",NSStringFromCGRect(keyboard.frame));
}
}
3.) Not completely sure this is possible, but with the supplied code I gave you. keyboard is now casted to a 'UIView', which you can apply your own transforms to.
This might not be the most elegant solution, but it works well for my case.
Hope this Helps !
We're using Phonegap to develop our mobile app, and we borrowed code from here to remove the black next/prev/done bar from the keyboard:
https://stackoverflow.com/a/9276023/35364
What that code does is it finds the black bar, as a UIView object, and calls 'removeFromSuperview' on it.
We're not familiar with the iOS SDK/API. So while we can look at the code and get an idea of what it's doing, we can't tell if it's doing it properly, or how to improve it.
The specific problem we're running into:
We have a text field for writing a message, and we're manually controlling the placement of this field to be exactly above the keyboard, similar to the native sms app. In other words, we're putting it where the black bar was supposed to be.
When we focus/type in the message field, the system pushes the view up. It seems like this is a mechanism to make sure the text field is not invisible when the user types in it.
This is happening even though the text field is visible.
I noticed that by putting the input field right above where the black bar would normally be (as oppose to behind it), the view doesn't scroll.
So it seems the system somehow thinks the black bar is still there!
(To double check: when the black bar is not removed, and we put the text field right above it, we can focus and type in it, and the view would not scroll).
So the question is:
Why does the "system" push the content up when editing a text-field that's place right "behind" where the black bar is supposed to be? Is it because the black bar is not completely removed yet? Do we need to do something to "completely" remove the black bar? Do we need to force iOS to recalculate the size of the keyboard? or what exactly?
Is this mechanism (pushing up the view) implemented by iOS's UIWebView, or by Phonegap?
Is there any phonegap app that has solved this problem?
replace
[subviewWhichIsPossibleFormView removeFromSuperview];
with
UIScrollView *webScroll = [webView.subviews lastObject];
CGRect newFrame = webScroll.frame;
float accesssoryHeight = subviewWhichIsPossibleFormView.frame.size.height;
newFrame.size.height += accesssoryHeight;
[subviewWhichIsPossibleFormView removeFromSuperview];
[webScroll setFrame:newFrame];
it resize the content scroll view for the amount of missing accessory space. It is as far using "private API" as the other code. In detail it isn't using private API directly but if Apple decide to change how a view appears (in this case Keyboard and WebView) then it will crash.
For example if they rename UIWebFormAccessory, your code will not work anymore.
EDIT:
on iOS 5.0+ you can call webView.scrollView directly. So you can split the code to have a pre iOS 5 fallback:
UIScrollView *webScroll;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 5.0) {
webScroll = webView.scrollView;
} else {
webScroll = [webView.subviews lastObject]; // iOS 2.x (?) - 4.x
// make sure this code runs appropriate on older SDKs
}
This worked for me: https://github.com/don/KeyboardToolbarRemover
You will have to know though, there is no Cordova.plist file as of Phonegap 2.3.0 - instead edit your config XML file with the following:
<plugin name="KeyboardToolbarRemover" value="KeyboardToolbarRemover" />
in the branch
My iPad app uses an external "device" that acts as a hardware keyboard. But, at some point in the settings, I need to input text and I can't use the "device" ("device" is not a keyboard).
So, is there any way to force pop the soft keyboard even thought I have a hardware keyboard connected?
Yes. We've done this in a few of our apps for when the user has a Bluetooth scanner "keyboard" paired with the device. What you can do is make sure your textField has an inputAccessoryView and then force the frame of the inputAccessoryView yourself. This will cause the keyboard to display on screen.
We added the following two functions to our AppDelegate. The 'inputAccessoryView' variable is a UIView* we have declared in our app delegate:
//This function responds to all textFieldBegan editing
// we need to add an accessory view and use that to force the keyboards frame
// this way the keyboard appears when the scanner is attached
-(void) textFieldBegan: (NSNotification *) theNotification
{
UITextField *theTextField = [theNotification object];
// NSLog(#"textFieldBegan: %#", theTextField);
if (!inputAccessoryView) {
inputAccessoryView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, navigationController.view.frame.size.width, 1)];
}
theTextField.inputAccessoryView = inputAccessoryView;
[self performSelector:#selector(forceKeyboard) withObject:nil afterDelay:0];
}
//Change the inputAccessoryView frame - this is correct for portrait, use a different
// frame for landscape
-(void) forceKeyboard
{
inputAccessoryView.superview.frame = CGRectMake(0, 759, 768, 265);
}
Then in our applicationDidFinishLaunching we added this notification observer so we would get an event anytime a text field began editing
//Setup the textFieldNotifications
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(textFieldBegan:) name:UITextFieldTextDidBeginEditingNotification object:nil];
Hope that helps!
There’s no way to do this with the current SDK. Please let Apple know via the Bug Reporter.
The solutions here didn't work on iOS 13 or aren't App Store compatible so I solved the problem by creating my own soft keyboard. It is pretty basic but works. Feel free to contribute!
Project on Github
All you have to do is add SoftKeyboardView.swift to your project and somewhere (e.g. appDidFinishLaunching) hit the singleton:
Usage:
SoftKeyboardManager.shared.disabled = false
Since I have the same problem, the closest solution I have found is to use Erica Sadun's app called KeysPlease which is available via cydia and modmyi. It's description is "Use soft kb even when connected to a BT kb.".
Additionally I have found that if you have a physical keyboard also attached, in my case via the iPad keyboard doc, you can bring up the keyboard using a key which seems to map to the eject key on a bluetooth keyboard. Perhaps there is a way to inject this key as if it was pressed on an attached keyboard?
I really wish there was a more official coding solution to this.
When my app connect bluetooth device, keyboard wouldn't show.I try set force the frame of the inputAccessoryView as Brian Robbins say. It didn't work.
Then I use a stupid way to solve.I found when I click textfield or textview one more time, keyboard will show.
So I just need to simulate touch in textfield or textview once , it works.
If you want to do some simulate touch, check this.
https://github.com/HUYU2048/PTFakeTouch
I am getting an interesting warning at build time (iPhone simulator) that gives the following:
EditView.xib:35:0 UITextView does not support data detectors when the text view is editable.
This is basically non existent on google and I would like to remove it.
My editview.xib has a textview where I write notes into it. Is there any more info that is needed?
I have four different Xibs with similar TextViews that are used for notes as well. I was getting the same warnings. The suggestion to disable the "Detects Phone Numbers" and "Detects Links" does removes the warnings. However, I wanted my users to still have the ability to use the detectors in my notes.
This is how I solved the issue in my app:
In IB: I deselected the two properties for the TextView. -(which does stop the build warnings).
In my - (void)viewDidLoad { I set the properties of the textView to the following:
myTextView.dataDetectorTypes = UIDataDetectorTypeAll; which enables the data detectors of all types (phone numbers and url addresses).
In my View Controller's: -(void)textViewDidBeginEditing:(UITextView *)sender {
method, I turned the data detectors back OFF using: myTextView.dataDetectorTypes = UIDataDetectorTypeNone
Then taking advantage of the -(void)textViewDidEndEditing:(UITextView *)sender {
method, I turned them back ON using: myTextView.dataDetectorTypes = UIDataDetectorTypeAll;
This method disables the data detectors when the user is editing the UITextView and turns the data detectors back ON when the user is finished editing. This Fix allowed for selection of the phone numbers and URL from within the textView, so that I did not loose the function.
I found the following in the Apple Docs on the DataDetectors for UITextView: after playing around with the UITextView for a while, hope it helps.
UIDataDetectorTypes:
Defines the types of information that can be detected in text-based content.
Types:
UIDataDetectorTypePhoneNumber;
UIDataDetectorTypeLink;
UIDataDetectorTypeNone;
UIDataDetectorTypeAll;
Update: 11-5-2010;
Extra Note:
Data detectors are not permitted if UITextView is "Editable", because there would be too many variables to track users changes to text as well as touches with trying to execute phone call or links.
Solution:
Load the TextView with self.textView.editable = NO; and set you UIDataDetector's based on the types I listed above. This way if the user wants to "select" web address or phone number etc, the delegate can handle. When you need your user to edit the textView, then turn ON the self.textView.editing = YES; & remove your UIDataDetectors accordingly. This should assure no errors or warnings during compiling.
Special Consideration:
Be sure to first remove the datadectors when re-enabling, then enable "editing = YES;"...The order is important no to enable editing if UIdatadetectors are still assigned.
Therefore, the sequence order should be something like this...
To Edit textView: 1. remove data detectors, 2. then enable editing = YES.
To Use DataDetectors: 1. Disable Editing = NO; 2. then add data detectors.
I was seeing this warning as well. Here's how I fixed it:
In the xib file in Interface Builder, select your text view, and bring up the attributes inspector. Make sure that "Detects Phone numbers" and "Detects Links" are both UNCHECKED.
I had "Detects Links" checked, and turns out that's what was causing the warning. Basically, if the textview is editable, you don't want these auto-detect features turned on.
So Wordy!
textView.editable = NO;
textView.dataDetectorTypes = UIDataDetectorTypeAll;
the URL address must start with "http://", otherwise the textview cannot detect it.
I thought about trying to use a Tap-Gesture-Recognizer with "delaysTouchesBegan = YES" and "cancelsTouchesInView = NO"
It is still quite easy to solve!
Load view with editable disabled as well as UIDataDetectorTypeAll or the types of links you want to detect. Then add a GestureRecognizer:
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(editTextRecognizerTabbed:)];
recognizer.delegate = self;
recognizer.numberOfTapsRequired = 1;
[self.textViewNotes addGestureRecognizer:recognizer];
So you can change settings within this method:
- (void) editTextRecognizerTabbed:(UITapGestureRecognizer *) aRecognizer;
{
self.textViewNotes.dataDetectorTypes = UIDataDetectorTypeNone;
self.textViewNotes.editable = YES;
[self.textViewNotes becomeFirstResponder];
}
And at least you have to change the edit and detections settings back after user has finished the text input:
- (void)textViewDidEndEditing:(UITextView *)textView;
{
self.textViewNotes.editable = YES;
self.textViewNotes.dataDetectorTypes = UIDataDetectorTypeAll;
}
works lika a charm!
Data detectors for the UITextView would be for copy and paste. Since you are setting it as editable, copy/paste shouldn't be allowed where you think paste should, but copy shouldn't.
Simplenote somehow does this on iOS 4. (There's a free/lite version in case you wanna try.)
It acts a little bit different:
When tapping on one of the highlighted parts, it still starts the editing, and won't follow the link.
But when you tap-and-hold on a detected dataTpye, it shows yout the menu for calling, open the link or whatever.
Also, when tapping inside the text the editing really starts at the place you tapped.
So they somehow remove the dataDectectors, enable editing AND get the touches forwarded to the editable UITextview AFTER the tap is recognized.
Any ideas how to do that?
I thought about trying to use a Tap-Gesture-Recognizer with "delaysTouchesBegan = YES" and "cancelsTouchesInView = NO"
So I can remove the dataConnectorTypes and set it editable on the action method of the recognizer,
and hopefully the touches to the UITextview are delivered AFTER that.
But haven't had time to test it so far.
I'm implementing some simple text chatting capabilities in my app and I'm having issues with scrolling the UITextView programmatically. I'm using a UITextView created in Interface Builder that appends a new line and some text to the preexisting text. When the new text is added it should scroll to the bottom.
I built a test application to nail down the concept before adding it to my app. The text in the UITextView updates with the text from a UITextField, however no scrolling occurs.
- (IBAction)enteredText {
CGPoint currentPosition = [textWindow contentOffset];
[textWindow setText:[NSString stringWithFormat:#"%#\n%#", textWindow.text, textInput.text]];
[textWindow setContentOffset:currentPosition animated:NO];
[textWindow scrollRangeToVisible:NSMakeRange([textWindow.text length], 0)];
[textInput setText:#""];
[textInput becomeFirstResponder];
}
I remember implementing a very similar feature in another application I developed a while ago andfrom what I remember the code is similar. The only difference is that the earlier application was for iPhone OS 2 but this one is for 3.0. I read in some forums that the 3.0 beta had some issues with scrolling when the UITextView was created in Interface Builder. I checked the current release notes and I didn't see anything indicating that.
Edit: The IB action is called because text is updated in the UITextView. And "Cancellable Content Touches" is checked.
Edit: Confirmed that the same code works on 2.2.1 but not 3.0
I found that after the user has tapped on the UITextView the scrolling begins to work. So after I loaded this particular view I temporarily set the UITextView as FirstResponder, then the UITextField as FirstResponder:
[myChatRoomViewController.chatWindow becomeFirstResponder];
[myChatRoomViewController.input becomeFirstResponder];
The scrolling then happened automatically, albeit it seemed less smoother than what I remembered in iPhone OS 2.
Are you sure the IBAction is getting called? If so, try making sure that “Cancellable Content Touches” is checked in Interface Builder. This should solve the problem you hinted about in your post.