PHPで配列やオブジェクトをログに出力する

ログに現在のオブジェクトの状態を確認したいけど、画面に出力できないときの対処法。
たとえばiPhoneアプリ->サーバ間の通信でJSONとかXMLを使って処理してるけど、うまくオブジェクトが出せないときに・・・。
ちなみに今回はPHPのお話です。


やる事はすごく簡単で、error_logにjson_encodeをかませるだけ。

error_log(json_decoe($obj));

エラーログに見た目jsonの行を発見したらそれが↑のデータって認識で多分大丈夫です。

XCode 4系で「Developer Tools Accessはデバッグを続けるためにほかの..」というメッセージが出て、シミュレータが使えない場合の対処

なんらかの事情でAdmin権限が使えないMac上で発生する模様。

管理者にお願いして、以下のコマンドを使うと行けるみたい。

sudo dscl . append /Groups/_developer GroupMembership <ユーザ名>

それで、dsclコマンドって何者なのかというと、
「Directory Service Command Line Utility」の略だそうで。
指定したユーザを_developerっていうグループに追加するってことでいいのかなあ。


http://stackoverflow.com/questions/1837889/authorize-a-non-admin-developer-in-xcode-mac-os

http://slashdot.jp/journal/441679/Mac-OS-X%E3%81%A7%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%83%A9%E3%82%A4%E3%83%B3%E3%81%8B%E3%82%89%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B

起動画面を表示した直後にもう1枚画像を表示したい場合

例えば、企業ロゴを表示した後、アプリのロゴ画像を出したいって処理を実装したい場合。

メインのビューにstatusBarを表示しないんであれば、一番最初に表示されるUIViewControllerのViewDidLoadあたりにそういう処理を書いてしまえばいいんだけど、アプリ内でステータスバーを表示したい場合はなんとなく処理がややこしそうだと想像。

なので適当な対応策として、アプリ起動にUIWindowにUIImageViewを追加し、AppDelegate.m内で処理をしてあげれば一番最初に表示されるUIViewControllerに影響がでないんではないかと思ったので、以下のようなプログラムを書いてみました。

今回の場合、1枚目の起動画面をDefault.png、2枚目の起動ファイルをDefault2.pngと指定します。
アプリの起動時にはDefault.pngが表示されるように設定しておきます。


ヘッダファイル側(.h)

@interface hogehogeAppDelegate : NSObject <UIApplicationDelegate> {
    UIImageView *firstSplashView_;      // 起動画面1枚目(Default.png)
    UIImageView *secondSplashView_;     // 起動画面2枚目(Default2.png)
}

プログラム側(.m)

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"Default" ofType:@"png"];
    UIImage *img = [[UIImage alloc] initWithContentsOfFile:filePath];
    firstSplashView_ = [[UIImageView alloc] initWithImage:img];
    [img release];

    [self.window addSubview:firstSplashView_];
    [self.window makeKeyAndVisible];
    
    [self performSelector:@selector(dispSecondSplashView) withObject:nil afterDelay:2.0f];
    return YES;
}

// 画像2枚目を表示
- (void)dispSecondSplashView {
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:1.0f];
    firstSplashView_.alpha = 0.0f;
    [UIView commitAnimations];
    
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"Default2" ofType:@"png"];
    UIImage *img = [[UIImage alloc] initWithContentsOfFile:filePath];
    secondSplashView_ = [[UIImageView alloc] initWithImage:img];
    [img release];
    
    [self.window insertSubview:secondSplashView_ belowSubview:firstSplashView_];
    [self performSelector:@selector(dispMainView) withObject:nil afterDelay:2.5f];
}

// メイン画面表示
- (void)dispMainView {
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:1.0f];
    secondSplashView_.alpha = 0.0f;
    [UIView commitAnimations];
    
    [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:YES];
    [self.window insertSubview:self.viewController.view belowSubview:secondSplashView_];
    [self performSelector:@selector(removeSplashView) withObject:nil afterDelay:1.1f];
}

// 起動画面系削除
- (void)removeSplashView {
    [firstSplashView_ removeFromSuperview];
    firstSplashView_ = nil;
    [secondSplashView_ removeFromSuperview];    
    secondSplashView_ = nil;
}

ただ、このままだと起動画面が3枚、4枚と増えるようになってくるとそのぶんメソッドが増えてしまいますね。
ループでまわすような処理を作ってやる方がいいのかな・・・。

UITextFieldで文字数制限(非フリック入力対応?)

UITextFieldで文字数制限をかけてみようと検索したところ、
UITextFieldDelegateの

  • (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string;

を使えば良い。
UITextFieldの入力制限を実装する - プログラマでありたい


と思って実装してみたんですが、
iPhone日本語キーボードのフリック入力では、設定した文字数まで入力できたんですが、
フリック入力をしないケータイ打ち(ボタン連打)の方法で入力しようとすると、
最後の文字が正しく入力できないということが判明。

たとえば最後の文字に「ん」を入力したい場合、
フリック入力だと、「わ」を押しながら上にフリックすると入力できるんだけど、
ケータイ打ちの場合は「わ」→「を」→「ん」と入力しようとすると
「わ」以降の入力を受け付けてくれません。


このとき、shouldChangeCharactersInRange:のrangeの値を出力してみたところ
locationが最大文字数+1,rangeが0になっていて、ケータイ打ちの場合、iPhoneの内部処理的には
「1文字追加して、前の1文字消す」的な処理をしてるかもしれませんね。(想像)
フリック入力をしない人のためにどうしようかと考えてみたんですが、
UITextFieldにUIControlEventEditingChangedのアクションをつける方法で対応を考えました。
UITextField でのバリデーションのタイミング (フェンリル | デベロッパーズブログ)


ただ、上記の方法を使う場合、UITextFieldの編集中に呼び出されるメソッド内で
UITextFieldの文字を変更してしまうと、ずっとUIControlEventEditingChangedを呼び出しつづける
無限ループが発生してしまいます。

なので、UITextField内の文字を変更するときは、 変更する直前に以下の方法をとる必要があります。

  1. addTarget:した対象を外してあげる
  2. テキストを追加
  3. 再度addTarget:し直す


コードとして書くとこんな感じになります。
今回の場合はtextField_というUITextFIeldに対し、文字数の入力制限をかけています。
今回の制限文字数は8文字にしています。
制限したい文字数を変えたい場合は、textFieldEditingChanged:の
maxLengthの値を適宜変更してください。

- (void)viewDidLoad
{
    [super viewDidLoad];
   
    [textField_ addTarget:self action:@selector(textFieldEditingChanged:) forControlEvents:UIControlEventEditingChanged];
}

-(IBAction)textFieldEditingChanged:(id)sender {
    int maxLength = 8;
    UITextField *txt = (UITextField *)sender;
    NSString *str = txt.text;
    if ([str length] >= maxLength) {
        [textField_ removeTarget:self action:@selector(textFieldEditingChanged:) forControlEvents:UIControlEventEditingChanged];
        textField_.text = [str substringToIndex:maxLength];
        [textField_ addTarget:self action:@selector(textFieldEditingChanged:) forControlEvents:UIControlEventEditingChanged];
    }
}

なんとかこれで動くようにはなったのですが、
このばあい9文字以降を入れようとすると、「文字は表示されないけど、予測変換が出てしまう」
という問題が出てしまいますがね・・・。

2011/9/13 追記
実はこれでもケータイ打ちには対応できなかったので、
結局のところUITextFieldDelegateで「確定」が押されたときに
テキストを切るように設定しました。

// メッセージ入力終了処理
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    NSString *str = nameTextField_.text;
    int maxLength = 8;
    if ([str length] >= maxLength) {
        nameTextField_.text = [str substringToIndex:maxLength];
    }
    
    [textField resignFirstResponder];
    return YES;
}

XCode4.0.2で file was built for archive which is not the architecture being linked (armv7) というエラーが出たとき

約3ヶ月も何も書いてませんでした。
どういう方針で書いていけばいいかもまだ決めていなかったので適当ですw


さて、タイトルの話。
XCode3時代に作ったアプリをXCode4で開き直し、「さてビルドしよう」と思った矢先に出たエラー。

コンソールにはこんな感じの出力が

Ignoring file <アプリのディレクトリ>/Classes/libHogeHoge.a, file was built for archive which is not the architecture being linked (armv7)


ビルドの設定では以下のように指定。
Architectures:Standard(armv6,armv7)
Base SDK:iOS4.3
iOS Deployment Target:iOS 3.1


Google様を頼っていろいろ検索してみるも、よき解決策が見つからず・・・。
結局armv7でコンパイルされたlibHogeHoge.aを用意してもらわなければならないのかなあと途方に暮れていました。

「ビルドの設定でアーキテクチャをarmv6,armv7で指定したけど、XCode4様がarmv7でしかコンパイルしてくれないんじゃね?」と思い、Archtecturesの値を「armv6」と直接手打ちで入力してみたところ、ビルドが通り、アプリが動作することを確認できました!!


こんな感じです。

しかし、これであっているのかは正直微妙・・・。
iOS4.3.3/iPhone4 と iOS 3.1.3/iPhone 3GSでは動作することを確認してみたけど・・・。

画像ファイルの拡張子を一気に変更

大量のpngファイルがあるけど、これらを全てjpegにしたいときはconvertコマンドを使う

たとえばこんなコマンド

 $ for f in `ls`;do convert $f `echo "$f" |sed -e "s/.png/.jpg/g"` ;done


出力結果はこんな感じ

bash-3.2$ ls
1.png 2.png 3.png 4.png 5.png 6.png 7.png 8.png
bash-3.2$ for f in `ls`;do convert $f `echo "$f" |sed -e "s/.png/.jpg/g"` ;done
bash-3.2$ ls
1.jpg 2.jpg 3.jpg 4.jpg 5.jpg 6.jpg 7.jpg 8.jpg
1.png 2.png 3.png 4.png 5.png 6.png 7.png 8.png
bash-3.2$ 


convertコマンドを使って変換しているので、
全てのjpgファイルにぼかしを入れたいって時はこんなコマンド

 $ for f in `ls`;do convert -blur 7 $f `echo "$f" |sed -e "s/.jpg/.b.jpg/g"` ;done

タッチした位置が指定された範囲内かどうかを調べる

CGRectContainsPointという関数が便利。
1つ目の引数で指定された範囲内(CGRect)に、2つ目の引数で指定された位置(CGPoint)があればtrueが帰ってくる。


たとえば指定したビューのとある位置がタッチされたら何かアクションを
起こしたい場合はこんな感じかな。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    CGPoint pt = [[touches anyObject] locationInView:self.view];
    if (CGRectContainsPoint(CGRectMake(0,0,100,100),pt)) {
        NSLog(@"あたり");
    }
}