Cocoa練習帳 -29ページ目

[iOS]Tweeting(アカウント管理)

iOS5からTwitter frameworkが用意されるようになり便利になった。ただ、機能的には簡素な部分があるので、他のオープンソースのフレームワークの方が便利な事もあるが。




Twitter.frameworkとAccounts.frameworkをプロジェクトに追加し、それぞれのヘッダー・ファイルをimportする。




OS X Lionには、Twitter/Accounts frameworkが用意されていない為か、シミュレータではtweetを投稿できない。iOSではTwitterのアカウント管理の機能がOS側に用意されている。アカウントの変更の通知を受ける為と、投稿可能かどうかを知るため、以下のコードを追加する。





- (void)viewDidLoad
{
    [super viewDidLoad];
 
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(canTweetStatus)
                                                 name:ACAccountStoreDidChangeNotification
                                               object:nil];
}
 
- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    
    [self canTweetStatus];
}
 
- (void)canTweetStatus {
    if ([TWTweetComposeViewController canSendTweet]) {
        self.tweetStatusLabel.text = @"can send tweet";
    }
    else {
        self.tweetStatusLabel.text = @"can't send tweet";
    }
}



OS Xのシミュレータでは"can send tweet"、iOSの実機では"can't send tweet"と表示されるはずだ。




このサンプル・コードを実機で起動した状態で、iOSのTwitterアカウントに変更を加えたら、どうなるだろうか?試してみよう。




設定で、Twitterアカウントを削除してみたが、通知はやってこないようだ。一旦、アプリケーションを終了させて、起動させると"can't send tweet"と表示された。




OSのTwitter機能を利用する場合、ユーザに利用していいのか確認のダイアログが表示されたが、何をやったら表示されるのだろうか?投稿可能かどうかのメソッドを呼ぶだけでは、この確認は表示されないようだ。そこで、投稿機能を追加してみた。





- (IBAction)tweet:(id)sender
{
    TWTweetComposeViewController *tweetViewController = [[TWTweetComposeViewController alloc] init];
    
    [tweetViewController setInitialText:@"hello, world"];
    
    [tweetViewController setCompletionHandler:^(TWTweetComposeViewControllerResult result) {
        NSString *output;
        
        switch (result) {
            case TWTweetComposeViewControllerResultCancelled:
                output = @"Tweet cancelled.";
                break;
            case TWTweetComposeViewControllerResultDone:
                output = @"Tweet done.";
                break;
            default:
                break;
        }
        
        [self performSelectorOnMainThread:@selector(displayText:)
                               withObject:output
                            waitUntilDone:NO];
        
        [self dismissModalViewControllerAnimated:YES];
    }];
    
    [self presentModalViewController:tweetViewController animated:YES];
}
 
- (void)displayText:(NSString *)text
{
    self.tweetStatusLabel.text = text;
}



おっと、先ほどアカウント情報を削除していたので、設定を催促するダイアログが表示された!




NoTwitterAccounts




今回は、既に設定済みの場合を確認したいので、アプリケーションを終了させて、アカウント情報を設定後に、再起動させてみた。




何も言われず投稿できてしまった。また、設定のTwitterを確認したところ、許可するアプリケーションの一覧にこのサンプルコードが追加されてる。あれ、ユーザに確認がこなかったけ?とりあえず、許可をOffしてみる。




すると、確認のダイアログが表示された。予想通りの動きでない部分があるが、この確認をフレームワーク側で勝手にやってくれるのかどうかを確認したかったので、よしとしよう。




TwitterIsNotEnabled





ソースコード

GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/ios/Tweets - GitHub




関連情報

iOS Twitter framework

Twitter Developersサイトの情報。

Tweeting

Developerサイトのサンプル・コード

[iOS]電子書籍への道:PDF表示(その4)

前回までの内容で、一つ、大きな過ちを犯している事に気がついた。プロジェクトを新規作成する際にDevcesをUniversalでなくiPhoneを選んでしまった。


結局、新たにプロジェクトを作成して、それに変更個所を反映して、iPad対応を行ったが、iPad対応で手を加えるべき場所をあげてみる。




iPad用のStoryboardを用意する。今回の場合、新規のPage-Based Applicationを作成して、そこから、iPad用のStoryboardをコピーすればよい。




Storyboard




TargetsでDevicesをUniversalに変更。




Universal




するとiPad Deployment Infoが現れるので、それに、先ほど追加してiPad用のStoryboardを設定する。







iPad用Storyboardコピー用に生成したプロジェクトで、iPhoneとiPadで条件わけしているコードを反映する。




ただ、自分で試してみたのだが、iPadで横置きにした際に、見開き表示になるはずだが、上手くいかなかったので、新規プロジェクトに変更個所を反映する対応にしてしまった。




run




まだ、多少の不具合があるが、続きは次回。






ソースコード

GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/ios/Books - GitHub




関連情報

第4回 出版プラットフォームとしてのiOS

Think IT 連載: iOSでつくる2011アプリ開発状況から。

Quartz 2d Programming Guide

Acroll View Programming Guide for iOS

ZoomingPEFViewer

Apple Developerサイトの情報。

Programming with Quartz: 2D and PDF Graphics in Mac OS X

OS XとiOSのCore Graphicsの解説書。

[iOS]電子書籍への道:PDF表示(その3)

前回のサンプル・プログラムをリソースとして持っているPDF文書からページ数を取得して、頁に対応したページを表示するように拡張する。




Page-Based Applicationの雛形は、データを管理するクラスModelControllerが用意されている。また、iOSには複数の文書を扱う仕組みが用意されているので、いずれは、それへの対応という事になるが、今回は自分が単一文書を扱う場合に用意している独自のDocumentクラスでPDF書類を管理する事にした。




本当は、PDF文書の扱いはDocumentクラスで抽象化すべきと思うが、サンプル・コードという事で、単にプロパティとして持っている事で許して欲しい。




@interface Document : NSObject

@property (nonatomic, assign) CGPDFDocumentRef  pdf;

@end



そして、各コントローラクラスで、Documentクラスをプロパティとして参照するようにする。例えば、初期化をinitメソッドで行う場合は、以下のとおり。




#import "AppDelegate.h"

- (id)init
{
    self = [super init];
    if (self) {
        AppDelegate
        appl = (AppDelegate *)[[UIApplication sharedApplication] delegate];
        self.document = appl.document;
    }
    return self;
}



ページ・ビュー・コントローラの利用では、各クラスの関係や、モデルの初期化のタイミングは少し悩んだので、分かりやすいように掻い摘んで説明する。




ADCのサンプルZoomingPDFViewでは、決めうちで、リソースで持っているPDF文書の1頁だけを表示するに固定されているので、PDFScrollViewの初期時にPDF文書関連の初期化を行っていたが、それを止めて、外部から指定されたタイミングで指定された頁を表示する様に変更する。




@interface PDFScrollView : UIScrollView 

- (void)setIndexOfPDF:(NSUInteger)index;
- (NSUInteger)getIndexOfPDF;

@end
 
@implementation PDFScrollView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
    }
    return self;
}
 
- (void)awakeFromNib
{
    self.decelerationRate = UIScrollViewDecelerationRateFast;
    self.delegate = self;
 
    AppDelegate
    appl = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    self.document = appl.document;
}
 
- (void)setIndexOfPDF:(NSUInteger)index
{
    self.page = CGPDFDocumentGetPage(self.document.pdf, index + 1);
    CGPDFPageRetain(self.page);
    
    CGRect pageRect = CGPDFPageGetBoxRect(self.page, kCGPDFMediaBox);
    self.pdfScale = self.frame.size.width/pageRect.size.width;
    pageRect.size = CGSizeMake(pageRect.size.width * self.pdfScale, pageRect.size.height * self.pdfScale);
    
    UIGraphicsBeginImageContext(pageRect.size);
    
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGContextSetRGBFillColor(context, 1.0,1.0,1.0,1.0);
    CGContextFillRect(context,pageRect);
    
    CGContextSaveGState(context);
    CGContextTranslateCTM(context, 0.0, pageRect.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
     
    CGContextScaleCTM(context, self.pdfScale, self.pdfScale);
    CGContextDrawPDFPage(context, self.page);
    CGContextRestoreGState(context);
    
    UIImage *backgroundImage = UIGraphicsGetImageFromCurrentImageContext();
    
    UIGraphicsEndImageContext();
    
    self.backgroundImageView = [[UIImageView alloc] initWithImage:backgroundImage];
    self.backgroundImageView.frame = pageRect;
    self.backgroundImageView.contentMode = UIViewContentModeScaleAspectFit;
    [self addSubview:self.backgroundImageView];
    [self sendSubviewToBack:self.backgroundImageView];
    
    self.pdfView = [[TiledPDFView alloc] initWithFrame:pageRect andScale:self.pdfScale];
    [self.pdfView setPage:self.page];
    
    [self addSubview:self.pdfView];
}
 
- (NSUInteger)getIndexOfPDF
{
    return (NSUInteger)CGPDFPageGetPageNumber(self.page);
}

@end



雛形ではカレンダーのデータを保持していたDataViewControllerクラスを表示している頁番号とPDFScrollViewクラスを扱うように変更する。




@interface DataViewController : UIViewController

@property (strong, nonatomic) Document                  *document;
@property (nonatomic, assign) NSUInteger                index;

- (void)setIndexOfPDF:(NSUInteger)index;
- (NSUInteger)getIndexOfPDF;
@end
 
@implementation DataViewController

- (void)setIndexOfPDF:(NSUInteger)index
{
    self.index = index;
    PDFScrollView   *pdfScrollView = (PDFScrollView *)self.view;
    [pdfScrollView setIndexOfPDF:index];
}
 
- (NSUInteger)getIndexOfPDF
{
    return self.index;
}

@end



頁に対応するビュー・コントローラDataViewControllerは、モデル・コントーラで管理される?ということで、PDF表示に対応させる。




@implementation ModelController

- (DataViewController *)viewControllerAtIndex:(NSUInteger)index storyboard:(UIStoryboard *)storyboard
{   
    if (CGPDFDocumentGetNumberOfPages(self.document.pdf) < index)
        return nil;
     
    DataViewController *dataViewController = [storyboard instantiateViewControllerWithIdentifier:@"DataViewController"];
    [dataViewController setIndexOfPDF:index];
    return dataViewController;
}
 
- (NSUInteger)indexOfViewController:(DataViewController *)viewController
{   
    return viewController.index;
}
 
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
    NSUInteger index = [self indexOfViewController:(DataViewController *)viewController];
    if ((index == 0) || (index == NSNotFound)) {
        return nil;
    }
     
    index--;
    return [self viewControllerAtIndex:index storyboard:viewController.storyboard];
}
 
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
    NSUInteger index = [self indexOfViewController:(DataViewController *)viewController];
    if (index == NSNotFound) {
        return nil;
    }
    
    index++;
    if (index == CGPDFDocumentGetNumberOfPages(self.document.pdf))
        return nil;
    return [self viewControllerAtIndex:index storyboard:viewController.storyboard];
}

@end



このサンプルをGitHubから取得して、実行すると、強制的に著者がCocoa勉強会で発表した文書を読まされる事になる。




Books





ソースコード

GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/ios/Books - GitHub




関連情報

第4回 出版プラットフォームとしてのiOS

Think IT 連載: iOSでつくる2011アプリ開発状況から。

Quartz 2d Programming Guide

Acroll View Programming Guide for iOS

ZoomingPEFViewer

Apple Developerサイトの情報。

Programming with Quartz: 2D and PDF Graphics in Mac OS X

OS XとiOSのCore Graphicsの解説書。

[iOS]電子書籍への道:PDF表示(その2)

先日紹介した『[iOS]ページ・ビュー・コントローラについて』のサンプル・プログラムにApple Developerサイトのサンプル・プログラムZoomingPDFViewを組み込んだだけなので、細かくは説明しない。




組み込む際に変更した箇所は、AtoryBoard対応や、最新のObjective-Cの記法への変更ぐらいで、ほぼ、そのまま。




電子書籍




次回以降で、このサンプル・プログラムを電子書籍アプリケーションの雛形として完成度を高めて行きたいと考えている。




ソースコード
GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/ios/Books - GitHub


関連情報
第4回 出版プラットフォームとしてのiOS

Think IT 連載: iOSでつくる2011アプリ開発状況から。

Quartz 2d Programming Guide

Acroll View Programming Guide for iOS

ZoomingPEFViewer

Apple Developerサイトの情報。

[iOS]電子書籍への道:PDF表示

前回の『[iOS]ページ・ビュー・コントローラについて』で作成した(殆ど、雛形のままだが)サンプル・プログラムに、Apple Developerサイトで交際されているサンプル・プログラムZoomingPDFViewを組み込んで、PDF表示に対応させようと考えている。




Think ITの連載「iOSでつくる2011アプリ開発状況」の「第4回 出版プラットフォームとしてのiOS」によるとiOSでPDFを表示させる為には、アプリケーション側での工夫が必要という事だ。Core Grahicsを使って、ViewのdrowRect:で描画すると、各代表辞された場合にぼやけてしまう。レイヤーに描画すれば拡大してもぼやけないが、描画が遅くなってしまう。そこで、拡大表示する場合は拡大前に作成した画像を表示させておいて、後で、奇麗な描画結果に差し替えるという対応が必要となるが、それのサンプルがZoomingPDFViewということのようだ。




続きは次回で。




ソースコード

GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/ios/Books - GitHub




関連情報

第4回 出版プラットフォームとしてのiOS

Think IT 連載: iOSでつくる2011アプリ開発状況から。


Quartz 2d Programming Guide

Acroll View Programming Guide for iOS

ZoomingPEFViewer

Apple Developerサイトの情報。

[OSX][iOS][Aruduino]銀座 Arduino ハンズオン (3回目)

AppleStore銀座で、三階目のAruduinoハンズオンセミナー初級編が開催されたので参加してきた。


今回こそは、完璧に理解してやるぞ!と意気込んだが、会場内だけで解決するのは無理なようだ。


その後の復習の結果をここで報告できたらと考えている。




Arduino





関連情報


Arduino

本家のホームページです。


Arduino 日本語リファレンス

助かります。ありがとうございます。


TETRASTYLE-dev-BLOG

iOSとArduinoの情報が豊富です。

[iOS]ページ・ビュー・コントローラについて

既存の紙の書籍の電子化は、ePubでiBooks。電子化ならではは、独自にアプリケーション化という流れがはっきりしてきたのか、独自にアプリケーション化を助けるフレーウワークとして、iOS 5からUIPageViewControllerが用意された。




新規プロジェクトとしてPage-Based Applicationが用意された。




新規プロジェクト




興味深いのは、ModelControllerクラス。以前、著者はモデルを管理するクラスとしてDocumentクラスについて説明したが、それに対応するクラスということか。




ModelController




各ページは、DataViewControllerで表示している。ページに対応するデータは、ModelControllerが管理している。




iPhoneの場合




iPhoneの場合は、前ページと後ページの同時に2ページ表示される。iPadでは、裏も表示されるので最大4ページ。これをフレームワークで管理してくれるということのようだ。




ソースコード
GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/ios/Books - GitHub


関連情報
UIPageViewController Class Reference

Apple Developerのサイト。

[Web]INTER-Mediatorについて(その2)

著者は、MacBook上のApacheを利用したが、PHPを有効にしておく事。


INTER-Mediatorのサイトからダウンロードした「develop-im」フォルダ内の「INTER-Mediator」が本体なので、これをデモ用のサイト配下にコピーする。




「index.php」ファイルを作成する。




<?php
require_once('INTER-Mediator/INTER-Mediator.php');
 
IM_Entry(














?>



HTMLファイルを用意する。




<!DOCTYPE  PUBLIC "-//W3C//DTD X 1.0 Transitional//EN" "http://www.w3.org/TR/x1/DTD/x1-transitional.dtd">
< xmlns="http://www.w3.org/1999/x">




















</html>



基本的に、TDタグにcassとしてIM[Artist@ARTIST_NAME]と指定すれば、ArtistテーブルのARTIST_NAMEが表示されている事が確認できると思う。

Web


簡単でしょ!




関連情報
INTER-Mediator

INTER-Mediatorのサイト。

[Web]INTER-Mediatorについて(その1)

INTER-Mediatorは、データベースとの連動が簡単に行える、Webのフレームワークだ。




MySQLにデモ用のデータベースを用意する。




$ mysqladmin -u root -p create DEMOCompactDisc
Enter password: 
$ mysql -u root -p mysql
Enter password: 

mysql> GRANT ALL PRIVILEGES ON DEMOCompactDisc.* TO demo
    -> IDENTIFIED BY 'test';
 
mysql> GRANT ALL PRIVILEGES ON DEMOCompactDisc.* TO demo@localhost
    -> IDENTIFIED BY 'test';
 
mysql> FLUSH PRIVILEGES;
 
mysql> quit
 
$ mysql -u demo -p DEMOCompactDisc
Enter password: 

mysql> CREATE TABLE CD (CD_ID INT NOT NULL,
    -> RECORD_LABEL_ID INT,
    -> CD_TITLE TEXT,
    -> PRIMARY KEY (CD_ID));
 
mysql> CREATE TABLE Artist (ARTIST_ID INT NOT NULL,
    -> ARTIST_NAME TEXT,
    -> PRIMARY KEY (ARTIST_ID));
 
mysql> CREATE TABLE Song (SONG_ID INT NOT NULL,
    -> CD_ID INT,
    -> ARTIST_ID INT,
    -> SONG_NAME TEXT,
    -> PRIMARY KEY (SONG_ID));
 
mysql> CREATE TABLE RecordLabel (RECORD_LABEL_ID INT NOT NULL,
    -> RECORD_LABEL_NAME TEXT,
    -> PRIMARY KEY (RECORD_LABEL_ID));
 
mysql> INSERT INTO Artist (ARTIST_ID, ARTIST_NAME) VALUES (1, "Robert Leroy Johnson");
 
mysql> select * from Artist;
+-----------+----------------------+
| ARTIST_ID | ARTIST_NAME          |
+-----------+----------------------+
|         1 | Robert Leroy Johnson |
+-----------+----------------------+
 
mysql> quit
$

[iOS]Looping Recorderの基礎(Audio Unit (7))

一応、録音した音が再生できた。だが、かなり荒いコードだ。そもそも、iPhoneシミュレータでAudio Unitを使用する場合、今のXcodeのバージョンだと問題があるようで、エラーメッセージが表示されていて、意図した動きをしない。自分のバグの可能性もあるが。実機で動作できたが、モノラル/ステレオの扱い等がいい加減なコードだ。でも、第一歩としては、今回はこれでヨシとしよう。




録音したデータをバッファに保存するコードだ。




- (void)write:(UInt32)inNumberFrames data:(AudioBufferList *)ioData
{
    uint32_t    available = self.maxSampleCount - self.startingSampleCount;
    if (available < inNumberFrames) {
        inNumberFrames = available;
    }
    memcpy(self.buffer + self.startingSampleCount, ioData->mBuffers[0].mData, sizeof(AudioUnitSampleType) * inNumberFrames);
    self.startingSampleCount = self.startingSampleCount + inNumberFrames;
    if (self.maxSampleCount <= self.startingSampleCount) {
        [self stop:nil];
    }
}

static OSStatus MyAURenderCallack(void *inRefCon,
                                  AudioUnitRenderActionFlags *ioActionFlags,
                                  const AudioTimeStamp *inTimeStamp,
                                  UInt32 inBusNumber,
                                  UInt32 inNumberFrames,
                                  AudioBufferList *ioData)
{
    AudioUnitViewController *viewController = (AudioUnitViewController *)inRefCon;
    [viewController write:inNumberFrames data:ioData];
    return noErr;
}



ループ再生するコードだ。




- (void)read:(UInt32)inNumberFrames data:(AudioBufferList *)ioData
{
    uint32_t    available = self.maxSampleCount - self.startingSampleCount;
    uint32_t    num = inNumberFrames;
    if (available < num) {
        num = available;
    }
    memcpy(ioData->mBuffers[0].mData, self.buffer + self.startingSampleCount, sizeof(AudioUnitSampleType) * num);
    self.startingSampleCount = self.startingSampleCount + num;
    if (self.maxSampleCount <= self.startingSampleCount)
        self.startingSampleCount = 0;
    if (num < inNumberFrames) {
        num = inNumberFrames - num;
        memcpy(ioData->mBuffers[0].mData, self.buffer + self.startingSampleCount, sizeof(AudioUnitSampleType) * num);
        self.startingSampleCount = self.startingSampleCount + num;
    }
    memcpy(ioData->mBuffers[1].mData, ioData->mBuffers[0].mData, sizeof(AudioUnitSampleType) * inNumberFrames);
}

static OSStatus MyPlayAURenderCallack (
                                       void                        *inRefCon,
                                       AudioUnitRenderActionFlags  *ioActionFlags,
                                       const AudioTimeStamp        *inTimeStamp,
                                       UInt32                      inBusNumber,
                                       UInt32                      inNumberFrames,
                                       AudioBufferList             *ioData
                                       )
{
    AudioUnitViewController *viewController = (AudioUnitViewController *)inRefCon;
    [viewController read:inNumberFrames data:ioData];
    return noErr;
}



例えば、鍵盤楽器のようなアプリケーションを作る場合は、Audio Unitのレスポンスの早さは魅力的だと思う。そうでなければ、ちょっと、面倒かな。




ソースコード

GitHubからどうぞ。

https://github.com/murakami/DemoAudio - GitHub




関連情報

Cocoa Life KOF2011特別編 - Facebook

Cocoa勉強会 関西の会誌。

iPhone Core Audioプログラミング(永野 哲久 著)

とれも参考にさせていただきました。