「OC」知乎日报——第三周

「OC」知乎日报——第三周

本周总结

本周完成了知乎日报的评论区功能,实现了用UITextView和富文本字符串结合实现了评论区的展开和回收,学会使用Masnory实现评论区的自定义行高,修改了原先详情页的点赞收藏状态的逻辑问题和滚动视图初始位置不符的问题。

请添加图片描述

评论区展开

关于评论区展开大致有三个要点,首先是判断这个评论是否是否有回复,第二个就是判断textView之中文字的行数,最后一个就是当前textView的文字需要展开的时候进行的相关操作

长评和短评的Model

#import <Foundation/Foundation.h>
#import "YYModel.h"
@class ReplyTo;
NS_ASSUME_NONNULL_BEGIN

@interface ShortComment : NSObject<YYModel>
@property (nonatomic, copy) NSString *author;
@property (nonatomic, copy) NSString *content;
@property (nonatomic, copy) NSString *avatar;
@property (nonatomic, assign) NSInteger commentId;
@property (nonatomic, assign) NSInteger likes;
@property (nonatomic, assign) NSTimeInterval time;
@property (nonatomic, strong) ReplyTo *replyTo;
@property (nonatomic, assign) BOOL isLike;

@property (nonatomic, assign) BOOL isExpanded;
@end

#import <Foundation/Foundation.h>
#import "YYModel.h"
@class ReplyTo;
NS_ASSUME_NONNULL_BEGIN

@interface LongComment : NSObject<YYModel>
@property (nonatomic, copy) NSString *author;
@property (nonatomic, copy) NSString *content;
@property (nonatomic, copy) NSString *avatar;
@property (nonatomic, assign) NSInteger commentId;
@property (nonatomic, assign) NSInteger likes;
@property (nonatomic, assign) NSTimeInterval time;
@property (nonatomic, strong) ReplyTo *replyTo;
@property (nonatomic, assign) BOOL isExpanded;
@property (nonatomic, assign) BOOL isLike;

@end

NS_ASSUME_NONNULL_END

这些就是对应的短评和长评的Model,接下来还需要给我们写一个相关的网络申请在我们的NetworkManager之中,以下是方法实现

- (void)fetchLongCommentsForNewsID:(NSString *)ID completion:(void (^)(NSMutableArray<LongComment *> *comments, NSError *error))completion {
    NSString *urlString = [NSString stringWithFormat:@"https://news-at.zhihu.com/api/4/story/%@/long-comments", ID];
    [self GET:urlString parameters:nil headers:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        if (completion) {
            completion(comments, nil);
        }
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
       
        if (completion) {
            completion(nil, error);
        }
    }];
}


- (void)fetchShortCommentsForNewsID:(NSString *)ID completion:(void (^)(NSMutableArray<LongComment *> *comments, NSError *error))completion {
    NSString *urlString = [NSString stringWithFormat:@"https://news-at.zhihu.com/api/4/story/%@/short-comments", ID];
    [self GET:urlString parameters:nil headers:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) 
        if (completion) {
            completion(comments, nil);
        }
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        if (completion) {
            completion(nil, error);
        }
    }];
}

我们申请的数据返回的是关于长评和短评的数组,所以tableView的相关信息源就是根据这两个数组来的,大致也没什么难点,就以这个逻辑判断为主就行

if (indexPath.section == 0 && self.longComments.count > 0) {
  
    } else if ((indexPath.section == 1 || (indexPath.section == 0 && self.longComments.count == 0)) && self.shortComments.count > 0) {
       
    }

cell的内容

关于cell的布局在这里就不给出了,就直接给出如何实现评论区展开的代码,首先是如何判断textView之中的内容是否超过三行,我们使用sizeThatFits给出对应限制的宽度,就能得到文本框的高度,因为font之中也有关于每一行行高的属性,我们访问font.lineHeight就可以得到,将行数向下取整,就能算出对应的文本行数

 CGSize size = [self.replyTextView sizeThatFits:CGSizeMake(317, CGFLOAT_MAX)];
 CGFloat lineCount = floor(size.height / self.replyTextView.font.lineHeight);

如果这个文本框内容超过三行,那么就使用富文本将内容包装,以下是相关逻辑

if (lineCount > 3 && !self.isExpanded) {
        NSString *Text = [text substringToIndex:[self truncatedTextIndexForText:text]];
        NSMutableAttributedString *expandableText = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@...展开", Text]];
        [expandableText addAttribute:NSForegroundColorAttributeName value:[UIColor grayColor] range:NSMakeRange(0, Text.length)];
        [expandableText addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:14] range:NSMakeRange(0, Text.length)];
        [expandableText addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:NSMakeRange(Text.length, 5)];
        [expandableText addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:14] range:NSMakeRange(Text.length, 5)];
        self.replyTextView.attributedText = expandableText;
    } else if (lineCount > 3 && self.isExpanded) {
        NSMutableAttributedString *fullText = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@ 收起", text]];
        [fullText addAttribute:NSForegroundColorAttributeName value:[UIColor grayColor] range:NSMakeRange(0, fullText.length)];
        [fullText addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:14] range:NSMakeRange(0, fullText.length)];
        [fullText addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:NSMakeRange(fullText.length - 2, 2)];
        [fullText addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:14] range:NSMakeRange(fullText.length - 2, 2)];
        self.replyTextView.attributedText = fullText;
    }

这个富文本字符串的创建,还是比较简单的,这里就不过多赘述,下面介绍的才是这个展开的主体,truncatedTextIndexForText也就是这个函数,这个函数是用于获取当文本框处于未展开的状态时的被截断的字符串

- (NSUInteger)truncatedTextIndexForText:(NSString *)text {
    UIFont *font = self.replyTextView.font ?: [UIFont systemFontOfSize:14];
    CGFloat maxHeight = font.lineHeight * 3;

    // 容纳 "...展开" 的宽度预估(根据字体和文字大小)
    NSString *expandSuffix = @"...展开";
    CGSize expandSize = [expandSuffix boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)
                                                   options:NSStringDrawingUsesLineFragmentOrigin
                                                attributes:@{NSFontAttributeName: font}
                                                   context:nil].size;
    CGFloat reservedWidth = expandSize.width;

    NSString *substring = text;
    for (NSInteger i = text.length - 1; i >= 0; i--) {
        substring = [text substringToIndex:i];

        // 计算剩余文字的显示大小
        CGSize substringSize = [substring boundingRectWithSize:CGSizeMake(317 - reservedWidth, CGFLOAT_MAX)
                                                       options:NSStringDrawingUsesLineFragmentOrigin
                                                    attributes:@{NSFontAttributeName: font}
                                                       context:nil].size;

        // 如果高度小于等于最大高度,返回当前索引
        if (substringSize.height <= maxHeight) {
            return i;
        }
    }
    return text.length;
}

我们来解析这个函数,首先是被截断字符串的后缀,...展开我们用boundingRectWithSize的方法计算这个后缀的长度,然后就是for循环之中的内容,我们每次都将字符串的长度减去一个单位,检验宽度是否会大于三行,若检验到当前的字符串满足不高于三行的条件,就将下标i进行返回。

自适应行高

我们还需要实现评论区自适应行高,首先在tableView之中设置属性,使得tableView

    self.tableView.rowHeight = UITableViewAutomaticDimension;

然后在cell的布局之中使用Masonry这个库进行自定义行高的计算,就是我们在布局textView的时候,上下两边都直接和cell的contentView进行约束

 [self.commentText mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.usernameLabel.mas_bottom);
        make.left.equalTo(self.usernameLabel);
        make.right.equalTo(self.contentView).offset(-20);
    }];
    
    [self.replyTextView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.commentText.mas_bottom);
        make.left.equalTo(self.commentText.mas_left);
        make.right.equalTo(self.contentView).offset(-20);
        make.bottom.equalTo(self.contentView).offset(-15);
    }];

这样就可以实现相关的cell自适应行高。

下周计划

其实本周看来没有写很多东西,但是确实在实现评论区展开的功能时,耗费了很多时间去学习一些的东西,但从结果体现不出耗费的时间的意义,很多时候扣细节也没将细节扣好,比如无法控制展开按钮一定跟在第三行的最后面,遇到这些问题可能也算得上是写项目的必由之路了。

下周计划就是对tableViewCell进行相关的优化,实现用字典存储相关的行高信息,掌握FMDB的用法,用本地化的方式进行收藏以及点赞信息的存储。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值