学习文章
IOS学习笔记(六)inputAccessoryView,inputView
效果
简单介绍
UIPickerView并不是一个使用频率很高的控件,但指不定你的应用中就会用到.
UIPickerView的用法符合苹果一贯的设计,跟UITableView很像,而且比UITableView简单.
跟UITableView做一下类比,UIPickerViewDataSource用来监听数据源,UIPickerViewDelegate用来监听事件.还有一个与UIPickerView相关的协议UIPickerViewAccessibilityDelegate,这是用来实现Accessibility and VoiceOver的,参见.
看网上的教程说UIPickerView的高度是一定的,然而我测试,高度还是由你设定的frame决定的,并没有什么固定值.
再说一下showsSelectionIndicator这个布尔值属性,iOS7之后没有效果了,参见
最后说一下,关于定制UIPickerView 的component 的三个代理方法
1.
- (nullable NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component __TVOS_PROHIBITED;
2.
- (nullable NSAttributedString *)pickerView:(UIPickerView *)pickerView attributedTitleForRow:(NSInteger)row forComponent:(NSInteger)component NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED; // attributed title is favored if both methods are implemented
3.
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(nullable UIView *)view __TVOS_PROHIBITED;
这三个方法的优先级是递增的,也就是说,你实现了方法3,就不走方法2,你实现了方法2,就不走方法1了.
简单介绍的源码
#import "ViewController.h"
#define SCREEN_WIDTH ([UIScreen mainScreen].bounds.size.width)
#define SCREEN_HEIGHT ([UIScreen mainScreen].bounds.size.height)
// 还有一个与UIPickerView相关的协议UIPickerViewAccessibilityDelegate,这是用来实现Accessibility and VoiceOver的
@interface ViewController ()<UIPickerViewDataSource,UIPickerViewDelegate>
@property (nonatomic, strong) UIPickerView *m_pickerView;
@property (nonatomic, strong) NSMutableArray *m_yearArray;
@property (nonatomic, strong) NSMutableArray *m_monthArray;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"UIPickerView简单介绍";
self.m_yearArray = [NSMutableArray arrayWithArray:@[@"2014年",@"2015年"]];
self.m_monthArray = [NSMutableArray arrayWithArray:@[@"1月",@"2月",@"3月",@"4月",@"5月",@"6月",@"7月",@"8月",@"9月",@"10月",@"11月",@"12月"]];
// 看网上的教程说UIPickerView的高度是一定的,然而我测试,高度还是由你设定的frame决定的,并没有什么固定值
self.m_pickerView = [[UIPickerView alloc]initWithFrame:CGRectMake(0, self.view.frame.size.height - 200, self.view.frame.size.width, 200)];
[self.view addSubview:self.m_pickerView];
self.m_pickerView.dataSource = self;
self.m_pickerView.delegate = self;
// iOS7之后没有效果了,参见https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/UIKitUICatalog/UIPickerView.html
self.m_pickerView.showsSelectionIndicator = YES;
}
#pragma mark - UIPickerViewDataSource
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 2;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
if (component == 0) {
return self.m_yearArray.count;
} else {
return self.m_monthArray.count;
}
}
#pragma mark - UIPickerViewDelegate
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component {
return self.view.frame.size.width/2.0;
}
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component __TVOS_PROHIBITED {
return 40;
}
- (nullable NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
if (component == 0) {
return self.m_yearArray[row];
} else {
return self.m_monthArray[row];
}
}
// 此方法的优先级高于上面的方法,实现了此方法,则上面的方法就不执行了,这个很好理解
// 经过我的测试,此方法的reusingView并没什么用,可能是iOS7之后不再使用,方法说明中关于此参数是这样说的"A view object that was previously used for this row, but is now hidden and cached by the picker view.",另外参考http://stackoverflow.com/questions/20635949/reusing-view-in-uipickerview-with-ios-7
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(nullable UIView *)view {
UILabel * label = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 100, 40)];
if (component == 0) {
label.text = self.m_yearArray[row];
} else {
label.text = self.m_monthArray[row];
}
return label;
}
@end
UIPickerView 作为 InputView
我们知道,UITextField 和UITextView 有两个属性 inputView 和 inputAccessoryView,可以用来自定义键盘和键盘的附属视图,其实,这两个属性UIResponder就有,只不过是readonly.
根据此,我们可以将UIPickerView 作为 任何 UIResponder 的子类的 InputView ,不过,我们需要做一些调整.
比如,我们准备让UIPickerView 作为 UIButton 的 InputView,我们需要实现一个 UIButton 的子类,在子类中,我们将这两个属性改为readwrite,并使这个子类可以成为第一响应者.
// 将UIResponder的这两个readonly属性变为readwrite
@property (nonatomic,strong,readwrite) __kindof UIView * inputView;
@property (nonatomic,strong,readwrite) __kindof UIView * inputAccessoryView;
// 这个方法必须实现
- (BOOL) canBecomeFirstResponder {
return YES;
}
接下来,我用两种方式来实现将 UIPickerView 作为 InputView ,一种将 UIPickerView封装到View层(用UIButton的子类作为例子),一种将UIPickerView 封装到控制器层(用UILabel的子类作为例子).
封装到View层源码
ButtonWithPickerView.h
#import <UIKit/UIKit.h>
// 此类用以说明如何将 PickerView 封装到 View 层
@interface ButtonWithPickerView : UIButton
@end
ButtonWithPickerView.m
#import "ButtonWithPickerView.h"
#define SCREEN_WIDTH ([UIScreen mainScreen].bounds.size.width)
#define SCREEN_HEIGHT ([UIScreen mainScreen].bounds.size.height)
@interface ButtonWithPickerView () <UIPickerViewDataSource,UIPickerViewDelegate>
// 将UIResponder的这两个readonly属性变为readwrite
@property (nonatomic,strong,readwrite) __kindof UIView * inputView;
@property (nonatomic,strong,readwrite) __kindof UIView * inputAccessoryView;
@property (nonatomic, strong) NSMutableArray *m_yearArray;
@property (nonatomic, strong) NSMutableArray *m_monthArray;
@property (nonatomic, strong) NSString * m_yearStr;
@property (nonatomic, strong) NSString * m_monthStr;
@end
@implementation ButtonWithPickerView
// 这个方法必须实现
- (BOOL) canBecomeFirstResponder {
return YES;
}
// inputView背景色设为透明是无效的
- (UIPickerView *)inputView {
if (_inputView == nil) {
self.m_yearArray = [NSMutableArray arrayWithArray:@[@"2014年",@"2015年"]];
self.m_monthArray = [NSMutableArray arrayWithArray:@[@"1月",@"2月",@"3月",@"4月",@"5月",@"6月",@"7月",@"8月",@"9月",@"10月",@"11月",@"12月"]];
self.m_yearStr = self.m_yearArray[0];
self.m_monthStr = self.m_monthArray[0];
UIPickerView *pickerView = [[UIPickerView alloc]initWithFrame:CGRectMake(0, SCREEN_HEIGHT - 200, SCREEN_WIDTH, 200)];
pickerView.dataSource = self;
pickerView.delegate = self;
return pickerView;
}
return _inputView;
}
// inputAccessoryView 是 UIView即可,不一定非得是UIToolbar
- (UIToolbar*)inputAccessoryView {
if (_inputAccessoryView == nil) {
UIToolbar * toolBar = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, 44)];
UIBarButtonItem * item = [[UIBarButtonItem alloc]initWithTitle:@"Done" style:UIBarButtonItemStyleDone target:self action:@selector(btnResignFirstResponder)];
toolBar.items = @[item];
return toolBar;
}
return _inputAccessoryView;
}
- (void)btnResignFirstResponder {
[self resignFirstResponder];
}
#pragma mark - UIPickerViewDataSource
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 2;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
if (component == 0) {
return self.m_yearArray.count;
} else {
return self.m_monthArray.count;
}
}
#pragma mark - UIPickerViewDelegate
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component {
return SCREEN_WIDTH/2.0;
}
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component __TVOS_PROHIBITED {
return 40;
}
- (nullable NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
if (component == 0) {
return self.m_yearArray[row];
} else {
return self.m_monthArray[row];
}
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
if (component == 0) {
self.m_yearStr = self.m_yearArray[row];
} else {
self.m_monthStr = self.m_monthArray[row];
}
[self setTitle:[NSString stringWithFormat:@"%@ %@",self.m_yearStr,self.m_monthStr] forState:UIControlStateNormal];
}
@end
SecondViewController.m
#import "SecondViewController.h"
#import "ButtonWithPickerView.h"
#define SCREEN_WIDTH ([UIScreen mainScreen].bounds.size.width)
#define SCREEN_HEIGHT ([UIScreen mainScreen].bounds.size.height)
@interface SecondViewController ()
@end
@implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"Button调出PickerView";
self.view.backgroundColor = [UIColor whiteColor];
ButtonWithPickerView *btn = [ButtonWithPickerView buttonWithType:UIButtonTypeCustom];
[self.view addSubview:btn];
btn.frame = CGRectMake(0, 0, 300, 40);
btn.center = self.view.center;
btn.backgroundColor = [UIColor purpleColor];
[btn setTitle:@"PickView" forState:UIControlStateNormal];
[btn addTarget:self action:@selector(btnAction:) forControlEvents:UIControlEventTouchUpInside];
}
- (void)btnAction:(UIButton*)sender {
[sender becomeFirstResponder];
}
@end
封装到控制器层源码
LabelWithPickView.h
#import <UIKit/UIKit.h>
@interface LabelWithPickView : UILabel
// 将UIResponder的这两个readonly属性变为readwrite
@property (nonatomic,strong,readwrite) __kindof UIView * inputView;
@property (nonatomic,strong,readwrite) __kindof UIView * inputAccessoryView;
@end
LabelWithPickView.m
#import "LabelWithPickView.h"
#define SCREEN_WIDTH ([UIScreen mainScreen].bounds.size.width)
#define SCREEN_HEIGHT ([UIScreen mainScreen].bounds.size.height)
@implementation LabelWithPickView
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapAction)];
[self addGestureRecognizer:tap];
}
return self;
}
- (void)tapAction {
[self becomeFirstResponder];
}
// 这个方法必须实现
- (BOOL) canBecomeFirstResponder {
return YES;
}
@end
ThirdViewController.m
#import "ThirdViewController.h"
#import "LabelWithPickView.h"
#define SCREEN_WIDTH ([UIScreen mainScreen].bounds.size.width)
#define SCREEN_HEIGHT ([UIScreen mainScreen].bounds.size.height)
@interface ThirdViewController ()<UIPickerViewDataSource,UIPickerViewDelegate>
@property (nonatomic, strong) NSMutableArray *m_yearArray;
@property (nonatomic, strong) NSMutableArray *m_monthArray;
@property (nonatomic, strong) LabelWithPickView *m_label;
@end
@implementation ThirdViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"Label调出PickerView";
self.view.backgroundColor = [UIColor orangeColor];
self.m_yearArray = [NSMutableArray arrayWithArray:@[@"2014年",@"2015年"]];
self.m_monthArray = [NSMutableArray arrayWithArray:@[@"1月",@"2月",@"3月",@"4月",@"5月",@"6月",@"7月",@"8月",@"9月",@"10月",@"11月",@"12月"]];
UIPickerView *pickerView = [[UIPickerView alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, 200)];
pickerView.dataSource = self;
pickerView.delegate = self;
pickerView.backgroundColor = [UIColor redColor];
UIToolbar * toolBar = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, 44)];
UIBarButtonItem * item = [[UIBarButtonItem alloc]initWithTitle:@"Done" style:UIBarButtonItemStyleDone target:self action:@selector(toolBarItemAction)];
toolBar.items = @[item];
self.m_label = [[LabelWithPickView alloc]initWithFrame:CGRectMake((SCREEN_WIDTH - 300)/2.0, 100, 300, 40)];
[self.view addSubview:self.m_label];
self.m_label.userInteractionEnabled = YES;
self.m_label.backgroundColor = [UIColor redColor];
self.m_label.text = @"Tap Me";
self.m_label.textAlignment = NSTextAlignmentCenter;
// inputView背景色设为透明是无效的
self.m_label.inputView = pickerView;
// inputAccessoryView 是 UIView即可,不一定非得是UIToolbar
self.m_label.inputAccessoryView = toolBar;
self.m_label.inputView.backgroundColor = [UIColor clearColor];
}
- (void)toolBarItemAction {
[self.m_label resignFirstResponder];
UIPickerView *pickerView = self.m_label.inputView;
NSInteger year = [pickerView selectedRowInComponent:0];
NSInteger month = [pickerView selectedRowInComponent:1];
NSString *yearStr = self.m_yearArray[year];
NSString *monthStr = self.m_monthArray[month];
self.m_label.text = [NSString stringWithFormat:@"%@ %@",yearStr,monthStr];
}
#pragma mark - UIPickerViewDataSource
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 2;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
if (component == 0) {
return self.m_yearArray.count;
} else {
return self.m_monthArray.count;
}
}
#pragma mark - UIPickerViewDelegate
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component {
return self.view.frame.size.width/2.0;
}
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component __TVOS_PROHIBITED {
return 40;
}
- (nullable NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
if (component == 0) {
return self.m_yearArray[row];
} else {
return self.m_monthArray[row];
}
}
@end