ios小項目——新浪微博客戶端總結

們仍是登陸不了,大家要用仍是得本身申請appkey而且把回調網址設爲baidu。或者是再下面留言,留下你的微博uid我把你加入測試用戶。ios

就像題目說的,此次的小項目是作一個新浪微博的客戶端。web

平臺是Xcode4.2,用storyboard和ARC,由於本身一開始接觸ios開發就學的是ios5的,因此就一直都是用storyboard和arc進行開發,對於以前的xib和沒有arc的開發,之後會找機會學習,各有各的優缺點吧。storyboard的話,各個場景之間的切換一目瞭然,能夠在一個屏幕上管理全部視圖,xib的話,就須要用代碼來串接各個視圖。可是storyboard幫咱們完成了不少工做,使得對於底層的一些運做不是很清晰。相反的,看了一些xib的例子,以爲那些在代碼上視圖的關係比較清晰,可是沒有那麼方便咯。json

好了,迴歸正題。api

1、OAuth2.0認證篇數組

要使用新浪微博讀取用戶的數據,須要先進行OAuth受權,如今新浪主推是OAuth2.0,因此此次的認證,固然也是用新的OAuth機制進行認證。新浪提供給移動應用的認證方式有兩種,可是對於咱們這種練手的,非商業的用戶,其實就只有一種,那就是web認證方式,web認證說明。web認證的URL是https://api.weibo.com/oauth2/authorize須要傳的參數有幾個。瀏覽器

首先是client_id(申請一個應用後就會有,就是那個appkey)。服務器

還有redirect_uri回調網址,因爲我是作客戶端,因此這個回調網址,我沒設置,傳參數的時候,值就都不用寫。併發

第三個是response_type,這個參數很重要,由於我在調用新浪的其餘API時,須要access_token,因此在這個這個參數的類型要選擇token,這樣就能夠在返回的數據中找到返回的access_token。app

第四個參數是display,設置這個參數爲mobile,由於個人是在iphone上運行的,因此這個大小恰好是手機屏幕的大小。iphone

傳參的格式,就是在認證的URL後面先加一個'?'而後在傳各個參數和值,每一個參數之間用'&'隔開,這個看API的說明就知道了。

在程序中,認證的界面是個人第一個界面,顯示的時候,像下面這個樣子:


就是在UIViewController上面添加一個WebView,而後爲這個webView添加一個outlet到這個controller的類中。

在這個類中,我將用來存放獲取到的access_token的變量設爲靜態變量,而且爲該類添加了一個類方法來返回access_token,緣由待後面解說。

下面是個人viewDidLoad函數中的代碼:

[cpp] view plaincopyprint?

  1. - (void)viewDidLoad  

  2. {  

  3.     [super viewDidLoad];  

  4.       

  5.     NSString *url = [[NSString alloc]initWithString:OAuthUrl];  

  6.     NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:url]];  

  7.     [self.webView setDelegate:self];  

  8.     [self.webView loadRequest:request];  

  9.       

  10. }  

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    NSString *url = [[NSString alloc]initWithString:OAuthUrl];
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
    [self.webView setDelegate:self];
    [self.webView loadRequest:request];
    
}

在代碼中能夠看到,首先是建立了一個字符串url在裏面我放的就是用來進行web認證的url,而後就loadrequest咯,要取回新浪給個人access_token,須要用到webView的delegate中的方法,因此在這裏先設置delegate爲本類。

你們能夠在瀏覽器中實驗一下,用web認證的方式來獲取access_token,能夠看到在輸入完用戶的賬號密碼以後,新浪會將如今的web重定向到你的redirect_uri中,可是因爲我沒有設置個人redirect_uri因此會出現一個沒法加載的空白頁面,可是在該頁面的URL中,新浪已經把access_token給了我,我只須要在這個URL中提取就能夠了,個人作法hava a little trick.我事先數好了重定向後的URL會在哪一個字符後面出現access_token,而且access_token的長度都是同樣的,因此我就用NSString的方法來提取。在什麼地方來提取呢?固然是在這個重定向以後的網頁上提取,當webview進行重定向,而且加載完成時,會調用下面這個函數。

[cpp] view plaincopyprint?

  1. -(void)webViewDidFinishLoad:(UIWebView *)webView  

  2. {  

  3.     NSString *url = webView.request.URL.absoluteString;  

  4.     NSRange rang = NSMakeRange(52, 32);  

  5.     _access_token = [url substringWithRange:rang];  

  6.     //NSLog(@"access_token:%@",_access_token);   

  7.     if([_access_token characterAtIndex:1] == '.')  

  8.     {      

  9.         //NSLog(@"OK");   

  10.         [self.webView setHidden:YES];  

  11.         [self performSegueWithIdentifier:@"show" sender:nil];  

  12.     }  

  13. }  

-(void)webViewDidFinishLoad:(UIWebView *)webView
{
    NSString *url = webView.request.URL.absoluteString;
    NSRange rang = NSMakeRange(52, 32);
    _access_token = [url substringWithRange:rang];
    //NSLog(@"access_token:%@",_access_token);
    if([_access_token characterAtIndex:1] == '.')
    {    
        //NSLog(@"OK");
        [self.webView setHidden:YES];
        [self performSegueWithIdentifier:@"show" sender:nil];
    }
}

一開始當web認證頁面剛出來的時候,也會調用這個函數,因此我須要判斷我提取到的東西是否是我要的,利用的就是access_token的第二個字符是一個小數點,在整個URL中,我暫時沒發現哪一個字段是有小數點的,因此就用這個來進行判斷咯。

在獲取到access_token以後是 [self performSegueWithIdentifier:@"show" sender:nil];跳轉到下一個界面,爲了後面的講解的方便,我先貼出這個程序的storyboard和文件列表的圖片。



從圖中能夠發現,第二個界面是一個UITabbarController,因爲TabbarController不是在NavigationController的嵌套下的,因此segue的屬性不能夠是push,而要用modal,這樣才能夠實現跳轉。

如今就來講爲何我要把第一個頁面的類中的access_token設爲靜態變量。

就是由於個人第二個頁面是一個UITabbarController,我不能直接去控制他,因此不能夠去傳參數給他,因此爲了後面的頁面可使用API,因此後面的類只須要調用OAuthWebViewController類中的類方法,就能夠了。


2、微博內容篇

下面就是客戶端的主體部分了,下面是各個頁面的內容:



這就是各個Tab的內容。上面的按鈕後面再說。

下面就以兩個類來作說明,其中前三個標籤的類基本上是同樣的,因此只說一個,後面的獲取評論的類稍微有點不同,因此就拿出來再說一下。

下面就先說我是如何獲取熱門轉發的微博內容的。

獲取熱門轉發的微博內容須要用到是熱門轉發API,這個API的請求方式是GET,因此直接用NSData來下載就能夠了。

調用這個API必選的參數只有一個,就是access_token,其餘參數就根據本身的須要去設置咯

我處理這個UITableViewController的類是HotWeiboViewController。

下面這個圖片是第一次切換到熱門轉發這個tab時的樣子

頭文件中各個屬性的定義:

[cpp] view plaincopyprint?

  1. //   

  2. //  HotWeiboViewController.h   

  3. //  SinaWeibo   

  4. //   

  5. //  Created by mac11 on 12-4-16.   

  6. //  Copyright (c) 2012年 __MyCompanyName__. All rights reserved.   

  7. //   

  8.   

  9. #import <UIKit/UIKit.h>   

  10. #import "UserDetailViewController.h"   

  11. #import "OAuthWebViewController.h"   

  12.   

  13. @interface HotWeiboViewController : UITableViewController  

  14.   

  15. @property (strong ,nonatomic) NSString *access_token;  

  16. @property (strong, nonatomic) NSMutableArray *statusArray;  

  17. @property (strong, nonatomic) NSMutableArray *imageItems;  

  18. @property (strong, nonatomic) NSMutableArray *userNames;  

  19. @property (strong, nonatomic) NSMutableArray *userText;  

  20. @property (strong, nonatomic) UIActivityIndicatorView *activityIndicator;  

  21. @property (strong, nonatomic) NSMutableArray *weiboId;  

  22. @end  

//
//  HotWeiboViewController.h
//  SinaWeibo
//
//  Created by mac11 on 12-4-16.
//  Copyright (c) 2012年 __MyCompanyName__. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "UserDetailViewController.h"
#import "OAuthWebViewController.h"

@interface HotWeiboViewController : UITableViewController

@property (strong ,nonatomic) NSString *access_token;
@property (strong, nonatomic) NSMutableArray *statusArray;
@property (strong, nonatomic) NSMutableArray *imageItems;
@property (strong, nonatomic) NSMutableArray *userNames;
@property (strong, nonatomic) NSMutableArray *userText;
@property (strong, nonatomic) UIActivityIndicatorView *activityIndicator;
@property (strong, nonatomic) NSMutableArray *weiboId;
@end


[cpp] view plaincopyprint?

  1. - (void)viewDidLoad  

  2. {  

  3.     [super viewDidLoad];  

  4.       

  5.     self.access_token = [OAuthWebViewController getAccesstoken];  

  6.     self.imageItems = [[NSMutableArray alloc] init];  

  7.     self.userText = [[NSMutableArray alloc] init];  

  8.     self.userNames = [[NSMutableArray alloc] init];  

  9.     self.weiboId = [[NSMutableArray alloc] init];  

  10.       

  11.     self.activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];  

  12.     [self.activityIndicator setColor:[UIColor grayColor]];  

  13.       

  14.     [_activityIndicator setCenter:CGPointMake(160, 220)];  

  15.     [self.tableView addSubview:_activityIndicator];  

  16.     [_activityIndicator startAnimating];  

  17.     [self downloadData];  

  18. }  

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.access_token = [OAuthWebViewController getAccesstoken];
    self.imageItems = [[NSMutableArray alloc] init];
    self.userText = [[NSMutableArray alloc] init];
    self.userNames = [[NSMutableArray alloc] init];
    self.weiboId = [[NSMutableArray alloc] init];
    
    self.activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
    [self.activityIndicator setColor:[UIColor grayColor]];
    
    [_activityIndicator setCenter:CGPointMake(160, 220)];
    [self.tableView addSubview:_activityIndicator];
    [_activityIndicator startAnimating];
    [self downloadData];
}

在這個函數中,首先經過OAuthWebViewController的類方法來獲取access_token。而後接下來是四個NSMutableArray來申請空間。分別是存用戶的頭像、微博、暱稱和該微博的id,id的用法後面再說。

而後是建立了一個UIActivityIndicatorView的對象,也就是上圖的那個風火輪,在函數的最後調用了downloadData函數。這個函數就是經過API來下載熱門轉發榜上面的微博數據。

[cpp] view plaincopyprint?

  1. -(void)downloadData  

  2. {  

  3.     dispatch_queue_t downloadQueue = dispatch_queue_create("download data", NULL);  

  4.       

  5.     dispatch_async(downloadQueue, ^{  

  6.         NSMutableString *url = [[NSMutableString alloc]initWithString:Hotweibo];  

  7.         [url appendFormat:@"?access_token=%@&count=30",self.access_token];  

  8.         NSData *userdata = [[NSData alloc]initWithContentsOfURL:[NSURL URLWithString:url]];  

  9.           

  10.         NSError *error;  

  11.         NSMutableArray *timeLine = [NSJSONSerialization JSONObjectWithData:userdata options:kNilOptions error:&error];  

  12.           

  13.         for(int i=0;i < [timeLine count];i++)  

  14.         {  

  15.             NSDictionary *dict = (NSDictionary *)[timeLine objectAtIndex:i];  

  16.             [self.userText addObject:[dict objectForKey:@"text"]];  

  17.             [self.weiboId addObject:[dict objectForKey:@"id"]];  

  18.             [self.userNames addObject:[[dict objectForKey:@"user"] objectForKey:@"screen_name"]];  

  19.             NSString *url = [[dict objectForKey:@"user"] objectForKey:@"profile_image_url"];  

  20.             NSData *imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:url]];  

  21.             [self.imageItems addObject:[UIImage imageWithData:imageData]];  

  22.         }  

  23.           

  24.         dispatch_async(dispatch_get_main_queue(), ^{  

  25.             self.statusArray = timeLine;  

  26.         });  

  27.     });  

  28.     dispatch_release(downloadQueue);  

  29.       

  30. }  

-(void)downloadData
{
    dispatch_queue_t downloadQueue = dispatch_queue_create("download data", NULL);
    
    dispatch_async(downloadQueue, ^{
        NSMutableString *url = [[NSMutableString alloc]initWithString:Hotweibo];
        [url appendFormat:@"?access_token=%@&count=30",self.access_token];
        NSData *userdata = [[NSData alloc]initWithContentsOfURL:[NSURL URLWithString:url]];
        
        NSError *error;
        NSMutableArray *timeLine = [NSJSONSerialization JSONObjectWithData:userdata options:kNilOptions error:&error];
        
        for(int i=0;i < [timeLine count];i++)
        {
            NSDictionary *dict = (NSDictionary *)[timeLine objectAtIndex:i];
            [self.userText addObject:[dict objectForKey:@"text"]];
            [self.weiboId addObject:[dict objectForKey:@"id"]];
            [self.userNames addObject:[[dict objectForKey:@"user"] objectForKey:@"screen_name"]];
            NSString *url = [[dict objectForKey:@"user"] objectForKey:@"profile_image_url"];
            NSData *imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:url]];
            [self.imageItems addObject:[UIImage imageWithData:imageData]];
        }
        
        dispatch_async(dispatch_get_main_queue(), ^{
            self.statusArray = timeLine;
        });
    });
    dispatch_release(downloadQueue);
    
}

在這個函數中,咱們用了GCD,因爲我只是作一個操做,就是下載並解析數據,因此在這裏用併發的隊列或者是連續的隊列均可以。用一個異步函數來下載數據並解析,下載的數據類型是JSON,在ios5中,加入了一個很強大的解析JSON的類,就是NSJSONSerialization。解析完再對數組中的每一項進行提取,拿本身須要的數據,對於和UI相關的操做,都要在主線程中進行,因此在主線程中,將存儲有數據的指針複製給一個屬性statusArray,此時這個statusArray就會調用它的setter函數

[cpp] view plaincopyprint?

  1. -(void)setStatusArray:(NSMutableArray *)statusArray  

  2. {  

  3.     if(_statusArray != statusArray)  

  4.     {  

  5.         _statusArray = statusArray;     

  6.         if(self.tableView.window)  

  7.             [self.tableView reloadData];  

  8.     }  

  9.     [_activityIndicator stopAnimating];  

  10.       

  11. }  

-(void)setStatusArray:(NSMutableArray *)statusArray
{
    if(_statusArray != statusArray)
    {
        _statusArray = statusArray;   
        if(self.tableView.window)
            [self.tableView reloadData];
    }
    [_activityIndicator stopAnimating];
    
}

判斷數據是否改變了,是的話就對視圖進行reloadData,以前還加了一個判斷,用來判斷該視圖如今是否在主頁面上。

關於block和GCD的知識,會再寫一篇總結,這裏就只是大概講一下。

因爲用了GCD因此在加載數據的時候,頁面也就不會卡住了。

下面對於UITableView的dataSource的操做,就不說什麼了,以前一篇UITableViewController總結已經有說明,下面就直接給出代碼。

[cpp] view plaincopyprint?

  1. #pragma mark - Table view data source   

  2.   

  3. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section  

  4. {  

  5.     return [self.statusArray count];  

  6. }  

  7.   

  8. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath  

  9. {  

  10.     static NSString *CellIdentifier = @"Cell";  

  11.       

  12.     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];  

  13.     if (cell == nil) {  

  14.         cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];  

  15.     }  

  16.       

  17.     // Configure the cell...   

  18.     cell.imageView.image = [self.imageItems objectAtIndex:indexPath.row];  

  19.     cell.textLabel.text = [self.userNames objectAtIndex:indexPath.row];  

  20.     cell.detailTextLabel.text = [self.userText objectAtIndex:indexPath.row];  

  21.     return cell;  

  22. }  

#pragma mark - Table view data source

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [self.statusArray count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }
    
    // Configure the cell...
    cell.imageView.image = [self.imageItems objectAtIndex:indexPath.row];
    cell.textLabel.text = [self.userNames objectAtIndex:indexPath.row];
    cell.detailTextLabel.text = [self.userText objectAtIndex:indexPath.row];
    return cell;
}

當選中其中某一行以後,會跳轉到另外一個頁面,顯示那條微博的一些內容,以下圖所示:


這個UIViewController的類是UserDetailViewController。在storyboard中已經使用了segue來進行跳轉,因此在熱門轉發的VC(ViewController)中,只須要在prepareForSegue方法中傳相應的參數便可。

[cpp] view plaincopyprint?

  1. -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender  

  2. {  

  3.     if([segue.identifier isEqualToString:@"userDetail4"])  

  4.     {  

  5.         [segue.destinationViewController setImage:[self.imageItems objectAtIndex:self.tableView.indexPathForSelectedRow.row]];  

  6.         [segue.destinationViewController setName:[self.userNames objectAtIndex:self.tableView.indexPathForSelectedRow.row]];  

  7.         NSString *time = [[self.statusArray objectAtIndex:self.tableView.indexPathForSelectedRow.row]objectForKey:@"created_at"];  

  8.         [segue.destinationViewController setTime:time];  

  9.         [segue.destinationViewController setText:[self.userText objectAtIndex:self.tableView.indexPathForSelectedRow.row]];  

  10.         [segue.destinationViewController setWeiboId:[self.weiboId objectAtIndex:self.tableView.indexPathForSelectedRow.row]];  

  11.     }  

  12. }  

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if([segue.identifier isEqualToString:@"userDetail4"])
    {
        [segue.destinationViewController setImage:[self.imageItems objectAtIndex:self.tableView.indexPathForSelectedRow.row]];
        [segue.destinationViewController setName:[self.userNames objectAtIndex:self.tableView.indexPathForSelectedRow.row]];
        NSString *time = [[self.statusArray objectAtIndex:self.tableView.indexPathForSelectedRow.row]objectForKey:@"created_at"];
        [segue.destinationViewController setTime:time];
        [segue.destinationViewController setText:[self.userText objectAtIndex:self.tableView.indexPathForSelectedRow.row]];
        [segue.destinationViewController setWeiboId:[self.weiboId objectAtIndex:self.tableView.indexPathForSelectedRow.row]];
    }
}

如今看會熱門轉發的這個界面,在界面的navigationBar中咱們看到有兩個按鈕,一個是刷新,一個是發微博。

刷新按鈕,作的操做和downdata這個方法的工做同樣,就是下載數據,最後若是數據和如今頁面上的不同的話,就reloaddata,同樣是用GCD,因此不會影響當前視圖。

發微博這個按鈕,就是經過segue來到一個新的VC中。


3、發微博

上面這個界面就是發微博時候的界面,就是一個TextView,只能實現輸入文本,沒法發送圖片和表情。

在輸入前右上角的done按鈕不會出現,這個按鈕是爲了在用戶輸入完成後,可讓鍵盤縮回去而設置的。原先的設計是在下面會有一個發送按鈕,可是後來被老師一說,也以爲其實在done按鈕實現發送就能夠了,不必再多弄一個按鈕。因此done按鈕的任務就是將鍵盤推下和發送微博。

首先,先說下如何讓鍵盤消失的,是經過TextView的delegate來實現的。當輕點TextView時,鍵盤上來時會同時響應下面這個函數

[cpp] view plaincopyprint?

  1. -(void)textViewDidBeginEditing:(UITextView *)textView  

  2. {  

  3.     UIBarButtonItem *done = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(pressDone)];  

  4.     self.navigationItem.rightBarButtonItem = done;  

  5.     self.weiboText.text = @"";  

  6. }  

-(void)textViewDidBeginEditing:(UITextView *)textView
{
    UIBarButtonItem *done = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(pressDone)];
    self.navigationItem.rightBarButtonItem = done;
    self.weiboText.text = @"";
}

我就是在這個函數中爲WeiboSendViewController添加一個done按鈕,並將原來屏幕上的提示輸入的信息去掉。

當鍵盤推下去時,又會響應下面的函數

[cpp] view plaincopyprint?

  1. -(void)textViewDidEndEditing:(UITextView *)textView  

  2. {  

  3.     self.navigationItem.rightBarButtonItem = nil;  

  4. }  

-(void)textViewDidEndEditing:(UITextView *)textView
{
    self.navigationItem.rightBarButtonItem = nil;
}

在裏面將done按鈕去掉。

前面說了,當按下done的時候,讓鍵盤推下併發送微博,在發送前要對微博內容進行檢查,由於要符合內容不能爲空或者是不能夠超過140字,新浪的140字是這樣計算的,中文狀態下,每一個漢字和符號都算一個字,在英文狀態下,字母和符號都算半個字。針對這個來檢查看看是否不符合條件,給出警告。符合條件的時候就發送。

下面是統計微博內容長度的函數

[cpp] view plaincopyprint?

  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. }  

-(int)textLength:(NSString *)dataString
{
    float sum = 0.0;
    for(int i=0;i<[dataString length];i++)
    {
        NSString *character = [dataString substringWithRange:NSMakeRange(i, 1)];
        if([character lengthOfBytesUsingEncoding:NSUTF8StringEncoding] == 3)
        {
            sum++;
        }
        else
            sum += 0.5;
    }
    
    return ceil(sum);
}

上面判斷的方法是根據,當用NSUTF8StringEncoding來編碼時,中文和中文狀態下的字符都會是三個字節,英文狀態下是1個字節,最後向下取整。

[cpp] view plaincopyprint?

  1. -(void)pressDone  

  2. {  

  3.     [self.weiboText resignFirstResponder];  

  4.     if([self.weiboText.text isEqualToString:@""])  

  5.     {  

  6.         UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"weibo" message:@"content is empty! Please input something !" delegate:nil cancelButtonTitle:@"YES" otherButtonTitles:nil];  

  7.         [alert show];  

  8.         return ;  

  9.     }  

  10.     if([self textLength:self.weiboText.text] > 140)  

  11.     {  

  12.         UIAlertView *alert2 = [[UIAlertView alloc] initWithTitle:@"weibo" message:@"contents is more than 140!" delegate:nil cancelButtonTitle:@"YES" otherButtonTitles:nil];  

  13.         [alert2 show];  

  14.         return ;  

  15.     }  

  16.       

  17.     NSString *url = [[NSString alloc]initWithString:UpdateUrl];  

  18.       

  19.     NSString *params = [[NSString alloc] initWithFormat:@"access_token=%@&status=%@",self.access_token,self.weiboText.text];  

  20.     NSMutableData *postData = [[NSMutableData alloc] init];  

  21.     [postData appendData:[params dataUsingEncoding:NSUTF8StringEncoding]];  

  22.       

  23.     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:3.0];  

  24.     [request setHTTPMethod:@"POST"];  

  25.     [request setHTTPBody:postData];  

  26.       

  27.     self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];  

  28. }  

-(void)pressDone
{
    [self.weiboText resignFirstResponder];
    if([self.weiboText.text isEqualToString:@""])
    {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"weibo" message:@"content is empty! Please input something !" delegate:nil cancelButtonTitle:@"YES" otherButtonTitles:nil];
        [alert show];
        return ;
    }
    if([self textLength:self.weiboText.text] > 140)
    {
        UIAlertView *alert2 = [[UIAlertView alloc] initWithTitle:@"weibo" message:@"contents is more than 140!" delegate:nil cancelButtonTitle:@"YES" otherButtonTitles:nil];
        [alert2 show];
        return ;
    }
    
    NSString *url = [[NSString alloc]initWithString:UpdateUrl];
    
    NSString *params = [[NSString alloc] initWithFormat:@"access_token=%@&status=%@",self.access_token,self.weiboText.text];
    NSMutableData *postData = [[NSMutableData alloc] init];
    [postData appendData:[params dataUsingEncoding:NSUTF8StringEncoding]];
    
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:3.0];
    [request setHTTPMethod:@"POST"];
    [request setHTTPBody:postData];
    
    self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
}

上面的代碼即是當按下done按鈕時候的響應函數,第一行的resignFirstResponder讓UITextView放棄做爲當前的響應者,讓Controller恢復第一響應者的身份,因爲UITextView再也不是第一響應者,鍵盤也就消失咯,在接下來檢測完若是微博的內容符合發送要求,那麼就進行發送咯

僅發送文本內容的微博是使用statuses/update這個API,這個API是POST請求方式的,因此不能像以前的GET請求的API同樣直接下載數據。

在ios中,使用NSMutableURLRequest和NSURLConnection來實現POST請求類型的API的發送。

首先是設置發送請求的URL,和以前的GET請求不同的是,在這個URL中,不包含參數,因此URL就僅僅是update這個API的URLhttps://api.weibo.com/2/statuses/update.json

而這個API內須要設置兩個必選的參數,一個是access_token,另外一個是微博內容status,發送以前須要對內容進行URLencode。

爲了發送POST請求的URL,首先是新建一個NSMutableURLRequest的對象,而後設置兩個參數一個是HTTPMethod,還有一個是HTTPBody,從代碼中能夠看到,HTTPBody中放的就是要傳的參數,是編完碼以後的,而後再利用NEURLConnection來建立鏈接,就能夠了。

那麼要怎麼知道是否發送成功了呢?

發如今NSURLConnection的建立函數中有一個delegate參數,咱們就是經過delegate來獲取鏈接狀態的,看下面四個函數

[cpp] view plaincopyprint?

  1. #pragma mark - NSURLConnection delegate Methods   

  2.   

  3. -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response  

  4. {  

  5.     self.responseData = [[NSMutableData alloc] initWithLength:0];  

  6. }  

  7.   

  8. -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data  

  9. {  

  10.     [self.responseData appendData:data];  

  11. }  

  12.   

  13. -(void)connectionDidFinishLoading:(NSURLConnection *)theconnection  

  14. {  

  15.     NSError *error;  

  16.     NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:self.responseData options:kNilOptions error:&error];  

  17.     NSLog(@"%@",dict);  

  18.   

  19.     NSString *date = [dict objectForKey:@"created_at"];  

  20.     if(date)  

  21.     {  

  22.         UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"weibo" message:@"send succeed!" delegate:nil cancelButtonTitle:@"YES" otherButtonTitles:nil];  

  23.         [alert show];  

  24.     }  

  25.     else {  

  26.         UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"weibo" message:@"send Fail!" delegate:nil cancelButtonTitle:@"YES" otherButtonTitles:nil];  

  27.         [alert show];  

  28.     }  

  29.   

  30.     [self.connection cancel];  

  31. }  

  32.   

  33. -(void)connection:(NSURLConnection *)theconnection didFailWithError:(NSError *)error  

  34. {  

  35.     UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"weibo" message:@"send fail!" delegate:nil cancelButtonTitle:@"YES" otherButtonTitles:nil];  

  36.     [alert show];  

  37.     [self.connection cancel];  

  38. }  

#pragma mark - NSURLConnection delegate Methods

-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    self.responseData = [[NSMutableData alloc] initWithLength:0];
}

-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [self.responseData appendData:data];
}

-(void)connectionDidFinishLoading:(NSURLConnection *)theconnection
{
    NSError *error;
    NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:self.responseData options:kNilOptions error:&error];
    NSLog(@"%@",dict);

    NSString *date = [dict objectForKey:@"created_at"];
    if(date)
    {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"weibo" message:@"send succeed!" delegate:nil cancelButtonTitle:@"YES" otherButtonTitles:nil];
        [alert show];
    }
    else {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"weibo" message:@"send Fail!" delegate:nil cancelButtonTitle:@"YES" otherButtonTitles:nil];
        [alert show];
    }

    [self.connection cancel];
}

-(void)connection:(NSURLConnection *)theconnection didFailWithError:(NSError *)error
{
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"weibo" message:@"send fail!" delegate:nil cancelButtonTitle:@"YES" otherButtonTitles:nil];
    [alert show];
    [self.connection cancel];
}

首先經過新浪的發送微博的API的說明文檔能夠知道,當發送成功時,會返回該微博的數據,當中會有該微博的發送時間,發送失敗的話,固然也就不會有那個時間啦,因此我是以收到的數據中是否有建立時間。

上面的函數中第一個是當服務器有足夠的資源而且準備響應這個鏈接時響應的,我在裏面將存放返回數據的NSMutableData長度置0。

第二個函數是每次有數據傳送的時候都會響應的函數,我在這裏面就將數據存起來。

第三個函數就是當數據都接受完的時候響應的,在該函數中我也根據接收到的數據進行解析並判斷是否發送成功,而且須要調用cancle方法來釋放鏈接。

最後一個函數就是當鏈接發生錯誤的時候響應的咯,當鏈接出現錯誤時,也要釋放鏈接,由於不會再接收到數據了。

以上就是發送微博的整個過程的實現了,經過這一個例子,其餘的POST類型的API的調用,也就幾乎同樣了。


4、獲取評論與發表評論

在前面的微博內容篇中有說獲取評論和其餘的有點不同,其實就是在獲取評論的時候,多存儲一個評論的id,不只僅是存儲微博的id,其餘的基本上和獲取其餘微博內容的同樣操做。

重點是說下下面的對一條微博進行評論和恢復一條評論

這兩個操做是不同的,道理誰都知道的啦,評論一條微博使用comments/create這個API,使用方法和前面發微博的那個API的方法同樣,多了個參數微博id而已。

回覆一條評論,就使用comments/reply這個API,多了個參數,就是評論的id。

回覆評論和評論微博我是在同一個VC中來控制的,在顯示某一條評論的內容時,右邊的按鈕就會顯示一個回覆的按鈕,按下就到評論界面


評論的這個VC和以前的發微博的那個同樣,也是一個TextView,不同的地方主要就是下面的按下done按鈕後的響應。

[cpp] view plaincopyprint?

  1. -(void)pressDone  

  2. {  

  3.     [self.pingLunText resignFirstResponder];  

  4.       

  5.     if([self.pingLunText.text isEqualToString:@""])  

  6.     {  

  7.         UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:@"內容爲空,請輸入要發佈的內容再按發送!" delegate:nil cancelButtonTitle:@"肯定" otherButtonTitles:nil];  

  8.         [alert show];  

  9.         return ;  

  10.     }  

  11.     if([self textLength:self.pingLunText.text] > 140)  

  12.     {  

  13.         UIAlertView *alert2 = [[UIAlertView alloc] initWithTitle:@"提示" message:@"內容超過140字,請刪減後再按發送!" delegate:nil cancelButtonTitle:@"肯定" otherButtonTitles:nil];  

  14.         [alert2 show];  

  15.         return ;  

  16.     }  

  17.       

  18.     NSString *params,*url;  

  19.       

  20.     if(!self.pinglunId)  

  21.     {  

  22.         url = [[NSString alloc] initWithString:HuiFuUrl];  

  23.           

  24.         params = [[NSString alloc] initWithFormat:@"access_token=%@&comment=%@&id=%@",self.access_token,self.pingLunText.text,self.weiboId];  

  25.     }  

  26.     else  

  27.     {  

  28.         url = [[NSString alloc] initWithString:ReplyUrl];  

  29.           

  30.         params = [[NSString alloc] initWithFormat:@"access_token=%@&comment=%@&cid=%@&id=%@",self.access_token,self.pingLunText.text,self.pinglunId,self.weiboId];  

  31.     }  

  32.       

  33.     NSMutableData *postData = [[NSMutableData alloc] init];  

  34.     [postData appendData:[params dataUsingEncoding:NSUTF8StringEncoding]];  

  35.       

  36.     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:3.0];  

  37.     [request setHTTPMethod:@"POST"];  

  38.     [request setHTTPBody:postData];  

  39.       

  40.     self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];  

  41.       

  42. }  

-(void)pressDone
{
    [self.pingLunText resignFirstResponder];
    
    if([self.pingLunText.text isEqualToString:@""])
    {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:@"內容爲空,請輸入要發佈的內容再按發送!" delegate:nil cancelButtonTitle:@"肯定" otherButtonTitles:nil];
        [alert show];
        return ;
    }
    if([self textLength:self.pingLunText.text] > 140)
    {
        UIAlertView *alert2 = [[UIAlertView alloc] initWithTitle:@"提示" message:@"內容超過140字,請刪減後再按發送!" delegate:nil cancelButtonTitle:@"肯定" otherButtonTitles:nil];
        [alert2 show];
        return ;
    }
    
    NSString *params,*url;
    
	if(!self.pinglunId)
    {
		url = [[NSString alloc] initWithString:HuiFuUrl];
        
		params = [[NSString alloc] initWithFormat:@"access_token=%@&comment=%@&id=%@",self.access_token,self.pingLunText.text,self.weiboId];
	}
	else
	{
		url = [[NSString alloc] initWithString:ReplyUrl];
		
		params = [[NSString alloc] initWithFormat:@"access_token=%@&comment=%@&cid=%@&id=%@",self.access_token,self.pingLunText.text,self.pinglunId,self.weiboId];
	}
    
	NSMutableData *postData = [[NSMutableData alloc] init];
    [postData appendData:[params dataUsingEncoding:NSUTF8StringEncoding]];
    
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:3.0];
    [request setHTTPMethod:@"POST"];
    [request setHTTPBody:postData];
    
    self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
    
}

主要是經過判斷是否存在評論id,存在的話,就是回覆一條評論,不存在的話就是評論一條微博。調用相應的API便可。

剩下的就和發送微博的VC幾乎同樣了。

在代碼中,能夠看到在調用相應的API時,對應的URL我都是用一個變量來代替,我是將用到的URL都集中用宏定義定義在一個頭文件中,便於管理和修改。


關於block 和GCD的博客已經更新 block && Grand Central Dispatch,有興趣的朋友能夠去看看

終於寫完啦~,這個客戶端還有不少地方能夠再提高,加入查看每條微博的評論,增長轉發功能等,還有用上core data(還沒學,學完就改善)存儲用戶的數據,用來存儲一些數據,讓用戶能夠不用每次都進行受權登陸。


源碼放到了資源區了,須要5分下載 有興趣的朋友就去下來看看咯 加上了簡單的coredata的應用。漏洞有很多有興趣的朋友就本身修改下咯 ios小項目——新浪微博客戶端

相關文章
相關標籤/搜索