Hatena::ブログ(Diary)

Over&Out その後 このページをアンテナに追加 RSSフィード Twitter

2015-07-14

watchOS 2 の Core Graphics は何ができて何ができないのか #potatotips

本日開催された、「【第19回】potatotips」にて、watchOS 2 における Core Graphics について発表をさせていただきました。



概要

watchOS 1 では Core Graphics が使えなかったので、次のような UI を実現するにあたって、


f:id:shu223:20150214162301p:image:w600


Appleのサンプル「Lister」では、360個の連番pngを使用するという相当な力技でやっていたことは記憶に新しいところです。


f:id:shu223:20150714210052p:image:w425


で、watchOS 2 でついに Core Graphics が使えるようになったわけですが、発表されて1ヶ月経過した今も、(自分の探し方が悪いのか、)10本ぐらいあるAppleのWWDC動画にもサンプルにも、GitHub や StackOverflow にも未だに具体的なコードは見当たりませんでした


単にまだ誰も書いてないというだけかもしれないし、何か重要なクラスが watchOS 上で使えないとかで結局使えないのかもしれない、実際に試してみないとわからない・・・ということで何ができて何ができないのか、検証コードを書いて確認してみました。


ポイント

詳細はスライドを見ていただくとして、ここではポイントだけ。

  • watchOS では UIGraphicsGetCurrentContext() でグラフィックス コンテキストを取得できない
  • ビットマップコンテキストを作成し、そこに描画 → ビットマップコンテキストから UIImage を生成して WKInterfaceImage や WKInterfaceGroup に表示すればOK

(2015.7.21 修正)@hoppenichu 様よりご指摘&Pull Request をいただき、上記に間違いがあることがわかりました。(打ち消し線を入れた箇所)


UIGraphicsBeginImageContext() すれば、 UIGraphicsGetCurrentContext() でグラフィックス コンテキストを取得できます


また僕の当初のサンプルでは UIGraphicsBeginImageContext() したあとさらに CGBitmapContextCreate() でビットマップコンテキストを生成していましたが、Beginの時点で既に生成されているので、このステップは必要ないということになります。


// Create a graphics context
let size = CGSizeMake(100, 100)
UIGraphicsBeginImageContext(size)
let context = UIGraphicsGetCurrentContext()

// Setup for the path appearance
CGContextSetStrokeColorWithColor(context, UIColor.whiteColor().CGColor)
CGContextSetLineWidth(context, 4.0)

// Draw lines
CGContextBeginPath (context);
CGContextMoveToPoint(context, 0, 0);
CGContextAddLineToPoint(context, 100, 100);
CGContextMoveToPoint(context, 0, 100);
CGContextAddLineToPoint(context, 100, 0);
CGContextStrokePath(context);

// Convert to UIImage
let cgimage = CGBitmapContextCreateImage(context);
let uiimage = UIImage(CGImage: cgimage!)

// End the graphics context
UIGraphicsEndImageContext()

image.setImage(uiimage)

こちらの commit を見ると、間違いをどう修正していただいたかわかります。(修正ここまで)


うまくいったこと
  • CGPath を用いたパス描画
  • UIBezierPath を用いたパス描画
  • SVG ファイルからのパス描画
  • グラデーション描画

(描画の話なのに、NDAにつきまだスクショ貼れないのが残念。。)


watchOS の Core Graphics でできないこと

グラフィックスコンテキストの問題が解決できたことで、結構いろいろなことができることがわかったわけですが、基本的に CALayer や UIView の API を使う必要がある処理は実現できません。スライドでは例として下記3つを挙げています。

  • スクリーンキャプチャ
    • UIView または CALayer の内容をコンテキストに描画する必要がある
  • キーパスアニメーション
    • AddAnimation: しようにも CALayer オブジェクトにアクセスできない
  • 手書き
    • タッチイベントを取るAPIがまだ開放されていない

キーパスアニメーションができないということは、冒頭に挙げたような Circular Progress 的な UI の実現も結局難しい、ということになりますね。。(アニメーションの各フレームを UIImage として生成することもできますが、非現実的な方法かと)


おわりに

最初 UIGraphicsGetCurrentContext() でグラフィックスコンテキストが取得できず、ググってもあまり情報が見当たらなかったときは「もしかして何もできないのか・・・!?」と思いましたが、上述のようにそこは解決して、パス描画やグラデーション描画などなど、結構いろんなことができました。


ただ最後に書いたように、タッチイベントが取れない、キーパスアニメーションが使えない、という大きな制約もまだあるので、「Apple製アプリではできてサードパーティにはできない」ことというのは色々とありそうです。手書きの筆跡を共有する 「Digital Touch」的なこととか、Circular Progress的なUIを動的に描画するとか。



2015-07-02

WWDC15のチケットは外れたけどサンフランシスコに行ってきたメモ

LTするまでがWWDC、ということで諸々の勉強会が終わってからブログ書こうと思っていたら7月に入ってしまい、完全にタイミングを逸してしまいました。。が、昨年のも書いといてよかったと思うので、やっぱり書いておこうと思います。


タイトルの通り、今年はWWDCのチケットは残念ながら外れてしまったのですが、初参加した昨年を振り返ってみると

  • セッションを(英語力と理解力と集中力の点で)リアルタイムで理解できない
  • → セッション中にドキュメント等で勉強しようとする
  • → セッション会場は(椅子間の距離が)狭いし、ネットも遅い
  • 1Fのネットコーナーに入り浸る

という、日本でもできる、というかむしろ日本での方が快適にできる過ごし方をしていたわけで、良かったのは何よりもその「iOSエンジニアの祭典」的空気感なわけで、じゃあチケットなくても行く価値はあるかも、ということで航空券購入に踏み切りました。旅程は 6/6 〜 6/15 の 8泊10日。


やったこと

ヨセミテ旅行

WWDCが始まる前の土日を利用して、iOSエンジニア4人でヨセミテへ行ってきました。シリコンバレーには何度か来ているものの、なかなか観光をする機会がなかったので、今回が初。


空港で集合し、レンタカーでそのままヨセミテ方面へ。宿はヨセミテ内ではなく、そこから車で1時間ぐらいの、別荘地的なところ。


f:id:shu223:20150703093037j:image:w600

f:id:shu223:20150703093034j:image:w600


Airbnbで(Tonnyさんが)予約してくれたところなのですが、雰囲気があって超よかったです。



ヨセミテも想像以上に楽しかった。


f:id:shu223:20150607100935j:image:w600

(川遊び)


f:id:shu223:20150703042256j:image:w600

(某El Capitan)


f:id:shu223:20150703034635j:image:w400

(なぜか乗せてもらえた本場のハーレー!)


日々もくもく開発@Realm オフィス

サンフランシスコの Realm オフィスのモニタで、みんなで基調講演のストリーミングを見ました。


f:id:shu223:20150703034810j:image:w400

(広くて綺麗で快適でした)


Realmオフィスへは基調講演以降もほぼ毎日お邪魔し、もくもくドキュメント見ては手を動かして新機能を試してました。


f:id:shu223:20150703034918j:image:w400

(ランチ風景)


f:id:shu223:20150703035002j:image:w400

(念願のTシャツもいただきました。愛用してます)


StackOverflow デビュー

StackOverflow でいくつかの質問に回答し、Reputation もいくらかつき、ついに自分も vote up やコメントができるようになりました。

今まで、「なんて素晴らしい回答だ!ありがとう!」と思っても vote up する権利すらない自分の無力さを何度歯痒く思ったことか。。


StackOverflow の Reputation は GitHub 同様に世界で通用する客観的&定量的評価だし、英語の練習になるし、人の役にも立つ、ということで自分の中でずっと重要TODOだったのですが、日々の仕事を言い訳にずっと後回しにしてきたことなので、非常に達成感があります。


HomeKit 対応デバイス購入

iOS 8 で発表された HomeKit、IoT の文脈から注目はされていたものの、対応製品がない、という状況がずっと続いていました。で、最近ぽつぽつと製品が出始めている、ということでサンフランシスコの Apple Store に行ってゲットしてきました。


f:id:shu223:20150703035336j:image:w400


Lutron社の「Caséta Wireless」という製品です。229ドルもしました。とりあえず HomeKit を体験したいだけなのでもっと安いのを・・・と店内の製品パッケージをくまなく探してみましたが、残念ながらこれしか置いてないようなので購入。


後日またブログかLT等で使用&実装報告したいと思います。


WHILL HQ 訪問、野球観戦など

昨年のTechHouse内オフィスから移転したWHILLの米国本社や、とある企業のオフィス等に遊びに行きました。


f:id:shu223:20150703035436j:image:w400



あと人生初の野球観戦。


f:id:shu223:20150703035511j:image:w400


ホットドッグ&ビール&野球という経験ができてよかったです。


Apple Watchハッカソン参戦

最終日、ちょうどSFでAppleWatchハッカソンが開催されてたので参戦。


なんと、賞をいただいてしまいました!!!!


f:id:shu223:20150614131616j:image:w400


つくったのは watchOS-2-Sampler。コミュニティへの貢献を評価していただけたようです。めっちゃ嬉しかったです。


ちなみに英語プレゼンはアドリブでは絶対にグダグダになる自信があったので、いったん話したいことを全部書き出して、流れだけ頭に入れつつ反復練習して、ハック中に話しかけてくれた優しそうな現地の人に聞いてもらったりもしました。


後日談として、こういうこともありました。


やっぱ海外でハッカソンに参加するのは楽しい。


費用

気になるお値段ですが、

  • 飛行機代:¥125,140
  • 宿代:¥44,860($45 × 8 × 124.6)

で、基本費用は17万円程度。40万オーバーだった昨年と比べて、大幅にコストダウンしました。WWDCチケット代がかからなかったのと、宿を全泊TechHouseにしたのが効いたな―と。(TechHouseについては昨年の記事をご参照ください)


あとは日々の食費、ヨセミテ旅行費(レンタカー+宿。手渡しだったのでいくらか忘れた。。)、野球観戦のチケット代(確か8000円ちょい)がかかってるので、総旅行費用としては20万円ちょいかと。


LT

せっかくSFの空気に触れて高まった意識も、帰国して日々の仕事の波にさらされるとあっという間に消えてなくなるので、「人前で発表しないといけないプレッシャー」ドリブンで勉強を進めるべく、WWDC関連の勉強会にはできるだけLT枠で申し込みをするようにしました。


6/16 「potatotips #18」


ブログ:watchOS 2 新機能の細かい話5つ #potatotips - Over&Out その後


6/17 「UI Crunch #5 スマートウォッチUIデザインの今」


ブログ:UI/UX に影響の大きい watchOS 2 の新機能 3つ #uicrunch - Over&Out その後


6/19 「WWDC Afterparty Roppongi」


ブログ:【iOS9】Core Image の新機能:文字認識/追加フィルタ47種 - Over&Out その後


6/24 「WWDC2015報告共有会@ネクスト」


ブログ:【iOS9】Audio Unit Extensions 〜オーディオエフェクトのアプリ間共有〜 - Over&Out その後


所感

チケットなしWWDC、ものすごく行ってよかったです。ドキュメント見るなら日本でもできる、とはいえ日本にいたら絶対仕事を入れてしまってドキュメント見たりサンプルつくってみたりLTしたりしなかったと思うので。


あと、WWDCのチケットがあると、高いチケット代払ってるので朝ちゃんと起きてフルに会場にいないといけないという気がどうしてもしてしまってなかなかしんどいのですが、そういうプレッシャーもなく健やかな気持ちで日々を過ごせたのもよかったです。結局毎日ドキュメント見てコード書いてたわけですが、義務感があるのとないのとでは健やか度がだいぶ違うなと。


それと、Realmオフィス。Realm の中の人をはじめ、CocoaPods の中の人や RestKit の中の人など、真のスーパーハッカーな方々が集う場で、なんというかそこにいるだけで「もっと精進せねば・・・」と意識が高まりました。


そんなこんなで、来年もチケット外れてもまた行きたい所存です。現地でお世話になった皆様方、ありがとうございました!


2015-06-24

【iOS9】Audio Unit Extensions 〜オーディオエフェクトのアプリ間共有〜

昨日開催された「WWDC2015報告共有会@ネクスト」にて、iOS 9 で追加された新しい Extension Point のひとつ、「Audio Unit Extensions」についてLTをさせていただきました。



概要

Audio Unit とは?
  • Core Audio においてもっとも低レベル(ハードより)に位置するフレームワーク
  • 低遅延での音声処理が可能
  • ユニット同士を繋げて複雑なオーディオ処理を実現可能
  • iOS 8 より AVFoundation に AVAudioEngine が追加され、Audio Unit の利用ハードルがグッと下がった

Audio Unit Extensions とは?
  • iOS 9 で追加された Extension Point のひとつ
  • Audio Unit をアプリ間で共有できる

→ つまり自作エフェクトや他社製エフェクトを繋げてこういうこともできるようになる!


f:id:shu223:20150624072335j:image:w600

(※写真はイメージです)


  • Audio Unit にはエフェクト以外にもさまざまなタイプがある
    • GarageBand では Audio Unit Extensions の楽器を利用できるようになるらしい。
var kAudioUnitType_Output: UInt32 { get }
var kAudioUnitType_MusicDevice: UInt32 { get }
var kAudioUnitType_MusicEffect: UInt32 { get }
var kAudioUnitType_FormatConverter: UInt32 { get }
var kAudioUnitType_Effect: UInt32 { get }
var kAudioUnitType_Mixer: UInt32 { get }
var kAudioUnitType_Panner: UInt32 { get }
var kAudioUnitType_Generator: UInt32 { get }
var kAudioUnitType_OfflineEffect: UInt32 { get }
var kAudioUnitType_MIDIProcessor: UInt32 { get }

Audio Unit Extensions の利用方法

WWDC のサンプルコード "AudioUnitV3Example" に入っている "FilterDemo" というアプリには、Audio Unit Extensions が含まれています。まずはこのアプリをインストールして、「エクステンション利用側」を実装してみます。


利用可能なユニットのリストを取得する

iOS 9 で追加されたクラス AVAudioUnitComponentManager の `componentsMatchingDescription:` メソッドを呼ぶと、

var anyEffectDescription = AudioComponentDescription()
anyEffectDescription.componentType = kAudioUnitType_Effect
anyEffectDescription.componentSubType = 0
anyEffectDescription.componentManufacturer = 0
anyEffectDescription.componentFlags = 0
anyEffectDescription.componentFlagsMask = 0

availableEffects = AVAudioUnitComponentManager.sharedAudioUnitComponentManager()
    .componentsMatchingDescription(anyEffectDescription)

利用可能な Audio Unit の AVAudioUnitComponent (ユニットのタイプや制作者などの情報が入っているクラス)のリストが得られます。


この中に、インストールした"FilterDemo"サンプルが contain しているユニット "FilterDemo" も入ってきます。


ユニットを適用する

以下の手順で、所望の Audio Unit をサウンドに適用します。

  1. AVAudioUnitComponent の AudioComponentDescription (構造体)を取得する
  2. AVAudioUnit を `instantiateWithComponentDescription:options:` で生成する(このメソッドは iOS 9 で新規追加)
  3. AVAudioEngine に attachNodeする
  4. エフェクトノードと、プレイヤー・ミキサー等のノードを `connect:to:format:` で接続する

※AVAudioEngine を用いて Audio Unit のエフェクトをかける基本的なサンプルは、iOS8-Sampler にも入っているので、よろしければご参考に。


f:id:shu223:20150624072630j:image:w200

(iOS8-Sampler の "Audio Effects" サンプル)


Audio Unit Extensions の作成方法

なんと、Xcode 7 beta には Audio Unit Extensions を作成するためのテンプレートがまだありません(という話は WWDC セッション内でも話されています)。とりあえず現状では "FilterDemo" からコピペして使ってくれ、とのこと。


Audio Unit の関連記事


2015-06-22

【iOS9】Core Image の新機能:文字認識/追加フィルタ47種

先日、Gunosy さん主催の勉強会「WWDC Afterparty Roppongi」にて標題の発表をさせていただきました。



タイトルの通り、iOS 9 の Core Image の新機能について紹介&デモ *1 しました。


概要

大きく分けて、文字認識、新フィルタの紹介の2つ。


文字認識

WWDC初日に書いた下記記事でもわりと反響の大きかった機能。


で、OCR的なものを期待していたわけですが、リファレンスで CIFeatureTypeText を見ると、認識結果の文字列が入ってくるプロパティはないので、文字の「内容」を認識するのではなく、文字の「領域」を検出するものでした、という話。

class CITextFeature : CIFeature {
    
    var bounds: CGRect { get }
    var topLeft: CGPoint { get }
    var topRight: CGPoint { get }
    var bottomLeft: CGPoint { get }
    var bottomRight: CGPoint { get }
}

いわゆる「顔認識」も、「誰の顔か」を認識するのではなくて、顔の「領域」を検出するものがほとんどなのと同様ですね。


それはそれとしてOCRの前処理とかに便利だと思います。実装は非常に簡単で、顔認識とかとほぼ同じです。

let image  = CIImage(CGImage: imageView.image!.CGImage!)

let detector = CIDetector(
    ofType: CIDetectorTypeText,
    context: nil,
    options: [CIDetectorAccuracy: CIDetectorAccuracyHigh])

let features = detector.featuresInImage(image)

for feature in features as! [CITextFeature] {
    // 認識結果の矩形を描画する処理
}

NDAのため iOS 9 での実行結果スクショを貼るのは自粛しますが、デモでは以下の2種類の画像について試し、


f:id:shu223:20150622065519p:image:w450



f:id:shu223:20150622065439j:image:w320


  • 前者は文字領域を完璧に検出
  • 後者はGitHub マークのある黒地に白文字の "shu223" というものしか認識されず

という結果になりました。わずかなパースや陰影(後者のような「写真」画像は、人間には均一に見える部分でも、二値化してみると結構色に偏りがあったりする)で認識精度がガクッと落ちるようです。


新フィルタ

CIFilter は

CIFilter(name: filterName, withInputParameters: params)

っていう感じでフィルタ名を文字列で指定する形式なので、API Diffs とかには新規追加フィルタは表記されない(Filter Reference というドキュメントもあるけど更新が遅い)ため、次のようにビルトインフィルタ名を全部出力しておいて、

let filterNames = CIFilter.filterNamesInCategory(kCICategoryBuiltIn)
for filterName in filterNames {
    print(filterName)
}

そのあと `diff` コマンドで新規追加分を抽出する、ということを毎年 iOS がメジャーバージョンアップするたびにやっています。


で、今年はフィルタ数の変化をグラフにしてみたのですが、


f:id:shu223:20150622071620j:image:w425


ひさびさにフィルタ数がグッと増えていて、ちょっと盛り上がりを感じます。


例によってNDAによってフィルタをかけた結果のスクショ掲載は控えますが、以下に新フィルタ名47種の一覧を載せておくので、名前から効果を察していただけると。

  • CIAreaAverage
  • CIAreaMaximum
  • CIAreaMaximumAlpha
  • CIAreaMinimum
  • CIAreaMinimumAlpha
  • CIBoxBlur
  • CICircularWrap
  • CICMYKHalftone
  • CIColumnAverage
  • CIComicEffect
  • CIConvolution7X7
  • CICrystallize
  • CIDepthOfField
  • CIDiscBlur
  • CIDisplacementDistortion
  • CIDroste
  • CIEdges
  • CIEdgeWork
  • CIGlassLozenge
  • CIHeightFieldFromMask
  • CIHexagonalPixellate
  • CIKaleidoscope
  • CILenticularHaloGenerator
  • CILineOverlay
  • CIMedianFilter
  • CIMotionBlur
  • CINoiseReduction
  • CIOpTile
  • CIPageCurlTransition
  • CIPageCurlWithShadowTransition
  • CIParallelogramTile
  • CIPDF417BarcodeGenerator
  • CIPointillize
  • CIRippleTransition
  • CIRowAverage
  • CIShadedMaterial
  • CISpotColor
  • CISpotLight
  • CIStretchCrop
  • CISunbeamsGenerator
  • CITorusLensDistortion
  • CITriangleTile
  • CIZoomBlur

会場ではシミュレータでデモしたのですが、GPUが効くので、実機でやるとチョッパヤです。



ちなみにちょっと余談ですが、会場で Yahoo の佐野さんから飛び出した「Apple は何のためにそんなにがんばってフィルタを追加しているのか?」という質問が印象的でした。


確かに、CIDetector 系は実用的だし、ボックスブラーとかノイズリダクションとか、色のブレンド系etcは写真加工系に幅広く使えそうなのでわかるとしても、CICrystallize とかの飛び道具系って実際には使う人いなそうだなぁと。そして僕はそういえば案件で Core Image 使ったことなんて記憶に無いのになぜ毎年やってるのだろう、と・・・


おわりに

他の発表者のみなさんもかぶりがないように幅広い内容で発表されていて、人数もちょうどよく和やかな雰囲気で質疑応答も盛り上がり、大変有意義な時間を過ごさせていただきました。主催のグノシーさん、発表者・参加者のみなさまどうもありがとうございました!


次回は「WWDC2015報告共有会@ネクスト」にて、Audio Unit Extensions について話させていただく予定です。


*1:参加者は Apple Developer アカウントを持っている人のみ

2015-06-17

UI/UX に影響の大きい watchOS 2 の新機能 3つ #uicrunch

本日開催された、『UI Crunch #5 スマートウォッチUIデザインの今』というイベントで、標題の発表をしてきました。



イベントタイトルからも察していただけるかもしれませんが、今回は勝手知ったる「iOSエンジニアの技術勉強会」ではなく、デザイナーさんやディレクターさんも集まる会なので、発表内容は相当に迷いました。タイミングとしてWWDC直後なので、そこで発表されたことについて話そう、ということだけは決めてはいたものの、具体的な実装の話をしてもポカーンとしてしまう方も多いだろうし、新機能をつらつら紹介しても退屈だろうし。。


というわけで、数ある新機能の中から、特に Apple Watch アプリの UI/UX にインパクトが大きいであろう(と僕は考えている)機能3つを抜粋して紹介しました。


具体的には以下の3つ。各内容についてはスライドをご覧いただければと。

  1. Complication (ClockKit)
  2. Watch Connectivity
  3. センサへのアクセス


他の登壇者の方々はがっつりつくりこんだウォッチアプリの話や、UIの考え方の話をされていて、非常に勉強になりました。お誘いいただいた主催のグッドパッチさん、DeNA さん、発表者・参加者のみなさまどうもありがとうございました!


2009 | 08 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2012 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2013 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2014 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2015 | 01 | 02 | 03 | 04 | 05 | 06 | 07 |