Cocoa練習帳 -25ページ目

[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勉強会

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

日時: 2012/06/30(土) 13:00-17:00
会場:新宿三丁目 新宿伊藤ビル 4F 
(地下鉄 新宿三丁目 E1またはE2出口花園神社を通りすぎ、 
 ファミリーマート手前の左手にあります) 
集合:現地 
会費:500円 
プロジェクタあり、インターネット回線あり(無線・有線ともにあり) 

見学者は以下のフォームで募集しています。 
http://www.cocoa-study.com/mail/

WWDC2012

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


[iOS]グラフ描画ライブラリ(3)

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




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




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




- (void)viewDidLoad
{
    [super viewDidLoad];
 
    //self.s7graphView = [[S7GraphView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];


    //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
{

}



上記の例は、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"];

        NSString    *date_source = [NSString stringWithFormat:@"2011/4/30 %02d:00", i];
        date_converted = [formatter dateFromString:date_source];
        [array addObject:date_converted];


}



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




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




- (NSArray *)graphView:(S7GraphView *)graphView yValuesForPlot:(NSUInteger)plotIndex
{









            [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]];


            [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]];




}



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




S7GraphView




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




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

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


関連情報
http://code.google.com/p/s7graphview/

S7GraphViewのサイト。残念ながら、閉鎖されたようだ。

[iOS]グラフ描画ライブラリ(2)

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




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




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




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








データ


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

デリゲート








続きは次回。




関連情報
http://code.google.com/p/s7graphview/

S7GraphViewのサイト。残念ながら、閉鎖されたようだ。

[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




関連情報

Xcode Unit Testing Guide

iOS開発ガイド:iOSアプリケーション開発ワークフローについて

[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


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


関連情報
Xcode Unit Testing Guide

[Web]INTER-Mediator Jelly

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







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




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




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




関連情報

INTER-Mediator

新宿 - TRAVELERS Coworking

[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の日々』いつも参考にさせていただいています。ありがとう!助かります。

[iOS]アラートのカスタマイズ(4)

Storyboardは画面の遷移を扱うので、これでViewControllerを生成すると全画面になる。なので、以前のようにnib(xib)で全画面でないViewControllerを用意すれば、アラート風のモーダル・ビュー・コントローラが出来るのでは?

IB






実行!

Run






駄目だ!モーダル・ビュー・コントローラではなくて、アラート風ビューを子ビューとして追加するしかないみたい。






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

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


関連情報
[iOS] UIAlertView 上に UIProgressView を載せる [2] キャンセルボタン表示

『Cocoaの日々』いつも参考にさせていただいています。ありがとう!助かります。