0

I was following the tutorial at http://www.youtube.com/watch?v=YgeuauhsDhQ to create the previous, next, done button above the keyboard that slides up with the keyboard.

However, I noticed he is doing it to a specific view and I do not want to have to copy / paste this into every view that has a text field. I tried making a class for this but then realised that he resigns the keyboard based on the field that is currently in focus.

Is anyone aware of a class already out there so I do not have to create this for every view controller that has a text field? Or know how I can modify his version to work as a class I could import into my view controllers?

Sorry I am sorta new to iPhone development.

//KeyboardToolbar.h

#import <Foundation/Foundation.h>

@interface KeyboardToolbar : NSObject
{
    UIToolbar *keyboardToolbar;
}

@property (nonatomic, retain) UIToolbar *keyboardToolbar;

-(void)resignKeyboard:(id)sender;
-(void)previousField:(id)sender;
-(void)nextField:(id)sender;
@end

and

//KeyboardToolbar.m
#import "KeyboardToolbar.h"


@implementation KeyboardToolbar

@synthesize keyboardToolbar;

- (void)loadToolbar
{
    if (keyboardToolbar == nil) {

        keyboardToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 44.0)];

        UIBarButtonItem *previousButton = [[UIBarButtonItem alloc] initWithTitle:@"Previous" style:UIBarButtonItemStyleBordered target:self action:@selector(previousField:)];

        UIBarButtonItem *nextButton = [[UIBarButtonItem alloc] initWithTitle:@"Next" style:UIBarButtonItemStyleBordered target:self action:@selector(nextField:)];

        UIBarButtonItem *extraSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];

        UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(resignKeyboard:)];

        [keyboardToolbar setItems:[[NSArray alloc] initWithObjects:previousButton, nextButton, extraSpace, doneButton, nil]];
    }
}

- (void)resignKeyboard:(id)sender
{
    //Resign the keyboard here? I would need to get the element with the keyboard then hide it.
}

@end

How would I make this class more dynamic so I can use it in all of my views with very little copy / pasting into each controller.

Bot
  • 11,868
  • 11
  • 75
  • 131

2 Answers2

3

I'm new at this too but I think I can help. I did something similar. I set up a helper object to act as delegate for whatever view controller the user was currently interacting with. You'll write your code for making the toolbar appear. You'll implement those methods in the .m file of your helper (delegate) class. Add your helper class as an observer for the UIKeyboardDidShowNotification. So, in the viewDidLoad of my UIViewControllers, I set the delegate to my helper object. Then, when a keyboard appears on that current view controller, the notification is sent to the delegate (helper object). Here's a little code to clarify: In init method of helper object:

        [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWasShown:) 
                                                 name:UIKeyboardDidShowNotification 
                                               object:nil];

Then, in the .m file of your helper object, in keyboardWasShown:, call [self makeToolbar] or whatever your methods are called. Sorry for the verbosity. Like I said, I'm pretty new but I hope this helps.

EDIT: So I made a simple little test/illustration. Here's the .h for my ViewController class. This is the ViewController that will contain the UITextFields, both of which bring up a keyboard and add a toolbar to it via a delegate method. Sorry for the lengthy response, but I thought it would be most useful to just give you all of what got it working for me.

#import <UIKit/UIKit.h>
#import "TextFieldDelegate.h"

@interface ViewController : UIViewController <UITextFieldDelegate> {
    //Outlets for 2 UITextFields, added in IB. Selecting either will add the toolbar to your keyboard as long as the delegate is assigned and the methods in the delegate are implemented as shown. 
    IBOutlet UITextField *myTextField;
    IBOutlet UITextField *myOtherTextField;

    //we'll need to set the delegate for this class, so go ahead and declare a variable and make it a property (and synthesize it in the .m). 
    id delegate;
}

@property (nonatomic, strong) id delegate;

@end

ViewController.m:

@implementation ViewController
@synthesize delegate;

//removed method stubs and only left my modifications to the template. 

- (void)viewDidLoad
{
    [super viewDidLoad];

    //create an instance of your delegate class and set it as the view controller's delegate. 
    //the text fields need their parent as the delegate, and the parent in turn assigns the helper class as its delegate. 
    //really this is the whole point, that you can just assign delegates to your view
    //controllers and text fields and they can all access the one method implementation
    //instead of each having to implement it separately themselves. 
    [myTextField setDelegate:self];
    [myOtherTextField setDelegate:self];
    TextFieldDelegate *myDelegate = [[TextFieldDelegate alloc] init];
    [self setDelegate: myDelegate];

    //set the delegate's currentViewController property so that we can add a subview to this View. 
    [delegate setCurrentViewController:self];

}

- (void)textFieldDidBeginEditing:(UITextField *)textField {
    //set the delegate's current text field property so that we can resignFirstResponder. 
    [delegate setCurrentTextField:textField];
}

@end

Now here's the delegate class: TextFieldDelegate.h:

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface TextFieldDelegate : UIViewController <UITextFieldDelegate> {
    UIViewController *currentViewController;
    UITextField *currentTextField;
    UIToolbar *keyboardToolbar;
    CGSize kbSize;
}

- (void)loadToolbar;

@property (nonatomic, strong) UIViewController *currentViewController;
@property (nonatomic, strong) UITextField *currentTextField;

@end

And the implementation of the delegate: TextFieldDelegate.m:

#import "TextFieldDelegate.h"

@implementation TextFieldDelegate

//synthesize properties so that View Controllers can set them as needed. 
@synthesize currentViewController, currentTextField;

- (id)init {
    self = [super init];
    if (self) {
        //register for the keyboard did show notification
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(keyboardWasShown:)
                                                     name:UIKeyboardDidShowNotification
                                                   object:nil];
    }
    return self;
}

- (void)keyboardWasShown:(NSNotification *)aNotification {
    //get the keyboard size for positioning the toolbar. (depending on where you want it, I guess, I was just imagining it directly above the keyboard. 
    NSDictionary *info = [aNotification userInfo];
    kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
    //call the method to make the toolbar appear. 
    [self loadToolbar];
}

- (void)loadToolbar
{
    if (keyboardToolbar == nil) {

        //setting the position of the toolbar. 
        CGRect frameRect = self.view.frame;
        frameRect.size.height -= kbSize.height;

        keyboardToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0, frameRect.size.height - 60.0, 320.0, 60.0)];

        //your code for toolbar setup. 
        UIBarButtonItem *previousButton = [[UIBarButtonItem alloc] initWithTitle:@"Previous" style:UIBarButtonItemStyleBordered target:self action:@selector(previousField:)];

        UIBarButtonItem *nextButton = [[UIBarButtonItem alloc] initWithTitle:@"Next" style:UIBarButtonItemStyleBordered target:self action:@selector(nextField:)];

        UIBarButtonItem *extraSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];

        UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(resignKeyboard:)];

        [keyboardToolbar setItems:[[NSArray alloc] initWithObjects:previousButton, nextButton, extraSpace, doneButton, nil]];

        //this line sends a message to the currently active view controller, telling it to add as a subview the toolbar that we have just created. 
        [[currentViewController view] addSubview:keyboardToolbar];
    }
}

- (void)resignKeyboard:(id)sender {
    [currentTextField resignFirstResponder];
    [keyboardToolbar removeFromSuperview];
    //set the toolbar to nil so that when you touch the other text field, it will build      again. 
    keyboardToolbar = nil;
}

@end

So, there it is. Like I said, I'm new as well. But I hope this helps.

@jostster EDIT: Oh, if you move the call to the custom delegate method loadToolbar into the ViewController's textFieldDidBeginEditing method, it works. In ViewController.m

- (void)textFieldDidBeginEditing:(UITextField *)textField {
    //set the delegate's current text field property so that we can resignFirstResponder. 
    [delegate setCurrentTextField:textField];
    [delegate loadToolbar];
}

And then you can just remove the [self loadToolbar] call from keyboardWasShown in the delegate implementation.

EDIT: to get your next and previous buttons working, first give your delegate a property: NSArray *textFieldArray. Then, in your view controller, create an array of your text fields (we'll call it tfArray), and then do [delegate setTextFieldArray:tfArray]; Then implement your previousField: and nextField: methods something like this:

- (void)previousField:(id)sender {
    //get the index in the array of your currently active textField
    int activeIndex = [textFieldArray indexOfObject:currentTextField];
    //make sure you're not going to try to access an empty spot in the array
    if (activeIndex > 0) {
    UITextField *previousField = [textFieldArray objectAtIndex:activeIndex - 1];
    [previousField becomeFirstResponder];
    }
}
//same again but with a plus sign
- (void)nextField:(id)sender {
    int activeIndex = [textFieldArray indexOfObject:currentTextField];

    if (activeIndex < 1) {
    UITextField *nextField = [textFieldArray objectAtIndex:activeIndex + 1];
    [nextField becomeFirstResponder];
    }
}

There might be a cleaner way to do this but the amount of code is so minimal that I don't feel like typing it twice is that big of a deal. That's the gist of it though and if you had a whole lot of text fields to advance through, you would just want to say something like:

if (activeIndex < [textFieldArray count]) {
//advance to the next textField
}
geraldWilliam
  • 4,123
  • 1
  • 23
  • 35
  • I edited my OP to include the code if you could assist with this. – Bot Jan 12 '12 at 23:54
  • thanks! Also when you do an update make sure you post a comment with the @ and the persons name you want to get the attention of so we can see we have an update. One issue is that in the youtube video the toolbar will slide up with the keyboard and will work on orientation change. I noticed this one doesn't do either. – Bot Jan 13 '12 at 16:15
  • ok I got it to work by doing `keyboardToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0, 0.0, self.view.bounds.size.width, 44.0)];` and instead of `[[currentViewController view] addSubview:keyboardToolbar];` using `currentTextField.inputAccessoryView = keyboardToolbar;` however I have to click the field then another field for the toolbar to show up. Do you know how to get this to work on first text field click? – Bot Jan 13 '12 at 16:33
  • you will want to make a comment to either the original post or you post with the @ username for them to see the message. Not in the actual post. Your edit worked! thanks for all your help! – Bot Jan 13 '12 at 20:07
  • what about the next and previous? – Bot Jan 13 '12 at 20:09
  • @jotster: see above edits re: next and previous buttons. (aside, I think this is what you meant about tagging someone in a comment. idk why I haven't got it figured out :P). – geraldWilliam Jan 13 '12 at 20:38
  • Just want to know if this one worked for ios5 :) . I just want to use the done button within the toolbar for my project. I've seen a lot of tutorials yet they're mostly for versions earlier than ios5 :| – Grauzten Jul 10 '12 at 02:21
  • Yes the answer was written for ios5 with ARC. – geraldWilliam Jul 10 '12 at 21:02
0

I found this project on Github that might be useful.

https://github.com/rexfinn/RFKeyboardToolbar

Hackmodford
  • 3,901
  • 4
  • 35
  • 78