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しにくくなったりするんですよ。
たとえば、最初は全部隠れているけど、アニメーションで動かして画面が出てくることをしたいとか。
うーん、悩ましい。
• • •