Sunday, November 4, 2012

UITextField with Input from UIPickerView

Sometimes, we need an UITextField whose input is picking from an array of strings. People may popup an UIPickerView from an UIActionSheet/UIPopoverController, but it is easier to use UITextField's inputView to do this. Doing this way is more intuitive and more OOP. I would like to achieve something shown below.

To do this, I created a subclass of UITextField, called PickerTextField
/*
Copyright (c) 2012 Cullen SUN
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
//
// PickerTextField.h
// http://creiapp.blogspot.hk/2012/11/uitextfield-with-input-from-uipickerview.html
//
#import <UIKit/UIKit.h>
#import "PickerTextFieldHandler.h"
@interface PickerTextField : UITextField<UIPickerViewDelegate,UIPickerViewDataSource> {
NSArray *sourceStringsArray;
UIPickerView * picker;
int pickedIndex;
PickerTextFieldHandler * deleHandler;
}
@property (nonatomic, retain) UIPickerView *picker;
@property (nonatomic, assign) int pickedIndex;
-(void)setDataSource:(NSArray *) sourceArray andPlaceHolder:(NSString*)theholder;
-(void)pickerTextFieldDidBeginEditing;
@end
/*
Copyright (c) 2012 Cullen SUN
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
//
// PickerTextField.m
// http://creiapp.blogspot.hk/2012/11/uitextfield-with-input-from-uipickerview.html
//
#import "PickerTextField.h"
@interface PickerTextField()
- (void) setup;
@end
@implementation PickerTextField
@synthesize pickedIndex,picker;
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setup];
}
return self;
}
- (void) awakeFromNib{
[super awakeFromNib];
[self setup];
}
- (void) setup{
self.pickedIndex=-1;
deleHandler=[[PickerTextFieldHandler alloc]init];
self.delegate=(id)deleHandler;
}
-(void)setDataSource:(NSArray *) sourceArray andPlaceHolder:(NSString*)theholder
{
[self setPlaceholder:theholder];
sourceStringsArray=[[NSArray alloc] initWithArray:sourceArray];
//setup pickerview here
if (!self.picker) {
UIPickerView * _picker=[[UIPickerView alloc] init];
self.picker=[_picker autorelease];
self.inputView=self.picker;
}
[self.picker setFrame:CGRectMake(0, 40, 320, 216)];
self.picker.delegate=self;
self.picker.dataSource=self;
self.picker.showsSelectionIndicator=YES;
if (self.pickedIndex!=-1) {
[self setText:[sourceStringsArray objectAtIndex:self.pickedIndex]];
}
}
#pragma mark -
#pragma mark PickerViewDelegate
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 1;
}
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component
{
return 50;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
return [sourceStringsArray count];
}
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row
forComponent:(NSInteger)component reusingView:(UIView *)view
{
UILabel *cellLable = (UILabel*) view;
if (cellLable == nil)
{
cellLable = [[[UILabel alloc] init] autorelease];
[cellLable setFrame:CGRectMake(40, 5, 260, 40)];
cellLable.backgroundColor=[UIColor clearColor];
cellLable.numberOfLines=2;
cellLable.font=[UIFont boldSystemFontOfSize:16];
}
cellLable.text=[sourceStringsArray objectAtIndex:row];
return cellLable ;
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
self.text=[sourceStringsArray objectAtIndex:row];
self.pickedIndex=row;
}
#pragma mark -
#pragma mark Forwarded UITextFieldDelegate
-(void) pickerTextFieldDidBeginEditing{
if (self.pickedIndex==-1) {
self.pickedIndex=0;
[self setText:[sourceStringsArray objectAtIndex:self.pickedIndex]];
}
[self.picker selectRow:self.pickedIndex inComponent:0 animated:NO];
}
- (void)dealloc {
[sourceStringsArray release];
[deleHandler release];
[picker release];
[super dealloc];
}
@end

In the header file, we can see it is a direct subclass from UITextField. I created some varibles: 1. sourceStringsArray to hold the array of strings to select from; 2. picker to be the inputView of the TextView. 3. pickedIndex to indicated the selected index.

pickedIndex is also declared as a property, used to be an API from other class to access it. You may wondering, how can we access the text from outside? Super easy. my_sub_class.text will give you the text, just like a normal UITextField.

The method:'{setDataSource: andPlaceHolder:}' is an important method for us to set the sourceStringsArray and the textfield's placeholder property.

First two methods are initialization, they won't be called at the same time. Please refer to the comment in the code. Also note '[super setDelegate:self];', which direct the UITextField's delegate to this subclass itself.

'{setDataSource: andPlaceHolder:}' as mentioned earlier, used to assign sourceStringsArray. It has to be called before using it. Besides, we also setup the picker here, and assign the UITextField's inputView to the picker.

 if (self.pickedIndex!=-1) {
        [self setText:[sourceStringsArray objectAtIndex:self.pickedIndex]];
    }
If you want pre-select certain element for the PickerTextField, you call assign a pickedIndex before calling the method.

The remaining code is quite straight forward. UIPickerView's delegate methods. We change the pickedIndex and the text when the picker's selection changes. The following class is only to serve as the mid-object to forward UITextFieldDelegate methods to the PickerTextField. We pre-select first element, if pickerIndex is not assigned with any value and remains -1. I tried to simply use'[super setDelegate:self]' in PickerTextField, but that will lead to infinite loop, end up have to create the PickerTextFieldHandler class to forward.

/*
Copyright (c) 2012 Cullen SUN
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
//
// PickerTextFieldHandler.h
// http://creiapp.blogspot.hk/2012/11/uitextfield-with-input-from-uipickerview.html
//
#import <Foundation/Foundation.h>
@interface PickerTextFieldHandler : NSObject
@end
/*
Copyright (c) 2012 Cullen SUN
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
//
// PickerTextFieldHandler.m
// http://creiapp.blogspot.hk/2012/11/uitextfield-with-input-from-uipickerview.html
//
#import "PickerTextFieldHandler.h"
#import "PickerTextField.h"
@implementation PickerTextFieldHandler
#pragma mark -
#pragma mark UITextFieldDelegate
-(void)textFieldDidBeginEditing:(UITextField *)textField{
[(PickerTextField*) textField pickerTextFieldDidBeginEditing];
}
@end

You can also download the code from git.

No comments:

Post a Comment