Cocoa練習帳 -33ページ目

[Mac][iOS]minizip ファイルの圧縮と解凍(その2)

前回の続き。得られたZipファイル内のパスからファイルのデータを取り出す。




        unzLocateFile(file, filename, 0);
        unzOpenCurrentFile(file);
        NSMutableData   *data = [NSMutableData data];
        void            *buffer = (void *)malloc(BUFSIZ);
        int             len;
        while ((len = unzReadCurrentFile(file, buffer, BUFSIZ)) != 0) {
            [data appendBytes:buffer length:len];
        }
        free(buffer);
        printf("----------\n");
        for (NSUInteger i = 0U; i < [data length]; i++) {
            printf("%c", ((char *)[data bytes])[i]);
        }
        printf("\n----------\n");
        unzCloseCurrentFile(file);



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

https://github.com/murakami/workbook/tree/master/mac/Zip


関連情報
zlib

A Massively Spiffy Yet Delicately Unobtrusive Compression Library

Minizip

Zip and UnZIp additionnal library

ZipArchive

An Objective C class for zip/unzip on iPhone and Mac OSX

objective-zip

An iOS wrapper for ZLib and MiniZip

Objective-CでZIPアーカイブを読み取る

@marvelphさんのブログです。

関東第51回Cocoa勉強会のご案内

関東第51回Cocoa勉強会の日程が決定いたしました。
日時: 2012/03/17(土) 13:00-17:00
会場:新宿三丁目 新宿伊藤ビル 4F
集合:現地
会費:500円
見学者は以下のフォームで募集しています。
http://www.cocoa-study.com/mail/

[Mac][iOS]minizip ファイルの圧縮と解凍

前回のZipArchiveライブラリが内部で利用していたminizipライブラリを直接利用する事に挑戦する。


minizipを直接利用する利点は、複数のファイルが固められて圧縮されていた場合、全てを解凍するのではなくて、必要なファイルのみにアクセスする事が可能になる事だ。




以下のMinizipのサイトからminizipライブラリ一式をダウンロードする。




Minizip



minizip一式には、コマンドラインのプログラム用のソースファイルが含まれているので、ライブラリとして使用する場合に必要となる、以下のファイルのみをプロジェクトに追加する。




crypt.h
ioapi.c
ioapi.h
mztools.c
mztools.h
unzip.c
unzip.h
zip.c
zip.h



プロジェクトに以下のライブラリを追加する。




libz.dylib



文書オープンのダイアログで指定したzipファイルの中身を出力するコードを追加する。




@interface AppDelegate : NSObject 
        :
- (IBAction)openDocument:(id)sender;
@end



#import "unzip.h"
        :
@interface AppDelegate ()
- (void)unzip:(NSString *)path;
@end
 
@implementation AppDelegate
        :
- (IBAction)openDocument:(id)sender
{
    DBGMSG(@"%s", __func__);
    NSOpenPanel *panel = [NSOpenPanel openPanel];
    [panel beginSheetModalForWindow:self.window
                  completionHandler:^(NSInteger returnCode){
        NSURL       *pathToFile = nil;
        NSString    *path = nil;
         
        if (returnCode == NSOKButton) {
            pathToFile = [[panel URLs] objectAtIndex:0];
            path = [pathToFile path];
            DBGMSG(@"%@", pathToFile);
            DBGMSG(@"%@", path);
            dispatch_async(dispatch_get_main_queue(), ^{
                [self unzip:path];
            });
        }
    }];
}
 
- (void)unzip:(NSString *)path
{
    DBGMSG(@"%s, %@", __func__, path);
    int error = UNZ_OK;
    unzFile file = NULL;
    file = unzOpen([path UTF8String]);
    unzGoToFirstFile(file);
    while (error == UNZ_OK) {
        unz_file_info   fileInfo;
        char            filename[PATH_MAX];
        unzGetCurrentFileInfo(file, &fileInfo, filename, PATH_MAX, NULL, 0, NULL, 0);
        DBGMSG(@"%s", filename);
        error = unzGoToNextFile(file);
    }
    unzClose(file);
}
@end



以下のディレクトリ構造のフォルダをzipで圧縮する。





junk
 | file01.txt
 | file02.txt
 +- folder01
 |    file11.txt
 |    file12.txt
 +- folder02
      file21.txt
      file22.txt



これを先ほどのコードでディレクトリ構造をデバッグ出力した結果の抜粋が以下。




junk/
junk/file01.txt
junk/file02.txt
junk/folder01/
junk/folder01/file11.txt
junk/folder01/file12.txt
junk/folder02/
junk/folder02/file21.txt
junk/folder02/file22.txt



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

https://github.com/murakami/workbook/tree/master/mac/Zip


関連情報
zlib

A Massively Spiffy Yet Delicately Unobtrusive Compression Library

Minizip

Zip and UnZIp additionnal library

ZipArchive

An Objective C class for zip/unzip on iPhone and Mac OSX

objective-zip

An iOS wrapper for ZLib and MiniZip

Objective-CでZIPアーカイブを読み取る

@marvelphさんのブログです。

[Mac][iOS]ZipArchive ファイルの圧縮と解凍

iOSはストレージ容量もメモリも節約を求められる。また、データサイズが小さくなる事によって、操作性の向上も期待できる。

その為にキーとなる事の一つにデータの圧縮と解凍があるが、今回は、Cocoaでzipデータを扱う初歩的に事に挑戦する。


業務上パスワード付きzip圧縮ファイルを受け取る事が多いのだが、何時もこれを解凍するのをコマンドラインでおこなっていた。




$ unzip -P パスワード ファイル.zip



これをAppStore時代では存在が小さくなってしまったが、Dropletsとして実装してみよう。


Mac OS XのCocoa Applicationを新規作成し、TARGETSの設定のDocument TypesにZip文書を追加する。







アプリケーションのデリゲートに、以下のコードを追加する。




- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
{
    DBGMSG(@"%s, filename(%@)", __func__, filename);
    return NO;
}



これでドラッグ&ドロップされた一つのファイルのパスが取得できるようになった。


次に、ZipArchiveライブラリを以下のサイトから入手する。




http://code.google.com/p/ziparchive/


入手したZipArchiveライブラリをプロジェクトに組み込み、ZipArchive.hでUIKit.hをインクルードしているのでコメントアウトし、




ZipArchive.h




ZipArchive.mmをARCの対象から外す。




ZipArchive.mm




そして、かなり手抜きをしているが、zipファイルが渡されると、末尾の"zip"サフィクスを削るコードを先ほどのメソッドに追加する。




#import "ZipArchive.h"
        :
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
{
    ZipArchive  *archiver = [[ZipArchive alloc] init];
    [archiver UnzipOpenFile:filename];
    NSString    *path = [filename stringByDeletingPathExtension];
    [archiver UnzipFileTo:path overWrite:NO];
    [archiver UnzipCloseFile];
    return YES;
}
        :



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

 https://github.com/murakami/workbook/tree/master/mac/UnZip


関連情報
ZipArchive

An Objective C class for zip/unzip on iPhone and Mac OSX

[iOS][Web]ネイティブWebアプリケーション(その2)

DocumentsディレクトリやLibraryディレクトリ配下への配置を考えたが、課題があるということで、バンドル・リソース内に配置する方法を試してみた。




実際にWebアプリケーションを組み込む状況を想定して、前回、用意してHTMLコンテンツが置かれているwebappフォルダ配下に、infoとaboutフォルダを追加して、このディレクトリ構造が保たれたまま、アクセスできる事を確認したいと思う。




dir




infoとaboutフォルダをXcodeのプロジェクトにドラッグ&ドロップする。すると、以下のシートが表示されるが、Distinationのチェックは外し、Foldersは2番目の"Create folder references for any added folders"を選択する。




dragdrop




するとプロジェクトでの表示は、以下となる。




proj




"webapp/index.html"の内容は以下のとおり。


<html>
    <head>
        <title>My WebApp</title>
    </head>
    <body>
        <p>My WebApp</p>
        <a href="info/index.html">info</a><br />
        <a href="about/index.html">about</a>
    </body>
</html>



"webapp/info/index.html"の内容は以下のとおり。


<html>
    <head>
        <title>Info</title>
    </head>
    <body>
        <p>Info</p>
    </body>
</html>



"webapp/about/index.html"の内容は以下のとおり。


<html>
    <head>
        <title>About</title>
    </head>
    <body>
        <p>About</p>
    </body>
</html>



実行。infoとaboutのリンクが機能するので、バンドル・リソース内でもディレクトリ構造は保持されている事が分かる。




index




info

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

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


関連情報
File System Programming Guide

iOS標準ディレクトリのファイル保存場所が説明されている。

[iOS][Web]ネイティブWebアプリケーション

WWDCで、最初のiPhoneの開発環境の紹介があった際は、Webアプリケーションのみを認めるという説明だったが不要だった為、その後、ネイティブ・アプリケーションが開発できるSDKがリリースされた。


とは云うものの、Webアプリケーションでも対応できる分野はあると思われるし、WebアプリケーションとネイティブAPIを組み合わせる手法も出来ているので、今回は、ネイティブなWebアプリケーションの初歩の初歩に挑戦してみた。




Single View Applicationのプロジェクトを新規生成する。




新規プロジェクト




ViewをUIWebViewに置換する。




WebView




HTMLコンテンツを置くフォルダをグループとして登録し、"index.html"を新規ファイルとして生成する。




webapp




ViewControllerに、アプリケーション内部のHTMLコンテンツを表示するコードを追加する。




- (void)viewDidLoad
{
    [super viewDidLoad];
     
    UIWebView   *webView = (UIWebView *)self.view;
    NSString    *path = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"];
    NSURL   *fileURL = [[NSURL alloc] initFileURLWithPath:path];
    NSURLRequest    *req = [NSURLRequest requestWithURL:fileURL];
    [webView loadRequest:req];
}



実行。




実行




まだ、これは初歩の初歩で、HTMLコンテンツの置き場所はバンドル・リソースでいいのか?とか、ディレクトリ構造を保持したまま、実行時の環境にコピーするには?とか課題があるが、それは次回で。




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

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


関連情報
User Experience Coding How-To's

Apple Developerサイトの情報。

iPhone Webアプリケーション

Webアプリケーションの見本として参考になる。

[Mac]談話と特殊効果(Text-to-Speech)

WWDCに行くと、どんな方向を目指しているのか感じ取る事が出来る。また、その場で理解できなくても、これは聞いた事があるな?と、調査のヒントを得られる。今回は、WWDCが解決の糸口になった例だ。




OS Xでテキストの内容をスピーチされる事は簡単だ。




Cocoaでは、




NSSpeechSynthesizer *synthesizer = [[NSSpeechSynthesizer alloc] init];
[synthesizer setDelegate:self];
[synthesizer startSpeakingString:@"Hello, world."];
 
- (void)speehSynthesizer:(NSSpeechSynthesizer *)sender
    didFinishSpeaking:(BOOL)finishedpeaking
{
    ...
}



Core Foundationでは、




SpeechChannel *chan;
err = NewSpeechChannel(NULL, &chan);
CFNumber *callback = CFNumberCreate(NULL, kCFNumberLongType, HighlightSpokenWord);
err = SetSpeechProperty(chan, kSpeechWordCFCallBack, callback);
err = SpeakCFString(chan, CFSTR("Hello, world."), NULL);
 
void HighlightSpokenWord(SpeechChannel chan,
    SRefCo refCon,
    CFStringRef aString,
    CFRange wordRange)
{
    ....
}



でも、それにAudio Unitを使った効果を適用させるには、どうすればいいのか?




どうして、これが出来ると思ったのかは今となっては思い出せないが、多分、WWDCでのセッションの内容が頭の片隅にあったのだろう。それで、WWDCのセッションのビデオを見直しのが、WWDC2009のSession 129『Text-to-Speech: Adventures with Alex』だった。




一度、解決の糸口をつかむと、後は芋づる式。ヘッダーファイルのコメントで説明されていた。




SpeechSynthesis.h
 
/*------------------------------------------*/
/* AudioUnit constants - new in 10.5        */
/*------------------------------------------*/
enum {
  kAudioUnitSubType_SpeechSynthesis = 'ttsp', /* kAudioUnitType_Generator */
  kAudioUnitProperty_Voice      = 3330, /* Get/Set (VoiceSpec)      */
  kAudioUnitProperty_SpeechChannel = 3331 /* Get (SpeechChannel)      */
};



指定した文章をSpeech Synthesisで喋らせ、それをAudio Unitのコンポーネントでディレイさせて再生させる例が以下だ。




AUNode

NewAUGraph(&_auGraph);
    
AudioComponentDescription
cd.componentType = kAudioUnitType_Generator;
cd.componentSubType = kAudioUnitSubType_SpeechSynthesis;
cd.componentManufacturer = kAudioUnitManufacturer_Apple;
cd.componentFlags = 0;
cd.componentFlagsMask = 0;

AUGraphAddNode(_auGraph, &cd, &inputNode);

cd.componentType = kAudioUnitType_Effect;
cd.componentSubType = kAudioUnitSubType_Delay;
AUGraphAddNode(_auGraph, &cd, &effectNode);

cd.componentType = kAudioUnitType_Output;
cd.componentSubType = kAudioUnitSubType_DefaultOutput;
AUGraphAddNode(_auGraph, &cd, &outputNode);

AUGraphConnectNodeInput(_auGraph, inputNode, 0, effectNode, 0);
AUGraphConnectNodeInput(_auGraph, effectNode, 0, outputNode, 0);

AUGraphOpen(_auGraph);
AUGraphInitialize(_auGraph);
    
AudioUnit
AUGraphNodeInfo(_auGraph, inputNode, NULL, &generateAudioUnit);
SpeechChannel
UInt32
AudioUnitGetProperty(generateAudioUnit, kAudioUnitProperty_SpeechChannel,


AUGraphStart(_auGraph);

SpeakCFString(channel, CFSTR("Hello, world."), NULL);



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

https://github.com/murakami/workbook/tree/master/mac/TextToSpeech - GitHub


関連情報
Speech

Speech Synthesis Programmig Guide

Using the Japanese Analysis Engine and Access Method



WWDCノススメ

この時期になってくると気になるのは、今年のWWDCについてだ。


今年は、どうしましょう。行くべきでしょうね。


Cocoa勉強会でも、WWDCについての情報を発信しています。




WWDCノススメ 2008

WWDCノススメ 2009

WWDCノススメ 2010

[iOS]Image Masking(その3)

前回のクリッピングではCoreGraphicsの関数を使ったが、今回はUIKitのUIBezierPathを使ったクリップングに挑戦だ。




前回のCoreGraphicsの関数を使ったコードだ。




- (void)drawRect:(CGRect)rect
{
    CGContextRef    context = UIGraphicsGetCurrentContext();
    CGSize  imageSize = self.image.size;
    CGRect  imageRect = {10.0, 10.0, imageSize.width, imageSize.height};
    float   radius = 10.0;
    CGFloat minX = CGRectGetMinX(imageRect);
    CGFloat midX = CGRectGetMidX(imageRect);
    CGFloat maxX = CGRectGetMaxX(imageRect);
    CGFloat minY = CGRectGetMinY(imageRect);
    CGFloat midY = CGRectGetMidY(imageRect);
    CGFloat maxY = CGRectGetMaxY(imageRect);
    
    /* 現状の描画環境を保存 */
    CGContextSaveGState(context);
    
    /* 四角形の辺に接する、半径radiusの円弧を四隅に追加 */
    CGContextMoveToPoint(context, minX, midY);
    CGContextAddArcToPoint(context, minX, minY, midX, minY, radius);
    CGContextAddArcToPoint(context, maxX, minY, maxX, midY, radius);
    CGContextAddArcToPoint(context, maxX, maxY, midX, maxY, radius);
    CGContextAddArcToPoint(context, minX, maxY, minX, midY, radius);
    CGContextClosePath(context);
    
    /* 先ほどのパスをクリップ領域として設定 */
    CGContextClip(context);
    
    /* 描画 */
    [self.image drawAtPoint:CGPointMake(10.0, 10.0)];
    
    /* 描画環境を先ほどの保存時点に戻す */
    CGContextRestoreGState(context);
}



UIBezierPathを使うとこうなる。ちょっと、四隅が丸い四角の例なので、簡単なコードになってしまったが。




- (void)drawRect:(CGRect)rect
{
    CGContextRef    context = UIGraphicsGetCurrentContext();
    CGSize  imageSize = self.image.size;
    CGRect  imageRect = {10.0, 10.0, imageSize.width, imageSize.height};
    float   radius = 10.0;
    
    /* 現状の描画環境を保存 */
    CGContextSaveGState(context);
    
    /* 四角形の四隅を半径radiusの円弧に */
    UIBezierPath*   aPath = [UIBezierPath bezierPathWithRoundedRect:imageRect cornerRadius:radius];
    
    /* パスをクリップ領域として設定 */
    [aPath addClip];
    
    /* 描画 */
    [self.image drawAtPoint:CGPointMake(10.0, 10.0)];
   
    /* 描画環境を先ほどの保存時点に戻す */
    CGContextRestoreGState(context);
}



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

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


関連情報
Programming with Quartz: 2D and PDF Graphics in Mac OS X

CoreGraphicsを解説した書籍。

Drawing and Printing Guide for iOS

iOS Developer Libraryの情報。

[iOS]座標と描画(その4)

前回の例で、ビットマップ・コンテキストを利用していたが、Retina displayとなった現在では、以下の方法がお勧めだ。




前回では、CoreGraphicsの関数を使ってビットマップ・コンテキストを生成して、そこに描画していた。




- (void)drawRect:(CGRect)rect
{
    CGContextRef    context = UIGraphicsGetCurrentContext();
    
    /* LLO(lower-left-origin) */
    size_t  witdh = rect.size.width;
    size_t  height = rect.size.height;
    size_t  bytesPerRow = witdh * 4;
    bytesPerRow = COMPUTE_BEST_BYTES_PER_ROW(bytesPerRow);
    unsigned char   *rasterData = calloc(1, bytesPerRow * height);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef    bitmapContext = CGBitmapContextCreate(rasterData, witdh, height,
                                                8, bytesPerRow,
                                                colorSpace,
                                                kCGImageAlphaPremultipliedLast);
    
    CGContextSetRGBStrokeColor(bitmapContext, 1.0, 0.0, 0.0, 1.0);
    CGContextSetLineWidth(bitmapContext, 4.0);
    CGContextBeginPath(bitmapContext);
    CGContextMoveToPoint(bitmapContext, 5.0, 25.0);
    CGContextAddLineToPoint(bitmapContext, 5.0, 5.0);
    CGContextDrawPath(bitmapContext, kCGPathStroke);
    CGContextMoveToPoint(bitmapContext, 5.0, 5.0);
    CGContextAddLineToPoint(bitmapContext, 25.0, 5.0);
    CGContextDrawPath(bitmapContext, kCGPathStroke);


    CGImageRef  cgimage = CGBitmapContextCreateImage(bitmapContext);
    CGContextDrawImage(context, rect, cgimage);
    CGContextRelease(bitmapContext);
    free(rasterData);
    CGColorSpaceRelease(colorSpace);
}



iOS4からは、以下のように記述できる。




- (void)drawRect:(CGRect)rect
{
    CGContextRef    context = UIGraphicsGetCurrentContext();
    
    /* ULO(upper-left-origin) */
    UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0);
    CGContextRef    bitmapContext = UIGraphicsGetCurrentContext();
    
    CGContextSetRGBStrokeColor(bitmapContext, 1.0, 0.0, 0.0, 1.0);
    CGContextSetLineWidth(bitmapContext, 4.0);
    CGContextBeginPath(bitmapContext);
    CGContextMoveToPoint(bitmapContext, 5.0, 25.0);
    CGContextAddLineToPoint(bitmapContext, 5.0, 5.0);
    CGContextDrawPath(bitmapContext, kCGPathStroke);
    CGContextMoveToPoint(bitmapContext, 5.0, 5.0);
    CGContextAddLineToPoint(bitmapContext, 25.0, 5.0);
    CGContextDrawPath(bitmapContext, kCGPathStroke);
 
    UIImage*    uiimage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    [uiimage drawAtPoint:CGPointMake(0.0, 0.0)];
}



この方法だと、- (void)drawRect:(CGRect)rectメソッドの描画環境と座標系が同じとなる。




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

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


関連情報
Programming with Quartz: 2D and PDF Graphics in Mac OS X

Drawing and Printing Guide for iOS

Apple Developerの情報です。