トップ 最新 追記

Cocoa練習帳

iOS/iPhone/iPad/watchOS/tvOS/MacOSX/Android プログラミング, Objective-C, Cocoa, Swiftなど

2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|09|10|11|12|
2024|01|02|

2012-06-03 [iOS]アラートのカスタマイズ(5)

モーダルビューが全画面表示される場合、裏に回ったビューが解放される事もあると思うので、モーダルビューが透けて裏が見えるのは宜しくないのだろう。ということで、アラート風のビューを表示する方法を試す事にした。

Storyboardでビューを用意。InterfaceBuilderの仕様上か、自分が使い方を知らない為か、アラート風のビューを既存の画面の上に生成した。

view

このビューを指すアウトレットと、ボタンに対応するアクションをヘッダーに追加する。

@interface ViewController : UIViewController
	...
@property (strong, nonatomic) IBOutlet UIView   *modalPaneView;
	...
- (IBAction)done:(id)sender;
- (IBAction)cancel:(id)sender;
	...
@end

アラートの表示/非表示は、ビューの表示/非表示で簡単に行える。

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.modalPaneView.hidden = YES;
}
 
- (IBAction)done:(id)sender
{
    self.modalPaneView.hidden = YES;
}
 
- (IBAction)cancel:(id)sender
{
    self.modalPaneView.hidden = YES;
}
run

カスタマイズするのなら、アラート風のビューを用意するでいいのかもしれないが、アラートとして動作して欲しいのなら、UIAlertViewにビューを追加し、アラート上の各要素の位置を調整する方法でもいいのかな?

_ ソースコード

GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/ios/Dialog - GitHub

_ 関連情報

[iOS] UIAlertView 上に UIProgressView を載せる [2] キャンセルボタン表示
『Cocoaの日々』いつも参考にさせていただいています。ありがとう!助かります。

2012-06-04 [Web]INTER-Mediator Jelly

Webアプリケーションフレームワーク『INTER-Mediator』のJellyが、新宿 - TRAVELERS Coworkingで開催されていたので参加してきた。

新宿 - TRAVELERS Coworking

参加の理由は、サーバ側のスキルアップというのもあるが、7月から在宅作業の比率が上がるため、外の仕事場の利用の実験もかねている。

その為、朝から一日中コワーキング・スペースに閉じこもって作業してみたのだが、作業効率は高かったが、他人の目があるので緊張感があり、非常に疲れた。ただ、在宅作業の場合、緊張感が下がることがあるかと思うが、たまには、こんな場所で仕事をして気を引き締めるのはいいかなと思った。

Jelly自体の内容は、いずれ、再度、INTER-Mediatorを取り上げたいと思っているので、そこで触れたいと思う。


2012-06-05 [iOS]単体試験

Xcodeのプロジェクトの雛形には、単体試験が用意されているが、少し疑問もあったので、この機会に試してみた。

単体試験と考えると、クラス単位でインスタンスを生成して、各メソッドの動作を確認して、インスタンスを破棄という流れが思い浮かぶが、iOSアプリケーションの様に、他のクラスとの関係がある場合は、あるクラスを試験する為に、他のクラスを生成してと手順があり、このやり方は現実的でないと考えたのだろうか?単体試験を実行すると、アプリケーションが起動して、その状態で単体試験を行う仕組みになっているようだ。

最初は違和感を持ったが、こちらの方がいいと思い出してきた。

単体試験はプロジェクト生成後に組み込む事は出来るが、新規プロジェクトの作成時に用意するのが楽だ。

新規プロジェクト

サンプルコードでは、以前紹介した事がある、Documentクラスが追加されただけの状態で。

今回は、このDocumentクラスに対する単体試験のクラスDocumentTestsを追加してみた。

試験ケース

その内容だが、アプリケーション自体でDocumentクラスのインスタンスを生成しているので、それを取り出し、各メソッドに対して試験を行う。

#import <SenTestingKit/SenTestingKit.h>
#import "Document.h"
 
@interface DocumentTests : SenTestCase
 
@property (strong, nonatomic) Document  *document;
 
@end
#import "AppDelegate.h"
#import "DocumentTests.h"
 
@implementation DocumentTests
 
@synthesize document = _document;
 
- (void)setUp
{
    DBGMSG(@"%s", __func__);
    [super setUp];
    
    AppDelegate	*appl = nil;
	appl = (AppDelegate *)[[UIApplication sharedApplication] delegate];
	self.document = appl.document;
}
 
- (void)testInit
{
    DBGMSG(@"%s", __func__);
    STAssertNotNil(_document, @"初期化失敗");
    STAssertFalse(self.document.version == nil, @"メンバーversion初期化不正");
    STAssertFalse(self.document.message == nil, @"メンバーmessage初期化不正");
}
 
- (void)tearDown
{
    DBGMSG(@"%s", __func__);
    self.document = nil;
     
    [super tearDown];
}
 
- (void)testClearDefaults
{
    DBGMSG(@"%s", __func__);
    [self.document clearDefaults];
    STAssertNil([[NSUserDefaults standardUserDefaults] objectForKey:@"message"], @"初期化失敗");
}
 
- (void)testUpdateDefaults
{
    DBGMSG(@"%s", __func__);
    NSString    *msg = [[NSDate date] description];
    self.document.message = msg;
    [self.document updateDefaults];
    STAssertNotNil([[NSUserDefaults standardUserDefaults] objectForKey:@"message"], @"初期化失敗");
    STAssertTrue([msg compare:[[NSUserDefaults standardUserDefaults] objectForKey:@"message"]] == NSOrderedSame,
                 @"メンバーmessage保存失敗");
}
 
- (void)testLoadDefaults
{
    DBGMSG(@"%s", __func__);
    [self.document loadDefaults];
    STAssertFalse(self.document.version == nil, @"メンバーversion読み出し不正");
    STAssertFalse(self.document.message == nil, @"メンバーmessage読み出し不正");
}
 
@end

以下は、試験のログだ。

2012-06-06 00:04:57.633 Exam[1795:fb03] -[Document init]
2012-06-06 00:04:57.640 Exam[1795:fb03] -[Document loadDefaults]
2012-06-06 00:04:57.642 Exam[1795:fb03] read message:2012-06-05 15:02:08 +0000
Test Suite 'All tests' started at 2012-06-05 15:04:57 +0000
Test Suite '/Users/ユーザー/Library/Developer/Xcode/DerivedData/Exam-文字列/Build/Products/Debug-iphonesimulator/ExamTests.octest(Tests)' started at 2012-06-05 15:04:57 +0000
Test Suite 'DocumentTests' started at 2012-06-05 15:04:57 +0000
Test Case '-[DocumentTests testClearDefaults]' started.
2012-06-06 00:04:57.770 Exam[1795:fb03] -[DocumentTests setUp]
2012-06-06 00:04:57.771 Exam[1795:fb03] -[DocumentTests testClearDefaults]
2012-06-06 00:04:57.772 Exam[1795:fb03] -[Document clearDefaults]
2012-06-06 00:04:57.773 Exam[1795:fb03] remove message:2012-06-05 15:04:57 +0000
2012-06-06 00:04:57.774 Exam[1795:fb03] -[DocumentTests tearDown]
Test Case '-[DocumentTests testClearDefaults]' passed (0.005 seconds).
Test Case '-[DocumentTests testInit]' started.
2012-06-06 00:04:57.775 Exam[1795:fb03] -[DocumentTests setUp]
2012-06-06 00:04:57.776 Exam[1795:fb03] -[DocumentTests testInit]
2012-06-06 00:04:57.777 Exam[1795:fb03] -[DocumentTests tearDown]
Test Case '-[DocumentTests testInit]' passed (0.003 seconds).
Test Case '-[DocumentTests testLoadDefaults]' started.
2012-06-06 00:04:57.778 Exam[1795:fb03] -[DocumentTests setUp]
2012-06-06 00:04:57.779 Exam[1795:fb03] -[DocumentTests testLoadDefaults]
2012-06-06 00:04:57.780 Exam[1795:fb03] -[Document loadDefaults]
2012-06-06 00:04:57.781 Exam[1795:fb03] -[DocumentTests tearDown]
Test Case '-[DocumentTests testLoadDefaults]' passed (0.004 seconds).
Test Case '-[DocumentTests testUpdateDefaults]' started.
2012-06-06 00:04:57.782 Exam[1795:fb03] -[DocumentTests setUp]
2012-06-06 00:04:57.783 Exam[1795:fb03] -[DocumentTests testUpdateDefaults]
2012-06-06 00:04:57.784 Exam[1795:fb03] -[Document updateDefaults]
2012-06-06 00:04:57.785 Exam[1795:fb03] current aVersion:1.0
2012-06-06 00:04:57.786 Exam[1795:fb03] save message:2012-06-05 15:04:57 +0000
2012-06-06 00:04:57.788 Exam[1795:fb03] -[DocumentTests tearDown]
Test Case '-[DocumentTests testUpdateDefaults]' passed (0.007 seconds).
Test Suite 'DocumentTests' finished at 2012-06-05 15:04:57 +0000.
Executed 4 tests, with 0 failures (0 unexpected) in 0.019 (0.020) seconds
Test Suite 'ExamTests' started at 2012-06-05 15:04:57 +0000
Test Case '-[ExamTests testExample]' started.
2012-06-06 00:04:57.789 Exam[1795:fb03] -[ExamTests setUp]
2012-06-06 00:04:57.790 Exam[1795:fb03] -[ExamTests testExample]
2012-06-06 00:04:57.791 Exam[1795:fb03] -[ExamTests tearDown]
Test Case '-[ExamTests testExample]' passed (0.003 seconds).
Test Suite 'ExamTests' finished at 2012-06-05 15:04:57 +0000.
Executed 1 test, with 0 failures (0 unexpected) in 0.003 (0.003) seconds
Test Suite '/Users/ユーザー/Library/Developer/Xcode/DerivedData/Exam-文字列/Build/Products/Debug-iphonesimulator/ExamTests.octest(Tests)' finished at 2012-06-05 15:04:57 +0000.
Executed 5 tests, with 0 failures (0 unexpected) in 0.022 (0.023) seconds
Test Suite 'All tests' finished at 2012-06-05 15:04:57 +0000.
Executed 5 tests, with 0 failures (0 unexpected) in 0.022 (0.023) seconds

合格のようだ。

_ ソースコード

GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/ios/Exam - GitHub

2012-06-06 [iOS]単体試験(2)

日本語のドキュメントがあってよかった。

Xcodeで用意されている単体試験の環境は、以前、Cocoa勉強会でも発表された事があるオープンソースのSenTestingKitフレームワークだ。

このフレームワークは、ロジックテストとアプリケーションテストの2種類の単体試験を提供しているものだが、Xcodeの新規プロジェクトの雛形に用意されているのは、アプリケーションテストの方だ。これは、ログに「All tests」という文言が印字されているので分かる。

前回の記事では、単体試験で利用できるマクロについて説明する事が出来なかったので、一覧を用意してみた。これは著者自身の為でもある。

STFail(failure_description, ...)
テストケースを失敗させる。
STAssertEqualObjects(object_1, object_2, failure_description, ...)
2つのオブジェクトが異なる場合、テストケースは失敗する。
STAssertEquals(value_1, value_2, failure_description, ...)
2つの値が異なる場合、テストケースは失敗する。
STAssertEqualsWithAccuracy(value_1, value_2, accuracy, failure_description, ...)
2つの値の差が閾値より大きい場合、テストケースは失敗する。
STAssertNil(expression, failure_description, ...)
式がnilでない場合、テストケースは失敗する。
STAssertNotNil(expression, failure_description, ...)
式がnilの場合、テストケースは失敗する。
STAssertTrue(expression, failure_description, ...)
式が偽の場合、テストケースは失敗する。
STAssertFalse(expression, failure_description, ...)
式が真の場合、テストケースは失敗する。
STAssertThrows(expression, failure_description, ...)
式が例外を発生させない場合、テストケースは失敗する。
STAssertThrowsSpecific(expression, exception_class, failure_description, ...)
式が特定の例外を発生させない場合、テストケースは失敗する。
STAssertThrowsSpecificNamed(expression, exception_class, exception_name, failure_description, ...)
式が特定の名前の例外を発生させない場合、テストケースは失敗する。
STAssertNoThrow(expression, failure_description, ...)
式が例外を発生させる場合、テストケースは失敗する。
STAssertNoThrowSpecific(expression, exception_class, failure_description, ...)
式が特定の例外を発生させる場合、テストケースは失敗する。
STAssertNoThrowSpecificNamed(expression, exception_class, exception_name, failure_description, ...)
式が特定の名前の例外を発生させる場合、テストケースは失敗する。
STAssertTrueNoThrow(expression, failure_description, ...)
式が偽または例外発生の場合、テストケースは失敗する。
STAssertFalseNoThrow(expression, failure_description, ...)
式が真または例外発生の場合、テストケースは失敗する。

_ ソースコード

GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/ios/Exam - GitHub

2012-06-07 [iOS]グラフ描画ライブラリ(2)

ずっと棚上げしていた、S7GraphViewに代わるグラフ描画ライブラリの作成に挑戦する。

そもそもの動機が、S7GraphViewの開発が中止となってしまった為で、気に入らない訳ではない。パクリにならないように、独自の実装にしたいのだが、どうしても影響を受けてしまうのが辛い。

参考にしたのが、Cocoa Touchのテーブルビューだ。こいつはデータをデリゲートで受け取っている。この構成にしてみようと思った。

思いついた実装は2種類で、データ一式を渡して、ライブラリ内部で描画してもらう方法。

データ

S7GraphViewは、この方法。テーブルビューはデリゲートだ。

デリゲート

続きは次回。

_ 関連情報

http://code.google.com/p/s7graphview/
S7GraphViewのサイト。残念ながら、閉鎖されたようだ。

2012-06-09 [iOS]グラフ描画ライブラリ(3)

S7GraphViewに代わるものを!と考えて、S7GraphViewって何だっけ?と考えると、結構忘れていた。
入手できなくなったS7GraphViewを使って例で申し訳ないが、ちょっと、S7GraphViewを使ったサンプルを作成してみて、どんな物だったのかを思い出してみた。

S7GraphViewインスタンスの生成はStoryboardで行っている。

以下は所属するビューコントローラの初期か部分だ。

- (void)viewDidLoad
{
    [super viewDidLoad];
 
    //self.s7graphView = [[S7GraphView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
	//self.view = self.s7graphView;
	self.s7graphView.dataSource = self;
    //self.view.backgroundColor = [UIColor yellowColor];
 
    NSNumberFormatter *numberFormatter = [NSNumberFormatter new];
    [numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
    [numberFormatter setMinimumFractionDigits:0];
    [numberFormatter setMaximumFractionDigits:0];
    
    self.s7graphView.yValuesFormatter = numberFormatter;
    
    NSDateFormatter *dateFormatter = [NSDateFormatter new];
    [dateFormatter setTimeStyle:NSDateFormatterShortStyle];
    [dateFormatter setDateStyle:NSDateFormatterNoStyle];
    [dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"ja_JP"]];
    
    self.s7graphView.xValuesFormatter = dateFormatter;
        
    self.s7graphView.backgroundColor = [UIColor blackColor];
    
    self.s7graphView.drawAxisX = YES;
    self.s7graphView.drawAxisY = YES;
    self.s7graphView.drawGridX = YES;
    self.s7graphView.drawGridY = YES;
    
    self.s7graphView.xValuesColor = [UIColor whiteColor];
    self.s7graphView.yValuesColor = [UIColor whiteColor];
    
    self.s7graphView.gridXColor = [UIColor whiteColor];
    self.s7graphView.gridYColor = [UIColor whiteColor];
    
    self.s7graphView.drawInfo = NO;
    self.s7graphView.info = @"Load";
    self.s7graphView.infoColor = [UIColor whiteColor];
    
    [self.s7graphView reloadData];
}

X軸もY軸も、どんな書式にするかは、フォーマッタが設定できるのでカスタマできる。これはなかなか、いい。

前回の日記は間違っていた。S7GraphViewはデリゲードだった。ただ、値を一気に渡しているが、ここに改良のポイントがあるのかなと思っている。

同一のX軸に対して、Y軸の値の種類の個数は、以下のメソッドで返す。

- (NSUInteger)graphViewNumberOfPlots:(S7GraphView *)graphView
{
	return 3;
}

上記の例は、3個だ。

X軸の値を返すメソッドだ。

- (NSArray *)graphViewXValues:(S7GraphView *)graphView
{
    NSMutableArray  *array = [[NSMutableArray alloc] initWithCapacity:24];
    NSDate          *date_converted;
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"ja_JP"]];
    [formatter setDateFormat:@"yyyy/MM/dd HH:mm"];
	for (int i = 0; i < 24 ; i++) {
        NSString    *date_source = [NSString stringWithFormat:@"2011/4/30 %02d:00", i];
        date_converted = [formatter dateFromString:date_source];
        [array addObject:date_converted];
	}
	return array;
}

2011年4月30日の0時から24時までの日時を返している。

Y軸の値を返すメソッドだ。

- (NSArray *)graphView:(S7GraphView *)graphView yValuesForPlot:(NSUInteger)plotIndex
{
	NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:101];
	switch (plotIndex) {
		default:
		case 0:
			for ( int i = 0; i < 24 ; i ++ ) {
				[array addObject:[NSNumber numberWithInt:4000]];		
			}
			break;
		case 1:
            [array addObject:[NSNumber numberWithInt:2613]];
            [array addObject:[NSNumber numberWithInt:2505]];
            [array addObject:[NSNumber numberWithInt:2452]];
            [array addObject:[NSNumber numberWithInt:2418]];
            [array addObject:[NSNumber numberWithInt:2396]];
            [array addObject:[NSNumber numberWithInt:2408]];
            [array addObject:[NSNumber numberWithInt:2597]];
            [array addObject:[NSNumber numberWithInt:2768]];
            [array addObject:[NSNumber numberWithInt:3013]];
            [array addObject:[NSNumber numberWithInt:3195]];
            [array addObject:[NSNumber numberWithInt:3234]];
            [array addObject:[NSNumber numberWithInt:3256]];
            [array addObject:[NSNumber numberWithInt:3080]];
            [array addObject:[NSNumber numberWithInt:3255]];
            [array addObject:[NSNumber numberWithInt:3269]];
            [array addObject:[NSNumber numberWithInt:3245]];
            [array addObject:[NSNumber numberWithInt:3266]];
            [array addObject:[NSNumber numberWithInt:0]];
            [array addObject:[NSNumber numberWithInt:0]];
            [array addObject:[NSNumber numberWithInt:0]];
            [array addObject:[NSNumber numberWithInt:0]];
            [array addObject:[NSNumber numberWithInt:0]];
            [array addObject:[NSNumber numberWithInt:0]];
            [array addObject:[NSNumber numberWithInt:0]];
			break;
		case 2:
            [array addObject:[NSNumber numberWithInt:2643]];
            [array addObject:[NSNumber numberWithInt:2526]];
            [array addObject:[NSNumber numberWithInt:2474]];
            [array addObject:[NSNumber numberWithInt:2442]];
            [array addObject:[NSNumber numberWithInt:2432]];
            [array addObject:[NSNumber numberWithInt:2453]];
            [array addObject:[NSNumber numberWithInt:2648]];
            [array addObject:[NSNumber numberWithInt:2811]];
            [array addObject:[NSNumber numberWithInt:3060]];
            [array addObject:[NSNumber numberWithInt:3220]];
            [array addObject:[NSNumber numberWithInt:3234]];
            [array addObject:[NSNumber numberWithInt:3235]];
            [array addObject:[NSNumber numberWithInt:3045]];
            [array addObject:[NSNumber numberWithInt:3223]];
            [array addObject:[NSNumber numberWithInt:3255]];
            [array addObject:[NSNumber numberWithInt:3237]];
            [array addObject:[NSNumber numberWithInt:3251]];
            [array addObject:[NSNumber numberWithInt:3209]];
            [array addObject:[NSNumber numberWithInt:3343]];
            [array addObject:[NSNumber numberWithInt:3328]];
            [array addObject:[NSNumber numberWithInt:3214]];
            [array addObject:[NSNumber numberWithInt:3095]];
            [array addObject:[NSNumber numberWithInt:3020]];
            [array addObject:[NSNumber numberWithInt:2828]];
			break;
	}
	
	return array;
}

このサンプルは、『節電対策』アプリケーションを制作した際に用意した物で、電力会社が公表している、(0)ピーク時供給力と(1)当日実績、(2)予測値のグラフだ。

S7GraphView

このサンプルをたたき台に、どういうインタフェースのライブラリを作成するのかを検討してゆこうと考えている。

_ ソースコード

GitHubからどうぞ。
https://github.com/murakami/SimpleChart - GitHub

_ 関連情報

http://code.google.com/p/s7graphview/
S7GraphViewのサイト。残念ながら、閉鎖されたようだ。

2012-06-13 WWDC2012

WWDC2012が開催されましたね。
来年は参加できるように頑張るぞ!


2012-06-15 関東第53回Cocoa勉強会のご案内

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

日時: 2012/06/30(土) 13:00-17:00

会場:新宿三丁目 新宿伊藤ビル 4F

(地下鉄 新宿三丁目 E1またはE2出口花園神社を通りすぎ、

ファミリーマート手前の左手にあります)

集合:現地

会費:500円

プロジェクタあり、インターネット回線あり(無線・有線ともにあり)

見学者は以下のフォームで募集しています。

http://www.cocoa-study.com/mail/


2012-06-17 [iOS]segueの分岐

Cocoa勉強会で、編集中にテーブルのセルを選択可能にする方法は?という質問が出たので試してみた。

Table Viewを選択して、attributes inspectorのTable VewのEditingが"No Selecting During Editing"になっているのを"Single Selecting During Editing"に変更すれば編集中も選択できるようになった。

編集中選択可能

ただし、編集中も画面遷移してしまった。Storyboardでsegueで画面遷移させている場合、編集中だと画面遷移させないという処理の分岐を実装するにはどうすればいいのだろうか?

UIStoryboardSegueのサブクラスを作成する方法等考えたのだが、もっと簡単な方法は?

これもCocoa勉強会の仲間からアドバイスを貰った。

まず、table view cellに設定されているsegueを削除する。

segue削除

そして、view controllerにsegueを設定する。

segue追加

そして、デリゲートのdidSelectRowAtIndexPath:で分岐を書けばよい。

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (!self.tableView.editing) {
        [self performSegueWithIdentifier:@"showDetail" sender:self];
    }
}

一人で仕事をしていると質問する相手がいないので困ってしまうが、Cocoa勉強会の仲間に感謝!

_ 関連情報

Cocoa勉強会

2012-06-19 [iOS]多言語化

しっ!ここだけの話、著者が明るくない分野として多言語化というのがある。なので弊社のアプリは英語版のみとなっている。これは他言無用だ!

というわけで、今回は多言語化だ!

そして、後ろを振り向かない著者が取り組むのは、NIBファイルでなく、Storyboardファイルだ!

Storyboardファイルを選択して、情報領域のLocalizaionの+ボタンを押下する。

Localization

Japaneseを選択する。

日本語を選択

すると日本語向けStoryboarファイルが用意されるので、タイトル等を日本語に変更する。

日本語化

環境設定で日本語を選ぶと、日本語のリソースが選択された!

実行

機会があれば、ibtoolのような、もっと手順化できる方法に挑戦したい。


2012-06-20 [iOS]多言語化(2)

ibtoolは仕様が代わったようで、以前のiPhone SDK向けの情報と違いがあるようだ。

まずは、基本的なibtoolを使用方法を試してみる。

前回の手作業で日本語版Storyboardのローカライズは、自動化というか、機械化というか、手順化できないという課題がある。ibtoolはそれを行う為のツールだ。

ベースとなる英語版Storybardから文字列情報を抜き出す。

$ ibtool --export-strings-file MainStoryboard.strings MainStoryboard.storyboard

生成したMainStoryboard.stringsを日本語環境にコピーして、文言の日本語に変更する。

$ cp MainStoryboard.strings ../ja.lproj/

これがオリジナル。

/* Class = "IBUITableViewController"; title = "Master"; ObjectID = "12"; */
"12.title" = "Master";
 
/* Class = "IBUIViewController"; title = "Detail"; ObjectID = "21"; */
"21.title" = "Detail";
 
/* Class = "IBUINavigationItem"; title = "Detail"; ObjectID = "26"; */
"26.title" = "Detail";
 
/* Class = "IBUILabel"; text = "Detail view content goes here"; ObjectID = "27"; */
"27.text" = "Detail view content goes here";
 
/* Class = "IBUINavigationItem"; title = "Master"; ObjectID = "36"; */
"36.title" = "Master";
 
/* Class = "IBUILabel"; text = "Title"; ObjectID = "phq-AM-6qj"; */
"phq-AM-6qj.text" = "Title";

これを翻訳する。


/* Class = "IBUITableViewController"; title = "Master"; ObjectID = "12"; */
"12.title" = "原盤";
 
/* Class = "IBUIViewController"; title = "Detail"; ObjectID = "21"; */
"21.title" = "詳細表示";
 
/* Class = "IBUINavigationItem"; title = "Detail"; ObjectID = "26"; */
"26.title" = "詳細表示";
 
/* Class = "IBUILabel"; text = "Detail view content goes here"; ObjectID = "27"; */
"27.text" = "ここに詳細を表示";
 
/* Class = "IBUINavigationItem"; title = "Master"; ObjectID = "36"; */
"36.title" = "原盤";
 
/* Class = "IBUILabel"; text = "Title"; ObjectID = "phq-AM-6qj"; */
"phq-AM-6qj.text" = "表題";

これから日本語版Storyboardを生成。

$ ibtool --import-strings-file MainStoryboard.strings --write MainStoryboard.storyboard ../en.lproj/MainStoryboard.storyboard

翻訳されている。

storyboard

勿論、実行しても。

run

2012-06-21 [iOS]多言語化(3)

今回は、『iPhoneアプリケーションプログラミング』で紹介されていた方法。Xcodeでibtoolを使う方法だ。

新規ターゲットを追加する。

NewTarget

以前のXcodeはShell Script Targetというのがあったが、今はないのでAggregateを選択する。

shell_script_target

ターゲット名を設定。

GenerateStringsFile

「スクリプトを実行」を追加する。

AddRunScript

スクリプトを記述。※例のプロジェクト名はHomepwner。

script
ibtool --export-strings-file Homepwner/en.lproj/MainStoryboard.strings Homepwner/en.lproj/MainStoryboard.storyboard
cp Homepwner/en.lproj/MainStoryboard.strings Homepwner/ja.lproj/MainStoryboard.strings
exit 0

アクティブターゲットを切り替える。

select_target

実行。更新されている。

$ ls -l *.lproj
en.lproj:
total 32
-rw-r--r--@ 1 yukio  staff    45  6 17 15:38 InfoPlist.strings
-rw-r--r--  1 yukio  staff  8174  6 18 07:50 MainStoryboard.storyboard
-rw-r--r--  1 yukio  staff  1228  6 21 22:33 MainStoryboard.strings
 
ja.lproj:
total 24
-rw-r--r--@ 1 yukio  staff  8050  6 20 23:19 MainStoryboard.storyboard
-rw-r--r--  1 yukio  staff  1228  6 21 22:33 MainStoryboard.strings

次回は、日本語Storybardの生成の自動化だ。


2012-06-22 [iOS]多言語化(4)

今回も、『iPhoneアプリケーションプログラミング』で紹介されていた方法。Xcodeでibtoolを使った日本語Storyboar生成の方法だ。

「ビルドフェーズ」に「スクリプトを実行」を追加する。

target add_run_script

以前と同様、ja.lprojのMainStoryboard.stringsを日本語化する。

/* Class = "IBUITableViewController"; title = "Master"; ObjectID = "12"; */
"12.title" = "原盤";
 
/* Class = "IBUIViewController"; title = "Detail"; ObjectID = "21"; */
"21.title" = "詳細";
 
/* Class = "IBUINavigationItem"; title = "Detail"; ObjectID = "26"; */
"26.title" = "詳細";
 
/* Class = "IBUILabel"; text = "Detail view content goes here"; ObjectID = "27"; */
"27.text" = "詳細を表示";
 
/* Class = "IBUINavigationItem"; title = "Master"; ObjectID = "36"; */
"36.title" = "原盤";
 
/* Class = "IBUILabel"; text = "Title"; ObjectID = "phq-AM-6qj"; */
"phq-AM-6qj.text" = "表題";

スクリプトを記述。

script
ibtool --import-strings-file Homepwner/ja.lproj/MainStoryboard.strings --write Homepwner/ja.lproj/MainStoryboard.storyboard Homepwner/en.lproj/MainStoryboard.storyboard
exit 0

実行

storyboard
run

日本語化されている。


2012-06-23 [iOS]多言語化(5)

今回はStoryboardではなく、ソースコード中に言語に依存する文字列を埋め込んでいる場合の多言語化に挑戦だ。

ソースコード中に@"文言"で文字列を埋め込んでいる箇所をNSLocalizedString(@"文言", nil)に置換する。ベースは英語版の前提で話を進めているので、文言に相当する文字列はASCIIだ。

cell.detailTextLabel.text = NSLocalizedString(@"Detail", nil);

前回、Storyboardから文言を抜き出すターゲットを用意したが、これにNSLocalizedString()で多国語化する文字列を埋め込んだソースから文言を抜き出すスクリプトを追加する。

日本語を抜き出す
genstrings Homepwner/MasterViewController.m -o Homepwner/en.lproj
cp -p Homepwner/en.lproj/Localizable.strings Homepwner/ja.lproj/

このターゲットを実行すると、Localizable.stringsというファイルが生成されるはずだ。

ここからが前回の反省点をふまえた手順だ。

resourcesというグループを作成して多国語化で使用する文言のファイルをここに格納する。Finderからドラッグ&ドロップで追加という事になるか、これで追加するのは、en.lproj配下のファイルのみ。

追加したLocalizable.stringsを選択して、LocalizationとしてJapaneseを追加する。

日本語を選択

すると、ja.lprojにコピーが生成され、Xcode上の表示も操作しやすくなると思う。この時、この操作の前にja.lproj配下のファイルを日本語化していたら、英語版ファイルで上書かれるので注意する事。

多国語化
日本語化

Localizable.stringsをカスタマイズ。

/* No comment provided by engineer. */
"Detail" = "詳細表示";

実行

実行

2012-06-24 [OSX][Python]QuartzとPythonスクリプト

Pythonをマスターする必要が出てきた。でも漠然と素のスクリプトの練習をしても仕方がないと思う。多分、PythonのWebアプリケーションのフレームワークを使ったサイトを立ち上げてみるのが要求に対する適切な対応だと思うが、ここはCocoa練習帳ですし、他とは少し違うスキルが必要という事で、QuartzツールをPythonスクリプトで制作する事に挑戦だ!

OS X Lion (10.7)には、2.7系列が組み込まれているようだ。

$ python -V
Python 2.7.1
$ python --version
Python 2.7.1
$ which python
/usr/bin/python

以前は、/Developer/Examples/Quartz/Python/API-SUMMARY にQuartz Python APIの文書があったようが、現在はない。

色々探してみて見つけたのがこれ。実は新しいXcodeインストール時に誤って古いXcodeを削除しなかったのだが、それがよかった。/Developer/Documentation/Python/ に情報があった。

早速、Retina以前のiPhone画面サイズの空のPDF書類「demo.pdf」を作成。

$ cat demo.py 
#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
from CoreGraphics import *
 
# iOS size
mediaRect = CGRectMake(0.0, 0.0, 640.0, 960.0)
 
context = CGPDFContextCreateWithFilename("demo.pdf", mediaRect)
context.beginPage(mediaRect)
 
context.endPage()
context.finish()
 
# End Of File

このスクリプトを実行すれば、demo.pdfというファイルが生成されるのが確認できると思う。

空白ページだと寂しいので、赤色の四角形を描画。

$ cat demo.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
from CoreGraphics import *
 
# iOS size
mediaRect = CGRectMake(0.0, 0.0, 640.0, 960.0)
 
context = CGPDFContextCreateWithFilename("demo.pdf", mediaRect)
context.beginPage(mediaRect)
 
context.setRGBFillColor(1.0, 0.0, 0.0, 1.0)
ourRect = CGRectMake(20.0, 20.0, 130.0, 100.0)
context.fillRect(ourRect)
 
context.endPage()
context.finish()
 
# End Of File

あれ、CGContextSetRGBFillColorに相当する箇所でエラーとなっている。

$ ./demo.py
Traceback (most recent call last):
  File "./demo.py", line 12, in 
    context.setRGBFillColor(1.0, 0.0, 0.0, 1.0)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/CoreGraphics/__init__.py", line 528, in setRGBFillColor
    def setRGBFillColor(*args): return _CoreGraphics.CGContext_setRGBFillColor(*args)
TypeError: in method 'CGContext_setRGBFillColor', argument 2 of type 'CGFloat'

何故だろう?


2012-06-25 [iOS]モーダルViewController(その3)

presentModalViewController:animated: の使用は推奨されなくなったようだ。

presentViewController:animated:completion: を使用するのだが、最後にBlockを指定するので期待したのだが、これはモーダル・ビュー表示後に実行する処理を記述する為で、独自にデリゲートやBlocksを用意する手間を省く為のものではないようだ。残念。


- (IBAction)modalPane:(id)sender
{
    ModalPaneViewController *viewController = [[ModalPaneViewController alloc] 
                                               initWithNibName:@"ModalPaneViewController"
                                               bundle:nil];
    [viewController setCompletionHandler:^(ModalPaneViewControllerResult result) {
        switch (result) {
            case ModalPaneViewControllerResultCancelled:
                [self performSelectorOnMainThread:@selector(didCancel:) withObject:nil waitUntilDone:NO];
                break;
            case ModalPaneViewControllerResultDone:
                [self performSelectorOnMainThread:@selector(didDone:) withObject:nil waitUntilDone:NO];
                break;
            default:
                break;
        }
        
        [self dismissModalViewControllerAnimated:YES];
    }];
    /* [self presentModalViewController:viewController animated:YES]; */
    [self presentViewController:viewController animated:YES completion:nil];
}

_ ソースコード

GitHubからどうぞ。
https://github.com/murakami/ModalPane - GitHub

2012-06-26 [OSX][iOS]Code Snippet Library

スニペットのライブラリは、よく使うコードを直ぐに取り出せる機能だ。

設定による位置は変わるが、初期状態だと右下にスニペット・ライブラリが表示される。

CodeSnippetLibrary

登録したいコードをCode Snippet Libraryにドラッグ&ドロップする。

DragDrop

追加されたスニペットを選択して開く。そして、Editボタンを押下する。

open

Titileを編集して、Doneボタンを押下する。

edit

利用者が入力する項目がある場合は、<#と#>で囲まれた文字列を追加する。

dbgmsg

登録したスニペットをソースファイルにドラッグ&ドロップすれば追加できるようになる。

_ 関連情報

Xcode 4 User Guide

2012-06-27 [iOS][Web]ネイティブWebアプリケーション(その3)

ほんの最初の一歩にしかならないと思うが。以前紹介したWebアプリケーションで、HTMLとObjective-Cでやり取りするのに挑戦した。

HTMLコンテンツにObjective-Cから値(日付)を設定する領域を用意する。

Date: <input name="demo" type="text"><br /> 

UIWebViewを管理するビューコントローラをUIWebViewDelegateに対応させる。

@interface ViewController : UIViewController <UIWebViewDelegate>
@end

デリゲートメッソドに、HTMLコンテンツの表題を取得するコードを追加する。

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    NSString    *title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
    NSLog(@"%@", title);
}

デリゲートメッソドに、HTMLコンテンツの要素demoの値を書き換えるコードを追加する。

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    NSString    *date = [NSString stringWithFormat:
            @"document.getElementsByName('demo').item(0).value='%@'",
            [NSDate date]];
    [webView stringByEvaluatingJavaScriptFromString:date];
}

_ ソースコード

GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/ios/WebApp - GitHub

2012-06-28 [OSX][iOS]データ解析

データ・ファイルを解析する際の、よくあるパターンとして行単位で読み込んで、それを解析するというのがある。これをPerlで処理する場合、こんな感じになる。

#!/usr/bin/perl
my $line;
while ($line = <>) {
	chomp $line;
	print $line, "\n";
}

これをCocoaで行うとどうなるか?まずは、標準入力の内容を標準出力に印字する。

#import <Foundation/Foundation.h>
 
int main(int argc, const char * argv[])
{
    @autoreleasepool {        
        NSFileHandle    *fhi = [NSFileHandle fileHandleWithStandardInput];
        NSFileHandle    *fho = [NSFileHandle fileHandleWithStandardOutput];
 
        NSData  *datainput = [fhi readDataToEndOfFile];
        NSString    *str = [[NSString alloc] initWithData:datainput encoding:NSUTF8StringEncoding];
        
        NSData  *dataout = [[NSData alloc] initWithBytes:[str UTF8String] length:[str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];
        [fho writeData:dataout];
    }
    return 0;
}

次に行単位で取り込む。

#import <Foundation/Foundation.h>
 
int main(int argc, const char * argv[])
{
    @autoreleasepool {        
        NSFileHandle    *fhi = [NSFileHandle fileHandleWithStandardInput];
        NSFileHandle    *fho = [NSFileHandle fileHandleWithStandardOutput];
 
        NSData  *datainput = [fhi readDataToEndOfFile];
        NSString    *str = [[NSString alloc] initWithData:datainput encoding:NSUTF8StringEncoding];
        
        NSError *error = NULL;
        NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(.+)\n|(.+)"
                                                                               options:NSRegularExpressionCaseInsensitive
                                                                                 error:&error];
        NSArray    *array = [regex matchesInString:str options:0 range:NSMakeRange(0, str.length)];
        NSTextCheckingResult    *matches;
        for (matches in array) {
            NSString    *s = [str substringWithRange:[matches rangeAtIndex:0]];
            NSData  *dataout = [[NSData alloc] initWithBytes:[s UTF8String] length:[s lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];
            [fho writeData:dataout];
        }
    }
    return 0;
}

_ ソースコード

GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/mac/analog - GitHub

2012-06-29 [OSX][iOS]データ解析(2)

現在のXcodeはビルドした結果の出力先が隠されている為、コマンドライン・ツールをビルドして、操作するという流れが面倒になっている為、標準入力から値を得る部分をデバッグ用に内部変数から値を得る様に修正する。

#ifndef DEBUG
        NSFileHandle    *fhi = [NSFileHandle fileHandleWithStandardInput];
#endif
        NSFileHandle    *fho = [NSFileHandle fileHandleWithStandardOutput];
 
#ifndef DEBUG
        NSData  *datainput = [fhi readDataToEndOfFile];
        NSString    *str = [[NSString alloc] initWithData:datainput encoding:NSUTF8StringEncoding];
#else
        NSString    *str = @"12:23:45 start\n1,10\n2,13";
#endif

行単位に切り出せたら、Perlのchompのように末尾の改行文字を削ろう。

            NSCharacterSet  *charSet = [NSCharacterSet newlineCharacterSet];
            line = [line stringByTrimmingCharactersInSet:charSet];

著者はよくやっているのだが、ログのある時刻の抜き出しをやってみよう。

            regex = [NSRegularExpression regularExpressionWithPattern:@"(\\d\\d:\\d\\d:\\d\\d)"
                                                              options:NSRegularExpressionCaseInsensitive
                                                                error:&error];
            NSTextCheckingResult    *match = [regex firstMatchInString:line
                                                               options:0
                                                                 range:NSMakeRange(0, line.length)];
            if (0 < [match numberOfRanges]) {
                NSString    *tm = [line substringWithRange:[match rangeAtIndex:0]];
                NSLog(@"time: %@", tm);
            }

完成。

@autoreleasepool {        
#ifndef DEBUG
        NSFileHandle    *fhi = [NSFileHandle fileHandleWithStandardInput];
#endif
        NSFileHandle    *fho = [NSFileHandle fileHandleWithStandardOutput];
 
#ifndef DEBUG
        NSData  *datainput = [fhi readDataToEndOfFile];
        NSString    *str = [[NSString alloc] initWithData:datainput encoding:NSUTF8StringEncoding];
#else
        NSString    *str = @"12:23:45 start\n1,10\n2,13";
#endif
        
        NSError *error = NULL;
        NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(.+)\n|(.+)"
                                                                               options:NSRegularExpressionCaseInsensitive
                                                                                 error:&error];
        NSArray    *array = [regex matchesInString:str
                                           options:0
                                             range:NSMakeRange(0, str.length)];
        NSTextCheckingResult    *matches;
        for (matches in array) {
            NSString    *line = [str substringWithRange:[matches rangeAtIndex:0]];
 
#ifdef DEBUG
            NSData  *dataout = [[NSData alloc] initWithBytes:[line UTF8String]
                                                      length:[line lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];
            [fho writeData:dataout];
#endif
            
            NSCharacterSet  *charSet = [NSCharacterSet newlineCharacterSet];
            line = [line stringByTrimmingCharactersInSet:charSet];
 
            regex = [NSRegularExpression regularExpressionWithPattern:@"(\\d\\d:\\d\\d:\\d\\d)"
                                                              options:NSRegularExpressionCaseInsensitive
                                                                error:&error];
            NSTextCheckingResult    *match = [regex firstMatchInString:line
                                                               options:0
                                                                 range:NSMakeRange(0, line.length)];
            if (0 < [match numberOfRanges]) {
                NSString    *tm = [line substringWithRange:[match rangeAtIndex:0]];
                NSLog(@"time: %@", tm);
            }
        }
    }

_ ソースコード

GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/mac/analog - GitHub

2012-06-30 第53回Cocoa勉強会 関東

_ 第53回Cocoa勉強会 関東

今回も貸し会議室の席は埋まりました。
飛び入りの発表があったので漏れていたらすいません。今回は、Cocoaで書いたデータ解析ツールとSandboxとPrivilege Separation、MPMusicPlayerControllerとAVPlayer、Xcode Tips、質問コーナと盛りだくさんでした。

Cocoa勉強会

_ 関連情報

Cocoa勉強会

トップ 最新 追記