美文网首页iOS 实用技术demo程序员
iOS 实现微信朋友圈评论回复功能(一)

iOS 实现微信朋友圈评论回复功能(一)

作者: CoderMikeHe | 来源:发表于2017-02-13 12:57 被阅读10277次
    一、概述
    • iOS开发中,相信许多开发者都遇到过,类似于像微信朋友圈的评论回复功能的开发,难点莫过于 Cell里面的子控件布局点击事件的回调评论回复的逻辑处理
    • 笔者将通过 两种方法来实现微信朋友圈评论回复功能,也将通过一个仿优酷视频评论和回复的Demo 来实战一番,本文将通过利用UITableView段头+cell+段尾来实现,希望能为广大开发者提供一点思路,少走一些弯路,填补一些细坑。
    • iOS实现微信朋友圈评论回复功能(二)
    • iOS 实现优酷视频的评论回复功能

    二 、 效果图


    效果图.gif
    三、利用UITableView段头+cell+段尾实现
    1. 页面分析
    1. 技术分析
    • 列表展示数据流
      • 列表通常利用tableView来实现。
      • tableview实现又分为自动布局绝对布局(即Frame布局
        自动布局:个人推荐利用Masonry+UITableView-FDTemplateLayoutCell的方案来实现自动布局。
        Frame布局: 事先计算出Cell子控件的Frame以及Cell的高度,存入ModelFrame里面,虽然计算稍微复杂,但是性能好,可控性强,易动画,扩展维护成本低
        两种布局方式不是本文的重点,依旧个人喜好,笔者偏好是Frame布局,本文案例也将采用这种方式。
    • Cell里面的昵称支持点击跳转用户信息
      • 首先明确内容支持 attributedString(富文本)而不是text(普通文本)
      • 可以使用 UILabelattributedText,或者采用第三方框架TYAttributedLabelYYText,笔者在此采用的是YYText
    3. 技术难点
    • Frame布局计算复杂

      1. 实际项目中,我们从服务器获取的数据,转换为数据模型(Model),对应的数据有些不能直接显示在视图(View)上,需要二次处理。tableviewCell高度计算,是tableView使用以及优化的重点对象,最好的方式莫过于高度缓存,笔者这里为每个模型(Model)配备一个模型尺寸(ModelFrame),主要用来计算tableViewCell的高度(cellHeight),以及子控件的尺寸(frame),而且保证模型(Model)的纯净性以及减少胖模型的生成。
      2. ModelFrame模型持有Model,并开放cell中各个子控件的Frame属性,,以及cellHeight缓存cell高度。注意:这里属性应该设置为readonly来修饰更为合理,防止外界修改对应的尺寸(Frame),切记:细节显能力
      3. 重写ModelFrameModelsetter方法,计算tabelViewCellheaderView子控件的Frame以及缓存cell的高度cellHeightheaderView的高度height
      4. tableViewCellheaderView自身持有各自ModelFrame模型,在tableView的数据源给tableViewCellCellheaderView注入ModelFrame时,确定tabelViewCellheaderView的子控件的布局(Frame)以及数据。
    • Cell宽度的修改
      只要重写tableViewCellframesetter方法即可统一修改cell的尺寸。

       - (void)setFrame:(CGRect)frame
       {
             frame.origin.x = MHVideoTopicAvatarWH+2*MHVideoTopicHorizontalSpace;
             frame.size.width = MHMainScreenWidth - frame.origin.x - MHVideoTopicHorizontalSpace;
             [super setFrame:frame];
        }
      
    • 复杂的事件回调
      事件传递三种方式delegatenotificationblock。笔者项目采用的是代理delegate,将所有事件代理给viewController,事件的传递详情,请查看文章下方给出的Demo地址。

    四、细节处理
    1. 设置文本的额外区域,防止文字过少,用户无法点中文本的bug
      Label的额外区域@2x.png
      // 文本
      YYLabel *contentLabel = [[YYLabel alloc] init];
    
      // 设置文本的额外区域,修复用户点击文本没有效果
      UIEdgeInsets textContainerInset = contentLabel.textContainerInset;
      textContainerInset.top = MHVideoTopicVerticalSpace;
      textContainerInset.bottom = MHVideoTopicVerticalSpace;
      contentLabel.textContainerInset = textContainerInset;
      
      contentLabel.numberOfLines = 0 ;
      contentLabel.textAlignment = NSTextAlignmentLeft;
      [self.contentView addSubview:contentLabel];
    
    1. 点击评论昵称获取用户模型(MHUser
    • 点击评论昵称,在评论模型(MHComment)中通过通知传递用户模型(MHUser)。虽获取用户模型(MHUser)简单,但使用通知会增加项目的耦合性
    // 文本高亮模型
          YYTextHighlight *toUserHighlight = [YYTextHighlight highlightWithBackgroundColor:[UIColor colorWithWhite:0.000 alpha:0.220]];
          // 这里痛过属性的userInfo保存User模型,后期通过获取模型然后获取User模型
          toUserHighlight.userInfo = @{MHCommentUserKey:self.toUser};
          
          // 点击用户的昵称的事件传递
    //        toUserHighlight.tapAction = ^(UIView *containerView, NSAttributedString *text, NSRange range, CGRect rect)
    //        {
    //            // 这里通过通知把用户的模型传递出去
    //        };
    
    • 点击评论昵称,通过label的点击事件来获取,由于YYLablehighlightTapAction的事件,无法获取YYTextHighlight模型,但是通过KVC可以获取到YYTextHighlight模型,从而得到textHighlight.userInfo。这样一来通过delegate就可将用户模型(MHUser)传递出去。
    __weak typeof(self) weakSelf = self;
        contentLabel.highlightTapAction = ^(UIView *containerView, NSAttributedString *text, NSRange range, CGRect rect) {
            
            // 利用KVC获取UserInfo 其实可以在MHComment模型里面利用 通知告知控制器哪个用户被点击了
            YYTextHighlight *highlight = [containerView valueForKeyPath:@"_highlight"];
            
            if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(commentCell:didClickedUser:)]) {
                [weakSelf.delegate commentCell:weakSelf didClickedUser:highlight.userInfo[MHCommentUserKey]];
            }       
        };
    
    五、期待
    1. 文章若对您有点帮助,请给个喜欢❤️,毕竟码字不易;若对您没啥帮助,请给点建议💗,切记学无止境。
    2. 针对文章所述内容,阅读期间任何疑问;请在文章底部评论指出,我会火速解决和修正问题。
    3. GitHub地址:https://github.com/CoderMikeHe
    六、代码

    MHDevelopExample_Objective_C - MHTopicOneController.h/m

    相关文章

      网友评论

      • 魏志军:点击评论后想让tableview上移,cell不被遮挡
      • 蒲公英守候_c082:老哥,这个github下载下来的,为什么pod文件没有,也pod不进去,麻烦你能看看不
        CoderMikeHe:@蒲公英守候_c082 将 CMHDEBUG 这个宏改成 0 即可
        蒲公英守候_c082:百度云的下载了,只是没找到,朋友圈评论这个demo
        CoderMikeHe:@蒲公英守候_c082 有百度云,详见ReadMe
      • JYou:大神你好,最近公司要快速开发一个即时通讯app,看了一些方案,觉得您的仿微信demo很不错,但整体demo过于复杂,请问下有单独朋友圈的demo分享吗?谢谢
      • ZhangCc_:一个section里,都用cell会有问题吗?
        CoderMikeHe:@ZhangCc_ :blush:
        ZhangCc_:@CoderMikeHe :smile: :blush: :kissing_heart:
        CoderMikeHe:@ZhangCc_ 稳如藏獒。:+1:
      • 呃哈哈:你好,我对ReactiveCocoa不是很熟,我想删除其中某一个MHMomentItemViewModel,但是NSArray不可修改。我看到你备注的NSMutableArray不支持KVO,不能被RACObserve。所以有什么好的建议吗
      • ad3fd5aca069:新人学习,请教下几个问题
        这整个一块(段头 + cell + 段尾)是一个大的cell吧,然后里面的小cell应该是又是一个tableView列表,但这样的话不就是方案二吗(cell 里面嵌套tableView),所以猜测这整个一块难道就是一个tableView,然后内容就是组头+内容+组尾的方式,那这样的话这一个列表得多少个tableView,糊涂了,请教下大神
        CoderMikeHe:@bls 你理解错误了呢! 整个一块是由`组头+ 回复cell+组尾`组成,而不是你所谓的一个自定义的大cell 。这里的Cell只是单纯用来显示评论或回复数据。百度网盘地址私发给你了。
        ad3fd5aca069:@CoderMikeHe demo下载失败,这三个方法我知道,我的意思是,你这整个一块是一个自定义的大cell ,大cell里面的布局有组头+ 回复cell+组尾(这不就是一个tableView么),那现在列表有100条数据,就是有100个大cell,那每个大cell里面不就是都有一个(组头+ 回复cell+组尾即一个tableView),我是这么理解的
        CoderMikeHe:@bls 没有呀,先看看Demo咯。
        段(组)头:通过 `- (nullable UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section`获取
        段(组)尾巴:通过 `- (nullable UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section`获取
        Cell : 通过 `- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath` 获取
        这能明白吗???
      • 呃哈哈:如果我说百度云失效了,你信不信
        麻烦给我发一下好吗,谢谢了
        呃哈哈:@CoderMikeHe 试过了。三遍
        CoderMikeHe:@呃哈哈 去过github上去,那个是最新的
      • 953641bb06b7:大神哥哥,能把代码一写成能评论的样子吗
        953641bb06b7:@CoderMikeHe 今天上午我试过了,没成功~~可能我比较菜:sweat: ,还有评论后滑倒当前评论的位置,我想按照优酷视频那个写,但是看不明白。。:sob:
        CoderMikeHe:@开水上锅 评论无非是拼接数据罢了
        953641bb06b7:功能一
      • 953641bb06b7:CopyPNGFile /Users/lijie/Library/Developer/Xcode/DerivedData/MHDevelopExample-cgjniftsqgccnearzdpjeifgtvpi/Build/Products/Debug-iphonesimulator/MHDevelopExample.app/cmh_default_buy_goods@3x.png /Users/lijie/XNB/Resource/Architecture/Default/cmh_default_buy_goods@3x.png
        cd /Users/lijie/Desktop/下载在/MHDevelopExample_Objective_C/MHDevelopExample
        export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
        export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
        export SDKROOT=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator11.4.sdk
        export TOOLCHAINS=
        /Applications/Xcode.app/Contents/Developer/usr/bin/copypng -compress -strip-PNG-text /Users/lijie/XNB/Resource/Architecture/Default/cmh_default_buy_goods@3x.png /Users/lijie/Library/Developer/Xcode/DerivedData/MHDevelopExample-cgjniftsqgccnearzdpjeifgtvpi/Build/Products/Debug-iphonesimulator/MHDevelopExample.app/cmh_default_buy_goods@3x.png

        ERROR: Can't find /Users/lijie/XNB/Resource/Architecture/Default/cmh_default_buy_goods@3x.png
        Command /Applications/Xcode.app/Contents/Developer/usr/bin/copypng failed with exit code 1 下载后遇到这样的错误,两种方法都试过了
        CoderMikeHe:@开水上锅 好的,已经修复提交了。
        953641bb06b7:找到了删掉也没用
        953641bb06b7:而且这个cmh_default_buy_goods@3x.png这个图片找不到
      • 魏志军:大神如果评论那一条数据也没有,直接返回cell的高度为零可以吗,还是还有别的地方需要注意,望指教
        CoderMikeHe:@魏志军 一条数据都没有,怎么会去创建cell。
      • 魏志军:你是cell里面又嵌套一个tableView吗
        魏志军:@CoderMikeHe 大神如果评论那一条数据也没有,直接返回cell的高度为零可以吗,还是还有别的地方需要注意,望指教
        CoderMikeHe:@魏志军 https://www.jianshu.com/p/395bac3648a7 这个方案用的是 ` 组头 + Cell + 组尾`的方式做的,灰色部分每一条评论就是一个`Cell`。建议看完文章和看看Demo
      • 魏志军:如果整体部分是个cell的话,那为什么会是一行行的,这是怎么 做的,不理解
      • 魏志军:灰色部分整体是cell吗
      • 魏志军:评论哪如果整体是一个cell的话,那怎么一行行的啊
      • L05T:仔细研读了下,完美用在自己的项目中~
        CoderMikeHe:@L05T 其实并不难..其实很简单。:+1:
      • 899781bdee56:有一个问题,进入朋友圈以后,可以不可以吧评论和点点赞隐藏掉,用户想看朋友圈的评论的时候在显示出来,类似于微博。期待你的回复!!!!
        CoderMikeHe:@newdivide39 当然可以的呀,微博跟微信需求不一样吧。微博那种是通过数字的形式告诉用户,点赞数,评论数,转发数。如果是微博那种样式,直接采用Cell的方式即可,而微信这种采用 `组头 + Cell + 组尾`这种。当然微博实现方式,完全可以自行百度,一大堆。
      • 690a597c0ae8:后端数据库用的什么?
        690a597c0ae8:@CoderMikeHe 多谢回复,最近在把Firebase的project转移到国内的baas,有机会多交流iOS开发
        CoderMikeHe:@David_f31f 那得问后端了,前端只是拿数据来展示罢了。
      • EchooJ:pod install后报出已下错误
        [!] Error installing CHTCollectionViewWaterfallLayout
        [!] /usr/bin/git clone https://github.com/chiahsien/CHTCollectionViewWaterfallLayout.git /var/folders/wp/pfk_75q917dc0rjsrbbj7b4w0000gn/T/d20180403-1060-xcbw1 --template= --single-branch --depth 1 --branch 0.9.6

        Cloning into '/var/folders/wp/pfk_75q917dc0rjsrbbj7b4w0000gn/T/d20180403-1060-xcbw1'...
        error: RPC failed; curl 18 transfer closed with outstanding read data remaining
        fatal: The remote end hung up unexpectedly
        fatal: early EOF
        fatal: unpack-objects failed
        CoderMikeHe:@linlin泠迹 https://blog.csdn.net/drift_axe/article/details/54924359 你参照这个试试。
        EchooJ:@CoderMikeHe 不行啊,我试过了的,操作步骤就是按照你给的readme.md文件描述,到了pod install 后就出现该问题
        CoderMikeHe:@linlin泠迹 重新pod install
      • 82295265a0fd:楼主,这个我自己写了下,但是有个问题,就是段头下面的cell会有个默认的缩进动画。每个整体的section都有,很烦啊,楼主怎么解决的
        CoderMikeHe:@0号故事 哈哈,注意细节哈
        82295265a0fd:解决了,UITableViewStyleGrouped tableview类型。。我傻了
      • itonny:何时分享点 socket,以及长链接的文章啊
      • 大慧慧_2258:cd到项目根目录 执行pod install 还是不行额 请问怎么弄
        CoderMikeHe:@大慧慧_2258 https://pan.baidu.com/s/1skPKNhz 密码:6grn
        CoderMikeHe:@大慧慧_2258 具体是什么错误呢。
      • a9a307e7fb5d:这才是真正的自我完善的路,不只是会用,还能写出来,说明对知识点的梳理和运用已经很娴熟。demo和简述都很清楚,代码质量好高,大牛,膜拜膜拜。
        CoderMikeHe:@一声童谣 好的,必须的。
        a9a307e7fb5d:@CoderMikeHe 评论回复这块,先自己研究研究您的代码,如果有哪里不懂的,还望不惜赐教。 : )
        CoderMikeHe:@一声童谣 哈哈,谢谢,对你有帮助,才是文章意义所在哈。
      • Henrya:楼主也可以看看这个高仿微信朋友圈的demo,用的是SDAutolayout
        https://github.com/gsdios/GSD_WeiXin
        CoderMikeHe:@Henrya 那必须的。
        Henrya:@CoderMikeHe 言之有理,先研究下楼主的写法。开源精神值得称赞!
        CoderMikeHe:@Henrya 嗯嗯 ,已借鉴过,但是在朋友圈这块,其实DSD_WeiXin性能十分不友好,一旦评论数据过多,由于DSD_WeiXin的评论展示是一个简单的View,这样做不到复用,一旦你滚动的时候,这个View就会先删掉所有的子控件,在添加子控件,这是在程序开发要避免的,尽量一口气创建完,然后控制其隐藏显示。DSD_WeiXin主要是想体现SDAutolayout的实用性,突出的重点是自动布局。 所以,针对类似微信这种支持无限评论的话。还是建议使用我用的这个方案。还是根据各自场景,使用不同方案吧。
      • PGOne爱吃饺子:楼主的这个获取富文本是怎么搞的,思路是怎么样的,打印了一下富文本,好乱啊,能不能给个思路
        PGOne爱吃饺子:@CoderMikeHe 问一下,你这里面一个人回复一个人之后,下个回复好像有一个空格,请问空格也是在里面拼接的么
        PGOne爱吃饺子:@CoderMikeHe 好的 谢谢
        CoderMikeHe:@4140d18ee6fc 建议你先看看 YYText 的使用方法。 顺便看看 YYKit的Demo。思路无非就是拼接数据,以及处理富文本的点击事件即可。
      • 兰德耍:pod install

        好多库啊 0.0。已经迫不及待了
        最近也在做类似的项目。希望有帮助
        兰德耍:@CoderMikeHe
        CoderMikeHe:@兰德耍 嗯嗯,主要里面有好几个Demo,可能每个库服务不同的Demo
      • piaodang1234: 你好作者,你这修改了cell的frame,那么如果点击cell左侧的区域如何响应呢?
        CoderMikeHe:@piaodang1234 好的 ,最近较忙。有时间我验证验证哈 。
        piaodang1234:@CoderMikeHe hello 做法有两个:1.在cell中通过hittest来获取point的坐标的x为负值的话,则是cell外面,但是使用hitTest存在问题,当点击第一个section,hitTest执行了6次,往下滑动在点击的话hitTest执行了几十次,在stackoverflow上面没有查到解决方法,所以不推荐
        2.自定义UITableView,在这里面通过touchBegain来判断点击的view和point,当点击的view为HeaderfooterView则忽略,如果点击的View为当前自定义的UITableView,那么修改point的x值,使x位于cell的frame内,然后通过indexPathForRowAtPoint获取到点击的indexPath,通过协议返回到ViewController中
        - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        UITouch *touch = [touches anyObject];
        if ([touch.view isKindOfClass:[self class]]) {
        CGPoint originalLocation = [touch locationInView:self];
        originalLocation.x = CGRectGetWidth(self.frame) / 2.0;//此处需要根据实际情况调整,确保x的值在cell的frame内
        NSIndexPath *indexPath = [self indexPathForRowAtPoint:originalLocation];
        if (self.ksDelegate && [self.ksDelegate respondsToSelector:@selector(tableView:clickOutOfCellAtIndexPath:)]) {
        [self.ksDelegate tableView:self clickOutOfCellAtIndexPath:indexPath];
        }
        }
        [super touchesBegan:touches withEvent:event];
        }
        推荐使用第二种方法,感觉比较完美
        CoderMikeHe:@piaodang1234 嗯嗯,我很早发现了这个问题,例如优酷视频的评论点击cell左侧是可以响应的,个人觉得严禁的做法,是在cell左侧的区域,添加一个view,利用hitTest方法来处理。有时间会完善一下。
        但是,我们程序猿也得强硬一点,用户点击左侧没响应,会乖乖去点击cell。
      • 脚下的斑马线:作者你好,组头的高度是自己算的么?
        CoderMikeHe:@城北小客 对的,都是自己算,没有使用autoLayout。用代码换性能吧。
      • 没有名字就是我的名字:欢迎各路大神没事来这群里面吹吹技术的牛逼 聊聊技术的问题,超过100人改为付费群,且行且珍惜号:614194935
      • 嘿丶花姑娘:为什么回复列表不用YYText做呢,用\n换行 在设置一个行间距,和magin,一样可以实现这个效果,而且效果更好,滚动更流畅
        CoderMikeHe:@嘿花姑娘 如果用一个YYText代替整个回复列表 ,那是非常不妥,想想如果有100条回复数据,如果你一直去拼接数据,工程量太大;其次,事件处理将变得复查;
      • 7a45b5f6be54:代码写的太规范了,看的眼睛发亮,:sob:我都不想看自己写的代码了。
        CoderMikeHe:@相_c0f8 利人利己,何乐不为。
      • Aacmr:楼主加上评论折叠功能呀, 还有那个单行折叠。我已经做过一种,想看看你的比较好的实现方式。
        CoderMikeHe:展开评论这个功能,就类似于扣扣的分组功能,利用组头+cell非常容易实现,只要在MHTopic增加一个属性来判断是否展开评论数据,如果是隐藏评论数据,那门就在tableview的返回多少个cell的代理中,return 0即可反之如果显示评论数据,那门就正常返回即可。
        Aacmr:@CoderMikeHe 展开全文是一种,还有一种展开评论,不知道是获取行高合理,还是获取评论的个数合理。
        CoderMikeHe:@Aacmr 请问是类似微信的 展开全文 那种吗??
      • EdenChow:良心大牛。
        CoderMikeHe:@Eden_chow 没有,只是自己平常开发遇到的坑而已,希望对你有所帮助
      • 6b985f46497d:可以呀 小伙子
      • 酒红色T恤:良心好文,赞一个!
        CoderMikeHe:@低碳环保_好好学习 谢谢,互相学习。
      • 搜捕儿:博主优酷的demo用的是方法一还是方法二
        CoderMikeHe:@搜捕儿 用的是方法一。
      • 就是一个春天的花朵:良心:+1: Demo写的这么清晰
        就是一个春天的花朵:@CoderMikeHe 弱弱的问一句,这个是不是就是MVVM的用法了,用两层model
        就是一个春天的花朵:@CoderMikeHe 好的,我算是学艺不精了,之前一直只是把model简单的用来存储数据模型,没想到还能有这么好的用法.还得继续学习呀:smile:
        CoderMikeHe:@ozill 谢谢,我会继续努力的。喜欢的话请在 github 上star一下,有时间我会把我平常遇到的问题全部分享出来。一起学习,共同进步。
      • c2fffd2b0090:有demo吗?给个链接啊
        CoderMikeHe:@王丹阳1995 前提你得支持cococapods,在终端cd到项目的根目录,执行pod install即可,我github上 不是有说明的么?
        8fe8946fa366:请问怎么pod install@CoderMikeHe
        CoderMikeHe:@打了鸡血 我刚更新本文文章,文章末尾有链接地址,第一次使用Demo需要pod install。若文章对你有帮助,还请给我好评❤️。谢谢

      本文标题:iOS 实现微信朋友圈评论回复功能(一)

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