NimbusKit 介绍与使用实践

介绍

NimbusKit 官网
NimbusKit 源码

NimbusKit是一组用于快速开发的iOS框架,是源自Facebook的著名框架Three20的替代者,包括下面几大类的功能

  • Attributed Label - 富文字Label
  • Badge - 数字角标
  • Interapp - 应用间交互
  • Launcher - 类桌面启动器
  • Network Image - 网络图片下载显示
  • Photo Albums - 相册
  • Web Controller - 浏览器
  • Table Models - 表格数据模型
  • Overview - 直观方便的调试分析内嵌图形工具
  • 等…

NimbusKit的demo很直观,编译运行以后就可以体验其强大的功能了

Nimbus Demo

使用

因为之前的项目的富文本设计要支持iOS5iOS6,而attributeString特性只有iOS6支持,所以使用了NIAttributedLabel

富文本

这次新的项目由于有大量的表单页面,考虑到完全自己实现太耗时间,所以又重新考虑了NimbusKitTable Models

NimbusKit提供的表单组件如下

|| 组件 || 功能 ||
||:————————————————:||:——————:||
|| NIRadioGroup || 多选一 ||
|| NITextInputFormElement || 文本输入 ||
|| NISwitchFormElement || 开关 ||
|| NISliderFormElement || 滑动条 ||
|| NISegmentedControlFormElement || 分段 ||
|| NIDatePickerFormElement || 时间选择 ||
|| NITitleCellObject || 单行文本 ||
|| NISubtitleCellObject || 主副文本 ||
|| … || 等其他 ||

基础表单组件

这里首先要介绍一下NimbusKit的Table-Modal-Action模型,先上一段简单代码

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

@interface GGJSZController ()
<
UITableViewDelegate,
UITextFieldDelegate
>

@property (nonatomic, strong) UITableView *tableView;

@property (nonatomic, strong) NITableViewModel* model;
@property (nonatomic, strong) NITableViewActions *action;

@property (nonatomic, strong) NIActionBlock queryBlock;

@end



@implementation GGJSZController

- (id)init
{
self = [super init];
if (self) {

self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
...
...
self.tableView.delegate = self;
[self.view addSubview:self.tableView];

[self configureBlock];
[self configureForm];

}
return self;
}

- (void)configureBlock
{
__weak GGUserController *weakSelf = self;

self.queryBlock = ^BOOL(id object, UIViewController *controller, NSIndexPath* indexPath) {

...
...
...

return YES;
};


}

- (void)configureForm
{
self.action = [[NITableViewActions alloc] initWithTarget:self];

NSArray* tableContents =
[NSArray arrayWithObjects:
@"请输入驾驶证号",
[NITextFieldFormElement textFieldElementWithID:0 labelText:@"驾驶证号" placeholderText:@"请输入驾驶证号" value:TESTID delegate:self],
[self.action attachToObject:[NITapCellObject objectWithTitle:@"查询" color:COLOR_TAP]
navigationBlock:self.queryBlock],
nil];

self.model = [[NITableViewModel alloc] initWithSectionedArray:tableContents
delegate:(id)[NICellFactory class]];

self.tableView.dataSource = self.model;
self.tableView.delegate = [self.action forwardingTo:self];

}

@end

这段代码的效果如图

表单效果

其关键的地方就是

1
2
3

@property (nonatomic, strong) NITableViewModel* model;
@property (nonatomic, strong) NITableViewActions *action;

NITableViewModel 接管了UITableViewdataSource,并用一种更简单直观的方式创建表格内容,只需要创建对应的tableContents即可生成表单

NITableViewActions 接管了UITableViewdelegate,提供了NITableViewModel中已连接对象(attachToObject)对点击事件以block的方式进行响应

NITableViewModelNITableViewActions的恰当配合则能够生成所需的表单

自定义表单

在日常的开发中,仅仅依靠NimbusKit自带的表单组件肯定是无法完全满足我们的需求的,所以自定义表单组件则是非常必要的功能,而在NimbusKit中自定义表单组件也是非常容易的,比如自带的表单组件中只有独立的Label或者Input组件,并没有像上图那样左边为Label右边为Input的组件,而上图中所见的输入身份证的组件,即为我自定义的表单组件之一,其代码如下

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

@interface NITextFieldFormElement : NIFormElement

+ (id)textFieldElementWithID:(NSInteger)elementID labelText:(NSString*)labelText placeholderText:(NSString *)placeholderText value:(NSString *)value delegate:(id<UITextFieldDelegate>)delegate;


@property (nonatomic, copy) NSString* labelText;
@property (nonatomic, copy) NSString* placeholderText;
@property (nonatomic, copy) NSString* value;
@property (nonatomic, assign) id<UITextFieldDelegate> delegate;

@end


@interface NITextFieldFormElementCell : NIFormElementCell <UITextFieldDelegate>
@property (nonatomic, readonly, NI_STRONG) GGTextField* textField;
@end





@implementation NITextFieldFormElement

+ (id)textFieldElementWithID:(NSInteger)elementID labelText:(NSString *)labelText placeholderText:(NSString *)placeholderText value:(NSString *)value delegate:(id<UITextFieldDelegate>)delegate
{

NITextFieldFormElement* element = [super elementWithID:elementID];
element.labelText = labelText;
element.placeholderText = placeholderText;
element.value = value;
element.delegate = delegate;
return element;
}

- (Class)cellClass {
return [NITextFieldFormElementCell class];
}

@end



@implementation NITextFieldFormElementCell

@synthesize textField = _textField;

///////////////////////////////////////////////////////////////////////////////////////////////////
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if ((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) {
self.selectionStyle = UITableViewCellSelectionStyleNone;

_textField = [[GGTextField alloc] init];
[_textField setTag:self.element.elementID];
[_textField setAdjustsFontSizeToFitWidth:YES];
[_textField setMinimumFontSize:10.0f];
[_textField setTextAlignment:NSTextAlignmentRight];
[_textField addTarget:self action:@selector(textFieldDidChangeValue) forControlEvents:UIControlEventAllEditingEvents];
[self.contentView addSubview:_textField];
}
return self;
}


///////////////////////////////////////////////////////////////////////////////////////////////////
- (void)layoutSubviews {
[super layoutSubviews];

CGFloat labelWidth = 80;
CGRect frame = UIEdgeInsetsInsetRect(self.contentView.bounds, UIEdgeInsetsMake(5, 10, 5, 10));
frame = CGRectMake(frame.origin.x+labelWidth, frame.origin.y, frame.size.width-labelWidth, frame.size.height);
_textField.frame = frame;
}


///////////////////////////////////////////////////////////////////////////////////////////////////
- (void)prepareForReuse {
[super prepareForReuse];

self.textLabel.text = nil;

_textField.placeholder = nil;
_textField.text = nil;
}


///////////////////////////////////////////////////////////////////////////////////////////////////
- (BOOL)shouldUpdateCellWithObject:(NITextFieldFormElement *)textfieldElement {
if ([super shouldUpdateCellWithObject:textfieldElement]) {

self.textLabel.text = textfieldElement.labelText;

_textField.placeholder = textfieldElement.placeholderText;
_textField.text = textfieldElement.value;
_textField.delegate = textfieldElement.delegate;

_textField.tag = self.tag;

[self setNeedsLayout];
return YES;
}
return NO;
}


///////////////////////////////////////////////////////////////////////////////////////////////////
- (void)textFieldDidChangeValue {
NITextFieldFormElement* textInputElement = (NITextFieldFormElement *)self.element;
textInputElement.value = _textField.text;
}

@end

上述代码给出了如何自定义NimbusKit中的表单组件,可见表单组件几乎都是NIFormElement的子类,只要基于此类,定义显示所需的控件到NIFormElement内部,并调整对应位置即可

至此我们已经知道如何自定义组件了,但是如果有稍微复杂的需求的话,要如何实现呢?

复杂的自定义组件

如图,输入出发城市目的城市的组件也是一个自定义组件,除了有很多Label和Input之外,其高度也跟普通的组件不一样.

其实自定义高度很简单,只要重载NICell(比如NITextFieldFormElementCell)的此方法并返回所需的高度

1
+ (CGFloat)heightForObject:(id)object atIndexPath:(NSIndexPath *)indexPath tableView:(UITableView *)tableView;

然后在使用的ViewController中调用UITableView的方法

1
2
3
4
5
6
7
8
9
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGFloat height = tableView.rowHeight;
id object = [(NITableViewModel *)tableView.dataSource objectAtIndexPath:indexPath];
id class = [object cellClass];
if ([class respondsToSelector:@selector(heightForObject:atIndexPath:tableView:)]) {
height = [class heightForObject:object atIndexPath:indexPath tableView:tableView];
}
return height;
}

即可自定义此组件的高度了(其实直接在上述方法中直接返回对应的高度即可,但是上述代码则有很高的通用性)

小结

至此,我们已经了解了NimbusKit的基本功能和如何对其表单模型进行自定义.

除了此次着重介绍的Table-Action模型之外,其实NimbusKit还有很多值得使用的功能,如Launcher功能,Overview功能,实用且集成起来很简单,在这就不一一介绍了.

最后,简单介绍一下NimbusKit的作者Jeff Verkoeyen,其主要作品有:

  • Facebook for iPad ( June 2010 - June 2011 )
  • Google Maps for iPhone ( June 2012 - April 2013 )