I have a UITableView with an Add cell... row and would like to have a keyboard pop up with a view above it like in the "Messages" application, where the user can type the name of the new cell.
I realize that there are several other ways to get user data, and several ways to implement the functionality that I am trying to achieve.
For example, the iPod application presents a popup when a new playlist should be created.
Right now, I have a hidden text field that is set to be the first responder when the Add cell... row is pressed, and I assign the view containing the input field and confirmation button as the inputAccessoryView for this hidden field. Alternatively, I could add this view as a subview of the table and position it using keyboard notifications.
I would just like to know if there is a cleaner way to accomplish what I am trying to do (such as setting the inputAccessoryView of the input textField to be displayed to be the textField's superview). I have tried several approaches, but I cannot seem to be able to dismiss the keyboard using resignFirstResponder when the view should close. I can get the inputAccessoryView to disappear, but the keyboard remains resolutely open, taking up necessary screen real estate. Also, when the nib's view is a UITableView with a UITableViewController as the File's Owner rather than a UIView with a UIViewController as the File's Owner, I get a very odd error: "setting the first responder view of the table but we don't know its type (cell/header/footer)"
Thanks in advance,
Julian Ceipek
You are on the right track with the setInputAccessoryView on the UITextView/UITextField classes' This method allows you to add any view you want to the top of a keyboard.
The view you create would then use a delegation method to tell the main view controller to resignFirstResponder.
So, to get you started:
#protocol TextFieldAccessoryViewDelegate
#required
- (void)keyboardShouldDismiss;
#end
#interface TextFieldAccessoryView : UIView
#property (nonatomic, retain) id<TextFieldAccessoryViewDelegate> delegate;
- (id)initWithFrame:(CGRect)frame withDelegate (id<TextFieldAccessoryViewDelegate>)aDelegate;
#end
The implementation might look a little like (only posting the code that makes the view):
#pragma mark - Private methods
- (void)doneButtonTapped:(id)sender
{
[delegate keyboardShouldDismiss];
}
- (void)setUpChildrenView
{
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithTitle:#"Done" style:UIBarButtonItemStyleDone target:self action:#selector(doneButtonTapped:)];
UINavigationItem *navigationItem = [[UINavigationItem alloc] initWithTitle:#""];
[navigationItem setRightBarButtonItem:doneButton];
[navigationItem setHidesBackButton:YES];
UINavigationBar *navigationBar = [[[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, self.width, 44.0f)] autorelease];
[navigationBar pushNavigationItem:navigationItem animated:NO];
[self addSubview:navigationBar];
}
I have used a standard NavigationBar looking view, but you could put in anything you like and include buttons, textfields, images of robot unicorns, the works
If you don't get everything thats going on in the above code you might need to brush up on Delegation and creating views programmatically.
- (void)viewDidLoad
{
[self createAccessoryView];
[textField setDelegate:self];
[textField setKeyboardType:UIKeyboardTypeDefault];
[textField setInputAccessoryView:fieldAccessoryView];
}
- (void)createAccessoryView
{
CGRect frame = CGRectMake(0.0, self.view.bounds.size.height, self.view.bounds.size.width, 44.0);
fieldAccessoryView = [[UIToolbar alloc] initWithFrame:frame];
fieldAccessoryView.barStyle = UIBarStyleBlackOpaque;
fieldAccessoryView.tag = 200;
[fieldAccessoryView setBarStyle:UIBarStyleBlack];
UIBarButtonItem *spaceButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(done:)];
UISegmentedControl* segmentedControl = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:NSLocalizedString(#"Previous", #""), NSLocalizedString(#"Next", #""), nil]];
[segmentedControl addTarget:self action:#selector(segmentAction:) forControlEvents:UIControlEventValueChanged];
segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;
[segmentedControl setMomentary:YES];
UIBarButtonItem *segmentButton = [[UIBarButtonItem alloc] initWithCustomView:segmentedControl];
[fieldAccessoryView setItems:[NSArray arrayWithObjects:segmentButton, spaceButton, doneButton, nil] animated:NO];
[segmentButton release];
[spaceButton release];
[doneButton release];
[segmentedControl release];
}
Related
Note, this is a DESIGN question, NOT a functionality question. I already know how to implement the following, I'm just trying to figure out the best way to design it.
I have an iOS app where a few UIViewControllers throughout the app have UITextFields with UIDatePicker input views. The code for this is below:
- (void) viewDidLoad
{
self.dateField.inputView = [self createDatePicker];
}
- (UIView *) createDatePicker
{
UIView *pickerView = [[UIView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height, self.view.frame.size.width, TOOLBAR_HEIGHT + KEYBOARD_HEIGHT)];
UIDatePicker *picker = [[UIDatePicker alloc] init];
[picker sizeToFit];
picker.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
picker.datePickerMode = UIDatePickerModeDate;
[picker addTarget:self action:#selector(updateDateField:) forControlEvents:UIControlEventValueChanged];
[pickerView addSubview:picker];
// Create done button
UIToolbar* toolBar = [[UIToolbar alloc] init];
toolBar.barStyle = UIBarStyleBlackTranslucent;
toolBar.translucent = YES;
toolBar.tintColor = nil;
[toolBar sizeToFit];
UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem* doneButton = [[UIBarButtonItem alloc] initWithTitle:#"Done"
style:UIBarButtonItemStyleDone target:self
action:#selector(doneUsingPicker)];
[toolBar setItems:[NSArray arrayWithObjects:flexibleSpace, doneButton, nil]];
[pickerView addSubview:toolBar];
picker.frame = CGRectMake(0, toolBar.frame.size.height, self.view.frame.size.width, pickerView.frame.size.height - TOOLBAR_HEIGHT);
toolBar.frame = CGRectMake(0, 0, self.view.frame.size.width, TOOLBAR_HEIGHT);
return pickerView;
}
- (void) doneUsingPicker
{
[self.dateField resignFirstResponder];
}
- (void) updateDateField: (UIDatePicker *) datePicker
{
self.dateField.text = [self.formatter stringFromDate:datePicker.date];
}
The problem is, I keep on having to paste this code throughout the app in different classes that have UITextFields with UIDatePicker inputviews. What would be the best way to design this so as to minimize duplicated code. I've thought about having a UIDatePickerableViewController superclass that contains this code, but this doesn't seem extensible. For instance, what if I soon have other types of input views that could be attached to text fields. How should I design this?
You can refactor code/methods shared between the classes in a common superclass, and inherit subclasses inside which you only modify the parts that are needed to be different.
Or, if you approach the problem from a different point of view: create a custom InputWiewWithDatePicker class and move the (self-)configuration and -initialization code inside the - init method of that class. This way you don't have to paste all this everywhere, and only a single line will be duplicated:
customControl = [[InputViewWithDatePicker alloc] init];
My first thought would be to create a new UIView subclass that contains a date picker and text field with the layout you desire. This can be done with a nib or in code. Anyplace you want to add this new kind of view, it's either a one-liner in viewDidLoad, or paint a UIView into a nib and change it's class to your new view class.
Subclass your desired layout, then when you allocate it, it will come with all the options you have defined.
In my loadView methoad of my view controller I have the following code:
// Populate self.view and add some views/UI elements
// load Gender selection Bar
self.navigationController.toolbarHidden = NO;
self.navigationController.toolbar.tintColor = [UIColor colorWithRed:0 green:0.37 blue:0.5 alpha:1];
self.genderControl = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:NSLocalizedString(#"Male", nil), NSLocalizedString(#"Female", nil), nil]];
genderControl.segmentedControlStyle = UISegmentedControlStyleBar;
genderControl.frame = CGRectMake(0, 0, 200, 30);
genderControl.tintColor = [UIColor colorWithRed:0 green:0.37 blue:0.5 alpha:1];
[genderControl addTarget:self action:#selector(changeGender:) forControlEvents:UIControlEventValueChanged];
genderControl.selectedSegmentIndex = GENDER_MALE;
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithCustomView:genderControl];
UIBarButtonItem *flexSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
[[[self navigationController] toolbar] setItems:[NSArray arrayWithObjects:flexSpace, item, flexSpace, nil] animated:YES];
[item release];
However, if I load the view it displays the toolbar, but the Segmented Control does not get displayed. If I move my code to the viewWillAppear: method it works, but once I hide my view and redisplay it again the segmented control is gone anew.
Does anybody know this problem and/or has an idea how to solve it? Looks very strange to me.
Most of your code looks fine to me, and everything should work when called from the viewDidLoad or the loadView method.
However, when you use the included toolbar from the NavigationController, toolbar items are set on the ViewController rather than the toolbar itself. So replace this line:
[[[self navigationController] toolbar] setItems:[NSArray arrayWithObjects:flexSpace, item, flexSpace, nil] animated:YES];
With this:
[self setToolbarItems:[NSArray arrayWithObjects:flexSpace, item, flexSpace, nil] animated:YES];
Documentation here: http://developer.apple.com/library/ios/documentation/uikit/reference/UIViewController_Class/Reference/Reference.html#//apple_ref/occ/instm/UIViewController/setToolbarItems:animated:
You will also want to add this line:
[flexSpace release];
Are you sure that this code should be in loadView? Check the UIViewController docs: loadView is used for constructing your view controller's view entirely programmatically - nothing is loaded from a nib if you implement loadView. Should this code be in viewDidLoad instead? I see that you're not even creating and assigning the main UIView in your loadView method -- which you'd normally expect to see, e.g.:
self.view = [[[UIView alloc] init] autorelease];
If you did mean to implement loadView, as you have, you should add the above line for assigning your main view towards the top of the method, rather than later on.
Btw, perhaps don't bother calling [toolbar setItems] with animated=YES; not much point and I've seen the exact value of the animated parameter break things inexplicably in the past!
How do you make a UIPickerView act like the one with a webview wherein there is a drop down selection box and instead of dropping down like usual websites do, the iphone makes it into a UIPickerView with all the selections in. When you select one, a check becomes visible beside your selection and changes the value of the drop box. And how do you put the "Done" button on top of the UIPickerView to dismiss the UIPickerView?
I already know that [pickerview setHidden:YES] is the method to use to hide the pickerview. I just don't know how to include the "Done" button in the UIPickerView.
Regards,
Chris
This piece of code will slide out a picker view as keyboard and attached a done button on top of it. Basically, you want to set a inputAccessoryView with your input field.
You should call this method on a touch down event for your input field.
- (IBAction)showYourPicker:(id)sender {
// create a UIPicker view as a custom keyboard view
UIPickerView* pickerView = [[UIPickerView alloc] init];
[pickerView sizeToFit];
pickerView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
pickerView.delegate = self;
pickerView.dataSource = self;
pickerView.showsSelectionIndicator = YES;
self.yourPickerView = pickerView; //UIPickerView
yourTextField.inputView = pickerView;
// create a done view + done button, attach to it a doneClicked action, and place it in a toolbar as an accessory input view...
// Prepare done button
UIToolbar* keyboardDoneButtonView = [[UIToolbar alloc] init];
keyboardDoneButtonView.barStyle = UIBarStyleBlack;
keyboardDoneButtonView.translucent = YES;
keyboardDoneButtonView.tintColor = nil;
[keyboardDoneButtonView sizeToFit];
UIBarButtonItem* doneButton = [[[UIBarButtonItem alloc] initWithTitle:#"Done"
style:UIBarButtonItemStyleBordered target:self
action:#selector(pickerDoneClicked:)] autorelease];
[keyboardDoneButtonView setItems:[NSArray arrayWithObjects:doneButton, nil]];
// Plug the keyboardDoneButtonView into the text field...
yourTextField.inputAccessoryView = keyboardDoneButtonView;
[pickerView release];
[keyboardDoneButtonView release];
}
Finally, your Done button calls the "pickerDoneClicked" method, where you should add
[yourTextField resignFirstResponder]; which will hide the picker view.
The "Done" button is placed in UIToolBar.
Use the below method of UIToolBar for adding the "Done" buttons.
- (void)setItems:(NSArray *)items animated:(BOOL)animated {
UIToolbar* mypickerToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 320, 56)];
mypickerToolbar.barStyle = UIBarStyleBlackOpaque;
[mypickerToolbar sizeToFit];
NSMutableArray *barItems = [[NSMutableArray alloc] init];
UIBarButtonItem *flexSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
[barItems addObject:flexSpace];
UIBarButtonItem *doneBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(DatePickerDoneClick)];
[barItems addObject:doneBtn];
[mypickerToolbar setItems:barItems animated:YES];
}
I want to add a custom toolbar at the top of the UIPickerView control. I did this on Keyboard but not able to do with UIPickerView.
Nishant
make a toolBar by IBoutlet and set its y coordinate to 100 or more than view's height. then in particular action change y coordinate accordingly. this is done via animation.
You also use this with keyboard .
by using
-(void) keyboardWillShow:(NSNotification *) note
and
-(void) keyboardWillHide:(NSNotification *) note
UIToolBar *toolBar= [[UIToolbar alloc] initWithFrame:CGRectMake(0,0,320,44)];
[toolBar setBarStyle:UIBarStyleBlackOpaque];
UIBarButtonItem *barButtonDone = [[UIBarButtonItem alloc] initWithTitle:#"Done" style:UIBarButtonItemStyleBordered target:self action:#selector(doneBtnTapped:)];
toolBar.items = [[NSArray alloc] initWithObjects:barButtonDone,nil];
barButtonDone.tintColor=[UIColor blackColor];
[popoverView addSubview:toolBar];
[popoverView addSubview:pickerView];
I created a custom view and added a done button on that along with picker view.
Thanks
I want to add Toolbar on the upperside of PickerView and want to add one BarButton on the Toolbar Dynamically and on the click event of that button i want to dismiss the Picker as well as toolbar so please help me in this task...Thanks in Advance..
You can create the UIToolBar and its buttons this way -
UIToolbar * toolBar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0f, 324.0f, 320.0f, 44.0f)];
[toolBar setBarStyle:UIBarStyleBlackTranslucent];
UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
doneBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(dismissPicker:)];
[toolBar setItems:[NSArray arrayWithObjects:flexibleSpace, doneBtn, nil] animated:YES];
[flexibleSpace release];
[editButton release];
You can add the done button and call a method on tap of this button, in this method you can dismiss the UIPickerView.
Mr. Ankit - Try to build your objects using through Interface builder because there is no need to maintain all the objects which are being constructed interface builder ( according to me - yet not sure - please add comment, if I am wrong anywhere ).
See,the Attached Snapshots to place Buttons on Your toolbar. Just make connections to your class IBOutlet objects.
I know - you have mentioned "Dynamic" word in your question.
You just need to add subview in to your view.
For example
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:toolBarView]; // add whenever required.
// alternate option
toolBarView.hidden=YES; // or NO whatever required.
}
alt text http://img694.imageshack.us/img694/7516/snap1i.png