一:总想着写一些封装相关的文章,但是如果没有实例空讲的话也没意思。现在就开发中自己封装的一些小东西,拿出来说一说,希望能帮助到他人。
前言:有很多人会说,这有啥好封装的。若一个控制器出现一个actionSheet,没啥,如果多个,就比较恶心。
1.先说下出现的时候,会有一些恶心的场景,一个控制器弹出3个or3个➕的actionSheet,那么你是怎么设计的呢。比如,上下图片看效果
2.看看用法吧,控制器内的调用; 三句代码,三个ActionSheet框就展示出来了。
/// 弹出对应的actionSheet
- (void)showActionSheetAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 2) { // 已使用年限
[self.actionSheetManeger setActionSheetWithTitle:@"请选择房产证年限" message:nil otherButtonTitles:@[@"未满2年", @"2-5年", @"满5年"] tag:kSecondHandHouseYearTypeActionSheetTag];
}else if (indexPath.row == 3) { // 卖家房子类型
[self.actionSheetManeger setActionSheetWithTitle:@"请选择卖家是否唯一住房" message:nil otherButtonTitles:@[@"非唯一房", @"唯一房"] tag:kSecondHandHouseOnlyTypeSheetTag];
} else if (indexPath.row == 4) { // 买家房子类型
[self.actionSheetManeger setActionSheetWithTitle:@"请选择买家是否首套房" message:nil otherButtonTitles:@[@"首套房", @"非首套房"] tag:kSecondHandHouseTypeActionSheetTag];
}
}
#pragma mark - SCYActionSheetManagerDelegate
- (void)actionSheetManagerdidClickAlertController:(UIAlertController *)alertController clickedButtonTitle:(NSString *)title {
switch (alertController.tag) {
case kSecondHandHouseYearTypeActionSheetTag:
if ([title isEqualToString:@"未满2年"]) {
self.secondHandHouseYearType = 0;
}else if ([title isEqualToString:@"2-5年"]) {
self.secondHandHouseYearType = 1;
} else if ([title isEqualToString:@"满5年"]) {
self.secondHandHouseYearType = 2;
}
break;
case kSecondHandHouseOnlyTypeSheetTag:
self.secondHandHouseOnlyType = [title isEqualToString:@"非唯一房"] ? 0 : 1;
break;
case kSecondHandHouseTypeActionSheetTag:
self.secondHandHouseType = [title isEqualToString:@"首套房"] ? 0 : 1;
break;
default:
break;
}
[self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:2 inSection:0],
[NSIndexPath indexPathForRow:3 inSection:0],
[NSIndexPath indexPathForRow:4 inSection:0]] withRowAnimation:UITableViewRowAnimationFade];
}
我不知道,你能不能清楚的感受到这样做的好处,好处我列出来吧.
1):封装的好处:代码量降低,代码可读性强。
2):模块化,以后遇到多个ActionSheet的容器,直接搞定。能把actionSheet的事件统一在一处处理。
3):解耦合:其实也就是,actionSheet长什么样,我不需要知道,只传入数据就行,哪天actionSheet样式需要修改,我只要在ActionSheetManager
的内部修改就行了,外部控制器(or UIView)的调用代码不需要修改。(当然这种情况很少,很少有需要要自定义actionSheet的样式)。
那么问题来了:要是你,你是怎么设计的? 卧槽,这还不简单,直接写就是了。不是三个actionSheet,用tag值区分一下。比如下面这段代码
ios7的话代码是这样的,然后还要在代理方法中写着各种东西。
self.titlesArray是你的内容数组,弹出多少个内容也不清楚
UIActionSheet *actionSheet = [[UIActionSheet alloc]
initWithTitle:title
delegate:self
cancelButtonTitle:@"取消"
destructiveButtonTitle:nil
otherButtonTitles:self.titlesArray.firstObject, self.titlesArray[1], self.titlesArray[2], self.titlesArray[3], self.titlesArray.lastObject,nil];
actionSheet.tag = 标记第一个actionSheet;
ios8的话,是这样写的,一个事件要加一个UIAlertAction,关键是这样恶心的代码,要写三处。。是在是恶心至极。
+ UIAlertController *actionSheetVc = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleActionSheet]; + UIAlertAction *actionSheetAction = [UIAlertAction actionWithTitle:actionSheetTitle style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + + }]; }
+ UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {+ }];+ [actionSheetVc addAction:cancelAction];+ [actionSheetVc addAction:actionSheetAction];
开发中,只要是看到功能相类似的代码出现两次,或者更多,如果你第一时间想到的不是抽取方法,该合并的合并,那么你肯定不是一个合格的开发者。今天加个功能,从别处拷贝代码一帖,明天加个功能还是相同策略,控制器原本300行,半年后变成了1000行,你说你写着这些垃圾恶心的,这一坨那一坨的代码,真的还能敲下去。反正我是看不了这样的代码。。当我看到这样的代码时,第一时间是懒得看,不得不看是就肯定想着,这是哪个傻逼写这么多傻逼代码。。我所做的就是,努力不成为别人口中的傻逼。
3.现在说说自己是如何设计的。直接上代码吧,说多了无益。咋们就懂代码
SCYActionSheetManager的.h文件
#import <Foundation/Foundation.h>
@protocol SCYActionSheetManagerDelegate <NSObject>
@optional
- (void)actionSheetManagerdidClickAlertController:(UIAlertController *)alertController clickedButtonTitle:(NSString *)title;
@end
@interface SCYActionSheetManager : NSObject
@property (nonatomic, weak) id<SCYActionSheetManagerDelegate> delgate;
- (void)setActionSheetWithTitle:(NSString *)title
message:(NSString *)message
otherButtonTitles:(NSArray *)titlesArray
tag:(NSInteger)tag;
@end
显示actionsheet的title,message,事件的title 数组,tag值。
SCYActionSheetManager的.m文件
#import "SCYActionSheetManager.h"
#import "UIAlertController+HControllerTag.h"
@implementation SCYActionSheetManager
#pragma mark - public
- (void)setActionSheetWithTitle:(NSString *)title message:(NSString *)message otherButtonTitles:(NSArray *)titlesArray tag:(NSInteger)tag{
if (_delgate) {
if (titlesArray.count == 0 || titlesArray.count == 1 || titlesArray.count > 6) {
CDLog(@"data is wrong");
return;
}
UIAlertController *actionSheetVc = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleActionSheet];
actionSheetVc.tag = tag;
for (NSString *actionSheetTitle in titlesArray) {
UIAlertAction *actionSheetAction = [UIAlertAction actionWithTitle:actionSheetTitle style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
if ([_delgate respondsToSelector:@selector(actionSheetManagerdidClickAlertController:clickedButtonTitle:)]) {
[_delgate actionSheetManagerdidClickAlertController:actionSheetVc clickedButtonTitle:actionSheetTitle];
}
}];
[actionSheetVc addAction:actionSheetAction];
}
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
}];
[actionSheetVc addAction:cancelAction];
if ([_delgate isKindOfClass:[UIViewController class]]) {
__weak UIViewController *controller = (UIViewController *)_delgate;
[controller presentViewController:actionSheetVc animated:YES completion:nil];
}
if ([_delgate isKindOfClass:[UIView class]]) {
__weak UIView *view = (UIView *)_delgate;
[[self getCurrentViewControllerWithView:view] presentViewController:actionSheetVc animated:YES completion:nil];
}
}
}
/** 获取view的控制器对象 */
-(UIViewController *)getCurrentViewControllerWithView:(UIView *)view{
UIResponder *next = [view nextResponder];
do {
if ([next isKindOfClass:[UIViewController class]]) {
return (UIViewController *)next;
}
next = [next nextResponder];
} while (next != nil);
return nil;
}
@end
delegate容器支持UIView,和UIViewController两种容器,处理事件。短短100行不到的代码 就实现了最简单的使用
4.最后说下UIAlertController的Tag属性,这是受到UIActionSheet的启发,那么就运行时增加属性就是了。当一个控制器的需要多个ActionSheet的时候,通过tag值也好区分。
UIAlertController+HControllerTag.m的实现
#import "UIAlertController+HControllerTag.h"
#import <objc/runtime.h>
@implementation UIAlertController (HControllerTag)
static const char kf_UIAlertControllerTagKey = '\0';
- (NSInteger)tag{
NSString *tag = objc_getAssociatedObject(self, &kf_UIAlertControllerTagKey);
return tag.integerValue;
}
- (void)setTag:(NSInteger)tag {
NSString *Tag = [NSString stringWithFormat:@"%ld", (long)tag];
objc_setAssociatedObject(self, &kf_UIAlertControllerTagKey, Tag, OBJC_ASSOCIATION_COPY);
}
@end
o了,运行时在分类中动态增加属性,这个在开发中还是很常见的,不难吧。
总结:本文可能举例的地方不是太好,但也是能充分说明问题,开发中,一个问题的解决方式千千万万中,但一定要尽力做到最好的一种。封装的一大好处就是模块话,那么以后这个管理类目,无论带到哪个项目里,直接就可以使用了。封一次,受益终身啊。
github:地址,后续会把说封装这一块的代码,都整理好放在github上,等笔者收拾好代码吧。