Hatena::ブログ(Diary)

24/7 twenty-four seven

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  Japan Subway Route Map こころくろっく 英辞郎 on the WEB for iPhone(アルク)
i-Radio くるりんぱ性格診断 英辞郎検索ランキング(アルク) kotobank - コトバンク miil

iPad アプリケーション

LCD Clock HD 「超」整理手帳 for the iPad

Windows 8 Store アプリケーション

クックパッド

共著

2013-04-22

iOS/Macの両方で使えて、文字の選択やリンクのクリックに対応したテキストビューをテスト公開しました。

kishikawakatsumi/SECoreTextView ? GitHub

iOS ScreenShot 1


OS X ScreenShot 1


SECoreTextView はリッチテキストの表示と文字の選択(現在はOS Xのみ)やリンクがクリック可能だったりするテキストビューです。

別のアプリでテーブルビューのセルにリンクを含むテキストを表示するのに、既存のものでMacで使えるいい感じのものが今ひとつ見つからなかったので書きました。

OS X で使うだけだとなんなので、せっかくだから iOS にも対応してみました。

UITableVIewやNSTableVIewのセルで使うと便利だと思います。

iOS のほうは半日くらいでちょちょっと書いただけなのでおかしなところが結構あると思うので見つけたら教えてください。

2012-12-29

iPhone の画面操作を録画するライブラリを公開しました。

kishikawakatsumi/ScreenRecorder ? GitHub

ScreenRecorder は iOS デバイスの画面を連続的にキャプチャして、動画に変換することで画面の操作を録画することができる機能をアプリケーションに追加します。

開発中のソフトウェアのユーザーテストなどに利用すると効果的です。


使い方

1. 以下のファイルをプロジェクトに追加します

  • Lib/SRScreenRecorder.h
  • Lib/SRScreenRecorder.m
  • Vendor/KTouchPointerWindow.h
  • Vendor/KTouchPointerWindow.m

2. 以下のフレームワークをリンクします

  • QuartzCore.framework
  • CoreVideo.framework
  • CoreMedia.framework
  • AVFoundation.framework

startRecording で録画を開始します。

デフォルトの設定は

  • バックグラウンドに入ったときに自動保存
  • 10 分ごとに自動保存、ファイルのローテート
  • 30FPSで録画
  • タッチ箇所の表示

となっています。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [[SRScreenRecorder sharedInstance] startRecording];
    return YES;
}

いくつかの挙動は、設定で変更することができます。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    SRScreenRecorder *recorder = [SRScreenRecorder sharedInstance];
    recorder.frameInterval = 1; // 60 FPS
    recorder.autosaveDuration = 1800; // 30 minutes
    recorder.showsTouchPointer = NO; // hidden touch pointer
    recorder.filenameBlock = ^(void) {
        return @"screencast.mov";
    }; // change filename
    
    [recorder startRecording];
    
    return YES;
}

タッチ・ポインターの表示には @ さんの KTouchPointerWindow を利用しています。

iPhone/iPadの画面にタッチ位置を表示するためのソースコード KTouchPointerWindow を公開しました

itok/KTouchPointerWindow ? GitHub

2012-10-24

リンクするだけで iOS 6 で Google Map が使えるようになる YAMapKit を公開しました。

kishikawakatsumi/YAMapKit ? GitHub

YAMapKit は MapKit.framework と(ほぼ)互換性のある代替ライブラリです。

Google Maps Javascript API と UIWebView を利用して iOS 6 で Apple の標準地図の代わりに Google Map を使った表示ができます。

MapKit.framework と(ほぼ)互換性があるのでリンク先を差し替えるだけで動作します(たいていの場合は)。

あまりヘビーな利用には向きませんが、アプリケーションの中でちょっと MapKit を使って地図を表示したりピンを挿したりしているという場合に効果的です。


まだ未サポートの機能がたくさんあるので、手伝ってくれる方や、バグレポート、要望を歓迎します。

使い方

  1. MapKit.framework のリンクを外します。
  2. libMapKit.a をリンクします。
  3. CoreLocation.framework をリンクします。

できないこと

  • ジオコーディング(代わりに 'CLGeocoder' を使ってください)
  • カスタムビューのオーバーレイ表示(組み込みのオーバーレイ (MKPolylineView, MKCircleView など) しか使えません)
  • アノテーションのドラッグ&ドロップ
  • アノテーションのコールアウトを表示したあとで更新する

(たぶん他にもいっぱいあります)


利用例

f:id:KishikawaKatsumi:20121024221827p:image:w200 f:id:KishikawaKatsumi:20121024221820p:image:w200 f:id:KishikawaKatsumi:20121024221831p:image:w200

f:id:KishikawaKatsumi:20121024221835p:image:w200 f:id:KishikawaKatsumi:20121024221839p:image:w200 f:id:KishikawaKatsumi:20121024221843p:image:w200

2012-10-23

Objective-C でサブクラスのインスタンスから任意のスーパークラスのメソッドを呼ぶ

サブクラスのインスタンスからポリモーフィズムを無視して任意のスーパークラスのメソッドを呼びます。


↓ 下のように Shape クラスと Shape クラスを継承した Path クラス、および Path クラス を継承した Circle があります。

それぞれのクラスで draw メソッドをオーバーライドしています。

////////////////////////////////////////////////////////////////////////
#pragma mark - Shape
////////////////////////////////////////////////////////////////////////
@interface Shape : NSObject
@end

@implementation Shape

- (void)draw {
    NSLog(@"%@", @"Shape.");
}

@end

////////////////////////////////////////////////////////////////////////
#pragma mark - Path
////////////////////////////////////////////////////////////////////////
@interface Path : Shape
@end

@implementation Path

- (void)draw {
    NSLog(@"%@", @"Path.");
}

@end

////////////////////////////////////////////////////////////////////////
#pragma mark - Circle
////////////////////////////////////////////////////////////////////////
@interface Circle : Path
@end

@implementation Circle

- (void)draw {
    NSLog(@"%@", @"Circle.");
}

@end

↓ Circle クラスのインスタンスから draw メソッドを呼び出すと Circle クラスの draw メソッドが実行されて "Circle." と出力されます。

たまに多態性を無視してスーパークラスや、スーパークラスのさらにスーパークラスのメソッドを実行したいということってありますよね。

そういうときは 対象メソッドの IMP (メソッドを参照する関数へのポインタ) を使います。

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    Shape *shape = [[Circle alloc] init];
    [shape draw]; // => Circle.
    
    SEL selector = @selector(draw);
    
    void(*pathFunction)(id, SEL, ...) = (void(*)(id, SEL, ...))[Path instanceMethodForSelector:selector];
    pathFunction(shape, selector); // => Path.
    
    void(*shapeFunction)(id, SEL, ...) = (void(*)(id, SEL, ...))[Shape instanceMethodForSelector:selector];
    shapeFunction(shape, selector); // => Shape.
}

@end

↑ IMP の定義のままだと ARC が戻り値を retain しようとするので、戻り値が void の関数ポインタにキャストしています。


↓ 上記のコードの出力は下記になります。

2012-10-23 13:15:20.632 Monomorphism[50033:c07] Circle.
2012-10-23 13:15:20.633 Monomorphism[50033:c07] Path.
2012-10-23 13:15:20.633 Monomorphism[50033:c07] Shape.

サブクラスのインスタンスからポリモーフィズムを無視して任意のスーパークラスのメソッドが呼べました。

↓ 試したコードの全体を載せておきます。

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@end

////////////////////////////////////////////////////////////////////////
#pragma mark - Shape
////////////////////////////////////////////////////////////////////////
@interface Shape : NSObject
@end

@implementation Shape

- (void)draw {
    NSLog(@"%@", @"Shape.");
}

@end

////////////////////////////////////////////////////////////////////////
#pragma mark - Path
////////////////////////////////////////////////////////////////////////
@interface Path : Shape
@end

@implementation Path

- (void)draw {
    NSLog(@"%@", @"Path.");
}

@end

////////////////////////////////////////////////////////////////////////
#pragma mark - Circle
////////////////////////////////////////////////////////////////////////
@interface Circle : Path
@end

@implementation Circle

- (void)draw {
    NSLog(@"%@", @"Circle.");
}

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    Shape *shape = [[Circle alloc] init];
    [shape draw]; // => Circle.
    
    SEL selector = @selector(draw);
    
    void(*pathFunction)(id, SEL, ...) = (void(*)(id, SEL, ...))[Path instanceMethodForSelector:selector];
    pathFunction(shape, selector); // => Path.
    
    void(*shapeFunction)(id, SEL, ...) = (void(*)(id, SEL, ...))[Shape instanceMethodForSelector:selector];
    shapeFunction(shape, selector); // => Shape.
}

@end

2012-10-20

iOS 6.0 と iOS 5.x の両方で動作するアプリケーションをビルドする設定

iOS 4.0 と iPhone OS 3.x の両方で動作するアプリケーションをビルドする設定 - 24/7 twenty-four seven

↑ こちらも参考に

iOS 4.0 が登場したくらいのときに上の記事を書いて、仕組みは変わってないのですけど Xcode 4.x 系だと UI が変わってるので現在のやり方をまとめます。


ベース SDK と Deployment Target を設定する

プロジェクトの "Build Settings" で "Base SDK" を "Latest iOS" にします。

前にも書きましたが、ベース SDK は最新を指定したほうがいいです。

f:id:KishikawaKatsumi:20121020113008p:image:w600


プロジェクトの "Info" で "Deployment Target" をサポートする OS の最も低いバージョンにします。

(下の場合は iOS 5.0 以降で動作する。)

f:id:KishikawaKatsumi:20121020113320p:image:w600


今なら、Base SDK 6.0 でビルドして、Deployment Target を 5.0 または 5.1 にするのが効果的でしょうか。2世代サポートということで。


新しい Framework を Weak Link (Optional) に設定する

古い環境には含まれていない Framework をリンクしていると、Dynamic Linker がシンボルのロードに失敗してアプリケーションが起動しません。

その場合は、新しい OS にのみ存在する Framework を Weak Link に設定します(今回は Social.framework)。

f:id:KishikawaKatsumi:20121020113833p:image:w600

↑ リンクの設定はプロジェクトではなくターゲットに対してしか行えないので、ターゲットを選択して "Build Phases" > "Link Binary With Libraries" から設定します。

リンクするフレームワークを設定して、 Required > Optional に変更します。


OS のバージョンごとに処理を分岐する

古い API には存在しないセレクタを呼び出したり、クラスを参照したりするとクラッシュしますので必要に応じて処理を分岐します。

- (void)tweet:(id)sender
{
    Class clazz = NSClassFromString(@"SLComposeViewController");
    if (clazz) {
        SLComposeViewController *controller = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter];
        [self presentViewController:controller animated:YES completion:nil];
    } else {
        TWTweetComposeViewController *controller = [[TWLandscapeTweetComposeViewController alloc] init];
        [self presentViewController:controller animated:YES completion:nil];
    }
}

Auto Layout に注意

Auto Layout は iOS 6 以降しか使えません。Auto Layout を使用していると iOS 5.x などで起動時に関連のクラスが無くてクラッシュします。StoryBoard を新しく追加したときなどにうっかり Auto Layout のチェックをつけたままにしてしまったりするので注意しましょう。

f:id:KishikawaKatsumi:20121020114404p:image:w300

2012-10-05

iOS 6 では Supported interface orientations の順番に注意!

f:id:KishikawaKatsumi:20121005033214p:image:w480

最近の Xcode ではアプリケーションが対応しているデバイスの向きをターゲットの Summary 画面から GUI を用いて設定できるようになりましたが、ここから設定する場合はボタンを押す順番に注意する必要があります。

というのも、この画面で設定した内容は、Info.plist の Supported interface orientations (UISupportedInterfaceOrientations) に反映されるのですが、この項目は Array の値で順番が起動時の状態に影響するからなのです。


上記の画面の状態になるように、ボタンを左から順に押していった場合、Info.plist の UISupportedInterfaceOrientations は下記のようになります。これは新規プロジェクトを作成した場合のデフォルト値です。

f:id:KishikawaKatsumi:20121005033335p:image:w480


今度は同じ状態になるように、ボタンを「右から」順に押していきます。すると Info.plist は下記のようになります。

値の順番が変わっています。

f:id:KishikawaKatsumi:20121005034024p:image:w480


実はこの順番は起動時の画面の向きに関係していて、iOS 6 では一番先頭に指定されている画面の向きで起動することになります。

つまり、前の例では縦画面で起動するのですが、後の例では横向きで起動することになります。


別のキーに Initial interface orientation (UIInterfaceOrientation) というものがあって、こちらを指定すると初期状態を指定できそうですが、試したところ iOS 6 ではどうもこの値は無視されるようです。

2012-10-04

iOS 6 ではグループスタイルのテーブルビューの背景色がこっそり非推奨になっている。

f:id:KishikawaKatsumi:20121004223424p:image:w320


iOS 6 では上記のカラーを生成するメソッドがヘッダのコメントでひそかに deprecated になっています。

UIInterface.h

// Group style table view backgrounds can no longer be represented by a simple color.
// If you want to have a background in your own view that looks like the table view background,
// then you should create an empty table view and place it behind your content.
+ (UIColor *)groupTableViewBackgroundColor; // This method will be deprecated during the 6.0 seed program

ドキュメントの記載は変わってないのですが、実際に使ってみると今までは下記のコードでピンストライプのカラーが設定されていましたが、iOS 6 だと真っ黒になってしまいます。

self.view.backgroundColor = [UIColor groupTableViewBackgroundColor];

この背景色を使いたい場合は空のテーブルビューを設定しろということなので、iOS 6 から代替のコードは下記のようになります。

UITableView *tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
[self.view addSubview:tableView];

2012-10-01

はやりのシンボルフォントを iOS で画像として扱える SymbolFontKit を公開しました。

kishikawakatsumi/SymbolFontKit ? GitHub

ScreenShot1

↑ シミュレータに表示されている画像やツールバーのボタン、タブバーのアイコンは全てフォントです。

シンボルフォントとは要するにアイコン画像などをフォント形式にしたものです。

Webだと最近のブラウザだとWebフォントが使えるので、利用者の環境にフォントがインストールされていなくても使えるので、最近は解像度非依存ということもあっていろいろな Github や Twitter などいろいろなサイトで利用されています。

シンボルフォントについて詳しくは下記のリンク先などを見てください。

Ligature Symbols 〜ほんとにべんりなフォントのはなし〜

【完全版】Ligature Symbols フォントセットの自作方法 - くらげだらけ

Ligature Symbols

で、フォントなのでベクターデータのためどんな解像度でもキレイに表示されることや、1つのアイコンで色違いや別のサイズを表示することが簡単だったり、はやっているので様々なデザインのシンボルフォントが使いやすいライセンスで入手できるなど、iOS でも利用できたら便利だと思って作りました。

使い方

  1. SFKImage.h/m をプロジェクトにコピーします。
  2. 利用したいフォントファイルをプロジェクトにコピーします。
  3. 上記でコピーしたフォントファイルのファイル名を UIAppFonts をキーにして Info.plist に追加します。

API は UIImage 互換なので UIImage と同様の使い方ができます。(実は現在は imageNamed: 以外のインスタンス化はできません)

SFKImage *image = [SFKImage imageNamed:@"print"];

インスタンス化した SFKImage オブジェクトは UIImage のインスタンスと同様に UIButton や UIImageView、UITabBarItem などに直接設定することができます。

self.imageView1.image = [SFKImage imageNamed:@"share"];

UITabBarItem *calendarTabBarItem = [[UITabBarItem alloc] initWithTitle:@"calendar" image:[SFKImage imageNamed:@"calender"] tag:1];
UITabBarItem *globeTabBarItem = [[UITabBarItem alloc] initWithTitle:@"globe" image:[SFKImage imageNamed:@"globe"] tag:2];
_tabBar.items = @[calendarTabBarItem, globeTabBarItem];

フォントのレンダリングは実際に画面に描画されるときまで遅延されるので、UIImage と違って、1つのインスタンスを途中で色や大きさを変えたりできます。

SFKImage *image = [SFKImage imageNamed:@"compass"];
image.size = CGSizeMake(20, 20);
image.color = [UIColor redColor];
self.imageView6.image = image;
    
image.size = CGSizeMake(40, 40);
image.color = [UIColor yellowColor];
self.imageView7.image = image;
    
image.size = CGSizeMake(80, 80);
image.color = [UIColor blueColor];
self.imageView8.image = image;

UIImage として振る舞うためにちょっと無茶をしているのでそのまま AppStore の審査に通るかどうかは「?」ですが近いうちに適当なアプリを提出して調査したいと思います。


SymbolFontKit は第1回iphone_dev_jp東京 iPhone/Mac Hackathon 〜みんなが幸せになるハッカソン〜の成果物です。

2012-09-25

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

Hatena touch

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

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

iOS 6, iPhone 5をサポートしました。


変更点

  • iOS 6, iPhone 5をサポートしました。

2012-09-18

はてなブックマークの ShareKit 拡張を書きました

ShareKit/Classes/ShareKit/Sharers/Services/Hatena at hatena ? kishikawakatsumi/ShareKit ? GitHub


ShareKit といういろいろな外部サービスとの連携機能を提供するライブラリがあるのですが、それのプラグイン (ShareKit では Sharer と呼びます) として「はてなブックマーク」とリンクを共有するものを書きました。

オリジナルを fork して hatena ブランチにコミットしています。利用するには clone して hatena ブランチに切り替えます。


本家にPull Request を送ったので、もしかしたらマージされるかもしれません。

(既存の Sharer に日本のサービスはなかったのでイマイチ勝手がわかりませんでした。)


↓ 発端は fladdict さんとの下記のやりとりです。


はてなブックマークへの投稿は昔書いたことがあったので、認証を OAuth にしたらあとはカンタンそうだなと思って安請け合いしました。いやまあ、特に難しいことはなかったんですけど。




↑ ちょっとだけハマったのは、付属している OAuth ライブラリ (OAuthConsumer) が不具合のあるリビジョンのもので、シグネチャを組み立てるときに追加の OAuth パラメータの部分を2重に URL エンコードしていました。

そのため、はてなの OAuth は必ずコールバック URL を oauth_callback パラメータで指定する必要があるのですが、シグネチャが invalid になって OAuth が成功しないということがありました。


↓ こちらの不具合は別の Pull Request でパッチを送ったので現在は修正されています。

Fix double URI encode for extra OAuth parameters. by kishikawakatsumi ? Pull Request #515 ? ShareKit/ShareKit ? GitHub



↑ そんなこんなでとりあえず書いてみた割りにはちゃんと使えてるようでよかったです。

OAuth で許可するスコープに write_private を書いたら失敗する点とか気になるところはあるけど。。。



↑ Pull Request しました。↓

Add Hatena Bookmark Sharer. by kishikawakatsumi ? Pull Request #521 ? ShareKit/ShareKit ? GitHub