iOS封装浅谈-一句代码弹出actionSheet,如何优雅的设计一个ActionSheetManager

一:总想着写一些封装相关的文章,但是如果没有实例空讲的话也没意思。现在就开发中自己封装的一些小东西,拿出来说一说,希望能帮助到他人。

    前言:有很多人会说,这有啥好封装的。若一个控制器出现一个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];
    }
}

最后实现带方法,拿到对应点击的Title,刷新UI,记录数据,是不是完美了。
#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) {
+           
+         }];
         }
+        [actionSheetVc addAction:actionSheetAction];
+ UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {+ }];+ [actionSheetVc addAction:cancelAction];






开发中,只要是看到功能相类似的代码出现两次,或者更多,如果你第一时间想到的不是抽取方法,该合并的合并,那么你肯定不是一个合格的开发者。今天加个功能,从别处拷贝代码一帖,明天加个功能还是相同策略,控制器原本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上,等笔者收拾好代码吧。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值