WEBアプリケーション研究室 開発ノート TOP

WEBアプリケーション研究室 開発ノート iPhone開発

スポンサーサイト

-------- --:--

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

    このエントリーをはてなブックマークに追加

iPhoneのFacebook風メニューを実装してみました。
Cocoa Controlsにも幾つかありましたがあわせて使いたかったライブラリとの相性が悪かったので自分で作ってみました。

あわせてこちらのブログも参考にさせていただきました。

ソースコードはGitHubで見ることができます。大して難しいことはしていませんが参考にどうぞ。

うまくまとめられたらそのうちライブラリを公開するかもしれません。

    このエントリーをはてなブックマークに追加

スマートフォン向けのjavascriptを書いていてちょっとはまったので書いておきます。
クリックしたらただalertを出すだけの簡単なスクリプトです。
マークアップが


UPPER

LOWER

こんな感じでjavascriptが

document.getElementById("upper_button").addEventListener("touchend", function(e){
alert("upper");
}, false);

document.getElementById("lower_button").addEventListener("touchend", function(e){
alert("lower");
}, false);
こんな感じです。

androidは大丈夫でしたがiPhone(4.3.4)ではボタンを交互に押したとき、前のイベントが実行されてしまいます。

問題はalertを出していることです。touchendとalertが干渉しているみたいです。
こういう時のおまじない

document.getElementById("upper_button").addEventListener("touchend", function(e){
setTimeout(function(){ alert("upper"); }, 0);

}, false);

document.getElementById("lower_button").addEventListener("touchend", function(e){
setTimeout(function(){ alert("lower"); }, 0);
}, false);
setTimeoutを使うと、touchendの処理を待ってから、alertを呼んでくれます。timeoutは0でOKです。

    このエントリーをはてなブックマークに追加

TabBarアプリケーションですが何回か作っているうちにわかってきました。本当はXCodeにテンプレートがあって簡単に作れるのですが、詳細を理解するため一から作ってます。

まずApplicationのデリゲートにUITabBarControllerのポインタを持たせます。これは起動時にwindowにUITabBarControllerを突っ込むために使います。IB(InterfaceBuilder)でUITabBarControllerをこの変数に接続し、Appのデリゲートで


- (void)applicationDidFinishLaunching:(UIApplication *)application
{
[window addSubview:tabBarController.view];
[window makeKeyAndVisible];
}
こんな感じです。

で、IBでUITabBarControllerに必要なViewControllerを足していけば、とりあえず切り替えだけは動きます。まず新しいViewControllerを作るときの手順ですが。

1 ClassesにUIViewControllerの子クラスを作ります。
2 Resourcesにnibを作ります。
3 作ったnibをIBで開いてFiles Ownerのクラスを作成したクラスに変えます。
4 Files Ownerのviewプロパティとviewを接続します。

これでViewの準備は完了。これをUITabBarControllerに入れていきます。

1 MainWindow.xibをIBで開いてUITabBarControllerを選択します。
2 Attributesを開いてViewControllerを+します
3 UITabBarControllerの中にVIewControllerができますので選択し、AttributesでNIB Nameを先ほどのnibに変更します。
4 Identityタブに変えclassを先ほど作ったViewControllerに変更

以上でOKです。最初ややこしいと思いましたが、全体像がわかるとなんてことないです。一から手作業は多少面倒ですけど。

    このエントリーをはてなブックマークに追加

UITextFieldはHTMLフォームのinput textのような入力欄です。View上に配置するだけで、クリック時にキーボードがでる優れものですが、キーボードを消す挙動は実装しなければなりません。まず、returnキーを押したら消えるようにしましょう。これにはdelegateを使います。delegate先に指定したクラスに下のメソッドを実装します。


- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
FirstResponderとは名前の通りですが、ユーザーのアクションに対して最初に応答するオブジェクトです。入力中のフィールドはFirstResponderなのでこれを放棄することによりキーボードを消すことができます。

あと、フィールドの外側をタップするとキーボードが消えるようにしようと思います。これにはフィールドの外側に透明のでかいボタンをおいて、そのアクションでFirstResponderを放棄するように書きます。ボタンはIBでTypeをCustomにすると見えなくなります。このボタンは重なり順が一番奥にないと、ほかのアクションを邪魔してしまうので注意しましょう。

- (IBAction)backgroundClick:(id)sender
{
[field1 resignFirstResponder];
[field2 resignFirstResponder];
[field3 resignFirstResponder];
[field4 resignFirstResponder];
}
例えば複数フィールドがあっても、resignFirstResponderはFirstResponderではないものに送っても問題がないので、確かめずに送れます。

    このエントリーをはてなブックマークに追加

例えば同時にいくつかHTTPリクエストを投げて一気に画像をとってくる等、非同期通信を使うといい時がありますが、簡単にできるようクラスが用意されてます。同時リクエスト数を簡単に制御できたりもします。Cocoaってすごいフレームワークだなあ(みつを風に)
その名もNSOperationです。


#import "MyOperation.h"
@implementation MyOperation
@synthesize number;
- (void)start
{
NSLog(@"(%@) start");
[super start];
}
- (void)main
{
[NSThread sleepForTimeInterval:0.4];
NSLog(@"(%@) end main");
}
- (void)cancel
{
NSLog(@"(%@) cancel");
[super cancel];
}
-(void) dealloc
{
[name release];
[super dealloc];
}
@end
クライアントではNSOperationQueueにNSOperationをaddOperationしてつかいます。KVCのaddObserverを使ってisFinishedを監視すると終了時に処理をすることが簡単にできます。

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

MyOperation *mo = [[MyOperation alloc]init];
[mo addObserver:self
forKeyPath:@"isFinished"
options:NSKeyValueObservingOptionNew
context:queue//前リクエストの終了を待ってqueueをreleaseするために渡してみました。
];
[queue addOperation:mo];

//同じクラスのメソッド
- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object
change:(NSDictionary*)change context:(void*)context
{
NSLog(@"finish");
// キー値監視を解除する
MyOperation *mo = (MyOperation *) object;
[(NSOperationQueue *)context release];
[mo removeObserver:self forKeyPath:keyPath];
[mo release];
}
HTTPリクエストを使う場合はもうちょっと複雑です。NSOperationはデフォルトではmain関数の終了時にisFinishedがYESになります。ところが、HTTPリクエスト自体が非同期通信でdelegateを使うため、通信終了のdelegateで独自にisFinishedを更新してやらないとだめです。

- (id)initWithRequest: (NSURLRequest*)request
{
self = [super init];

if (self)
{
urlRequest = [request retain];
isFinished = NO;
isExecuting = NO;
isYes = [[NSNumber alloc] initWithBool:YES];
isNo = [[NSNumber alloc] initWithBool:NO];
}
return self;
}

- (BOOL)isConcurrent
{
return YES;
}

- (BOOL)isExecuting
{
return isExecuting;
}

- (BOOL)isFinished
{
return isFinished;
}

#pragma mark -- Operating --
//-------------------------------------------------------------------------------------//

- (void)start
{
// ダウンロードを開始する
if (![self isCancelled])
{
// コネクションを作成する
urlConnection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
[self setValue:isYes forKey:@"isExecuting"];
}
//[super start];これを呼び出してしまうと、main関数まで行って通信終了前にisFinishedがYESになってしまいます。
}

- (void)cancel
{
[urlConnection cancel];
// 親クラスのメソッドを呼び出す
[super cancel];
}
#pragma mark -- NSURLConnection delegate --

- (void)connection:(NSURLConnection*)connection
didReceiveData:(NSData*)data
{
// データを追加する
}

- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
// データを追加する
// 終了処理
[self setValue:isNo forKey:@"isExecuting"];
[self setValue:isYes forKey:@"isFinished"];
}

- (void)connection:(NSURLConnection*)connection
didFailWithError:(NSError*)error
{
//エラー時の処理
}
(抜粋してるのでrelease処理なんか書いてません)もう一点重要なのが

- (BOOL)isConcurrent
{
return YES;
}
です。これは並列処理をするかしないかのBOOL値でデフォルトはNOを返すのですが、非同期通信のNSURLConnectionを使う時は、YESを返すようにオーバーライドしないと、autorelease関係のエラーが出ます。

autoreleased with no pool in place - just leaking
こんなのがいっぱい出ます。これで、土曜日の半日がなくなりました・・・

    このエントリーをはてなブックマークに追加
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。