MOSAD_HW3 网络访问和本地存储
一、实验目的
-
学习使用NSURLSession或AFNetworking库进行网络访问
-
学习iOS沙盒机制,进行文件读写操作
二、实现功能
- 实现两个页面:登陆页和消息页
- 登陆页使用login API进行登陆,并进行成功判断,登陆成功之后跳转到信息页,并显示登陆的个人用户信息
- 消息页为三个button和一个tableview组成,tableview有两个section,第一个section显示用户信息,第二个section显示图片
- 点击"加载"按钮,若Cache中没有缓存的文件,则加载网络图片并显示在图片列表中,要求:图片下载完成前,显示loading图标;图片下载后,存入沙盒的Cache中
- 点击"加载"按钮,若Cache中已存在图片文件,则直接从Cache中读取出图片并显示
- 点击"清空"按钮,清空图片列表中的所有图片
- 点击"删除缓存"按钮,删除存储在Cache中的图片文件
三、实现效果图
- 登陆页:背景是动态背景(使用UIWebView实现)
- 信息页:显示用户信息和图片
加载前:
加载中:出现加载图标
加载后:
四、程序实现
4.0 基础架构
本次项目基本架构使用了根控制器为UINavigationController,然后push登录页进去,之后通过登录页跳转到信息页的方法(就是登录页跳转tabbarcontroller的方式)实现:
- 根控制器:
UINavigationController * rootcontroller = [[UINavigationController alloc]init];
self.window.rootViewController = rootcontroller;
ViewController* loginpage = [[ViewController alloc] init];
[rootcontroller pushViewController:loginpage animated:YES];
4.1 登陆页
动态背景图
首先实现登陆的背景,这里想试一下类似于微博登陆页的广告推送的效果,所以学习了一下动态背景的添加方式,使用UIWebView实现了登陆页的动态背景
- UIWebView原本是一个可以获取网络数据进行加载的一个View,但是由于它可以支持gif图片的播放功能,这里就用它来实现我们登录页的动态背景
- 首先获得我们的gif图片的NSData数据:
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"4" ofType:@"gif"];
NSData *gif = [NSData dataWithContentsOfFile:filePath];
- 之后初始化我们的UIWebview,这里要手动定义调整下我们的页面位置,因为图片大小可能会超过屏幕,所以要设定开始的位置为负值,并且设定ScrollView为不可滚动,否则就会变成滚动浏览,不符合我们的动态背景图的要求:
UIWebView *web = [[UIWebView alloc] initWithFrame:CGRectMake(-70, -50, 500, 1000)];
[web loadData:gif MIMEType:@"image/gif" textEncodingName:@"" baseURL:nil];
web.scrollView.scrollEnabled = NO;
[self.view addSubview:web];
这样就完成了动态背景图的加入;
用户登录操作
- 在点击登录button的登录按钮中,调用了AFNetwork的方法来进行数据传输,这里直接设置url为TA给的网址url,请求方式为GET,请求和返回数据类型为JSON类型,之后在回调函数的success中,判断返回的信息是success还是fail,如果是success则跳转进入下一个界面,如果失败则调用下方的弹窗函数,弹出一个登录失败的提示窗(PS:具体AFNetwork的使用方法就不再具体介绍了,可以看期中项目的相关部分,写的很多很详尽了)
#pragma mark -login
-(void)login_start{
self.name = self.useremail.text;
self.pass = self.password.text;
NSLog(@"%@", self.name);
NSLog(@"%@", self.pass);
//AFNetwork
AFHTTPSessionManager* manager = [[AFHTTPSessionManager alloc]init];
NSString *url = [NSString stringWithFormat:@"http://172.18.176.202:3333/hw3/signup"];
NSDictionary * dic = @{@"name":self.name,@"pwd":self.pass};
manager.responseSerializer = [AFJSONResponseSerializer serializer];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
[manager POST:url parameters:dic headers:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable resdic){
//NSLog(@"login:%@",resdic);
NSDictionary * dic = (NSDictionary *)resdic;
NSString * str = [dic valueForKeyPath:@"msg"];
//NSLog(@"str:%@",str);
if ([str isEqual: @"success"]) {
RootTabBarController *roottabbar = [[RootTabBarController alloc]init];
roottabbar.delegate =self;
[self.navigationController pushViewController:roottabbar animated:YES];
}
if ([str isEqual: @"fail"]) {
[self showError:@"登陆失败"];
}
} failure:^(NSURLSessionDataTask * _Nonnull task, NSError*_Nonnull error){
NSLog(@"fail");
}];
}
- (void)showError:(NSString *)errorMsg {
// 1.弹框提醒
// 初始化对话框
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示" message:errorMsg preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil]];
// 弹出对话框
[self presentViewController:alert animated:true completion:nil];
}
4.2 信息页
TableView
- 我们在信息页要显示用户的个人信息(网络请求得到)、五张图片、URL网络请求得到,所以我们要定义一个拥有两个section的tableview来存储两部分信息
#pragma mark - tableview
- (UITableView*)tableView{
if(_tableView == nil){
_tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 118, self.view.frame.size.width, self.view.frame.size.height-200) style:UITableViewStyleGrouped];
_tableView.dataSource = self;
_tableView.delegate = self;
_tableView.backgroundColor = [UIColor clearColor];
[_tableView setScrollEnabled:YES];
//_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;//去除分割线
}
return _tableView;
}
#pragma mark - 重写----设置有groupTableView有几个分区
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 2; // 返回值是多少既有几个分区
}
#pragma mark - 重写----设置每个分区有几个单元格
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
switch (section) {
case 0:
return 4;
break;
case 1:
return 5;
default:
break;
}
return 1;
}
这样我们完成了tableview的创建,剩下的就是向其中添加内容
获取用户信息
这里我们通过GET请求,使用AFNetwork来获取请求,因为是异步网络请求,所以我们不能在tableview的加载过程中直接调用加载数据,这样会先完成渲染,之后再取得数据,所以我们选择再viewdidload中获得数据,之后调用网络请求,完成之后调用reloaddata函数重新加载tableview:
//AFNetwork
AFHTTPSessionManager* manager = [[AFHTTPSessionManager alloc]init];
NSString *url = [NSString stringWithFormat:@"http://172.18.176.202:3333/hw3/getinfo?name=MOSAD"];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
[manager GET:url parameters:nil headers:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable resdic){
//NSLog(@"login:%@",resdic);
NSDictionary * dic = (NSDictionary *)resdic;
_namestr = [dic valueForKeyPath:@"name"];
_levelstr = [dic valueForKeyPath:@"level"];
_emailstr = [dic valueForKeyPath:@"email"];
_phonestr = [dic valueForKeyPath:@"phone"];
[self.tableView reloadData];
} failure:^(NSURLSessionDataTask * _Nonnull task, NSError*_Nonnull error){
NSLog(@"fail");
}];
加载
这次作业的唯一的难点也是有收获的地方就是这里了主要解决的有三个问题
- 点击加载后判断cache中存在数据,则从cache加载
- 点击后,没有数据,从网络请求
- 加载过程中显示加载动画
- 首先是加载cache和网络请求,我们这里使用NSFileManager的fileexistPath函数来判断存在,加载则动过NSData的datafromURL来对图片进行直接加载
- 这里就遇到了第一个坑,就是图片的加载是一个同步操作,所以是线程阻断的,直到图片下载完成线程才会继续,这个一开始没有想到,所以在加入加载图像时,一直加载不进去,一开始还以为自己没有addSubview或者add错了,后来查询了下NSData的加载操作才发现它竟然是同步操作hhhh
- 所以知道它是同步操作之后就很好处理了,我们使用子线程来完成加载动作即可
完整加载函数代码如下:(至于加载从哪里加载和是否存在文件的判断,期中项目完全做过了,可以去看其中项目,有比较详细的介绍)
-(void)jiazaiphoto{
_activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(self.view.bounds.size.width/2-50, 300, 100, 100)] ;
_activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray ;
_activityIndicator.backgroundColor = [UIColor grayColor];
_activityIndicator.color = [UIColor yellowColor];
[self.view addSubview:_activityIndicator] ;
[_activityIndicator startAnimating] ;
[[[NSOperationQueue alloc] init] addOperationWithBlock:^{
NSLog(@"下载中。。。。。");
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSArray* array = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *path = array[0];
NSFileManager * existmanager = [NSFileManager defaultManager];
NSString *imageFilePath = [path stringByAppendingPathComponent:@"Image1.png"];
NSData *data1 = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://hbimg.huabanimg.com/d8784bbeac692c01b36c0d4ff0e072027bb3209b106138-hwjOwX_fw658"]];// 本地
_image1 = [UIImage imageWithData:data1]; // 取得图片
NSLog(@"get image1");
NSLog(@"cunrupath:%@",imageFilePath);
[UIImagePNGRepresentation(_image1) writeToFile:imageFilePath atomically:YES];
imageFilePath = [path stringByAppendingPathComponent:@"Image2.png"];
NSData *data2 = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://hbimg.huabanimg.com/6215ba6f9b4d53d567795be94a90289c0151ce73400a7-V2tZw8_fw658"]];// 本地
_image2 = [UIImage imageWithData:data2]; // 取得图片
[UIImagePNGRepresentation(_image2) writeToFile:imageFilePath atomically:YES];
imageFilePath = [path stringByAppendingPathComponent:@"Image3.png"];
NSData *data3 = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://hbimg.huabanimg.com/834ccefee93d52a3a2694535d6aadc4bfba110cb55657-mDbhv8_fw658"]];// 本地
_image3 = [UIImage imageWithData:data3]; // 取得图片
[UIImagePNGRepresentation(_image3) writeToFile:imageFilePath atomically:YES];
imageFilePath = [path stringByAppendingPathComponent:@"Image4.png"];
NSData *data4 = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://hbimg.huabanimg.com/f3085171af2a2993a446fe9c2339f6b2b89bc45f4e79d-LacPMl_fw658"]];// 本地
_image4 = [UIImage imageWithData:data4]; // 取得图片
[UIImagePNGRepresentation(_image4) writeToFile:imageFilePath atomically:YES];
imageFilePath = [path stringByAppendingPathComponent:@"Image5.png"];
NSData *data5 = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://hbimg.huabanimg.com/e5c11e316e90656dd3164cb97de6f1840bdcc2671bdc4-vwCOou_fw658"]];// 本地
_image5 = [UIImage imageWithData:data5]; // 取得图片
[UIImagePNGRepresentation(_image5) writeToFile:imageFilePath atomically:YES];
[_activityIndicator stopAnimating] ;
[self.tableView reloadData];
}];
}];
}
清空
- 清空操作只要将图片设置为空,然后reload以下即可:
-(void)qingkongphoto{
_image1 = nil;
_image2 = nil;
_image3 = nil;
_image4 = nil;
_image5 = nil;
[self.tableView reloadData];
}
清除缓存
- 清除缓存调用NSFileManager的removeItemAtPath即可删除对应路径的缓存文件,要注意的是记得初始化为defaultmanager
-(void)deletecache{
NSArray* array = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *path = array[0];
// NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *imageFilePath1 = [path stringByAppendingPathComponent:@"Image1.png"];
NSString *imageFilePath2 = [path stringByAppendingPathComponent:@"Image2.png"];
NSString *imageFilePath3 = [path stringByAppendingPathComponent:@"Image3.png"];
NSString *imageFilePath4 = [path stringByAppendingPathComponent:@"Image4.png"];
NSString *imageFilePath5 = [path stringByAppendingPathComponent:@"Image5.png"];
//[[NSFileManager defaultManager] removeItemAtPath:pathFullerror:nil];
NSLog(@"deletefilepath:%@",imageFilePath1);
NSFileManager * filemanager= [NSFileManager defaultManager];
NSError * error;
[filemanager removeItemAtPath:imageFilePath1 error:&error];
NSLog(@"error:%@",error);
if ([filemanager removeItemAtPath:imageFilePath2 error:nil]) {
NSLog(@"删除成功");
}
//[filemanager removeItemAtPath:imageFilePath2 error:nil];
[filemanager removeItemAtPath:imageFilePath3 error:nil];
[filemanager removeItemAtPath:imageFilePath4 error:nil];
[filemanager removeItemAtPath:imageFilePath5 error:nil];
}
五、个人总结
本次作业是对于网络请求的使用,由于刚做完期中项目,而且负责的就是这一部分,所以这次作业的制作还是比较简单,要说收获的话,就是在做加载动画时候,发现了图片的加载方式是同步加载,所以要开子线程执行。