iOSでちょっと多めのツールアイテムに対応可能なツール選択 UI Component

# SegmentedToolControl

お絵描き系のアプリや画像編集系のアプリを作成する時、多くのツールを用意したい場合があったりします。そんなツールを選択させたい場合に、`UISegmentedControl` を使いたいと思うことがあるかもしれません。ただ、`UISegmentedControl` はアイテムを横に並べるのは得意ですが、アイテムを縦に並べる事は正攻法ではできません。よって画面の横側に縦に並べたようなデザインにしようと思うと困ってしまいます。

また、画面のサイズの都合もあるので、アイテムの数が増えると、ずらずらと一列に並べるというわけにはいきません。アイテムをある程度グループに分類して、多段階的に選択できれば、画面の表示領域を節約できます。

今回はこんな目的の為に、必要に迫られて書いた `SegmentedItemControl` を公開できるように少々手を入れたので、紹介したいと思います。

`SegmentedToolControl` は GitHub より取得可能です。
https://github.com/codelynx/SegmentedToolControl

`SegmentedToolControl` は名前にControl とついていますが、`UIControl` のサブクラスではなく`UIView`のサブクラスです。`SegmentedToolControl`は、縦、横向きに配置が可能で、複数のアイテムをカテゴリー毎などにグループ化して一つのアイコンとして配置可能です。そして、それらのカテゴリーを代表するアイコンを一列に配置し、UI のコンポーネントとして利用できます。

以下に利用例の画像を紹介します。画面の右端にアイテムを縦並びに並べて、カテゴリで長押しすると同カテゴリのサブアイテムがポップします。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65634/0643ef1c-abc2-b034-e242-84c9796dac4a.png" width="200"/>

実際に選択状態にあるツールは常に一つです。ユーザーはまずカテゴリーアイコンを長押しするとサブアイテムのアイコンがポップアップします。ポップアップする向きはプロパティで指定する事ができます。目的のサブアイテムへ指を動かして離すと、そのアイテムを選択した事になります。

選択状態は delegate に通知されるので、クライアント側のコードはどのアイテムが選択されたか知る事ができます。

現段階では、Interface Builder から様々な設定ができるわけではなく、コーディングによるコンフィグレーションの設定が必要になります。

## 主なクラス

#### `SegmentedItem`

`SegmentedToolControl`で選択可能なアイテムです。

```.swift
SegmentedItem(identifier: "paintbrush", image: UIImage(systemName: "paintbrush")
```

#### `SegmentedCategoryItem`

`SegmentedItem` をカテゴリーまたはグループ別にまとめて表示上一つのアイテムのように表示します。

```.swift
SegmentedCategoryItem(items: [
   SegmentedItem(identifier: "hammer", image: UIImage(systemName: "hammer"),
   SegmentedItem(identifier: "wrench", image: UIImage(systemName: "wrench")
])
```

#### `SegmentedToolControl`

`SegmentedToolControl` の本丸です。コードでのセットアップの例です。

```.swift
let toolControl: SegmentedToolControl = ...
toolControl.itemSize = Self.itemSize
toolControl.orientation = .vertical
toolControl.direction = .right
toolControl.delegate = self
toolControl.segmentedCategoryItems = [
    SegmentedCategoryItem(items: [
        SegmentedItem(identifier: "hammer", image: UIImage(named: "hammer"),
        SegmentedItem(identifier: "wrench", image: UIImage(named: "wrench")
    ]),
    SegmentedCategoryItem(items: [
        SegmentedItem(identifier: "hare", image: UIImage(named: "hare"),
        SegmentedItem(identifier: "tortoise", image: UIImage(named: "tortoise")
    ])
]
```

`orientation` プロパティはアイテムの並びが水平か垂直かを指定します。
`direction` プロパティはサブアイテムがポップする際の方向を指定します。

そしてデリゲートを実装すれば、アイテムが選ばれた際に呼ばれます。

```.swift
    func segmentedToolControl(_ control: SegmentedToolControl, didSelectItem: SegmentedItem) {
        switch control.selectedItem.identifier {
        case "hammer": // ...
        case "wrench": // ...
        }
    }
```

サンプルプロジェクトより、orientation と direction のそれぞれを指定したサンプルのスクリーンショットを貼っておきます。

<div>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65634/a0600f12-0e5e-3da0-5fb8-ddb9d9ff66ff.png" width="140"/>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65634/62d99e0e-9127-30fd-4670-4038d78e5e57.png" width="140"/>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65634/7beabd33-aacd-158f-4978-351aa75a3b15.png" width="150"/>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65634/ab474bbc-831f-2ff5-c01a-ee64a9347127.png" width="150"/>
</div>

以下に執筆時点での環境を記します。

```.console
Xcode Version 11.2.1 (11B500)
$ swift --version
Apple Swift version 5.1.2 (swiftlang-1100.0.278 clang-1100.0.33.9)
Target: x86_64-apple-darwin19.0.0
```