アプリ【StackOne】最新バージョン 1.4 リリース〜 iPad対応

2014年7月11日金曜日 | Published in | 0 コメント

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

エバーノート投稿アプリ【StackOne】の最新バージョン 1.4 がリリースされました。




今回のリリースから iPadでも使えるようになりました。








投稿後のノートはこんな感じで見えます(エバーノートiPad版)。


紹介記事など






- - - -
iPad対応記念セール実施中です。
400円→100円

興味のある方はこの機会にどうぞ。


UITableView 初期位置設定 in UINavigationController (iOS7)

2014年7月5日土曜日 | Published in | 0 コメント

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

極私的メモ。UINavigationControllerで遷移してきた画面でUITableViewを表示する時、任意の位置のセルで初期表示したい。
- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self.tableView reloadData];
    [self _scrollAnimated:NO];          // 任意のセルへスクロール
}
viewWillAppearに書いてもだめ(iOS7)。viewDidAppearだと画面がちらつく。

viewDidLayoutSubviewsに書いたらうまくいった。ただしフラグを使って初回だけに限定してやる。
@property (nonatomic, assign) BOOL didLayoutSubviews;

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    if (!self.didLayoutSubviews) {
        [self _scrollAnimated:NO];
        self.didLayoutSubviews = YES;
    }
}



アプリ【StackOne】最新バージョン 1.3 リリース(プロモーション動画作ってみました)

2014年6月13日金曜日 | Published in | 0 コメント

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

エバーノート投稿アプリ【StackOne】の最新バージョン 1.3 がリリースされました。





主な新機能
・TextExpander
・ルール並び替え
・ルールリスト
ほか


今回はプロモーション動画を作ってみた。

素人感丸出し。。当初は手に持って実際に操作しているところを撮影しようかと思い、他のiPhoneで撮影するなどやってみたがこれは手間がかかりすぎるため断念。今回はスクリーンだけを写す簡易な方法を撮った。最近はスクリーンだけのプロモ動画は少なく、数年前の古さを感じさせる雰囲気あり。


使ったアプリは2つだけ。

[1]Reflector - iPhoneの画面をAirPlay経由でMacに移すアプリ。$12.99で購入。

これは結構役にたった。なおYosemite+iOS8では標準で同等のことができるようになるらしいが。

[2]iMovie - 動画の編集

iMovieは初めての操作だったけど、今回の動画は3〜4時間ぐらいでできたと思う。操作する時はあまり細かい事は考えずにとにかく必要な場面(シーン)をたくさん撮るのがコツで、最後にiMovieで切り貼り編集して見られる動画にまとめていくという流れ。一度場面が揃えばあとは編集で(ある程度)どうにでもなるので一旦慣れてしまえば意外に面倒さはなかった。心がけたのは1分に収まり、映像がテンポよく流れていくところ。

BGMはここのものを使わせてもらった。

アプリは軽快さが売りの一つなので、そのイメージに合う曲を探して充ててみた。

- - - -
今後AppStoreでは紹介動画の掲載も可能になるということなので動画制作にも徐々に慣れた方が良さそう。

【Library】写真のグルーピングやフィルタリングが可能な ALAssetsLibrary用ライブラリ

2014年5月31日土曜日 | Published in | 0 コメント

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

ALAassetsLibraryから取得した写真を月ごとにグルーピングしたり、JPEGだけあるいはスクリーンショットだけを抜き出すといった処理が行えるライブラリを作りました。このライブラリではモデルクラスのみを提供しています(ビューはありません)。



基本


クラス図はこんな感じ。

左の3クラス(LKAssetsLibrary,LKAssetsGroup,LKAsset)がALAssetsLibraryの主要クラスのラッパーとなっていて便利メソッドが用意されている。

LKAssetの定義
@interface LKAsset : NSObject

// Properties (Status)
@property (assign, nonatomic, readonly) BOOL deleted;

// Properties (Image)
@property (weak  , nonatomic, readonly) UIImage* thumbnail;
@property (weak  , nonatomic, readonly) UIImage* aspectRatioThumbnail;
@property (weak  , nonatomic, readonly) UIImage* fullScreenImage;
@property (weak  , nonatomic, readonly) UIImage* fullResolutionImage;

// Properties (Date number)
@property (assign, nonatomic, readonly) NSTimeInterval timeInterval;
@property (assign, nonatomic, readonly) NSInteger dateTimeInteger;  // yyyyMMddHH

// Properties (ALAsset property)
@property (strong, nonatomic, readonly) NSURL* url;
@property (strong, nonatomic, readonly) CLLocation* location;
@property (strong, nonatomic, readonly) NSDate* date;
@property (strong, nonatomic, readonly) NSString* fileExtension;    // upper string JPG, PNG, ...
@property (assign, nonatomic, readonly) CGSize size;
@property (assign, nonatomic, readonly) LKAssetType type;

// Properties (Filter)
@property (assign, nonatomic, readonly) BOOL isJPEG;
@property (assign, nonatomic, readonly) BOOL isPNG;
@property (assign, nonatomic, readonly) BOOL isScreenshot;
@property (assign, nonatomic, readonly) BOOL isPhoto;
@property (assign, nonatomic, readonly) BOOL isVideo;
  :
ちなみに fullScreenImageとfullResolutionImageは回転している写真を適正に補正してくれる。

使い方の例。
- (void)viewDidLoad
{
    [super viewDidLoad];
    
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(_assetsLibraryDidSetup:)
                                                 name:LKAssetsLibraryDidSetupNotification
                                               object:nil];
    self.assetsLibrary = [LKAssetsLibrary assetsLibrary];
    [self.assetsLibrary reload];
}

ALAssetsLibraryと異なり blocksではなく通知ベースで使う。アセットの取得が済むと LKAssetsLibraryDidSetupNotification が通知されるので、後は LKAssetsGroupとLKAssetを取り出して使っていく。

- (void)_assetsLibraryDidSetup:(NSNotification*)notification
{
    [self.tableView reloadData];
}

グループのタイトルとサムネイルをテーブルのセルへ表示する例
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"GroupCell" forIndexPath:indexPath];

    LKAssetsGroup* assetsGroup = self.assetsLibrary.assetsGroups[indexPath.row];   
    cell.imageView.image = assetsGroup.posterImage;
    cell.textLabel.text = assetsGroup.description;
    return cell;
}

アセット(画像)のサムネイルをUICollectionViewCellへ表示する例
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    AssetCell* cell = (AssetCell*)[collectionView dequeueReusableCellWithReuseIdentifier:@"AssetCell"
                                                                           forIndexPath:indexPath];
    LKAsset* asset = [self.assetsCollection assetForIndexPath:indexPath];
    cell.imageView.image = asset.thumbnail;
    return cell;
}


コレクション


さてこのライブラリの売りは基本で紹介したクラスを利用して作られた LKAssetsCollectionにある。LKAssetsCollectionは様々な条件を満たすアセットの集合を取り扱うことができる。例えばLKAssetsCollectionを使うと「月ごと」に分類された 「JPEGとPNG画像」を「日付降順でソート」したアセットの配列を作ることができる。

LKAssetsCollection
   .entries
   |
   |--LKAassetsCollectionEntry(2014年5月)
   |   .assets
   |    |
   |    |--LKAsset(JPEG画像 2014/05/21 10:00)
   |    |--LKAsset(PNG 画像 2014/05/11 10:00)
   |    |--LKAsset(JPEG画像 2014/05/01 10:00)
   |    :
   |--LKAassetsCollectionEntry(2014年4月)
   |   .assets
   |    |
   |    |--LKAsset(JPEG画像 2014/04/21 10:00)
   |    |--LKAsset(PNG 画像 2014/04/11 10:00)
   |    |--LKAsset(JPEG画像 2014/04/01 10:00)
   |    :
   :

LKAssetsCollectionの配下には LKAssetsCollectionEntryの配列があり、これがさらに LKAssetsの配列を持つ。

一旦コレクションが作れれば後は indexPathで末端の LKAssetを取り出せるので UITableView や UICollectionView で簡単に使うことができる。
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    AssetCell* cell = (AssetCell*)[collectionView dequeueReusableCellWithReuseIdentifier:@"AssetCell"
                                                                           forIndexPath:indexPath];
    LKAsset* asset = [self.assetsCollection assetForIndexPath:indexPath];
    cell.imageView.image = asset.thumbnail;
    return cell;
}

LKAssetsCollectionのインターフェイス。
@interface LKAssetsCollection : NSObject

@property (nonatomic, weak  , readonly) LKAssetsGroup* group;
@property (nonatomic, strong, readonly) NSArray* entries;   // 

@property (nonatomic, strong, readonly) id <LKAssetsCollectionGrouping> grouping;
@property (nonatomic, strong) id <LKAssetsCollectionFilter> filter;
@property (nonatomic, strong) id <LKAssetsCollectionSorter> sorter;

+ (instancetype)assetsCollectionWithGroup:(LKAssetsGroup*)group grouping:(id <LKAssetsCollectionGrouping>)grouping;

@end

@class LKAsset;
@interface LKAssetsCollection (NSIndexPath)
- (LKAsset*)assetForIndexPath:(NSIndexPath*)indexPath;
@end

コレクションに含めるアセットを決める条件は3種類指定することができる。

・グルーピング  (例)年ごと、月ごと、日ごと、時ごと
・フィルター   (例)JPEGのみ、PNGのみ、スクリーンショットのみ、ほか
・ソーター    (例)日付昇順・降順

上記の処理を定義するためにそれぞれプロトコルが定義されている。
 LKAssetsCollectionGrouping
 LKAssetsCollectionFilter
 LKAssetsCollectionSorter
これらを実装するインスタンスを LKAssetsCollectionへ渡すことで条件に合致するアセットの集合ができあがる。

初期化の例
LKAssetsCollectionGrouping* grouping = [LKAssetsCollectionGrouping assetsCollectionGroupingWithType:self.groupingType];
   
self.assetsCollection = [LKAssetsCollection assetsCollectionWithGroup:self.assetsGroup
                          grouping:[LKAssetsCollectionDateGrouping groupingWithType:self.groupingType]];
self.assetsCollection.filter = [LKAssetsCollectionGenericFilter filterWithType:LKAssetsCollectionGenericFilterTypeJPEG|LKAssetsCollectionGenericFilterTypePNG];
self.assetsCollection.sorter = [LKAssetsCollectionDateSorter sorterAscending:NO];

LKAssetsCollectionDateGrouping や LKAssetsCollectionGenericFilter、LKAssetsCollectionDateSorter は先程のプロトコルの実装クラスになる。これらは標準で提供される。なおグルーピングと、ソースとなる LKAssetsGroupは初期設定しかできず後からは変更できない(immutable)。一方、フィルターとソーターは後から変更を適用することが可能。

LKAssetsCollectionDateGrouping で指定可能なグルーピング単位
typedef NS_ENUM(NSInteger, LKAssetsCollectionGroupingType) {
    LKAssetsCollectionGroupingTypeAll      = 0,
    LKAssetsCollectionGroupingTypeYearly   = 11,          // 年ごと
    LKAssetsCollectionGroupingTypeMonthly  = 12,          // 月ごと
    LKAssetsCollectionGroupingTypeWeekly   = 13,          // 週ごと(月曜始まり〜日曜までを1週間とみなす)
    LKAssetsCollectionGroupingTypeDaily    = 14,          // 日ごと
    LKAssetsCollectionGroupingTypeHourly   = 15,          // 時ごと
};

LKAssetsCollectionGenericFilter で指定可能なフィルタ条件。ビットORで複数指定が可能。
typedef NS_ENUM(NSUInteger, LKAssetsCollectionFilterType) {
    LKAssetsCollectionFilterTypePhoto        = (1 << 0),
    LKAssetsCollectionFilterTypeVideo        = (1 << 1),
    LKAssetsCollectionFilterTypeJPEG         = (1 << 2),
    LKAssetsCollectionFilterTypePNG          = (1 << 3),
    LKAssetsCollectionFilterTypeScreenShot   = (1 << 4),
    LKAssetsCollectionFilterTypeAll          = 0xFFFFFFFF,
};

コレクションクラスは、グルーピング・フィルター・ソーターの各プロトコルに準拠したインスタンスを利用するので、標準以外の処理を行わせたい場合はプロトコルに適合したクラスを用意すればいい(もちろん標準提供のこれらのクラスをサブクラス化しても良い)。


その他

グループやアセットの変更通知

グループやアセットが追加されたり、変更や削除された場合、LKAssetsLibraryはそれを検出して内部で持っているグループをそれに合わせて更新する。アセットの増減があった場合はコレクションにも変更が自動的に反映される。

また処理後に独自の通知も出す。
// Notifications (Update)
// store updated group into userInfo[LKAssetsLibraryGroupsKey]
extern NSString * const LKAssetsLibraryDidInsertGroupsNotification;
extern NSString * const LKAssetsLibraryDidUpdateGroupsNotification;
extern NSString * const LKAssetsLibraryDidDeleteGroupsNotification;

// Notifications (keys)
extern NSString * const LKAssetsLibraryGroupsKey;

NSNotificationの userInfoから LKAssetsLibraryGroupsKeyをキーにして追加・変更のあった(あるいは削除された)LKAssetsGroupインスタンスを取得できる。

- (void)_assetsLibraryDidInsertGroup:(NSNotification*)notification
{
    NSArray* groups = notification.userInfo[LKAssetsLibraryGroupsKey];
    NSLog(@"%s|inserted: %@", __PRETTY_FUNCTION__, groups);
}

iCloud上の写真

iCloud上で作成した共有アルバムの写真は、サムネイルは入手できるものの実体画像は取得できない場合がある。これは明示的にダウンロードする必要がある為。標準の写真アプリで閲覧(ダウンロード)してある場合はそのキャッシュが使える(表示できる)。最初のバージョンではこのケースに対応できていない(デモプログラムでもその場合、サムネイルは表示されても詳細が表示できない)。フォトストリームは自動的にダウンロードされるので大丈夫なようだ。



デモプログラム


付属のデモプログラムを実行すると最初にグループの一覧が表示される。
これは LKAssetsLibrary.assetsGroup(NSArray)を並べたもの。

グループを1つ選んでタップすると中の写真が一覧される。
グルーピング(ALL | Year | Month | Week | Day | Hour)、並び順(Ascending | Descending)、種別(All | JPEG | Screen | Video)が選べて、その場で表示が変わる。ここで先ほど説明した LKAssetsCollection を使っている。

なおビデオはサムネイルは表示されるが、詳細では表示されない(ビデオ再生を実装していないだけ)。


その他


グルーピングの Weeklyの処理はいまいちイケていないので少し遅い。大量の写真がある場合、デモでも他のグループに比べて一瞬遅れるのが分かるかもしれない。


インストール


CocoaPodsを使う。

$ pod install LKAssetsLibrary

今までプラベートなリポジトリしか使っていなかったので CocoaPodsの公式へ登録するのは初めて。先日公開された Trunkを早速使った。この辺りは下記のブログが参考になった。

CocoaPods Trunkを利用したライブラリの追加方法


ライセンス


MIT です。基本好きに使って下さい。


- - - -
ようやくモデルができた。次は選択画面を作る。これは複数選択可能なイメージピッカーみたいなやつになる予定。

Evernote投稿アプリStackOneの無料版が出ました(広告なし!)

2014年5月21日水曜日 | Published in | 0 コメント

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

StackOneの無料版が出ました。

広告はなしの完全無料です。
有料版との違い
・ルールの追加ができません
・フォント種類/サイズ設定ができません
・投稿画面のカラー設定ができません

それ以外は有料版とまったく同じ。「サクサク投稿」はもちろん、約40種類の予約語が使えるヘッダ・フッタのカスタマイズ、1日分の投稿まとめなどStackOneならではの機能が健在です。

有料版紹介ページ


有料版レビューより
「動作が可愛い」
「衝撃がデカイです」
「手放せません」
「サクサクです」
「ホームに置いて活躍しています」
「使いやすい」
「素晴らしいアップデート」
「最高です」
「日記を残すのが楽しくなるアプリ」


新感覚のエバーノート投稿アプリを是非お試し下さい。
(サクサクですよ)

Static cell を隠す

2014年5月13日火曜日 | Published in | 0 コメント

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

Storyboardで作った Staticな Cellを実行時に消には UITableViewDelegateの2つのメソッドを使う。

例)セクション[2]を隠す

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.section == 2) {    // セクション[2]内おセルをすべて非表示
        cell.hidden = YES;
    }
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.section == 2) {    // セクション[2]内のセルの高さをすべて0に
        return 0;
    } else {
        return [super tableView:tableView heightForRowAtIndexPath:indexPath];
    }
}


高さを0にするだけだと下のセルに重なって表示されてしまうので合わせてhiddenにもしておく。これはクリッピングすれば不要かもしれない。


追記)セクションヘッダがある場合はこれも高さ0にしてやる。
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    if (section == 2) {
        return 0;
    } else {
        return [super tableView:tableView heightForHeaderInSection:section];
    }
}

【Library】Task Completion ライブラリ

2014年4月29日火曜日 | Published in | 0 コメント

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

Task Completionが数行で使える小さなライブラリ。



Task Completion や実装については下記を参考のこと。


iOS7からは10分→3分と短くなっている。本格的な処理はiOS7から導入されたバックグラウンド送受信を使い、こちらはそこまで大げさではないちょっとした処理の延長で使う位置づけになったのだと思う。

使い方は簡単で基本1行のみ。AppDelegateの中で setupメソッドを1回呼ぶ。
#import "LKTaskCompletion.h"

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [LKTaskCompletion.sharedInstance setup];
    :
}

バックグラウンドのタスクが終了した時はできれば下記を呼んでおく。
[LKTaskCompletion.sharedInstance endBackgroundTask];

バックグラウンド処理を走らせたくないときは enabledプロパティを NOにしておく。
LKTaskCompletion.sharedInstance.enabled = NO;


サンプルプログラムがついていて、実行すると1秒単位で1行を消しこむ処理が走る。

残件がバッジに表示されていて、ホーム画面へ戻っても処理がバックグラウンドで行われているのがわかるようになっている。


CocoaPods対応 & MITライセンス





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