Instagramライクな画像フィルタライブラリをCocoaPodsで公開してみた

この記事はGithubで公開中の画像フィルタライブラリSharakuの中身と作り方を解説した記事になっています。記事内容としては

- CocoaPodsを用いたライブラリの作成方法
- Core ImageのCIFilterの使い方

などが主になっています。
手っ取り早く画像フィルタ機能を使いたいという方は``` pod 'Sharaku'```をpodfileに追加して使ってみてください。
ライブラリのGithubはこちら。
https://github.com/makomori/Sharaku

## 完成形
![sharaku_header.png](https://qiita-image-store.s3.amazonaws.com/0/62625/d6eb5f41-0def-6853-36c6-a5bd11b71ea8.png)

## 作り方
### CocoaPodsによるプロジェクトの作成
``` ruby
pod lib create Sharaku
```
上記のコマンドを実行すると、ライブラリのテンプレートが作られます。色々と聞かれるので適宜答えていきましょう。「demo applicationを含める」にYesと答えるとExample for Sharakuというディレクトリが作られるのでそこにサンプルアプリを書いていき、Podsの中に入っているDevelopment Podsで実際のライブラリの開発を行います。

### 画像フィルタ機能の作成
肝心のUIImageのフィルタ機能に関してはCore ImageのCIFilterというクラスを使います。このクラスを使うとInstagramっぽいフィルタを数行で作ることができます。
Sharakuでは以下のようなメソッドを作って画像にフィルタをかけています。

```swift

func createFilteredImage(filterName: String, image: UIImage) -> UIImage {
        // 1 - create source image
        let sourceImage = CIImage(image: image)

        // 2 - create filter using name
        let filter = CIFilter(name: filterName)
        filter?.setDefaults()

        // 3 - set source image
        filter?.setValue(sourceImage, forKey: kCIInputImageKey)

        // 4 - output filtered image as cgImage with dimension.
        let outputCGImage = context.createCGImage((filter?.outputImage!)!, from: (filter?.outputImage!.extent)!)

        // 5 - convert filtered CGImage to UIImage
        let filteredImage = UIImage(cgImage: outputCGImage!)

        return filteredImage
    }
```

Sharakuで使用しているフィルタの一覧は以下の通りです。

```swift

"CIPhotoEffectChrome",
"CIPhotoEffectFade",
"CIPhotoEffectInstant",
"CIPhotoEffectMono",
"CIPhotoEffectNoir",
"CIPhotoEffectProcess",
"CIPhotoEffectTonal",
"CIPhotoEffectTransfer",
"CILinearToSRGBToneCurve",
"CISRGBToneCurveToLinear"
```

### UIImageViewにUIGestureRecognizerを乗せて左右のスワイプジェスチャーを取得する
```swift

@IBAction func imageViewDidSwipeLeft() {
    if filterIndex == filterNameList.count - 1 {
        filterIndex = 0
        imageView?.image = image
    } else {
        filterIndex += 1
    }
    if filterIndex != 0 {
        applyFilter()
    }
    updateCellFont()
    scrollCollectionViewToIndex(itemIndex: filterIndex)
}
```

### メインのImageViewに適用されているフィルタと下のCollectionViewを連動させる

```swift

extension  SHViewController: UICollectionViewDataSource, UICollectionViewDelegate
{
    public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SHCollectionViewCell
        var filteredImage = smallImage
        if indexPath.row != 0 {
            filteredImage = createFilteredImage(filterName: filterNameList[indexPath.row], image: smallImage!)
        }

        cell.imageView.image = filteredImage
        cell.filterNameLabel.text = filterDisplayNameList[indexPath.row]
        updateCellFont()
        return cell
    }

    public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return filterNameList.count
    }

    public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        filterIndex = indexPath.row
        if filterIndex != 0 {
            applyFilter()
        } else {
            imageView?.image = image
        }
        updateCellFont()
        scrollCollectionViewToIndex(itemIndex: indexPath.item)
    }
}
```

### 選択されているフィルタに応じてLabelのfont-sizeを変える

```swift
func updateCellFont() {
    // update font of selected cell
    if let selectedCell = collectionView?.cellForItem(at: IndexPath(row: filterIndex, section: 0)) {
        let cell = selectedCell as! SHCollectionViewCell
        cell.filterNameLabel.font = UIFont.boldSystemFont(ofSize: 14)
    }

    for i in 0...filterNameList.count - 1 {
        if i != filterIndex {
            // update nonselected cell font
            if let unselectedCell = collectionView?.cellForItem(at: IndexPath(row: i, section: 0)) {
                let cell = unselectedCell as! SHCollectionViewCell
                if #available(iOS 8.2, *) {
                    cell.filterNameLabel.font = UIFont.systemFont(ofSize: 14.0, weight: UIFontWeightThin)
                } else {
                    // Fallback on earlier versions
                    cell.filterNameLabel.font = UIFont.systemFont(ofSize: 14.0)
                }
            }
        }
    }
}
```

### 選択されているフィルタの変更に応じてCollectionViewを動かす
```swift
func scrollCollectionViewToIndex(itemIndex: Int) {
    let indexPath = IndexPath(item: itemIndex, section: 0)
    self.collectionView?.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}
```

主なプログラムは以上になります。CIFilterを知るまでは何か難しそうに感じていた画像フィルタも実際に作ってみると一瞬でした。

## ライブラリの宣伝の仕方
- [Cocoacontrols](https://www.cocoacontrols.com/)に投稿する。（これが一番効果的でした。）
- Githubのトレンドに載る。（Cocoacontrolsなどからの流入で1日50starほど付けば上位に上がってくるのでstarが付きやすいです。）
- awesome-iosなどのレポジトリに載せてもらう。

ライブラリを宣伝する他の良い方法などご存知の方いらっしゃいましたら是非教えてください。

## 余談
ちなみにこのライブラリはインスタグラムライクなカメラ機能を提供してくれるライブラリ[fusuma]("https://github.com/ytakzk/Fusuma")にインスパイアされて作りました。「シンプルで、綺麗なフィルタを導入したい。」そんなニーズに応える小規模なUIライブラリになっています。

ソースコードが良くない、こんな機能が欲しい、など何かご意見がございましたらQiitaもしくはGithubどちらでも構いませんので是非ご指摘ください！