Auto Adjust UITextView and UITextField on appearance on keyboard [duplicate] - iphone

This question already has answers here:
How can I make a UITextField move up when the keyboard is present - on starting to edit?
(98 answers)
Closed 9 years ago.
I have a project which Im working on this time and I would like to adjust the UITextView and UITextField above the keyboard when it appears, because when the keyboard appears the TextView and TextField and hidden behind it.
Here are my screen shots,
Without Keyboard:
And with the keyboard,

This is a fairly common problem, there are all sorts of solutions to it. I put one together and made it part of my EnkiUtils package which you can download from https://github.com/pcezanne/EnkiUtils
Short version: You'll want to watch for keyboard events and call the Enki keyboardWasShown method, passing it the current view (and cell if you are in a table).
Long Version: Here's the text from my blog (http://www.notthepainter.com/expose-the-uitextfield-when-keyboard-is-shown/)
Notice that it is a class method, not an instance method. I keep a class called EnkiUtilities around to hold useful things. To call it, we first set up our observer:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
And in the keyboardWasShown method we call our utilities method:
- (void)keyboardWasShown:(NSNotification*)aNotification
{
[EnkiUtilities keyboardWasShown:aNotification view:self scrollView:myTableView activeField:activeField activeCell:activeCell];
}
So what is activeField and activeCell? Lets look at our textViewDidBeginEditing method to see how those are set
-(void)textViewDidBeginEditing:(UITextView *)sender
{
activeField = sender;
if ([sender isEqual:descriptionTextView]) {
activeCell = descriptionCell;
if (shouldClearDescription) {
[descriptionTextView initWithLPLStyle:#""];
shouldClearDescription = false;
}
}else if ([sender isEqual:hintTextView]) {
activeCell = hintCell;
if (shouldClearHint) {
[hintTextView initWithLPLStyle:#""];
shouldClearHint = false;
}
} else {
activeCell = nil;
}
}
This method is called when a UITextField begins editing. I set the activeField to the sender and then I set the activeCell to the cell that corresponds to the field.
The only bit remaining is to restore the view when the keyboard disappears.
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
myTableView.contentInset = contentInsets;
myTableView.scrollIndicatorInsets = contentInsets;
}
When you are working without a UITableView, just put the text fields into a UIScrollView and pass that, not the UITableView, to keyboardWasShown.

Related

iPhone - Keyboard corner button

I would like to make an extra button in the iPhone keyboard left bottom corner like on the photo bellow. Is it possible to do this ?
the only way to customize those buttons is to rebuild the keyboard itself.
http://www.raywenderlich.com/1063/ipad-for-iphone-developers-101-custom-input-view-tutorial
Ray has always got some good tutorials on iphone dev. being able to customize your inputView is only half the battle tho. You will then need to build the custom view. Likely you will want to emulate the existing keypad, with your custom button obviously.
as a side note. to dismiss the keyboard you need to resignFirstResponder via the first responder.
When you get that far, here is the code I use to do exactly that
#implementation UIView (FindAndResignFirstResponder)
- (BOOL)findAndResignFirstResponder
{
UIView *responder = [self findFirstResponder];
if (responder) {
[responder resignFirstResponder];
return YES;
}
return NO;
}
- (UIView*)findFirstResponder
{
if (self.isFirstResponder) {
return self;
}
for (UIView *subView in self.subviews) {
UIView *responder = [subView findFirstResponder];
if (responder != nil)
return responder;
}
return nil;
}
#end
call the if you have a hold of the superview of all your inputs, you can call findAndResignFirstResponder on that view.
Or as you can see the findAndResignFirstResponder calls resignFirstResponder on the "found" firstResponder. therefore if you have the first responder you can just resign it
No it is not. The Keyboard is owned by the system. You can change the kind of keyboard (normal, numeric, twitter etc), but you can't customise it. It is a question which is worth a +1

Interface Builder Tab Order Typing

I have noticed, in one of my views in an iPad app I am building the next button on the keyboard goes through all the UITextFields from left to right down the screen.
Is it possible somehow to make it go top to bottom then right, top to bottom?
So say I have to two long columns of text fields, I wan to go top to bottom not left to right, make sense?
Any help appreciated, thanks.
I don't think there is a way through IB, but you can do this way in code. You're not actually tabbing, you'd be using the return key.
Put this in your UITextField's delegate:
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
BOOL shouldChangeText = YES;
if ([text isEqualToString:#"\n"]) {
// Find the next entry field
BOOL isLastField = YES;
for (UIView *view in [self entryFields]) {
if (view.tag == (textView.tag + 1)) {
[view becomeFirstResponder];
isLastField = NO;
break;
}
}
if (isLastField) {
[textView resignFirstResponder];
}
shouldChangeText = NO;
}
return shouldChangeText;
}
Found here: http://iphoneincubator.com/blog/tag/uitextfield
You'll want to implement UITextFieldDelegate's - (BOOL)textFieldShouldReturn:(UITextField *)textField method. An example of how to use this method to control the order is in this question.
I would like to elaborate on #sprocket's answer addressing the same issue. Just because something works out of the box doesn't mean you should stop thinking about a better way -- or even the right way -- of doing something. As he noticed the behavior is undocumented but fits our needs most of the time.
This wasn't enough for me though. Think of a RTL language and tabs would still tab left-to-right, not to mention the behavior is entirely different from simulator to device (device doesn't focus the first input upon tab). Most importantly though, Apple's undocumented implementation seems to only consider views currently installed in the view hierarchy.
Think of a form in form of (no pun intended) a table view. Each cell holds a single control, hence not all form elements may be visible at the same time. Apple would just cycle back up once you reached the bottommost (on screen!) control, instead of scrolling further down. This behavior is most definitely not what we desire.
So here's what I've come up with. Your form should be managed by a view controller, and view controllers are part of the responder chain. So you're perfectly free to implement the following methods:
#pragma mark - Key Commands
- (NSArray *)keyCommands
{
static NSArray *commands;
static dispatch_once_t once;
dispatch_once(&once, ^{
UIKeyCommand *const forward = [UIKeyCommand keyCommandWithInput:#"\t" modifierFlags:0 action:#selector(tabForward:)];
UIKeyCommand *const backward = [UIKeyCommand keyCommandWithInput:#"\t" modifierFlags:UIKeyModifierShift action:#selector(tabBackward:)];
commands = #[forward, backward];
});
return commands;
}
- (void)tabForward:(UIKeyCommand *)command
{
NSArray *const controls = self.controls;
UIResponder *firstResponder = nil;
for (UIResponder *const responder in controls) {
if (firstResponder != nil && responder.canBecomeFirstResponder) {
[responder becomeFirstResponder]; return;
}
else if (responder.isFirstResponder) {
firstResponder = responder;
}
}
[controls.firstObject becomeFirstResponder];
}
- (void)tabBackward:(UIKeyCommand *)command
{
NSArray *const controls = self.controls;
UIResponder *firstResponder = nil;
for (UIResponder *const responder in controls.reverseObjectEnumerator) {
if (firstResponder != nil && responder.canBecomeFirstResponder) {
[responder becomeFirstResponder]; return;
}
else if (responder.isFirstResponder) {
firstResponder = responder;
}
}
[controls.lastObject becomeFirstResponder];
}
Additional logic for scrolling offscreen responders visible beforehand may apply.
Another advantage of this approach is that you don't need to subclass all kinds of controls you may want to display (like UITextFields) but can instead manage the logic at controller level, where, let's be honest, is the right place to do so.

iPhone - possible to not show keyboard but still show the cursor in a UITextField?

I have a custom keyboard I want to show when the user taps a UITextField. But at the same time I want to show the cursor in the textfield. If if return a NO for canBecomeFirstResponder, it doesn't show the default keyboard but doesn't show the cursor either.
Can someone please help me out?
Thanks.
The answer to your problem is to create a view for your custom keyboard and then edit the inputView property of your UITextField and pass it the view for your custom keyboard.
I hope that helps.
override following two methods in UITextFieldDelegate. Note that this approach is valid for both UITextField and UITextView (in which case you override corresponding methods in UITextViewDelegate)
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
if (!textField.inputView) {
//hides the keyboard, but still shows the cursor to allow user to view entire text, even if it exceeds the bounds of the textfield
textField.inputView = [[UIView alloc] initWithFrame:CGRectZero];
}
return YES;
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
return NO;
}
Seems like what you want is a UITextField with a custom keyboard. Create the class CustomKeyboard : UIView and add buttons/layout the view. Then for your textfield just set the inputView property to an instance of the class CustomKeyboard textField.inputView = customKeyboard;. You'll need to set the inputView property to be readwrite as well #property (readwrite, retain) UIView *inputView; By setting the inputView property, the standard iPhone keyboards will not appear when the textfield becomes first responder.
Register as keyboard notification observer (e.g. in the view controller where you want to hide the keyboard):-
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(hideKeyboard:) name:UIKeyboardWillShowNotification object:nil];
Put in the hideKeyboard: function:-
-(void)hideKeyboard:(NSNotification *)notification {
for (UIWindow *keyboardWindow in [[UIApplication sharedApplication] windows]) {
for (UIView *keyboard in [keyboardWindow subviews]) {
if([[keyboard description] hasPrefix:#"<UIKeyboard"] == YES) {
keyboard.alpha = 0;
}
}
}
}
(Thanks to luvieere in this post for showing me how to get the keyboard)
I'm not sure of the point, but why not just use a UILabel with the same contents of the text field and decorated to look like your text field with a cursor in it. Swap it out for a UITextField when you want input.
There are 2 solutions to your problem.
1) Setting the alpha of the keyboard to 0 will make the keyboard invisible... which may be all you want. The cursor will appear.
2) UITextField implements the UITextInputTraits Protocol. It will always call the keyboard when it becomes the first responder. You will need to inherit from either it or anther class to change that default behavior.
Good luck.
If you tell us what your trying to accomplish we might be about to suggest a more elegant way of accomplishing it.
Have fun.
I see two solutions - either create custom animation (and stop or start it depending on the first responder status of the text field), or play with inputView property.
Here is a solution for inputView approach:
Set inputView property of the UITextField to empty view and ask it to become first responder. This will effectively hide default inputView (i.e. keyboard), but will continue showing blinking cursor.
Add Tap gesture recognizer, and when user taps UITextField, set the inputView property to your custom keyboard, dismiss the keyboard and ask the UITextField to become first responder again.
class BlinkingTextFieldVC: UIViewController {
var blinkingTextField: UITextField!
override func onViewDidLoad() {
setupView()
}
func setupView() {
blinkingTextField = UITextField()
blinkingTextField.inputView = UIView() // empty view will be shown as input method
blinkingTextField.becomeFirstResponder()
let tapGuesture = UITapGestureRecognizer(target: self, action: #selector(blinkingTextFieldTapped(_:)))
blinkingTextField.addGestureRecognizer(tapGuesture)
}
func blinkingTextFieldTapped(_ gesture: UITapGestureRecognizer) {
if gesture.state == .ended {
view.endEditing(true)
blinkingTextField.inputView = nil // set your custom input view or nil for default keyboard
blinkingTextField.becomeFirstResponder()
}
}
}
Why do you even need the cursor ?
I think all you need to do, is when ever a user press a key on your own keyboard, you can update the text value of the input.

How can I tell when something outside my UITableViewCell has been touched?

Similar to this question I have a custom subclass of UITableViewCell that has a UITextField. Its working fine except the keyboard for doesn't go away when the user touches a different table view cell or something outside the table. I'm trying to figure out the best place to find out when something outside the cell is touched, then I could call resignFirstResponder on the text field.
If the UITableViewCell could receive touch events for touches outside of its view then it could just resignFirstResponder itself but I don't see any way to get those events in the cell.
EDIT: I tried this (below) in my UITableViewCell subclass but it doesn't work, I think because touchesBegan:withEvent: doesn't get called if the event was handled by a control. I think I need to catch the events before they get send down the responder chain somehow.
The solution I'm considering is to add a touchesBegan:withEvent: method to the view controller. There I could send a resignFirstResponder to all tableview cells that are visible except the one that the touch was in (let it get the touch event and handle it itself).
Maybe something like this pseudo code:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint touchPoint = // TBD - may need translate to cell's coordinates
for (UITableViewCell* aCell in [theTableView visibleCells]) {
if (![aCell pointInside:touchPoint withEvent:event]) {
[aCell resignFirstResponder];
}
}
}
I'm not sure if this is the best way to go about this. There doesn't seem to be any way for the tableviewcell itself to receive event notifications for events outside its view.
EDIT2: I thought I had an answer (I even posted it as an answer) using hitTest:withEvent: but that didn't work out. It doesn't always get called. :-(
[Edited: removed previous attempt which didn't always work, this one does]
OK, I finally figured a solution that fully works. I subclassed UITableView and overrode the hitTest:withEvent: method. It gets invoked for all touches anywhere in the table view, the only other possible touches are in the navbar or keyboard and the tableview's hitTest doesn't need to know about those.
This keeps track of the active cell in the table view, and whenever you tap a different cell (or non-cell) it sends a resignFirstResponder to the cell going inactive, which gives it a chance to hide its keyboard (or its datepicker).
-(UIView*) hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
// check to see if the hit is in this table view
if ([self pointInside:point withEvent:event]) {
UITableViewCell* newCell = nil;
// hit is in this table view, find out
// which cell it is in (if any)
for (UITableViewCell* aCell in self.visibleCells) {
if ([aCell pointInside:[self convertPoint:point toView:aCell] withEvent:nil]) {
newCell = aCell;
break;
}
}
// if it touched a different cell, tell the previous cell to resign
// this gives it a chance to hide the keyboard or date picker or whatever
if (newCell != activeCell) {
[activeCell resignFirstResponder];
self.activeCell = newCell; // may be nil
}
}
// return the super's hitTest result
return [super hitTest:point withEvent:event];
}
In my UITableViewCell subclasses that have a UITextField, I add the following code to get rid of the keyboard (or date picker, which slides up just like the keyboard):
-(BOOL)resignFirstResponder
{
[cTextField resignFirstResponder];
return [super resignFirstResponder];
}
Yay!
I think you're on the right track, but touchesBegan:withEvent: is a UIResponder method, so you'd actually have to override it in a UIView subclass rather than in your UIViewController subclass. Your options are:
If you're already subclassing UITableViewCell, override touchesBegan:withEvent: there.
If you're using a standard UITableViewCell, implement tableView:didSelectRowAtIndexPath in your UITableView's delegate.
That is a very good solution, the best I've found on the net. The only glitch I've discovered is that if you go from one cell with a textfield to another, the keyboard dismisses and reappears resulting in a jerky type animation.

Can I hook into UISearchBar's Clear Button?

I've got a UISearchBar in my interface and I want to customise the behaviour of the the small clear button that appears in the search bar after some text has been entered (it's a small grey circle with a cross in it, appears on the right side of the search field).
Basically, I want it to not only clear the text of the search bar (which is the default implementation) but to also clear some other stuff from my interface, but calling one of my own methods.
I can't find anything in the docs for the UISearchBar class or the UISearchBarDelegate protocol - it doesn't look like you can directly get access to this behaviour.
The one thing I did note was that the docs explained that the delegate method:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText;
is called after the clear button is tapped.
I initially wrote some code in that method that checked the search bar's text property, and if it was empty, then it had been cleared and to do all my other stuff.
Two problems which this though:
Firstly, for some reason I cannot fathom, even though I tell the search bar to resignFirstResponder at the end of my method, something, somewhere is setting it back to becomeFirstResponder. Really annoying...
Secondly, if the user doesn't use the clear button, and simply deletes the text in the bar using the delete button on the keyboard, this method is fired off and their search results go away. Not good.
Any advice or pointers in the right direction would be great!
Thanks!
Found the better solution for this problem :)
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
if ([searchText length] == 0) {
[self performSelector:#selector(hideKeyboardWithSearchBar:) withObject:searchBar afterDelay:0];
}
}
- (void)hideKeyboardWithSearchBar:(UISearchBar *)searchBar{
[searchBar resignFirstResponder];
}
The answer which was accepted is incorrect. This can be done, I just figured it out and posted it in another question:
UISearchbar clearButton forces the keyboard to appear
Best
I've got this code in my app. Difference is that I don't support 'live search', but instead start searching when the user touches the search button on the keyboard:
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
if ([searchBar.text isEqualToString:#""]) {
//Clear stuff here
}
}
Swift version handling close keyboard on clear button click :
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.characters.count == 0 {
performSelector("hideKeyboardWithSearchBar:", withObject:searchBar, afterDelay:0)
}
}
func hideKeyboardWithSearchBar(bar:UISearchBar) {
bar.resignFirstResponder()
}
You could try this:
- (void)viewDidLoad
{
[super viewDidLoad];
for (UIView *view in searchBar.subviews){
for (UITextField *tf in view.subviews) {
if ([tf isKindOfClass: [UITextField class]]) {
tf.delegate = self;
break;
}
}
}
}
- (BOOL)textFieldShouldClear:(UITextField *)textField {
// your code
return YES;
}
I would suggest using the rightView and rightViewMode methods of UITextField to create your own clear button that uses the same image. I'm assuming of course that UISearchBar will let you access the UITextField within it. I think it will.
Be aware of this from the iPhone OS Reference Library:
If an overlay view overlaps the clear button, however, the clear button always takes precedence in receiving events. By default, the right overlay view does overlap the clear button.
So you'll probably also need to disable the original clear button.
Since this comes up first, and far as I can see the question wasn't really adequately addressed, I thought I'd post my solution.
1) You need to get a reference to the textField inside the searchBar
2) You need to catch that textField's clear when it fires.
This is pretty simple. Here's one way.
a) Make sure you make your class a , since you will be using the delegate method of the textField inside the searchBar.
b) Also, connect your searchBar to an Outlet in your class. I just called mine searchBar.
c) from viewDidLoad you want to get ahold of the textField inside the searchBar. I did it like this.
UITextField *textField = [self.searchBar valueForKey:#"_searchField"];
if (textField) {
textField.delegate = self;
textField.tag = 1000;
}
Notice, I assigned a tag to that textField so that I can grab it again, and I made it a textField delegate. You could have created a property and assigned this textField to that property to grab it later, but I used a tag.
From here you just need to call the delegate method:
-(BOOL)textFieldShouldClear:(UITextField *)textField {
if (textField.tag == 1000) {
// do something
return YES;
}
return NO;
}
That's it. Since you are referring to a private valueForKey I can't guarantee that it will not get you into trouble.
Best solution from my experience is just to put a UIButton (with clear background and no text) above the system clear button and than connect an IBAction
- (IBAction)searchCancelButtonPressed:(id)sender {
[self.searchBar resignFirstResponder];
self.searchBar.text = #"";
// some of my stuff
self.model.fastSearchText = nil;
[self.model fetchData];
[self reloadTableViewAnimated:NO];
}
Wasn't able to find a solution here that didn't use a private API or wasn't upgrade proof incase Apple changes the view structure of the UISearchBar. Here is what I wrote that works:
- (void)viewDidLoad {
[super viewDidLoad];
UITextField* textfield = [self findTextFieldInside:self.searchBar];
[textfield setDelegate:self];
}
- (UITextField*)findTextFieldInside:(id)mainView {
for (id view in [mainView subviews]) {
if ([view isKindOfClass:[UITextField class]]) {
return view;
}
id subview = [self findTextFieldInside:view];
if (subview != nil) {
return subview;
}
}
return nil;
}
Then implement the UITextFieldDelegate protocol into your class and overwrite the textFieldShouldClear: method.
- (BOOL)textFieldShouldClear:(UITextField*)textField {
// Put your code in here.
return YES;
}
Edit: Setting the delegate on the textfield of a search bar in iOS8 will produce a crash. However it looks like the searchBar:textDidChange: method will get called on iOS8 on clear.