ラベル iOS 6.1.3 の投稿を表示しています。 すべての投稿を表示
ラベル iOS 6.1.3 の投稿を表示しています。 すべての投稿を表示

2015年2月2日月曜日

YouTube などのフルスクリーンで再生される UIWebView の動画をプログラムから終了させる方法

UIWebViewでyoutube.comやvine.coなどのサイトの動画を開いた場合、動画がフルスクリーンの専用動画プレイヤーViewControllerで再生されることがあります。このフルスクリーン動画を閉じる方法です。

方法1: 安全な方法

JavaScriptのFullscreen APIを使って安全に閉じることができます。iOS 6以上で動作確認済みです。

Fullscreen APIについては以下の資料が詳しいです。


方法2: animated, completionの制御もしたい場合

ここからがお待ちかねの黒魔法になります。
先ほどのJavaScriptを使った方法ではフルスクリーン動画を閉じる際のアニメーションを制御できません(必ずアニメーションが発生します)。またフルスクリーン動画を閉じ終わったタイミングのcompletion handlerが存在しません(JavaScriptでハンドルしようにも、videoタグのonendedイベントは通常の再生終了時にもイベントが飛ぶ上にアニメーション終了時ではなく再生終了時にイベントが飛ぶため使いづらい)。

通常はこれらが問題になることはまずありませんし、万一あったとしても仕様のほうを変えるほうが適切ですが、何らかの止むに止まれぬ理由によりなんとかしなければならなくなる場合が稀によくあります。

そこで画面上にUIWebView経由で表示されている動画プレイヤーViewControllerをビュー階層をたどって見つけ出し無理矢理dismissViewController:completion:で消すという方法を取ります。iOS 6以上にて動作確認済みです。ただしiOS 7以下の場合とiOS 8の場合で全く構造が異なり、将来にわたって動作するか非常に怪しいです。

Private APIの名前がバリバリ含まれるコードになっておりますので審査に出すアプリには使用しないことを強くおすすめさせていただきます。

2014年9月8日月曜日

Apple Push Notification で送られてきた通知を通知センターから消すたった一つの方法

いや、それがたった一つしか方法ないのはさすがにどうかと思うのですが。

皆さんもiOSのアプリを使っていて、通知を受け取った後にアプリを開いても通知センターから通知が消えず地味にイライラする現象に見舞われたことがあるのではないかと思います。私もお恥ずかしながらつい最近知ったのですが、実はこれアプリ側で何も対応しないと通知センターから通知は消えません。それどころかなんと通知センターからプッシュ通知を消すためのAPIも一切提供されていません。つまり普通にアプリを作ると通知センターから通知が消えないのです。Appleふざけんな

※注記: ここで私が消せない通知と呼んでいるものはApple Push Notification経由でのRemote Notiifcationです。UILocalNotificationは自由自在にアプリ側から消せるので、適切なタイミングで消してないのは単なる実装者の怠慢となります。

しかしながらTwitterやFacebookアプリはきちんとアプリが開かれたタイミングで通知センターから通知が消えるようになっています。不思議です。そこでちょっと調べてみました。

解決策

以下のリンク先にあるように、一度applicationIconBadgeNumberを1以上の数字に設定してから、0に戻すと、全てのApple Push Notification経由での通知が消えるようです。

http://stackoverflow.com/questions/8682051/ios-application-how-to-clear-notifications
http://stackoverflow.com/questions/9925854/remove-single-remote-notification-from-notification-center

iOS 5~8のすべてのバージョンで動作することを確認しています。少々ダサいですがこの方法でしのいでいきましょう!

※注記: 個別に通知を消す方法はありません。なおiOS 8より、ユーザーが直接通知センターから通知をタップしてアプリが起動した場合のみ、タップされた通知が自動的に消えるように仕様が変更されています。相変わらずアプリから明示的に消すことはできません。Appleふざけんな

2014年8月12日火曜日

Apple Push Notification (APN) 使用時の delegate の挙動について、 iOS 7以降 / iOS 6以前の差をまとめた

iOS 7以降とiOS 6以前で、俗にいうリモートPush通知の受け取り方と受け取った際の挙動がまるで違っているので、最近リモートPush通知を実装した時につまづいた箇所をまとめてみました。

使用するdelegate methodの違い

iOS 7以降


  • いかなる種類のPush通知においてもapplication:didReceiveRemoteNotification:fetchCompletionHandler:を使用します。
  • application:didReceiveRemoteNotification:fetchCompletionHandler:とapplication:didReceiveRemoteNotification:が両方実装されている場合も、application:didReceiveRemoteNotification:fetchCompletionHandler:しか呼び出されません。application:didReceiveRemoteNotification:fetchCompletionHandler:のみ考えればOKです。
  • ただし、application:didReceiveRemoteNotification:が実装されており、application:didReceiveRemoteNotification:fetchCompletionHandler:が存在しない場合は、iOS 6以前同様の挙動になります。
  • 以下、長ったらしいのでapplication:didReceiveRemoteNotification:fetchCompletionHandler:を新メソッドと呼称します。

iOS 6以前


  • application:didReceiveRemoteNotification:を使用します。
  • 新メソッドが実装してあっても一切反応しません。
  • 以下、長ったらしいのでapplication:didReceiveRemoteNotification:を旧メソッドと呼称します。

挙動の違いまとめ

以下に表にしてまとめました。ここで、
iOS 7以降とは新メソッドを使用する場合、iOS 6以前とは旧メソッドを使用する場合のことを指します。


iOS 6以前iOS 7以降
初回起動application:didFinishLaunchingWithOptions:に
UIApplicationLaunchOptionsRemoteNotificationKeyが付いて呼び出される。
旧メソッドは呼び出されない。
application:didFinishLaunchingWithOptions:に
UIApplicationLaunchOptionsRemoteNotificationKeyが付いて呼び出される。
その後、UIApplicationStateActiveの状態で、新メソッドも呼び出される。
handlerについては、content-available指定が1の場合のみ付いてくる(未確認)。
起動中UIApplicationStateActiveの状態で、旧メソッドが呼び出される。UIApplicationStateActiveの状態で、新メソッドが呼び出される。
handlerについては、content-available指定が1の場合のみ付いてくる(未確認)。
未起動UIApplicationStateInactiveの状態で、旧メソッドが呼び出される。UIApplicationStateInactiveの状態で、新メソッドが呼び出される。
handlerについては、content-available指定が1の場合のみ付いてくる(未確認)。
データ取得不可能。UIApplicationStateBackgroundの状態で、新メソッドが呼び出される。
handlerは必ず付いてくる。
この通知がアラートや通知センターに表示された場合、ユーザーがそれらとインタラクションしたらさらにもう一回UIApplicationStateInactiveの状態で新メソッドが呼び出される。

バックグラウンドでの通知受け取りとデータ取得

iOS 7から、Push通知のペイロードのapsオブジェクトにcontent-availableというプロパティが新たに追加されました。content-availableプロパティの値を数字の1に設定すると、ユーザーのインタラクションを介さずにバックグラウンドでアプリケーションが起き上がることができ、サーバから最新のデータの取得を行ったりする事が可能です。詳しくは「Push通知 バックグラウンド iOS7 content-available」とかその辺の単語を適当に組み合わせてググってください。山ほど解説しているページがあります。

サイレント通知が届かないときの処方箋

さて、上記バックグラウンドでの通知受け取りとデータ取得について、余り解説が見当たらなかった点についてまとめました。

iOS 7以降のPush通知は、apsオブジェクトのalertプロパティの有無とcontent-availableプロパティの有無によって挙動が変わります。具体的には以下の表のようになります。

content-availableがあるcontent-availableがない
alertがある通常通知
同時にバックグラウンドデータ取得も可能
通常通知
alertがないサイレント通知
バックグラウンドデータ取得のみ可能
送信できない

content-availableがない場合はiOS 6以前のPush通知と全く同じなので説明を割愛します。次にcontent-availableとalert両方が指定されている場合ですが、これはiOS 6以前の通常通知と基本は同じで、異なる点は通知を受け取った瞬間にバックグラウンドでアプリが立ち上がってデータの取得ができるという点だけです。したがいましてちょっと上記に記載しましたが、通知を受け取った瞬間と、ユーザーがインタラクションした瞬間で2回新メソッドが呼び出されるので、必ず新メソッドの中でUIApplicationStateを見て処理を分岐するようにしてください。

問題になってくるのがcontent-availableだけが指定されている通知、サイレント通知についてです。ほかのサイトでもよく紹介されていますが、この通知を使ってバックグラウンドでアプリを起動させて最新のデータをダウンロードしたりすることができます。以下にサイレント通知のペイロードの例を示します。

上が通常のサイレント通知ですが、下のようにサイレント通知と同時にバッジの数を更新することもできます。

さてこのサイレント通知ですが、とにかく「届かない!」というトラブルを耳にします。実際私が試した際にもなかなか届かないで弱りました。そこで調査したところ、サイレント通知のペイロードを工夫することできちんと通知が到達するようになりました。以下に到達率を改善したサイレント通知のペイロードの例を示します。

ご覧のように、空のsound要素を追加することで到達率が劇的に向上します。また2つ目の例のように、priorityというプロパティを10に設定するとこれまた到達率が劇的に向上します。どうやらこのpriorityプロパティはalertプロパティかsoundプロパティが設定されている場合はデフォルト10に、設定されていないサイレント通知の場合はデフォルト5に設定されているようで、それが原因で到達しない事が多いということがわかりました。
参考記事: http://stackoverflow.com/questions/19239737/silent-push-notification-in-ios-7-does-not-work

それから、Xcode上からプロジェクトのCapabilitiesの設定をするとき、Background fetchとRemote notificationsの両方の指定がおそらく必須と思われます。中にはRemote notificationsだけ設定しておけば良いと解説しているところもありますが、どうやらRemote notificationsは通知を受け取る箇所までだけで、肝心のバックグラウンドでのデータの取得はBackground fetchがないと実行できないのではないかと思われます。

2013年3月24日日曜日

Unity でソースコードに書いたリテラル文字列の文字コードを調べてみた

Unity3.5.6においてソースコード上にリテラル文字列として日本語を置いた時、時々ログ出力が上手くいかないことがあるということがわかり、ちょっと調べてみました。するとリテラル文字列を置いたソースコードの種類に応じて文字コードが違うということがわかりました。
  • C# (.cs) はUTF-16
  • JS (.js) はUTF-8
  • Boo (.boo) は不明
  • ネイティブのiOSはUTF-8
また出力する側、要するにDebug.Logメソッドは基本的にUTF-8のみを受け付けられるようになっているということがわかりました。まとめると以下の図のようになります。



最終的にJSファイルに全ての日本語リテラルをまとめて回避しましたが、そもそも日本語リテラルは使わず国際化文言ファイルみたいなものを別途用意しておいたほうが筋が良いと思います。iOSならLocalizable.stringsがありますしね。