Tag Archives: Ios

Objective-CではURLエンコード/デコードをするのがちょっと面倒です。

JavaScriptのencodeURIComponent()と同じように使えるC関数を提供するライブラリを書きました

使い方は単純です。

AZEncodeURIComponent(@"日本語"); // -> "%E6%97%A5%E6%9C%AC%E8%AA%9E"
AZDecodeURIComponent(@"%E6%97%A5%E6%9C%AC%E8%AA%9E"); // 日本語

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

pod "AZEncodeURIComponent"

単純なライブラリですが、何故かこれだけを提供するものがなかったので書きました。。

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

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

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

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

既に通知の許可済みの状態でもregisterUserNotificationSettingsを呼ぶと、didRegisterUserNotificationSettingsが呼ばれてしまうのため現在の状態を見て分岐する必要があります。

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

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

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

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

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

インストール

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

pod "SnoozeLocalNotification"

使い方

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

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

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

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

これらの処理を AppDelegate に 書きます。

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

- (BOOL)application:(UIApplication *) application didFinishLaunchingWithOptions:(NSDictionary *) launchOptions {
    UILocalNotification *localNotif = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
    [[SnoozeLocalNotificationCenter center] cancelSnoozeForNotification:localNotif]
    return YES;
}

- (void)applicationWillEnterForeground:(UIApplication *) application {
    [[SnoozeLocalNotificationCenter center] cancelUnnecessarySnooze];
}

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

// Schedule 4 notification.
// fireDate -> 10min -> 30min -> 60min
NSArray *snoozeMinutes = @[@10, @30, @60];
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.fireDate = [[NSDate date] dateByAddingTimeInterval:1000];
localNotification.alertBody = @"message";
[[SnoozeLocalNotificationCenter center] schedule:localNotification snoozeMinutes:snoozeMinutes];

API

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

@interface SnoozeLocalNotificationCenter : NSObject
+ (instancetype)center;

// schedule notification and snooze
- (void)schedule:(UILocalNotification *) localNotification snoozeDateComponents:(NSArray *) dateComponentsList;
- (void)schedule:(UILocalNotification *) snoozeLocalNotification snoozeMinutes:(NSArray *) snoozeMinutes;

// cancel all snooze notifications
- (void)cancelAllSnooze;
// cancel unnecessary notifications.
/* e.g) notificationA -> snoozeX -> snoozeY
 * Notify notificationA, then user launch app.
 * snoozeX and snoozeY are unnecessary notifications.
 */
- (void)cancelUnnecessarySnooze;
// cancel notification and snooze
- (void)cancelSnoozeForNotification:(UILocalNotification *) aNotification;
@end

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

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

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

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

gif

インストール

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

pod "UITextFieldWithLimit"

使い方

以下で試すことが出来ます。 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(@"%s", sel_getName(_cmd));
}
- (void)textFieldLimit:(UITextFieldWithLimit *) textFieldLimit didWentOverLimitWithDisallowedText:(NSString *) text inDisallowedRange:(NSRange) range {
    NSLog(@"%s", sel_getName(_cmd));
}

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

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

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

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

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

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

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

screenshot

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

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

インストール

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

pod "AKUAssetManager"

使い方

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するといいと思います。