2011年2月18日金曜日

NSDate と NSDateFormatter

-(void)showDateFormatter{
  NSDate *date = [NSDate date];
  NSDateFormatter *df01 = [[[NSDateFormatter alloc] init] autorelease];
  [df01 setDateStyle:NSDateFormatterMediumStyle];
  [df01 setTimeStyle:NSDateFormatterNoStyle];
  NSString * df01s = [df01 stringFromDate:date];
  NSLog(@"date formatter locale : %@", [[df01 locale] localeIdentifier]);
  NSLog(@"df01s : %@", df01s);

  NSDateFormatter *df02 = [[[NSDateFormatter alloc] init] autorelease];
  [df02 setDateStyle:NSDateFormatterFullStyle];
  [df02 setTimeStyle:NSDateFormatterFullStyle];
  NSString * df02s = [df02 stringFromDate:date];
  NSLog(@"df02s : %@", df02s);

  NSDateFormatter *df03 = [[[NSDateFormatter alloc] init] autorelease];
  [df03 setDateFormat:@"HH:mm 'on' EEEE MMMM d"];
  NSString * df03s = [df03 stringFromDate:date];
  NSLog(@"df02s : %@", df03s);
}

日付のスタイルと時間のスタイルを別々にセットできる。三番目のようにフォーマットを指定するのもできる。

フォーマットに使用出来る形式はここにあるみたい(iOSライブラリのマニュアルに書いてあったリンク)

UIScreen と CGRect

    // ステータスバーを含む画面のサイズ
  CGRect rect = [[UIScreen mainScreen] bounds];
  DLogR(rect);
    // ステータスバーを含まない画面のサイズ
  CGRect rectapf = [[UIScreen mainScreen] applicationFrame];
  DLogR(rectapf);

CGRect と UIScreen を見てみた。ちなみに、DLogR() はこんなの。Prefix header に書いている。

define DLogR(p) NSLog(@"x:%f,y:%f,w:%f,h:%f",p.origin.x,p.origin.y,p.size.width,p.size.height);

2011年2月17日木曜日

UILabel

-(void)showLabel{
  UILabel * label1 = [[[UILabel alloc] initWithFrame:CGRectMake( 60, 300, 200, 40 )] autorelease];
  UILabel * label2 = [[[UILabel alloc] initWithFrame:CGRectMake( 60, 350, 200, 40 )] autorelease];
  UILabel * label3 = [[[UILabel alloc] initWithFrame:CGRectMake( 60, 400, 200, 40 )] autorelease];
  
  label1.textAlignment = UITextAlignmentLeft;
  label2.textAlignment = UITextAlignmentCenter;
  label3.textAlignment = UITextAlignmentRight;
  
  label1.text = @"UITextAlignmentLeft";
  label2.text = @"UITextAlignmentCenter";
  label3.text = @"UITextAlignmentRight";

  label1.font = [UIFont fontWithName:@"Arial-BoldItalicMT" size:[UIFont systemFontSize]];
  label2.font = [UIFont fontWithName:@"Cochin-BoldItalic" size:[UIFont systemFontSize]];
  label3.font = [UIFont fontWithName:@"Courier" size:[UIFont systemFontSize]];
  
  label1.backgroundColor = [UIColor clearColor];
  label2.backgroundColor = [UIColor blackColor];
  label2.textColor = [UIColor whiteColor];
  label3.backgroundColor = [UIColor darkGrayColor];
  
  [self.view addSubview:label1];
  [self.view addSubview:label2];
  [self.view addSubview:label3];  
}

UILabel をコードで描画して配置する。textColor を clearColor にしたら文字が見えなくなった。背景を透過する抜き文字みたいにするにはどうしたらいいのかな。。。

2011年2月16日水曜日

UISwitch をコードで描画してみたけれども

- (void)showSwitch{

// center で描画すると座標が真ん中なのがね。。。
//  UISwitch * mySwitch = [[[UISwitch alloc] init] autorelease];
//  mySwitch.center = CGPointMake(200, 300);

// と思って CGRect にしたんだけど、UISlider と違って、
// サイズを指定しても背景領域もヒット領域も見た目も変化ないみたい?
  CGRect frame = CGRectMake(100.0, 100.0, 50.0 , 50.0 );
  UISwitch * mySwitch = [[[UISwitch alloc] initWithFrame:frame]autorelease];

  mySwitch.backgroundColor = [UIColor blueColor]; // スイッチの四隅に少し色がつくだけ?
  mySwitch.on = YES;
  [mySwitch addTarget:self action:@selector(showSwitchChange:) forControlEvents:UIControlEventValueChanged];

  // つまんないから縦にしてみたw
  CGAffineTransform scale  = CGAffineTransformMakeScale(1.0, 1.0);
  CGAffineTransform trans  = CGAffineTransformMakeRotation(270.0f * M_PI / 180.0f);
  CGAffineTransform concat = CGAffineTransformConcat(scale,trans);
  [mySwitch setTransform:concat];
  
  [self.view addSubview:mySwitch];
  
}
  // アクションセレクターにもだいぶ慣れた(同じ事しかしてないけど)
-(void)showSwitchChange:(id)sender{
  NSString * message = [[NSString alloc] initWithFormat:@"Switch : %d",[sender isOn]];
  
  UIAlertView * alert = [[UIAlertView alloc] initWithTitle:@"スイッチは"
                                                   message:message
                                                  delegate:self
                                         cancelButtonTitle:@"OK"
                                         otherButtonTitles:nil];
  [alert show];
  [alert release];
  [message release];  
}

スイッチも書いてなかったみたいなので。コメントにも書いたけど、UISwitchってUISliderと違ってサイズを指定してもヒット領域とかに変化が見られないみたい?書き方が悪い訳ではないと思うんだけど。。。

2011年2月15日火曜日

UIPickerView をコードで追加するのと、delegate と dataSource を外だしにしてみた

需要があるかどうかは知らないけど、今の機能に手軽にピッカーを追加できるように考えたら、ピッカーの追加とデータ取得だけを現在のコントローラに書いて、Delegate と dataSource はひとまとめにした別のファイルにしたらいいんじゃないかと思ってやってみた。いつものとおり、IBは使わないコース。まぁ練習。

// MyPicker.h
#import 
@interface MyPicker : NSObject <UIPickerViewDelegate,UIPickerViewDataSource>
{
  NSArray *      pickerData;
}
@property (nonatomic,retain) NSArray *      pickerData;
- (id) makeObj;
@end

// MyPicker.m
#import "MyPicker.h"
@implementation MyPicker
@synthesize pickerData;
-(id)makeObj
{
  NSArray * array = [[NSArray alloc] initWithObjects:@"test",@"test2",@"test3",nil];
  self.pickerData = array;
  [array release];
  return self;
}
#pragma mark -
#pragma mark UIPicker Data Source methods
  // コンポーネントの数を返す
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
  return 1;
}
  // コンポーネント毎(0始まり)の行数を返す
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
  return [pickerData count];
}
#pragma mark -
#pragma mark UIPicker delegate methods
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
  return [pickerData objectAtIndex:row];
}
#pragma mark -
- (void)dealloc {
  [pickerData release];
  [super dealloc];
}
@end

これで、現在のコントローラ中で以下のようにする。

// 現在のコントローラ.m

#import "MyPicker.h"

なんかの処理 {
  [self showPicker];
}
- (void)showPicker {
    // ピッカーを作って配置
  MyPicker * myPickObj = [[MyPicker alloc]autorelease]; // デリゲートとデータソースのオブジェクト
  [myPickObj makeObj];  // データいれる
    // ピッカーを描画
  UIPickerView * myPickerView = [[[UIPickerView alloc] init]autorelease];
  myPickerView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  myPickerView.showsSelectionIndicator = YES;
  myPickerView.delegate = myPickObj; // ここを self 以外にするにはどうすればいいのか悩んでた
  myPickerView.dataSource = myPickObj; // 単にオブジェクト作って与えるだけで問題なかった
  [myPickerView setTag:123]; // 先日 #hidev で教わった、addSubview したビューを後で拾うためのタグ付け
  [self.view addSubview:myPickerView];

    // ボタンを作って配置
  UIButton* button1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
  button1.frame = CGRectMake(0,300,200,50);
  [button1 setTitle:@"Pickeeeeer" forState:UIControlStateNormal];
  [button1 addTarget:self action:@selector(showPickerSelected:) forControlEvents:UIControlEventTouchUpInside];
  [self.view addSubview:button1];
}

-(void)showPickerSelected:(id)sender{
    // タグでビューからピッカーを引っ張る
  UIPickerView * myPickerView = (UIPickerView *)[self.view viewWithTag:123];
  NSInteger row = [myPickerView selectedRowInComponent:0];
  NSString * message = [[NSString alloc] initWithFormat:@"row : %d",row];
  
  UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"選択したもの"
                                                  message:message
                                                 delegate:self
                                        cancelButtonTitle:@"OK"
                                        otherButtonTitles:nil];
  [alert show];
  [alert release];
  [message release];
}

使いどころもよくわからないまま書いたので、これの出番が本当にあるのかどうかはわからないけど、UIPickerViewの実装雛形としては使える、はず。MyPickerにデリゲートとデータソースを追いやることで、既存のソースとごっちゃにならないでいいかな、とか。

ちょっとぐぐっただけだと、デリゲートとデータソースを持ったViewControllerを使うサンプルばかりで、どうやってデリゲートとデータソースを外部化すればいいのか全然わからなかったけど、先日書いたサブクラス化のイメージでオブジェクト化すればいいんじゃね?と思ったらうまくいったのでよかった。

実はもっとうまい方法あるんだよ、とかあったら教えてください。

言うまでもないけど、先日書いた記事のとおり、Build and Analyzeしてるので、たぶん問題ないと思うんだけど、コードの書きっぷりとか、思う所があればそれも。。。

UIButtonをコードで追加する

    // ボタンを作って配置
  UIButton* button1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
  button1.frame = CGRectMake(0,0,200,50);
  [button1 setTitle:@"押してください" forState:UIControlStateNormal];
  [button1 setTitle:@"押されました" forState:UIControlStateHighlighted];
  [button1 setTitle:@"押せません" forState:UIControlStateDisabled];
  [button1 addTarget:self action:@selector(showAlert:) forControlEvents:UIControlEventTouchUpInside];
  [self.view addSubview:button1];

書いたような気がしたけど書いてなかったようなので。

あんまり画像使うの好きじゃないっつーか、HTML&CSSなら後で調整すればいい話なんだけど、iPhoneネイティブアプリの場合は、画像のサイズ合わせ、位置合わせが必要なので、結構めんどくさく感じる。やることはそんなに変わらないけれども。

あと、UIControlStateDisabled の使い道はイマイチわからない。ドキュメントには押せないボタンは作るべきじゃないとか書いてなかったっけ?

2011年2月13日日曜日

Objective-C で サブクラスの理解を深める一歩目

本とか読んでても、元々オブジェクト指向的な言語を使ってたり開発をやってないと、サブクラスとかよくわかんない。本や資料にはほにゃららのサブクラスを作ってどうこうって書いてあるけど、イメージしにくい。ので、小さいプログラムで、実際どう書けばいいかをちょっと書いた。

//  MyClass.h
#import 
@interface MyClass : NSObject {
}
- (void)logme;
@end
// ファイルを MySubClass.hにしてもいいけど、メモだから同じファイルに書いておいた。
// MyClass.h に書いてあっても、プログラム的には問題ない(はず)実用には向かないし奨めないけど。
@interface MySubClass : MyClass{  
}
- (void)logme;
@end

NSObject のサブクラス MyClass と、さらにそのサブクラス MySubClass を定義して、MyClass.h とした。

//  MyClass.m
#import "MyClass.h"

@implementation MyClass
- (void)logme {
  NSLog(@"MyClass logme now");
}
@end

// 同じく MySubClass.m にしたほうがいいけど。
@implementation MySubClass
- (void)logme {
  NSLog(@"MySubClass logme now");
}
@end

MyClass は logme メソッドを持っていて、NSLog() でログをはく。同じく MySubClass も logme メソッドを持っていて、 NSLog() でログをはく。

// どっか実行するメインのファイル たとえば MainViewController.m で MyClass.h を import
#import "MyClass.h"

// さらにどっかで実行。たとえば viewDidLoad()

- (void)viewDidLoad {
  [[MyClass alloc] logme];
  [[MySubClass alloc] logme];
}

なんの芸もないけど、ここから膨らませていけば、いろんなことができるはず。例えば、MyClass にだけ logmain() などを書いて、両方のクラスから呼んでみるとかね。

afconvert で .wav を .caf に変換する

find . -name '*.wav' -exec /usr/bin/afconvert -f caff -d LEI16@22050 -c 1 {} \;

ここで見て、なるほどと思ったので、手持ちの wave ファイル(効果音系)を caff 形式に変換。

afconvert に限らず、ヘルプはたいてい -h 。それによると、

afconvert -h
(略)
afconvert [option...] input_file [output_file]
afconvert input_file [-o output_file [option...]]...
General options:
    { -d | --data } data_format[@sample_rate][/format_flags][#frames_per_packet]
        [-][BE|LE]{F|[U]I}{8|16|24|32|64}          (PCM)

ってなっていて、フォーマットについては、-hfって書いてあるので、見てみる。

$ afconvert -hf
Audio file and data formats:
    '3gpp' = 3GP Audio (.3gp)
               data_formats: 'aac ' 'aace' 'aach' 'aacl' 'aacp' 'samr' 
    '3gp2' = 3GPP-2 Audio (.3g2)
               data_formats: 'aac ' 'aace' 'aach' 'aacl' 'aacp' 'samr' 
    'adts' = AAC ADTS (.aac, .adts)
               data_formats: 'aac ' 'aach' 'aacp' 
    'ac-3' = AC3 (.ac3)
               data_formats: 'ac-3' 
    'AIFC' = AIFC (.aifc, .aiff, .aif)
               data_formats: I8 BEI16 BEI24 BEI32 BEF32 BEF64 UI8 'ulaw' 
                             'alaw' 'MAC3' 'MAC6' 'ima4' 'QDMC' 'QDM2' 
                             'Qclp' 'agsm' 
    'AIFF' = AIFF (.aiff, .aif)
               data_formats: I8 BEI16 BEI24 BEI32 
    'amrf' = AMR (.amr)
               data_formats: 'samr' 
    'caff' = Apple CAF (.caf)
               data_formats: '.mp1' '.mp2' '.mp3' 'QDM2' 'QDMC' 'Qclp' 
                             'Qclq' 'aac ' 'aace' 'aach' 'aacl' 'aacp' 
                             'alac' 'alaw' 'dvi8' 'ilbc' 'ima4' I8 BEI16 
                             BEI24 BEI32 BEF32 BEF64 LEI16 LEI24 LEI32 
                             LEF32 LEF64 'ms\x00\x02' 'ms\x00\x11' 'ms\x001' 
                             'samr' 'ulaw' 
    'm4af' = Apple MPEG-4 Audio (.m4a)
               data_formats: 'aac ' 'aace' 'aach' 'aacl' 'aacp' 'alac' 
    'MPG1' = MPEG Layer 1 (.mp1, .mpeg, .mpa)
               data_formats: '.mp1' 
    'MPG2' = MPEG Layer 2 (.mp2, .mpeg, .mpa)
               data_formats: '.mp2' 
    'MPG3' = MPEG Layer 3 (.mp3, .mpeg, .mpa)
               data_formats: '.mp3' 
    'mp4f' = MPEG-4 Audio (.mp4)
               data_formats: 'aac ' 'aace' 'aach' 'aacl' 'aacp' 
    'NeXT' = NeXT/Sun (.snd, .au)
               data_formats: I8 BEI16 BEI24 BEI32 BEF32 BEF64 'ulaw' 
    'Sd2f' = Sound Designer II (.sd2)
               data_formats: I8 BEI16 BEI24 BEI32 
    'WAVE' = WAVE (.wav)
               data_formats: UI8 LEI16 LEI24 LEI32 LEF32 LEF64 'ulaw' 
                             'alaw' 

難しいってゆうか、興味の範疇外なので、あんまり詳しいことはわからない。
サウンドファイルの形式の取捨選択はイマイチどういった基準で選べばいいのかよくわからない。iPhone(Mac)だからAIFF形式でいいんでしょ、と思ってたら、拡張子は .caf がいいらしいっつーか、.aif とどう違うのかとかはよくわかってない。単純には、Apple のサンプルプログラム群が、.caf を使ってるから、という理由だけで使うことにしてるんだけど。

ちなみに、wave ファイルは、この辺でたくさん見つかる。もうちょっと検索条件とか方式とかいろいろ考えて欲しい。

2011年2月12日土曜日

CFBundleURLSchemes でアプリを外部から起動する

ここを参考に設定してみた。いつだったか、iOSのバージョンが上がった時に、add Item で Item No の始まりが1から0に変わったとかあったなーと思っていたので特に問題なかった。

  1. Info.plist を開く
  2. Information Property List を右クリックして Add Row
  3. キーに"URL types"を設定
  4. その中に出来る Item 0 の中に URL identifier を設定(デフォルトで設定されるかも)
  5. その値を適当に入れる(BundleIdentifierの様に独自URLの逆順がいいみたい)
  6. Item 0 を選択して右クリック、再度 Add Row
  7. 今度は URL Schemes をキーに設定
  8. その値をアプリ名など、好きに入れる(これが外部からの呼び出し時の名前)

これにより、safari などで URL 入力欄に "myapp(設定した URL Schemes の値)://" などとすると、アプリが立ち上がる。

ちなみに、上記のページには、起動された時に引数を受ける方法等も書いてある。今作成中のアプリには必要ないのでパスw

それと、xcode で Info.plist を開いてる最中に、右クリックからコピーってすると、XML形式でコピーされているみたい?テキストエディタに貼付けると、XML形式で貼り付いた。面白い。

2011年2月10日木曜日

UITextField で入力して UIAlertView に表示

- (void) showTextField{
  UITextField * textField = [[[UITextField alloc] initWithFrame:CGRectMake(0, 100, 200, 40)] autorelease];
  textField.borderStyle = UITextBorderStyleRoundedRect; // 線の種類
  textField.clearButtonMode = UITextFieldViewModeAlways; // 初期化ボタンの表示
  textField.font = [UIFont fontWithName:@"AmericanTypewriter" size:[UIFont systemFontSize]]; // フォント
  textField.textAlignment = UITextAlignmentRight; // アラインメント
  textField.textColor = [UIColor blackColor]; // 文字色
  textField.placeholder = @"デフォルトで何か書いておける"; // デフォルトで書かれている薄いアレ
  textField.keyboardType = UIKeyboardTypeDefault; // キーボードの種類
  textField.returnKeyType = UIReturnKeyDone; // 右下のボタン
  [textField addTarget:self action:@selector(showAlertText:) forControlEvents:UIControlEventEditingDidEndOnExit];
  [self.view addSubview:textField];
}

- (void)showAlertText:(id)sender {
  UITextField * textField = sender;
  UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"テキスト入力"
                                                  message:textField.text
                                                 delegate:self
                                        cancelButtonTitle:@"OK"
                                        otherButtonTitles:nil];
  [alert show];
  [alert release]; 
}

テキストフィールドを描画して、値を入れ終わったらアラートビューで表示してみるとか。あ、アラートビューのデリゲートメソッドは別途ね。

アプリがクラッシュする場合のcrashファイルの見方

iPhoneアプリのテストって大変ですねw アドホックビルドしたアプリのテストをお願いしていたところ、iPhone3G,3GS では問題ないのに、iPhone4だけ起動直後に落ちるという報告をいただいていた。ちょっと調べてみたところ、iPhoneでアプリが落ちた場合、.crashというファイルが作成されて、母艦との同期時に保存されるらしい。そのファイルを送ってもらって、調べてみたところ、うまく不具合が解消できたようなのでメモ。

まず、ビルド時に一緒に生成される、.dSYMファイルが必須。ビルドしたものを配布するときは、そのビルドしたファイルと一緒に、バックアップを残しておくようにする。

次に、落ちた報告があった場合、次の場所に.crashファイルが出来ているので、送ってもらう。

Mac OS X : ~/Library/Logs/CrashReporter/MobileDevice/<DEVICE_NAME>
Windows XP: C:\Documents and Settings\<USERNAME>\Application Data\Apple computer\Logs\CrashReporter/<DEVICE_NAME>
Windows Vista: C:\Users\<USERNAME>\AppData\Roaming\Apple computer\Logs\CrashReporter/MobileDevice/<DEVICE_NAME>

次に、symbolicatecrash というツールを使うけど、ファイル階層が深いので /usr/local/bin あたりにコピーする。

$ sudo cp /Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Plug-ins/iPhoneRemoteDevice.xcodeplugin/Contents/Resources/symbolicatecrash /usr/local/bin/
$ symbolicatecrash -h

そして、.crash ファイルと、.dSYM ファイルを同じディレクトリにおいて(アプリはなくてもいいような?)実行する。

$ symbolicatecrash 送ってもらった.crash 保存しておいた.app.dSYM > 標準出力されるので適当な名前のファイル

最後に保存したログをテキストエディタなどで開いてチェック。今回のアプリではスレッドも多用してるので、たくさんログが出て来るが、Thread 0 Crashed:などと書いてある、どこのスレッドでクラッシュしているか、を最初に探す。もちろんこの場合はスレッド0を見る。
そうすると、ログがスタックされている(要は実行されているものが積み上がっている)ので、なるべく上(左の番号が若い)にある自分のアプリ名を探す、と、呼び出しているメソッドとその行番号がわかるので、その辺りが怪しいとわかる。

今回の自分のアプリでは、NSAutoreleasePool 辺りが怪しかったので、コメントにしたらクラッシュしなくなった。

元々ごにょごにょコードが書いてあったところを、少しづつメソッド化して切り出していった後の名残だったので、とりあえず問題ないかなと。

参考にさせてもらったのは以下のサイト

あとは、昨日書いたアナライズも役に立ってると思う。

2011/02/08 [Tue] Office L on Twitter

Powered by twtr2src.

2011年2月9日水曜日

xcodeでBuild And Analyze

Cocoaの日々さんの記事で初めて知ったのだけど、cmd+shift+aで、Build And Analyzeを実行すると、通常のビルドでは表示してくれない、コードの修正ポイントのようなものを教えてくれるらしい。

製作中のアプリに対して処理してみたところ、Cネイティブなコードで一カ所(これは今の俺だとちょっと直しようがないライブラリ的なところ)と、8カ所ほどautorelease関係のお知らせが出ていたので修正してみた。

個人的に入れ子の激しい複雑なコードは好きじゃないので、autoreleaseを多用しつつ、ひとつづつ変数を作っていくようなコードを書いていたのだけど、どうもそれだと何度も呼ばれる時によろしくないらしい。ちょっとぐぐって見たところ、同様の事象をたくさんみかけて、どうやら解決策としては、入れ子にしてしまって全てコンパイラ任せで処理するのがいいみたいだった。

ついうっかり修正前コードを破棄してしまったので再掲できないが、修正後コードはこんな感じ(背景に画像をセットする)

  NSString * imageName =[NSString stringWithFormat:@"bg%d.png",num]; 
  self.view.backgroundColor = [UIColor colorWithPatternImage:
                               [UIImage imageWithContentsOfFile:
                                [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:imageName]]];

要は、NSStringでファイル名を作って、バンドルのパス付きのファイルパスを作って、UIImageを作って、UIColorに渡す、のを別々に書いていたのだけど、ひとつなぎ(財宝じゃないしw)にした感じ。これで前述の警告はでなくなった。

アナライザの性能ってゆうか、情報がどこまで正なのかはよくわからないけど、頼れるものは頼った方が楽なので、今後はcmd+shift+aを多用しようと思う。

2011年2月8日火曜日

2011/02/07 [Mon] Office L on Twitter

Powered by twtr2src.

xcode の user scripts でコメントトグルの仕様変更

先の記事に書いたとおり、選択範囲の行頭がコメントかどうかだけで、コメントアウト、アンコメントを切り替えてしまうと、行頭にコメント記号のない行を最初の行として選択してしまった場合に、期待したとおりの結果をうまない。

というわけで、ほんのちょっと仕様変更して、選択した最初の行がコメントであるかどうかを、選択範囲の最初にコメントがあるかどうかではなく、選択範囲の最初の行がコメントであるかどうか(言い回しが難しいけど、要はその行がコメントであるかどうか)で判断するようにしてみた。以下全文。

#! /usr/bin/perl -w
#
# un_commentLines.pl -  Comments or uncomments the selected lines
#   Uses '# ' for Perl and shell scripts; '// ' otherwise

my $outputString = "";
my $perlCmt = "#";
my $cCmt = "//";

# Get the first few lines of the file
my $fileString = <<'HEADTEXT';
%%%{PBXHeadText}%%%
HEADTEXT


# determine the type of file we have by looking for the #! line at the top
# careful--it might already be commented out!
my $commentString;
if ($fileString =~ m!^($perlCmt|$cCmt)?#\!\s*.*?/perl|^($perlCmt|$cCmt)?#\!\s*.*?/sh!) {
 $commentString = $perlCmt;
} else {
 $commentString = $cCmt;
}

my @selection = ;       # read the selection from standard input

# no chars in selection, so create an empty selection
if (!@selection) {
    push @selection, "";
};  

# add or remove comment markers depending on the state of the first line of the selection
# if it is uncommented, comment all lines.  If it is commented, remove comment markers, if present
my $firstLineOfSelection = $selection[0]; #get first line
my $addingCommentsString = 1;
# ※1 この行をコメントして下記のように(\s*?)を活かす
#if ($firstLineOfSelection =~ /^$commentString/) { #selection starts with comment
if ($firstLineOfSelection =~ /^(\s*?)$commentString/) { #selection starts with comment
    $addingCommentsString = 0;
}

foreach my $line (@selection) {
    if ($addingCommentsString == 1) {
        $outputString .= $commentString.$line;
    } else {
# ※2 この行をコメントして下記のように(\s*?)を活かす
#        $line =~ s/^$commentString//;
        $line =~ s/^(\s*?)$commentString//;
        $outputString .= $line;
    }
}

print "%%%{PBXSelection}%%%";
print $outputString;
print "%%%{PBXSelection}%%%";

ご覧のように、選択範囲の先頭がコメント記号であるかどうか、ではなく、選択範囲の先頭から空白(ホワイトスペース、タブ等が含まれる)を無視して、コメント記号が最初にくるか、で判断するように変更した。※1
また、アンコメントする部分も、空白を含めて削除するようにした。※2

これにより、下記のようになった。

  // コメント
  ^ここから選択すると
   コメント
のようになり、
  // コメント
^ここから選択すると
 コメント
のようになる

一旦ここまでで満足していたのだけれど、eclipse (のPDTでしか見てないけど)のコメントトグルは、実は下記のように動く

  // コメント 文面だったりする
               ^ このあたりにカーソルがある場合でも
   コメント 文面だったりする
こんな風にちゃんとその行を見て処理してくれる。頭いい!

これもちゃちゃっと実装できないかなーと思ったのだけど、そもそもこのユーザースクリプトというシロモノ、入力と出力に規定の形があり、cmd+/は、入力が”選択部分”、出力が”選択部分を置き換える”となっており、この例のように選択されていない範囲を適用範囲に加えることができなさそう。。。もちろん、簡単じゃないだけで、何かしら抜け道はあるのだろうけれど。

例えば入力を”書類全体”にし、出力を”書類の内容を置き換える”にして、さらに選択範囲を把握することができれば、書類全体を書き換えてしまうことで対処可能なのかも。さすがにそこまではちょっとよろしくない気がするので、とりあえず置いておく事にするけど。誰かうまいことやったら教えてください。

xcode の user scripts でコメントトグルの仕様確認

先の記事にも書いた、xcode のコメントトグルのショートカット(cmd+/)がeclipseに比べておばかさん、の続き。

結論から言うと、少し賢くなった。はずw

その説明の前に少しおさらい。デフォルトの状態だと、cmd+/でコメントをトグルする時は、

  // コメント
^ここから選択を始めると
//  // コメント
のようになる

要は、行頭がコメントじゃないと、トグルせずに、コメントを重ねてしまう、のが問題だと思っていた。

が、先の記事に書いたリンク先で、デフォルトの cmd+/がperlで書かれたスクリプト、ということを知って、少し追ってみたところ、次のことがわかった。

  // コメント
  ^ここから選択を始めると
   コメント
のようになる

要は、選択範囲の先頭がコメント記号だとアンコメントし、そうでなければコメントアウトする、という仕様になっていた。(でもなぜだ?!)

つまり、選択範囲の最初をコメント記号にするようにすればあんまり問題ないようだ。。。

いや、ここはプログラマたるもの、些細なことも便利にしてなんぼ!いちいち選択範囲の先頭をコメントになるように選択するよりも、行範囲でまとめて選択するほうが楽だし、事実そうしているから問題だと思ったんじゃないか!

というわけで、次の記事で、当該ユーザスクリプトの説明と修正箇所を示したいと思います。

2011年2月7日月曜日

Default.pngの次のスプラッシュ画面

昨日の #hidev で、まーちさんにいろいろ教わったので忘れないうちにメモ。Default.pngの後のスプラッシュ画面の作り方。

loadView()で addSubViewして、NSThreadで指定秒後にそれを削除。addしたsubviewを取得する方法として、addする際にtagをつけておいて、削除する時にtagで取得するというのを教わった。

実際、コードはできているんだけど、それだけだと芸がないので、アニメーション等を加えてみてから再度記事にする予定。

xcode でユーザースクリプト

昨日の #hidev の最中に、xcode のコメントショートカット(cmd+/)は、eclipseのそれよりおばかさん、というのをつぶやいたところ、ユーザスクリプトでできるかもよ、というお話をいただいたので、ちょっと調べる。

Xcode Workspace Guide:User Scriptsが最初に見るところ。

ぐぐるとこんな記事にあたったので、別途勉強する。

日本語では、XcodeのUserScript - griffin-stewieの日記と、みるくCocoaさんのこの記事を見る。って、cmd+/もユーザスクリプトだったのか。。。

とりあえず、やりたいことは、cmd+/によるコメントのトグルを修正して、eclipseに近づける、要は、その行がコメントならコメントを外し、コメントじゃなければコメントにする、を実現したい。
今は、行頭にコメントがないとまたコメントしてしまうという仕様なので、たぶんこれがPerlで書かれてるんなら正規表現部だけなんとかすればいけそうな気がしていたりいなかったり。

2011/02/06 [Sun] Office L on Twitter

Powered by twtr2src.

2011年2月6日日曜日

sleep みたいなこと

  NSLog(@"start");
  [NSThread sleepForTimeInterval:3]; // 3 秒待つ
  NSLog(@"end");

Objective-C には sleep() がないらしい。で、iPhone で sleep(3) みたいにするには、スレッドに3秒待てと言えばいいってことか。

2011年2月5日土曜日

2011年2月4日金曜日

2011/02/03 [Thu] Office L on Twitter

Powered by twtr2src.

UISlider を縦にするとか

  UISlider* slider = [[[UISlider alloc]
                   initWithFrame:CGRectMake(0, 200, 200, 100)] autorelease];

  CGAffineTransform scale  = CGAffineTransformMakeScale(2.0, 2.0);
  CGAffineTransform trans  = CGAffineTransformMakeRotation(M_PI / 180.0f * 270.0f);
  CGAffineTransform concat = CGAffineTransformConcat(scale,trans);
  [slider setTransform:concat];

以前から試してみたかった、UISlider を縦にする方法を試してみた。おまけでスケールも二倍にw CGAffine を複数使う時は Concat でくっつけるといいそうだ。

UIを縦にすることができれば、アプリのデザインの幅も広がりそうな気がする今日この頃。

2011年2月3日木曜日

iPod のモデルとOSのバージョン

今作ってるアプリの摘要範囲を知るために、iPod (Touch)のモデルとバージョンを調べてみた。
iOS(アップル)(Wikipedia) と iPodのモデルを区別する方法(アップルのサポートページ) でいいのかな。

これによると、友人の初代 iPod Touch は iOS(この頃は OS X iPhone?)3.1.3 が最新らしい。てことは、初代 iPod Touch を範囲に含めるとしたら、ビルドターゲットを 3.1とかにすればいいってことかな?

実際、どこまで対応してるのかとか、他の開発者に聞いたことがないので、次回の #hidev でちょっと聞いてみようと思います。

あと、そろそろこち亀あたりで iPod / iPhone / iPad のハードウエアとOSの系譜とかやってもいい気がするw

iOSシミュレータで Retina ディスプレイをテスト

うちの開発機は Mac Book Pro 15inch なのですが、iOSシミュレータを使って Retinaディスプレイをテストすると、いつも小さく表示されていました。というよりも、むしろそれが普通だと思っていました。

アプリを開発するにあたり、画像の差し替え、3G機と4機の自動 Retina対応などをテストするにあたり、画像ファイル(@2xのアレ)に印をつけて対応していたのですが、iOSシミュレータのウィンドウメニューの表示サイズで100%を選択すると、大きく表示できることを知りました。15inchなので画面からあふれてしまい、スクロールを余儀なくされるのは玉にきずですが。。。

ちなみに通常のiPhoneのシミュレーションの場合は大きくしたりできないようです。

2011年2月1日火曜日

2011/01/31 [Mon] Office L on Twitter

Powered by twtr2src.