2010年12月26日

UINavigationController使用時にフルスクリーン表示する方法

環境iOS(iPhone) SDK 4.0

UINavigationControllerを使うと、Viewのサイズが勝手にリサイズされてしまいます。
普段は便利なのかもしれませんが、 ナビゲーションバー(UINavigationBar)やツールバー(UIToolbar)を透明にしたとき、 その裏に描画できなくて困ります。
これを避けて、以下のように画面全体に描画する方法を調べましたので書いておきます。
フルスクリーン表示

UIViewControllerにwantsFullScreenLayoutというプロパティがあるので、 それをYESに設定すれば完了です。

- (void)viewDidLoad {
  [super viewDidLoad];
  self.wantsFullScreenLayout = YES;
}

UIViewControllerがnibに登録されている場合は、以下のようにInterface Builderから設定することもできます。
フルスクリーン表示設定

以上です。 知っていれば簡単ですが、知らないとハマるところではないかと思います。

(参考リンク)
A-Liaison BLOG: iPhone付属のPhotos(写真)アプリのような、回転可能な全画面表示ビューを作る方法

2010年12月20日

平成22年度 秋期 システムアーキテクト試験 成績照会

平成22年度 秋期 システムアーキテクト試験 成績照会

受験番号 SAXXX-XXXX の方は,合格です

午前Ⅰ得点***.**点(免除)
午前Ⅱ得点88.00点
午後Ⅰ得点72点
午後Ⅱ評価ランクA

2回目の受験にして合格。
プロジェクトマネージャ試験に次いで2つ目の論文試験合格になります。
残りの論文試験は、ITストラテジスト試験、ITサービスマネージャ試験、システム監査技術者試験の3つ。
私の専門領域と被っていたこれまでの試験と違い、これらの試験はほぼ専門外。さてどうしたものか・・・。

2010年12月15日

NSString+Geometry

環境iOS(iPhone) SDK 4.0

UIViewのframeの状態をコンソールに出力するのが結構面倒でしたので、 以下のようなクラスを作って対応することにしました。

以下がヘッダファイルです。

//NSString+Geometry.h
#import <Foundation/Foundation.h>

@interface NSString (Geometry)

+ stringWithPoint:(CGPoint)point;
+ stringWithSize:(CGSize)size;
+ stringWithRect:(CGRect)rect;
+ stringWithAffineTransform:(CGAffineTransform)affineTransform;

@end

以下がソースファイルです。

//  NSString+Geometry.m
#import "NSString+Geometry.h"

@implementation NSString (Geometry)

+ stringWithPoint:(CGPoint)point {
  return [NSString stringWithFormat:@"%f,%f", point.x, point.y];
}

+ stringWithSize:(CGSize)size {
  return [NSString stringWithFormat:@"%fx%f", size.width, size.height];
}

+ stringWithRect:(CGRect)rect {
  return [NSString stringWithFormat:@"%g,%g:%gx%g",
          rect.origin.x, rect.origin.y,
          rect.size.width, rect.size.height];
}

+ stringWithAffineTransform:(CGAffineTransform)affineTransform {
  return [NSString stringWithFormat:@"[%g %g 0][%g %g 0][%g %g 1]",
          affineTransform.a, affineTransform.b,
          affineTransform.c, affineTransform.d,
          affineTransform.tx, affineTransform.ty];
}

@end

これでUIViewのframeは以下のようなコードで出力できます。

UIView *view = ...
NSLog(@"view = %@", [NSString stringWithRect:view.frame]);

もっと簡単な方法はないのですかね・・・。

(2010/12/31追記)
SDKの以下の関数を使うことにより同じことが実現できることが分かりました。

  • NSStringFromCGPoint
  • NSStringFromCGSize
  • NSStringFromCGRect
  • NSStringFromCGAffineTransform

あるものを使った方が良いので上記のNSString+Geometryは使わないことにしました。

(2011/06/13追記)
関数名が一部間違えていたので修正しました。

2010年12月13日

GTMLoggerクラス

環境iOS(iPhone) SDK 4.0
ライブラリGoogle Toolbox for Mac 1.6.0

Google Toolbox for Macに入っていたロギング用のGTMLoggerクラスを使ってみました。

GTMLoggerはNSLogの強化版のようなものです。

@implementation XXXAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  GTMLoggerDebug(@"GTMLoggerDebug");
  GTMLoggerInfo(@"GTMLoggerInfo");
  GTMLoggerError(@"GTMLoggerError");
  GTMLoggerAssert(@"GTMLoggerAssert");
  // 以下略

とすると、Debug構成ではコンソールには以下のように出力されます。メソッド名などが付加されています。

[XXXAppDelegate application:didFinishLaunchingWithOptions:] GTMLoggerDebug
[XXXAppDelegate application:didFinishLaunchingWithOptions:] GTMLoggerInfo
[XXXAppDelegate application:didFinishLaunchingWithOptions:] GTMLoggerError
[XXXAppDelegate application:didFinishLaunchingWithOptions:] GTMLoggerAssert

なお、Debug構成かどうかはDEBUGマクロで判断しているようですので、以下のようにDebug構成ではプリプロセッサマクロにDEBUGを追加しておきましょう。
Debug設定

Release構成では以下のように出力されます。

[XXXAppDelegate application:didFinishLaunchingWithOptions:] GTMLoggerError
[XXXAppDelegate application:didFinishLaunchingWithOptions:] GTMLoggerAssert

DebugとInfoが出力されません。
Infoについては、以下のように環境変数を書き換えることにより出力することができるようになります。

setenv("GTMVerboseLogging", "1", 1);

ファイル出力などもサポートされており、デバッグに活用できると思います。
自前でも似たようなものを作ることはできると思いますが、 こういうものを使っておくと車輪の再発明を避けることができます。 品質も自分が作ったものより高いでしょうから。

2010年12月11日

Google Toolbox for Macの使い方

環境iOS(iPhone) SDK 4.0
ライブラリGoogle Toolbox for Mac 1.6.0

XMLやHTMLを扱うとき、"&amp;"といった実体参照文字列を、"&"に置き換えたい場合があったので、 適当なライブラリがないか調べてみました。
Google Toolbox for Macのgtm_stringByUnescapingFromHTMLを 使うのが良さそうだったので、これを導入してみることにしました。
導入手順を参考までに残しておきます。

ソースコードの取得

インストールパッケージは無いようですので、Subversionを使って直接ソースコードを取得します。
まずはリポジトリのtagsディレクトリを見て入手するバージョンを決めます。
私は現時点で最新の1.6.0にしました。
次はsvnを使ってダウンロードを行います。

svn export http://google-toolbox-for-mac.googlecode.com/svn/tags/google-toolbox-for-mac-1.6.0/

プロジェクトへの追加

気の利いたやりかたはないようですので、一般的な手順でやることになります。
最初にソースコードのコメントを読んで必要そうなソースコードを見繕っておきましょう。 GTMDefines.hなど内部的に#importしているソースコードもあるので注意して下さい。
あとはXcodeのメニューから、プロジェクト→プロジェクトに追加 でソースコードを選択すれば追加できます。

以上でお終いです。

2010年12月08日

Objective-C@iOSの例外処理

環境iOS(iPhone) SDK 4.0

最近Objective-CでiOSのアプリケーションを書いているのですが、ライブラリ的な役割をするコードで異常を検出した際、 呼び出し元への通知に例外処理を使ってよいのかどうかで良く悩みます。

Objective-Cは例外処理をサポートしていて、構文やサンプルコードはググれば簡単に見つかります。 しかしながら、具体的にどのような異常に例外処理を適用するべきかが良く分かりません。

私はこういうときは、標準的なライブラリの挙動を参考にするのですが、 iOS SDKのライブラリを使っていてもあまり例外に遭遇しないので、参考になりません。

例えばNSStringクラスの 文字列を整数に変換するメソッドであるintValueは、整数に変換できない文字列を受けても0を返すだけです。

Javaで似たようなことをするIntegerクラスのparseIntは、 整数に変換できない文字列を受けてNumberFormatExceptionを投げます。

そもそもObjective-Cの仕様として、nilのメソッドを呼び出しても何もエラーがおきません。 最近はこれを頼りにコードを書いてしまっているので、Javaに移ったらNullPointerExceptionの嵐に悩まされそうでちょっと怖いです。

という感じで、例外を受け取ることがあまりないので、どんなときに例外処理を使えば良いのか分からないでおります。 私の書くコードは、メソッドの戻り値でエラーを返すコードばかりです。 さてどうしたものやら。

2010年12月05日

HTTPモニタFiddler

HTTPを扱うアプリケーションを開発するとき、 HTTPの生データをモニタするために横取り丸とInetSpyというソフトを使っていました。

ところがこれらのソフトはHTTPSをサポートしていないようでしたので、 HTTPSをサポートしているFiddlerに切り替えました。

Fiddler
(HTTPSの通信内容を確認しているところ。もちろんコンテンツまでは取れませんが、通信先は分かります。)

このソフトはPROXYとして機能しますので、横取り丸とInetSpyの組み合わせと似たような感じで使えます。
今のところはトラブルはなく便利に使っております。

2010年12月04日

UIViewControllerのframe設定

環境iOS(iPhone) SDK 4.0

Window-based Applicationプロジェクトを作成して、initWithNibName:bundle:を使ってUIViewControllerを追加して表示しようとすると、 以下のようにInterface Builderの表示とiPhone Simulatorの表示がStatusBarの分ずれることがあります。

Interface Builder iPhone Simulator
Interface Builder iPhone Simulator

initWithNibName:bundle:を使った場合はViewの再配置をしてくれないためこうなります。
(「Resize View From NIBの意味」に分かりやすく書いてあります)

以下のように、UIViewControllerのview.frameを変更することにより、 Interface Builderの表示と合わせることができます。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  // ViewControllerはUIViewControllerのSubclass、contorllerは(ViewController *)型のインスタンス変数
  controller = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil];
  controller.view.frame = [UIScreen mainScreen].applicationFrame;
  [window addSubview:controller.view];
  [window makeKeyAndVisible];
  return YES;
}
Interface Builder iPhone Simulator
Interface Builder iPhone Simulator

なお、UINavigationControllerを使っているときは、UINavigationControllerが勝手にViewを再配置してくれるようですので、この問題は発生しません。

2010年12月03日

libxml2でHTML Parse

環境iOS(iPhone) SDK 4.0

iOSでHTML(not XML)を解析(Parse)するコードを書く機会がありましたので、トラブル情報や解決策などをまとめておきます。

ライブラリはNSXMLDocumentとNSXMLParserとlibxml2あたりを考えてみましたが、 NSXMLDocumentはiOSには無いみたいですし、NSXMLParserは壊れたHTMLを最後までパースできないようでしたので、 消去法でlibxml2を使うことにしました。

iPhoneアプリ開発、その(143) libxml2を使ってみる」を参考にプロジェクトにlibxml2を追加し、
Googleを活用しつつコードを書いていったところ、問題が起きました。
エラーを無視して解析を続けるオプションで解析しても、途中で解析が止まるのです。 以下のサンプルコードで問題を確認することができます。
libxml2のバージョンは2.7.3でした。

static void startElementHandler(void *ctx, const xmlChar *name, const xmlChar **atts) {
  NSLog(@"startElement name=%s", name);
}
static void endElementHandler(void *ctx, const xmlChar *name) {
  NSLog(@"endElement name=%s", name);
}

- (void)test {
  NSData *data = [@"<html><body><![if !(lt IE 7.0)]><![endif]></body></html>" dataUsingEncoding:NSUTF8StringEncoding];
  xmlParserCtxtPtr parserContext;
  xmlSAXHandler saxHandlerStruct = {0};
  parserContext = xmlCreatePushParserCtxt(&saxHandlerStruct, NULL, NULL, 0, NULL);
  saxHandlerStruct.startElement = &startElementHandler;
  saxHandlerStruct.endElement = &endElementHandler;
  int options = xmlCtxtUseOptions(parserContext, XML_PARSE_RECOVER | XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
  int result = xmlParseChunk(parserContext, (const char *)[data bytes], [data length], 1);
  // ここでresult == 1 (XML_ERR_INTERNAL_ERROR) になる
  // startElementHandler, endElementHandlerは呼び出されない。
  xmlFreeParserCtxt(parserContext);
}

以下のようにdataを設定すると、問題はおきません。

  NSData *data = [@"<html><body><!--[if !(lt IE 7.0)]--><!--[endif]--></body></html>" dataUsingEncoding:NSUTF8StringEncoding];

HTMLデータの方に問題があるということになるのでしょうが、XML_ERR_INTERNAL_ERRORが起きる理由は良く分かりません。 私のユースケースではHTMLデータを修正できないので、別の方法を探してみました。
結果、libxml2にあるHTML 4.0 non-verifying parserを使うことで解析できることを確認しました。
サンプルコードを以下に示します。

- (void)test {
  NSData *data = [@"<html><body><![if !(lt IE 7.0)]><![endif]></body></html>" dataUsingEncoding:NSUTF8StringEncoding];
  htmlParserCtxtPtr parserContext;
  htmlSAXHandler saxHandlerStruct = {0};
  saxHandlerStruct.startElement = &startElementHandler;
  saxHandlerStruct.endElement = &endElementHandler;
  parserContext = htmlCreatePushParserCtxt(&saxHandlerStruct, NULL, NULL, 0, NULL, XML_CHAR_ENCODING_UTF8);
  int options = htmlCtxtUseOptions(parserContext, HTML_PARSE_RECOVER | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING);
  int result = htmlParseChunk(parserContext, (const char *)[data bytes], [data length], 1);
  // ここでresult == 68 (XML_ERR_NAME_REQUIRED)になる。
  // startElementHandler, endElementHandlerは正常に(それぞれhtmlとbodyで2回ずつ)呼び出される。
  htmlFreeParserCtxt(parserContext);
}

htmlParseChunkが返すエラーをどのようにハンドリングすれば良いのか良く分かりませんが、 解析が止まるわけではないので、エラーを無視すれば特に問題は起きないようです。 この方向でしばらくコードを書いてみることにします。

しかしC言語業界のHTML Parserの定番って何になるんでしょうね。 XML Parserは実装も情報もたくさんあるのですが、HTML Parserは実装も情報も少なく、調べるのに苦労しました。