Hatena::Diary

24/7 twenty-four seven Twitter

Bloglinesで閲読登録 ADD TO Hatena::RSS Subscribe with livedoor Reader Add to Google

mail address

Follow me on twitter.

iPhone アプリケーション

Hatena touch LDR touch TV Listings LCD Clock MyWebClip MyWebClip LITE こころくろっく 英辞郎 on the WEB for iPhone(アルク) i-Radio

iPad アプリケーション

LCD Clock HD

共著

2010-08-15

ネットワークの通信速度を制限する Preference Pane "SpeedLimit"

mschrag@github

f:id:KishikawaKatsumi:20100815062601p:image


SpeedLimit は Macネットワーク通信速度に制限をかけることができる Preference Pane です。

上限のプリセット値として 1572k (T1), 768k (DSL), 384k (3G), 64k (Edge), and 48k (Dialup) の5種類が用意されています。

上限値を選択して "Slow Down" ボタンを押すと、通信速度が制限値まで遅くなります。

ネットワークを使用した iPhone アプリケーションのテストをシミュレータ上で実行するときに便利です。

対象となるホストを指定することができるので、iPhone アプリケーションの接続先のみ制限して、他の Mac の通信速度はそのまま、ということもできるようになっています。

2010-08-12

2つ以上の UIScrollView (UITableView, UITextView などを含む) が存在するビューで StatusBar のタップで先頭に戻る機能を有効にするには。

UIScrollView 系のビューが2つ以上存在するとき、ステータスバーをタップして一気に先頭にスクロールする機能を有効にするには、先頭にスクロールする機能を有効にしたいスクロールビューのみ scrollsToTop プロパティを YES にし、それ以外のスクロールビューは scrollsToTop を NO にします。


要するに scrollsToTop が YES のスクロールビューが1つだけの場合に、ステータスバーをタップする機能が働きます。


UIScrollView と UITextView を2つ使う場合の例です。

テキストビューの scrollsToTop プロパティを NO にしているので、ステータスバーをタップしたときはテーブルビューが先頭にスクロールします。

tableView = [[UITableView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 160.0f, 460.0f)];
tableView.delegate = self;
tableView.dataSource = self;
[self.view addSubview:scrollView];
[tableView release];
    
textView = [[UITextView alloc] initWithFrame:CGRectMake(160.0f, 0.0f, 160.0f, 460.0f)];
textView.scrollsToTop = NO;
[self.view addSubview:textView];
[textView release];

UIWebView は上記のクラスと違って UIScrollView を継承しているわけではないので、ちょっと面倒ですが下記のような感じになります。

もちろん、将来のバージョンで変わる可能性があります。

[[webView.subviews objectAtIndex:0] setScrollsToTop:YES];

参考リンク

2010-08-11

はてな touch 1.2.4 をリリースしました。

Hatena touch

はてな touch/Hatena touch はてな touch / Hatena touch

はてな touch 1.2.4 がアップルの審査を通過しました。

iPhone OS 3.x の環境で画像をアップロードしようとするとクラッシュする不具合を修正しました。

変更点

  • iPhone OS 3.x の環境で画像をアップロードできない問題を修正。

2010-08-06

はてな touch 1.2.3 をリリースしました。

Hatena touch

はてな touch/Hatena touch はてな touch / Hatena touch

はてな touch 1.2.3 がアップルの審査を通過しました。

iOS 4 に対応しました。

ただ、iPhone OS 3.x の環境で画像をアップロードしようとするとクラッシュしてしまう不具合が見つかりましたので、修正バージョンを 1.2.4 として申請しました。

1.2.4 が審査を通るまで、iPhone OS 3.x をお使いの方は、アップデートせずに旧バージョンをお使いください。

変更点

  • iOS 4 に対応。

2010-07-15

i-Radio 1.0.0 がリリースされました。

i-Radio

i-Radio i-Radio


ネットラジオ "i-Radio" の番組を iPhone から聴くことのできるアプリケーションです。

私がプログラムを担当しました。


iOS 4 のバックグラウンド再生に対応していますので、番組を聴きながら別のアプリケーションを操作することができます。

番組の内容は随時更新されます。

楽しい番組が揃っていますので、ぜひダウンロードしてみてください。無料です。


f:id:KishikawaKatsumi:20100715140449p:image

f:id:KishikawaKatsumi:20100715140621p:image


バックグラウンド再生中はホームボタンをダブルクリックして右にスワイプすると、i-Radio アプリの再生、停止、スキップなどのコントロールができます。

f:id:KishikawaKatsumi:20100715141014p:image


関連リンク

2010-07-14

In App Purchase のアイテム数の上限が 3000 個に増えました。

これまでは 1000 個が上限でしたが 3 倍の 3000 個に拡張されました。

他にも今回のバージョンでは下記のように変更点がたくさんあるので、改めて新しいガイドには目を通しておくといいと思います。

Version 5.6 - June 14, 2010
  • Support for iAd including iAd Network contract request, enabling for iAds and visibility of the iAd Net- work module on iTunes Connect
  • iTunes Connect Mobile iPhone App released
  • In App Purchase limit raised to 3000
  • Ability to delete an app from your iTunes Connect view
  • Promotional Codes request process enhancements
  • High resolution screenshot (960x640, 960x600, 640x960, 640x920) and small icon (114x114) require- ments
  • Crash log fetching on demand
iTunes Connect Developer Guide

あと、こちらのニュースを RSS 登録しておくといいでしょう。

News and Announcements for iOS Developers

2010-07-10

LDR touch 1.2.8をリリースしました。

LDR touch

LDR touch LDR touch

LDR touch 1.2.8が審査を通過しました。

iOS 4.0で使用したときにクラッシュする問題を修正しました。

他の修正は下記のとおりです。

変更点

  • iOS 4 でクラッシュする問題を修正。
  • ログインにSSLを使用するように変更。
  • iAdの広告を追加。
  • Mobilizer を Google Mobile Proxy から Instapaper に変更。
  • その他不具合の修正。

2010-07-07

アプリケーションを iPhone 4 の Retina Display に対応するための方法いろいろ

iPhone 4 の Retina Display の高解像度表示にアプリケーションを対応させるための方法をいくつか書きます。

これだけですべての場面に対応できるわけではないですが(例えば OpenGL での描画など)何かの役に立てばと思います。


高解像度の画像リソースを用意する

Retina Display は従来のディスプレイの倍の解像度を持っているので、倍の解像度に合わせた画像を用意します。

もちろん単純に拡大しただけではダメなので、解像度が高くなったぶん、なめらかな画像を用意することになります。

f:id:KishikawaKatsumi:20100707234133p:image

f:id:KishikawaKatsumi:20100707234134p:image


上記の例は、上が従来の画像、下が Retina Display 対応の画像です。


命名規則によって自動的に解像度に合わせた画像を読み分ける

[UIImage ImageNamed:] で読み込む場合は、ファイル名のサフィックスを判断して自動的にディスプレイの解像度に合わせた画像を読み分けてくれます。

高解像度の画像ファイル名には "@2x" のサフィックスを付けます。

上記の例だと、それぞれ

time_0.png
time_0@2x.png

という名前を付けます。

そのため [UIImage imageNamed:] を使っている部分は画像リソースを追加するだけで Retina Display に対応できます。

コードを変更する必要はありません。


ちなみに、iOS 4 から、imageNamed: の引数は拡張子が必要なくなりました。

Special Considerations

On iOS 4 and later, the name of the file is not required to specify the filename extension. Prior to iOS 4, you must specify the filename extension.

http://developer.apple.com/iphone/library/documentation/uikit/reference/UIImage_Class/Reference/Reference.html#//apple_ref/occ/clm/UIImage/imageNamed:

しかし、iPhone OS 3.x で実行された場合は、拡張子を付けた名前を指定しないと画像が読み込まれないため、動作環境を iOS 4 以上に限定しない限り、従来通り拡張子を含んだ形で指定することになります。


imageNamed: 以外のメソッドで画像を読み込んでいる場合は、上記の機構は働きません。

ドキュメントには imageWithContentsOfFile: と、initWithContentsOfFile: でも同様だと書いてあるのですが、試してみたところダメでした。

On devices with high-resolution screens, the imageNamed:, imageWithContentsOfFile:, and initWithContentsOfFile: methods automatically looks for a version of the requested image with the @2x modifier in its name. It if finds one, it loads that image instead. If you do not provide a high-resolution version of a given image, the image object still loads a standard-resolution image (if one exists) and scales it during drawing.

iOS Application Programming Guide: Supporting High-Resolution Screens

解像度に応じて処理を分岐して、読み込む画像を変化させる

imageNamed: は読み込んだ画像を自動的にキャッシュするため、使用したくない場合があります。

その場合、imageWithContentsOfFile: か initWithContentsOfFile: を使用することになりますが、これらのメソッドは上記の命名規則による読み分けの機構が働かないため、自分でディスプレイの解像度に合わせて、読み込む画像を変える必要があります。

例えば、下記のようにします。

static BOOL isPad;

NSString* model = [[UIDevice currentDevice] model];
isPad = [model rangeOfString:@"iPad"].location != NSNotFound;

if (!isPad && [UIScreen instancesRespondToSelector:@selector(scale)] && [[UIScreen mainScreen] scale] == 2.0) {
    NSString *path2x = [[mainBundle bundlePath] stringByAppendingPathComponent:
                        [NSString stringWithFormat:@"%@@2x.%@", [name stringByDeletingPathExtension], [name pathExtension]]];
    return [UIImage imageWithContentsOfFile:path2x];
}

UIScreen の scale プロパティの値を見ることで、座標の 1 ポイントとディスプレイの 1 ピクセルがどのような比率で対応するかが分かります。

要するに Retina Display だと倍の密度があるので 2.0 になります。従来のディスプレイでは 1.0 です。scale の値は 0 にはなりません。


ただし、iPad で iPhone アプリケーションを 2 倍モードで実行した場合も、scale の値は 2.0 になります。

(scale プロパティはいちおう iOS 4.0 以降ですが、iPhone OS 3.2 から存在しているので、respondToSelector:@selector(scale) は YES になりますし、値も返ってきます。)

そのため、モデル名を調べて、iPad かどうかを判断しています。

ちなみに、シミュレータで実行した場合はモデル名が必ず "iPhone Simulator" になる (iPad シミュレータでも) ので、上記のコードだと正しく動きません。

「必ず」ではありませんでした。iPhone アプリケーションを iPad のシミュレータの iPhone モードで実行した場合に "iPhone Simulator" と返ってきます。

iPad アプリをシミュレータで実行した場合は "iPad Simulator" になります。


ビットマップイメージをグラフィックコンテキスト (CGContextRef) に新しく描画する場合

画像をオフスクリーンで合成するなど、グラフィックコンテキストにビットマップを描画する場合は、解像度に合わせてグラフィックコンテキストを作る必要があります。


解像度に合わせてビットマップイメージ用のグラフィックコンテキストを作成するには UIGraphicsBeginImageContextWithOptions 関数を使用します。

void UIGraphicsBeginImageContextWithOptions(
   CGSize size,
   BOOL opaque,
   CGFloat scale
);

iPhone OS 3.x までの環境で使用していた、UIGraphicsBeginImageContext 関数に opaque と scale の引数が追加された新しい関数です。

void UIGraphicsBeginImageContext (
   CGSize size
);

今回は UIImage のカテゴリとして下記のようなメソッドを考えます。

@implementation UIImage()

- (UIImage *)imageByCompositeWithImage:(UIImage *)image {
    CGRect rect = CGRectMake(0.0f, 0.0f, self.size.width, self.size.height);
    if (UIGraphicsBeginImageContextWithOptions != NULL) {
        UIGraphicsBeginImageContextWithOptions(rect.size, NO, self.scale);
    } else {
        UIGraphicsBeginImageContext(rect.size);
    }
    [self drawInRect:rect];
    [image drawInRect:rect blendMode:kCGBlendModeNormal alpha:1.0f];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}

@end

まず OS バージョンを UIGraphicsBeginImageContextWithOptions 関数のポインタが NULL を指しているかどうかで判断しています。

UIGraphicsBeginImageContextWithOptions 関数が利用できる場合は iOS 4 以上かつ iPhone 4 である可能性があるので、scale 引数を渡し、解像度に合わせたグラフィックコンテキストを開始します。

グラフィックコンテキストを開始した以降の処理はこれまでと同様です。


今回の例では scale の値は UIImage のプロパティから取得しています。

UIGraphicsBeginImageContextWithOptions 関数および UIImage の scale プロパティは iOS 4 以降にしか存在しないため、この例では iPad かどうかは判断していません。


また、グラフィックコンテキストの仕様が iPhone OS 3.2 から下記のように変更になっているのでこちらも覚えておくといいと思います。

You use this function to configure the drawing environment for rendering into a bitmap. The format for the bitmap is as follows:

  • For bitmaps created in iPhone OS 3.2 and later, the drawing environment uses the premultiplied ARGB format to store the bitmap data. If the opaque parameter is YES, the bitmap’s alpha channel is ignored.
  • For bitmaps created in iPhone OS 3.1.x and earlier, the drawing environment uses the premultiplied RGBA format to store the bitmap data.
UIKit Function Reference

CATiledLayer にイメージを描画する場合

CATiledLayer を使用する場合、Retina Display だと tileSize に対する描画エリアの倍率がおかしくなります。

説明が難しいので以下の画像を見てください。こんなふうになります。笑えますね。

f:id:KishikawaKatsumi:20100707234135p:image


この問題を解決するには CATiledLayer を使用しているビューの contentScaleFactor の値を 1.0 に設定します。

設定するタイミングと場所は ビューの layoutSubviews メソッド内がいいでしょう。

@implementation TiledView

+ (Class)layerClass {
    return [CATiledLayer class];  
}

- (void)layoutSubviews {
    if ([self respondsToSelector:@selector(contentScaleFactor)]) {
        self.contentScaleFactor = 1.0f;
    }
}

@end

ビューが再表示される場合は layoutSubviews を再度呼ぶ必要があるかもしれません。

その場合は、ビューコントローラの viewWillAppear: で setNeedsLayout メソッドを呼びましょう。

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [tiledView setNeedsLayout];
}

TiledLayerView と「そのまま使える iPhone アプリプログラム」のサンプルコードは、この修正を施したバージョンになっていますのでご覧ください。


参考サイトなど

iOS Application Programming Guide: Supporting High-Resolution Screens

まずは iPhone アプリケーションプログラミングガイドの該当の章を読みましょう。


WWDC 2010 Session Videos - Apple Developer Session 134 - Optimize your iPhone App for the Retina Display

WWDC のセッションビデオはデベロッパには無料で公開されていますので見ておきましょう。

Retina Display 対応のセッションは 134 番のセッションになります。

2010-07-01

Retina Display に対応した iPhone アプリ用のアイコンセット

edditiPhone UI Icon Set がアップデートされて、iPhone 4 の Retina Display にも対応しました。


通常のディスプレイ用のリソースと、Retina Display 対応のリソースの両方を準備するのはかなり大変な作業なので嬉しいですね。


以前のものを購入済みでも再度購入する必要がありますが、単に拡大しただけではなくて、デザインの微調整をしてるということですので、その手間を考えると当然かと思います。

ただ、6月中に購入した人に限っては Paypal のレシートをメールすると無料でアップデートできるようです。

eddit: Notebook: iPhone UI Icon Set Updated for the iPhone 4 Retina Display

2010-06-30

iOS 4 のマルチタスキングサービスの種類

適当訳です。

詳しくは WWDC 2010 のセッション 105「Adopting Multitasking on iPhone OS, Part 1」を見ましょう。


Fast app switching

高速な復帰と状態の保存。

Push notifications

リモートサーバから通知を送ることができる。(iPhone OS 3.0 から存在)

Local notifications

サーバ不要のプッシュ通知。日時を指定する。

Background audio

バックグラウンドの音声再生。

Task completion

ファイルのアップロードなど、時間のかかる処理をバックグラウンド中に実行できる。現在は 10 分間の制限付き。

Location―Navigation

バックグラウンドで位置情報を追跡できる。

Location―Significant location change, region monitoring

バックグラウンドで予め指定したエリアへの出入を監視できる。

Voice over IP

バックグラウンドでインターネット電話の通話ができる。


iOS 4 のバックグラウンドタスク (Task completion) の制限時間は 10 分

下記のコードで確認。適当コードなのでマネしてはいけません。

- (void)onTimer:(id)timer {
    NSLog(@"Time remaining: %g", [[UIApplication sharedApplication] backgroundTimeRemaining]);
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    NSLog(@"%s", __func__);
    
    UIDevice *device = [UIDevice currentDevice];
    
    BOOL backgroundSupported = NO;
    if ([device respondsToSelector:@selector(isMultitaskingSupported)]) {
        backgroundSupported = device.multitaskingSupported;
    }
    
    if (backgroundSupported) {
        backgroundTask = [application beginBackgroundTaskWithExpirationHandler:^{
            NSLog(@"%@", @"End background task.");
            [timer invalidate];
            timer = nil;
            [application endBackgroundTask:backgroundTask];
            backgroundTask = UIBackgroundTaskInvalid;
        }];
        
        timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(onTimer:) userInfo:nil repeats:YES];
    }
}

実行結果。なんでか 5 秒前に終了しちゃうけど。

2010-06-30 21:14:43.761 Test[61557:207] -[AppDelegate applicationDidEnterBackground:]
2010-06-30 21:14:44.763 Test[61557:207] Time remaining: 598.998
2010-06-30 21:14:45.923 Test[61557:207] Time remaining: 597.838
2010-06-30 21:14:46.763 Test[61557:207] Time remaining: 596.998
2010-06-30 21:14:47.784 Test[61557:207] Time remaining: 595.977
2010-06-30 21:14:48.768 Test[61557:207] Time remaining: 594.993
(中略)
2010-06-30 21:24:32.743 Test[61557:207] Time remaining: 10.9981
2010-06-30 21:24:33.743 Test[61557:207] Time remaining: 9.99815
2010-06-30 21:24:34.743 Test[61557:207] Time remaining: 8.99815
2010-06-30 21:24:35.743 Test[61557:207] Time remaining: 7.99819
2010-06-30 21:24:36.743 Test[61557:207] Time remaining: 6.99812
2010-06-30 21:24:37.743 Test[61557:207] Time remaining: 5.99799
2010-06-30 21:24:38.743 Test[61557:207] Time remaining: 4.99807
2010-06-30 21:24:38.744 Test[61557:207] End background task.