山寨新浪微博客户端与新浪微博API调用的总结

  这次是我第一次写的项目总结,虽然这只是一个小项目,但确实是获益良多。虽然说是独立完成,但其实在做的过程中,也有和大家交流了很多意见。尽管如此,我对这个尚不能算写好的项目,还是有非常多的不满意。不过碍于能力有限,不满意的地方也就先将就将就。日后有兴趣的话,肯定会再次改进,完善得更好。

  项目的主要内容就是用Xcode写出一个具有基本功能的iPhone新浪微博客户端,其中必不可少地需要调用新浪微博应用的API,途中遇到的“难题”也不少,不过大部分都算解决了。

 

 

一、获取用户Access Token

  一开始的第一个麻烦,由于微博使用OAuth2.0的授权方式,主要流程是去指定的网页,在用户输入账号和密码授权后,获取回调网页地址上的Access Token。往后对API的调用,全部都是通过Access Token来进行身份认证,一个Access Token对应一个应用和一个用户,都是唯一的。具体新建微博应用的过程,我也不多说,主要就对调用API获取Access Token进行说明。

http://open.weibo.com/wiki/Oauth2 这里已经有基本的说明,我主要调用的是“Javascript Client的验证授权”,因为返回的地址里已经包含了Access Token,不需要二次调用。我在UIViewController上面拉了一个UIWebView,UIWebView用了Outlet。

代码如下:

1 - (void) viewDidAppear:(BOOL)animated
2 {
3     NSString *urlStr = [[NSString alloc] initWithFormat:@"%@?client_id=%@&redirect_uri=%@&response_type=token&display=%@", AuthorizeUrl, AppKey, RedirectUri, DisplayMode];
4     
5     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlStr]];
6     [accessWebView loadRequest:request];
7     accessWebView.delegate = self;
8 }

 其中AuthorizeUrl

#define AuthorizeUrl @"https://api.weibo.com/oauth2/authorize"

 

 

AppKey可以在“应用信息”找到,不过RedirectUri必须和自己在应用中填写的网址一样,否则会出错。

 

然后,等用户授权完,就要判断网址是否有我们需要的Access Token,有的话再进行下一步。这里就要用到<UIWebViewDelegate>协议,来使用方法webViewDidFinishLoad,

 1 - (void)webViewDidFinishLoad:(UIWebView *)webView
 2 {
 3     NSURL *url = [webView.request URL];
 4     
 5     if ([url.lastPathComponent isEqualToString:CmpUrl]) {
 6         tokenData *tmp = [self.userToken initWithUrl:url];
 7         if (tmp != nil) {
 8             self.userToken = tmp;
 9             [self.userToken saveToken];
10             [self performSegueWithIdentifier:@"FirstView" sender:nil];
11         } else {
12             [webView goBack];
13         }
14     }

我在这里封装了几个方法,saveToken是用plist进行保存token的方法,用于后续登陆免输密码,这里先跳过。主要是[url.lastPathComponent isEqualToString:CmpUrl],由于请求用户授权时,加载完成后也会调用webViewDidFinishLoad,所以必须判断Access Token出现了没有。如果去到了回调网址“https://api.weibo.com/oauth2/default.html”,则url.lastPathComponent为“default.html”。根据这点再提取Access Token,具体方法就是用NSString的方法去提取URL里Access Token的位置。不过我也做了一个错误处理,以防用户按了“取消”后,程序就“无处可点”。所以如果页面网址类似“https://api.weibo.com/oauth2/default.html#error_uri=%2Foauth2%2Fauthorize”,就重新返回授权的页面。(好吧,我承认我有点猥琐)

具体initWithUrl的方法

 1 - (id) initWithUrl:(NSURL *)url
 2 {
 3     if (self = [super init]) {
 4         NSString *urlStr = [url absoluteString];
 5         NSArray *strarr = [urlStr componentsSeparatedByString:@"&"];
 6         
 7         if ([[strarr objectAtIndex:0] isEqualToString:ErrUrl]) {
 8             NSLog(@"Error!!\n");
 9             return nil;
10         } else {
11             NSString *tmpToken = [[strarr objectAtIndex:0] substringFromIndex:55];
12             NSString *tmpUid = [[strarr objectAtIndex:3] substringFromIndex:4];
13             
14             NSDictionary *tmpDic = [weiboUrl weiboAPIUserShowWithToken:tmpToken andUid:tmpUid OrScreenName:nil];
15             
16             self.token = tmpToken;
17             self.uid = tmpUid;
18             self.userName = [tmpDic objectForKey:@"name"];
19         }
20     }
21     
22     return self;
23 }

 

新浪微博的API又被我封装到另一个类weiboUrl中,其中weiboAPIUserShowWithToken: andUid: OrScreenName:就是返回用户个人资料字典的一个类方法。

 1 + (NSDictionary *)weiboAPIUserShowWithToken:(NSString *)token andUid:(NSString *)uid OrScreenName:(NSString *)screenname
 2 {
 3     NSError *error;
 4     NSString *urlstr;
 5     if (!screenname)
 6        urlstr = [NSString stringWithFormat:@"%@?access_token=%@&uid=%@", UserShow, token, uid];
 7     else
 8         urlstr = [NSString stringWithFormat:@"%@?access_token=%@&screen_name=%@", UserShow, token, [screenname stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
 9     
10     NSURL *url = [[NSURL alloc]initWithString:urlstr];
11     NSData* data = [NSData dataWithContentsOfURL: url];
12     NSDictionary *returnDic = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
13     
14     return returnDic;
15 }

由于新浪微博返回的都是JSON数据,所以可以直接用NSJSONSerialization的方法进行编码。

 

 

二、调用API和处理数据

微博API众多,如果每次都要打一次API,查错的时候必定非常麻烦。所以我就封装了几个常用的API。

 1 //以下是封装WeiboAPI的方法
 2 
 3 //根据用户ID获取用户信息,返回NSDictionary类型的数据
 4 + (NSDictionary *) weiboAPIUserShowWithToken:(NSString *)token andUid:(NSString *)uid OrScreenName:(NSString *)screenname;
 5 
 6 //获取当前登录用户及其所关注用户的最新微博,返回其微博的NSMutableArray数组,可指定参数since_id, max_id, count,对应即比since_id时间晚的微博,
 7 //小于或等于max_id的微博,单页返回的记录条数
 8 + (NSMutableArray *) weiboAPIHomeTimeLineWithToken:(NSString *)token andSinceid:(NSString *)sinceid andMaxid:(NSString *)maxid andCount:(NSString *)count;
 9 
10 //测试status的汉字数是否超过140个
11 + (BOOL) testStatusLengthWithString:(NSString *)sourceString;
12 
13 //发布一条新微博
14 + (NSDictionary *) weiboAPIUpdateStatusesWithToken:(NSString *)token andStatus:(NSString *)status andLat:(float)latitude andLong:(float)longtitude;
15 
16 //对一条微博进行评论
17 + (NSDictionary *) weiboAPICreateCommentsWithToken:(NSString *)token andComment:(NSString *)comment andID:(NSString *)ID andCommentori:(int)comment_ori;
18 
19 //转发一条微博
20 + (NSDictionary *) weiboAPIRepostStatusesWithToken:(NSString *)token andStatus:(NSString *)status andID:(NSString *)ID andIscomment:(int)is_comment;
21 
22 //添加一条微博到收藏里
23 + (NSDictionary *) weiboAPICreateFavouritesWithToken:(NSString *)token andID:(NSString *)ID;
24 
25 //根据微博ID返回某条微博的评论列表
26 + (NSMutableArray *) weiboAPICommentsShowWithToken:(NSString *)token andSinceid:(NSString *)sinceid andMaxid:(NSString *)maxid andCount:(NSString *)count andID:(NSString *)ID;
27 
28 //获取当前登录用户所接收到的评论列表
29 + (NSMutableArray *) weiboAPICommentsToMeWithToken:(NSString *)token andSinceid:(NSString *)sinceid andMaxid:(NSString *)maxid andCount:(NSString *)count;
30 
31 //获取最新的提到登录用户的微博列表,即@我的微博
32 + (NSMutableArray *) weiboAPIStatusesMentionsWithToken:(NSString *)token andSinceid:(NSString *)sinceid andMaxid:(NSString *)maxid andCount:(NSString *)count;
33 
34 //回复一条评论
35 + (NSDictionary *) weiboAPICommentsReplyWithToken:(NSString *)token andComment:(NSString *)comment andID:(NSString *)ID andCID:(NSString *)CID;
36 
37 //获取当前登录用户的收藏列表
38 + (NSMutableArray *) weiboAPIFavouritesWithToken:(NSString *)token andCount:(int)count andPage:(int)page;

 

 

不过,这里不得说一下,说来惭愧,我有很多的API都未严格按照新浪给出的类型封装,很多都直接使用NSString类型。由于赶时间所以这么做,但其实严格按照类型封装会更好,我后面还会提到为什么这样说。

封装好API以后,每次就不用复制、粘贴一大段新浪API,而且可读性相对好了点。不过由于,这类API我都只是对数据进行了一点“粗加工”,使用起来还不方便(因为返回的要么是NSDictionary类型,要么就是NSMutableArray,里面也是NSDictionary)。所以还要再将数据再封装一次,说到这里,我又惭愧了,因为我将评论和微博等都全封装成同一个类型。如果科学点,其实也是要分开的。我封装的类大概有这些属性

 1 @property (strong, nonatomic) NSString *ID;
 2 @property (strong, nonatomic) NSString *SID;
 3 @property (strong, nonatomic) NSString *text;
 4 @property (strong, nonatomic) NSString *source;
 5 @property (strong, nonatomic) NSString *time;
 6 @property (strong, nonatomic) NSString *userName;
 7 
 8 @property (strong, nonatomic) NSURL *originalPicURL;
 9 @property (strong, nonatomic) NSURL *bmiddlePicURL;
10 @property (strong, nonatomic) NSURL *profileImageURL;
11 @property (strong, nonatomic) NSURL *largeImageURL;
12 
13 @property (strong, nonatomic) UIImage *profileImage;
14 @property (strong, nonatomic) UIImage *largeImage;
15 @property (strong, nonatomic) UIImage *bmiddleImage;
16 
17 @property (strong, nonatomic) NSString *retweetName;
18 @property (strong, nonatomic) NSString *retweetText;

 

里面还封装了用字典初始化属性各个值的方法

+ (weiboCellData *) weiboCellDataWithSourceDictionary: (NSDictionary *)sourceDictionary

最后再新建一个NSMutableArray的类别来生成全部weiCellData类型的数组,这个就不多说了。

下一步在首页的ControllerviewDidLoad方法

    self.cellsDataArray = [NSMutableArray weiboCellDataArrayWithSourceArray:[weiboUrl weiboAPIHomeTimeLineWithToken:self.userToken.token andSinceid:@"0" andMaxid:@"0" andCount:@"50"]];

 

这样,tableView所有Cell的内容就都已经有了,接下来就是处理Cell内容的排列。由于微博博文长短不一,所以高度必须要为每一个Cell进行特别的设置。

 1  static NSString *CellIdentifier = @"weiboCell";
 2     
 3     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
 4     if (cell == nil) {
 5         cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
 6     }
 7     
 8     
 9     weiboCellData *myCell = [self.cellsDataArray objectAtIndex:indexPath.row];
10     UILabel *cellName = (UILabel *)[cell viewWithTag:1];
11     UILabel *cellText = (UILabel *)[cell viewWithTag:2];
12     UILabel *cellRetweet = (UILabel *)[cell viewWithTag:3];
13     UIImageView *cellImage = (UIImageView *)[cell viewWithTag:4];
14     
15     
16     cellName.text = myCell.userName;
17     cellText.text = myCell.text;
18     
19     //设置原文的label高度
20     CGSize constraint = CGSizeMake(cellText.frame.size.width, 20000.0f);
21     CGSize textSize = [cellText.text sizeWithFont:[UIFont systemFontOfSize:14.0] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
22     [cellText setFrame:CGRectMake(cellText.frame.origin.x, cellText.frame.origin.y, cellText.frame.size.width, textSize.height)];
23     
24     
25     if (myCell.retweetText != nil) {
26         cellRetweet.text = [NSString stringWithFormat:@"%@:%@", myCell.retweetName, myCell.retweetText];
27         //设置转发文的高度
28         constraint = CGSizeMake(cellRetweet.frame.size.width, 20000.0f);
29         CGSize retweetSize = [cellRetweet.text sizeWithFont:[UIFont systemFontOfSize:14.0] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
30         [cellRetweet setFrame:CGRectMake(cellRetweet.frame.origin.x, cellText.frame.origin.y + textSize.height + frameGap, cellRetweet.frame.size.width, retweetSize.height)];
31     } else
32         [cellRetweet setFrame:CGRectMake(cellRetweet.frame.origin.x, cellText.frame.origin.y + textSize.height + frameGap, cellRetweet.frame.size.width, 0)];

其中frameGap = 5.0,textSize主要为获取一个系统字体为14的合适label大小,再通过setFrame,就可以设置好原文的label。下面转发文的原理基本一样,不过需要判断一下是否有转发和修改一下origin。另外还要重载tableView的一个方法,

 1 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
 2 {
 3     static NSString *CellIdentifier = @"weiboCell";
 4     
 5     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
 6     if (cell == nil) {
 7         cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
 8     }
 9     
10     UILabel *cellText = (UILabel *)[cell viewWithTag:2];
11     UILabel *cellRetweet = (UILabel *)[cell viewWithTag:3];
12     weiboCellData *myCell = [self.cellsDataArray objectAtIndex:indexPath.row];
13     
14     CGSize constraint = CGSizeMake(cellText.frame.size.width, 20000.0f);
15     CGSize textSize = [myCell.text sizeWithFont:[UIFont systemFontOfSize:14.0] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
16     
17     cellRetweet.text = [NSString stringWithFormat:@"%@:%@", myCell.retweetName, myCell.retweetText];
18     constraint = CGSizeMake(cellRetweet.frame.size.width, 20000.0f);
19     CGSize retweetSize = [cellRetweet.text sizeWithFont:[UIFont systemFontOfSize:14.0] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
20     
21     if (myCell.retweetText != nil)
22         return (43.0 + textSize.height + 3 * frameGap + retweetSize.height);
23     else
24         return (43.0 + textSize.height + 2 * frameGap);
25 }

这段也不需要解释,和之前差不多,这种写法的好处是可以先在storyboard设置好custom的Cell调整好位置,最后才在代码匹配高度。就算以后改变Cell的位置,代码也基本不用改,在storyboard那里改动就好。原本我想直接在- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath设置Cell的大小,不过都失败了。还没有知道什么原因,知道的话,能告诉我一下么。

至于cellImage就用GCD异步下载,我也并不是很熟悉其中的用法。而且我的程序刚开始也出现了一个关键的问题,当滑动速度非常快的时候,头像会出现闪动和错误,不过这个问题总算解决了。

 1     dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 2     
 3     if (myCell.profileImage) {
 4         cellImage.image = myCell.profileImage;
 5     } else if ([self.imageDic objectForKey:myCell.userName]) {
 6         cellImage.image = [self.imageDic objectForKey:myCell.userName];
 7         myCell.profileImage = cellImage.image;
 8     } else {        
 9         dispatch_async(concurrentQueue, ^{
10             
11             dispatch_sync(concurrentQueue, ^{
12 //                for (int i = 0; i < 100000000; i++);/*模拟长时间下载*/
13                 [myCell downloadProfileImage];
14                 if (myCell.profileImage)
15                     [self.imageDic setObject:myCell.profileImage forKey:myCell.userName];
16             });
17             
18             dispatch_sync(dispatch_get_main_queue(), ^{
19                 cellImage.image = [self.imageDic objectForKey:cellName.text];
20             });
21         });
22     }

由于我不想每次都使用字典这种比较耗时的操作,所以尽可能使用属性来读取头像。具体思路很简单,就是先查看Cell对应的数组属性profileImage有没下载好的头像,没有就去Controller的字典imageDic,根据cellName来寻找,并将对应的数组元素赋值。由于cellName相同的,头像必相同,所以可以通过这一点减少重复头像的下载。如果依然没有,那就只能够下载了,这里使用了一个异步里面包含了两个同步,头像下载好后,再根据cellName来对cellImage赋值。这样做,就可以防止跳图的现象产生。另外NSMutableDictionary不可以添加nil,所以添加前必须进行一次简单的判断。此外,我又做了一个很猥琐的设计,将存放头像的字典改为全局静态变量。因此其他Controller都可以在加载完成后,立即匹配头像,再一次跳过下载重复头像。其实这么做主要是为了应对低网速下的环境,如果每次都重复下载头像,体验肯定会大打折扣。

 

微博必不可少的刷新,我也实现了,不过并不是单纯的刷新,属于增量加载。判断一下比已加载的最新一条微博还要新的微博是否达到50条,如果超过就直接替换原数组,否则就将原数组加在新数组后面。这样就不会错过任何的微博,而且刷新卡顿时间短一些,毕竟大多数情况下都不会有达到50条的新微博,而且可以减少加载后面更多微博的压力。不过,内存会使用得更多,也有不足。

 1 - (IBAction)refreshButton:(UIBarButtonItem *)sender {
 2     NSString *tmpSinceid = [[self.cellsDataArray objectAtIndex:0] ID];
 3     __block NSMutableArray *refreshDataArray;
 4     
 5     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
 6         refreshDataArray = [NSMutableArray weiboCellDataArrayWithSourceArray:[weiboUrl weiboAPIHomeTimeLineWithToken:self.userToken.token andSinceid:tmpSinceid andMaxid:@"0" andCount:@"50"]];
 7         dispatch_async(dispatch_get_main_queue(), ^{
 8             
 9             if ([refreshDataArray count]) {
10                 if ([refreshDataArray count] >= 50) {
11                     self.cellsDataArray = refreshDataArray;
12                 } else {
13                     [refreshDataArray addObjectsFromArray:self.cellsDataArray];
14                     self.cellsDataArray = refreshDataArray;
15                 }
16             }
17             
18             [self.tableView reloadData];
19         });
20     });21 }

然后“查看更多”的按键也是类似的做法。不过由于我封装API的时候,把max_id也改成NSString,所以在加载好后面微博的时候,还要把第一条重复的微博删掉,真的很马虎。由于微博的API我都封装了,这里没有看到具体实现。其实微博API就GET和POST两种调用方法,GET是获取数据的时候用,POST是发送数据,下面我会举两个具体的例子。

 

 

三、发微博和需要注意的小细节

GET方法

 1 + (NSDictionary *)weiboAPIUserShowWithToken:(NSString *)token andUid:(NSString *)uid OrScreenName:(NSString *)screenname
 2 {
 3     NSError *error;
 4     NSString *urlstr;
 5     if (!screenname)
 6        urlstr = [NSString stringWithFormat:@"%@?access_token=%@&uid=%@", UserShow, token, uid];
 7     else
 8         urlstr = [NSString stringWithFormat:@"%@?access_token=%@&screen_name=%@", UserShow, token, [screenname stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
 9     
10     NSURL *url = [[NSURL alloc]initWithString:urlstr];
11     NSData* data = [NSData dataWithContentsOfURL: url];
12     NSDictionary *returnDic = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
13     
14     return returnDic;
15 }

 

POST方法

 1 + (NSDictionary *)weiboAPIUpdateStatusesWithToken:(NSString *)token andStatus:(NSString *)status andLat:(float)latitude andLong:(float)longtitude
 2 {
 3     if ([weiboUrl testStatusLengthWithString:status] == NO)
 4         return nil;
 5     
 6     NSString *requestStr = [NSString stringWithFormat:@"&access_token=%@&status=%@&lat=%f&long=%f", token, status, latitude, longtitude];
 7     const char *requestCstr = [requestStr UTF8String];
 8     
 9     
10     NSData *RequestData = [NSData dataWithBytes:requestCstr length:strlen(requestCstr)];
11     
12     
13     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString: UpdateStatuses]];
14     [request setHTTPMethod:@"POST"]; 
15     [request setHTTPBody:RequestData];
16     NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
17     
18     NSDictionary *returnDic = [NSJSONSerialization JSONObjectWithData:returnData options:kNilOptions error:nil];
19     
20     return returnDic;
21 }

POST以后会有返回数据,根据返回的数据可以判断微博是否发送成功等状态,所以我封装的时候都会有返回数据的字典。说到POST,就不得不说微博“内容不超过140个汉字”的限制。微博所说的汉字是一个汉字或者汉字的标点就计数加1,英文或者英文的标点就计数加0.5,总计数是向上取整。全角等我尚未测试,所以不做解释。主要问题是如何计算字数出是否符合“内容不超过140个汉字”的标准。这里我提供三种方法给大家,其中一种是借鉴Kay_Sprint的《ios小项目——新浪微博客户端总结》其中的方法。

第一种是我自己想出来的

 1 + (BOOL) testStatusLengthWithString:(NSString *)sourceString
 2 {
 3     const char *UTFStr = [sourceString UTF8String];
 4 
 5     unsigned int A = strlen(UTFStr);                //汉字UTF8编码在strlen中每个占3位
 6     unsigned int B = [sourceString length];         //NSString length方法汉字和英文均占用1位
 7     
 8     unsigned int x = (A - B) / 2;                   //解二元一次方程,可得汉字和英文的个数
 9     unsigned int y = B - x;
10     
11     
12     if (2 * x + y > 280) {                         //微博API接口限制汉字为140个,英文两个算一个汉字
13         UIAlertView *al = [[UIAlertView alloc] initWithTitle:@"Error!" message:@"内容不超过140个汉字" delegate:self cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
14         [al show];
15         return (NO);
16     } else
17         return YES;
18 }

如果超出字数,就会弹出UIAlertView的警告。这个理解起来应该不算难吧??

 

第二种是我从网上找回来的

1 NSString *test = [NSString stringWithString:@"这是一个中文test1"];
2 NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
3 NSLog(@”%@ length is: %d”,test,[test lengthOfBytesUsingEncoding:enc]);

出处是http://www.xffox.com/blog/archives/327,这样的话,要计算相对的长度也不是什么问题。不过由于这种编码方法,我也不是很清楚,所以具体会遇到什么问题,我也不知道。

 

第三种就是Kay_Sprint

 1 -(int)textLength:(NSString *)dataString  
 2     {  
 3         float sum = 0.0;  
 4         for(int i=0;i<[dataString length];i++)  
 5         {  
 6             NSString *character = [dataString substringWithRange:NSMakeRange(i, 1)];  
 7             if([character lengthOfBytesUsingEncoding:NSUTF8StringEncoding] == 3)  
 8             {  
 9                 sum++;  
10             }  
11             else  
12                 sum += 0.5;  
13         }  
14           
15         return ceil(sum);  
16     }  

具体作用可以看Kay_Sprint的说明。

这样的话,调用新浪微博API的障碍就基本扫清了,其余的接口都是大同小异,不会有什么大的变化。
不过转发的时候也要注意转发的文字,光标位置在最前面,而且键盘是自动打开的。

 

此外,为了不要每次都重复提取Access Token,我还用plist做了一下储存

1 - (void) saveToken
2 {
3     NSString *tokenFile = [[NSBundle mainBundle] pathForResource:fileName ofType:@"plist"];
4     NSArray *tmpArray = [[NSArray alloc]initWithObjects:self.token, self.uid, self.userName, nil];
5     
6     [tmpArray writeToFile:tokenFile atomically:YES];
7 }

 

加载Access Token

 1 + (id) loadToken
 2 {
 3     tokenData *tmpid = [[tokenData alloc] init];
 4     
 5     NSString *tokenFile = [[NSBundle mainBundle] pathForResource:fileName ofType:@"plist"];
 6     NSArray *tmpArray = [[NSArray alloc] initWithContentsOfFile:tokenFile];
 7     
 8     if (tmpArray == nil)
 9         return nil;
10     
11     tmpid.token = [tmpArray objectAtIndex:0];
12     tmpid.uid = [tmpArray objectAtIndex:1];
13     tmpid.userName = [tmpArray objectAtIndex:2];
14     
15     return tmpid;
16 }


如何要注销账号,我就先清空Access Token,然后dismissModal。最后返回最初的Controller的时,只要判定一下Access Token是否存在和是否过期即可。

 


 四、View的复用

我先发我那充满违和感的storyboard出来吧

很明显看到我有几个View是密集地多次指向的,因为那些都是经过多次复用的,不过在这视图上看的确充满违和感。但是View复用可以减少大量的冗余代码,我是通过View的Controller属性的状态来判断现在的应该执行什么操作,改变其状态则可以在prepareForSegue等发生时进行操作。例如发新微博的View其实还可以用于评论、转发,那么新建一个独立的View作用不大。而且,查看微博内容的View也会被经常调用,就只是微博的ID不同。对它做小小的改动,完全可以胜任任何环境。

 具体的操作,我也会在源代码里给出来。

 

 

五、最后的话

这个山寨新浪微博客户端还有很多的不足,特别是错误异常处理做得很不好,空微博、空收藏、无网络连接下的POST问题等等,我都没有去解决。这些就导致了闪退、假死的问题。类封装马虎的问题,导致了我每次发现有需要添加的新属性,都往同一个类加。最后变得很臃肿,经常有大量的属性没有切实使用。虽然我也有几个View复用了,但其实还有可以提升的空间。功能的不完善是很明显的,主流客户端都做了缓存,即便在低网速的环境也不会在加载View的时候卡顿太久。网络切换时容易造成闪退等等各种问题暂时就先搁置,留待以后我有能力解决再一一慢慢除虫。最后还是要感谢Kay_Sprint等人的指导和交流,不然我一个星期也搞不出这货。

 

posted @ 2012-07-18 15:29  Pinka  阅读(1115)  评论(1编辑  收藏  举报