微博阅读器demo(二) 微博列表
来源:互联网 发布:淘宝超a鞋店推荐 编辑:程序博客网 时间:2024/04/28 15:46
微博阅读器demo实现的第二部分: 微博列表,这也是本demo的主要部分。
微博列表通过一个UITableView来实现,所要面临的问题有如下几个:
1.调用开放平台的微博API,获取json数据并解析;
2.UITableViewCell的布局设计;
3.下拉刷新和上拉加载更多。
实现的效果如下:
一、调用开放平台的微博API,获取json数据并解析
本demo主要调用https://api.weibo.com/2/statuses/friends_timeline.json 这个API获取当前登录用户及其所关注用户的最新微博,
查阅api的介绍,这个接口采用get方法,参数如下:
我们只要指定access_token和page这两个参数,其他默认。count默认为20,也就是说我们一次能获得20条微博。为了不阻塞主线程,
访问网络的操作都采用GCD,
访问成功之后得到NSData,并对其解析。
说到这里,我要先介绍一下我的微博类(WBStatuses)的模型
#import <Foundation/Foundation.h>#import "WBRetweetedStatus.h"@interface WBStatuses : NSObject@property NSString * profile_image; // 微博头像地址@property NSString * userName; // 用户名@property NSString * from; // 微博来源@property NSString * text; // 微博正文@property NSString * idstr; // 微博id@property NSString * createAt; // 微博创建时间@property NSMutableArray * thumbnailPictureUrls; // 缩略图地址@property NSMutableArray * bmiddlePictureUrls; // 中等大小图片地址@property NSMutableArray * largePictureUrls; // 原图地址@property WBRetweetedStatus * retweetedStatus; // 转发的微博@property bool hasRetweetedStatuses; // 是否含有转发微博@end
针对微博API所返回的json数据的格式,解析如下:
#import "WBStatusesImpl.h"@implementation WBStatusesImpl-(NSMutableArray *) httpRequestWithPage:(int) page{ HttpHelper * httpHelper = [[HttpHelper alloc]init]; UrlHelper * urlHelper = [[UrlHelper alloc]init]; NSString * url = [NSString stringWithFormat:[urlHelper urlForKey:@"friends_timeline"],[urlHelper accessToken],page]; // 获取完整地址 NSLog(@"%@",url); NSData * data = [httpHelper SynchronousGetWithUrl:url]; // get 方法访问网络 NSMutableArray * statuses = [[NSMutableArray alloc]init]; // 存放20条微博的数组 if(data) // 获取数据成功 { NSDictionary * dic =[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil]; NSArray * statusesArray =[dic objectForKey:@"statuses"]; // 讲NSData解析成数组 for(NSDictionary * d in statusesArray) // 循环解析每条微博 { NSString * tem; NSArray * urlTem; NSDictionary * user = [d objectForKey:@"user"]; // 获得用户信息 NSDictionary * retweetedStatus = [d objectForKey:@"retweeted_status"]; // 转发的微博 WBStatuses * wbstatuses = [[WBStatuses alloc]init]; wbstatuses.profile_image = [user objectForKey:@"profile_image_url"]; wbstatuses.userName =[user objectForKey:@"name"]; wbstatuses.text=[d objectForKey:@"text"]; wbstatuses.idstr = [d objectForKey:@"idstr"]; wbstatuses.createAt = [d objectForKey:@"created_at"]; wbstatuses.createAt = [DateHelper timePassedSinceDateString:wbstatuses.createAt]; // 计算发微博的时间到现在的时间间隔 if(retweetedStatus!=nil) //解析转发微博,方法与微博一致 { wbstatuses.hasRetweetedStatuses = true; wbstatuses.retweetedStatus.text = [retweetedStatus objectForKey:@"text"]; NSDictionary * retweetedUser = [retweetedStatus objectForKey:@"user"]; wbstatuses.retweetedStatus.userName = [retweetedUser objectForKey:@"name"]; wbstatuses.retweetedStatus.createAt = [retweetedStatus objectForKey:@"created_at"]; urlTem =[d objectForKey:@"pic_urls"]; for(NSDictionary * urlDic in urlTem) { NSString * str =[urlDic objectForKey:@"thumbnail_pic"]; [wbstatuses.retweetedStatus.thumbnailPictureUrls addObject:str]; [wbstatuses.retweetedStatus.bmiddlePictureUrls addObject:[str stringByReplacingOccurrencesOfString:@"sinaimg.cn/thumbnail/" withString:@"sinaimg.cn/bmiddle/"]]; [wbstatuses.retweetedStatus.largePictureUrls addObject:[str stringByReplacingOccurrencesOfString:@"sinaimg.cn/thumbnail/" withString:@"sinaimg.cn/large/"]]; // NSLog(@"%@",str); } } urlTem =[d objectForKey:@"pic_urls"]; for(NSDictionary * urlDic in urlTem) // 解析微博图片地址,缩略图与其他图地址只是前面部分不同 { NSString * str =[urlDic objectForKey:@"thumbnail_pic"]; [wbstatuses.thumbnailPictureUrls addObject:str]; [wbstatuses.bmiddlePictureUrls addObject:[str stringByReplacingOccurrencesOfString:@"sinaimg.cn/thumbnail/" withString:@"sinaimg.cn/bmiddle/"]]; [wbstatuses.largePictureUrls addObject:[str stringByReplacingOccurrencesOfString:@"sinaimg.cn/thumbnail/" withString:@"sinaimg.cn/large/"]]; } tem = [d objectForKey:@"source"]; wbstatuses.from=@"来自:"; tem = [[[tem substringToIndex:[tem length]-4] componentsSeparatedByString:@">"] lastObject]; wbstatuses.from = [wbstatuses.from stringByAppendingString:tem]; [statuses addObject:wbstatuses]; } return statuses; } return nil;}@end
二、UITableViewCell的布局设计
很明显,cell需要采用自定义的模式。观察发现每个微博cell就只有头像,用户名,时间,来源的位置和大小相对固定,可以在storyboard中先确定。
微博正文高度不定,图片个数不定,需要动态加载。同时图片的获取需要采用GCD。
首先,每个cell的高度都不同,要在微博列表对应的控制类中实现UITableView的委托方法
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 来确定每个cell的高度,这其中有一些要注意
的在我另一篇博客(UITableView中heightForRowAtIndexPath 产生 EXC_BAD_ACCESS 的原因)说明。此外,因为UITableViewCell的重用机制,并且
每个cell都动态添加图片和正文,这将导致cell中的内容错位(即上一个cell的内容出现在下一个cell中)如下图所示:
解决的办法是给每个动态加载的图片设置一个标记然后在
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath中遍历cell的子视图,
若为动态加载的就将其remove,然后再重新加载新的子视图:
static NSString *CellIdentifier = @"WBStatusesCell"; WBStatusesCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; // 重用 for(UIView * v in [cell subviews]) // 遍历子视图 { if(v.tag == 1) // 标记为1,是动态加载的,将其remove { [v removeFromSuperview]; } } WBStatuses *statuses = [self.statuses objectAtIndex:indexPath.row]; // 指定位置的微博数据 [cell contentWithWBStatuses:statuses]; // 重新加载
还有就是图片的下载,因为不能每一次加载都重新下载图片,所以,下载过的图片就存放在一个NSDictionary中以图片地址作为键值,
重新加载的时候,先查看NSDictionary中有没有对应的图片,有就直接用,没有再下载:
UIImage *image = [self.imageDictionary objectForKey:statuses.profile_image]; cell.profile_image.image = image; if(image==nil) { cell.profile_image.image = nil; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{ cell.profile_image.image =[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:statuses.profile_image]]]; [self.imageDictionary setValue:cell.profile_image.image forKey:statuses.profile_image]; }); }
三、下拉刷新和上拉加载更多
下拉刷新采用ios自带的UIRefreshControl,UIScrollerView及其子类都有一个refreshControl属性用于下拉刷新,只需在viewDidLoad中对refreshControl
进行设置即可:
self.refreshControl = [[UIRefreshControl alloc]init]; self.refreshControl.attributedTitle = [[NSAttributedString alloc]initWithString:@"下拉刷新"]; [self.refreshControl addTarget:self action:@selector(Refresh) forControlEvents:UIControlEventValueChanged];将要执行的代码放在Refresh方法中
-(void)Refresh{ [self.refreshControl beginRefreshing]; self.refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:@"加载中..."]; [self performSelector:@selector(loadData) withObject:nil afterDelay:1.0f];}-(void)loadData{ if (self.refreshControl.refreshing == true) { [self performHttpRequestWithPage:1]; }}
加载完成后,修改refreshControl的属性:
if(page==1&&self.refreshControl.refreshing) { [self.refreshControl endRefreshing]; self.refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:@"下拉刷新"]; }
上拉加载更多其实就是拉到最底部显示一个按钮,点击按钮就加载更多内容:
因为每个cell的高度不固定,所以整个tableView的contentSize也就不固定,加载按钮的位置也要动态变化。每次有新的cell加入时contenSize的高度就改变,
按钮的位置(frame)就要随之改变,相反,contenSize的高度不变,按钮的位置就不应该变化。所以,我们通过tableView的tag来控制按钮的位置是否需要改变。
viewDidLoad中设置并添加按钮:
UIButton * btn =[[UIButton alloc]init]; [btn addTarget:self action:@selector(loadMore) forControlEvents:UIControlEventTouchDown]; [btn setTitle:@"加载更多" forState:UIControlStateNormal]; [btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; self.loadMoreButton = btn; [self.tableView addSubview:self.loadMoreButton]; self.tableView.tag = NEED_READD_BUTTON;
UITableView也是一个UIScrollView,可以实现方法- (void)scrollViewDidScroll:(UIScrollView *)scrollView判断是否滑到底部:
- (void) scrollViewDidScroll:(UIScrollView *)scrollView{ CGPoint contentOffsetPoint = self.tableView.contentOffset; // 偏移点 CGRect frame = self.tableView.frame; if (contentOffsetPoint.y == self.tableView.contentSize.height - frame.size.height) // 滑到了底部 { if(self.tableView.tag == NEED_READD_BUTTON) // 是否重新改变按钮位置 { self.tableView.contentSize = CGSizeMake(self.tableView.contentSize.width, self.tableView.contentSize.height+40); // 增加contenSize的高度,用于放置按钮 self.loadMoreButton.frame = CGRectMake(0,self.tableView.contentSize.height-40,self.tableView.contentSize.width,40); // 改变按钮的位置 self.tableView.tag = NOT_READD_BUTTON; // 将tableView 标记改为不需改变 } }}
数据加载完成,回到主线程,改变tableView的标记,将其隐藏,并reloadData:
dispatch_async(dispatch_get_main_queue(), ^{ self.tableView.tag = NEED_READD_BUTTON; self.loadMoreButton.frame = CGRectMake(0, 0, 0, 0); [self.tableView reloadData]; });
微博阅读器demo的主要部分就完成了,下面是我的源码(注意将WBForWang-Prefix.pch文件中的REDIRECT_URI、 APP_KEY以及APP_SECRECT改为自己对应的):
下载链接
- 微博阅读器demo(二) 微博列表
- 微博阅读器demo(一)OAuth 2.0 认证
- 电子书及阅读器Demo
- 企业微信获取部门列表demo
- 微信精选阅读器
- react native (二)‘电影列表demo’
- 腾讯微博--DEMO
- iOS 新浪微博客户端Demo实践之(六) 微博评论列表页面和发评论
- ios epub电子书阅读器demo
- ios epub电子书阅读器demo
- Web版RSS阅读器(二)——使用dTree树形加载rss订阅分组列表
- 如何将Google阅读器中的条目发送的新浪、腾讯微博
- 新浪微博的demo
- 新浪微博授权demo
- 微博展示小Demo
- 新浪微博分享demo
- 微信小程序框架wxss(二)电话列表展示demo
- Android聊天列表Demo(QQ,微信,等通讯工具的聊天列表)
- poj 3764 The xor-longest Path
- 基于格子的AOI算法
- 【Leetcode】Merge Two Sorted Lists
- Codefoces 432C Prime Swaps(数论+贪心)
- 黑马程序员-Java基础09
- 微博阅读器demo(二) 微博列表
- 最优路径问题 mod 4 的最小值
- javascript/js的ajax请求方式:GET与POST
- 安卓ContentObserver模式获取短信用正则自动填充验证码
- Codeforces 432B Football Kit(水题)
- C++优化考虑
- 上传代码到谷歌
- 两个栈共享一块存储空间新解
- 黑马程序员.if语句小结