欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 锐评 > 【iOS】知乎日报总结

【iOS】知乎日报总结

2025/2/25 3:14:37 来源:https://blog.csdn.net/2301_80467753/article/details/144013484  浏览:    关键词:【iOS】知乎日报总结

文章目录

  • 前言
  • 首页
    • 网络请求
    • 轮播图
    • 上滑加载
    • 图片请求
  • 文章详情页
    • WKWebView的使用
    • 点赞、收藏持久化——FMDB的使用
  • 其他问题
    • 沙盒问题
    • 单元格点击
    • 其他
  • 总结

前言

  在系统学习了OC语言和UI控件后,知乎日报是第一个比较大的项目,耗时一个多月时间,里面使用到了YYModel、Masonry、AFNetworking、FMDB等多个第三方库。项目总体采用MVC大框架,下面是对这段时间知乎日报项目的总结。

首页

  首页主要是由一个轮播图和一个TableViewCell构成的,其中都要通过知乎日报的API对顶部轮播图和下面的实时文章进行网络请求。
在这里插入图片描述

网络请求

  在这里进行网络请求时,笔者将所有的网络请求封装到了一个manager类里,在需要时可直接通过manager单例进行方法调用。这里可以参考【iOS】使用AFNetworking进行网络请求
【iOS】使用一个单例通过AFNetworking来实现网络请求
该项目中使用到的开源API可参考:知乎日报 API 分析(如何规范api设计)

这里以对顶部的文章进行网络请求为例:

//Manager.h
+ (instancetype)sharedManager;- (void)requestTopStoriesData: (LatestStoriesBlock)success failure: (ErrorBlock)failure;//Manager.m
static id manager = nil;+ (instancetype)sharedManager {if (!manager) {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{manager = [[Manager alloc] init];});}return manager;
}- (void)requestTopStoriesData:(LatestStoriesBlock)success failure:(ErrorBlock)failure {[[AFHTTPSessionManager manager] GET:@"https://news-at.zhihu.com/api/4/news/latest" parameters: nil progress: nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {LatestStoriesModel* latestStoriesModel = [[LatestStoriesModel alloc] initWithDictionary: responseObject error: nil];success(latestStoriesModel);} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {failure(error);}];
}

对当天推送内容和过往内容的申请类似,就不多赘述了。

轮播图

至于顶部的轮播图,跟之前ZARA和网易云仿写项目的轮播图有异曲同工之妙。

//MainViewController.h
@property (nonatomic, strong)NSMutableArray* imageViewArray;
@property (nonatomic, strong)NSMutableArray* titleArray;
@property (nonatomic, strong)NSMutableArray* userNameArray;//MainViewController.m
- (void) topStroies {self.imageViewArray = [[NSMutableArray alloc] init];self.titleArray = [[NSMutableArray alloc] init];self.userNameArray = [[NSMutableArray alloc] init];UIImageView* firstImageView = [[UIImageView alloc] init];Top_Stories* top_stories = self.latestStoriesModel.top_stories[4];[Manager setImage: firstImageView WithString: top_stories.image];[self.imageViewArray addObject: firstImageView];for (Top_Stories* top_stories in self.latestStoriesModel.top_stories) {UIImageView* imageView = [[UIImageView alloc] init];[Manager setImage: imageView WithString: top_stories.image];[self.imageViewArray addObject: imageView];[self.titleArray addObject: top_stories.title];[self.userNameArray addObject: top_stories.hint];}UIImageView* lastImageView = [[UIImageView alloc] init];top_stories = self.latestStoriesModel.top_stories[0];[Manager setImage: lastImageView WithString: top_stories.image];[self.imageViewArray addObject: lastImageView];self.mainView.imageViewArray = self.imageViewArray;self.mainView.titleArray = self.titleArray;self.mainView.userNameArray = self.userNameArray;
}

上滑加载

对于过往文章的上滑加载,我们只需要进行通知传值,然后请求数据后更新主页面的tableView即可。

- (void)viewDidLoad {[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(loadBeforeStories) name: @"requestBeforeStories" object: nil];
}- (void)requestBeforeStories {StoriesModel* storiesModel = [self.beforeStoriesModelArray lastObject];NSString* before1Date = storiesModel.date;[self.manager requestBeforeDate: before1Date beforeStoriesData:^(StoriesModel * _Nonnull beforeStoriesModel) {[self.beforeStoriesModelArray addObject: beforeStoriesModel];[self sendViewStories: beforeStoriesModel];NSString* before2Date = [DateModel getBeforeDateWithTimeString: beforeDate];[self.manager requestBeforeDate: before2Date beforeStoriesData:^(StoriesModel * _Nonnull beforeStoriesModel) {[self.beforeStoriesModelArray addObject: beforeStoriesModel];[self sendViewStories: beforeStoriesModel];NSString* before3ForeDate = [DateModel getBeforeDateWithTimeString: foreForeDate];[self.manager requestBeforeDate: before3Date beforeStoriesData:^(StoriesModel * _Nonnull beforeStoriesModel) {[self.beforeStoriesModelArray addObject: beforeStoriesModel];[self sendViewStories: beforeStoriesModel];dispatch_async(dispatch_get_main_queue(), ^{self.mainView.isLoading = NO;[self.mainView.tableView reloadData];});} failure:^(NSError * _Nonnull error) {NSLog(@"请求前三天消息失败");}];} failure:^(NSError * _Nonnull error) {NSLog(@"请求前两天消息失败");}];} failure:^(NSError * _Nonnull error) {NSLog(@"请求前一天消息失败");}];
}

主页面这里在程序运行加载view时可能会出现view空白的情况,查询资料了解到可能是线程的问题,我们是在Controller中调用的网络请求,请求下来的数据会赋值给View的各种属性,然后UI就会将这些数据展现出来,但请求是需要时间的,而UI的布局会比网络请求快,所以如果在viewDidLoad里面先初始化view,那么数据还没请求下来,UI就已经布局,导致我们的View里的UI控件是空白的,为解决这一问题,这里需要用到GCD的一个方法:

dispatch_async(dispatch_get_main_queue(), ^{[self sendViewStories: latestStoriesModel];[self topStories];[self setViewAndModel];[self requestBeforeStoriesWithDate: self.latestStoriesModel.date];
});

dispatch_async(dispatch_get_main_queue(), ^{ … }):这个函数调用将一个代码块提交到主队列(主线程)上异步执行。这通常用于确保UI更新和其他需要在主线程执行的操作能够正确运行,特别是在从后台线程返回结果时。

这里笔者对GCD的学习还较为浅显,只是能用于解决UI更新冲突的问题,至于具体的原理和GCD更多的使用方法,笔者后期还会再进行学习。

图片请求

在对图片进行网络请求时,请求到的图片内容都是url,此时我们没法直接将其转化为图片形式,所以就要使用一个第三方库——SDWebImage库,这个库可以将我们请求到的url转为图片,其用法如下:
首先,我们在podfile文件中导入该库,然后使用终端命令导入项目,跟之前Masonry库的导入步骤一样,然后在我们需要获取图片的文件里导入SDWebImage的头文件,之后获取我们通过网络请求到的图片的url,使用 SDWebImage 中的 sd_setImageWithURL: 方法将网络图片加载到 UIImageView 中。

NSString *string = [string stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];NSURL* url = [NSURL URLWithString:string];[self.imageView sd_setImageWithURL: url placeholderImage: [UIImage imageNamed: @"placeholder.png"]];

文章详情页

  在文章详情页主要实现一个文章内容的布局,文章底部工具栏的设置和文章的点赞、收藏功能及其的持久化,还有文章评论区页面的展开。
在这里插入图片描述

WKWebView的使用

WKWebView 是苹果在 iOS 8 及以后版本中引入的一个用于加载和显示网页内容的类,它是 WebKit 框架的一部分,用于替代旧的 UIWebView。WKWebView 提供了更好的性能、更强的功能和更高的安全性。
要在iOS应用中使用 WKWebView,需要导入 WebKit 框架,并创建 WKWebView 的实例。

#import <WebKit/WebKit.h>// 创建一个 WKWebView 实例
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.frame];// 加载一个网页
NSURL *url = [NSURL URLWithString:@"https://www.example.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[webView loadRequest:request];

在编写文章详情页时,刚开始会出现UI加载空白即WKWebView网页加载不出来的情况,查询资料并检查代码之后,发现是App Transport Security Settings(ATS)设置问题:
如果没有在 Info.plist 文件中设置 App Transport Security Settings,加载 https 链接可能会失败。需要在 Info.plist 中添加 NSAppTransportSecurity 字典,并将 NSAllowsArbitraryLoads 设置为 YES 以允许连接到任何域。

在这里插入图片描述

App Transport Security (ATS) 是苹果公司在其移动操作系统 iOS 9 和 OS X 10.11 El Capitan 及更高版本中引入的一项安全特性。ATS 的目的是提高应用程序的网络安全性,确保所有的网络通信都通过安全的方式进行。
强制使用 HTTPS:
ATS 要求应用程序的所有网络请求都必须通过 HTTPS 发送,以保护用户数据免受中间人攻击和窃听。
数据加密:
ATS 强制使用强加密标准,包括 TLS 1.2 或更高版本,并要求使用前向保密(Forward Secrecy)和强加密套件。
限制明文请求:
ATS 限制了对明文 HTTP、FTP 或非加密的 WebSocket 连接的请求,以防止敏感信息在传输过程中被截获。
允许自定义:
开发者可以在 Info.plist 文件中配置 ATS 设置,以适应特定的网络通信需求,例如允许特定的非安全连接或为特定域名设置例外。
内容安全策略:
ATS 鼓励开发者使用内容安全策略(CSP)来减少跨站脚本(XSS)攻击的风险。
网络代理:
ATS 支持网络代理的使用,允许应用程序通过代理服务器进行网络通信。
应用审核:
苹果在应用审核过程中会检查 ATS 的合规性,如果开发者需要为应用启用非安全的网络连接,需要在应用审核时提供充分的理由。
隐私保护:
ATS 有助于保护用户的隐私,因为它限制了第三方跟踪用户网络行为的能力。
逐步淘汰:
从 iOS 11 和 macOS 10.13 High Sierra 开始,苹果鼓励开发者使用更现代的网络技术,如 WKWebView,这些技术默认启用 ATS 并提供更好的性能和安全性。

点赞、收藏持久化——FMDB的使用

使用FMDB库之前,先通过 Cocoapods 引入到项目中,使用 pod ‘FMDB’ 命令进行安装。
关于FMDB的具体用法见:【iOS】iOS的轻量级数据库——FMDB

这里以对收藏功能实现持久化为例:

- (void)createStoriesCollect {NSString* doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];NSString* filename = [doc stringByAppendingPathComponent: @"collectDatabase.sqlite"];self.collectDatabase = [FMDatabase databaseWithPath: filename];if ([self.collectDatabase open]) {BOOL result = [self.collectDatabase executeUpdate: @"CREATE TABLE IF NOT EXISTS collectDatabase (idLabel text NOT NULL)"];if (result) {FMResultSet* resultSet = [self.collectDatabase executeQuery: @"SELECT * FROM collectDatabase"];while ([resultSet next]) {[self.storiesCollect addObject: [resultSet stringForColumn: @"idLabel"]];}NSLog(@"create succeed");} else {NSLog(@"create error");}[self.collectDatabase close];}
}- (void)saveStoriesCollect {if ([self.collectDatabase open]) {for (NSString* ID in self.storiesCollectSet) {FMResultSet* resultSet = [self.collectDatabase executeQuery: @"SELECT * FROM collectDatabase WHERE idLabel = ?", ID];if (![resultSet next]) {BOOL result = [self.collectDatabase executeUpdate: @"INSERT INTO collectDatabase (idLabel) VALUES (?)", ID];if (result) {NSLog(@"insert succeed");} else {NSLog(@"insert error");}}}[self.collectDatabase close];}
}- (void)deleteCollectWithID:(NSString*)ID {if ([self.collectDatabase open]) {BOOL result = [self.collectDatabase executeUpdate: @"delete from collectDatabase WHERE idLabel = ?", ID];if (result) {NSLog(@"delete succeed");} else {NSLog(@"delete error");}}
}

其他问题

沙盒问题

刚开始写的时候运行项目会Failed,然后出现这样的报错:Sandbox: rsync.samba(27013) deny(1) file-write-create /Users /…

查询完资料说是:

在 iOS 项目中,特别是在使用 Xcode 时,沙盒环境阻止了应用写入不被允许的文件或目录。

解决方法:在 Xcode 中,在项目的 Build Settings 中搜索 User Script Sandboxing 并将其值设置为 NO 。
在这里插入图片描述

单元格点击

单元格点击后自动取消选中需要使用deselectRowAtIndexPath: animated:方法。

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {if (indexPath.row == 1) {[[NSNotificationCenter defaultCenter] postNotificationName: @"didselectCollection" object: nil userInfo: nil];}[tableView deselectRowAtIndexPath:indexPath animated:YES];
}

其他

xcode SDK does not contain ‘libarclite‘问题解决方法:
在这里插入图片描述
Framework ‘Pods_______’ not found问题解决参考:
framework not found Pods_xxx xcode打包错误解决方法

Linker command failed with exit code 1 (use -v to see invocation)问题解决参考:
Xcode出现( linker command failed with exit code 1)错误总结

总结

  知乎日报是第一个比较大的项目,耗时一个多月时间,在完成过程中遇到了很多问题,也学到了很多,比如YYModel、AFNetworking、SDWebImage、FMDB等的用法,后续还会继续学习GCD等关于iOS项目线程的问题。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词