【OSX】Viewにブラーをかける

![advent_calender_8.png](https://qiita-image-store.s3.amazonaws.com/0/61792/8a78ea21-7615-1ed4-1825-01a9fd949462.png)

この記事は [Goodpatch Advent Calendar 2015](http://qiita.com/advent-calendar/2015/goodpatch) 8日目の記事です。昨日は 
@subuta_nico の[Wallaby.jsを活用したテスト駆動開発](http://sub-labo.com/2015/12/07/introduction-to-wallabyjs/)についてでした！

---
こんにちは@hanamijuです。GoodpatchでOSXアプリを開発しています。

OSX Yosemiteからツールバーやサイドメニューなどに、擦りガラスのようなブラー効果をかけれるようになりました。こちらのやり方についてご紹介します。

# とりあえずブラーをかける

ブラーのかかったViewを表示するためにYosemite以降では特別がんばる必要はありません。
実際に表示するためにはNSViewをいじるのではなく、NSVisualEffectViewというNSViewのサブクラスを使用します。


## やり方
Interface BuilderのViewのクラス名をいじるだけです。
![スクリーンショット 2015-12-05 17.03.15.png](https://qiita-image-store.s3.amazonaws.com/0/61792/559e42fd-1ff4-3014-b8e5-fab528d1bafc.png)


## 結果
![スクリーンショット 2015-12-05 15.25.37（2）.png](https://qiita-image-store.s3.amazonaws.com/0/61792/8c2fef58-0be0-af5c-8773-fe12d5a92188.png)


超簡単ですね。
ちなみに、Inteface BuilderにもNSVisualEffectViewがあります。

<img width="1515" alt="スクリーンショット 2015-12-05 15.24.20.png" src="https://qiita-image-store.s3.amazonaws.com/0/61792/bfda1318-b92a-e1ee-70da-1b334b98f710.png">

ここからViewに設定してもブラーのかかったViewを表示することができます。

# ブラーをかける対象について(In behind-window or With in-window)
NSVisualEffectViewがブラーをかける対象は以下の二つがあります。

* In behind-window (ブラーがかかっているViewの背景にブラーをかける)
* With in-window (ブラーがかかっている同一ウィンドウ内でブラーをかける)

## In behind-window
こちらのわかりやすい例はFinderのサイドメニューです。ウィンドウの外側（の壁紙）に対してブラーがかかっています。背景が透けているのでウィンドウレベルでの階層の視覚化ができます。

![finder_sidebar_2x.png](https://qiita-image-store.s3.amazonaws.com/0/61792/c1dca9b4-d60a-c99a-66ff-cd5834408865.png)

## With in-window 
こちらのわかりやすい例としてはMapsの経路表示です。ウィンドウ内の階層を意識させたい時に使えます。
↓では経路表示のViewのブラーはその下のMapに対してかかっていることがわかります。

![overlay_defers_2x.png](https://qiita-image-store.s3.amazonaws.com/0/61792/736bffdf-18fe-e515-5d30-7937fa6fca64.png)


## やり方
NSVisualEffectViewの```blendingMode```を変更するだけです。
```blendingMode```を```WithinWindow```にする場合は`view.wantsLayer = true`にする必要があります。

```swift:
effectView.wantsLayer = true             // これないと実行時に落ちる。
effectView.blendingMode = .WithinWindow
```

# ブラーの上でベタ塗りのViewを重ねたい
NSVisualEffectViewにControlを乗せるといい感じに色がブレンディングされます。
いちいちControlの設定変更を行わないで済むので楽なのですが、ブラーの上にブラーのかかっていないViewを重ねたい場面もあると思います。簡単にできそうなのですがちょっとつまづいたのでやり方を残します。

## サンプル
以下のように、ブラーの上にベタ塗りのViewを貼り付けるサンプルを作ります。

![スクリーンショット 2015-12-06 13.40.21.png](https://qiita-image-store.s3.amazonaws.com/0/61792/d4a45cac-bd57-14f0-3507-a8fdff05cd30.png)

### StoryBoardをいじる
View → VisualEffectView → ベタ塗りしたいView(今回はHogeView[^注1]) → その上のコントロール　っていう階層でコンポーネントを配置します。
[^注1]: HogeViewはNSViewのサブクラスです。

<img width="1515" alt="スクリーンショット 2015-12-06 13.45.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/61792/cd222339-4583-8112-b07d-bbfc7eaeee50.png">


余談ですが、VisualEffectViewのAppearanceはデフォルトでDarkに設定されているのでInterfaceBuilder上で設定を変えるにはView - Appearanceの設定をVibrant Lightに設定します。
（VisualEffectViewの項目になかったので一瞬えっ！ってなりました...）

### この状態でビルドしてみた

![スクリーンショット 2015-12-06 16.36.59.png](https://qiita-image-store.s3.amazonaws.com/0/61792/1a6f4a6d-1d5b-6515-118c-5876eba97422.png "スクリーンショット 2015-12-06 16.36.59.png")

このように、HogeViewがブラーに同化されてしまいました。
以下で、背景色を設定していきます。

### コーディング
普通に実装すると```drawRect()```で```NSColor.whiteColor().set()```的なことをする[^2]のですが、これだと上図と同じくHogeViewにブラーがかかったままになります。
[^2]: NSViewにはUIViewみたいな```BackgroundColor```的な**親切プロパティ**は存在しません。(InterfaceBuilderからも色の設定はできない。)

それじゃどうするのかというとCALayerレベルで設定します。
まずHogeViewをOutletに設定して```wantsLayer = true```します。
その後HogeViewの`layer.backgroundColor`で背景色の設定をします。

```swift:ViewController.swift
import Cocoa

class ViewController: NSViewController {

    @IBOutlet weak var hogeView: HogeView!
    override func viewDidLoad() {
        super.viewDidLoad()

        hogeView.wantsLayer = true
    }
}

class HogeView: NSView {

    override func drawRect(dirtyRect: NSRect) {
        super.drawRect(dirtyRect)
        //NSColor.whiteColor().set()      <- コレ呼んでも背景ベタ塗りされないよ
    }
    
    override func layout() {
        super.layout()
        self.layer?.backgroundColor = NSColor.whiteColor().CGColor // 背景色
        // お好みでどうぞ
        self.layer?.borderColor = NSColor.grayColor().CGColor // ボーダー色
        self.layer?.borderWidth = 2 // ボーダーの太さ
        self.layer?.cornerRadius = 8 // 角丸の半径
    }
}

```
## 結果
できて............ない！

<img width="562" alt="スクリーンショット 2015-12-06 14.19.56.png" src="https://qiita-image-store.s3.amazonaws.com/0/61792/98d51b09-5ecb-061e-44e8-1e85acf7c8ed.png">

Labelの背景にブラーがかかっています。

これを直すにはLabelの背景色を透明にする必要があります。
InterfaceBuilder上でDisplay - Draws Backgroundにチェックを入れた状態でBackgroundのAlphaを0に設定します。

<img width="764" alt="スクリーンショット 2015-12-06 14.22.06.png" src="https://qiita-image-store.s3.amazonaws.com/0/61792/6a927ff0-f600-c84d-12b9-0d9c7253b2d6.png">

ちょっとイマイチな感じですが、これで最初の画像のように上手く表示することができます。

# TableViewにブラーをかけたい
TableView(OutlineView)にブラーをかける時はNSVisualEffectViewを使用するのではなく、TableView自体にブラー設定するプロパティが存在するのでそれを使います。

## やり方
InterfaceBuilderから対象のTableViewを選択して、TableView - Highlightの項目をSource Listにするだけです。

![スクリーンショット 2015-12-06 12.59.56.png](https://qiita-image-store.s3.amazonaws.com/0/61792/55a2a59c-0d3b-44ad-7d7d-4839d1c61012.png)


# Cellの選択状態の背景色をブラーに設定したい
私が調べた限りではFinderのサイドバーのように、TableViewにあるCellが選択状態の時の背景をブラーに設定する方法は見つかりませんでした。（知っている方いたら教えて欲しいです。）

![スクリーンショット 2015-12-06 17.02.16.png](https://qiita-image-store.s3.amazonaws.com/0/61792/9fcb1462-bfc4-ded9-7bfb-199debe32310.png "スクリーンショット 2015-12-06 17.02.16.png")

ですが、それっぽく見せる方法は存在します。

## やり方
TableViewのサブクラスを作り、FirstResponderを受け付けなくすると選択した時に濃いブラーがかかった状態になります[^注3]。

[^注3]: FirstResponderを受け付けなくするとセルにフォーカスが当たらなくなるので作るアプリによっては注意が必要です。

以下のコードをTableViewのサブクラスに記述します。

```swift:MyTableView.swift
override var acceptsFirstResponder: Bool { return false }
```

参考：[How to get the source lists selection highlight to use the Dark Vibrancy appearance in OS X 10.10?](http://stackoverflow.com/questions/26596621/how-to-get-the-source-lists-selection-highlight-to-use-the-dark-vibrancy-appeara)

# おわりに
以上、NSViewのブラーまわりの話でした。
[公式ドキュメント](https://developer.apple.com/library/mac/documentation/Foundation/Reference/NSVisualEffectView_Class/)以外あんまり情報が転がってないのでちょっと調べるのが手間でしたが分かれば簡単です。

明日はGoodpatchのProject Managerの@Hasedaから、[エディタ初心者がSublimeTextでHTML5/CSS3を書く環境を作るのに苦労した話](http://qiita.com/Haseda/items/028e8f06dc79c4ccec43)をお届けします。お楽しみに！