知乎日报——第二周
知乎日报——第二周
文章目录
- 知乎日报——第二周
- 本周总结
- Model
- View
- Controller
- 无限右滑
- 预加载
- 总结
本周总结
本周主要完成了知乎日报的详情页面,实现了无限右滑和动态申请详情页的内容,实现了预先加载。以及通过位置获取相应的格外信息,使用字典进行缓存的相关操作。大致内容如下
Model
首先是相关的model建立,显示相关的新闻的详细信息以及点赞数量,我是使用了一个extraInfo
和一个DetailNews
的类
DetailNews
:
这个类主要存储的是对应新闻ID的详细信息
#import <Foundation/Foundation.h>
#import "YYModel.h"
NS_ASSUME_NONNULL_BEGIN
@interface DetailNews : NSObject<YYModel>
@property (nonatomic, copy) NSString *title; // 标题
@property (nonatomic, copy) NSString *body; // 正文内容
@property (nonatomic, copy) NSString *imageURL; // 主图链接
@property (nonatomic, copy) NSString *newsID; // 分享链接
@property (nonatomic, copy) NSString *originURL; // 原文链接
@property (nonatomic, copy) NSArray *images; // 其他图像链接
@property (nonatomic, copy) NSString *imageHue; // 图像色调
@end
extraInfo
:
这个类主要是用于存储当前页面的评论和点赞的相关信息
#import <Foundation/Foundation.h>
#import "YYModel.h"
NS_ASSUME_NONNULL_BEGIN
@interface extraInfo : NSObject<YYModel,NSCopying>
@property (nonatomic, copy) NSString *newsID;
@property (nonatomic, copy) NSString *newsTopic;
@property (nonatomic, copy) NSString *newsIamge;
@property (nonatomic, assign) NSInteger longComments;
@property (nonatomic, assign) NSInteger popularity;
@property (nonatomic, assign) NSInteger shortComments;
@property (nonatomic, assign) NSInteger comments;
@property (nonatomic, assign) BOOL isLiked;
@property (nonatomic, assign) BOOL isFavorited;
@end
NS_ASSUME_NONNULL_END
两个BOOL类型的属性使用来存储相关的点赞和收藏状态的,当滚动视图滑动到对应的视图的时候进行调用可以展示点赞状态
View
控制器的主体有分为展示内容的ScrollView和下层展示对应新闻数据的BottomView
BottomView
的具体内容如下
#import <UIKit/UIKit.h>
#import "BottomViewDelegate.h"
@class extraInfo;
NS_ASSUME_NONNULL_BEGIN
@interface BottomView : UIView
@property (strong, nonatomic) UIButton *back;
@property (strong, nonatomic) UIButton *like;
@property (strong, nonatomic) UIButton *comment;
@property (strong, nonatomic) UIButton *star;
@property (strong, nonatomic) UIStackView *stackView;
@property (strong, nonatomic) extraInfo *info;
@property (strong, nonatomic) NSMutableArray *isSelected;
@property (nonatomic, weak) id<BottomViewDelegate> delegate;
@end
NS_ASSUME_NONNULL_END
#import "BottomView.h"
#import "extraInfo.h"
#import "BottomViewDelegate.h"
#import "Masonry.h"
#import "DBTool.h"
@implementation BottomView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setupView];
}
return self;
}
//当获取到当前位置的extraInfo就进行布局
-(void)setInfo:(extraInfo *)info {
_info = info;
NSLog(@"%ld",info.popularity);
[self.like setTitle:[NSString stringWithFormat:@"%ld",info.popularity] forState:UIControlStateNormal];
[self.like setSelected:info.isLiked];
[self.star setSelected:info.isFavorited];
[self.like setTitle:[NSString stringWithFormat:@"%ld",info.popularity + 1] forState:UIControlStateSelected];
[self.comment setTitle:[NSString stringWithFormat:@"%ld",info.comments] forState:UIControlStateNormal] ;
}
-(void)setupView {
self.backgroundColor = [UIColor colorWithRed:246.0 / 256.0 green:246.0 / 256.0 blue:246.0 / 256.0 alpha:1];
self.stackView = [[UIStackView alloc] init];
self.stackView.axis = UILayoutConstraintAxisHorizontal;
self.stackView.distribution = UIStackViewDistributionEqualSpacing;
self.stackView.alignment = UIStackViewAlignmentLeading;
[self addSubview: self.stackView];
[self.stackView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self).offset(20);
make.right.equalTo(self).offset(-35);
make.top.equalTo(self).offset(15);
make.height.equalTo(@30);
}];
self.like = [UIButton buttonWithType:UIButtonTypeCustom];
[self.like setImage:[UIImage imageNamed:@"dianzan-2.png"] forState:UIControlStateNormal];
[self.like setImage:[UIImage imageNamed:@"dianzan.png"] forState:UIControlStateSelected];
[self.like setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
self.like.titleEdgeInsets = UIEdgeInsetsMake(-12, -12, 0, 0);
self.like.titleLabel.font = [UIFont systemFontOfSize:12];
[self.like setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
[self.like addTarget:self action:@selector(clicklike:) forControlEvents:UIControlEventTouchUpInside];
self.star = [UIButton buttonWithType:UIButtonTypeCustom];
[self.star setImage:[UIImage imageNamed:@"shoucang.png"] forState:UIControlStateNormal];
[self.star setImage:[UIImage imageNamed:@"shoucang-2.png"] forState:UIControlStateSelected];
[self.star addTarget:self action:@selector(clickStar:) forControlEvents:UIControlEventTouchUpInside];
self.comment = [UIButton buttonWithType:UIButtonTypeCustom];
[self.comment setImage:[UIImage imageNamed:@"pinglun.jpg"] forState:UIControlStateNormal];
self.comment.titleEdgeInsets = UIEdgeInsetsMake(-15, 0, 0, 0);
self.comment.titleLabel.font = [UIFont systemFontOfSize:12];
[self.comment setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
self.back = [UIButton buttonWithType:UIButtonTypeCustom];
[self.back setImage:[UIImage imageNamed:@"zuojiantou.png"] forState:UIControlStateNormal];
[self.stackView addArrangedSubview:self.back];
[self.stackView addArrangedSubview:self.comment];
[self.stackView addArrangedSubview:self.like];
[self.stackView addArrangedSubview:self.star];
}
-(void)clickStar:(UIButton *)sender {
sender.selected = !sender.selected;
self.info.isFavorited = sender.selected;
}
-(void)clicklike:(UIButton *)sender {
sender.selected = !sender.selected;
self.info.isLiked = sender.selected;
}
@end
对应展示的滚动视图即为正常的滚动视图,其显示内容为webView,我们将申请获得的URL内容,这是webView的相关操作
@implementation DetailViewController
-(void)setDetailNews:(DetailNews *)detailNews {
_detailNews = detailNews;
NSURL *url = [NSURL URLWithString:self.detailNews.originURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:request];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.webView = [[WKWebView alloc] init];
self.webView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
[self.view addSubview:self.webView];
[self.webView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.view);
make.right.equalTo(self.view);
make.bottom.equalTo(self.view).offset(-120);
make.top.equalTo(self.view);
}];
}
@end
Controller
无限右滑
实现无限右滑,首先我们需要明确我们的操作,当我们向右滑的时候就会出现上一篇新闻,当这一天的新闻到最后一篇之后就需要到下一天的第一篇,这个过程我一开始想使用的当我们新闻即将滑动至申请的data数据末尾,大概是最后的两到三页时,进行网络申请,并且更新滚动视图,添加contentView的宽度,但是后面发现,如果我滑动的速度很快直接超过了申请网络请求的速度的话,数组就会直接越姐。
于是我更改了我的思路,在一开始将滚动视图布局的时候,大小就是当前控制器的持有的data元素大小,当我们右滑到边界的时候,直接添加五个contentView,在控制器之中的scrollViewDidEndDecelerating
的协议方法之中添加一个定时器,只有暂停在当前页面0.5秒才开始进行相关的网络申请,然后我使用一个currentPage的属性来保存当前滑动的位置,右滑一次就加1,左滑一次就减1,再根据这个找到对应的元素。如果当前滚动视图的当前页对应元素无法被访问,即数组越界,那么我们就会进行网络申请,然后再申请过后再递归进行嵌套,判断是否越界,不越界就将信息显示在滚动视图当中
加入滚动视图的定时器和相关函数
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
CGFloat pageWidth = scrollView.frame.size.width;
NSInteger newPage = (scrollView.contentOffset.x + pageWidth * 0.5) / pageWidth;
if (newPage != self.currentPage) {
self.currentPage = newPage;
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(loadCurrentPage) object:nil];
[self performSelector:@selector(loadCurrentPage) withObject:nil afterDelay:0.5];
}
if (scrollView.contentOffset.x + pageWidth * 3 >= scrollView.contentSize.width - pageWidth) {
self.total += 5;
self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width * (self.total), 0);
}
}
根据相关位置的index进行加载
- (void)loadStoryAtIndex:(NSInteger)index {
NSInteger currentIndex = index;
NSInteger sectionIndex = 0;
for (NSArray *sectionArray in self.data) {
if (currentIndex < sectionArray.count) {
break;
} else {
currentIndex -= sectionArray.count;
sectionIndex++;
}
}
if (sectionIndex >= self.data.count) {
[self requestMoreDataAndRetryWithIndex:index];
return;
}
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:currentIndex inSection:sectionIndex];
Story *story = self.data[sectionIndex][currentIndex];
if (!self.loadedDetailViews[indexPath]) {
DetailViewController *detailVC = [[DetailViewController alloc] init];
CGFloat xOffset = self.view.frame.size.width * index;
detailVC.view.frame = CGRectMake(xOffset, 0, self.view.frame.size.width, self.scrollView.frame.size.height);
[self addChildViewController:detailVC];
[self.scrollView addSubview:detailVC.view];
self.loadedDetailViews[indexPath] = detailVC;
[[NetworkManager sharedManager] fetchNewsDetail:story.id completion:^(DetailNews *news, NSError *error) {
if (!error && news) {
detailVC.detailNews = news;
}
}];
}
if (index == self.currentPage) {
extraInfo *cachedInfo = [[DBTool sharedManager] fetchInfoForNewsID:story.id];
if (cachedInfo) {
[self.bottomView.like setSelected:cachedInfo.isLiked];
[self.bottomView.star setSelected:cachedInfo.isFavorited];
} else {
cachedInfo = self.extraInfoCache[story.id];
if (!cachedInfo) {
cachedInfo = [[extraInfo alloc] init];
cachedInfo.newsID = story.id;
cachedInfo.newsTopic = story.title;
cachedInfo.newsIamge = [story.images firstObject];
cachedInfo.isLiked = NO;
cachedInfo.isFavorited = NO;
}
}
[[NetworkManager sharedManager] fetchNewsExtraInfo:story.id completion:^(extraInfo *info, NSError *error) {
if (!error && info) {
cachedInfo.comments = info.comments;
cachedInfo.popularity = info.popularity;
[self.bottomView setInfo:cachedInfo];
[self.bottomView.like setSelected:cachedInfo.isLiked];
[self.bottomView.star setSelected:cachedInfo.isFavorited];
self.extraInfoCache[story.id] = cachedInfo;
[[DBTool sharedManager] insertInfo:cachedInfo];
} else {
NSLog(@"Error fetching extra info: %@", error);
}
}];
}
}
这里,我还使用一个字典作为detailNews的缓存,每当我进行一次网络请求,我就会将当前新闻的ID作为键,申请到的detailNews为对应的值,当我滑动到当前页面时,先访问字典是否有对应的值,若有则直接使用,若是没有再进行网络申请,最后再将申请到的相关内容存入字典当中
预加载
预加载想要实现的就是当我在当前页的时候会向左右两边各自先加载一页,有利于用户进行操作,避免了网络请求的相关时间,
- (void)loadCurrentPage {
[self loadStoryAtIndex:self.currentPage];
if (self.currentPage > 0) {
[self loadStoryAtIndex:self.currentPage - 1];
}
[self loadStoryAtIndex:self.currentPage + 1];
}
在我们加载的函数我们传进了一个参数下标index,根据这个下标我们就可以进行对应页面的加载操作,预加载也很容易就能完成只要在翻页的时候进行loadCurrentPage
的运行,改变index即可。
总结
本周完成的无限右滑以及相关的预加载等操作,确实花费了不少的时间,下个星期的目标就是完成,评论区的相关内容,用富文本以及textView完成展开评论区的相关内容。