UIScrollViewを表示
とりあえず何も考えずにUIScrollViewを表示するメモ。
Viewベースのプロジェクトを作り、コントローラの vieDidLoadに書くだけ。
- (void)viewDidLoad { UIScrollView *sc1 = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 0, 768, 1024)] ; sc1.contentSize=CGSizeMake(768*3, 1024); UITextView *t = [[UITextView alloc]initWithFrame:CGRectMake(0, 0, 768*3, 1024)]; t.text = @"日本国民は、正当に選挙された国会における代表者を通じて行動し、われらとわれらの子孫のために、諸国民との協和による成果と、わが国全土にわたつて自由のもたらす恵沢を確保し、政府の行為によつて再び戦争の惨禍が起ることのないやうにすることを決意し、ここに主権が国民に存することを宣言し、この憲法を確定する。そもそも国政は、国民の厳粛な信託によるものてあつて、その権威は国民に由来し、その権力は国民の代表者がこれを行使し、その福利は国民がこれを享受する。これは人類普遍の原理であり、この憲法は、かかる原理に基くものである。われらは、これに反する一切の憲法、法令及び詔勅を排除する。"; [sc1 addSubview:t]; [self.view addSubview:sc1]; [super viewDidLoad]; }
UIScrollViewはマウスイベントが拾えなかったり等色々と癖があるので、細かい事はまた今度。
UIKITのクラスダンプ
class-dumpを使いヘッダファイルには無いプライベートクラスやメソッドを表示します。Objective-Cプログラマには常識とのことで、とりあえずclass-dumpを下記サイトから入手します。
http://www.codethecode.com/projects/class-dump/
というわけで、とりあえずUIKITの中身をダンプ
class-dump /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.2.sdk/System/Library/Frameworks/UIKit.framework/UIKit > ~/UIKIT.dump.txt
すると、ヘッダには記述のないメソッドがズラズラと・・・
手軽に試すにはmethod_exchangeImplementationsとかでメソッドスワップすると良いらしい。
XMLのロード
ローカルにあるXMLファイルの読み込みをします。
ネットワーク越しにXMLを読み込むサンプルは結構あるんですが、ローカルにあるXMLファイルを読み込むサンプルがあまり見つからなかったので載せておきます。
XMLファイルはプロジェクト内のどこかに「既存のファイルを追加」で追加しておいてください。
とりあえず読み込みのコード
- (void) loadWithFile:(NSString *)fname{ NSLog(@"読み込みを開始します\n"); NSURL *urlPath = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:fname ofType:@"xml"]]; NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:urlPath]; [parser setDelegate:self]; [parser parse]; [parser release]; } - (void)parserDidStartDocument:(NSXMLParser *)parser { NSLog(@"解析を開始しました\n"); } - (void)parserDidEndDocument:(NSXMLParser *)parser { NSLog(@"解析が完了しました\n"); } - (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError { NSLog(@"読み込み時にエラーが発生しました\n"); } - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{ NSLog(@"\t\t要素[%@]が見つかりました\n", elementName); } - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{ NSLog(@"文字列[%@]が見つかりました\n", string); }
読み込みは実装したクラスのインスタンスを作成してloadWithFileをファイル名と一緒に呼びます。ここではTypeがXMLで指定してあるので、拡張子を抜いたファイル名のみを指定してください。
[obj loadWithFile:@"filename"];
下記データを読み出す場合
<?xml version="1.0" encoding="UTF-8"?> <data> <url addr="http://google.co.jp/" priority="2.5">Google</url> <url addt="http://yahoo.co.jp/" priority="1.5">Yahoo</url> </data>
データはattributeDictに入っているので、キーを指定して下記のように取り出します。
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{ if([elementName isEqualToString:@"url"]){ NSString *urlStr = [attributeDict objectForKey:@"addr"] int priority = [[attributeDict objectForKey:@"priority"] floatValue] } }
間に挟まれた「Google」や「Yahoo」等の文字列は foundCharactersが呼ばれます。
didStartElementとdidEndElementの間に呼ばれるので、うまいこと拾ってください。
デバイスの回転
デバイスの回転の検出方法。
viewDidFinishedやinitWithFrameとかで下記を指定。
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRotate:) name:UIDeviceOrientationDidChangeNotification object:nil];
ここでは@selecterでdidRotateが指定してあるので、その名前のファンクションを作成。
- (void) didRotate:(NSNotification *)notification { UIDeviceOrientation orientation = [[notification object] orientation]; if (orientation == UIDeviceOrientationLandscapeLeft){ NSLog(@"Device rotate Leftl!"); } else if (orientation == UIDeviceOrientationLandscapeRight) { NSLog(@"Device rotate Rightl!"); } else if (orientation == UIDeviceOrientationPortraitUpsideDown){ NSLog(@"Device rotate UpsideDownl!"); } else if (orientation == UIDeviceOrientationPortrait) { NSLog(@"Device rotate Portraitl!"); } }
これで回転した場合にdidRotateが呼ばれます。
orientationに現在のデバイスの状況が入るのでそれぞれの状況での処理を書きます。
画面サイズの取得
iPhoneやiPadの開発をやっているとそれぞれの現在の画面サイズが欲しい時があります。
CGRect r = [[UIScreen mainScreen] bounds] CGFloat w = r.size.width; CGFloat h = r.size.height;
とりあえずこれで幅と高さが取れます。
ちなみにデフォルトでは上部にステータスバーが出ていますのでステータスバーの高さが引かれた数値が帰ってきます。ステータスバーを消したい場合はdidFinishLaunchingWithOptions等、最初に呼び出されるファンクションのどこかに下記をいれます。
[UIApplication sharedApplication].statusBarHidden = YES;
AS3のメモリ管理
ActionScript3にはGCがあるため、基本的にNewしたオブジェクトに対してDeleteは必要ありません。ガベージコレクタはどこからも参照がなくなったオブジェクトを勝手にメモリから開放してくれます。
ここで勘違いされがちな問題として、removeChildしたオブジェクトは画面から消えるために参照から外れたもの思っている人が多いようです。
addChildしたオブジェクトをremoveChildしただけではメモリから開放されないため、使い終わったオブジェクトにはnullを入れ参照を切らなければいけません。
また、子オブジェクト内で使用したオブジェクトも開放をしないと、親クラスからnullを入れても開放されません。また、リスナーに関しても子オブジェクト内でaddEventListnerをしたまま親からnullを入れても開放されることはありません。必ずremoveEventListenerをして開放をしましょう。マウス関係のリスナー等は開放忘れが頻発するので注意が必要です。
下記テストプログラムでは画面をクリックすることでオブジェクトの確保と開放を繰り返します。
memTest.as
package { import flash.display.Sprite; import flash.events.MouseEvent; public class memTest extends Sprite { private var o:CObj = null; public function memTest() { stage.addEventListener(MouseEvent.CLICK,onClickHandler); } private function onClickHandler(evt:MouseEvent):void{ if(o==null){ o=new CObj(); o.x=100; o.y=100; stage.addChild(o); }else{ stage.removeChild(o); o=null; } } } }
CObj.as
package { import flash.display.Sprite; import flash.events.Event; public class CObj extends Sprite { public function CObj() { super(); this.addEventListener(Event.ENTER_FRAME,onEnterframeHandler); this.addEventListener(Event.REMOVED,onRemovedHandler); this.graphics.beginFill(0); this.graphics.drawRect(0,0,100,10); this.graphics.endFill(); } private function onRemovedHandler(evt:Event):void{ if(evt.target!=this)return; this.removeEventListener(Event.REMOVED,onRemovedHandler); this.removeEventListener(Event.ENTER_FRAME,onEnterframeHandler); } private function onEnterframeHandler(evt:Event):void{ this.rotation++; } } }
CObj.as側でのポイントはEvent.REMOVEDで呼び出されるonRemoveHandlerです。
ご存知のとおり、AS3にはデストラクタが存在しないため、代わりにdestroy等のファンクションを作って呼び出す方法が取られますが、開放の後にdestroy関数を呼び忘れる可能性があります。
そこで、今回の方法を使用します。Event.REMOVEDはremoveChildがターゲットに対して実行された際に呼び出されます。そのためこれを監視することで、事実上デストラクタの代わりに使用することが可能となります。
注意点として、onRemoveHandlerは自分の保持する子オブジェクトをremoveChildしても呼び出されてしまうため、if(evt.target!=this)return;で、開放されたターゲットが自分自身でない場合は実行しないようにする必要があります。
この手法で全てのクラスがremoveHandlerをもつことで、必要なくなったオブジェクトはremoveChildしてあげれば連鎖的に末端のオブジェクトまでメモリが開放されることとなります。