美文网首页iOS 控件定制ios底层原理iOS学习笔记
iOS进阶|适配器模式(解决设置页面)

iOS进阶|适配器模式(解决设置页面)

作者: 艾江山 | 来源:发表于2016-11-13 20:52 被阅读2295次

    前言:

    现在各大应用类APP都会有个设置页面,设置界面通常是这种


    设置页面.PNG

    我们可以发现每一个cell长得很相似但是有会有比较小的差别。

    效果:

    今天我们要实现的效果:

    效果.png
    看到这种情况最傻的办法是在tableView的代理方法里对indexpath做判断像下面一样👇
    -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
        if(indexPath.seesion == 0){
             if(indexPath.row == 1){
                 //todo
            }
             
        }else if (indexPath.seesion == 1){
              if(indexPath.row == 1){
                 //todo
            }
        }
    }
    

    这种判断的方法维护的成本可想而知,哪天产品经理觉得第二个session应该放在第一个位置,第二天又想放回第二位子,小伙子漫漫加班路等着你的。
    如果你经验比较多可能会想到面对模型开发,在model中使用多态,然后在cell中对进来的model判断实现对应的效果可以看我这篇文章关于tableview的cell差异不大的处理方法这也是一种比较好的处理方式(demo),但是这种方式对于没加一种新的cell都会对cell代码有所改动,下面我会介绍一种更好用的解决方案

    适配器模式:

    今天我们要讲的是试用iOS设计模式中的适配器模式来解决这个问题。

    简单讲适配器模式的作用就是在封装控件,接收数据的时候,数据通过中间的适配器处理后再传给控件,主要是在自定义控件时使用。


    数据流向
    • 分析:
          我们把设置页面的cell看成一个自定义的view他需要的参数是:iconNameString、titleString、accessibilityView分别为下图的1、2、3。
          除了这个三个看得到的参数我们可能还需要cell点击跳转的目标控制器,或者有的cell点击不是跳转到某个页面而是执行一段代码,这里我们把参数写成block。
    cell分析
    • 实现
      1、创建抽象适配器对象
      这里首先创建一个协议AISettingCellAdapterProtocol
    #import <Foundation/Foundation.h>
    typedef void(^optionBlock)(void);
    @protocol AISettingCellAdapterProtocol <NSObject>
    
    - (NSString*)iconNameString;
    
    - (NSString*)titleString;
    
    - (optionBlock)optionBlock;
    
    - (Class)destVC;
    
    - (UIView*)accessibilityView;
    @end
    

    协议完成后创建抽象适配器遵循这个协议。由于oc中没有虚函数的说法,我们只有空实现来模拟虚函数

    #pragma mark --AISettingCellAdapterProtocol
    - (NSString*)iconNameString{
        return nil;
    }
    
    - (NSString*)titleString{
        return nil;
    }
    
    - (optionBlock)optionBlock{
        return nil;
    }
    
    - (Class)destVC{
        return nil;
    }
    
    - (UIView*)accessibilityView{
        return nil;
    }
    

    2、适配器与数据层建立输入联系
    我们需要再创建一个适配器继承刚才创建的抽象适配器。

    /**
     装载AISettingModel的适配器
     */
    @interface AISettingModelAdapter : AISettingCellAdapter
    

    在这个实体适配器中实现适配方法

    #pragma mark --AISettingCellAdapterProtocol
    - (NSString*)iconNameString{
        AISettingModel *model = self.data;
        return model.icon;
    }
    
    - (NSString*)titleString{
        AISettingModel *model = self.data;
        return model.title;
    }
    
    - (optionBlock)optionBlock{
        AISettingModel *model = self.data;
        return model.block;
    }
    
    - (Class)destVC{
        AISettingModel *model = self.data;
        return model.destVC;
    }
    
    - (UIView*)accessibilityView{
        AISettingModel *model = self.data;
        return model.accessibilityView;
    }
    

    建立输入链接

     AISettingModel *model1 = [[AISettingModel alloc]initWithIcon:@"banben" title:@"版本" destClass:[AIDestOneViewController class] andAccessibilityView:imageiew];
            
     AISettingCellAdapter *adapter1 = [[AISettingModelAdapter alloc]initWithData:model1];
    

    3、适配器与视图层建立输出联系

        AISettingTableViewCell *cell      = [AISettingTableViewCell createTableViewCellWithTableView:tableView];
        AISettingCellAdapter *cellAdapter = self.dataSource[indexPath.section][indexPath.row];
        cell.data                         = cellAdapter;
    

    查看完整的源码第16个cell喜欢的给个star

    源码位置

    相关文章

      网友评论

      • 蜀山之光:你这有个问题,如果再点击的时候需要做传值,动画,block处理等等,你用同一个方法不是还需要判断吗
      • kirito_song:可不可以理解为适配器模式必须要通过协议或者继承来实现?
        kirito_song:@艾江山 嗯嗯。懂了。谢谢
        艾江山:@kirito_song 不是必须,它只是一个中转,把数据处理成你想要的。也可以使用class实现
      • 吴欧:1、你这里面的 accessibilityView 对于相同样式的能复用吗?
        艾江山:@疾风劲草_c920 可以的
        b5cb115ed780:@艾江山 如果是一个有20几行的表单界面,cell样式多,这种模式还能适用吗?
        艾江山:没有,我这里只是提供一个面对模型开发的思路:blush:
      • LD_左岸:有个问题 不知道算不算bug
        //分割线
        UIView *lineView = [[UIView alloc]init];
        lineView.backgroundColor = [UIColor lightGrayColor];
        self.lineView = lineView;
        [self.contentView addSubview:lineView];


        [self.lineView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(self.iconImageView.mas_right).offset = 8;
        make.right.mas_equalTo(@-8);
        make.height.mas_equalTo(@1);
        make.bottom.mas_equalTo(@0);
        }];
        这个分割线 距离屏幕的最右边 缩进8个像素? 但是效果不是这样的

        用masonry约束cell上的子控件 常常遇见蛋疼的匪夷所思的问题:disappointed_relieved:
        艾江山:@左岸__ 控件是加到cell的contentview上的,contentview的层次结构是和accessoryView平行的
        LD_左岸: //lineView
        [self.lineView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(self.iconImageView.mas_right).offset = 8;
        //make.right.mas_equalTo(@-8);
        make.height.mas_equalTo(@1);
        make.bottom.mas_equalTo(@0);
        //make.right.mas_offset(-8);
        //make.right.mas_equalTo(self.contentView.mas_right).mas_offset(-8);
        // CGFloat width = [UIScreen mainScreen].bounds.size.width - 16;
        // make.width.mas_equalTo(width);
        // make.right.mas_offset(-8).priorityHigh();
        make.right.mas_equalTo(self.mas_right).mas_offset(-8);
        }];
        当我这么写的时候 看到了想达到的效果::smile:
      • Beta_0:虽然离iOS渐行渐远、不过先留着吧:relaxed:
      • 0诛仙0:感觉没什么用,是我理解太浅吗,还是你写的比较鸡肋
        a3ff24dd7e3b:像这种不复用的cell直接用静态的不就行了,简单实在,搞不懂这是在搞什么
        艾江山:@0诛仙0 如果你设置页面每一个cell点进去还是一样的tableView这样循环很多层,你在用判断的方式你的代码量,以及维护成本就上来了,我们应该用更好的技术避免自己加班
        艾江山:@0诛仙0 到后期维护的时候就可以看出作用了,特别是大工程。以及设置页面非常复杂的情况。像我demo这种小工程确实没直接判断快
      • cf5fd31d8916:好厉害,棒棒哒👏👏👏
        艾江山:@谣谣归期 谢谢
      • 清無:我把你的设置页面布局【Masonry】完善了下。

        setData{
        self.accView = [data accessibilityView]?[data accessibilityView]:nil;
        [self.contentView addSubview:self.accView];

        [self.accView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.right.equalTo(@(-20));
        make.centerY.mas_equalTo(self.contentView);
        make.width.equalTo(@(CGRectGetWidth(self.accView.bounds)));
        make.height.equalTo(@(CGRectGetHeight(self.accView.bounds)));
        }];
        }

        这样就会让accessoryView始终处于最右边,而ipad上,若用默认的cell.accessoryView去设置的话,两边是有空白区域,且不会铺到右边的。【你可以完善下】

        // 还有tableview可以快速自适应:
        self.view.autoresizesSubviews = true;
        _tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
        艾江山:@菲拉兔 我觉得辅助视图用苹果默认的更符合苹果设计的审美,当然这个也是应该根据公司美工取舍距离。如果使用自己添加view当成辅助视图,应该在-(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier;这个方法内添加控件,setData方法中使用Macsonry的update方法
        第二个使用iOS8的预估高度设置cell自适应,因为目前多数设置页面cell高度一致,而且预估高度执行效率不高,所以没有使用。还是非常感谢您的宝贵意见,希望以后多多交流一起进步
      • 清無:MVVM。。。。
        艾江山:@kelvin943 https://github.com/ReactiveCocoa/ReactiveObjC这个是reactiveOC版本,
        kelvin943:@艾江山 Unable to find a specification for `ReactiveObjC (~> 1.0.1)` 这个pod 为什么搜索不到
        艾江山:@菲拉兔 MVVM还没有具体的研究过
      • f7139db11dcd:能不能做个demo
        艾江山:@城北小客 不应该啊 :joy: 要不是在github上直接搜索AIAnimationDemo
        脚下的斑马线:@艾江山 连接点不开啊
        艾江山:@HZW_sister 点击最后的“源码”,第16个cell就是。或者点击这个链接,喜欢给个star:smile:
        https://github.com/aizexin/AIAnimationDemo
      • Coder_Gakki:你长得太吃藕,不看! :v:

      本文标题:iOS进阶|适配器模式(解决设置页面)

      本文链接:https://www.haomeiwen.com/subject/ioufpttx.html