一、前言
笔者最近一直忙于开发业务需求,频繁的使用着继承、分类,切身的体会到很多需求用这两种方案都可以解决,这就面临着幸福二选一的问题了!但越到后面,越能感觉到其明显的区别,于是有所感~
二、需求简要
iOS 时间选择器(UIDatePicker)大家都有用过吧,很方便对吧,那性别选择、省市区选择、付款方式选择等功能,我们如何实现类似的效果呢?用UIPickerView实现单选择功能,就能实现类似的用户体验。
三、应用场景
笔者最近遇到的开发需求是产品的属性选择,比如我们现在要发起订单(我们可以类比一下淘宝),选择了佳得乐,这时候我们还要选择口味(蓝莓、鲜橙、哈密瓜等),然后还可以选择净含量等。
四、代码实现
由于在实际开发中,通常都是与UITextField联合实现的,笔者就选择UITextField为最小颗粒度,对其进行处理。
- 1.使用继承方案
LYTextField.h
1
2
3
4
5
6
7
@interface LYTextField : UITextField
@property (nonatomic, strong) NSArray *dataArr;
@end
LYTextField.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
@interface LYTextField ()
<UIPickerViewDelegate, UIPickerViewDataSource>
@property (nonatomic, weak) UIPickerView *pickerView;
@end
@implementation LYTextField
- (instancetype)init {
self = [super init];
if (self) {
[self configFoundation];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self configFoundation];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self configFoundation];
}
return self;
}
- (void)configFoundation {
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
UIView *optionView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, screenWidth, 44)];
optionView.backgroundColor = [UIColor orangeColor];
UIButton *cancelBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[cancelBtn setFrame:CGRectMake(0, 0, 70, 44)];
[cancelBtn setTitle:@"取消" forState:UIControlStateNormal];
[cancelBtn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[cancelBtn.titleLabel setFont:[UIFont systemFontOfSize:17.0]];
[cancelBtn addTarget:self action:@selector(onClickCancelBtn) forControlEvents:UIControlEventTouchUpInside];
[optionView addSubview:cancelBtn];
UIButton *finishBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[finishBtn setFrame:CGRectMake(screenWidth - 70, 0, 70, 44)];
[finishBtn setTitle:@"完成" forState:UIControlStateNormal];
[finishBtn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[finishBtn.titleLabel setFont:[UIFont systemFontOfSize:17.0]];
[finishBtn addTarget:self action:@selector(onClickFinishBtn) forControlEvents:UIControlEventTouchUpInside];
[optionView addSubview:finishBtn];
UILabel *titleL = [[UILabel alloc]initWithFrame:CGRectMake(screenWidth / 2.0 - 75.0, 0, 150, 44)];
titleL.textAlignment = NSTextAlignmentCenter;
titleL.text = @"请选择类型";
[optionView addSubview:titleL];
self.inputAccessoryView = optionView;
UIPickerView *pickerView = [[UIPickerView alloc] init];
pickerView.dataSource = self;
pickerView.delegate = self;
pickerView.backgroundColor = [UIColor whiteColor];
self.pickerView = pickerView;
self.inputView = pickerView;
}
- (void)onClickCancelBtn {
[self resignFirstResponder];
}
- (void)onClickFinishBtn {
NSInteger index = [self.pickerView selectedRowInComponent:0];
if (index < self.dataArr.count) {
self.text = self.dataArr[index];
}
[self resignFirstResponder];
}
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
return self.dataArr.count;
}
- (NSString *)pickerView:(UIPickerView *)pickerView
titleForRow:(NSInteger)row
forComponent:(NSInteger)component {
if (row < self.dataArr.count) {
return self.dataArr[row];
}
return @"";
}
@end
- 2.使用分类方案
UITextField+Tool.h
1
2
3
4
5
6
7
8
9
10
11
12
typedef void(^DidSelectedBlock)(NSString *value);
@interface UITextField (Tool)
<UIPickerViewDelegate, UIPickerViewDataSource>
- (void)configSigleChoiceWith:(NSArray *)preData completion:(DidSelectedBlock)completion;
- (void)configValue:(NSString *)value;
@end
UITextField+Tool.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
@interface UITextField ()
@property (nonatomic, strong) NSArray *choiceData;
@property (nonatomic, copy) DidSelectedBlock didSelectedBlock;
@end
NSString *const KeyChoiceData = @"KeyChoiceData";
NSString *const KeyDidSelectedBlock = @"KeyDidSelectedBlock";
@implementation UITextField (Tool)
- (NSArray *)choiceData {
return objc_getAssociatedObject(self, &KeyChoiceData);
}
- (void)setChoiceData:(NSArray *)choiceData {
objc_setAssociatedObject(self, &KeyChoiceData, choiceData, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (DidSelectedBlock)didSelectedBlock {
return objc_getAssociatedObject(self, &KeyDidSelectedBlock);
}
- (void)setDidSelectedBlock:(DidSelectedBlock)didSelectedBlock {
objc_setAssociatedObject(self, &KeyDidSelectedBlock, didSelectedBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (void)configSigleChoiceWith:(NSArray *)preData completion:(DidSelectedBlock)completion {
self.choiceData = [preData mutableCopy];
self.didSelectedBlock = completion;
[self setSelectedList];
}
- (void)configValue:(NSString *)value {
self.text = value;
if (self.didSelectedBlock) {
self.didSelectedBlock(value);
}
if (value.length == 0) {
return;
}
for (NSInteger i = 0; i < self.choiceData.count; i ++) {
NSString *itemStr = self.choiceData[I];
if ([value isEqualToString:itemStr]) {
UIPickerView *pickerView = (UIPickerView *)self.inputView;
[pickerView selectRow:i inComponent:0 animated:YES];
break;
}
}
}
- (void)setSelectedList {
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
UIView *optionView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, screenWidth, 44)];
optionView.backgroundColor = [UIColor orangeColor];
UIButton *cancelBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[cancelBtn setFrame:CGRectMake(0, 0, 70, 44)];
[cancelBtn setTitle:@"取消" forState:UIControlStateNormal];
[cancelBtn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[cancelBtn.titleLabel setFont:[UIFont systemFontOfSize:17.0]];
[cancelBtn addTarget:self action:@selector(onClickCancelBtn) forControlEvents:UIControlEventTouchUpInside];
[optionView addSubview:cancelBtn];
UIButton *finishBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[finishBtn setFrame:CGRectMake(screenWidth - 70, 0, 70, 44)];
[finishBtn setTitle:@"完成" forState:UIControlStateNormal];
[finishBtn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[finishBtn.titleLabel setFont:[UIFont systemFontOfSize:17.0]];
[finishBtn addTarget:self action:@selector(onClickFinishBtn) forControlEvents:UIControlEventTouchUpInside];
[optionView addSubview:finishBtn];
UILabel *titleL = [[UILabel alloc]initWithFrame:CGRectMake(screenWidth / 2.0 - 75.0, 0, 150, 44)];
titleL.textAlignment = NSTextAlignmentCenter;
titleL.text = @"请选择类型";
[optionView addSubview:titleL];
self.inputAccessoryView = optionView;
UIPickerView *pickerView = [[UIPickerView alloc] init];
pickerView.dataSource = self;
pickerView.delegate = self;
pickerView.backgroundColor = [UIColor whiteColor];
self.inputView = pickerView;
}
- (void)onClickCancelBtn {
[self resignFirstResponder];
}
- (void)onClickFinishBtn {
UIPickerView *pickerView = (UIPickerView *)self.inputView;
NSInteger index = [pickerView selectedRowInComponent:0];
if (index < self.choiceData.count) {
[self configValue:self.choiceData[index]];
}
[self resignFirstResponder];
}
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
return self.choiceData.count;
}
- (NSString *)pickerView:(UIPickerView *)pickerView
titleForRow:(NSInteger)row
forComponent:(NSInteger)component {
if (row < self.choiceData.count) {
return self.choiceData[row];
}
return @"";
}
@end
- 3.使用代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21- (void)configTextFieldDemo {
CGFloat screenWidth = self.view.bounds.size.width;
LYTextField *textField1 = [[LYTextField alloc] init];
textField1.bounds = CGRectMake(0, 0, 200, 44);
textField1.borderStyle = UITextBorderStyleRoundedRect;
textField1.center = CGPointMake(screenWidth/2, 100);
textField1.placeholder = @"请选择口味";
textField1.dataArr = @[@"鲜橙", @"哈密瓜", @"蓝莓"];
[self.view addSubview:textField1];
UITextField *textField2 = [[UITextField alloc] init];
textField2.bounds = CGRectMake(0, 0, 200, 44);
textField2.borderStyle = UITextBorderStyleRoundedRect;
textField2.center = CGPointMake(screenWidth/2, 200);
textField2.placeholder = @"请选择净含量";
[self.view addSubview:textField2];
NSArray *dataArr = @[@"250 mL", @"500 mL", @"1000 mL"];
[textField2 configSigleChoiceWith:dataArr completion:nil];
}
五、写在最后
- 上例中,分类明显的优势在于可以孤立存在,也是最干净的编码。其实就是在一个对象的基础上增加额外的功能形成另外一个对象。
- 关于继承毫无疑问最大的优点是代码复用。但是很多时候继承也可能会被无止境的滥用,造成代码结构散乱,后期维护困难等,其中有可能带来最大的问题是高耦合,依赖性太强。
- 实际开发中如果继承超过2层的时候,就要慎重了,因为这可能是滥用继承的开始。
- 分类是一种平行的架构,相互独立存在,可移植性强,继承是纵向的架构,相互依赖,缺一个,整个架构就断层了。