1

一个自定义分享页面引发的思考

引言

相信绝大多数人都用过或者正使用着UMeng分享,但是往往UMeng提供的分享样式满足不了我们的需求,例如我们想额外填加一些按钮(复制链接、举报等)。
目前在做的这个项目一开始是使用的UMeng的分享样式,准备上架时由于审核要求,我们需要加一个举报按钮,所以将"分享"按钮改为"更多"按钮,然后需要将举报放在分享的下面一行,并且查看别人的内容的时候需要显示举报,查看自己的内容的时候是不需要显示举报按钮的(谁还需要举报自己啊,哈哈),所以一个页面有两种状态;
同时因为有些平台不支持网页跳转登录,所以我们需要检查用户手机上面是否有安装该平台软件来显示页面,大致效果如下图:
图片描述

当时需求只有微信、朋友圈、短信三个分享,为了赶进度,十分钟写完,当时的思路是:

  1. 整个页面是个UIView,点击更多的时候进行初始化,然后add到window上面;

  2. 页面最底下放置一个UIView来实现出现和隐藏的时候透明度在0-0.3之间渐变,添加点击手势响应Remove操作;

  3. 下面的主要操作区域放置一个背景颜色为rgb都是240的UIView,根据传入的type计算总高度,中间放置一个颜色为rgb都是224的UILabel做分割线,垂直方向动画移入和移出页面;

  4. 三个分享按钮+举报按钮都使用UIButton,设置图片和标题的EdgeInsets属性为UI所需样式,举报按钮根据传入的type控制;

  5. 布局页面的时候判断手机是否安装有微信,没有的话只显示短信分享;

  6. 使用delegate与调用分享的Controller通信;
    然后适配一下,这样写最直接,但是代码量大,纯代码设置每个按钮的各项属性,还要根据用户是否安装某些软件来配置显示的按钮的坐标,扩展性不强,按钮不多还好,要是随着业务发展加入更多的分享平台,就很坑爹了。

当然有很多人使用的是UIActionSheet+UIScrollView实现,同样的道理,这样实现需要根据按钮是否显示来动态布局页面,需求变更就得重新布局,代码写着不爽。
下面说说我的思路。

正题

前几天群里有个哥们问我怎么在UMeng的界面上面加个复制按钮?显然不太好弄,于是本着负责任的态度,结合我这一年来学到的知识,重新梳理了一遍整个需求:

  1. 每组按钮可能需要组头来放置文字提示;

  2. 每个分享的按钮的图文样式需要方便随时调节;

  3. 根据用户手机是否安装某些平台来控制是否生成/显示某些按钮(所有按钮的坐标需要动态控制);

  4. 可能有两行可能有一行,也可能有很多行(后期可能会有其他需求);

  5. 一组按钮数量过多时,可能需要一行横向滑动显示,也可能需要纵向多行排布;

  6. 需要有显示和隐藏的动画效果;

  7. 最底部可能需要有个取消或者"X"按钮;

前面提到的几种方法,一旦更改需求,比如横向滑动改成纵向排布,代码写起来就会很痛苦,还是那句话,代码写起来要自己觉得爽才行,下面看看这些需求对应的解决方案:

  1. 组头、组尾、可能单行也可能多行的需求,最底部有个取消按钮,一个UITableView就能满足了,header、section、footer,根据dataSource可以非常方便地控制显示什么和显示与否;

  2. 按钮样式的调节和有可能横向排布或纵向排布,使用UICollectionView可以完美解决,UITableView的每一行里面放置一个UICollectionView,UICollectionViewCell就是按钮样式,调节样式改一个地方就行,排布方式setScrollDirection即可;

  3. 根据软件的安装状态来动态控制显示按钮,用到这两个控件,很显然我们只需要控制数据源就好了;
    所以数据源的结构应该是这样的:

图片描述

值得一提的思路

由于目前在重构公司项目,所以文章结尾给出来的Demo中,分享按钮事件没有使用Delegate去操作Controller,而是采用的OC的runtime反射调用,根据方法名反过来构造SEL方法,然后判断传入actionVC是否响应该方法,响应就performSelector执行;
同样的道理,其实model一样可以有个方法属性,但是这样写的话构造数据源所在的类必须声明实现该方法,并且需要引用model文件,本人的想法是简化操作,不要写太多没有必要的代码。
看看代码:

对于Controller的引用

@property (nonatomic, weak) id actionVC;
A.使用Dictionary

1.构造数据源

        NSMutableDictionary *wechatDic = [[NSMutableDictionary alloc]initWithDictionary:@{@"imageName":@"wechat_icon", @"title":@"微信", @"actionName":@"shareToWeChatAction"}];

2.Item点击交互

    NSDictionary *dic = _dataSource[indexPath.row];
    NSString *actionStr = dic[@"actionName"];
    SEL action = NSSelectorFromString(actionStr);
     
    if ([self.actionVC respondsToSelector:action]) {
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"//去除警告
        [self.actionVC performSelector:action withObject:nil];
    }
B.使用model(使用的地方需要引入头文件和实现响应selector构造的方法)

1.构造数据源

        M_Share *wechatModel = [[M_Share alloc]init];
        wechatModel.title = @"微信";
        wechatModel.imageName = @"wechat_icon";
        wechatModel.selector = @selector(shareToWeChatAction);//所在类需要声明/实现该方法
        [sectionOne addObject:wechatModel];
    
        [_dataSource addObject:sectionOne];

2.Item点击交互

    M_Share *shareModel = _dataSource[indexPath.row];
    if ([self.actionVC respondsToSelector:shareModel.selector]) {
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"//去除警告
        [self.actionVC performSelector:shareModel.selector withObject:nil];
    }

值得一提的是,这里从逻辑上来讲没有任何问题,但是系统始终会报一个警告,看了一下前辈们的解决办法是加了一行代码去除leaks警告:

#pragma clang diagnostic ignored "-Warc-performSelector-leaks"

所以顺便了解一一下去除项目中不必要的警告的方法,比如方法的废弃警告,找到项目Target中的Build Phases下的Compile Sources,将里面想要过滤警告的item后面编辑写入-w后保存该文件里面的警告就过滤掉了。

最后

抽时间整理成Demo出来放到github上面了:UMengDiyShareDemo
感谢阅读,希望本文对你有帮助!

本人坐标杭州,后续我会陆续把工作中遇到的问题及解决方案分享出来,互相交流学习,本人QQ:815187811,欢迎结交[笑脸].


佳敏_朱
121 声望20 粉丝

iOS进阶中