iPhone Application Twitterクライアントを作ろう
皆さんこんにちわ。
お元気ですか。私は眠いです。
今日はiPhone ApplicationでTwitterクライアントを作ってみようと思います。
Xcodeのインストールは割愛します。
プロジェクトの作成
まずは、新しいプロジェクトを生成します。今回はSingleViewApplicationを選択
ProjectNameに適当な内容を追加、今回はTwitterApplicationとしました。
適当なフォルダでCreateを実行
StoryBoard
画面を作成
次にstoryboardで画面を構築します。Main_iPhone.storyboardを選択。
最初は以下のようになっていると思います。
早速ViewControllerを削除し、TableViewを追加
Table View Controllerを選択して、タブからEditor→Embed in →Navigation Controllerを選択
投稿ボタンを追加
次に投稿ボタンを作ってみましょう。まずは、ボタンを置く。
そしてプロパティにて変更する。identifierをComposeに
ファイルとViewの紐付け
新しくファイルを作ります。このファイルに先ほど生成したTable Viewの実装を記入します。
Objective-C classを選択し、名前は適当に作ります。今回はTwitterViewControllerとしました。
しかし、現在、TableViewとこのTwitterViewControllerが紐づいていません。
紐づけましょう。Main_iPhone.storyboardを開き、TableViewをクリック
Custom Classのタブを選択し、TwitterViewControllerを選択
これで紐付けができました。
投稿ボタン実装編
ボタンはまだ紐づいていません。まずは、ボタンを押した時に実行するメソッドを登録しましょう。
TwitterViewController.hを開きましょう。
その上で、ボタンをクリックし、コントロールを押しながらファイルに線をのばすと画面が表れます。
ConnectionはActionを選択し、名前をTwitterButtonとしました。
するとコードも変化し、以下のようになっていると思います。
#import <UIKit/UIKit.h> @interface TwitterViewController : UITableViewController - (IBAction)TwitterButton:(id)sender; @end
これでメソッドを登録することができました。
さて、ボタンのメソッドの本体を書きましょう。
今度はTwitterViewController.mを開きます。
最後の方に
- (IBAction)TwitterButton:(id)sender { }
が追加されているので、この中に書きます。
ヘッダーに
以下のように変更してください。
#import <Social/Social.h> (略) - (IBAction)TwitterButton:(id)sender { SLComposeViewController *twitterPostVC = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter]; [twitterPostVC setInitialText:@"てすと"]; [self presentViewController:twitterPostVC animated:YES completion:nil]; }
1行目でTwitterの画面を生成し、2行目でテキストを突っ込み、3行目で呼び出しているだけです。
まぁSocialのメソッドなりクラスが勝手に画面を作ってくれます。便利です。
Runしてボタンをクリックして以下のような画面が出れば成功です。
タイムラインの表示
さて、後はコードを書くだけです。
viewDidLoad
- (void)viewDidLoad { [super viewDidLoad]; //最近、APIが変更になったのでurlは気をつけましょう。 NSString *apiURL = @"https://api.twitter.com/1.1/statuses/home_timeline.json"; ACAccountStore *store = [[ACAccountStore alloc] init]; ACAccountType *twitterAccountType = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter]; [store requestAccessToAccountsWithType:twitterAccountType options:nil completion:^(BOOL granted,NSError *error){ if(!granted){ NSLog(@"Twitterの認証を拒否"); }else{ NSArray *twitterAccounts = [store accountsWithAccountType:twitterAccountType]; if ([twitterAccounts count] > 0) { ACAccount *account = [twitterAccounts objectAtIndex:0]; NSMutableDictionary *params = [[NSMutableDictionary alloc] init]; [params setObject:@"1" forKey:@"include_entities"]; NSURL *url = [NSURL URLWithString:apiURL]; SLRequest *request = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodGET URL:url parameters:params]; [request setAccount:account]; [UIApplication sharedApplication].networkActivityIndicatorVisible = YES; [request performRequestWithHandler:^(NSData *responseData,NSHTTPURLResponse *urlResponse,NSError *error) { if(!responseData){ NSLog(@"response error: %@", error); }else{ NSError *jsonError; tweets = [NSJSONSerialization JSONObjectWithData:responseData options: NSJSONReadingMutableLeaves error:&jsonError]; if(tweets){ dispatch_async(dispatch_get_main_queue(), ^{ // 追加 [self.tableView reloadData]; // 追加 }); }else{ NSLog(@"%@", error); } } }]; } } }];
viewDidLoadメソッドはページ実行時に実行されるメソッドです。
ポイントをいくつかご紹介します。
apiURL
apiURL 今年にTwitterのプロトコルが変更され、httpからhttpsに変更されました。その都合でいくつかのブログのurlでapi動作しないことがあります、気をつけましょう。
requestAccessToAccountsWithType
認証を要求する箇所。
grantedの中にTrue or Falseが入っている。
SLRequest
APIをたたくクラス、Oathなどをスルーしてくれるそうです。
NSJSONSerialization
返ってきたデータを変換する。
dispatch_async
同期的に実行する為のメソッド
self.tableView reloadData
データをリロードする。テーブルを再描写
numberOfSectionsInTableView
繰り返しのセルの数
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { // Return the number of sections. return 1; }
numberOfRowsInSection
行数を突っ込めば大丈夫です。
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // Return the number of rows in the section. return [tweets count]; }
cellForRowAtIndexPath
セルの内容を決めるメソッドです。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; // Configure the cell... if(cell == nil){ cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; } NSDictionary *status = [tweets objectAtIndex:indexPath.row]; NSString *text = [status objectForKey:@"text"]; cell.textLabel.text = text; return cell; }
描写するセルなどを指定しています。
参考文献
http://qiita.com/paming/items/9a6b51fa56915d1f1d64
http://www.appbank.net/2012/06/30/iphone-news/434166.php
http://ios.rainbowapps.jp/text_dev/10
次回はさすがにユーザー名を表示をしたいのでカスタムセル周りを対策したいと思います。
TwitterViewController
TwitterViewController.h
#import <UIKit/UIKit.h> @interface TwitterViewController : UITableViewController{ NSArray *tweets; } - (IBAction)TwitterButton:(id)sender; @end
TwitterViewController.m
#import "TwitterViewController.h" #import <Social/Social.h> #import <Accounts/Accounts.h> @interface TwitterViewController () @end @implementation TwitterViewController - (id)initWithStyle:(UITableViewStyle)style { self = [super initWithStyle:style]; if (self) { // Custom initialization } return self; } - (void)viewDidLoad { [super viewDidLoad]; [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"TweetCell"]; //最近、APIが変更になったのでurlは気をつけましょう。 NSString *apiURL = @"https://api.twitter.com/1.1/statuses/home_timeline.json"; ACAccountStore *store = [[ACAccountStore alloc] init]; ACAccountType *twitterAccountType = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter]; [store requestAccessToAccountsWithType:twitterAccountType options:nil completion:^(BOOL granted,NSError *error){ //Twitterの認証の拒否or認証 if(!granted){ NSLog(@"Twitterの認証を拒否"); }else{ NSArray *twitterAccounts = [store accountsWithAccountType:twitterAccountType]; if ([twitterAccounts count] > 0) { ACAccount *account = [twitterAccounts objectAtIndex:0]; NSMutableDictionary *params = [[NSMutableDictionary alloc] init]; [params setObject:@"1" forKey:@"include_entities"]; NSURL *url = [NSURL URLWithString:apiURL]; SLRequest *request = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodGET URL:url parameters:params]; [request setAccount:account]; [UIApplication sharedApplication].networkActivityIndicatorVisible = YES; [request performRequestWithHandler:^(NSData *responseData,NSHTTPURLResponse *urlResponse,NSError *error) { if(!responseData){ NSLog(@"response error: %@", error); }else{ NSError *jsonError; tweets = [NSJSONSerialization JSONObjectWithData:responseData options: NSJSONReadingMutableLeaves error:&jsonError]; if(tweets){ dispatch_async(dispatch_get_main_queue(), ^{ // 追加 [self.tableView reloadData]; // 追加 }); }else{ NSLog(@"%@", error); } //Tweet取得完了に伴い、Table Viewを更新 } }]; } } }]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { // Return the number of sections. return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // Return the number of rows in the section. return [tweets 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]; } NSDictionary *status = [tweets objectAtIndex:indexPath.row]; NSString *text = [status objectForKey:@"text"]; cell.textLabel.text = text; return cell; } - (IBAction)TwitterButton:(id)sender { SLComposeViewController *twitterPostVC = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter]; [twitterPostVC setInitialText:@"てすと"]; [self presentViewController:twitterPostVC animated:YES completion:nil]; } @end