自己写的,支持图片与文字的四种显示模式,支持原生Button属性
TabBar + 导航
TabBar
TabItem + TabBar
其中:
TabItem
控制单个Tab的Icon显示等和对应的ViewController
TabBar
容纳所有的Tab,并有delegate属性
每个Tab
都是一个Button,通过设置点击事件,来控制所有Tab
的selected和unSelected状态。
在点击事件里:
- 通过
[self setSelectedIndex:(sender.tag - ROOT_BUTTON_TAG)];
控制selected状态的切换
- 通过
[delegate shouldSelectItemAtIndex]
和[delegate didSelectItemAtIndex]
来控制ViewController(NavigationController的切换)
导航功能
RootTabBarController
作为TabBar
的delegate
,实现代理方法。
其中didSelectItemAtIndex
中调用[self setSelectedIndex:index]
来控制VC的切换:
1 2 3 4 5 6 7 8 9 10 11
| - (void)tabBar:(LNBaseTabBar *)tabBar didSelectItemAtIndex:(NSInteger)index { if (index < 0 || index >= [[self navControllers] count]) { return; }
[self setSelectedIndex:index];
if ([[self delegate] respondsToSelector:@selector(tabBarController:didSelectViewController:)]) { [[self delegate] tabBarController:self didSelectViewController:[self navControllers][index]]; } }
|
其中setSelectedIndex
的逻辑如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| - (void)setSelectedIndex:(NSUInteger)selectedIndex { if (selectedIndex >= self.navControllers.count) { return; } if () { ; ; ; }
_selectedIndex = selectedIndex; ; ; ; ; ;
; ; }
|
夜间模式
LNThemePicker
Picker基类:
1 2 3 4 5 6 7 8 9 10 11
| @interface BDHKThemePicker : NSObject
@property (nonatomic, readonly) NSArray * _Nullable array;
@property (nonatomic, readonly) NSObject * _Nullable value;
+ (BDHKThemePicker * _Nonnull)pickerWithArray:(NSArray * _Nonnull)array; - (instancetype _Nonnull)initWithArray:(NSArray * _Nonnull)array;
@end
|
ColorPicker子类:
1 2 3 4 5 6 7 8 9 10 11 12
| @interface BDHKThemeColorPicker : BDHKThemePicker
@property (nonatomic, readonly) UIColor * _Nullable value;
+ (BDHKThemeColorPicker * _Nonnull)pickerWithColors:(NSArray<UIColor *> * _Nonnull)colors; + (BDHKThemeColorPicker * _Nonnull)pickerWithStrings:(NSArray<NSString *> * _Nonnull)colors; + (BDHKThemeColorPicker * _Nonnull)pickerWithNumbers:(NSArray<NSNumber *> * _Nonnull)colors; - (instancetype _Nonnull)initWithColors:(NSArray<UIColor *> * _Nonnull)colors; - (instancetype _Nonnull)initWithStrings:(NSArray<NSString *> * _Nonnull)colors; - (instancetype _Nonnull)initWithWithNumbers:(NSArray<NSNumber *> * _Nonnull)colors;
@end
|
还有CGFloatPicker、ImagePicker等。
UIView的分类
首先对NSObject实现LNTheme的分类。
themePickers
属性懒加载时,会通过关联对象获取字典值。如果没有获取到对应的关联对象,则新生成,同时设置通知及方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @interface NSObject (LNTheme)
// 设置方法及其参数对应的picker, // 如@{ // @"setImage:"->@[@"#222222", @"#333333"], // @"setBackgroundColor:"->@[@"#222222", @"#333333"] // } @property (nonatomic, strong) NSMutableDictionary *themePickers;
- (void)performThemePicker:(NSString *)selector picker:(BDHKThemePicker *)picker state:(UIControlState)state; - (void)setThemePicker:(NSString *)selector picker:(BDHKThemePicker *)picker; - (void)setThemePicker:(NSString *)selector picker:(BDHKThemePicker *)picker state:(UIControlState)state; - (BDHKThemeStatePicker *)makeStatePicker:(NSString *)selector picker:(BDHKThemePicker *)picker state:(UIControlState)state;
@end
|
UIView、UIButton、UIImageView、UITableView、UILabel、UITextFeild、UINavigationBar等UI控件都实现了一个LNTheme的分类(Category)。
以UIImageView为例,其.h
文件如下:
1 2 3 4 5
| @interface UIImageView (BDHKTheme)
@property (nonatomic, strong) BDHKThemeImagePicker * _Nullable theme_image;
@end
|
其.m
文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| @implementation UIImageView (BDHKTheme)
- (BDHKThemeImagePicker *)theme_image { return [self.themePickers objectForKeyCheck:@"setImage:"]; }
- (void)setTheme_image:(BDHKThemeImagePicker *)theme_image { [self setThemePicker:@"setImage:" picker:theme_image]; }
@end
|
使用逻辑
讨论[UIImageView setImage:]
的方法,在不同的模式下,显示不同的图片。
这主要用在一些APP内部图上,如用户头像的默认图(网络请求的图一般是不会用这个功能的)。
UIImageView对象img,通过设置theme_image
这个属性(指定ImagePicker),来指定对应模式下应使用的图片。
而在theme_image
的set
方法总,会调用NSObject(LNTheme)
分类下的setThemePicker:picker:
方法,从而将系统方法setImage:
与我们指定的picker对应上。
当我们切换模式时,将发送对应的通知,设置了picker的UI对象会调用[NSObject updateTheme]
方法,遍历themePickers
字典,为所有设置了对应picker的系统方法,更新UI显示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| - (void)updateTheme { for (NSString *selector in self.themePickers) { BDHKThemePicker *picker = [self.themePickers objectForKeyCheck:selector class:[BDHKThemePicker class]]; if ([picker isKindOfClass:[BDHKThemeStatePicker class]]) { BDHKThemeStatePicker *statePicker = (BDHKThemeStatePicker *)picker; for (NSNumber *key in statePicker.values) { [self updateThemeCell:selector picker:picker state:[key unsignedIntegerValue]]; } } else { [self updateThemeCell:selector picker:picker state:0]; } } }
|
LNJastor
用于Model的转换,通过Runtime + KVC实现。
Runtime
KHJastorRuntimeHelper, 用于获取自定义的LNJastor子类有些什么属性,通过数组返回。
属性数组,数组中的单个元素类型为NSDictionary,有三个键值对:
- type -> 自定义属性的类型:如@“NSString”、@“ProblemItem”
- name -> 自定义属性的名称:如@“problemContent”、@“problemId”
- attributes -> 自定义属性的系统属性:如@“T@“UserInfoItem”,&,N,V_userInfo”
运用了Runtime,如:
- class_copyPropertyList(aClass, &itemCount)
- property_getName(property)
- property_getAttributes(property)
核心函数[KHJastorRuntimeHelper propertyNames:]
如下:
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
| + (NSArray *)propertyNames:(__unsafe_unretained Class)aClass { if (aClass == [LNJastor class]) { return [NSArray array]; }
if (propertyListByClass == nil) { propertyListByClass = [[LNThreadSafeMutableDictionary alloc] init]; }
NSString *className = NSStringFromClass(aClass); NSArray *names = [propertyListByClass ln_arrayForKey:className]; if (names != nil) { return names; }
LNThreadSafeMutableArray *items = [LNThreadSafeMutableArray array]; unsigned int itemCount = 0; objc_property_t *propertys = class_copyPropertyList(aClass, &itemCount); for (unsigned int i = 0; i < itemCount; ++i) { objc_property_t property = propertys[i]; const char *name = property_getName(property);
if (strcmp(name,"hash") == 0 || strcmp(name,"superclass") == 0 || strcmp(name,"class") == 0 || strcmp(name,"description") == 0 || strcmp(name,"debugDescription") == 0) { continue; }
const char *attribute = property_getAttributes(property); NSArray *attributes = [[NSString stringWithUTF8String:attribute] componentsSeparatedByString:@","]; NSString *attributeType = [attributes objectAtIndex:0]; NSString *propertyType = [attributeType substringFromIndex:1]; NSString *typeName = [self propertyType:[propertyType UTF8String]]; if (typeName == nil && [attributeType hasPrefix:@"T@"]) { typeName = [attributeType substringWithRange:NSMakeRange(3, [attributeType length] - 4)]; }
NSMutableDictionary *item = [NSMutableDictionary dictionary]; [item setObjectCheck:[NSString stringWithUTF8String:name] forKey:@"name"]; [item setObjectCheck:[NSString stringWithUTF8String:attribute] forKey:@"attributes"]; [item setObjectCheck:typeName forKey:@"type"]; [items addObjectCheck:item]; } free(propertys); [propertyListByClass setObjectCheck:items forKey:className];
NSArray *array = [LNJastorRuntimeHelper propertyNames:class_getSuperclass(aClass)]; if (![NSArray isEmpty:array]) { [items addObjectsFromArray:array]; } return items; }
|
基类mapping
方法,获取属性名->属性名
字典:
1 2 3 4 5 6 7 8 9 10
| - (NSDictionary *)mapping { NSArray *properties = [LNJastorRuntimeHelper propertyNames:[self class]]; NSMutableDictionary *maps = [[NSMutableDictionary alloc] initWithCapacity:properties.count]; for (NSDictionary *property in properties) { NSString *propertyName = [property ln_stringForKey:@"name"]; [maps setObjectCheck:propertyName forKey:propertyName]; } return maps; }
|
自定义类mapping
方法,获取属性名->后端字段名
字典:
1 2 3 4 5 6 7 8 9 10
| - (NSDictionary *)mapping { NSMutableDictionary *maps = [NSMutableDictionary dictionaryWithDictionary:[super mapping]];
[maps setObjectCheck:@"pId" forKey:@"p_id"]; [maps setObjectCheck:@"frontendId" forKey:@"frontend_id"]; [maps setObjectCheck:@"title" forKey:@"title"];
return maps; }
|
设计逻辑
假如我们自定义的LNJastor子类为ProblemItem,在使用initWithDictionary:
进行初始化时:
(ProblemItem自定义的所有属性,由一个属性数组properties表示,由RuntimeHelper提供。)
- 调用
mapping
方法:根据自定义的属性名与后端字段名的映射,生成一个字典maps,其元素形式为属性名->后端字段名
- 遍历properties:根据property的@"name"字段,获取自定义的属性名;maps根据属性名,获取后端字段;初始化字典根据后端字段,获取值。通过KVC,设置该属性的值。
刷新:MJRefresh
MJRefresh
一个刷新控件。
UIScrollView、UITableView、UICollectionView、UIWebView都可以使用。
空白页:EmptyTableView
DZNEmptyDataSet
DZNEmptyDataSet
一个使你的APP在UITableView和UICollectionView没有数据的时候不至于是白板的库。
主要思想是用swizzle,把reloadData方法置换掉。
SDWebImage
AFNetWorking
FMDB