八王子にいってきた
参加した目的
今後大勢の前でしゃべる必要があるので練習するために参加してみた。
反省
- 印刷すると思ったよりコードが長い事に気づいた。控えよう。
- 着席5分後にビールを飲んだら眠くなり、最初の1時間ぐらい人の話聞いてなかった気がする。控えよう。
- 自分の言いたい事をたくさん記述しておくと、読み切るので忙しくなるので途中で省略したくなる。控えよう。
収穫
印象に残った発言
GWに作りたいものは? => 「時間」
まとめ
八王子pmはそんなに怖くなかったよ。
jquery.tmpl
javascriptのテンプレート
jQuery1.5から標準で使えるそうです
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8" /> <meta name="author" content="" /> <meta name="keywords" content="" /> <meta name="description" content="" /> <title>Testing jquery.tmpl</title> <link type="text/css" href="css/prj.min.css?v=2" rel="stylesheet" media="screen" /> <link type="text/css" href="css/print.css?v=2" rel="stylesheet" media="print" /> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js" type="text/javascript"></script> <script src="http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js" type="text/javascript"></script> <script id="movieTemplate" type="text/x-jquery-tmpl"> <li><b>${Name}</b> (${ReleaseYear})</li> </script> <script type="text/javascript"> $(document).ready( function() { var movies = [ { Name: "The Red Violin", ReleaseYear: "1998" }, { Name: "Eyes Wide Shut", ReleaseYear: "1999" }, { Name: "The Inheritance", ReleaseYear: "1976" }, ]; $( "#movieTemplate" ).tmpl( movies ).appendTo( "#movieList" ); $("span").appendTo("#foo"); }); </script> </head> <body> <ul id="movieList"></ul> </body>
Testing ruby product 'keydown'
I tried to install keydown in my MacOSX.
And I wrote it down here.
git clone
% cd ~/project % git clone https://github.com/infews/keydown.git
PATH & RUBYLIB
% echo 'RUBYLIB=$HOME/project/keydown/lib:$RUBYLIB export RUBYLIB PATH=$HOME/project/keydown/bin:$PATH export PATH ' >> ~/.zshrc_custom % source ~/.zshrc_custom
gem install
% gem install albino [~/project] Fetching: posix-spawn-0.3.5.gem (100%) Building native extensions. This could take a while... Fetching: albino-1.3.2.gem (100%) Successfully installed posix-spawn-0.3.5 Successfully installed albino-1.3.2 2 gems installed Installing ri documentation for posix-spawn-0.3.5... Installing ri documentation for albino-1.3.2... Installing RDoc documentation for posix-spawn-0.3.5... Installing RDoc documentation for albino-1.3.2... % gem install rdiscount [~/project] Fetching: rdiscount-1.6.8.gem (100%) Building native extensions. This could take a while... Successfully installed rdiscount-1.6.8 1 gem installed Installing ri documentation for rdiscount-1.6.8... Installing RDoc documentation for rdiscount-1.6.8...
making slides
create presentation.
% cd ~/project % keydown generate my_presentation [~/project] create my_presentation create my_presentation/my_presentation.md create my_presentation/css/my_presentation.css create my_presentation/css/rocks.css create my_presentation/images create my_presentation/images/cc.large.png create my_presentation/images/flickr.png create my_presentation/js/my_presentation.js create my_presentation/js/rocks.js
change directory.
% cd my_presentation
edit my_presentation.md to avoid problem I don't know well.
perl -pi -e 's/``` ruby//g' my_presentation.md perl -pi -e 's/def method/ def method/g' my_presentation.md perl -pi -e 's/puts "Hello, World"/ puts "Hello, World"/g' my_presentation.md perl -pi -e 's/end/ end/g' my_presentation.md perl -pi -e 's/```//g' my_presentation.md
make slides.
% keydown slides my_presentation.md [~/project/my_presentation] Creating Keydown presentation from my_presentation.md create css/keydown.css create my_presentation.html
open my_presentation.html with Firefox.
% open -a 'Firefox' my_presentation.html
リモート画像を取得してUIImageViewを作成する
概要
MWPhotoBrowserというとてもシャープに動作するPhotoBrowserがあるのですが、こいつの仕組みを調査してみるために簡単な箇所だけ抜き出して実装してみたというお話です。
実装内容
- ImageViewを内包したMyPhotoクラスを準備
- [MyPhoto image]を実行するとisAvailableの場合は画像を返す。
- そうでない場合はリモート画像を非同期通信にて取得開始。
- ひとまずスピナーを表示する
- BackGroundで画像取得処理が終了する。
- MyPhotoはdelegateオブジェクトに対してphotoDidFinishLoadingかphotoDidFailToLoadへの通知を行う。
- 画像が取得できている場合は画像を再描画
コード
model
// MyPhoto.h #import <Foundation/Foundation.h> @class MyPhoto; // Delegate @protocol MyPhotoDelegate <NSObject> - (void)photoDidFinishLoading:(MyPhoto *)photo; - (void)photoDidFailToLoad:(MyPhoto *)photo; @end @interface MyPhoto : NSObject { NSURL *_photoURL; UIImage *_photoImage; BOOL _workingInBackground; } + (MyPhoto *)photoWithURL:(NSURL *)url; - (id)initWithURL:(NSURL *)url; // Public methods - (BOOL)isImageAvailable; - (UIImage *)image; - (UIImage *)obtainImage; - (void)obtainImageInBackgroundAndNotify:(id <MyPhotoDelegate>)notifyDelegate; - (void)releasePhoto; @end
#import "Global.h" #import "MyPhoto.h" // Private @interface MyPhoto () // Properties @property (retain) UIImage *photoImage; @property () BOOL workingInBackground; // Private Methods - (void)doBackgroundWork:(id <MyPhotoDelegate>)delegate; @end @implementation MyPhoto @synthesize photoImage = _photoImage; @synthesize workingInBackground = _workingInBackground; #pragma mark Class Methods + (MyPhoto *)photoWithURL:(NSURL *)url { LOG_METHOD; return [[[MyPhoto alloc] initWithURL:url] autorelease]; } #pragma mark NSObject - (id)initWithURL:(NSURL *)url { LOG_METHOD; if ((self = [super init])) { _photoURL = [url copy]; } return self; } - (void)dealloc { LOG_METHOD; [_photoURL release]; [_photoImage release]; [super dealloc]; } #pragma mark Photo - (BOOL)isImageAvailable { LOG_METHOD; return (self.photoImage != nil); } - (UIImage *)image { LOG_METHOD; return self.photoImage; } - (UIImage *)obtainImage { LOG_METHOD; // 画像が表示できる状態でない場合は、表示できるようになんとかする if (!self.photoImage) { // Load UIImage *img = nil; if (_photoURL) { LOG(@"%@", _photoURL); // Read image from URL and return NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:_photoURL]; [request setValue:@"http://refer.example.com" forHTTPHeaderField:@"Referer"]; NSError *error = nil; NSURLResponse *response = nil; NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; //[request release]; if (data) { img = [[UIImage alloc] initWithData:data]; } else { LOG(@"Photo from URL error: %@", error); } } // Force the loading and caching of raw image data for speed // [img decompress]; // Store self.photoImage = img; [img release]; } return [[self.photoImage retain] autorelease]; } // Release if we can get it again from path or url - (void)releasePhoto { LOG_METHOD; if (self.photoImage && ( _photoURL)) { self.photoImage = nil; } } // Obtain image in background and notify the browser when it has loaded - (void)obtainImageInBackgroundAndNotify:(id <MyPhotoDelegate>)delegate { LOG_METHOD; if (self.workingInBackground == YES) return; // Already fetching self.workingInBackground = YES; [self performSelectorInBackground:@selector(doBackgroundWork:) withObject:delegate]; } // Run on background thread // Download image and notify delegate - (void)doBackgroundWork:(id <MyPhotoDelegate>)delegate { LOG_METHOD; NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Load image UIImage *img = [self obtainImage]; // Notify delegate of success or fail if (img) { [(NSObject *)delegate performSelectorOnMainThread:@selector(photoDidFinishLoading:) withObject:self waitUntilDone:NO]; } else { [(NSObject *)delegate performSelectorOnMainThread:@selector(photoDidFailToLoad:) withObject:self waitUntilDone:NO]; } // Finish self.workingInBackground = NO; [pool release]; } @end
ViewController
// ImageViewerViewController.h #import <UIKit/UIKit.h> #import "MyPhoto.h" @interface ImageViewerViewController : UIViewController <MyPhotoDelegate> { MyPhoto *_photo; UIImageView *_photoImageView; UIActivityIndicatorView *_spinner; } -(void)displayImage; @end
// ImageViewerViewController.m #import "ImageViewerViewController.h" #import "MyPhoto.h" @implementation ImageViewerViewController // Implement loadView to create a view hierarchy programmatically, without using a nib. - (void)loadView { LOG_METHOD; [super loadView]; // Setup photo frame _photo = [MyPhoto photoWithURL:[NSURL URLWithString:@"http://farm6.static.flickr.com/5296/5425314834_535d56f8aa_b.jpg"]]; [_photo retain]; _photoImageView = [[UIImageView alloc] initWithFrame:self.view.bounds]; _photoImageView.backgroundColor = [UIColor whiteColor]; [self.view addSubview:_photoImageView]; [_photoImageView retain]; // Spinner _spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; _spinner.hidesWhenStopped = YES; _spinner.center = CGPointMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0); _spinner.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin; [self.view addSubview:_spinner]; } - (void)viewDidLoad { LOG_METHOD; [super viewDidLoad]; [self displayImage]; } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { LOG_METHOD; return (interfaceOrientation == UIInterfaceOrientationPortrait); } - (void)didReceiveMemoryWarning { LOG_METHOD; // Releases the view if it doesn't have a superview. [super didReceiveMemoryWarning]; // Release any cached data, images, etc. that aren't in use. } - (void)viewDidUnload { LOG_METHOD; [super viewDidUnload]; // Release any retained subviews of the main view. // e.g. self.myOutlet = nil; } - (void)dealloc { LOG_METHOD; [_photo release]; [_photoImageView release]; [_spinner release]; [super dealloc]; } #pragma mark - #pragma mark Photos - (void)displayImage { LOG_METHOD; // Get image or obtain in background if ([_photo isImageAvailable]) { [_spinner stopAnimating]; // Setup photo frame CGRect photoImageViewFrame; photoImageViewFrame.origin = CGPointZero; photoImageViewFrame.size = [_photo image].size; _photoImageView.frame = photoImageViewFrame; _photoImageView.image = [_photo image]; } else { // MyPhotoに画像取得処理を委譲。取得完了通知は // photoDidFinishLoadingあるいはphotoDidFailToLoadへ行う。 [_spinner startAnimating]; [_photo obtainImageInBackgroundAndNotify:self]; } } #pragma mark - #pragma mark PhotoDelegate - (void)photoDidFinishLoading:(MyPhoto *)photo { LOG_METHOD; [_spinner stopAnimating]; [self displayImage]; } - (void)photoDidFailToLoad:(MyPhoto *)photo { LOG_METHOD; [_spinner stopAnimating]; // 本来はここに失敗した場合の処理を記述する } @end
感想
とりあえず途中で大変な作業量になりそうなのでこの辺で切り上げました。
MWPhotoBrowserは内部で相互参照していたりして読むのが大変でした。
でもよくこんなにややこしい仕組みを実装して公開して下さったものです。感謝。
でももうちょっと再利用しやすいといいな…
Objective-Cのプライベートメソッド
@privateが使えるのはメンバ変数(インスタンス変数)のみ
プライベートなインスタンス変数は次のように宣言する事ができます。
//MyClass.h @interface MyClass : NSObject { @private id myInstanceVariable_; } // public methods - (id)myInstanceVariable; - (void)setMyInstanceVariable:(id)theVar; @end
ですが、privateなメソッドを記述する場合、.hファイルではなく.mファイルへ以下のように記述をします。
//MyClass.m @interface MyClass() -(void) privateMethod; @end @implemention MyClass // ここからヘッダーファイルに記述されている処理を実装する // privateMethodの実装もここにできる @end
つまり、プライベートメソッドに実装ファイル側の冒頭にinterface宣言と実装部をまとめて記述します。
Objective-C的にはこれはカテゴリーと呼ばれる機能です。
カテゴリーの補足説明
Perl寄りのHackerに向けて解説しておくと、
CPANモジュールにメソッド追加したい場合はそのモジュールを継承してからメソッドを追加する方法が一般的だと思います。
そんなときObjective-Cの場合は「親クラス+追加するメソッド名」をファイル名として既存クラスにメソッドを追加します。
以下、Twitter-OAuth-iPhoneで記述されている、NSStirngにURLEncodingの処理を追加したいのでカテゴリ機能を使って
機能を追加している例です。
// NSString+URLEncoding.h #import <Foundation/Foundation.h> @interface NSString (OAURLEncodingAdditions) - (NSString *)URLEncodedString; - (NSString *)URLDecodedString; @end
@implementation NSString (OAURLEncodingAdditions) - (NSString *)URLEncodedString { NSString *result = (NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)self, NULL, CFSTR("!*'();:@&=+$,/?%#[]"), kCFStringEncodingUTF8); [result autorelease]; return result; } - (NSString*)URLDecodedString { NSString *result = (NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault, (CFStringRef)self, CFSTR(""), kCFStringEncodingUTF8); [result autorelease]; return result; } @end
まとめ
Objective-Cではインスタンス変数をプライベート宣言する事はできるけど、メソッドをプライベートにする場合はカテゴリーをつかってね
ViewがTapされた時を検知するときはdelegate使うんだぜ!!
tapされた事を検知する処理はよく使う処理なので定型化したい。
次のようにDelegateして使うViewを用意しておく
#import <Foundation/Foundation.h> @protocol MyViewTapDelegate; @interface MyViewTap : UIView { id <MyViewTapDelegate> tapDelegate; } @property (nonatomic, assign) id <MyViewTapDelegate> tapDelegate; - (void)handleSingleTap:(UITouch *)touch; - (void)handleDoubleTap:(UITouch *)touch; - (void)handleTripleTap:(UITouch *)touch; @end @protocol MyViewTapDelegate <NSObject> @optional - (void)view:(UIView *)view singleTapDetected:(UITouch *)touch; - (void)view:(UIView *)view doubleTapDetected:(UITouch *)touch; - (void)view:(UIView *)view tripleTapDetected:(UITouch *)touch; @end
#import "MyViewTap.h" @implementation MyViewTap @synthesize tapDelegate; - (id)init { if ((self = [super init])) { self.userInteractionEnabled = YES; } return self; } - (id)initWithFrame:(CGRect)frame { if ((self = [super initWithFrame:frame])) { self.userInteractionEnabled = YES; } return self; } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; NSUInteger tapCount = touch.tapCount; switch (tapCount) { case 1: [self handleSingleTap:touch]; break; case 2: [self handleDoubleTap:touch]; break; case 3: [self handleTripleTap:touch]; break; default: break; } [[self nextResponder] touchesEnded:touches withEvent:event]; } - (void)handleSingleTap:(UITouch *)touch { if ([tapDelegate respondsToSelector:@selector(view:singleTapDetected:)]) [tapDelegate view:self singleTapDetected:touch]; } - (void)handleDoubleTap:(UITouch *)touch { if ([tapDelegate respondsToSelector:@selector(view:doubleTapDetected:)]) [tapDelegate view:self doubleTapDetected:touch]; } - (void)handleTripleTap:(UITouch *)touch { if ([tapDelegate respondsToSelector:@selector(view:tripleTapDetected:)]) [tapDelegate view:self tripleTapDetected:touch]; } @end
上記のViewを別のViewにかぶせて使います。
- (id)initWithFrame:(CGRect)frame { // Tap view for background _tapView = [[MyViewTap alloc] initWithFrame:frame]; _tapView.tapDelegate = self; _tapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; _tapView.backgroundColor = [UIColor blackColor]; [self addSubview:_tapView]; ... } - (void)handleSingleTap:(CGPoint)touchPoint { // singleTapの場合はこのメソッドが呼ばれる } - (void)handleDoubleTap:(CGPoint)touchPoint { // DoubleTapの場合 } - (void)handleTripleTap:(CGPoint)touchPoint { // TripleTapの場合 }
ユーザーから見るとベースとなっているViewの前に透明なViewが存在していて、
TapがSingleかDoubleかTripleかという事だけを判定して残りの処理はベースとなっているViewに委譲しています。
擬人化するとたぶんこんな感じじゃないかな?
BaseView「おう、ユーザーが選択した処理がAなのかBなのかCなのかを俺に言ってくれ。ただし、それに対する結果は俺が決める。どんな処理するかの決定権は俺に委譲してくれだぜ!!」
TapView「あいー」
間違ってたらだれかがきっと通りすがってコメントしてくれるさ。
iPhoneにアカウント情報を保存してみました。
やりたいこと
- UserIDを保存したい
- Passwordを保存したい
- UserIDは見られても構わない
- Passwordは誰にも見られたくない
UserIDはPinfoに保存する
こちらを参考にしました。
http://d.hatena.ne.jp/tomute/20091121/1258884514
要点としてはユーザー名をNSUserDefaultsに保存して、パスワードはセキュリテリを高めるためにKeyChainを使います。
- (void)saveUserInfo { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSString *oldUsername = [defaults objectForKey:@"USERNAME"]; NSError *error; // ユーザ名が変更になっていた場合は、古いユーザ名で保存したパスワードを削除 if (![oldUsername isEqualToString:_usernameField.text]) { [SFHFKeychainUtils deleteItemForUsername:oldUsername andServiceName:@"Test App" error:&error]; } // ユーザ名はNSUserDefaultsを使って保存 [defaults setObject:_usernameField.text forKey:@"USERNAME"]; // ラッパークラスを利用してパスワードをKeyChainに保存 [SFHFKeychainUtils storeUsername:_usernameField.text andPassword:_passwordField.text forServiceName:@"Test App" updateExisting:YES error:&error]; [self.navigationItem.rightBarButtonItem setEnabled:NO]; // saveが完了したらその旨をユーザーに通知 UIAlertView * alert = [[[UIAlertView alloc] initWithTitle:@"saved" message:@"complete" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil] autorelease]; [alert show]; }