Swiftで3D Touch

#3D Touchについて
##実装に必要なもの
####シミュレータで見る場合
・Xcode7.3以上
・iPhone 6s/6s Plus以降
・iOS9以上
・Force touchが使えるトラックパッドが必要   
####実機で見る場合
・Xcode7.1以上
・iPhone 6s/6s Plus以降
・iOS9以上
   
デフォルトでは3D Touchできるようになっているが、無効にしている場合は
`設定>一般>アクセシビリティ> 3Dタッチ`
で設定し直してください。
因みにここで感度を変える事ができ、3D Touchのテストもできます。

##用語一覧 [Appleの3D Touch](http://www.apple.com/jp/iphone-6s/3d-touch/)
Peek And Pop(軽くプレス時に表示される) | Peek Quick Actions(Peek時に上にスワイプして出てくる) | Home Screen Quick Action(アイコンをプレスすることで現れるアクション)
---- | ---- | ----
<img src="https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Adopting3DTouchOniPhone/Art/peek_2x.png" width="500"/> | <img src="https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Adopting3DTouchOniPhone/Art/peek_quick_actions_2x.png" width="500"/> | <img src="https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Adopting3DTouchOniPhone/Art/maps_directions_home_2x.png" width="500"/>

####UIViewControllerPreviewingDelegateプロトコル
Peekとして表示される 

```.swift
func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {}
```
とPopする直前に呼ばれる処理

```.swift
func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController) {}`    
```
が必須です。

ビューをPeek and Popの対象にする

```.swift
if self.traitCollection.forceTouchCapability == UIForceTouchCapability.Available {
  registerForPreviewingWithDelegate(self, sourceView: view)
}
```
####実装方法

```.swift
extension ViewController: UIViewControllerPreviewingDelegate {
  func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
      let secondVC = storyboard?.instantiateViewControllerWithIdentifier("secondVC") as! SecondViewController
      // Peek(軽くプレス)で呼ばれるViewController
      return secondVC
  }
  func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController) {
      // viewControllerToCommitはPeek時にセットされたViewController
      // 今回はNavigationViewControllerを継いでるのでshowViewControllerを使うがそうじゃない場合はpresentViewControllerでモーダル表示
      showViewController(viewControllerToCommit, sender: self)
      
      // Peekとは違う画面をPopで表示する事もできる
      // let thirdVC = storyboard?.instantiateViewControllerWithIdentifier("thirdVC") as! ThirdViewController
      // showViewController(thirdVC, sender: self)
  }
```
上記のコードの説明
1. previewingContext
プレビューのコンテキストオブジェクトで
previewingContextにはsourceViewがあります。

```.swift
if previewingContext.sourceView is UIButton {
    // do something
  } else if previewingContext.sourceView is UILabel {
    // do something
  }
```
こんなことができる。

2.location
tableView.indexPathForRowAtPoint(location)
TableViewを使っている時のどのセルをプレスされているかが取得できる

3.ViewControllerではなく、nilを返す事で無効にできる
###Home Screen Quick Action
####実装方法
やり方は2通りあり、
・info.plistに追加する方法
・コードで追加する方法
があります。

#####info.plistで実装

```.xml
<key>UIApplicationShortcutItems</key>
    <array>
        <dict>
            <key>UIApplicationShortcutItemIconType</key>
            <string>UIApplicationShortcutIconTypePlay</string>
            <key>UIApplicationShortcutItemTitle</key>
            <string>SEARCH</string>
            <key>UIApplicationShortcutItemType</key>
            <string>$(PRODUCT_BUNDLE_IDENTIFIER).Search</string>
        </dict>
        <dict>
            <key>UIApplicationShortcutItemIconType</key>
            <string>UIApplicationShortcutIconTypeAdd</string>
            <key>UIApplicationShortcutItemTitle</key>
            <string>ADD</string>
            <key>UIApplicationShortcutItemType</key>
            <string>$(PRODUCT_BUNDLE_IDENTIFIER).Add</string>
        </dict>
    </array>
```
これでホームアイコンをプレスするとADDとPLAYが表示される。

#####コードで実装する方法

```.swift
let shortcut = UIMutableApplicationShortcutItem(type: "UIApplicationShortcutIconTypePlay", localizedTitle: "SEARCH", localizedSubtitle: "再生する", icon: UIApplicationShortcutIcon(type: .Play), userInfo: nil)
            UIApplication.sharedApplication().shortcutItems = [shortcut]
```

コードとplistでの実装の違い
コードで実装する場合、アプリをインストールしてから一度アプリを起動する必要がある。
info.plistで実装する場合、アプリをインストール直後から実装されるが、動的に変えることができない。

下の画像は[3D Touch の Home Screen Quick Actions に対応する](http://qiita.com/takecian/items/d417498cfe6c3b52c4bc)から引用

iOS9.0で使えるアイコン達 |
---- |
<img src="http://cdn-ak.f.st-hatena.com/images/fotolife/k/kitoko552/20151013/20151013111615.png" width="300"> |
つまり上記のコードではADDとPLAYが表示されます。   

この中にアイコンがない場合は(自作でやりたい場合)

```.xml
<dict>
    <key>UIApplicationShortcutItemIconFile</key>
    <string>(画像)</string>
    <key>UIApplicationShortcutItemTitle</key>
    <string>SEARCH</string>
    <key>UIApplicationShortcutItemType</key>
    <string>$(PRODUCT_BUNDLE_IDENTIFIER).Search</string>
</dict>
```

UIApplicationShortcutItemIconFileを設定するとUIApplicationShortcutItemIconTypeは強制的に適用されなくなるので意味なくなります。

アイコンをタップ時に通知されるメソッドはAppDelegateにあり、

`func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void)`

で取得する事ができ、実装方法としては

```.swift
extension AppDelegate {
    
    private enum ShortcutItemType: String {
        case Play
        
        init?(shortcutItem: UIApplicationShortcutItem) {
            guard let last = shortcutItem.type.componentsSeparatedByString(".").last else { return nil }
            self.init(rawValue: last)
        }
        
        var type: String {
            return NSBundle.mainBundle().bundleIdentifier ?? "" + ".\(rawValue)"
        }
    }
    
    func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {
        guard let itemType = ShortcutItemType(shortcutItem: shortcutItem) else { return }
        switch itemType {
        case .Play:
            // do something
            break
        }
    }
    
}
```
こんな感じで取得する事ができます。

### [iOS Human Interface Guidelines](https://developer.apple.com/ios/human-interface-guidelines/interaction/3d-touch/)
- PeekのViewでボタンの要素などをおくのはやめる。
ユーザーがタップしようとして指を離したときに、Peekが消えるので

- Peekだけというのは避ける 
Peek And Popは一貫して採用、Peekしかできない場合ユーザーのデバイスに問題があるのではないかと思われる為

- Popを表示するためのアクションボタンをPeek quick actionsのボタンとして出すのはやめる。
表示するために深くプレスするので。

- Peek quick actionsのアクションでしかできないことにしないこと
必ずしも全てのデバイスが3D Touchをサポートしてるとは限らないので

- 適切なアクションボタンを用意しよう

- Peekingと全く同じ編集ボタンを用意するのはやめよう