iPhone4のアプリアイコンだけ有効にならない?!

同様のケースを耳にしたことはないんだけど、なぜか自分のとあるプロジェクトでは、icon@2x.pngが上書きインストールのときだけ有効にならない。アプリアイコン以外はきっちり@2xが有効になってるのになんで?!
というおかしな現象に2ヶ月間も悩まされ続け、「リリースしたらビルドおかしくて動かないケース出るんじゃね?」とか「この時期にアップデートしてRetina未対応に見えたらレビュー死すんじゃね?」みたいな恐怖でどないしたもんやろと思っていたときに救いの記事が!

アイコンファイル関連情報のまとめ〜強火で進め

ノーチェックだった「Icon files」。藁にもすがる気持ちでこっち使ってみたら、無事上書き時でもRetinaアイコンが有効になりました。

おかげさまで無事『付箋かんばん』のVer2.4がリリースできました。記事ありがとうございました。いつもブログチェックしてます。

iPhoneアプリ開発勉強会@福井高専

[福井]iPhoneアプリ開発勉強会 http://atnd.org/events/7182
懐かしの母校でのiPhoneアプリ開発勉強会に講師として参加したので、発表内容を簡単にまとめます。発表内容は下記のとおり。
・テーブルビュー基礎
・テーブルビューセルにUISwitch
・テーブルビューセルでバッヂ表示


■テーブルビュー基礎
こちらのページ(『混沌のiPhoneアプリケーション工房』さん)で丁寧に説明されてますので、そちらを見てください。次のページに進むとセルの追加や削除、移動も説明されていてオススメのサイトです。


■テーブルビューセルにUISwitch
TableViewControllerでセルにUISwitchを埋め込む。セルは2つ。UISwitchの変更を検出する。

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 2;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
        UISwitch *uiSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(0, 0, 90, 24.0)];
        cell.accessoryView = uiSwitch;
        [uiSwitch release];
    }
    
    // Configure the cell.
    if (indexPath.row == 0) {
        cell.textLabel.text = @"switch0";
        [(UISwitch *)cell.accessoryView addTarget:self action:@selector(switch0Changed:)
                                 forControlEvents:(UIControlEventValueChanged | UIControlEventTouchDragInside)];
    } else {
        cell.textLabel.text = @"switch1";
        [(UISwitch *)cell.accessoryView addTarget:self action:@selector(switch1Changed:)
                                 forControlEvents:(UIControlEventValueChanged | UIControlEventTouchDragInside)];
    }
    
    return cell;
}

- (void)switch0Changed:(id)sender {
    if ([(UISwitch *)sender isOn]) {
        NSLog(@"switch0 is ON");
    } else {
        NSLog(@"switch0 is OFF");
    }
}

- (void)switch1Changed:(id)sender {
    if ([(UISwitch *)sender isOn]) {
        NSLog(@"switch1 is ON");
    } else {
        NSLog(@"switch1 is OFF");
    }
}


■テーブルビューセルでバッヂ表示
TableViewControllerでセルにrowの値をバッヂを表示。セルは2つ。

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 2;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
        Badge *badge = [[Badge alloc] initWithFrame:CGRectMake(0, 0, 40, 25)];
        cell.accessoryView = badge;
        [badge release];
    }
    
    // Configure the cell.
    if (indexPath.row == 0) {
        cell.textLabel.text = @"badge0";
        ((Badge *)cell.accessoryView).num = indexPath.row;
    } else {
        cell.textLabel.text = @"badge1";
        ((Badge *)cell.accessoryView).num = indexPath.row;
    }

    return cell;
}

バッヂの宣言

#import <UIKit/UIKit.h>

@interface Badge : UIView {
    int num;
}

@property (nonatomic) int num;

@end

バッヂの実装

#import "Badge.h"

@implementation Badge

@synthesize num;

- (id)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        self.opaque = NO;
        self.userInteractionEnabled = NO;
    }
    return self;
}

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
	
    UIFont *font = [UIFont boldSystemFontOfSize:14];
    NSString *countString = [NSString stringWithFormat:@"%d", num];
    CGSize numberSize = [countString sizeWithFont:font];
    CGRect bounds = CGRectMake(0, 0, numberSize.width + 16 , 18);
    float radius = bounds.size.height / 2.0;
	
    UIColor *badgeColor = [UIColor colorWithRed:0.530 green:0.600 blue:0.738 alpha:1.000];
    CGContextSetFillColorWithColor(context, [badgeColor CGColor]);
    CGContextBeginPath(context);
    CGContextAddArc(context, (rect.size.width - bounds.size.width) + radius, radius, radius, M_PI / 2 , 3 * M_PI / 2, NO);
    CGContextAddArc(context, rect.size.width - radius, radius, radius, 3 * M_PI / 2, M_PI / 2, NO);
    CGContextClosePath(context);
    CGContextFillPath(context);
	
    [[UIColor whiteColor] set];
    bounds.origin.x = (rect.size.width - bounds.size.width) + (bounds.size.width - numberSize.width) / 2;
    [countString drawInRect:bounds withFont:font];
}

@end


別マシンからDropbox経由で同期取ったテキストファイルが開けなくてちょっとグダグダになった部分もあってすいませんでした。コーディングしながらの説明でこの内容を30分というのは、ちょっと無謀だったかも?とちょっと反省してますw。でも、こんなことが簡単にできるよというのは伝わったと思うので、あとは実際にやってみていただければと。

テーブルセルの並び替え

テーブルセルの並び替えをするときは、UITableViewControllerに以下のメソッドを実装します。セクションをまたいだ移動を禁止するなど、移動可能な範囲を制御することも可能です。
# 並び替え以外の箇所は省略します。

- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
  if (移動したいもの) return YES;
  else return NO;
}

移動可能なセルを決めます。"return YES;"したものが移動可能になります。

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
  // 移動後の後処理
}

このメソッドを実装すると、実際に移動できるようになります。移動後に処理することをここに記述します。

- (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath {
  if (sourceIndexPath.section == proposedDestinationIndexPath.section) {
    // 移動後のセクションが同じセクションならそのまま
    return [NSIndexPath indexPathForRow:proposedDestinationIndexPath.row inSection:proposedDestinationIndexPath.section];
  } else if (sourceIndexPath.section > proposedDestinationIndexPath.section) {
    // 移動後のセクションのほうが上(小さい)なら、元のセクションの一番上(0)にする
    return [NSIndexPath indexPathForRow:0 inSection:sourceIndexPath.section];
  } else {
    // 移動後のセクションのほうが下(大きい)なら、元のセクションの一番下にする
    return [NSIndexPath indexPathForRow:元のセクションの一番下 inSection:sourceIndexPath.section];
}

どこにでも移動可能にするなら、このメソッドは実装しなくてOKです。上の例では、セクションをまたいだ移動を禁止しています。

テスト機としてiPod touchの整備済製品買ってみた

予期せぬドナドナで初代touchがいなくなりiOS3環境がなくなったので、少し安いだけだけど安心感はあるしまぁいいかなーと、Apple Storeで第二世代8G機の整備済製品買ってみた。14,800円也。

Apple Storeの整備済製品って前から気にはなってたんだけど、やっぱ中古だしなーと二の足を踏んでたので、同じような人に向けて軽くレポートしてみます。

箱(白い!もしかして純正の箱じゃない?)


箱開けたところ


裏面(やっぱり傷はない)


表面


傷もないし、ちょっと安いんでありかなって思いました。もうちょっと出すだけで安い店で新品買えそうな気もしますがw

ちなみに、いつからの在庫なのか分かんないけど、バッテリーは完全に消費していて、電源は入りませんでした。どうせiTunesにつながないといけないからあんまり関係ないけど。

あと、写真の撮り方下手ですいません。影作ってみたり映り込んでみたりw

UITextField入りのUIAlertView

文字列入力または編集のポップアップを出したかったので調べました。色々な記事があるけど、参考にしたのは下記の記事です。

iPhone Coding Tutorial – Inserting A UITextField In A UIAlertView


あと、中に持ったUITextFieldが編集中のままUIAlertViewが消えるときに、"wait_fences: failed to receive reply: 10004003"と出るので、それの対応は下記の記事を参考にさせていただきました。

wait_fences: failed to receive reply: 10004003


最終的な自分のコードはこんな感じ。textFieldの宣言の箇所は省いてます。

- (void)showWithTitle:(NSString *)title text:(NSString *)text {
	UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title
				message:@" "
				delegate:self
				cancelButtonTitle:@"Cancel"
				otherButtonTitles:@"OK!", nil];
	textField = [[UITextField alloc] initWithFrame:CGRectMake(12, 45, 260, 25)];
	textField.text = text;
	CGAffineTransform myTransform = CGAffineTransformMakeTranslation(0, 60);
	[alert setTransform:myTransform];
	[textField setBackgroundColor:[UIColor whiteColor]];
	[alert addSubview:textField];
	[alert show];
	[alert release];
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
	[textField resignFirstResponder];
	if (buttonIndex == 1) {
		// OKのときの実装
	}
}

.appの名前が変わると実機では上書き実行できない

なぜかSDKを新しくしたら、プロジェクトのヘッダ検索パスを正常に扱ってくれなくなってコンパイルできなくなったので、プロジェクトを再作成することになりました。
そのときに、プロジェクト名を変えたら作成される.appの名前が変わったわけですが、そうなると実機に上書きで転送して実行することができなくなります。

CFBundleIdentifierだけで判断してくれるかなと思ったけどそれだけではダメなようで、エラーメッセージは、"Error from Debugger: The program being debugged being not run." になります。

結果的には、.xcodeprojディレクトリの中のproject.pbxprojの中の.app名をガリガリとテキスト編集して修正しましたが、他になんか良いやり方があるんじゃないのかなぁと。

ちなみに、InfoPlist.strings内のCFBundleExecutableの編集だけではXCode上のProductsやターゲットが変更されず、実行できませんでしたし、Info.plist内のCFBundleExecutableなどを編集すると、ビルドに失敗しました。

UITextFieldのカーソル位置に文字列を挿入

リファレンスにあたってもUITextFieldのカーソル位置への文字列挿入を見つけられなかったので、クリップボードに文字列突っ込んで、ペーストしてみた。

- (void)insertText:(NSString *)str {
  UIPasteboard* board = [UIPasteboard generalPasteboard];

  // 現在のクリップボードの文字列を保持する
  NSString *orgStr = board.string;

  // クリップボードに挿入したい文字列をコピーする
  [board setString:str];

  // UITextField上でペーストする
  [textField paste:textField];

  // クリップボードの文字列を復元する
  [board setString:orgStr];
}

文字列以外のクリップボードの情報が損なわれないかは検証してませんので、その点はご了承ください。