Author Archives: Admin

iOS8ではローカル通知も許可がないと動作しなくなってしまいます。
(iOS8向けにビルドすると明示的な許可が必要になる形のため、iOS8に未対応のアプリは一応の互換性を持っている)

通知の許可は起動時やアプリを終了時に求める形になりますが、起動時はチュートリアルとかと被ってしまうので微妙なケースもあります。

そのため、アプリがバックグラウンドに行く際に通知の許可をもとめてローカル通知を実装する形を取ってみました。

以下のようなパターンになると思います。

既に通知の許可済みの状態でも

registerUserNotificationSettings

を呼ぶと、

didRegisterUserNotificationSettings

が呼ばれてしまうのため現在の状態を見て分岐する必要があります。

もっとキレイに書けるといい気がします…

SnoozeLocalNotificationは擬似的にUILocalNotificationのローカル通知でスヌーズ対応するライブラリです。

UILocalNotificationを使った通知の設定について — ios-practice 0.1 documentationでも書かれていますが、iOSのローカル通知にはスヌーズ機能はありません。

そのため、ユーザーが止める(アプリを立ち上げる)まで何度も繰り返し通知を出すことで擬似的なスヌーズを作成出来ます。
(iOS8以降ならもっと別の手段があるかも)

SnoozeLocalNotification は通知を多重登録 + スヌーズの通知のみをキャンセル機能を持ったライブラリです。

インストール

CocoaPodsからインストールできます。


pod "SnoozeLocalNotification"

使い方

擬似的なスヌーズなので、アプリをユーザーが起動したらスヌーズを解除する必要があります。

この擬似スヌーズには主に2種類の通知があります。

  • メインとなる通知
  • メインとなる通知に紐づくスヌーズ通知

メインの通知からアプリが開かれたら、関連するスヌーズはいらないのでキャンセル。同様にメインの通知が既に通知済みの状態でアプリが起動したら関連するスヌーズはキャンセルするという処理が必要になります。

これらの処理を

AppDelegate

に 書きます。

cancelSnoozeForNotification

ではその通知に関係あるスヌーズ通知(userInfoのkeyを見ます)を解除します。


- (<span class="hljs-built_in" style="color: rgb(38, 139, 210);">BOOL</span>)application:(<span class="hljs-built_in" style="color: rgb(38, 139, 210);">UIApplication</span> *) application didFinishLaunchingWithOptions:(<span class="hljs-built_in" style="color: rgb(38, 139, 210);">NSDictionary</span> *) launchOptions {
    <span class="hljs-built_in" style="color: rgb(38, 139, 210);">UILocalNotification</span> *localNotif = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
    [[SnoozeLocalNotificationCenter center] cancelSnoozeForNotification:localNotif]
    <span class="hljs-keyword" style="color: rgb(133, 153, 0);">return</span> <span class="hljs-literal">YES</span>;
}

- (<span class="hljs-keyword" style="color: rgb(133, 153, 0);">void</span>)applicationWillEnterForeground:(<span class="hljs-built_in" style="color: rgb(38, 139, 210);">UIApplication</span> *) application {
    [[SnoozeLocalNotificationCenter center] cancelUnnecessarySnooze];
}

スヌーズ通知の登録はシンプルで、メインとなるUILocalNotificationとスヌーズ感覚を指定すれば、その分だけスヌーズ登録してくれます。


<span class="hljs-comment" style="color: rgb(147, 161, 161);">// Schedule 4 notification.</span>
<span class="hljs-comment" style="color: rgb(147, 161, 161);">// fireDate -&gt; 10min -&gt; 30min -&gt; 60min</span>
<span class="hljs-built_in" style="color: rgb(38, 139, 210);">NSArray</span> *snoozeMinutes = @[@<span class="hljs-number" style="color: rgb(42, 161, 152);">10</span>, @<span class="hljs-number" style="color: rgb(42, 161, 152);">30</span>, @<span class="hljs-number" style="color: rgb(42, 161, 152);">60</span>];
<span class="hljs-built_in" style="color: rgb(38, 139, 210);">UILocalNotification</span> *localNotification = [[<span class="hljs-built_in" style="color: rgb(38, 139, 210);">UILocalNotification</span> alloc] init];
localNotification<span class="hljs-variable" style="color: rgb(181, 137, 0);">.fireDate</span> = [[<span class="hljs-built_in" style="color: rgb(38, 139, 210);">NSDate</span> date] dateByAddingTimeInterval:<span class="hljs-number" style="color: rgb(42, 161, 152);">1000</span>];
localNotification<span class="hljs-variable" style="color: rgb(181, 137, 0);">.alertBody</span> = <span class="hljs-string" style="color: rgb(42, 161, 152);">@"message"</span>;
[[SnoozeLocalNotificationCenter center] schedule:localNotification snoozeMinutes:snoozeMinutes];

API

細かいAPIはヘッダを見るといいと思います。


<span class="hljs-class"><span class="hljs-keyword" style="color: rgb(133, 153, 0);">@interface</span> <span class="hljs-title" style="color: rgb(38, 139, 210);color: rgb(181, 137, 0);">SnoozeLocalNotificationCenter</span> : <span class="hljs-title" style="color: rgb(38, 139, 210);color: rgb(181, 137, 0);">NSObject</span></span>
+ (instancetype)center;

<span class="hljs-comment" style="color: rgb(147, 161, 161);">// schedule notification and snooze</span>
- (<span class="hljs-keyword" style="color: rgb(133, 153, 0);">void</span>)schedule:(<span class="hljs-built_in" style="color: rgb(38, 139, 210);">UILocalNotification</span> *) localNotification snoozeDateComponents:(<span class="hljs-built_in" style="color: rgb(38, 139, 210);">NSArray</span> *) dateComponentsList;
- (<span class="hljs-keyword" style="color: rgb(133, 153, 0);">void</span>)schedule:(<span class="hljs-built_in" style="color: rgb(38, 139, 210);">UILocalNotification</span> *) snoozeLocalNotification snoozeMinutes:(<span class="hljs-built_in" style="color: rgb(38, 139, 210);">NSArray</span> *) snoozeMinutes;

<span class="hljs-comment" style="color: rgb(147, 161, 161);">// cancel all snooze notifications</span>
- (<span class="hljs-keyword" style="color: rgb(133, 153, 0);">void</span>)cancelAllSnooze;
<span class="hljs-comment" style="color: rgb(147, 161, 161);">// cancel unnecessary notifications.</span>
<span class="hljs-comment" style="color: rgb(147, 161, 161);">/* e.g) notificationA -&gt; snoozeX -&gt; snoozeY
 * Notify notificationA, then user launch app.
 * snoozeX and snoozeY are unnecessary notifications.
 */</span>
- (<span class="hljs-keyword" style="color: rgb(133, 153, 0);">void</span>)cancelUnnecessarySnooze;
<span class="hljs-comment" style="color: rgb(147, 161, 161);">// cancel notification and snooze</span>
- (<span class="hljs-keyword" style="color: rgb(133, 153, 0);">void</span>)cancelSnoozeForNotification:(<span class="hljs-built_in" style="color: rgb(38, 139, 210);">UILocalNotification</span> *) aNotification;
<span class="hljs-keyword" style="color: rgb(133, 153, 0);">@end</span>

SnoozeLocalNotification はスヌーズのために大量に通知を登録しますが、iOSではローカル通知に登録出来る数に上限があるのであまり数を多くすると溢れて無駄な通知登録処理をすることになるので注意して下さい。

UITextFieldWithLimit はUITextFieldのサブクラスとして使えて文字数制限が出来るライブラリです。

iOS で文字数制限つきのテキストフィールドをちゃんと作るのは難しいという話 – blog.niw.at 等を読んだ事がある方は知っていると思いますが、日本語(IME)などを含めた文字数制限を実装するのはとても面倒です。

UITextFieldWithLimit はUITextFieldのサブクラスなので、クラスを置き換えるだけでうごきます。

gif

インストール

CocoaPodsでインストールできます。


pod &quot;UITextFieldWithLimit&quot;

使い方

以下で試すことが出来ます。

pod try UITextFieldWithLimit

基本的にはUITextFieldそのままですが、

maxLength

で制限する文字数を指定することが出来ます。(これはただのlength取ってるだけなのでまた正確な文字数とは別という話…)

limitDelegate

で文字数に達した時と、文字数を超えて入力された時のメソッドも定義出来ます。


- (void)viewDidLoad {
    [super viewDidLoad];
    self.limitedTextField = [[UITextFieldWithLimit alloc] init];
    self.limitedTextField.maxLength = @15;
    // optional
    self.limitedTextField.limitDelegate = self;
}
- (void)textFieldLimit:(UITextFieldWithLimit *) textFieldLimit didReachLimitWithLastEnteredText:(NSString *) text inRange:(NSRange) range {
    NSLog(@&quot;%s&quot;, sel_getName(_cmd));
}
- (void)textFieldLimit:(UITextFieldWithLimit *) textFieldLimit didWentOverLimitWithDisallowedText:(NSString *) text inDisallowedRange:(NSRange) range {
    NSLog(@&quot;%s&quot;, sel_getName(_cmd));
}

こういう制限は標準機能として欲しい感じです。。

JonathanGurebo/UITextFieldLimitの動きを元に日本語のケースを追加した感じです。

AKUAssetManager はUIImagePickerControllerの補助的なライブラリです。

iOS6以降 写真やGPSなどアクセスするのに許可が必要なものが増えてきていて、iOS8だとローカル通知やカメラ等もアクセス許可が必要です。

写真などはアクセスが無いときの表示はiOS側がやってくれるのでいいですが、カメラの場合は真っ黒な画面で撮影からキャンセルしないと戻れないというかなり微妙な事になります。

以前、iOS6でプライバシー &gt; 写真へのアクセス許可があるかどうかを判定する方法 | Technology-Gymというので書きましたが、許可がでているかは取得することができるので、許可がない場合はアラートを出して設定画面で変更してもらう必要があります。

AKUAssetManagerはこれのカメラと写真を対象に手助けするライブラリです。

screenshot

という感じで、カメラを呼ぶときに許可があるかどうかを見てから呼び出すことができ、また設定画面へ行くように促す機能が入っています。

iOS8からアプリの設定画面へアクセス出来るようになっているので、iOS8の場合は設定へ直接誘導してくれます。

インストール

CocoaPodsでインストールできます。


pod &quot;AKUAssetManager&quot;

使い方

pod try AKUAssetManager

で試すのが早いです。

- (void)viewDidLoad {
    [super viewDidLoad];
    self.manager = [[AKUImagePickerManager alloc] init];
}
// if allow to use camera, present UIImagePickerController
- (IBAction)openCamera:(id)sender {
    [self.manager openCameraWithDelegate:self];
}
// if allow to use photo, open UIImagePickerController
- (IBAction)openPhoto:(id)sender {
    [self.manager openPhotoAlbumWithDelegate:self inView:sender];
}

AKUImagePickerManager

のインスタンスにはカメラや写真をUIImagePickerControllerで開く + 権限チェック をしてくれるメソッドがそれぞれ生えています。

また、権限チェックだけを使いたい場合はAKUCaptureManager.hAKUAssetManager.hを見るといいでしょう。

(*カメラと写真の権限はそれぞれ別となっていて、カメラの権限はiOS7以降でしか触れません)

まだ、日本語しか入ってないのでローカライズが必要な場合はpull requestするといいと思います。

OpenFileInWebView というシンプルなライブラリを書きました。

これはWebView指定したファイルパスを開くViewControllerを返すという単機能のライブラリで主にデバッグ等に使う感じです。

以下のように使えば、file.pdfというファイルを表示してるWebViewを見ることが出来ます。(WebViewはPDFやdocなども表示出るので簡単なプレビュー側がわりになります)

UIViewController *controller = [OpenFileInWebView viewControllerToOpenFile:[[NSBundle mainBundle] pathForResource:@&quot;file&quot; ofType:@&quot;pdf&quot;]];
[self.navigationController pushViewController:controller animated:YES];

使用例としては、PDFを生成してメールで送るような機能をつけるときにシミュレータ上でPDFを直接確認する用途などに使えます。

system(&quot;open ~&quot;)

でMac OSXの方で開いてもいいのですが、Releaseビルドに残ってたりすると面倒なので、WebViewで開いてます。