2015年11月28日土曜日

プログラマーがSketch3を使ってみた

最近はあるアプリのUIを色々検討中でして、Sketchとにらめっこすることが多い。

Sketch は画像編集アプリで、買い切りの$99の製品。
Adobe製品を使うほどでもないけど、そこそこ画像を作りたい場面もある...のでこれを使っています。

他のツールを全然使っていないので比較はできないですが、
Sketch便利機能を紹介 @iOSアプリ開発プログラマーの目線

1. 複数サイズの画像を一気に出力できる

@2x, @3x とサイズの違う画像を用意するのが簡単。


出力メニューはこんな感じになっていて、2倍だったらファイルのSuffixに@2xをつけるなどの設定ができます。
一度設定すれば、再出力する際も Export ボタンを押すだけ。
画像の修正を繰り返す私にとっては、とても嬉しい機能。
1倍、2倍以外にも 幅が120pxで...という指定も可能です。

2. 角丸を作るのが簡単

少し角丸をつけたい...というときに、コードでもできるけど、画像がいい場合のときも多くあります。
例えば
ボタン背景とか (押したときのハイライトを自動的にいれたいので)
テーブルCell内でつかうものとか (cornerRadiusだと処理が重くなったりするので)

角丸の大きさは、Radius で数値の設定ができる。
数値が大きいほど大きな角丸になります。


四角形の4角同時に同じRadiusを設定もできるし、この角だけ.... とかも可能。
直接コントロールポイントを触ることももちろんできるのですが、それよりも簡単なレイヤーとしての設定があるのが嬉しい。

3. 白の画像が作りやすい

白色と透明の2色の画像を作る場合。
背景に色がある状態で作成するけど、出力する際には背景色をいれない ということが出来る。


結構、この手の画像多いんすよね。

4. ピクセルにぴったり合わせる機能

サイズを大きくしたり....小さくしたり...真ん中に寄せて....と繰り返していると、ピクセルにピッタリ合なくなる場合があります。
あっていないとぼんやりとした画像になっちゃうので合わせたい!
ピクセルに合わせる機能は、Layer > Round to Nearest Pixel Edge です。


5.  同じ要素を使いまわしたい(Symbol)

Symbol化すると、それをコピーしていくつかに使用されていても、一つを直すと全体に反映されます。プログラマー的にはClass化?
具体例として、先日、Timesheetアプリ のAppStore用スクリーンショットを作成したときに使いました。


背景と、端末のイメージは全て共通としてSymbol化しています。
(フォルダの色が、紫になっているのがSymbolになったもの)

ここから、端末内のイメージやらタイトルやらを書いて4枚のスクリーショットを作成。
やっぱ背景画像かえたいなー と、変えたい場合に一つ変えれば全てに反映される手軽さ。

実際はどうか知らないのだけど、.... 端末画像は大きいので複数貼り付けするとファイルサイズがでかくなるし、メモリ消費も激しくなりそうで気がひけるけど、Symbolだったら大元の参照1つなのかな? なんて思ったりも。

6.  PathをSVGに出力できる

読み込みもできるんですが、出力もできる!
SVGに出力できるので.....画像じゃなくて、プログラムでdrawするんだぜ!というときに、ネタをまずSketchで書いて、SVGに出力してその内容みるということも可能。



先日リリースした、tvOS向けアプリ OnePath では、お題となるパスを最初にアニメーションでdrawしています。
これは、パスをまずSketchで書いてから、SVGに出力 > 線のPoint情報をまるっとコピペ > コードからそれを読み込む ことをしていました。

7. iPhone端末へのミラーリング




SketchのArtboardをiPhone端末で直接見れる!
ただ、iPhoneアプリ Sketch Mirror とを別途購入(¥600) が必要です。

もー、これなしで画面デザインはできないぜ というぐらい今となったら大切な機能。

PCとiPhone実機とでは色の発色が違うので、色を決定するときには実機での確認がが必須。
それ以外にも、UIを検討する際でも実機でみないと判断つきにくいので、何度も実機でみながら考えます。

最初はケチってSketch Mirror を買わず、画像に出力して、iPhoneで眺めていたのですが、何度も何度も画像に出力するのが面倒!
Mirrorを使うと、Sketchで修正しながらリアルタイムで実機上でその反映が見れるので本当に便利。

本業がデザイナーの方に比べると、デザインを書き直しする回数がすごく多いのだと思う。だからこそ、手軽にできる環境はとても大切ですね。

8. Grid コピー

同じ要素を等間隔でいくつか並べたいときは Gridという機能が使えます。



縦、横の行数と、マージンを指定して、どさっとコピー!
画面のUI案を書いているときにはよく使う機能。



以上!


お絵描きする際のPathやMask系の話が全くないですが、ま、そこらへんは画像編集のツールならあって然りな機能なのかなと。


あ、最後に不満点を一つ。

alphaなしのpngの出力ができない!
(知っている人いたら教えてください...)

• • •

2015年11月26日木曜日

UITableViewのInset



UITableViewCellの左側のスペース(Inset.left)。
Defaultの設定では、表示する端末によってサイズが変わります。

iPhone5, 6 では 16px。
iPhone6Plusでは 20px。
Storyboard上では15px。

UITableViewCellのStyleを、提供されているBasicなどを使えば自動的にこの値になります。
Customでセルを自作する場合はSeparatorとの整合性を合わせるのを忘れずしないといけません。

SeparatorのInsetはStoryboardから変えられます。
UITableViewのAttribute Inspector > Separator inset を Customにして値を設定。



逆に、Separator Insetのサイズを、Customセルのラベルなどに反映する方向は...Storyboard上の設定だけで完結する方法が見つけられなかった。

コードで行う場合、Separator Insetの値が確定するのは、viewDidLayoutSubviews 以降になる。
そのタイミングで値を取得してConstraintに設定すれば、一応目的は果たせるが、ちょっと面倒。

簡単に済ますのであれば、

CustomとBasicなどの混在はしない
Customを使う場合は、Separator insetを指定する

ですね。
• • •

2015年11月19日木曜日

NSLayoutConstraintをコードで変更するお話

バリバリとStoryboardで制約をつけて開発...している毎日ですが、動的に制約内容を変えたくなる場面が多々発生します。
ボタンを押したら、このViewのサイズをググッと大きくする とか。

そうしたい時は、制約(NSLayoutConstraint)をViewControllerに紐付けて、ViewControllerで書き換えます。
view.frame で直接サイズを設定して変えることもできますが、元に戻ってしまうことがあるので、使わないほうがいいと思います。

書き換える方法は、NSLayoutConstraintのconstant(値)を変えればOK。

@IBOutlet weak var myConstraint: NSLayoutConstraint!

func changeSize() {
    myConstraint.constant = 20
    self.view.layoutIfNeeded()
}
でいける。
アニメーション付きでやりたい場合はlayoutIfNeededをアニメーション実行してあげればよし。
UIView.animateWithDuration(0.3, animations: { () -> Void in
    self.view.layoutIfNeeded()
 })
...が!
myConstraint.constant = 20
のように、値をハードコーディングしたくないですよね。
Storyboard上でせっかくLayoutを完結させているのに、コードでLayoutの固定値を入れるわけなので、Layoutを変更したらコードの方も変更が必要に。

そこで使えるのが、NSLayoutConstraint.activeプロパティ。(iOS8以上)
Discussionには以下のようにあります。
You can activate or deactivate a constraint by changing this property. Note that only active constraints affect the calculated layout. If you try to activate a constraint whose items have no common ancestor, an exception is thrown. For newly created constraints, the active property is NO by default.

Activating or deactivating the constraint calls addConstraint: and removeConstraint: on the view that is the closest common ancestor of the items managed by this constraint. Use this property instead of calling addConstraint: or removeConstraint: directly.

WWDCでもこれを使うようにオススメされていました。

モノとしては、active が true だと有効、false だと無効 という単純なもの。

Storyboard上で使う可能性があるNSLayoutConstraintを全て定義します。
たとえば、Viewの高さが 0px と、20pxと切り替えたい場合にはこの2つを用意。
このままだとStoryboard上でConflictするので、片方の制約の "Installed" をOffにします。
この2つの制約をViewControllerに紐付けて、タイミングに合わせてactiveの値を変えればOK。
px指定していないView...画面サイズの1/2 とか、Text文字の高さによって変わるとか...も数多くあるはずなのでこれで解決。

...ガ!

active の変更は、viewDidLoad で行っても反映されない罠(?)があります。
(constant の変更は、viewDidLoadで大丈夫。loadViewだって大丈夫。)

ViewControllerのLoad時のイベント発生の順序は
loadView
viewDidLoad
updateViewConstraints
traitCollectionDidChange
viewWillLayoutSubviews
viewDidLayoutSubviews
(※1)
viewWillAppear
(※1)
viewDidAppear

※1 レイアウトによって、viewWillLayoutSubviews, viewDidLayoutSubviews が繰り返しcallされる
ですが、activeはviewDidLayoutSubviews 以降ののタイミングでないとうまく反映されません。
viewDidAppearは表示が終わった後なのでタイミングが遅すぎる。

activeの切り替えはtraitが変わった際(端末が縦、横に回転した時など)にも使うのもなので、traitCollectionDidChange 以降で設定だ というのは納得できるのですが、viewDidLayoutSubviewsは、何回もcallされるメソッドなのであまり独自の処理をいれたくないという気持ちがある。
traitCollectionDidChange で設定できればよかったんだけどなー!

そもそも、画面をLoadする時に変更するのではなく、Storyboard上で起動時の設定にすでにしておくのがいいんですが...
そうすると、Storyboard上でLayoutしにくくなったりするんですよ。
たとえば、最初は全部隠れているけど、アニメーションで動かして画面が出てくることをしたいとか。
うーん、悩ましい。
• • •

2015年10月31日土曜日

OnePath リリースしました!(tvOSアプリ)

先日発売されたApple TVでは、サードパーティのアプリが解禁されました。
で、Flaskでもせっかくなのでなにかアプリを...ということで作成しましたよ!

OnePath

どれだけ正確に描けるかな?
Apple TVとiPhoneで遊べるシンプルなゲーム
まず、お題がでてきます。


これをSiriリモートのPadで正確になぞる。
これが、結構難しい。


完全に一致したらScore=100なんですが、私は70点以上をとったことがありませんw
GameCenterに接続すると、ランキングが見えます。

SiriRemoteのPadの操作は慣れるまで難しい。
本格的なゲームは、やっぱりGameControllerを別途接続してやらないとイライラしちゃうんじゃないかなぁーと思っている。
それを逆手にとったイライラ感を楽しむゲームです(笑

AppleTVを手にしたら、最初にちょっと試してみるアプリとして良いんじゃないかなと思うので、是非試しあれ!


• • •

2015年9月21日月曜日

tvOSの所感

秋に発売される New Apple TVで、サードパーティにもアプリが作れるようになりました。
tvOS とは、そのApple TVのSDKになります。

一体何ができるのか、どんな感じなのか?というのを見てみたので、一般公開されている内容を元に書いてみたいと思います。

Apple TVに向いてそうなアプリ

やっぱり、TVなので..... 画像がないと寂しいです。

画像をみせつつなにかするとしたら、やっぱり通販系がいいのかなと。
服、CD、本 などを売ってみたりとか。
TVショッピングを見ている人々をターゲットに。
Appleが代金支払部分のシステムを提供してくれたらいいのにー。

あとはゲーム。
レスポンスみながらどこまで作り込めるか...にもよりますが。
単純なものだったら、複数人でできるとよさそう。

が、あの小さなリモコン。基本の操作はあのdpad。
こまかな操作はかなり難しいと思います。
操作はシンプルに...

アプリの作成方法は2種類

1. TVJSとTVMLを使ってClient-Serverアプリ

tvOSにはUIWebViewが使えません。
その代わり(?)的に使える方法が、JSとTVMLを使う方法です。

TVJSは、JSの中で使えるものが限定されたセット。
→  TVJS Framework Reference

TVMLは、HTMLに変わるAppleTV専用マークアップと考えればOK。
テンプレートが幾つか用意されていてそれに合った形のUIを考えていく必要あり。
→ Apple TV Markup Language Reference - Templates

ネイティブアプリも必要ですが、最初の画面となるURLを設定するぐらい。
TVJSと、TVMLは自社サーバーに配置します。

iOSを知らない、ましてやObjectiveC/Swiftを知らない人でも簡単に作れそうです。
また、テンプレートが用意されているので、できることが限られている反面、UIデザインに自信がない人でも気軽にできる気がする。

そういう人を狙いましたよ! というのがビンビン伝わってきます(笑
わざとWebViewを提供せずに、UIを統一したいのだろうかと思ったりも....。

2. ネイティブアプリ

もちろん、今までどおり、ObjectiveC/Swiftでゴリゴリ書くことも可能。
ただ、Frameworkは制限されています。
Flaskでよく使っている、HealthKit, EventKitは無いので残念!
ゲームで使うSpriteKit,SceneKitも入っています。


容量は限られている

アプリは200MB以内でないといけない。
デバイスに保存された情報は、次回起動時に存在する保証はないから、iCloudを使ってねと。
→  iCloud Storage

NSUserDefaults.standardUserDefaults() をためしてみたら、データの保存はできている模様。
だけど、永続化されている保証はないということか?!

iCloudって試してみたのは随分昔なもので、あまりすんなり動かないイメージなんですが、今は安定して使えるのだろうか?


UIはフォーカスがキモ

TV用のインターフェースを考える際に重要なのがフォーカス。
フォーカスを移動してから、タップして選択というのが操作の基本なので。
このイメージはAppleTV だけでなく、PlayStationとか、dボタンおした時の表示とかでも経験あるかと。

性質上、UICollectionViewを使うことが多いと思うのですが、普通に使うと、どのセルにフォーカスがあるのかは分からない状態に。

そこで、使えるのが、UIImageView#adjustsImageWhenAncestorFocused
このプロパティをtrueにすると、フォーカスがあたったら自動的にちょっと大きくなり、影などの効果も追加されます。
(ImageViewサイズより大きくなるのでUIImage#clipsToBoundsをfalseにする必要あり)

Imageについてはこれで簡単!なのですが、Labelなどには無いことに注意。
CollectionViewのCellの中に文字を表示形式だと、画像が大きくなるのに文字は大きくならないというアンバランスになります。
文字は画像の下に置く。フォーカスがあるときのみ文字を表示する。
などの工夫が必要そうです。

フォーカスが当たったタイミングは、
UIFocusEnvironment#didUpdateFocusInContext で取得可能。
UIFocusEnvironmentはUIViewControllerやUIViewで実装されているので、UI系のクラスならどのクラスでも取得できるはず。

ちなみにUICollectionView#didHighlightItemAtIndexPath はCallされません!
didFocusItemAtIndexPath のようなものを提供してくれると嬉しかったのですが。

このフォーカス有無での表示関連は、TVMLを使っていたらある程度自動的にいい感じにしてくれてそうで楽な予感。


入力イベント

AppleTVではTouchとTapの種類がある。
ちょっとややこいが、詳細はおいておいて、取得できる方法を。

UITapGestureRecognizer
UISwipeGestureRecognizer
は使える

もう少し低レベルのAPIでPress系のものも用意されている

func pressesBegan(presses: Set, withEvent event: UIPressesEvent?) 
func pressesChanged(presses: Set, withEvent event: UIPressesEvent?)
func pressesEnded(presses: Set, withEvent event: UIPressesEvent?)

名前が示しているようにPresssの状態の時=Tapの時がとれる。
Touchの状態のものはとれない。

UIPress には、typeと phase というプロパティがあり、どのボタンが開始されたのか?を取得できる。
タッチパッドの右、左、上、下というのも取れる。

pressした状態で左右に動かした時というのをとってみたかったのだが、
pressesChanged がなぜかイベントが飛んでこなかった。
操作がわるいのか....SDKのバージョンアップによって変わる?

他の取得方法は、GCController を使えます。
GCControllerとは、外部のゲームコントローラーとの接続を管理するクラス。
Apple TV Remote(コントローラー)は、GCControllerとして検出できます。
GCControllerのdpadにvalueChangedHandlerを登録すれば、タッチパッド上の指の動き..Touch,Tap両方状態ともに...を検知できます。
(シミュレータでは動きません)
この実装はAppleのSampleの DemoBot が参考になります。

参考までに、XCodeのシミュレーターのApple TV Remoteでは、Remoteの画面をActiveにした状態でOptボタンを押しながらマウスポインタを画面上で動かすとTouchの動きになります。(マウスをPressする必要なし)


iPhoneの接続

CoreBluetoothを使ってiPhoneと接続ができる。
が、どうやら2台までらしい。
それ以上の時はBonjour使えとー。


UIScrollView

UIScrollViewの中に画像を置いて、ズーム可能な配置にしても、AppleTVではズーム出来なかった。そういうもんなのか?!
関連 : stackoverflow - UIScrollView on tvOS



• • •

2015年9月15日火曜日

Unknown class in Interface Builder file

XCodeで新規のプロジェクトを作成し、Storyboardで自作のViewControllerを設定すると、Unknown class  in Interface Builder file というエラーが発生するようになった。

自作のViewControllerのクラスが見つからないという状態。
“Unknown class in Interface Builder file” error at runtime らへんが同じ内容の話がある。

Build Phasesをみてもちゃんと設定されている。
Custom Class → Moduleに、プロジェクト名を入れると実行はできるが、Storyboard上でViewControllerの自作IBOutlet, IBActionは見えない状態。
~/Library/Developer/Xcode/DerivedData/を削除したり、XCodeを再起動したりしてもダメ。

結局、解決できたのですが...

CocoaPodsのプロジェクトにしてみるかと思い、出来たxcworkspaceを起動しようとしたのですが、xcworkspaceがFinder上で見えなかったので、なにげにOpt + DocのFinderを長押しして出てくるメニューでRelaunchして起動したらOKになりました。

そういや、最近、新しく作成したファイルがFinderで見えないということがある。
昔はなかった気がするが...私のMac壊れてるんかしら。

なので、結局、MacOSを再起動したら解決できていたのではないか?と想像中。
同じ現象になった方はお試し下さいw

XCodeのシミュレータが増殖する事件があったのですが、実はこれも同じ原因だったりするのかもですね。
ファイルは本当はあるのに、プログラムからは見えない状態になっていたので同じものが作成されていた とか。


2015/10/11 追記

Mac再起動でもダメでした。
同様の問題が発生して、どーしても解決できない状態に(涙

Macをクリーンインストールした環境で、同じプロジェクトを試したらすんなり動きました。




• • •

2015年7月18日土曜日

SpriteKitとSceneKit


SpriteKitとSceneKitのお勉強を始めました。

両方ともiOSでゲームを作るときのフレームワーク。
SpriteKitは2Dのゲームを、SceneKitは3Dのゲームが作れます。

他、Metalというのがあるけど、これはもっと下位レベルのフレームワークでOpenGLと同じレベル。



WWDC15 : Enhancements To Scenekit より

Metalは、A7, A8チップに特化することでパフォーマンスを最大限に引き出すよ というもの。
A7より下位のチップは動かないし、シミュレータでも動かない。(El Capitan‎だったら動くのかもしれない?)

SpriteKit, SceneKitを使ったらMetalでは動かないの?
というのが最初の疑問だったのですが、

iOS9ではMetalで動く環境であればMetalで動く。
SceneKitであれば指定も可能
ということらしい。

iOS9のリファレンスで、
New Featuresとして Metal Rendering Support
と記述されているので、iOS8ではMetalでは動かず、OpenGLで動いていたのかな?

SceneKitのSCNViewのInitialization Optionsで、SCNPreferredRenderingAPIKey が指定できる。
Storyboardにもこの設定がある。


Defaultにしておけば、よさげに動いてくれる。
SCNSceneRenderer#renderingAPI  から結果をデバッグしてみたら、
シミュレータではOpenGLES2
iPhone6 iOS9 beta3では Metal
にちゃんとなっていた。

SpriteKitはリファレンスに "metal rendering is automatically used" とあるので、自動的に選択....逆にいうと指定はできない という理解でいいのかな。

知らないって怖い。こんな最初のところからつまずいた。

プロジェクト作成するときの設定で、

と、横並びで選択がでてくるのも良くないと思うんだけどな!


今のところの次の疑問は、3Dモデルのファイル形式。

3Dの情報には、モデルデータ、テクスチャ、シェーダー、アニメーションなどが含まれていて、dae などの拡張子になっているのですよね。
daeは、SceneKitで読み込めるのですが、Appleのサンプルプロジェクトでは scn というファイル拡張子になっている。
scnファイルであれば、XCode上のEditorで修正できるよということだと思うのだけど、どこまでモデリングツールで作って、どのようにXCodeに持ってるくと手軽に作れるのだろう....

ModelIO.framework というのが使えるのだろうか。

The Model I/O framework provides a system-level understanding of 3D model assets and related resources. You can use this framework to import and export assets from and to a variety of industry standard file formats supported by popular authoring tools and game engines. You can also use Model I/O to generate or process model and texture data—for example, to create subdivision surfaces, bake ambient occlusion textures, or generate light probes. Model I/O can share data buffers with the MetalKit, GLKit, and SceneKit frameworks to help you load, process, and render 3D assets efficiently.

とリファレンスにあるので...?

あーー 何から何まで疑問だわー



• • •

2015年6月17日水曜日

WWDC2015の旅記録


WWDC2015に行ってきました!
今年は、 +Takako Horiuchi  と一緒にいけるという奇跡!!
やっぱり誰かと一緒に行けるのはとっても楽しかった♪

チェックイン

前日の午前中にチェックイン。
期間中はこれがないと入れない大切なやつGET。

今年もWWDCジャケットがありました。
SFは湿度がなく結構冷え込むので、このジャケットはとてもありがたい。
チェックイン時にサイズを指定してもらいます。
ちょっとDeveloperに嬉しいサイズ表記なんかもw

Apple本社への旅

前日午後に、Apple本社に行ってきました。

最初は、カルトレインで行こうと思っていたのですが、トレタ のお二人のレンタカーに便乗させてもらうことに!
素晴らしき運転&ナビゲーターのおかげで、迷うことなくたどり着け、しかも、Computer History MusimumGoogle Plexまで寄ることができました。
本当に感謝!

建設中の第二キャンパスらしきところも寄って回ってみたが、まだあまり進んでない予感?


でも、かなーーり広かった。またできたら、いってみたいな。

SF4大サードウェーブコーヒー制覇

最新!サンフランシスコ、4大サードウエーブコーヒー事情
これ、制覇してみたいねーと話していたのですが、なんと達成してきました!
引きこもりの私達なのに、頑張りましたw


Four Barrel Coffee系のThe Millをはじめに、Sightglass, Blue bottle Coffee, Ritual Coffee Roasters をめぐってきました。
ハンドドリップコーヒーで比較してみたけど、どこも浅煎り〜中煎りぐらいで酸味が強かった。
深煎りが好きな私としては、やっぱり深煎りのラテやエスプレッソの方が好きかな(笑
お店の雰囲気はどこも良くて、若い人が多くて過ごしやすい。
The Millのトースト、美味しかった!
日本のトーストよりもハードなずっしりしたパン。大好き。

美味しそうなパンが並んでいる店が多くて、1斤買いしたくなる衝動を何とか抑えました。
コーヒーというより、美味しいパンのカフェが上陸して欲しいなー。

ダンジネスクラブ体験

ダンジネスクラブという蟹を初体験してきました。
行ったお店はCrustacean Restaurant
ガーリックで味付けされている蟹ははじめて。しかもでかい!

自分の顔より大きいよー。
3人で2つ頼んだけど、食べきれませんでした。

The Talk Show Live From WWDC15

The Talk Show Live From WWDC15 にも行ってきました。
HoriuchiさんがチケットをGETしてくれ、私は据え膳で。
Hostは、John Gruber。
Guestはなんとっ、Phil Schiller。
最初は冗談で言っているのかと思ったら、本当に出てきてびっくりした。
なんとも豪華なShowでした。

diversityの話とかしていたけど、会場は欧米の男の人ばかりで、アジア系でしかも女性2人の私達なんて、めっちゃ浮いてた感じですw
空気にのまれて、終了したら会場をすぐ出てしまったのだけど、今思うと、2人に近寄って一緒に写真でも撮ってくればよかったーーという後悔。

コインランドリー体験

今回、7泊したのでコインランドリーにも行ってきました。
行ったところは、BrainWashというお店。

場所はSouthOfMarketにあるのでちょっと危ない地域。
できるだけセッションに参加したかったので、朝7時30分の早朝に向かってみました。
.... 怖かった!
朝早いから、普通の人が少ない=危ない人の割合が多い。
路上で座ってじっと見ていたり、後ろからわめきながら歩いてくる人がいたりしたので、ちょービビりながら無言で早歩きで向かいました。
お店の外の植木にちょっと目をやると、注射器が落ちてるー。ひぃーーー!
でも、お店の中は結構綺麗。
そして、近くにあるSightGlassのカフェでは普通の若者がいっぱいいるとか、なんか不思議な感じ。
ちなみに、大きな乾燥機なので仕上がりはGoodでした。

Googleランチ

直前に +Shinichi Ogawaから+Takashi Matuoさんに連絡をとってもらうことができ、Googleランチに連れて行ってもらえることに! 
もちろん、平日しか空いていないので、金曜日の午後セッションをブッチしていってきました。


Appleでも思ったけど、青い空と緑。となんだかリゾート地か、郊外の大学みたいな感じ。
そこにあるBIG TABLEという、Googleらしい名前のカフェ。
そして、歩いている人はみんな天才! という素敵な空間でした。


一見、普通のカフェ?と思うこの場所、実はGoogleの中にあるコーヒーラボ。
私、ここでずっと仕事したいですw

行きは時間もなかったのでUberで行ったのですが、9,000円ぐらい。
超絶便利だったけど、1.2倍レートだったし、円安だしちょっとイタイ。
なので帰りは節約してのんびりと、カルトレインで帰りました。


カルトレインは2階建てになっていて、1階は2人席、2階は1人席が通路を挟んで左右にあります。2階の席がまた居心地がいい!眠ってしまいました...


今回、Bart, Muni Bus, カルトレイン を利用したのですが、これらで利用できて便利だった ClipperCard。

プリペイド型のカードで、Suicaみたいなもの。
SFOのInformationの窓口で購入しました。
全部使い切ることはできないと思うので無駄な出費があるわけですが、あっちの券売機って操作が難しいのでこれがあるとほんと楽。
来年も当選して、これをまた使える日がくるといいな!

さいごに

途中で風邪を引いてしまい、しんどかったけど、Horiuchiさんがいてくれたから色々頑張れた気がするー & 色々やってもらった! 感謝。

来年もし行けたら、Flask アプリのステッカーを作って鞄とかに貼っていこう。
そして、「あ、このアプリ知っているよ!」と声をかけてもらえるよう頑張るー!

• • •

2015年5月25日月曜日

BitList アプリとチャレンジ

BitList - 買い物リスト & To Doリストを先日リリースしました。 無料!

iOS標準のアプリの「リマインダー」の情報を読み書きするアプリになります。
このリマインダー、TODOを管理できるのですが、なかなか使いづらくほとんど使っていません。
だけど、唯一使っているのが、お買い物リスト としての使い方。
使っている理由としては、iCloudのログインだけで他の人と共有できるから。That's all.

だけど、残念ながらApple Watchには「リマインダー」アプリが無い...!
ということで作成したのが、BitList です。

iPhoneのアプリでは、リマインダーアプリよりも更に機能を絞って、以下のみ可能です。
  • 項目の追加をすること
  • 完了にすること
  • 削除すること
  • 優先度の有無を設定すること
機能を絞ってシンプルにして使いやすくした というのが特徴。

Apple Watchのアプリでは、
  • 完了すること
のみ可能です。

このアプリ、実はチャレンジをしました。
チャレンジとは... 「2日でアプリを作ること

2日でアプリを作るチャレンジ


1日目:午前

リマインダーがApple Watchで見れるアプリ
ということだけが決まっている状態から開始です。

どんな画面にするか を決めるのを朝10時からお昼過ぎまでかかった。
これが一番苦しかった...!
最初は、リマインダーのリスト一覧画面もある という方針で考えていたのですが、この画面はすっぱり切って一つのリストのみ扱う形にした。
切った理由としては、使いやすいUIを考えるのに時間がかかってしまう割には、必須の機能でもないから。
単純に、リストがテーブル形式で名前が並んでいる画面なら、ちょいちょいって作ればいいだけだけど、もっと負担がなく選択できて、Watch上でもいい感じなもの と考えると、アイコンを付けたいとか欲が出てくる。
リストの名前が単純に並んでいる画面が最初に出てくるよりは、無い方がイイ。 というのが私達の選択。

ということで、作成する画面は以下7つ。
  1. iPhone:アイテム一覧画面
  2. iPhone:設定画面
  3. iPhone:初期設定フロー用 リマインダーアクセス許可画面
  4. iPhone:初期設定フロー用 アクセス許可されなかった時の画面
  5. iPhone:初期設定フロー用 リマインダー選択/作成画面
  6. Watch:タスク一覧画面
  7. Watch:Glance

1日目:午後

まず、iPhoneアプリから手をつけ始める
Storyboardのファイルのみ作って、@horiuniさんにパス。
@horiuniさんは、デザインも考えつつStoryboardをゴリゴリ。
その間に、私はUI制御以外のコードをゴリゴリ。

アイテム一覧画面と、設定画面がそれとなく動くところまで仕上げて1日目完了。

2日目:午前

私は、iPhoneの初期設定フロー画面を組み込み。
@horiuniさんは、アイテム一覧画面を実際に動かしつつStoryboardでデザイン調整。

2日目:午後

Apple Watch用のアプリ作成。
アプリの名前を決める。
アプリアイコンを作成。

という感じで、なんとか、目標の2日でアプリ完成!

3日目

2日ではApp Storeへの申請までこぎつけれなかったので、ここは3日目に突入...
説明文、スクショなどなど用意して申請Done!

チャレンジの感想

@horiuniさんと一緒に作ったアプリはBitListもいれると9本目。
お互い出来ることとか、作業のススメ方などは随分わかってきたので、集中してやればできるだろう!と正直最初は簡単に思っておりました。

が、想定範囲内ではあるものの、やはり厳しかった。
実際に手を動かしている時間でれば2日は十分な時間なんだけど、どんな内容にするかの意思決定は難しいし、時間がかかる。
が、一緒にやってきた実績があり、利害関係も一致しているので、一般よりはスムーズに意思決定が出来た方だと思いますよ...!

使いやすいか? というところが、Flaskでは譲れない重要ポイント。
普段であれば、実際動かせるものを手にとって数日試してみる ということをします。
使ってみて始めて、気づくこととか、新しいアイデアも出てきたりする。
それが出来ないのは苦しかった。

そして、普通ではあれば一番大事であろう、マネタイズも考えられなかった(笑

反面、ズルズルしないというのはメリット。
私たちの2日はコレぐらい作れる という指針が見えたのも凄く良かった。

そして、なにより...達成感を味わえるのが良かった!

このアプリ、ただいま完全無料だけど、好評だったら機能追加する予定ですー!
• • •

2015年5月7日木曜日

Apple Watch Glanceのレイアウト

Glanceに望むもの

Apple Watchを手に入れ、お気に入りのGlanceを設定。
でもなかなかお気に入りのものって無かったりする。

必要な情報なのか?
というのももちろんなんですが、綺麗なものでないと置きたくない。

Glanceは、表示専用画面を一つ作るようなものなので、とても手軽に作れるわけですが、デザイン的な重要さはWatchAppアプリよりも高いのではないだろうか。
デザイン的には、全然 気軽じゃないものだ。

Glanceはテンプレートベース

ご存知の方も多いと思うが、Apple WatchのGlanceはテンプレートベースでレイアウトする。

UpperとLowerに分かれていて、それぞれにテンプレートを適用して実装していく。
UpperとLowerのサイズは決められていて変更することはできない。

どんなテンプレートがあって、それらはどんなサイズになるのか?は、
Apple WatchkitのDesign Guides and Templatesにある
Apple Watch Design Resourcesのファイル見るのが一番早い。
なぜなら、XCode上ではフォントサイズが分からないから。(=サイズの修正は不可)
このリソースにはpsdとsketchのファイルが入っているので、そこからフォントサイズが分かる。

マッチするテンプレートがあれば幸せですが、無い方が多いのではないかと思う。
その場合は、自由に設定ができるGroupで囲まれたテンプレートがあるのでそれが使える。
だが、文字を表示する場合はできるだけそれを避けて、テンプレートのLabelを使えないか検討するのをオススメしたい。
(その理由は後述)

言い換えると、Glanceのデザインを考える際、
「画面デザインを考えてから、合ったテンプレートを探す」のではなく、
「テンプレートから合うものを考える」という方がよいかもしれない。

テンプレートを使うメリット

整ったレイアウトになる
というのは言わずもがなと思うが、他にもある。

1. 端末によってフォントサイズを変えてくれる

Apple Watchには38mmと42mmの端末がある。
端末によって、フォントサイズを変えた方が整うことが多い。
Watchは画面サイズが小さいので、iPhoneよりもそういう場面が増える。

テンプレートで用意されたラベルは、自動的にサイズを変えてくれる。
例えば、
42mmの時は、San Francisco Display/Regular 45pt のものは、
38mmの時は、San Francisco Display/Light 40pt になる。

2. フォントの上下マージン

Apple Watch標準フォントのSan Franciscoには、上下にマージンがある。

テンプレートのLabelは、このフォントの上下マージンが無い状態でレイアウトしてくれる。


左はテンプレートのラベル表示。
右はLowerをGroupにして、自分でラベルを追加した時の表示。(もちろん項目間のスペースは0に設定済み)
テンプレートのように詰めてラベルをレイアウトするのは、頑張ればできなくないが結構大変だと思う。
WatchでのレイアウトではX,Yの絶対座標での位置指定はできないし、(Groupを除いて)要素を重ねて置くことはできないから。

この"詰めれない"というのが、整ったレイアウトにするハードルをかなり上げてしまう。

3. 他アプリとの統一感

Glanceではページをめくって他アプリのGlanceも見る。
その際に、フォントサイズが各アプリでバラバラよりは、統一されていたほうが見やすいに違いない。
テンプレートは色々あるが、使われているフォントサイズはほぼ決まっていて、全体でも5つぐらいしかない。

また、大きい文字(=重要な文字)がある位置も、ある程度決まる。
テンプレート全体を見渡してみても、一番大きい文字が、一番下にレイアウトされることは無い。

提供されているテンプレートを使うとよい という締めくくりになるのだが、
この数日、私は、まさにGlanceのレイアウトで悩んでいた。
数が決まっているテンプレートではあるが、選択するのはとても難しい。
でも、自分でラベルを置いてレイアウトするよりは、よっぽど楽 という結論に今は落ち着いている。
• • •

2015年4月21日火曜日

Apple Watch向けカレンダーアプリ作ってみた

Apple Watchの発売まであと数日になりましたね!
一足早く、Watch対応アプリ こよみを、本日リリースしました!


月表示のカレンダーを、Apple Watchと、Today Widget で見れるという、とってもシンプルなアプリです。
2週間後っていつだっけ? 祝日あったけ? 予定あったけ? という時に、さっと見れる。
12ページ=12ヶ月分表示しているので、スワイプでページをめくって翌月、翌々月のカレンダーへとさっと移動できます。(これ、多分標準のカレンダーアプリではできないはず)
そして、Flaskらしく、いろんな色のテーマから好きなものを選んで、ちょっと見てて嬉しくなれるものを作りました。

本体は無料ですので、Apple Watchを手に入れ、"さて、なにか対応アプリないかしら?" という時にはぴったりと思うので、ぜひ試してください!
会議中はPCやiPhoneを見ないように気をつけている なんて人にもよいかも..です。

(週刊アスキー AppleWatch特集の、「アプリ開発者に聞く」というコーナーで、この、こよみのアプリとともに、私も少し登場しております。それもあり、本日リリースとなりました。)

Apple Watch向けアプリの検討

Watch対応アプリを検討する際、"Apple Watchで見たいものってなんだろう?" と随分悩みました。
サードパーティで実現できることが限られているし、iPhoneで十分では?と思う内容になってしまったりと、難しかった。
結局私では、いいアイデアは出てこなかったのですが、他の方がどんなものを仕上げてくるのかがとっても楽しみです。

さらに、実機がないので、実際にはどう動くのか分からない苦しみ!
Watch上でアプリが切り替わったら、アプリは終了するのか、しないのか?
iPhoneロック中ではどんな動きになるのか?
位置情報やカレンダー、通知などで許可を求める必要がある時、Watchではどうなるのか? などなど不明点がいっぱい。
不明のまま実装していく開発者の皆さん、本当に大変だと思います...
そこ大変だったよね! 途中で、アレが変更になってうぉーとなったよねー! と、気持ちを分かち合いたい....!!(募集中)

チャレンジしたアプリは実は3つ(+1)

実は、Flaskでは3つのアプリをApple Watchに向けて準備していました。
あとの2つは、「Timesheet - 作業時間ログ」と、「時差時計 Timelet」です。

Timesheet - 作業時間ログは、Apple Wachで、アラーム通知の表示、ストップウォッチの開始/停止 できるようになりました。(既にリリース済み)
このアプリでは、作業を開始してから、20分毎に通知が届く という機能があります。
通知は、どれだけ時間がたったのか?の把握と、作業終了のSTOPのし忘れ防止 のためにあるもので、Watchで受けるにはピッタリかな と。
いろんな通知がいっぱい届くのは好きになれない私ですが、今まさに作業している事についての通知なら、「是非きて!」 と思える。なので、入れてみた。

時差時計 Timeletは、Timeletは、「あっちの何時は、こっちの何時?」が、すぐにわかるシンプルな世界時計。
時差時計だから、Apple Watchにぴったりな訳なのですが、残念ながら審査が通りませんでした。
現時点では、時計のアプリはNGのようです。
たとえ、時差が表示する機能が別にあったとしても...

「時計を表示するアプリをサッとつくってアプリを出してみよう!」 と、Hello Worldのような手軽さで作られるのを避けているのでしょうか。
もしくは、Android WearのWatch Faceのような拡張を考えているから、それまで待ってね ということだったり??
真偽は不明ですが、ま、"使う人にとって良い、意味あるものを" と精査してくれているのはAppleのいいところなので、しょうがないですね。
もし、時計アプリを作ろうとしている人がいたら、それはNGかもしれないという危険性を認識しておいてもらえると、いいかと思います。

そして +1。
FitPort ヘルス&フィットネスダッシュボード」についても、歩数やFloor数をWatchで見てみたい と思いましたが、こちらも断念。
詳しくは、ほりうちさんのブログ FitPortはApple Watchに片思い でどうぞ。

Apple Watchは普及するのか?


値段もそこそこするし、果たして普及するのか? は謎ですが、開発者としては実際使ってみないと分からないので購入せざるを得ない訳で、42mm/Sports を予約。
42mmにしたのは、App Store用のスクショで42mmサイズのものが必要だから。
背景がBlurになっているGlanceや通知などのスクショを撮るには本体でないと撮れませんから!
Sportsなのは、次世代のものがでたら、どうせまた買わなくてはいけないだろうから、安いもので。

この選択からも察するように、正直、私は懐疑派です(笑)
だけど、値段は高いけど、オモチャのような見かけではなく、そこそこ高級感があって大人でも違和感なく付けれそう。
一番大事な時計の機能は、標準でいろいろある。(サードパーティに依存していない)
という、Apple Watchの方針はいいと思う。

気になるのは、バッテリー時間と大きさ
時計しかほとんどみないのであれば、年単位で動き続ける、普通の時計のほうがよっぽどいい。
時計以外の機能を、果たしてどれくらい見て、便利とおもえるのか?
せめて、1週間ぐらいバッテリーがもてば、数ヶ月は着け続けられるきがするのですが...ね。

大きさについては、Android Wearに比べたら思ったより小さいと感じたけど、服によっては袖口の下に入らないかもしれない。
だが、あれ以上小さくなったら触るのが難しいと思うので、今後は更に小さくなるというのは多分無いのでは?と思う。

こうなると、Apple Watchというより、スマートウォッチは有りなのか?
という議論になっていくわけですが、 この業界が進化していく過程で、この、"スマートウォッチを試す" というフェーズは多分無くてはならないもので、
Appleだったら成功するのか? Appleでも失敗するようだったら...?
と、見守っているのは、IT業界に限らず、時計業界もですよね。たぶん。


嗚呼! そんな流れの真っ只中にいるの、楽しいですね!!
• • •

2015年3月25日水曜日

iOSのFacebook Eventカレンダーの謎

iOSで設定されているカレンダーの一覧は

let store:EKEventStore = EKEventStore()
let cals = store.calendarsForEntityType(EKEntityTypeEvent)

で取得ができます。

EKEventStoreを廃棄した後に、 取得したEKCalendar#source や EKCalendar#description などを使おうとすると、以下のようなエラーを吐いて、クラッシュします。

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI sourceType]: unrecognized selector sent to instance 0x7fc7e2cba4b0'
*** First throw call stack:
(
 0   CoreFoundation                      0x0000000106c7da75 __exceptionPreprocess + 165
 1   libobjc.A.dylib                     0x00000001087d5bb7 objc_exception_throw + 45
 2   CoreFoundation                      0x0000000106c84d1d -[NSObject(NSObject) doesNotRecognizeSelector:] + 205
 3   CoreFoundation                      0x0000000106bdc9dc ___forwarding___ + 988
 4   CoreFoundation                      0x0000000106bdc578 _CF_forwarding_prep_0 + 120
 5   EventKit                            0x0000000106945475 -[EKCalendar type] + 67
 6   EventKit                            0x0000000106945649 -[EKCalendar description] + 97

まぁこれは、そういうもんなんだろうなー! という感じですよね。
多分、SourceはEKEventStoreで保持されていて、storeが廃棄されたらSourceも廃棄(nullじゃなくてなんか違うオブジェクトになっちゃう感じ?)になるとか、そんな感じだろうと。
が、ここで謎が一点浮上。

iPhoneに、Facebookアカウントの"Facebook Events"というカレンダーがあります。
このカレンダーは、EKCalendarでみると、
title は "Calendar"、 source は "CalDAV" になります。

"Facebook Events"という名前で取得できればいいのにー と思っていたのですが、
なぜか
EKEventStoreを廃棄した後に、EKCalendarのタイトルを見ると、"Facebook Events" という名前に変わっている!
(titleであれば、storeを廃棄した後でもクラッシュせずに取得できる)

わざとEKEventStoreを廃棄すれば、そのカレンダーはFacebook Eventsなのかどうかが判断出来るという。。。
• • •

2015年3月23日月曜日

iOS8.2でHealthKitが早くなったよ

iOS8.2になって、HealthKitからの情報読み込みがぐーんと早くなりました!

多分いちばん影響しているのが、iPhoneのモーションセンサーが記録している歩数などの記録間隔が長くなったこと。

iOS8.1では、1分間に2つぐらいのレコードが記録されていましたが、
iOS8.2では、5分に1つぐらいのレコードが記録されています。

そのため、全体のレコード数が随分少なくなりました。
分単位で細かい情報が知りたいということのほうがマレだと思うので、良い改善だったのではないかと思います。

「HealthKit試してみたけど、おもすぎて使えないー」と思っていた方がいたら、もう一度確認してみてもよいかもです!
FitPortアプリのグラフ表示も快適に♪

いやー、良かった良かった。


• • •

2015年3月6日金曜日

CocoaPodsで共通ライブラリ

CocoaPods を使って、自社用の共通ライブラリを整備中。
リソースファイルの扱いをどうすると一番やりやすいかを今日は悩んでいたのでメモ。

podsspecに下記のように書くと、そのリソースが MainBundleに含まれる。

spec.resources = "My/Resources/*"

この場合は、名前の重複に気をつける必要がアリ。
出来る限り、本体のアプリには影響を与えないようにライブラリを作成したいので、Bundleを試してみた。

Bundleの指定

spec.ios.resource_bundle = {'My-Resources' => ["My/Resources/*"]}

のようにかけば別のBundleになる。
上記の例だと、My/Resources/ 以下を全て含んだ My-Resources という名前のBundleができる。

ローカライズのファイル

ja.lproj/Localized.strings
en.lproj/Localized.strings

などは、
ja.lproj, en.lproj のディレクトリも含めてバンドルにする必要があるので、

Resources/* 

のようなディレクトリ指定にする。

Resouces/**/Localized.strings

のようなファイル指定だとディレクトリが出来ないのでダメ。

Bundleからリソース読み込み

NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"My-Resources" ofType:@"bundle"];
NSBundle *bundle = [NSBundle bundleWithPath:bundlePath];

のBundleを使用する。

ローカライズの文字列を取得するには

NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment)
で取得が可能。
Bundleにすると、Key名の重複を気にしなくてもいいのがマル。

Storyboardの扱い

Bundleの中にStoryboardファイルを入れた場合、

UIStoryboard#storyboardWithName:(NSString *)name bundle:(NSBundle *)storyboardBundleOrNil;

にて、bundleを適切なものを設定すれば読み込める。

ただし、Storyboardに画像を設定していて、その画像をAssetCatalog(xcassets)で管理している場合は、以下のメッセージとともに、画像読み込みが出来なかった。

Could not load the "imageName" image referenced from a nib in the bundle with identifier "(null)"

bundle identifierが(null)となっているのが気になるが、そういうものなのだろうか...

AssetCatalogで管理しているUIImageの扱い

StoryboardをBundleにした場合、上述のように画像読み込みが出来なかった訳だが、
StoryboardとAssetCatalog (Images.xcassets)を同じBundleにしても同様に読み込みは出来なかった。

NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"My-Resources" ofType:@"bundle"];
NSBundle *bundle = [NSBundle bundleWithPath:bundlePath];
UIImage *image = [UIImage imageNamed:@"btnName" inBundle:bundle compatibleWithTraitCollection:nil];

としても UIImageは取得出来ない。

[UIImage imageNamed:@"btnName"]

でなぜか画像が取得できる。

ということで、
AssetCatalog (Images.xcassets) をBundleに入れたとしても、
コンパイルしてAssets.carにまとめられるときに、MainBundleのものとして生成されているのと想像。

試してはないが、AssetCatalogを使わず、画像ファイルを直接扱えば、別Bundleとして扱える気がする。

AssetCatalogを使う場合には、画像名が重複しないようにプレフィックスなどをつける運用が必要。

現状まとめ

私の場合、StoryboardやAssetCatalogはデザイナーさんに通常のプロジェクトと同じように触って欲しいので、あれこれせずにMainBundleとして取り込む方針に。

  • Storyboard はBundleにしない
  • 画像名とStoryboard名は重複しないようにプレフィックスをつける運用へ
  • • • •

    2015年2月12日木曜日

    Extensionとアプリ本体間でイベント通知

    Today ExtensionやWatch Extensionはアプリ本体とは別のアプリになる。
    なので、NSUserDefaults に Observerを追加しても反応しないようだ。

    [NSUserDefaults standardUserDefaults] でも
    [[NSUserDefaults alloc] initWithSuiteName:APP_GROUP_NAME] でも反応してくれない。

    通知を渡すためには、CFNotificationCenterGetDarwinNotifyCenter を使うとできるらしい。

    - (void)addObserver {
    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
    NULL,
    MyCallBack,
    CFSTR("MyNotificationName"),
    NULL,
    CFNotificationSuspensionBehaviorDeliverImmediately);
    }
    static void MyCallBack(CFNotificationCenterRef center, void *observer, CFStringRef name,const void *object, CFDictionaryRef userInfo) {
    NSLog(@"observer: %@", observer);
    NSLog(@"name: %@", name);
    NSLog(@"userinfo: %@", userInfo);
    NSLog(@"object: %@", object);
    }
    - (void)postNotification {
    CFNotificationCenterRef notification = CFNotificationCenterGetDarwinNotifyCenter();
    CFNotificationCenterPostNotification(notification, CFSTR("MyNotificationName"), NULL, NULL, YES);
    }
    view raw Post hosted with ❤ by GitHub

    Watchではまだ試してないけど、Todayでは動いたので多分いけるのだろう。
    • • •

    2015年1月10日土曜日

    ObjCのコードフォーマット

    新年になって、いままで後回しになっていObjectCのコードフォーマットを見てみました。

    まず見てみたのは、uncrustify
    ポチポチ調べながら作成し..どんどんもっと触りたくなってき来た所でやめた。
    特にこだわりなはい と思っていたのですが、アレコレ設定を弄りたくなるもんだ。
    これが途中まで作っていたconfig。
    #
    # uncrustify config file for objective-c and objective-c++
    #
    newlines = auto # auto/lf/crlf/cr
    utf8_force = true
    output_tab_size = 4 # new tab size
    #
    # Indenting
    #
    indent_with_tabs = 0 # 0=space, 1=indent to level only, 2=indent with tabs
    indent_columns = output_tab_size
    indent_label = 2 # pos: absolute col, neg: relative column
    indent_align_assign = false
    indent_switch_case = indent_columns
    #
    # Inter-symbol newlines
    #
    nl_enum_brace = remove # "enum {" vs "enum \n {"
    nl_union_brace = remove # "union {" vs "union \n {"
    nl_struct_brace = remove # "struct {" vs "struct \n {"
    nl_do_brace = remove # "do {" vs "do \n {"
    nl_if_brace = remove # "if () {" vs "if () \n {"
    nl_for_brace = remove # "for () {" vs "for () \n {"
    nl_else_brace = remove # "else {" vs "else \n {"
    nl_while_brace = remove # "while () {" vs "while () \n {"
    nl_switch_brace = remove # "switch () {" vs "switch () \n {"
    nl_brace_while = remove # "} while" vs "} \n while" - cuddle while
    nl_brace_else = remove # "} else" vs "} \n else" - cuddle else
    nl_func_var_def_blk = 1 # add new lines after a block of variable decls
    nl_fcall_brace = remove # "list_for_each() {" vs "list_for_each()\n{"
    nl_fdef_brace = remove # "int foo() {" vs "int foo()\n{"
    nl_after_return = false
    # Force a newline in a define after the macro name for multi-line defines.
    nl_multi_line_define = true # false/true
    # Add a newline between ')' and '{' if the ')' is on a different line than the if/for/etc.
    # Overrides nl_for_brace, nl_if_brace, nl_switch_brace, nl_while_switch, and nl_catch_brace.
    nl_multi_line_cond = true # false/true
    # The number of newlines after '}' of a multi-line function body
    nl_after_func_body = 2 # number
    # The number of newlines after '}' of a single line function body
    nl_after_func_body_one_liner = 2 # number
    #
    # Source code modifications
    #
    mod_paren_on_return = ignore # "return 1;" vs "return (1);"
    mod_full_brace_if = ignore # "if (a) a--;" vs "if (a) { a--; }"
    mod_full_brace_for = add # "for () a--;" vs "for () { a--; }"
    mod_full_brace_do = ignore # "do a--; while ();" vs "do { a--; } while ();"
    mod_full_brace_while = remove # "while (a) a--;" vs "while (a) { a--; }"
    mod_full_brace_nl = 3 # don't remove if more than 3 newlines
    mod_add_long_ifdef_endif_comment = 20
    mod_add_long_ifdef_else_comment = mod_add_long_ifdef_else_comment
    mod_add_long_switch_closebrace_comment = mod_add_long_ifdef_else_comment
    mod_add_long_function_closebrace_comment = mod_add_long_ifdef_else_comment
    #
    # Inter-character spacing options
    #
    #sp_return_paren = force # "return (1);" vs "return(1);"
    sp_sizeof_paren = remove # "sizeof (int)" vs "sizeof(int)"
    sp_before_sparen = force # "if (" vs "if("
    sp_after_sparen = force # "if () {" vs "if (){"
    sp_after_cast = remove # "(int) a" vs "(int)a"
    sp_inside_braces = add # "{ 1 }" vs "{1}"
    sp_inside_braces_struct = add # "{ 1 }" vs "{1}"
    sp_inside_braces_enum = add # "{ 1 }" vs "{1}"
    sp_inside_fparen = remove # "func( param )" vs "func(param)"
    sp_inside_sparen = remove
    sp_paren_brace = force
    sp_sparen_brace = force # Add or remove space between ')' and '{' of 'if', 'for', 'switch', and 'while'
    sp_fparen_brace = force # Add or remove space between ')' and '{' of function
    sp_assign = add
    sp_arith = add
    sp_bool = add
    sp_compare = add
    sp_assign = add
    sp_after_comma = add
    sp_func_def_paren = remove # "int foo (){" vs "int foo(){"
    sp_func_call_paren = remove # "foo (" vs "foo("
    sp_func_proto_paren = remove # "int foo ();" vs "int foo();"
    sp_before_ptr_star = force
    sp_after_ptr_star = remove
    sp_before_unnamed_ptr_star = ignore
    sp_between_ptr_star = remove
    sp_after_ptr_star_func = force
    sp_before_ptr_star_func = force
    sp_cmt_cpp_start = add
    sp_cond_question = force
    sp_cond_colon = force
    sp_else_brace = force
    sp_brace_else = force
    sp_after_class_colon = force
    sp_before_class_colon = force
    sp_before_case_colon = force
    # Objective-C specifics
    sp_before_oc_colon = remove # '-(int) f: (int) x;' vs '-(int) f : (int) x;'
    sp_after_oc_colon = remove # '-(int) f:(int) x;' vs '-(int) f: (int) x;'
    sp_before_oc_dict_colon = remove # '@{@"foo": @"bar"};'
    sp_after_oc_dict_colon = remove # '@{@"foo" :@"bar"};'
    sp_before_send_oc_colon = remove # '[object setValue:1];' vs '[object setValue :1];'
    sp_after_send_oc_colon = remove # '[object setValue:1];' vs '[object setValue: 1];'
    sp_after_oc_scope = force
    sp_after_oc_type = remove # '-(int)f: (int) x;' vs '-(int)f: (int)x;'
    sp_after_oc_return_type = remove # '-(int) f:(int)x;' vs '-(int)f:(int)x;'
    sp_after_oc_at_sel = remove # '@selector(msgName)' vs '@selector (msgName)'
    sp_before_oc_block_caret = ignore # '^int (int arg){...}' vs. ' ^int (int arg){...}'
    sp_after_oc_block_caret = remove # '^int (int arg){...}' vs. '^ int (int arg){...}'
    sp_cond_colon = force # Add or remove space around the ':' in 'b ? t : f'
    sp_cond_question = force # Add or remove space around the '?' in 'b ? t : f'
    #
    # Aligning stuff
    #
    align_with_tabs = false # use tabs to align
    align_on_tabstop = false # align on tabstops
    align_keep_tabs = false
    align_enum_equ_span = 4 # '=' in enum definition
    align_struct_init_span = 4 # align stuff in a structure init '= { }'
    align_right_cmt_span = 8 # The span for aligning comments that end lines (0=don't align)
    align_right_cmt_gap = 8
    align_pp_define_span = 8
    align_typedef_span = 5
    align_typedef_gap = 3
    # Objective-C specifics
    align_oc_msg_colon_span = 20 # Span for aligning parameters in an Obj-C message call on the ':' (0=don't align)
    align_oc_msg_spec_span = 0 # The span for aligning ObjC msg spec (0=don't align)
    #align_oc_decl_colon = true
    #
    # Line Splitting options
    #
    # ls_func_split_full = true # Whether to fully split long function protos/calls at commas
    #
    # Comment modifications
    #
    cmt_star_cont = true # Whether to put a star on subsequent comment lines
    eat_blanks_before_close_brace = true # Whether to remove blank lines before '}'
    eat_blanks_after_open_brace = true # Whether to remove blank lines after '{'
    ちなみに、字下げはK&Rのスタイルでなくて、Javaスタイル。
    Javaの経験が長かったので目と手が慣れているというのが一番の理由なのだけど、迷わないのであれば無駄に改行をいれない方がさっと見渡せるしいいんじゃないかと思っている。
    K&Rにしたければまたフォーマットすればヨシ。

    ここまでイジってみたが、気にいらなかったのが、
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
    dispatch_async(dispatch_get_main_queue(), ^{
    });
    });
    view raw gistfile1.txt hosted with ❤ by GitHub
    という書き方。
    結構記述する事が多い書き方なんですが、blockの中のコードのインデントが、( までインデントされてしまう。
    4つスペースのインデントで十分だし、XCodeのフォーマットもその形なので合わせたかったのだが、方法がさっとわからなかった。

    いやいや、自我を捨てて標準的なフォーマットになればそれでOKだよ
    と思い直す。

    そこで、次にClangFormatを試す
    そこにある、GoogleのルールをベースにColumnの制限なしにして、インデントを4に変更した。
    BasedOnStyle:  Google
    ColumnLimit: 0
    IndentWidth: 4
    
    これで良い気がする。
    どこまで細かくフォーマットしてくれているのかが良くわからないが、暫くこれを使って運用してみよう。
    • • •