iOS项目实战-新浪微博客户端 在AppStore掘金 iOS项目实战-新浪微博客户端 本章内容概述 项目准备工作 搭建项目基础框架 项目功能概述 项目界面结构 获得最新微博信息 发布微博 微博详细 获得微博评论 获得微博转发 发表评论 转发微博 收藏微博 31.1 项目准备工作 在这一章里,我们通过一个新浪微博客户端项目,来对前面所学的基础知识进行综合,使得对基础理解更深入、使用更灵活。 开发新浪微博客户端,需要使用新浪的微博Open API接口,Open API即开放API,也称开放平台。 所谓的开放API(OpenAPI)是服务型网站常见的一种应用,网站的服务商将自己的网站服务封装成一系列API(Application Programming Interface,应用编程接口)开放出去,供第三方开发者使用,这种行为就叫做开放网站的API,所开放的API就被称作OpenAPI(开放API)。 现在Open API 服务非常多,例如,新浪微博、Google地图、豆瓣网、腾讯、淘宝等,这些Open API服务一是可以给开发这带来服务,二是可以给服务平台本身带来流量,可以说是双赢。 使用新浪微博开发平台的步骤如下: 需要注册一个微博账号。 可以在如下的地址注册新浪微博账号:http://weibo.com/signup/signup.php?c=&type=&inviteCode=&code=&spe=&lang=zh。如下图所示。 注册成功后,在如下地址进行登陆: http://open.weibo.com/。如下图所示。 登陆成功后,选择页面的顶部连接的"应用开发"下面的"移动开发",如下图所示。 点击右边的"创建应用"按钮,来创建应用。如下图所示。 在管理中心中可以找到我们创建的应用,如下图所示。 点击应用链接进入,应用的详细内容,如下图所示。 在应用详细页面中,有控制台、应用信息、数据统计、接口管理、转让应用和删除应用等链接,在这些链接中我们常用到的有:应用信息和接口管理。在"应用信息"中可以获得授权的apikey和加密信息。 在接口管理中我们可以查询到详细的api调用方法。 通过上述步骤,我们就申请了一个应用,这需要审核,审核通过后我们就可以使用该接口开发自己的微博客户端了。 31.2 搭建项目基础框架 在上一节我们已经申请了一个应用程序,本节我们来看如何搭建项目的基础框架。要开发新浪微博客户端,官方提供了开发文档和SDK,我们可以下载开发文档学习开发步骤,并且需要将SDK集成到我们的项目当中。 下载开发文档的网站是:http://open.weibo.com/wiki。在这里选择iOS SDK进行文档和SDK的下载。如下图所示。 在下载的SDK中包括了,包含SDK说明文档,源代码,示例代码。如下图所示。 将SDK集成到自己的项目当中需要,如下步骤: 创建新浪微博开放平台应用 1) 获取 app_key,app_secret 第三方开发者须到新浪微博开放平台 http://open.weibo.com/ 注册并创建第三方应用。进而获取应用专属 app_key 及 app_secret。App Key 及 App Secret 查看方式:第三方应用主页->应用信息->基本信息,应用基本信息一栏。 t 2) 配置授权回调页 授权回调页是为 Oauth2.0 认证机制中的登录认证地址,用户登录完成后最后会跳转此地址。需在应用中配置此跳转地址才能使用 SDK 完成用户登录。授权回调页查看及设置地址:第三方应用主页->应用信息->高级信息->Oauth2.0 授权设置。 注:在使用 SDK 时,配置授权回调页是必不可少的;若没有配置,则登陆完成后 无法检测到授权地址,就无法获取授权的 token 等信息。此地址并非必须得配置成 能访问的地址,保证格式正确即可。 下载新浪微博 sdk 在新浪微博开放平台文档一页有 SDK 下载一项,找到 IOS SDK 下载链接 https://github.com/mobileresearch/weibo/ios/sdk/sso-oauth ,打开后可打包下载。 上图"ZIP"按钮,下载的 zip 包是一个编译运行的 SDK Demo。SDK 的源码是在此 demo 工程中 src 目录下,包含 JSONKit 和 SinaWeibo 两个包。 创建新应用,添加 SDK 源码及配置使用环境 1) 第三方应用在本地环境用 Xcode 新建一个工程; 2) 将 src 中的源码引入至你的工程中,如下图的 SinaWeibo,JSONKit: 3) 在你的工程设置项,targets 一栏下,选中自己的 target,在 Info->URL Types 中添加 URL Schemes,此值是 sso 登录时回调时所用。本例采用默认格式:"sinaweibosso."+" 自己应用的 app key",如下图: 使用 sdk 1) 定义你的 app_key, app_secret 及授权回调页,此处定义是为了后面调用 SDK 接口做准备,如下图: #define kAppKey @"28796305491" #define kAppSecret @"76b5cd21a6e755840ce92e33f09c44d51" #define kAppRedirectURI @"http://www.any-phone.com" 2) 构造 SinaWeibo 对象 self.sinaweibo = [[SinaWeibo alloc] initWithAppKey:kAppKey appSecret:kAppSecret appRedirectURI:kAppRedirectURI andDelegate:self.viewController]; kAppKey,kAppSecret,kAppRedirectURI 即第一步定义的值。参数_viewController 作为 SinaWeibo 对象的代理,需要实现 SinaWeiboDelegate 接口: 3) 重写 AppDelegate 的 handleOpenURL 和 openURL 方法: - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url { return [self.sinaweibo handleOpenURL:url]; } - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { return [self.sinaweibo handleOpenURL:url]; } 4) 登录 直接调用 logIn 接口:[sinaweibo logIn]。 5) 接收登录信息需重写 sinaweiboDidLogIn 方法(在此例中_viewController 对象实现的 SinaWeiboDelegate 接口,故应该在此类中加入以下代码): -(void)sinaweiboDidLogIn:(SinaWeibo *)sinaweibo{ NSLog(@"here......."); NSLog(@"%msg=%@",sinaweibo); [self storeAuthData]; } - (void)request:(SinaWeiboRequest *)request didFinishLoadingWithResult:(id)result{ NSLog(@"result=%@",result); } 运行项目,登陆界面如下所示。 31.3 项目功能概述 新浪微博本身的功能非常多也非常复杂,在我们的项目里主要实现了如下功能: 最新微博浏览 发布微博 微博详细 获得微博评论 获得微博转发 转发微博 收藏微博 最新微博浏览 根据如下URL,https://api.weibo.com/2/statuses/home/timeline.json。可以获得获取当前登录用户及其所关注用户的最新微博信息。请求参数如下图所示。 发布微博 我们也可以将我们的新鲜事分享到微博平台,根据如下URL,https://api.weibo.com/2/statuses/update.json。来发一条新微博。请求参数如下所示。 程序运行结果如下所示。 微博详细 根据微博ID可以获得微博详细,包括发送微博的用户信息、评论数、转发数等。根据如下URL,https://api.weibo.com/2/statuses/show.json,可以获得微博详细内容。请求参数如下: 程序运行结果如下所示。 获得微博评论 在微博的详细下面可以显示该条微博的评论,使用如下URL, https://api.weibo.com/2/comments/show.json ,可以获得微博类型。请求参数如下所示。 程序运行结果如下所示。 获得微博转发 微博详细下面列出了评论和转发内容,可以使用如下URL, https://api.weibo.com/2/statuses/repost.json ,实现微博转发。请求参数如下所示。 程序运行结果如下所示。 发表评论 可以针对一条微博发表评论,发表评论的URL为,https://api.weibo.com/2/comments/create.json 。请求参数如下所示。 程序运行结果如下所示。 转发微博 也可以转发一条微博,转发微博的URL为,https://api.weibo.com/2/statuses/repost.json。请求参数如下所示。 程序运行结果如下所示。 收藏微博 可以对喜爱的微博进行收藏,收藏微博的URL为,https://api.weibo.com/2/favorites/create.json。请求参数如下所示。 程序运行结果如下所示。 31.4 项目界面结构 本项目的基础UI结构采用UITabbarController+UITableView的结构,UITabbarController将内容分为首页、消息、好友、广场和更多等分区。在各个分区中一般使用UITableView来展现内容。如下图所示。 主界面的实现步骤如下所示。 创建一个项目。 创建一个RootViewController,程序运行到该界面登陆界面。如果登陆成功,跳转到主界面。 分别创建HomeViewController、PlaceViewController、MessageViewController、FriendViewController和MoreViewController分别表示首页、消息、好友、广场和更多。在各个控制器的初始化方法中设置Tab的标题和图片。 - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Custom initialization self.tabBarItem.title = @"首页"; UIImage *originalImage = [UIImage imageNamed:@"home.png"]; self.tabBarItem.image = [ImageUtil scaleImage:originalImage andScale:2.0]; } return self; } 我们这里使用的图片稍大了一下,定义了一个工具类来实现图片的缩写,代码如下。 #import "ImageUtil.h" @implementation ImageUtil +(UIImage*)scaleImage:(UIImage*)img andScale:(float)scale{ // 缩小2倍 UIImage *scaledImage = [UIImage imageWithCGImage:[img CGImage] scale:(img.scale * scale) orientation:(img.imageOrientation)]; return scaledImage; } @end 在RootViewController的头文件中,声明UITabbarController和其他控制器实例。 #import <UIKit/UIKit.h> #import "SinaWeibo.h" #import "HomeViewController.h" #import "PlaceViewController.h" #import "MessageViewController.h" #import "FriendViewController.h" #import "MoreViewController.h" @interface RootViewController : UIViewController<SinaWeiboDelegate, SinaWeiboRequestDelegate> // 登陆方法 - (IBAction)login:(id)sender; // 跳转 - (void)forward; // UITabBarController 实例 @property (strong,nonatomic) UITabBarController *myTabController; // 首页控制器 @property (strong,nonatomic) HomeViewController *homeViewController; // 广场控制器 @property (strong,nonatomic) PlaceViewController *placeViewController; // 消息控制器 @property (strong,nonatomic) MessageViewController *messageViewController; // 好友控制器 @property (strong,nonatomic) FriendViewController *friendViewController; // 更多控制器 @property (strong,nonatomic) MoreViewController *moreViewController; @end 在实现类的viewDidLoad方法中实例化这些控制器。 - (void)viewDidLoad { [super viewDidLoad]; SinaWeibo *swb = [self getSinaWeibo]; [swb logIn]; // 实例化UITabBarController self.myTabController = [[UITabBarController alloc]init]; // 实例化主页控制器 self.homeViewController = [[HomeViewController alloc]initWithNibName:@"HomeViewController" bundle:nil]; // 主页导航控制器 UINavigationController *homeNav = [[UINavigationController alloc]initWithRootViewController:self.homeViewController]; // 微博广场控制器 self.placeViewController = [[PlaceViewController alloc]initWithNibName:@"PlaceViewController" bundle:nil]; // 广场导航 UINavigationController *placeNav = [[UINavigationController alloc]initWithRootViewController:self.placeViewController]; // 好友控制器 self.friendViewController = [[FriendViewController alloc]initWithNibName:@"FriendViewController" bundle:nil]; // 广场导航 UINavigationController *friendNav = [[UINavigationController alloc]initWithRootViewController:self.friendViewController]; // 消息控制器 self.messageViewController = [[MessageViewController alloc]initWithNibName:@"MessageViewController" bundle:nil]; // 消息导航 UINavigationController *messageNav = [[UINavigationController alloc]initWithRootViewController:self.messageViewController]; // 更多控制器 self.moreViewController = [[MoreViewController alloc]initWithNibName:@"MoreViewController" bundle:nil]; // 更多导航 UINavigationController *moreNav = [[UINavigationController alloc]initWithRootViewController:self.moreViewController]; // 为UITabBarController指定控制器集合 self.myTabController.viewControllers = @[homeNav,messageNav,friendNav,placeNav,moreNav]; } 定义跳转方法,如果登陆成功,跳转到程序主界面。 // 跳转 -(void)forward{ [self presentViewController:self.myTabController animated:YES completion:nil]; } // 登陆成功 -(void)sinaweiboDidLogIn:(SinaWeibo *)sinaweibo{ NSLog(@"%msg=%@",sinaweibo); // 保存用户数据到本地 [self storeAuthData]; // 跳转到主页 [self forward]; } 31.5 获得最新微博信息 用户登陆后,进入系统主界面,在UITabBarController的"首页"Tab中显示最新微博信息。最新微博信息使用自定义表展示。如下图所示。 获得最新微博信息的实现步骤如下: 导入ASIHttpRequest类库,在获得微博信息时,需要加载微博图片。这里我们使用之前讲述过的网络框架ASIHttpRequest。 1.1 下载ASIHttpRequest类库,地址如下: https://github.com/pokeb/asi-http-request/ 。 1.2 导入类库文件。如下图所示。 1.3 由于ASIHttpRequest不支持ARC,需要在"Targets"下的"Build Phases"做如下图所示设置。 在HomeViewController中实现SinaWeiboRequestDelegate(请求代理协议)、UITableViewDataSource(表视图数据源协议)和UITableViewDelegate(表视图代理协议),并添加需要的属性。这些属性有表视图属性、数据源属性、头像属性、图片属性、用户名称、博客内容、转发数量、评论数量、创建时间、来源、推荐数量等。 #import <UIKit/UIKit.h> #import "SinaWeibo.h" #import "HomeTableViewCell.h" #import "WeiboDetailViewController.h" @interface HomeViewController : UIViewController<SinaWeiboRequestDelegate,UITableViewDataSource,UITableViewDelegate> // 表视图属性 @property(nonatomic,strong)UITableView *tv; // 表视图数据源 @property(nonatomic,strong)NSMutableArray *tvDataSource; // 头像 @property(nonatomic,strong) IBOutlet UIImageView *profileImageView; // 图片 @property(nonatomic,strong)IBOutlet UIImageView *picImageView; // 用户名称、博客内容、转发数量、评论数量、创建时间、来源、推荐数量 @property(nonatomic,strong)IBOutlet UILabel *usernameLabel,*myTextLabel,*repostCountLabel,*commentCountLabel,*createAtLabel,*sourceLabel,*attitudeCountLabel; // 行高 @property(nonatomic)float cellHeight; @end 定义加载微博信息方法,并在viewDidLoad方法中调用。 // 加载微博信息 - (void)loadWeibo { // 获得SinaWeibo实例 SinaWeibo *sinaweibo = [self getSinaWeibo]; // 发出请求 [sinaweibo requestWithURL:@"statuses/home\_timeline.json" params:[NSMutableDictionary dictionaryWithObject:sinaweibo.accessToken forKey:@"access\_token"] httpMethod:@"GET" delegate:self]; } 加载微博信息方法调用后,微博的请求代理方法会被调用,在这样我们可以获得微博信息。 - (void)request:(SinaWeiboRequest *)request didFinishLoadingWithResult:(id)result{ // 转换为NSDictionary NSDictionary *dic = (NSDictionary*)result; // 获得key为statuses的内容 NSArray *statuses = [dic objectForKey:@"statuses"]; NSLog(@"statused=%@",statuses); // 清除数据源 [self.tvDataSource removeAllObjects]; // 设置数据源 [self.tvDataSource addObjectsFromArray:statuses]; // 重新加载表视图 [self.tv reloadData]; } 在viewDidLoad方法中,加载微博信息、实例化表视图、设置表视图代理和导航按钮。 - (void)viewDidLoad { [super viewDidLoad]; // 加载微博信息 [self loadWeibo]; // 表视图的frame CGRect tvFrame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height); // 实例化表视图 self.tv = [[UITableView alloc]initWithFrame:tvFrame]; // 设置代理 self.tv.dataSource = self; self.tv.delegate = self; // 将表视图添加到当前视图 [self.view addSubview:self.tv]; // 实例化表视图数据源 self.tvDataSource = [NSMutableArray arrayWithCapacity:5]; // 设置写微博导航按钮 UIBarButtonItem *writeWeiboBtnItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemCompose target:self action:@selector(writeWeibo)]; self.navigationItem.leftBarButtonItem = writeWeiboBtnItem; // 刷新微博导航按钮 UIBarButtonItem *refreshBtnItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(refresh)]; self.navigationItem.rightBarButtonItem = refreshBtnItem; } 自定义一个表格视图单元,HomeTableViewCell,继承UITableViewCell,实现自定义表视图行内容。 #import <UIKit/UIKit.h> @interface HomeTableViewCell : UITableViewCell @end 在获得表单元方法中,实例化UITableViewCell,并获得用户信息、头像URL,并异步加载头像。 // 标示id static NSString *cid = @"cid"; // 实例化UITableViewCell UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cid]; if (cell==nil) { cell = [[UITableViewCell alloc]initWithFrame:CGRectZero]; } // 获得当前行内容 NSDictionary *dic = [self.tvDataSource objectAtIndex:indexPath.row]; // 用户信息 NSDictionary *userDic = [dic objectForKey:@"user"]; // 1. 头像 NSString *profile\_image\_url = [userDic objectForKey:@"profile\_image\_url"]; NSURL *photoURL = [NSURL URLWithString:profile\_image\_url]; \_\_weak ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:photoURL]; // 异步加载头像信息 [request setDownloadCache:[ASIDownloadCache sharedCache]]; [request setCompletionBlock:^{ NSData *responseData = [request responseData]; UIImage *tempImage = [UIImage imageWithData:responseData]; self.profileImageView = [[UIImageView alloc]initWithImage:tempImage]; //[self.profileImageView.layer setBorderColor: [[UIColor whiteColor] CGColor]]; [[self.profileImageView layer] setBorderWidth:2.0f]; self.profileImageView.layer.cornerRadius = 5; self.profileImageView.frame = CGRectMake(5, 5, self.profileImageView.frame.size.width, self.profileImageView.frame.size.height); // NSLog(@"w=%f",self.profileImageView.frame.size.width); [cell.contentView addSubview:self.profileImageView]; [cell setNeedsLayout]; }]; [request startAsynchronous]; 获得用户名称属性,并添加到当前表视图行。 // 2. 用户名称 NSString *username = [userDic objectForKey:@"name"]; self.usernameLabel = [[UILabel alloc]initWithFrame:CGRectZero]; CGRect usernameFrame = CGRectMake(60, 5, 100, 21); self.usernameLabel.frame = usernameFrame; self.usernameLabel.text = username; self.usernameLabel.font = [UIFont systemFontOfSize:12]; [cell.contentView addSubview:self.usernameLabel]; 获得转发数和评论数并添加到当前表视图行。 // 3. 转发数 NSString *reposts\_count = [dic objectForKey:@"reposts\_count"]; self.repostCountLabel = [[UILabel alloc]initWithFrame:CGRectZero]; CGRect repostCountFrame = CGRectMake(160, 5, 60, 21); self.repostCountLabel.frame = repostCountFrame; self.repostCountLabel.text = [NSString stringWithFormat:@"转发数:%@",reposts\_count]; self.repostCountLabel.font = [UIFont systemFontOfSize:10]; [cell.contentView addSubview:self.repostCountLabel]; // 4. 评论数 NSString *comments\_count = [dic objectForKey:@"comments\_count"]; self.commentCountLabel = [[UILabel alloc]initWithFrame:CGRectZero]; CGRect commentsCountFrame = CGRectMake(220, 5, 60, 21); self.commentCountLabel.frame = commentsCountFrame; self.commentCountLabel.text = [NSString stringWithFormat:@"评论数:%@",comments\_count]; self.commentCountLabel.font = [UIFont systemFontOfSize:10]; [cell.contentView addSubview:self.commentCountLabel]; 获得微博信息,需要注意的是,微博信息是根据实际内容动态改变UILabel大小的。 // 5. 微博信息 NSString *text = [dic objectForKey:@"text"]; CGSize constraint = CGSizeMake(CELL\_CONTENT\_WIDTH - (CELL\_CONTENT\_MARGIN * 2), 20000.0f); CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:12] constrainedToSize:constraint lineBreakMode:NSLineBreakByWordWrapping]; self.myTextLabel = [[UILabel alloc]initWithFrame:CGRectZero]; self.myTextLabel.frame = CGRectMake(60, 26, size.width-20, size.height); self.myTextLabel.numberOfLines = 0; self.myTextLabel.lineBreakMode = NSLineBreakByWordWrapping; self.myTextLabel.font = [UIFont systemFontOfSize:12]; self.myTextLabel.text = text; [cell.contentView addSubview:self.myTextLabel]; 异步加载图片并根据是否有图标的情况,确定创建时间的位置。 // 6. 图片 NSString *thumbnail\_pic = [dic objectForKey:@"thumbnail\_pic"]; if (thumbnail\_pic!=nil&&[thumbnail\_pic length]>0) { NSURL *thumbnail\_picURL = [NSURL URLWithString:thumbnail\_pic]; request = [ASIHTTPRequest requestWithURL:thumbnail\_picURL]; \_\_block UIImage *tempImage; [request setDownloadCache:[ASIDownloadCache sharedCache]]; [request setCompletionBlock:^{ NSData *responseData = [request responseData]; tempImage = [UIImage imageWithData:responseData]; self.picImageView = [[UIImageView alloc]initWithImage:tempImage]; self.picImageView.frame = CGRectMake(60, size.height+30, tempImage.size.width, tempImage.size.height); [cell.contentView addSubview:self.picImageView]; // 7. 创建时间 NSString *created\_at = [dic objectForKey:@"created\_at"]; self.createAtLabel = [[UILabel alloc]initWithFrame:CGRectZero]; self.createAtLabel.frame = CGRectMake(5, size.height+self.picImageView.frame.size.height+50, 200, 21); self.createAtLabel.text = [NSString stringWithFormat:@"创建时间:%@",created\_at]; self.createAtLabel.font = [UIFont systemFontOfSize:10]; [cell.contentView addSubview:self.createAtLabel]; self.cellHeight = size.height+self.picImageView.frame.size.height+60; [cell setNeedsLayout]; }]; [request startAsynchronous]; }else{ // 7. 创建时间 NSString *created\_at = [dic objectForKey:@"created\_at"]; self.createAtLabel = [[UILabel alloc]initWithFrame:CGRectZero]; self.createAtLabel.frame = CGRectMake(5, size.height+50, 200, 21); self.createAtLabel.text = [NSString stringWithFormat:@"创建时间:%@",created\_at]; self.createAtLabel.font = [UIFont systemFontOfSize:10]; [cell.contentView addSubview:self.createAtLabel]; self.cellHeight = size.height+self.picImageView.frame.size.height+60; [cell setNeedsLayout]; } 31.6 发布微博 在需要的时候,我们也可以通过发送微博来分享自己的新鲜事。发送微博需要想服务器提交的信息是access_token和微博内容status,另外,用户需要是登陆状态。实现发送微博的步骤如下所示。 创建一个视图控制器PostWeiboViewController,实现请求代理协议SinaWeiboRequestDelegate,并定义一个UITextView属性来填写微博内容。 #import <UIKit/UIKit.h> #import "SinaWeiboRequest.h" @interface PostWeiboViewController : UIViewController<SinaWeiboRequestDelegate> // 微博内容 @property (strong, nonatomic) IBOutlet UITextView *postTextView; @end viewDidLoad方法中设置写微博和取消写微博的导航按钮,并使UITextView获得焦点,显示键盘。 - (void)viewDidLoad { [super viewDidLoad]; // 设置取消微博导航按钮 UIBarButtonItem *cancelBtnItem = [[UIBarButtonItem alloc]initWithTitle:@"取消" style:UIBarButtonItemStylePlain target:self action:@selector(cancel)]; self.navigationItem.leftBarButtonItem = cancelBtnItem; // 设置写微博导航按钮 UIBarButtonItem *postBtnItem = [[UIBarButtonItem alloc]initWithTitle:@"发送" style:UIBarButtonItemStylePlain target:self action:@selector(post)]; self.navigationItem.rightBarButtonItem = postBtnItem; // 是UITextView获得焦点 [self.postTextView becomeFirstResponder]; } 定义一个post方法发送微博,设置提交给服务器的信息,并设置提交方法为POST。 // 发送微博 - (void)post{ SinaWeibo *sinaweibo = [self getSinaWeibo]; NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:5]; [params setObject:sinaweibo.accessToken forKey:@"access\_token"]; [params setObject:self.postTextView.text forKey:@"status"]; [sinaweibo requestWithURL:@"statuses/update.json" params:params httpMethod:@"POST" delegate:self]; } 实现请求代理方法,获得请求返回结果。 // 实现请求代理方法,获得返回结果 - (void)request:(SinaWeiboRequest *)request didFinishLoadingWithResult:(id)result{ // NSLog(@"result=%@",result); NSDictionary *dic = (NSDictionary*)result; NSArray *statuses = [dic objectForKey:@"statuses"]; NSLog(@"statused=%@",statuses); } 程序运行结果如下所示。 31.7 微博详细 选中微博列表中的一个,将跳转到微博详细信息。详细信息会显示发微博的用户信息,包括头像、用户名称,以及微博详细内容和图片。另外,还有针对该微博的评论和转发。如下图所示。 微博详细的实现步骤如下: 定义一个微博详细视图控制器类WeiboDetailViewController,该类实现请求代理协议SinaWeiboRequestDelegate、表视图数据源协议UITableViewDataSource、表视图代理协议UITableViewDelegate和动作列表协议UIActionSheetDelegate。 #import <UIKit/UIKit.h> #import "SinaWeiboRequest.h" @interface WeiboDetailViewController : UIViewController<SinaWeiboRequestDelegate,UITableViewDataSource,UITableViewDelegate,UIActionSheetDelegate> 添加用户名称、博客内容、头像和图片等属性。 // 头像 @property(nonatomic,strong) IBOutlet UIImageView *profileImageView; // 图片 @property(nonatomic,strong)IBOutlet UIImageView *picImageView; // 用户名称、博客内容 @property(nonatomic,strong)IBOutlet UILabel *usernameLabel,*myTextLabel; 在viewDidLoad方法中获得用户信息,包括头像和用户名称,以及微博信息。 // 用户信息 NSDictionary *userDic = [self.weiboContentDic objectForKey:@"user"]; // 1. 头像 NSString *profile\_image\_url = [userDic objectForKey:@"profile\_image\_url"]; NSURL *photoURL = [NSURL URLWithString:profile\_image\_url]; \_\_weak ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:photoURL]; [request setDownloadCache:[ASIDownloadCache sharedCache]]; [request setCompletionBlock:^{ NSData *responseData = [request responseData]; UIImage *tempImage = [UIImage imageWithData:responseData]; self.profileImageView = [[UIImageView alloc]initWithImage:tempImage]; //[self.profileImageView.layer setBorderColor: [[UIColor whiteColor] CGColor]]; [[self.profileImageView layer] setBorderWidth:2.0f]; self.profileImageView.layer.cornerRadius = 5; self.profileImageView.frame = CGRectMake(5, 5, self.profileImageView.frame.size.width, self.profileImageView.frame.size.height); // NSLog(@"w=%f",self.profileImageView.frame.size.width); [self.view addSubview:self.profileImageView]; [self.view setNeedsLayout]; }]; [request startAsynchronous]; // 2. 用户名称 NSString *username = [userDic objectForKey:@"name"]; self.usernameLabel = [[UILabel alloc]initWithFrame:CGRectZero]; CGRect usernameFrame = CGRectMake(60, 5, 100, 21); self.usernameLabel.frame = usernameFrame; self.usernameLabel.text = username; self.usernameLabel.font = [UIFont systemFontOfSize:12]; [self.view addSubview:self.usernameLabel]; // 3. 分割线 CGRect lintViewFrame = CGRectMake(0, 70, 320, 1); UIView *lineView = [[UIView alloc]initWithFrame:lintViewFrame]; lineView.backgroundColor = [UIColor blackColor]; [self.view addSubview:lineView]; // 4. 微博信息 NSString *text = [self.weiboContentDic objectForKey:@"text"]; CGSize constraint = CGSizeMake(320 - (10 * 2), 20000.0f); CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:12] constrainedToSize:constraint lineBreakMode:NSLineBreakByWordWrapping]; self.myTextLabel = [[UILabel alloc]initWithFrame:CGRectZero]; self.myTextLabel.frame = CGRectMake(10, 75, size.width-20, size.height); self.myTextLabel.numberOfLines = 0; self.myTextLabel.lineBreakMode = NSLineBreakByWordWrapping; self.myTextLabel.font = [UIFont systemFontOfSize:12]; self.myTextLabel.text = text; [self.view addSubview:self.myTextLabel]; 以上是微博详细的部分内容,有关微博评论和微博转发类别将在后续小结中讲述。 31.8 获得微博评论和转发 在微博详细中来显示微博评论列表和微博转发列表,本节将讲述微博评论列表的实现。实现步骤如下: 在上一节的基础上,在微博详细头文件中声明评论和转发表视图和数据源以及其他属性。 // 评论表和转发表的容器视图 @property(nonatomic,strong)UIView *containerView; // 评论表 @property(nonatomic,strong) UITableView *commentTv; // 转发表 @property(nonatomic,strong) UITableView *repostTv; // 当前表 @property(nonatomic,strong) UITableView *currentTv; // 评论数据源 @property(nonatomic,strong) NSMutableArray *commentDataSource; // 转发数据源 @property(nonatomic,strong) NSMutableArray *repostDataSource; // 当前微博内容 @property(nonatomic,strong)NSDictionary *weiboContentDic; // 当前微博id @property(nonatomic,strong)NSString *sid; 在viewDidLoad方法中初始化导航按钮、表视图和表视图数据源,并设置代理。 // 评论表和转发表的容器视图 @property(nonatomic,strong)UIView *containerView; // 评论表 @property(nonatomic,strong) UITableView *commentTv; // 转发表 @property(nonatomic,strong) UITableView *repostTv; // 当前表 @property(nonatomic,strong) UITableView *currentTv; // 评论数据源 @property(nonatomic,strong) NSMutableArray *commentDataSource; // 转发数据源 @property(nonatomic,strong) NSMutableArray *repostDataSource; // 当前微博内容 @property(nonatomic,strong)NSDictionary *weiboContentDic; // 当前微博id @property(nonatomic,strong)NSString *sid; 加载微博图片,并根据图片位置动态设置评论和转发列表的位置,并加载评论和转发数据。 // 5. 图片 NSString *thumbnail\_pic = [self.weiboContentDic objectForKey:@"thumbnail\_pic"]; if (thumbnail\_pic!=nil&&[thumbnail\_pic length]>0) { NSURL *thumbnail\_picURL = [NSURL URLWithString:thumbnail\_pic]; request = [ASIHTTPRequest requestWithURL:thumbnail\_picURL]; \_\_block UIImage *tempImage; [request setDownloadCache:[ASIDownloadCache sharedCache]]; [request setCompletionBlock:^{ NSData *responseData = [request responseData]; tempImage = [UIImage imageWithData:responseData]; self.picImageView = [[UIImageView alloc]initWithImage:tempImage]; self.picImageView.frame = CGRectMake(60, size.height+80, tempImage.size.width, tempImage.size.height); [self.view addSubview:self.picImageView]; // 6. 转发和评论 根据文字和图片高度计算分段控件的位置 CGRect scFrame = CGRectMake(0, size.height+self.picImageView.frame.size.height+80, 320, 44); NSArray *items = @[@"转发",@"评论"]; sc = [[SDSegmentedControl alloc]initWithItems:items]; sc.backgroundColor = [UIColor clearColor]; sc.frame = scFrame; [self.view addSubview:sc]; [sc addTarget:self action:@selector(change:) forControlEvents:UIControlEventValueChanged]; // 容器视图 CGRect containerFrame = CGRectMake(0, size.height+self.picImageView.frame.size.height+80+44, 320, 300); self.containerView = [[UIView alloc]initWithFrame:containerFrame]; // self.containerView.backgroundColor = [UIColor redColor]; [self.view addSubview:self.containerView]; // 评论表 CGRect commentTvFrame = CGRectMake(0, 0, 320, 300); self.commentTv.frame = commentTvFrame; //[self.containerView addSubview:self.commentTv]; // 转发表 CGRect repostTvFrame = CGRectMake(0, 0, 320, 300); self.commentTv.frame = repostTvFrame; //[self.containerView addSubview:self.commentTv]; [self.containerView addSubview:self.repostTv]; }]; [request startAsynchronous]; }else{ // 如果没有图片,根据文字高度计算分段控件的位置 CGRect scFrame = CGRectMake(0, size.height+80, 320, 44); NSArray *items = @[@"转发",@"评论"]; sc = [[SDSegmentedControl alloc]initWithItems:items]; sc.backgroundColor = [UIColor clearColor]; sc.frame = scFrame; [self.view addSubview:sc]; [sc addTarget:self action:@selector(change:) forControlEvents:UIControlEventValueChanged]; // 容器视图 CGRect containerFrame = CGRectMake(0, size.height+80+44, 320, 300); self.containerView = [[UIView alloc]initWithFrame:containerFrame]; self.containerView.backgroundColor = [UIColor redColor]; [self.view addSubview:self.containerView]; // 评论表 CGRect commentTvFrame = CGRectMake(0, 0, 320, 300); self.commentTv.frame = commentTvFrame; //[self.containerView addSubview:self.commentTv]; // 转发表 CGRect repostTvFrame = CGRectMake(0, 0, 320, 300); self.repostTv.frame = repostTvFrame; [self.containerView addSubview:self.repostTv]; } // 6. 加载转发和评论数据 NSString *sid = [self.weiboContentDic objectForKey:@"idstr"]; NSLog(@"sid=%@",sid); [self loadComment:sid]; [self loadRepost:sid]; // 设置当前微博id self.sid = sid; 这里我们使用了一个开源的分段组件,下载地址是,https://github.com/rs/SDSegmentedControl](https://github.com/rs/SDSegmentedControl。 根据当前表的实例,设置表的行数。 // 表的行数 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ // 评论表 if ([tableView isEqual:self.commentTv]) { NSLog(@"count=%d",[self.commentDataSource count]); return [self.commentDataSource count]; } // 转发表 if ([tableView isEqual:self.repostTv]) { NSLog(@"count=%d",[self.repostDataSource count]); return [self.repostDataSource count]; } return 0; } 根据当前表实例,设置表的行单元格数据。 // 表行 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ // 评论行 if ([tableView isEqual:self.commentTv]) { static NSString *cid = @"cid"; UITableViewCell *cCell = [tableView dequeueReusableCellWithIdentifier:cid]; if (cCell==nil) { cCell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cid]; } NSDictionary *dic = [self.commentDataSource objectAtIndex:indexPath.row]; NSDictionary *userDic = [dic objectForKey:@"user"]; // 用户名称 NSString *name = [userDic objectForKey:@"name"]; cCell.textLabel.text = name; cCell.textLabel.font = [UIFont systemFontOfSize:10]; // 用户头像 NSString *profile\_image\_urlStr = [userDic objectForKey:@"profile\_image\_url"]; NSURL *profile\_image\_url = [NSURL URLWithString:profile\_image\_urlStr]; \_\_weak ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:profile\_image\_url]; \_\_block UIImage *tempImage; [request setDownloadCache:[ASIDownloadCache sharedCache]]; [request setCompletionBlock:^{ NSData *responseData = [request responseData]; tempImage = [UIImage imageWithData:responseData]; cCell.imageView.image = tempImage; [cCell setNeedsLayout]; }]; [request startAsynchronous]; //评论内容 NSString *text = [dic objectForKey:@"text"]; cCell.detailTextLabel.text = text; cCell.detailTextLabel.font = [UIFont systemFontOfSize:8]; return cCell; } // 转发行 if ([tableView isEqual:self.repostTv]) { static NSString *rid = @"rid"; UITableViewCell *rCell = [tableView dequeueReusableCellWithIdentifier:rid]; if (rCell==nil) { rCell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:rid]; } NSDictionary *dic = [self.repostDataSource objectAtIndex:indexPath.row]; NSDictionary *userDic = [dic objectForKey:@"user"]; // 用户名称 NSString *name = [userDic objectForKey:@"name"]; rCell.textLabel.text = name; rCell.textLabel.font = [UIFont systemFontOfSize:10]; // 用户头像 NSString *profile\_image\_urlStr = [userDic objectForKey:@"profile\_image\_url"]; NSURL *profile\_image\_url = [NSURL URLWithString:profile\_image\_urlStr]; \_\_weak ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:profile\_image\_url]; \_\_block UIImage *tempImage; [request setDownloadCache:[ASIDownloadCache sharedCache]]; [request setCompletionBlock:^{ NSData *responseData = [request responseData]; tempImage = [UIImage imageWithData:responseData]; rCell.imageView.image = tempImage; [rCell setNeedsLayout]; }]; [request startAsynchronous]; //评论内容 NSString *text = [dic objectForKey:@"text"]; rCell.detailTextLabel.text = text; rCell.detailTextLabel.font = [UIFont systemFontOfSize:8]; return rCell; } return nil; } 实现分段组件事件方法,实现转发和评论之间的相互切换。 // 分段切换事件 -(void)change:(UISegmentedControl*)sender{ int index = sender.selectedSegmentIndex; [self.currentTv removeFromSuperview]; switch (index) { case 0: [self.containerView addSubview:self.repostTv]; self.currentTv = self.repostTv; break; case 1: [self.containerView addSubview:self.commentTv]; self.currentTv = self.commentTv; break; default: break; } } 下面是加载评论和转发方法实现。 // 加载评论 - (void)loadComment:(NSString*)sid { SinaWeibo *sinaweibo = [self getSinaWeibo]; NSMutableDictionary *param = [NSMutableDictionary dictionaryWithCapacity:3]; [param setObject:sinaweibo.accessToken forKey:@"access\_token"]; [param setObject:sid forKey:@"id"]; [sinaweibo requestWithURL:@"comments/show.json" params:param httpMethod:@"GET" delegate:self]; } // 加载转发 - (void)loadRepost:(NSString*)sid { SinaWeibo *sinaweibo = [self getSinaWeibo]; NSMutableDictionary *param = [NSMutableDictionary dictionaryWithCapacity:3]; [param setObject:sinaweibo.accessToken forKey:@"access\_token"]; [param setObject:sid forKey:@"id"]; [sinaweibo requestWithURL:@"statuses/repost\_timeline.json" params:param httpMethod:@"GET" delegate:self]; } 获得请求结果,根据URL结尾判断是那个请求,包括评论、转发和收藏。 // 获得请求结果 - (void)request:(SinaWeiboRequest *)request didFinishLoadingWithResult:(id)result{ // 判断是评论还是转发 if ([request.url hasSuffix:@"comments/show.json"]){ NSDictionary *dic = (NSDictionary*)result; NSArray *commnts = [dic objectForKey:@"comments"]; NSLog(@"comments=%@",commnts); [self.commentDataSource removeAllObjects]; [self.commentDataSource addObjectsFromArray:commnts]; [self.commentTv reloadData]; // 转发 }else if([request.url hasSuffix:@"statuses/repost\_timeline"]){ NSDictionary *dic = (NSDictionary*)result; NSArray *reposts = [dic objectForKey:@"reposts"]; NSLog(@"reposts=%@",reposts); [self.repostDataSource removeAllObjects]; [self.repostDataSource addObjectsFromArray:reposts]; [self.repostTv reloadData]; // 收藏 }else if([request.url hasSuffix:@"favorites/create.json"]){ NSDictionary *dic = (NSDictionary*)result; NSDictionary *status = [dic objectForKey:@"status"]; NSString *favor = [status objectForKey:@"favorited"]; if ([favor boolValue]) { [SGInfoAlert showInfo:@"收藏成功!" bgColor:nil inView:self.view vertical:100]; // NSLog(@"收藏成功!"); }else{ // NSLog(@"收藏失败!"); [SGInfoAlert showInfo:@"收藏失败!" bgColor:nil inView:self.view vertical:100]; } }else{ } } 程序运行结果如下所示。 31.9 发表评论 可以针对某条微博发表评论,评论、转发和收藏通过UIActionSheet提供接口,点击评论跳转到评论界面,输入评论内容,点击导航栏上的发送按钮可以发送评论。如下图所示。 发表评论的实现步骤如下: 实现UIActionSheet,点击微博详细的右侧导航按钮,可以弹出UIActionSheet列表,选择评论按钮跳转到评论界面。 // 显示转发、评论、收藏弹出框 -(void)showActionSheet{ UIActionSheet *actions = [[UIActionSheet alloc]initWithTitle:nil delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:@"转发" otherButtonTitles:@"评论", @"收藏",nil]; [actions showInView:self.view]; } 实现UIActionSheetDelegate的协议方法。 - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{ NSString *title = [actionSheet buttonTitleAtIndex:buttonIndex]; if ([title isEqualToString:@"转发"]) { [self forwardToRepost]; } if ([title isEqualToString:@"评论"]) { [self forwardToComment]; } if ([title isEqualToString:@"收藏"]) { [self doFavor]; } } 定义一个评论视图控制器CommentViewController,实现SinaWeiboRequestDelegate请求代理协议,并设置评论内容输入框属性和微博id属性。 #import <UIKit/UIKit.h> #import "SinaWeibo.h" #import "SinaWeiboRequest.h" @interface CommentViewController : UIViewController<SinaWeiboRequestDelegate> // 评论内容输入框 @property (strong, nonatomic) IBOutlet UITextView *commentTextView; // 当前微博id @property(strong,nonatomic) NSString *sid; @end 在viewDidLoad方法中添加导航按钮,并使输入框获得焦点。 - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view from its nib. UIBarButtonItem *right = [[UIBarButtonItem alloc]initWithTitle:@"评论" style:UIBarButtonItemStyleBordered target:self action:@selector(comment)]; self.navigationItem.rightBarButtonItem = right; [self.commentTextView becomeFirstResponder]; } 点击评论按钮发出评论,并获得评论响应结果。 // 发出评论 - (void)comment { SinaWeibo *sinaweibo = [self getSinaWeibo]; NSMutableDictionary *param = [NSMutableDictionary dictionaryWithCapacity:3]; [param setObject:sinaweibo.accessToken forKey:@"access\_token"]; [param setObject:self.sid forKey:@"id"]; [param setObject:self.commentTextView.text forKey:@"comment"]; [sinaweibo requestWithURL:@"comments/create.json" params:param httpMethod:@"POST" delegate:self]; } // 获得请求结果 - (void)request:(SinaWeiboRequest *)request didFinishLoadingWithResult:(id)result{ NSDictionary *dic = (NSDictionary*)result; // NSArray *commnts = [dic objectForKey:@"comments"]; NSLog(@"dic=%@",dic); } 31.10 转发微博 转发微博和评论类似,使用UIActionSheet提供接口,点击转发跳转到转发界面,输入转发信息转发微博。 转发微博实现步骤如下: 定义一个转发微博的视图控制器类RepostViewController,并定义转发内容属性和微博id属性。 #import <UIKit/UIKit.h> #import "SinaWeibo.h" #import "SinaWeiboRequest.h" // 实现请求代理 @interface RepostViewController : UIViewController<SinaWeiboRequestDelegate> // 转发内容文本视图 @property (strong, nonatomic) IBOutlet UITextView *repostTextView; // 微博id @property(strong,nonatomic) NSString *sid; @end 在viewDidLoad方法中添加导航按钮并使UITextView获得焦点。 - (void)viewDidLoad { [super viewDidLoad]; // 转发导航按钮 UIBarButtonItem *right = [[UIBarButtonItem alloc]initWithTitle:@"转发" style:UIBarButtonItemStyleBordered target:self action:@selector(repost)]; self.navigationItem.rightBarButtonItem = right; // 内容框获得焦点 [self.repostTextView becomeFirstResponder]; } 点击转发按钮转发微博,并获得响应结果。 // 转发 - (void)repost { SinaWeibo *sinaweibo = [self getSinaWeibo]; NSMutableDictionary *param = [NSMutableDictionary dictionaryWithCapacity:3]; [param setObject:sinaweibo.accessToken forKey:@"access\_token"]; [param setObject:self.sid forKey:@"id"]; [param setObject:self.repostTextView.text forKey:@"status"]; [sinaweibo requestWithURL:@"statuses/repost.json" params:param httpMethod:@"POST" delegate:self]; } // 获得请求结果 - (void)request:(SinaWeiboRequest *)request didFinishLoadingWithResult:(id)result{ NSDictionary *dic = (NSDictionary*)result; // NSArray *commnts = [dic objectForKey:@"comments"]; NSLog(@"dic=%@",dic); } 31.11 收藏微博 可以将喜欢的微博收藏,使用UIActionSheet提供操作接口,点击收藏按钮实现收藏。收藏方法代码如下: // 收藏 -(void)doFavor{ SinaWeibo *sinaweibo = [self getSinaWeibo]; NSMutableDictionary *param = [NSMutableDictionary dictionaryWithCapacity:3]; [param setObject:sinaweibo.accessToken forKey:@"access\_token"]; [param setObject:self.sid forKey:@"id"]; [sinaweibo requestWithURL:@"favorites/create.json" params:param httpMethod:@"POST" delegate:self]; } 根据请求返回的结果判断收藏成功还是失败。 // 收藏 }else if([request.url hasSuffix:@"favorites/create.json"]){ NSDictionary *dic = (NSDictionary*)result; NSDictionary *status = [dic objectForKey:@"status"]; NSString *favor = [status objectForKey:@"favorited"]; if ([favor boolValue]) { [SGInfoAlert showInfo:@"收藏成功!" bgColor:nil inView:self.view vertical:100]; // NSLog(@"收藏成功!"); }else{ // NSLog(@"收藏失败!"); [SGInfoAlert showInfo:@"收藏失败!" bgColor:nil inView:self.view vertical:100]; } } 这里我们使用了一个开源的弹出框组件SGInfoAlert,下载地址是,https://github.com/sagiwei/SGInfoAlert](https://github.com/sagiwei/SGInfoAlert,示例程序运行结果如下所示。 到这里,微博项目就全部结束了。其实新浪微博的功能还有很多,但是操作流程都是类似的,即发出请求,获得响应结果,并将结果显示在界面上。 在AppStore掘金