アプリ EverGear をリリースしました

2012年5月18日金曜日 | Published in | 2 コメント

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

Evernote投稿用アプリ「EverGear」をリリースしました。1つのノートに20枚の写真やメモを付けることができたり、ノートのひな形(テンプレート)を作るといった特徴を持つアプリです。
※有料です。


写真


1つのノートに20枚までの写真を登録することができます。写真にメモを残すこともできます。



ログ


メモだけを残すこともできて、それらをまとめて時系列に表示できます。メモと写真の集まりを「ログ」と呼んでいます。※メモの件数に制限はありません。



ひな形


あらかじめ文字やノートブック、タグを定義しておいてそれをノート作成時に利用することができます。登録しておいたひな形はノート作成時にメニューから選んで利用できます。



送信


バックグラウンド送信に対応しています。送信中のノートは送信タブで見ることができます。



履歴


送信したノートを後から見返すことができます(最近50件まで、それ以前は自動削除)。



ノート編集


テキストの編集やノートブック指定、タグ指定など基本的な機能が揃っています。



設定画面





Evernote


ログは Evernoteに送られるとメモと写真(サムネイル)が時系列に記録されます。その下に写真が並びます。
構成イメージ
+ーーーーーーーーーー+
|   テキスト   |
+ーーーーーーーーーー+
|   ログ1    |
|   ログ2    |
|    :     |
+ーーーーーーーーーー+
|   写真1    |
|   写真2    |
|    :     |
+ーーーーーーーーーー+

Mac版のEvernoteで見たイメージ


ログ(メモ+サムネイルの時系列表示)

写真

iPhone版のEvernoteで見たイメージ


ログ(メモ+サムネイルの時系列表示)

写真


サポートサイト


Facebookにあります。リリース情報などは今後そちらで公開していきます。



UIDocumentInteractionController にファイルを別名で渡す

2012年3月6日火曜日 | Published in | 0 コメント

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

問題


UIDocumentInteractionController で他のアプリにファイルを渡すことができる。こんな感じ。
self.documentInteractionController =
    [UIDocumentInteractionController  interactionControllerWithURL:url];
self.documentInteractionController.delegate = self;
[self.documentInteractionController presentOptionsMenuFromBarButtonItem:
    self.sendApplicationButtonItem animated:YES];

普通はアプリで作成したファイルのURLをそのまま渡せば事足りるのだが、ネットワークからダウンロードしてキャッシュしているファイルだったりすると名前が管理上の適当な名前になっているケースがある。
実名:UserDocument.pdf
キャッシュ名:js9798sfs8df9s9fsd.pdf
キャッシュファイルのURLをそのまま他のアプリへ渡すと "js9798sfs8df9s9fsd.pdf" みたいな名前で扱われて使い勝手が悪い。できれば渡した先のアプリでも "UserDocument.pdf" として扱わせたい。

UIDocumentInteractionController にはこういった目的の為に nameプロパティが用意されている。
@property(nonatomic,copy) NSString *name
This property contains the filename without any preceding path information. The default value of this property is derived from the path information in the URL property. You can change the value of this property as needed if you want to associate a different name with the file.
ところが nameプロパティを設定しても送った先のアプリでは使われず "js9798sfs8df9s9fsd.pdf" のまま。

どうしても UIDocumentInteractionController へ渡す URLのファイル名を "UserDocument.pdf" にする必要があるようだ。方法としてはキャッシュファイルをコピーする方法がある、がこれは非効率すぎる。こういう時はシンボリックリンクを使うのがいい。

(参考)ソフトリンク - Wikipedia


シンボリックリンク


iOS でもシンボリックリンクを使うことができて NSFileManagerが標準でサポートしている。
createSymbolicLinkAtURL:withDestinationURL:error:

そこで UIDocumentInteractionController へ渡す直前にシンボリックリンクを作成してそのURLを渡してみた。
if ([fileManager createSymbolicLinkAtURL:cacheURL withDestinationURL:newURL error:&error]) {
        NSLog(@"%s|%@", __PRETTY_FUNCTION__, @"a symbolic link is created");
    } else {
        NSLog(@"%s|%@", __PRETTY_FUNCTION__, error);
    }

結果は ×

ファイル名は正しく渡るのだが送り先のアプリで正しく扱われない。詳しく調べていないがシンボリックリンク自体を開いているのだがと思われる(実体へのリンク情報が書かれているだけで実質何も入っていない)。

うーむ。


ハードリンク


どうするかと考えていたところ @nakiwo さんからこんなヒントが。

ハードリンクか!

(参考)ハードリンク - Wikipedia

iOS では NSFileManager がハードリンクもサポートしている。
linkItemAtURL:toURL:error:

早速試してみた。
if ([fileManager linkItemAtURL:cacheURL toURL:newURL error:&error]) {
        NSLog(@"%s|%@", __PRETTY_FUNCTION__, @"a hard link is created");
    } else {
        NSLog(@"%s|%@", __PRETTY_FUNCTION__, error);
    }

結果は ◯

送り先のアプリで意図したファイル名 "UserDocument.pdf" として扱われ、中身もちゃんと渡っている。

GoodReader, DropBox, Numbers などで動作確認できた(4S/iOS5.0.1)。
ただ iBookのみファイル名が英数字の羅列になっていた(読み込み時は意図したファイル名が一瞬表示されるのだが)。


実装パターン


作ったハードリンクは消しておく必要がある。消さないと元のファイルを消してもハードリンクが残ってしまう(参照カウントが残っている限りファイルの実体が消えない)。作るタイミングと消すタイミングは UIDocumentInteractionControllerDelegate で用意されているアプリ送出前後のメソッドを使うのがいい。
- (void)documentInteractionController:(UIDocumentInteractionController *)controller
           willBeginSendingToApplication:(NSString *)application
{
    // ハードリンク作成 ....
}

- (void)documentInteractionController:(UIDocumentInteractionController *)controller 
           didEndSendingToApplication:(NSString *)application
{
    // ハードリンク削除 ....
}
送るメニューを出す手順の中で呼び出す [UIDocumentInteractionController interactionControllerWithURL:url] の時点では実体が存在する必要はない。先ほどのデリゲート documentInteractionController:willBeginSendingToApplication: で作っても間に合った。


- - - -

※今回はサンプル無しです。


[iOS] iOS5 から標準で UINavigationBar に複数のボタンを配置することができるようになった

2012年1月5日木曜日 | Published in | 0 コメント

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

UINavigationItem の API に iOS5 から rightBarButtonItems があることに気がついた。複数形ということは...?

早速試してみた。こうすると。
UIBarButtonItem *item1 = [[UIBarButtonItem alloc]
                              initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
                              target:self
                              action:@selector(touched:)];
    
    UIBarButtonItem *item2 = [[UIBarButtonItem alloc]
                                initWithBarButtonSystemItem:UIBarButtonSystemItemCamera
                                target:self
                                action:@selector(touched:)];

    self.navigationBar.topItem.rightBarButtonItems =
        [NSArray arrayWithObjects:item1, item2, nil];
こうなる。
複数ボタンが標準でサポートされたのか。

横向きも問題なし。

スペースも入れられる。


3個もいける。


左も leftBarButtonItems が用意されている。

今回は試していないが戻りボタン(左のとんがっているやつ)がある場合はその右側に表示されるとのこと。

左右にボタンを配置するとタイトル→左→右の順で優先される。下記は左右に4つのボタンを指定した時の表示。幅が不足している場合は表示されない。

横向きにすると全部表示された。



タイトルが長いとその分表示幅が狭くなるので表示個数が減る。

なお右ボタンの場合、右から順番に配置される。


ソースコード


GitHub からどうぞ。
MultiNavigatorItemButtons at 2012-01-05 from xcatsan/iOS5-Sample - GitHub


関連情報


UINavigationBar に複数の UIBarButtonItem を配置するには - 24/7 twenty-four seven
UIToolbar をカスタムビューとして配置する方法。iOS 4 ではこういった方法しか無い。



リファレンスマニュアルを mac から ios に切り替える(割とどうでもいい)Tips

2012年1月3日火曜日 | Published in | 0 コメント

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

Googleで Appleのサイトのリファレンスマニュアルを検索すると iOSではなく Mac OS X の方がヒットすることがある(特にNS系クラスなど)。これを iOSに切り替えるTips。

方法は簡単で URLの mac を ios へ変えて再読込するだけ。

こうなる↓

両方のOSに存在する実装であれば、クラスリファレンスに限らず各種ガイド・マニュアル類でも同じ方法が使える。

Cocoaの日々 - 2012年正月

2012年1月2日月曜日 | Published in | 0 コメント

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

あけましておめでとうございます。
本年もよろしくお願いします。

毎年正月はこのブログのことについて書いています。今年も思ったことをとりとめなく書いておきます。

昨年はブログの毎日更新をやめた他、秋以降は忙しかったこともありブログの更新頻度はずいぶん下がってしまいました。今年も当面は頻度が下がると思いますが技術情報を少しでも提供して共有できればと思っています。

また昨年正月に目標としていたことはほとんど実行できませんでした。
Cocoaの日々: Cocoaの日々 - 2011年元旦
唯一できたのは「Cocoaの日々情報局」を立ち上げたくらい(RoRは使っていないが)。

その点プログラミングに関して昨年は不本意な一年でした。ただその分、子育てやハーフマラソン完走、TOEIC目標クリアなどそれなりに充実した一年だったと思います(なかなか両立は難しい...)。

さて今年は趣味のプログラミングに関して今までのやり方を大きく変えようと考えています。

(今まで)
・基本的にライブラリを使わず手作り
・知らない技術は網羅的に検証しながら進める
・その成果を基本的にブログで公開
・1つのアプリに時間をかける

これを今年は変えていく。

(今年)
→ 他の人のライブラリを積極的に利用する
→ 丁寧な検証はやらない
→ ブログは最小限に
→ 1つのアプリに時間をかけない

趣味なので時間をかけて検証して石を彫るように進める開発はそれなりに楽しかったけれど、同じやり方を続けるのは進歩がない。それに以前と違って使える時間も無い。そこで今年は思い切ってそれを変えていく。うまくいくかわからないが良い変化になればと考えている。

この数ヶ月は昨年来開発を続けてきたiPhoneアプリを完成させて公開することがまずは今年最初の目標になります(Evernote系のアプリを製作中)。その後は2〜3ヶ月毎に1本づつアプリを作って行きたい。

- - - -

情報局ともどもこのブログを今年もよろしくお願いします。



[iOS] AVFoundation のカメラでフォーカスと露出をタッチで合わせる

2011年12月2日金曜日 | Published in | 0 コメント

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

AVFoundation を使い、タッチでフォーカスと露出を決められるカスタムカメラを作る。今日の主題はタッチフォーカス&露出なのでプレビューを表示するだけで撮影(画像の保存)は実装していない。

サンプル


まずはサンプルから。起動すると真ん中に四角が表示される。

この状態で任意の場所をタッチするとそこにフォーカスと露出が合う。

手前を合わせてみた。


実装 - プレビュー


カメラ入力をプレビュー表示する部分の説明は今回割愛する。この辺りは下記のドキュメントが詳しい。ひと通り読めば簡単に実装できる。

AV Foundation プログラミングガイド

もしくは



など。後者の本は iOS4 となっているが iOS5 でも十分に役立つ情報が掲載されている。おすすめの本。


実装 - フォーカスと露出


任意の点にフォーカスと露出をあわせるプロパティが用意されている。
AVCaptureDevice.h

@property(nonatomic) CGPoint focusPointOfInterest;
@property(nonatomic) CGPoint exposurePointOfInterest;
基本的にはここにフォーカスもしくは露出を合わせたい座標を入れればいい。

ただしこのプロパティに設定する値は次のルールに従う必要がある。

(1) 0.0〜1.0 に正規化した値
(2) ランドスケープ(横向き/ホームボタン右)の時の左上を原点とする座標系

「AVFoundation プログラミングガイド(PDF)」によれば次のように解説されている。
ホームボタンが右側になる横長モードでは、{0,0}が画像領域の左上を表し、{1,1}が右下を表します。これ
は、デバイスが縦長モードであっても同じです。


つまり通常のポートレイト(縦向き/ホームボタン下)の状態の時に画面上のタッチ位置をこれらに反映させるには、(1)の正規化と(2)の座標系への変換が必要になる。

(1)の正規化は単純に大きさで位置座標を割ってやれば良い。 (x/width, y/height)

ポートレイトの座標系(左上が原点)を使っている時のそれぞれの座標系の関係はこんな感じ。

x と y を入れ替えて、かつ y軸の方向に反転してやればいい。これは(1)の正規化が済んでいれば簡単にできる。

サンプルでのコードはこんな感じ。
- (void)setPoint:(CGPoint)p
{
    CGSize viewSize = self.view.bounds.size;
    CGPoint pointOfInterest = CGPointMake(p.y / viewSize.height,
                                          1.0 - p.x / viewSize.width);

フォーカスの場合は、この値を focusPointOfInterest へ渡し、focusMode を設定すれば良い。こんな感じ。
if ([videoCaptureDevice isFocusPointOfInterestSupported] &&
            [videoCaptureDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
            videoCaptureDevice.focusPointOfInterest = pointOfInterest;
            videoCaptureDevice.focusMode = AVCaptureFocusModeAutoFocus;
        }

露出の方は、exposurePointOfInterest 渡すだけでは駄目で、もう少し手間が必要となる。
if ([videoCaptureDevice isExposurePointOfInterestSupported] &&
            [videoCaptureDevice isExposureModeSupported:
                  AVCaptureExposureModeContinuousAutoExposure]){
            self.adjustingExposure = YES;
            videoCaptureDevice.exposurePointOfInterest = pointOfInterest;
            videoCaptureDevice.exposureMode =
                  AVCaptureExposureModeContinuousAutoExposure;
        }
ここまではフォーカスとほぼ同じ。これに加えて露出の場合は、exposurePointOfInterest設定後に AVCaptureDevice の adjustingExposure プロパティを監視しこの値が NO(つまり露出合わせが終了)になったタイミングで exposureMode を AVCaptureExposureModeLocked にする必要がある。

- (void)viewDidLoad
{
   :
            [videoCaptureDevice addObserver:self
                                 forKeyPath:@"adjustingExposure"
                                    options:NSKeyValueObservingOptionNew
                                    context:nil];
   :
監視を設定しておき値が変化したら処理をする。
- (void)observeValueForKeyPath:(NSString *)keyPath
  ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (!self.adjustingExposure) {
        return;
    }
    
 if ([keyPath isEqual:@"adjustingExposure"]) {
  if ([[change objectForKey:NSKeyValueChangeNewKey] boolValue] == NO) {
            self.adjustingExposure = NO;
            AVCaptureDevice* videoCaptureDevice =
            [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
            
   NSError *error = nil;
   if ([videoCaptureDevice lockForConfiguration:&error]) {
     [videoCaptureDevice setExposureMode:AVCaptureExposureModeLocked];
    [videoCaptureDevice unlockForConfiguration];
   }
  }
 }
}

試してみよう。まずは右にある MacBook Pro の明るい部分に合わせた。

次に手前の暗い 3GS。

うまくいったようだ。


ソースコード


GitHub からどうぞ。
CameraSample at 2011-12-02 from xcatsan/iOS-Sample-Code - GitHub


参考情報


iPhone4でサポートされてない露出モードのエラーが出たけど - Debian GNU/Linux 3.1 on PowerMac G4

露出の位置指定はここの情報が参考になった(紹介されているコードは若干不備がある 2011-12-03追記:記事著者の方が修正してくれました。素早い!)。また毎回値監視をつけたり外したりするのは管理しずらいのでサンプルでは viewDidLoad で監視を開始している(監視外しは今見たら入れ忘れてた→dealloc, viewidiUnloadなどに入れておく)。


github で Forkしてみる

2011年11月25日金曜日 | Published in | 0 コメント

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

github にはソースを自分用にカスタマイズできる Fork という機能があるらしい。試してみた。

最初に github に自分のアカウントでログインしておきForkしたいソースのページを開く。そして右上の "Fork" ボタンを押す。

これだけ。自分のリポジトリに Forkされたリポジトリが作成される。

一旦、Fork したものは自分ですきなようにできる。例では MTStatusBarOverlay というライブラリを Fork してみた。

このライブラリにはサンプルアプリが付いていなかったので自分で作ってみる。Forkした先ほどのリポジトリを Clone して手元の PCへ持ってくる。

(参考)

Xcode でプロジェクトを開き、新しいターゲットを追加する。


簡単な動作確認なので Single View Application を使う。
ターゲットが追加された。

スキーマも一緒に作ってくれるのですぐにビルドできる。

アプリを動作させる為に必要なファイル一式も作成されている。

適当にコードを書いて動作確認が終わったらコミットする。GitHub.app で cloneしたならコミットもここでできる。
コミットが終わった。続いて github と同期を取る(git push する)。

完了。


github サイトで確認。でてる。

一旦 Fork してしまえば元のソースを気にせずカスタマイズができる。検証目的でいろいろコードを修正したい場合も便利。


人気の投稿(過去 30日間)