知乎日报前三周总结

目录

前言

首页

网络请求

上拉加载

详情页

加载WebView

左右滑动

主页与详情页同步更新

总结


前言

在这几周进行了知乎日报的仿写,这篇博客来总结一下前三周仿写的内容

首页

首页的界面如图所示,其实就是一个导航栏和一个数据视图组成的,这个数据视图的第一个cell中存放了一个无限轮播图,UI的仿写难度并不大,主要麻烦的地方在于对网络请求接口的使用和数据的处理。 

网络请求

这里网络请求通过GCD来解决异步导致的数据未请求完成就加载UI的问题,

// 进行网络请求dispatch_group_enter(group);[manager NetWorkGetWithCompletion:^(NSDictionary * _Nonnull userData, NSError * _Nonnull error) {if (!error) {self.homeModel = [HomeModel yy_modelWithDictionary:userData];NSArray *array = self.homeModel.stories;for (SubModel *subModel in array) {[self.allArray addObject:[subModel yy_modelToJSONObject]];}NSLog(@"cishi:%d",[self.allArray count]);}dispatch_group_leave(group); // 这个需要在所有请求的最后调用}];// 确保所有请求和图片加载完成后才更新 UIdispatch_group_notify(group, dispatch_get_main_queue(), ^{// 在这里进行视图更新self.homeView = [[HomeView alloc] initWithFrame:self.view.frame];self.homeView.tableView.delegate = self;self.homeView.tableView.dataSource = self;self.homeView.tableView.userInteractionEnabled = YES;[self.homeView.tableView reloadData];[self.view addSubview:self.homeView];});

这里就是把本来异步进行的网络请求,放进了一个串行队列中,当网络请求完成后,再通知视图更新UI。关于GCD的知识,笔者暂时只知道如何使用这一方法来解决异步的问题,后续学习了会再发博客进行补充。

这里申请到的文章图片只是图片的url,笔者使用了第三方库SDWebImage来加载图片,一开始我的思路是先将数据申请和图片加载都完成,并将数据和图片保存在数组中,加载UI时直接访问数组中对应元素,但是这样就会出现异步导致数组为空的问题,因为需要同时进行两个后台请求,当时无法解决,后来意识到在最外层的请求完成后再退出队列就好了。当时笔者改变了思路,将申请到的model传给view层,当view层需要访问图片时,再获取数组中的url并调用方法加载,这样既不会有问题了。

上拉加载

在首页还有一个比较重要的功能,也是笔者最新学到的,就是上拉加载的写法。

实现tableView中的下拉上拉刷新效果及相关基础概念

参考这篇文章,笔者在首页中实现了上拉加载的效果,简单来说,就是在tableView的下方创建一个视图,当tableView偏移量大于某一个值时,就调用方法获取新的数据,再将新的数据更新到UI上,并更新tableView下方的视图。主要这里获取新的数据时需要更新url,笔者使用了NSDate来处理日期。

- (void)reloadDate {NSDateFormatter *formatter = [[NSDateFormatter alloc] init];[formatter setDateFormat:@"yyyyMMdd"];NSString* dateString = [formatter stringFromDate:self.date];NSLog(@"%@", dateString);dispatch_group_t group = dispatch_group_create();Manager *manager = [Manager shareManeger];// 进行网络请求dispatch_group_enter(group);[manager NetWorkGetBefore: dateString WithCompletion:^(NSDictionary * _Nonnull userData, NSError * _Nonnull error) {if (!error) {BeforeModel* beforeModel = [[BeforeModel alloc] init];beforeModel = [BeforeModel yy_modelWithDictionary:userData];[self.beforeArray addObject:beforeModel];NSArray *array = beforeModel.stories;for (BeforeSubModel *beforeSubModel in array) {[self.allArray addObject:[beforeSubModel yy_modelToJSONObject]];}NSLog(@"%@",self.allArray);}dispatch_group_leave(group); // 这个需要在所有请求的最后调用}];// 确保所有请求和图片加载完成后才更新 UIdispatch_group_notify(group, dispatch_get_main_queue(), ^{// 在这里进行视图更新[self.homeView.tableView reloadData];self.homeView.footer.frame = CGRectMake(0, self.homeView.tableView.contentSize.height + 300, 394, 50);[self.homeView.footerLabel setText:@"下拉可以刷新"];self.homeView.footerRefreshing = NO;NSCalendar *calendar = [NSCalendar currentCalendar];NSDateComponents *oneDayBeforeComponents = [calendar components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:self.date];oneDayBeforeComponents.day -= 1;if (oneDayBeforeComponents.day < 1) {NSRange dayRange = [calendar rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:self.date];oneDayBeforeComponents.day = dayRange.length;oneDayBeforeComponents.month -= 1;if (oneDayBeforeComponents.month < 1) {oneDayBeforeComponents.month = 12;oneDayBeforeComponents.year -= 1;NSRange newDayRange = [calendar rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:self.date];oneDayBeforeComponents.day = newDayRange.length;}}self.date = [calendar dateFromComponents:oneDayBeforeComponents];});
}
- (void)dealFooter {if (self.homeView.footerRefreshing) {return;}CGFloat footerOffset = self.homeView.tableView.contentSize.height - self.homeView.tableView.frame.size.height + self.homeView.footerLabel.frame.size.height + 100;
//    NSLog(@"%d %d %d", self.homeView.tableView.contentSize.height, self.homeView.tableView.frame.size.height, self.homeView.footerLabel.frame.size.height);if (self.homeView.tableView.contentOffset.y >= footerOffset) {[self footerBeginRefreshing];}
}- (void)footerBeginRefreshing {if (self.homeView.footerRefreshing) {return;}[self.homeView.footerLabel setText:@"正在刷新数据"];self.homeView.footerRefreshing = YES;
//    [UIView animateWithDuration:0.25 animations:^{
//        UIEdgeInsets inset = self.homeView.tableView.contentInset;
//        inset.top += self.homeView.footerLabel.bounds.size.height;
//        self.homeView.tableView.contentInset = inset;
//    }];[self reloadDate];
}

详情页

加载WebView

详情页这里,webView的url可以通过接口获取到,需要调用方法将webView加载出来,笔者这里在使用webView前,加入了"WKWebView+AFNetworking.h"这个头文件。

NSURL *urlWeb = [NSURL URLWithString:self.allArray[self.page - 1][@"url"]];
NSURLRequest *webRequest = [[NSURLRequest alloc] initWithURL:urlWeb];
[self.wkWebView loadRequest:webRequest];
[self.view addSubview: self.wkWebView];

这样就可以加载出webView

左右滑动

详情页的一个比较重要的功能就是要实现左右滑动,这里也是笔者认为详情页面最复杂的地方。笔者的思路是计算每一个cell的位置,当点击某个cell时,将位置往controller和view层传值,并根据cell的位置来决定webView的位置和拿来加载webView的url在数组中的位置。每当滑动视图滑动到每一天的最后一页时,就加载后一天的数据,每滑动一页,就加载当前页的视图。

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {if (scrollView.contentOffset.x <= 0) {// 滚动到最左端,禁止继续向左滑动scrollView.contentOffset = CGPointMake(0, 0);}NSInteger currentPage = (scrollView.contentOffset.x / [UIScreen mainScreen].bounds.size.width) + 1;self.newpage= [NSNumber numberWithInteger:currentPage + 5];// 当滚动视图向右滚动且快接近画布右边缘时,触发加载数据的操作if (scrollView.contentOffset.x >  ([self.allArray count] * [UIScreen mainScreen].bounds.size.width  - [UIScreen mainScreen].bounds.size.width * 1.5) && self.isLoadingMoreData == NO) {self.isLoadingMoreData = YES;[self loadMoreData:(NSInteger)currentPage + 1];}if (currentPage != (self.webview.page) && isLoadingWebView == NO && self.isLoadingMoreData == NO && ![self.pageSet containsObject:self.newpage]) {NSLog(@"yin:%d %d %d",self.webview.page ,currentPage, [self.allArray count]);isLoadingWebView = YES;self.webview.page = currentPage;[self.pageSet addObject:self.newpage];[[NSNotificationCenter defaultCenter] postNotificationName:@"newPage" object:nil userInfo:nil];}
}

除此之外,还要实时更新下方工具栏中的点赞和评论数量,笔者这里不保存点赞和评论的数量,每一次滑动到一个新的视图,就重新加载一次点赞数和评论数。

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {NSInteger currentPage = (scrollView.contentOffset.x / [UIScreen mainScreen].bounds.size.width) + 1;if(isLoadingWebView == NO && _isLoadingMoreData == NO) {dispatch_group_t group = dispatch_group_create();Manager *manager = [Manager shareManeger];// 进行网络请求dispatch_group_enter(group);[manager NetWorkGetFor:self.allArray[currentPage][@"id"]WithCompletion:^(NSDictionary * _Nonnull userData, NSError * _Nonnull error) {if (!error) {ExtraModel* extraModel = [ExtraModel yy_modelWithDictionary:userData];self.webview.likeLabel.text = [NSString stringWithFormat:@"%d",extraModel.popularity];self.webview.commentLabel.text = [NSString stringWithFormat:@"%d",extraModel.comments];}dispatch_group_leave(group);}];dispatch_group_notify(group, dispatch_get_main_queue(), ^{});}
}

主页与详情页同步更新

这里笔者通过两个数组来实现同步更新,一个存放所有文章数据的字典,一个存放每天数据的字典,在从主页点击详情页时,直接通过属性传值,而在详情页加载好数据后,就只能通知传值并调用方法更新tableView和上拉加载的底部视图就好

- (void)loadMoreData:(NSInteger)currentPage {NSDateFormatter *formatter = [[NSDateFormatter alloc] init];[formatter setDateFormat:@"yyyyMMdd"];NSString* dateString = [formatter stringFromDate:self.date];NSLog(@"%@", dateString);NSMutableDictionary *updateDictionary = [[NSMutableDictionary alloc] init];dispatch_group_t group = dispatch_group_create();Manager *manager = [Manager shareManeger];// 进行网络请求dispatch_group_enter(group);[manager NetWorkGetBefore: dateString WithCompletion:^(NSDictionary * _Nonnull userData, NSError * _Nonnull error) {if (!error) {BeforeModel* beforeModel = [[BeforeModel alloc] init];beforeModel = [BeforeModel yy_modelWithDictionary:userData];NSArray *array = beforeModel.stories;for (BeforeSubModel *beforeSubModel in array) {[self.allArray addObject:[beforeSubModel yy_modelToJSONObject]];}[updateDictionary setValue:beforeModel forKey:@"beforeModel"];[updateDictionary setValue:self.allArray forKey:@"allArray"];}dispatch_group_leave(group); // 这个需要在所有请求的最后调用}];// 确保所有请求和图片加载完成后才更新 UIdispatch_group_notify(group, dispatch_get_main_queue(), ^{// 在这里进行视图更新NSCalendar *calendar = [NSCalendar currentCalendar];NSDateComponents *oneDayBeforeComponents = [calendar components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:self.date];oneDayBeforeComponents.day -= 1;if (oneDayBeforeComponents.day < 1) {NSRange dayRange = [calendar rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:self.date];oneDayBeforeComponents.day = dayRange.length;oneDayBeforeComponents.month -= 1;if (oneDayBeforeComponents.month < 1) {oneDayBeforeComponents.month = 12;oneDayBeforeComponents.year -= 1;NSRange newDayRange = [calendar rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:self.date];oneDayBeforeComponents.day = newDayRange.length;}}self.date = [calendar dateFromComponents:oneDayBeforeComponents];[updateDictionary setValue:self.date forKey:@"date"];[[NSNotificationCenter defaultCenter] postNotificationName:@"update" object:nil userInfo:updateDictionary];[[NSNotificationCenter defaultCenter] postNotificationName:@"layoutNewScrollView" object:nil userInfo:nil];self.isLoadingMoreData = NO; // 重置加载标志if (currentPage != (self.webview.page) && isLoadingWebView == NO && self.isLoadingMoreData == NO && ![self.pageSet containsObject:self.newpage]) {isLoadingWebView = YES;self.webview.page = currentPage;[self.pageSet addObject:self.newpage];[[NSNotificationCenter defaultCenter] postNotificationName:@"newPage" object:nil userInfo:nil];}});
}

这里笔者通知传值用的字典dictionary初始化和赋值是在不同的区域进行的,因为这个dictionary中不仅要保存新申请到的数据,还要保存更新后的日期,所以初始化必须在让dictio全区可以访问的位置, 赋值一个在获取完数据之后,另一个在日期更新之后。

总结

后续的评论区和收藏中心两个界面,笔者已经完成了评论区的数据请求,后续学习UItextView来对界面布局,收藏中心需要调用第三方库,笔者也将在后续完成

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/11395.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

小白快速上手 labelimg:新手图像标注详解教程

前言 本教程主要面向初次使用 labelimg 的新手&#xff0c;详细介绍了如何在 Windows 上通过 Anaconda 创建和配置环境&#xff0c;并使用 labelimg 进行图像标注。 1. 准备工作 在开始本教程之前&#xff0c;确保已经安装了 Anaconda。可以参考我之前的教程了解 Anaconda 的…

【算法】【优选算法】二分查找算法(上)

目录 一、二分查找简介1.1 朴素二分模板1.2 查找区间左端点模版1.3 查找区间右端点模版 二、leetcode 704.⼆分查找2.1 二分查找2.2 暴力枚举 三、Leetcode 34.在排序数组中查找元素的第⼀个和最后⼀个位置3.1 二分查找3.2 暴力枚举 四、35.搜索插⼊位置4.1 二分查找4.2 暴力枚…

自己构建ARM平台DM8镜像

&#xff1f;&#xff1f;&#xff1f; 为什么不使用官方提供的docker版本&#xff0c;测试有问题&#xff0c;分析函数不能使用&#xff0c;报错。 自己构建ARM平台的dm8镜像&#xff0c;参考 https://gitee.com/xlongfu/dm-docker/tree/master&#xff0c;发现一些问题 首先…

Linux之实战命令73:at应用实例(一百零七)

简介&#xff1a; CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a; 多媒体系统工程师系列【…

万字长文解读【深度学习面试——训练(DeepSpeed、Accelerate)、优化(蒸馏、剪枝、量化)、部署细节】

&#x1f33a;历史文章列表&#x1f33a; 深度学习——优化算法、激活函数、归一化、正则化深度学习——权重初始化、评估指标、梯度消失和梯度爆炸深度学习——前向传播与反向传播、神经网络&#xff08;前馈神经网络与反馈神经网络&#xff09;、常见算法概要汇总万字长文解读…

C++ | Leetcode C++题解之第554题砖墙

题目&#xff1a; 题解&#xff1a; class Solution { public:int leastBricks(vector<vector<int>>& wall) {unordered_map<int, int> cnt;for (auto& widths : wall) {int n widths.size();int sum 0;for (int i 0; i < n - 1; i) {sum wi…

DDei在线设计器V1.2.42版发布

V1.2.42版 新特性&#xff1a; 1.快捷编辑框可以映射到主控件的多个属性上&#xff0c;从而实现快速编辑。 2.跟随图形的支持范围增加&#xff0c;从仅支持线控件到支持所有控件 2.新增控件双击回调函数EVENT_CONTROL_DBL_CLICK&#xff0c;可以用于覆盖默认的快速编辑逻辑…

大数据的实时处理:工具和最佳实践

在当今的数字世界中&#xff0c;数据以前所未有的速度从无数来源生成&#xff0c;包括社交媒体、物联网设备、电子商务平台等。随着组织认识到这些数据的潜在价值&#xff0c;他们越来越多地转向实时处理&#xff0c;以获得即时、可操作的见解。但是&#xff0c;实时处理大数据…

【51单片机】蜂鸣器演奏音乐——小星星天空之城

学习使用的开发板&#xff1a;STC89C52RC/LE52RC 编程软件&#xff1a;Keil5 烧录软件&#xff1a;stc-isp 开发板实图&#xff1a; 文章目录 蜂鸣器按键发声无源蜂鸣器演奏音乐简单乐理小星星天空之城 蜂鸣器 蜂鸣器在开发板的位置如下&#xff1a; 蜂鸣器是一种将电信号转…

【含开题报告+文档+源码】高校校园二手交易平台的设计与实现

开题报告 随着互联网的快速发展&#xff0c;电子商务成为了现代化社会中不可或缺的一部分。线上交易平台的兴起&#xff0c;为商家和消费者创造了更多的交易机会和便利。然而&#xff0c;传统的电商平台通常由一家中央机构管理和控制&#xff0c;对商家和消费者的自由度有一定…

录制的音频听起来非常缓慢,声音很模糊

一、主题 录制的音频听起来非常缓慢&#xff0c;声音很模糊 二、问题背景 硬件&#xff1a;T113&#xff0c;R528等平台系列产品 软件&#xff1a;Tina5.0 三、问题描述 1、复现步骤 使用arecord进行录音。 arecord -Dhw:audiocodec -f S16_LE -r 16000 -c 2 -d 5 /tmp/t…

计算机的错误计算(一百五十)

摘要 探讨 MATLAB 中 的计算精度问题。当 为含有小数的大数或 &#xff08;&#xff09;附近数时&#xff0c;输出会有错误数字。 例1. 已知 计算 直接贴图吧&#xff1a; 另外&#xff0c;16位的正确值分别为 -0.7882256119904400e0、0.1702266977524110e0、-0.…

【网络安全 | 漏洞挖掘】Google SSO用户的帐户接管

未经许可,不得转载。 文章目录 DOM XSS获取 CSRF Token解除Google账户绑定在这篇博文中,我将详细介绍找到针对Google SSO用户的账号接管(ATO)漏洞的过程。 DOM XSS 我遇到 DOM XSS 漏洞的位置非常微妙,因为我遇到了非常严格的WAF。 获取 CSRF Token 在找到XSS漏洞后,我…

2024中国游戏出海情况

01 哪里出海更花钱&#xff1f; 报告显示&#xff0c;中国手游在全球不同市场的获客成本不同&#xff0c;整体来看北美市场竞争更加激烈&#xff0c;其安卓和iOS获客成本是拉丁美洲的12倍和7倍。 按具体市场划分&#xff0c;获客成本最高的TOP 3为韩国、美国和日本&#xff0c…

AI写作(七)的核心技术探秘:情感分析与观点挖掘

一、AI 写作中的关键技术概述 情感分析与观点挖掘在 AI 写作中起着至关重要的作用。情感分析能够帮助 AI 理解文本中的情感倾向&#xff0c;无论是正面、负面还是中性。在当今信息时代&#xff0c;准确把握用户情绪对于提供个性化体验和做出明智决策至关重要。例如&#xff0c;…

AlphaProof IMO 2024 P1 in LEAN 之 简介

AlphaProof 是用于进行数学证明的人工智能&#xff0c;其中&#xff0c;对于 IMO 2024 中的6道题中的 4 道。本系列博文&#xff0c;就 AlphaProof 对于 IMO 2024 P1 给出的答案进行详细讲述。这里是此系列的第一篇。 IMO 2024 P1 题目如下&#xff1a; IMO 2024 P1 答案 α 为…

CANFD与CAN区别

CANFD帧的帧格式相比于传统CAN帧的帧格式多了以下的不同的&#xff1a; 1.CANFD帧中用RRS位替换了CAN帧中的RTR位&#xff0c;CAN报文中的RTR&#xff08;Remote Transmission Request&#xff09;位是远程帧发送请求位&#xff0c;当RTR位为显性&#xff08;0&#xff09;时&…

并发编程设计模式——Balking模式(三十九)

Balking 模式 多线程下&#xff0c;维护一个共享状态满足某个条件时&#xff0c;执行业务逻辑&#xff1b;当不满足时则立即放弃。通常用互斥锁来确保共享状态线程安全&#xff0c;如果不需要保证共享状态原子性&#xff0c;也可以用 volitle 修饰&#xff0c;替换互斥锁。 Bal…

了解Synchronized与Lock的区别

前言&#xff1a; 在多线程编程中&#xff0c;保证线程安全是至关重要的。Java提供了两种主要的同步机制&#xff1a;synchronized关键字和Lock接口。尽管它们都是为了解决多线程并发访问共享资源的问题&#xff0c;但在使用方式和特性上存在一些显著的差异。 synchronized&am…