[iPhone][App] Nespresso Stock CheckerにGPS機能追加


Nespresso Stock Checkerに新機能を追加しました。GPS機能を使って最寄りのNespressoブティックを検索します。日本は17店舗あるのできっと役に立つと思います(USは7店舗)。
アプリケーション詳細はこちら。
» Nespresso Stock Checker
[iPhone]スタンフォード大学iPhone app講義のpodcast第8, 9回
第8回、9回講義(2009/04/27、29、要iTunes)聴講。第8回はUITableView(+UIScrollView)について、第9回はデータの読み込み・書き込みについて幅広く紹介。
第8回
ゲストスピーカーにUITableViewの開発チームの人(Jason Beaver)を呼んでTableViewについて解説してもらっているがこれが失敗。レクチャー慣れしていならしくUITableViewについて淡々と解説していくだけで、時折ソースコードなどを交えながら解説するも、すでに自前で書き終えたソースをズラズラ見せられるだけなので何を説明したいのか伝わってこない。特に目新しいことを説明しているわけでもないので、これなら教本でじっくり理解した方が良い。(おそろしいことに第9回の解説はUITableViewをすでに理解しているという前提の上に進んでいく)
第9回
こちらのスピーカーはいつものEvan Doll。Data persistenceについて、Property listやData archivingといったCocoaでよく見るデータ読み書き方法だけでなく、SQLiteをiPhone OS内で使うところまでも実際のコードを交えて解説(むしろSQLiteメインな解説だった)。外部データを扱う場面でもJSONの解説に時間が割かれている(一瞬だがFlickrからデータを取得するデモ付き)。このiPhone appコースだが、世にある書籍に書かれていない内容を意識的に扱うようにしているように感じられとても好印象だ。
- NSUserDefaultsもplistファイルを作成していることを知った。iPhoneシミュレーターで確認したらたしかにplistで保存されていた。これは便利。
~/Library/Application Support/iPhone Simulator/ User/Applications/APP_ID/Library/Preferences/
- さらに洗練されたデータ構造としてCore Dataをちらっと紹介。iPhone OS 2.xでは使えないと強調していたのでOS 3.0から使えるようになるのかな。
- 前々回(第7回)に引き続き、グローバル変数やシングルトンモデルを使うなと強調。前回は理由が納得できなかったが、特定のモジュールの単体テストができなくなることを理由に挙げていた。なるほど納得。
- 重要な内容なのに講義最後5分間ぐらいしか解説されていないが、idやプロトコルで定義されているインスタンス変数(例えばdelegate)のSetter/Getterの書き方やリリース方法について。何故retainではなくassignを使うべきなのか理由が今イチ理解できず。絵にしてもらえたらよかった。
- (CGSize)sizeWithFont:(UIFont *)font
Tags: iPhone, Objective-C, UIKit
[iPhone] HTMLリンクのようなUIButtonのサブクラスでボタンタイトルの文字列の長さに応じてアンダーラインを引く方法を試行錯誤していたが、NSStringオブジェクトのCGSizeを返すメソッドであっさり解決した。
// UIStringDrawing.h // UIKit @interface NSString(UIStringDrawing) - (CGSize)sizeWithFont:(UIFont *)font; @end
修正前
// HTMLLinkButton.m - (void)drawRect:(CGRect)rect { CGFloat w = rect.size.width; CGFloat h = rect.size.height; CGFloat capHeight = self.font.capHeight; CGFloat xHeight = self.font.xHeight; CGFloat underlineLength = [_buttonTitle length] * xHeight; CGFloat startX = (w - underlineLength) / 2.0; CGFloat startY = (h + capHeight) / 2.0; CGFloat endX = startX + underlineLength; CGFloat endY = startY; CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetStrokeColorWithColor(context, _colorController.currentColor.CGColor); CGContextMoveToPoint(context, startX, startY); CGContextAddLineToPoint(context, endX, endY); CGContextStrokePath(context); }
修正後
// HTMLLinkButton.m - (void)drawRect:(CGRect)rect { CGFloat w = rect.size.width; CGFloat h = rect.size.height; CGFloat titleWidth = [_buttonTitle sizeWithFont:self.font].width; CGFloat titleHeight = [_buttonTitle sizeWithFont:self.font].height; CGFloat descender = self.font.descender; CGFloat startX = (w - titleWidth) / 2.0; CGFloat startY = (h + titleHeight) / 2.0 + descender; CGFloat endX = startX + titleWidth; CGFloat endY = startY; CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetStrokeColorWithColor(context, _colorController.currentColor.CGColor); CGContextMoveToPoint(context, startX, startY); CGContextAddLineToPoint(context, endX, endY); CGContextStrokePath(context); }
[iPhone]スタンフォード大学iPhone app講義のpodcast第6, 7回
第6回、7回講義(2009/04/20、22、要iTunes)聴講。第6回はUIViewControllerについて、第7回はUINavigationControllerについて。
第6回
何もない。マジで何も感想がない。UIViewControllerを知らない人以外は見なくてよい(かも)。ただ、後半にNSUserDefaultsを使ったデータ保存の簡単なデモをやっていたのでData persistenceについて知らない人には有益(かも)。
第7回
第6回と異なり、良い意味で細かい情報満載だった。Dave Mark - Beginning iPhone DevelopmentやErica Sadun - The iPhone Developer’s Cookbookに解説されていない有益な情報多数、UINavigationControllerは思っていた以上にフレキシブルなことがわかった。UINavigationControllerはUITableViewとあわせて説明されている場合が多いが(欲張りな教本では一緒くたに解説されていることが多い)、今回はUITableViewは一切用いず説明しているため構造が理解しやすくなっている。
- 意外だったのだけど、UINavigationController配下(に限ったことではないが)のViewController間のデータの受け渡しはグローバル変数(Singletonモデルなど)を用いずに引数でちゃんと渡せと強調していた。コントローラーが再利用しにくくなるからとか何とか一般的な説明をしていたが、そもそもCocoaのコントローラー、特にViewControllerを再利用する機会があるのかどうか疑わしい。
- UIViewController.navigationControllerというプロパティが存在した!UINavigationController配下のViewControllerはAppDelegateなどを呼び出さずにUINavigationControllerオブジェクトにアクセスできる(UINavigationController配下でない場合はもちろんnil)。
- UIBarButtonSystemItemの存在を知った!APIドキュメント見たら20個ぐらい種類あって、しかもマーク画像まで掲載されていて感動。例えばプラスマーク表示したい時は、
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(add:)]; self.navigationItem.rightBarButtonItem = item; [item release];
(注意: ViewController内にadd:メソッドが定義済みと仮定)
- Backボタン(戻るボタン)の名前の変更方法があった!例えば親ViewController内に
self.title = @"This is my title"; UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Back" style:UIBarButtonItemStyleBordered target:nil action:nil]; self.navigationItem.backBarButtonItem = backButtonItem; [backButtonItem release];
と書いておくと、子ViewController内に表示されるBackボタンのタイトルを”This is my title”から”Back”に表示変更できる。(Backボタンはtargetもactionも設定不要)
[iPhone] HTMLリンクのようなUIButtonのサブクラス
Tags: iPhone, Objective-C, UIKit
UIKitにはテキストにアンダーラインを付ける仕組みがない。そのため、HTMLのリンク表示のようなボタンを作るためにはアンダーラインを自前で描画してやらなければならない。ここで問題になるのが、アンダーラインの長さと描画位置が、アンダーラインを付加したい文字列の長さとフォントサイズに依存するということだ。以下のカスタムクラス(HTMLLinkButton)は、ボタンのタイトル文字列の長さとフォントサイズに比例してアンダーラインを可能な限り適切な位置に描画する。
使い方は簡単で、ボタンタイトルとリンク先URLを設定してやるだけ。例えば、UIViewControllerクラスに以下のように記述してやると上記画像のようなHTMLリンクボタンが表示される。リンクをクリックするとテキストがハイライトされ、指定のURLへナビゲートされる。
#import "HTMLLinkButton.h" - (void)viewDidLoad { HTMLLinkButton *button1 = [[HTMLLinkButton alloc] initWithTitle:@"Google" url:@"http://google.com/"]; button1.frame = CGRectMake(50, 100, 100, 37); button1.font = [UIFont systemFontOfSize:24]; [self.view addSubview:button1]; [button1 release]; HTMLLinkButton *button2 = [[HTMLLinkButton alloc] initWithTitle:@"Yahoo" url:@"http://yahoo.com/"]; button2.frame = CGRectMake(150, 100, 150, 37); button2.font = [UIFont systemFontOfSize:40]; [self.view addSubview:button2]; [button2 release]; }
先ほど、可能な限り適切な位置に描画する、と言ったのには訳がある。ボタンタイトルの文字列の扱いに以下のような仮定があるからである。
- ボタン文字列の高さはUIButton.font.capHeight(現在使用されているフォントの大文字の高さ)に等しい
- ボタン文字列の各文字の幅はUIButton.font.xHeight(現在使用されているフォントの小文字xの高さ。幅ではない)に等しい
これらの仮定を用いた処理はHTMLButtonLinkクラスのdrawRect:メソッドで行われている。改良の余地アリ。
(**追記2009/05/02: 文字列の長さ・高さの扱いは- (CGSize)sizeWithFont:(UIFont *)fontでスマートに解決した)
Source code
// HTMLLinkButton.h #import <UIKit/UIKit.h> extern CGFloat const HTMLLinkButtonDefaultButtonWidth; extern CGFloat const HTMLLinkButtonDefaultButtonHeight; extern NSUInteger const HTMLLinkButtonDefaulltButtonNormalColor; extern NSUInteger const HTMLLinkButtonDefaulltButtonHighlightedColor; @interface HTMLLinkButton : UIButton { NSString *_buttonTitle; NSString *_url; UIColor *_normalColor; UIColor *_highlightedColor; UIColor *_currentColor; } @property (nonatomic, retain) NSString *buttonTitle; @property (nonatomic, retain) NSString *url; @property (nonatomic, retain) UIColor *normalColor; @property (nonatomic, retain) UIColor *highlightedColor; /** * The designated initializer. * @param title Button title. * @param url URL string to link to. * @param normalColor Color of the button title in the normal state. * @param highlightedColor Color of the button title when highlighted. */ -(id)initWithTitle:(NSString *)title url:(NSString *)url normalColor:(UIColor *)normalColor highlightedColor:(UIColor *)highlightedColor; /** * The same as the above initialize * except for that the title color sets to default. * @param title Button title. * @param url URL string to link to. */ -(id)initWithTitle:(NSString *)title url:(NSString *)url; @end
// HTMLLinkButton.m #import "HTMLLinkButton.h" #import "HTMLLinkButton-Private.h" #import "UIColor-HexEncoding.h" CGFloat const HTMLLinkButtonDefaultButtonWidth = 72.0; CGFloat const HTMLLinkButtonDefaultButtonHeight = 37.0; NSUInteger const HTMLLinkButtonDefaulltButtonNormalColor = 0×2255AA; NSUInteger const HTMLLinkButtonDefaulltButtonHighlightedColor = 0xFF9900; @implementation HTMLLinkButton @synthesize buttonTitle = _buttonTitle; @synthesize normalColor = _normalColor; @synthesize highlightedColor = _highlightedColor; @synthesize url = _url; -(id)initWithTitle:(NSString *)title url:(NSString *)url normalColor:(UIColor *)normalColor highlightedColor:(UIColor *)highlightedColor{ if (self = [super init]){ self.buttonTitle = title; self.url = url; self.normalColor = normalColor; self.highlightedColor = highlightedColor; _currentColor = _normalColor; [self setup]; } return self; } -(id)initWithTitle:(NSString *)title url:(NSString *)url{ UIColor *normalColor = [UIColor colorFromHex:HTMLLinkButtonDefaulltButtonNormalColor]; UIColor *highlightedColor = [UIColor colorFromHex:HTMLLinkButtonDefaulltButtonHighlightedColor]; return [self initWithTitle:title url:url normalColor:normalColor highlightedColor:highlightedColor]; } //Override - (void)drawRect:(CGRect)rect { CGFloat w = rect.size.width; CGFloat h = rect.size.height; CGFloat capHeight = self.font.capHeight; CGFloat xHeight = self.font.xHeight; CGFloat underlineLength = [_buttonTitle length] * xHeight; CGFloat startX = (w - underlineLength) / 2.0; CGFloat startY = (h + capHeight) / 2.0; CGFloat endX = startX + underlineLength; CGFloat endY = startY; CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetStrokeColorWithColor(context, _currentColor.CGColor); CGContextMoveToPoint(context, startX, startY); CGContextAddLineToPoint(context, endX, endY); CGContextStrokePath(context); } - (void)dealloc { [_buttonTitle release]; [_url release]; [_normalColor release]; [_highlightedColor release]; [_currentColor release]; [super dealloc]; } #pragma mark - #pragma mark Setters //Override -(void)setFrame:(CGRect)frame{ [super setFrame:frame]; [self setNeedsDisplay]; } //Override -(void)setFont:(UIFont *)font{ [super setFont:font]; [self setNeedsDisplay]; } //Override -(void)setNormalColor:(UIColor *)color{ if (_normalColor != color){ [_normalColor release]; _normalColor = [color retain]; [self setNeedsDisplay]; } } //Override -(void)setHighlightedColor:(UIColor *)color{ if (_highlightedColor != color){ [_highlightedColor release]; _highlightedColor = [color retain]; [self setNeedsDisplay]; } } @end
// HTMLLinkButton-Private.h #import <UIKit/UIKit.h> #import "HTMLLinkButton.h" @interface HTMLLinkButton (Private) /** * @private */ -(void)setup; -(void)changeTextColor; -(void)touchUpInside; @end
// HTMLLinkButton-Private.m #import "HTMLLinkButton-Private.h" @implementation HTMLLinkButton (Private) -(void)setup{ [self setFrame:CGRectMake(0, 0, HTMLLinkButtonDefaultButtonWidth, HTMLLinkButtonDefaultButtonHeight)]; [self setTitle:_buttonTitle forState:UIControlStateNormal]; [self setTitle:_buttonTitle forState:UIControlStateHighlighted]; [self setTitleColor:_normalColor forState:UIControlStateNormal]; [self setTitleColor:_highlightedColor forState:UIControlStateHighlighted]; [self addTarget:self action:@selector(changeTextColor) forControlEvents:UIControlEventTouchDown]; [self addTarget:self action:@selector(changeTextColor) forControlEvents:UIControlEventTouchDragExit]; [self addTarget:self action:@selector(changeTextColor) forControlEvents:UIControlEventTouchDragEnter]; [self addTarget:self action:@selector(touchUpInside) forControlEvents:UIControlEventTouchUpInside]; } -(void)changeTextColor{ if (_currentColor == _normalColor){ _currentColor = _highlightedColor; } else{ _currentColor = _normalColor; } [self setNeedsDisplay]; } -(void)touchUpInside{ [self changeTextColor]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:_url]]; } @end
// UIColor-HexEncoding.h #import <UIKit/UIKit.h> @interface UIColor (HexEncoding) +(UIColor *)colorFromHex:(NSUInteger)color24; @end
// UIColor-HexEncoding.m #import "UIColor-HexEncoding.h" @implementation UIColor (HexEncoding) +(UIColor *)colorFromHex:(NSUInteger)color24{ CGFloat r = (color24 >> 16) / 255.0f; CGFloat g = (color24 >> 8 & 0xFF) / 255.0f; CGFloat b = (color24 & 0xFF) / 255.0f; return [UIColor colorWithRed:r green:g blue:b alpha:1.0f]; } @end




