how to make textfield changes appear in two different views? - objective-c

I have a class where I used the textfield delegate method "shouldChangeCharactersInRange". If the user types something in a textfield in one view, I want those changes to appear in a textfield in a different view.
Right I now, I have two xib files with the same file's owner and I make a connection (in IB) in each xib file to my textfield (which i declared as an IBOutlet). It's an Ipad app so I switch between views when user rotates device.
It's not working yet so I must be missing something? Could someone please help me! thank you!
If this question is not clear, please let me know.

Wherever your shouldChangeCharactersInRange: method is implemented, if you have a reference to both of the textfields, what you can do is set the text of both. So where right now you have something like:
- (BOOL)textField: (UITextField *)textField shouldChangeCharactersInRange: (NSRange)range replacementString: (NSString *)string
{
if (textField.text.length >= MAX_LENGTH && range.length == 0)
{
return NO;
}
else
{
return YES;
}
}
You want to add something like:
- (BOOL)textField: (UITextField *)textField shouldChangeCharactersInRange: (NSRange)range replacementString: (NSString *)string
{
if (textField.text.length >= MAX_LENGTH && range.length == 0)
{
return NO;
}
else
{
[myFirstTextField setText:string];
[mySecondTextField setText:string];
return YES;
}
}
And if you set both textFields to delegate to that single function, you don't even need to care who delegated to you there. The action you want to take is the same no matter who got text entered in them.

Related

Change UITextField background when editing begins for multiple fields

So I found this thread a while ago:
Change UITextField background when editing begins
The top answer is a fantastic resource for changing the background image of a textField when the user edits it. However, my issue is, how can you enable this with MULTIPLE text fields?
Specifically (for my app) I have a login and password field. Using the code I'll post below I can get my first text field to change images correctly when the user taps it. However, I cannot get the next field to follow suit when the user taps either the next text field or the "Next" option on the keyboard. As you can only have one instance of "textFieldShouldBeginEditing" as well as ending, you need to set the code for image change for both fields under the same section. however, when I do this, both my fields change when the first field is tapped, and both restore when the second is tapped.
Any ideas on this one excellent community?
Here's some code:
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
_userNameTextField.background = [UIImage imageNamed:#"login_field_highlighted#2x"];
return YES;
_passwordTextField.background = [UIImage imageNamed:#"password_field_highlighted#2x"];
return YES;
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
_userNameTextField.background = [UIImage imageNamed:#"login_field#2x"];
return YES;
_passwordTextField.background = [UIImage imageNamed:#"password_field#2x"];
return YES;
}
- (BOOL) textFieldShouldReturn:(UITextField *)textField {
NSLog(#"textFieldShouldReturn");
if (textField == _userNameTextField) {
[_passwordTextField becomeFirstResponder];
} else if (textField == _passwordTextField) {
[_passwordTextField resignFirstResponder];
}
return YES;
}
In each of those methods, you just need to check to see which textfield is targeted, just like you did in textFieldShouldReturn:
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
if (textField == _userNameTextField) {
_userNameTextField.background = [UIImage imageNamed:#"login_field#2x"];
} else if (textField == _passwordTextField) {
_passwordTextField.background = [UIImage imageNamed:#"password_field#2x];
}
return YES;
}

Show a preloaded search results?

I have a non-tableview view with a searchbar in it, and while it works perfectly, the search display controller hides the table view and overlays a dark dimmed view when an empty string is in the searchbar. I want it to show a preloaded data when the empty string is in the searchbar instead of hiding the table view and overlaying the dark dimmed view underneath the searchbar. Just like how the Google search bar in Safari for iOS works.
I found a similar question asked on stackoverflow before:
UISearchDisplayController - how to preload searchResultTableView, I couldn't really get it to work.
I have no problem getting the preloaded data and setting the current data to it, but I'm not sure how to prevent the displaycontroller from removing the searchResultsTableView.
Thanks in advance.
I finally found a way to do this.
I found out that the searchDisplayController simply removes the searchResultsTableView from the superview, so I just added the table view back into the superview whenever the display controller tried to hide the table view:
- (void)searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView
{
// add the tableview back in
[self.view addSubview:self.searchDisplayController.searchResultsTableView];
}
and then I also have to show the tableview the first time the searchbar is clicked, so I did:
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
{
// after the data has been preloaded
self.searchResults = self.allItems;
[self.searchDisplayController.searchResultsTableView reloadData];
}
- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller
{
[self.view addSubview:self.searchDisplayController.searchResultsTableView];
}
For me, 'allItems' is where I stored all the searchable items and 'searchResults' is where the filtered items (after the search) is stored. And of course, you would have to preload the items (e.g. search history) before reloading the data.
I don't know if this is a nice way or not to do it in terms of the performance and what not, but it worked perfectly for me, and I hope this could be useful for other people as well. Please comment if there is a better way to do this.
After hours and hours I finally figured out a solution that works in iOS 7
Just implement the following two methods in your UISearchDisplayDelegate
-(void)searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView {
// We need to prevent the resultsTable from hiding if the search is still active
if (self.searchDisplayController.active == YES) {
tableView.hidden = NO;
}
}
When the search starts, the searchResultsTableView is being hidden automatically, so we need to unhide it again
- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller {
controller.searchResultsTableView.hidden = NO;
// Then we need to remove the semi transparent overlay which is here
for (UIView *v in [[[controller.searchResultsTableView superview] superview] subviews]) {
if (v.frame.origin.y == 64) {
[v setHidden:YES];
}
}
}
I found a much better solution to this issue, and it seems to work perfectly on iOS 6 and 7. While it is still a hack, its a much cleaner and future proof hack than the above. The other solutions do not work consistently and prevent some UISearchDisplayDelegate methods from ever firing! Further I had complex insetting issues which I could not resolve with the above methods. The main issue with the other solutions is that they seriously confuse the internals of the UISearchDisplayController. My solution is based on the observation that UISearchDisplayContoller is a UISearchbarDelegate and that the automatic undimming & showing of results table can be triggered by simulating a keypress in the search field! So:
- (void) searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller
{
if ([controller respondsToSelector: #selector(searchBar:textDidChange:)])
[(id<UISearchBarDelegate>)controller searchBar: controller.searchBar textDidChange: #" "];
}
This code is future proof against crashing by checking it responds to the UISearchbarDelegate method, and sends space #" " to trick the UISearchDisplayController into thinking user has typed a letter.
Now if the user types something and then erases it, the table will dim again. The other solutions try to work around this by doing something in the searchDisplayController:didHideSearchResultsTableView: method. But this doesn't make sense to me, as surely when you cancel the search it will need to truly hide your results table and you may need to run code in this case. My solution for this part is to subclass (note you could probably use a Method Swizzled Category to make it work everywhere if needed in your project):
// privately declare protocol to suppress compiler warning
#interface UISearchDisplayController (Super) <UISearchBarDelegate>
#end
// subclass to change behavior
#interface GMSearchDisplayController : UISearchDisplayController
#end
#implementation GMSearchDisplayController
- (void) searchBar: (UISearchBar *) searchBar textDidChange: (NSString *) searchString
{
if (searchString.length == 0)
searchString = #" ";
if ([super respondsToSelector: #selector(searchBar:textDidChange:)])
[super searchBar: searchBar textDidChange: searchString];
}
#end
This code works by intercepting the textDidChange delegate method and changing nil or empty strings in to space string #" " preventing the normal hiding/dimming that occurs on an empty search bar. If you are using this second bit of code, then you could modify the first bit to pass a nil instead of #" " as this second bit will do the needed conversion to #" " for you.
In my own project, I needed to handle the case that user does type a space, so instead of #" " above I used a defined token:
// arbitrary token used internally
#define SEARCH_PRELOAD_CONDITIONAL #"_#preresults#_"
And then handle it internally by converting it back to nil string:
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
if ([searchString isEqualToString: SEARCH_PRELOAD_CONDITIONAL])
searchString = nil;
}
Enjoy! :)
This works in iOS 8:
- (void)searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView
{
self.searchDisplayController.searchResultsTableView.hidden = NO;
}
- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller
{
self.searchDisplayController.searchResultsTableView.hidden = NO;
[self.searchDisplayController.searchResultsTableView.superview.superview bringSubviewToFront:self.searchDisplayController.searchResultsTableView.superview];
CGRect frame = self.searchDisplayController.searchResultsTableView.frame;
self.searchDisplayController.searchResultsTableView.frame = CGRectMake(frame.origin.x, 64, frame.size.width, frame.size.height);
}
When you start searching this method gets called. Add the searchResultsTableView and unhide it. It would then display your already preloaded data. I must have your data preloaded in order for this to work.
- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller
{
CGRect testFrame = CGRectMake(0, self.notesSearchBar.frame.size.height, self.notesSearchBar.frame.size.width, self.view.frame.size.height - self.notesSearchBar.frame.size.height);
self.searchDisplayController.searchResultsTableView.frame = testFrame;
[self.notesSearchBar.superview addSubview:self.searchDisplayController.searchResultsTableView];
// [self.view addSubview:self.searchDisplayController.searchResultsTableView];
controller.searchResultsTableView.hidden = NO;
}
-(void) searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView
{
CGRect testFrame = CGRectMake(0, self.notesSearchBar.frame.size.height, self.notesSearchBar.frame.size.width, self.view.frame.size.height - self.notesSearchBar.frame.size.height);
self.searchDisplayController.searchResultsTableView.frame = testFrame;
[self.notesSearchBar.superview addSubview:self.searchDisplayController.searchResultsTableView];
// [self.view addSubview:self.searchDisplayController.searchResultsTableView];
controller.searchResultsTableView.hidden = NO;
}
-(void) searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
controller.searchResultsTableView.hidden = YES;
}
iOS 9 working code.
- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller {
// Bring the search table view to the view's front
self.searchDisplayController.searchResultsTableView.hidden = NO;
[self.searchDisplayController.searchResultsTableView.superview bringSubviewToFront:self.searchDisplayController.searchResultsTableView];
}
- (void)searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView {
// We need to prevent the resultsTable from hiding if the search is still active
if (self.searchDisplayController.active == YES) {
tableView.hidden = NO;
}
}
Swift 2.0+ version
func searchDisplayControllerDidBeginSearch(controller: UISearchDisplayController) {
controller.searchResultsTableView.hidden = false
controller.searchResultsTableView.superview!.bringSubviewToFront(controller.searchResultsTableView)
}
func searchDisplayController(controller: UISearchDisplayController, didHideSearchResultsTableView tableView: UITableView) {
if ((searchDisplayController?.active) != nil) {
tableView.hidden = false
}
}

Moving Onto The Next UITextField When 'Next' Is Tapped

I have an iPad application which has a sign up form within it. The form is very basic and contains only two UITextFields which are for Name & Email address.
The first TextField is for the candidates Name, When they enter their name in and press 'Next' on the keyboard I want this to automatically move to the next Email Address TextField to editing.
Any idea how I can set the next button the keyboard to jump to the next keyboard?
Thanks
You need to make your view controller the UITextField delegate, and implement the UITextField delegate method:
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if (textField == nameField) {
[textField resignFirstResponder];
[emailField becomeFirstResponder];
} else if (textField == emailField) {
// here you can define what happens
// when user presses return on the email field
}
return YES;
}
Swift version:
func textFieldShouldReturn(textField: UITextField) -> Bool {
if textField == nameField {
textField.resignFirstResponder()
emailField.becomeFirstResponder()
} else if textField == emailField {
// here you can define what happens
// when user presses return on the email field
}
return true
}
You may also want to scroll your view for the emailField to become visible. If your view controller is an instance of UITableViewController, this should happen automatically. If not, you should read this Apple document, especially Moving Content That Is Located Under the Keyboard part.
Additionally to #lawicko 's answer I often change the button text to give that final finishing touch (e.g. says next when there are more fields and then done when on the last):
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
BOOL isLastTextField = //.. your logic to figure out if the current text field is the last
if (isLastTextField) {
textField.returnKeyType = UIReturnKeyDone;
} else {
textField.returnKeyType = UIReturnKeyNext;
}
}
Swift version of correct answer.
In my experience, you do not need to resignFirstResponder when switching textFields.
In this example, it's just your basic username and password textFields.
The keyboard "return key" in storyboard for username is set to "Next" and the one for password is set to "Done".
Then just connect the delegates for these two text fields and add this extension and you're pretty much done.
extension LoginViewController: UITextFieldDelegate {
func textFieldShouldReturn(textField: UITextField) -> Bool {
if textField == textFieldPassword {
self.view.endEditing(true)
} else {
textFieldPassword.becomeFirstResponder()
}
return true
}
}
A more consistent and robust way is to use NextResponderTextField
You can configure it totally from interface builder.
All you need to do is
Set the class type of your UITextField to be NextResponderTextField
Then set the outlet of the nextResponderField to point to the next responder it can be anything UITextField or any UIResponder subclass. It can be also a UIButton and the library is smart enough to trigger the TouchUpInside event of the button only if it's enabled.
Here is the library in action:
A Swift 4 extension. Just pass the array of UITextFields and it will connect each one to the next until the last one which resigns the first responder (hides the keyboard):
extension UITextField {
class func connectFields(fields: [UITextField]) {
guard let last = fields.last else { return }
// To reset the targets in case you call this method again to change the connected fields
fields.forEach { $0.removeTarget(nil, action: nil, for: .editingDidEndOnExit) }
for i in 0 ..< fields.count - 1 {
fields[i].returnKeyType = .next
fields[i].addTarget(fields[i + 1], action: #selector(UIResponder.becomeFirstResponder), for: .editingDidEndOnExit)
}
last.returnKeyType = .continue
last.addTarget(last, action: #selector(UIResponder.resignFirstResponder), for: .editingDidEndOnExit)
}
}
- (BOOL) textFieldShouldReturn:(UITextField *)textField
{
if (textField == self.textFieldName)
{
[self.textFieldName resignFirstResponder];
[self.textFieldPassword becomeFirstResponder];
}
else if (textField == self.textFieldPassword)
{
[self.textFieldPassword resignFirstResponder];
[self login:self];
}
return true;
}
#interface MLLoginViewController ()<UITextFieldDelegate>
#end
#implementation MLLoginViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.textFieldName.delegate = self;
self.textFieldPassword.delegate = self;
Make an outlet for the textfield, then
viewController.h
(IBAction)textFieldDoneEditing:(id)sender;
viewController.m
(IBAction)textFieldDoneEditing:(id)sender {
[textField resignFirstResponder];
if (textField == nameField) {
[emailField becomeFirstResponder];
}
}
Make the relation between (show the connections inspector > Sent Events)didEndOnExit and textFieldDoneEditing

UITextField editingChange Control Event not works

I have some textfields and I want to do when I change textfield1 text set text to other textfields. My code below. But it not works. How can I solve this?
- (IBAction)TCKimlikTextChange:(id)sender {
[TCKimlikText addTarget:self action:#selector(yourMethod: ) forControlEvents:UIControlEventEditingChanged];
}
-(void)yourMethod: (UITextField*)tf_{
if (tf_) {
if (TCKimlikText.text == #"1") {
AdinizText.text = #"Hacer";
}
}
}
Your code is very abstract. yourMethod, tf_ TCKimlikTextChange are all expressions that are not very human readable. You should work on your variable names.
I suppose your first method is a button handler. It just assigned a target and action to the text field, but does not call any method. You do not need that action if you use the delegate protocol.
To solve your problem: implement the UITextField delegate methods. Make sure you set the delegate (probably self) for your text fields. Your view controller must mention the <UITextFieldDelegate> protocol in its .h file. Thus, in textField:shouldChangeCharactersInRange:replacementString::
if ([textField.text isEqualToString:#"1"]) {
displayLabel.text = #"Hacer";
}
Notice that you need isEqualToString: to compare strings, a simple == won't do.
If u are want to change on the click of the return button use the delegate
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
if(textField == field1)
[field2 setText:field1.text];
[field1 resignFirstResponder];
return YES;
}
or u can use other delegates too like:
– textFieldShouldBeginEditing:
– textFieldDidBeginEditing:

IOS: action with enter key of iPad KeyBoard

I have two textfield, in first textfield I write "Hello" and when I push enter in iPad keyboard, I want that in second textfield appear "World"; How can I use enter to create an action in my application?
You would typically assign your view controller as the text field's delegate and then implement the textFieldShouldReturn: method, e.g.:
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
otherTextField.text = #"World"
return YES;
}
You can do that by implementing the UITextFieldDelegate protocol in your controller. For instance you could do something like:
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if (textField == theFirstTextField && [textField.text isEqualToString:#"Hello"]) {
theSecondTextField.text = #"World";
}
return YES;
}
Set your view controller to be the textfield's delegate then implement
-(BOOL)textFieldShouldReturn:(UITextField *)textField
this gets called when the enter button is pushed on the keyboard.
This is roughly what you'd do. Tweaking to condition around device-type (if you truly want iPad only):
#pragma mark - UITextField Delegate
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
if (textField == self.firstTextField && [textField.text isEqualToString:#"Hello"]) {
self.secondTextField.text = #"World";
}
return YES;
}