【Swift】iOSで放置型育成ゲームを作るよ(2) ~素材画像から一部を切り取ってgif化する~

# 今回のゴール
- 素材画像から一部分を切り取る
- 切り取った数枚のUIImageを重ね合わせてgif化して綺麗な画像にする
![output-ffmpeg-palette.gif](https://qiita-image-store.s3.amazonaws.com/0/115590/1934e44d-73a6-e40c-c56c-fa34a8fed2ca.gif)

# 今回のキーワード
- UIImage
- gif
- Extension

# やったこと
## 素材画像を見直すよ
- [前回](http://qiita.com/nasteng/items/6a8d2b875851689b9cc8)で使ったgif画像は実は素材画像を自分で切り貼りして無理やり作ったものだよ
- だからかなり荒いものになっていたよ
- 大量にある素材画像からgif化できないか探してたらこんなものがあったよ
- どうやら左上から右方向に3枚ごとにまとめればgifになりそうなので、この画像の左上から3枚目まで1枚ずつ切り抜いてgif化できないかなと考えたよ

![image.png](https://qiita-image-store.s3.amazonaws.com/0/115590/15f02ca7-0eb8-3d82-1535-452d22b4f8fe.png)

## 画像の一部の切り抜き方をググるよ
- 検索ワード「**UIImage 切り抜き**」
- ドンピシャな記事([Swift3.0で画像の切り抜き](http://qiita.com/takabosoft/items/391b7593f0b9ef7d77a5))が見つかったのでそのまま拝借するよ

## まず1枚切り抜いて表示してみるよ
- sozai.pngという名前でプロジェクトに素材画像を追加するよ
- UIImage+Cropping.swiftというExtension用のファイルを追加して先ほどのコードを追記するよ

```UIImage+Cropping.swift
import Foundation
import UIKit

extension UIImage {
    func cropping(to: CGRect) -> UIImage? {
        var opaque = false
        if let cgImage = cgImage {
            switch cgImage.alphaInfo {
            case .noneSkipLast, .noneSkipFirst:
                opaque = true
            default:
                break
            }
        }
        
        UIGraphicsBeginImageContextWithOptions(to.size, opaque, scale)
        draw(at: CGPoint(x: -to.origin.x, y: -to.origin.y))
        let result = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return result
    }
}
```

- 左上の座標(x: 0.0, y: 0.0)から(width: 64.0, height: 64.0)分のCGRectを作って関数に代入するよ　(色々調べた結果、1枚1枚は64 * 64のサイズでできているよ)

```ViewController.swift
~~~省略~~~
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 一旦コメントアウト
//        imageView.image = UIImage.gif(name: "character")
        
        // まずは素材画像からUIImageを宣言する
        let image = UIImage(named: "sozai.png")
        
        // 切り取る範囲を指定するCGRectを宣言する
        let croppingRect = CGRect(x: 0.0, y: 0.0, width: 64.0, height: 64.0)
        
        // imageをcroppingRectで切り抜いて、返り値をimageView.imageに代入する
        imageView.image = image?.cropping(to: croppingRect)
    }
~~~省略~~~
```

- キマシタワー　綺麗に切り抜けてるっぽい
![image.png](https://qiita-image-store.s3.amazonaws.com/0/115590/311391ec-8f94-ae66-bd7c-ab41a6b727e9.png)

## UIImageをgifにする方法が分からなかったのでググるよ
- 検索ワード「**UIImage gif化**」
- 色々参考になる記事はあったけど、分かりやすいこの記事([[iOS][swift] アニメーションGIFを作る](http://dev.classmethod.jp/smartphone/iphone/swift_anigif/))を参考にするよ
- 前回ダウンロードしてきたUIImage+Gif.swiftに追記していくよ
    - (人のライブラリいじっていいものなんだろうか・・:thinking: )
- 必要な処理が2つあるので分かりやすいように関数を2つに分けるよ
    - UIImageからgif用の3枚を切り抜く処理(**createCharacterImagesForGif**)
        - これはExtensionの中でしか使わないようにprivateにしたよ
    - UIImageの配列からgifファイルのURLを生成する処理(**createGifURL**)
        - これはほぼコピペだけど、動けばいいか！の精神で乗り切るよ

```UIImage+Gif.swift
import UIKit
import ImageIO
import MobileCoreServices

~~~省略~~~

extension UIImage {

~~~省略~~~
    // MARK: 自身のimageからgif用の配列を生成するメソッド
    private func createCharacterImagesForGif() -> [UIImage]? {
        let cropX = 64  // 切り抜くwidth
        let cropY = 64  // 切り抜くheight
        
        // 切り抜いた画像を保存しておく配列
        var images: [UIImage] = []

        // 3枚分for文を回す
        for i in 0..<3 {
            guard let image = self.cropping(to: CGRect(x: i * cropX, y: 0, width: cropX, height: cropY)) else {
                // クロッピングに失敗したらnilをreturnする
                return nil
            }
            
            // 切り抜けたUIImageを配列に追加
            images.append(image)
        }
        
        // うまいこといけば3枚分のUIImageが入った配列がreturnされる
        return images
    }
    
    // MARK: UIImageの配列からgifを生成し、保存したURLを返すメソッド
    func createGifURL() -> URL? {
        guard let images = self.createCharacterImagesForGif() else {
            return nil
        }
        
        let charaImages: [CGImage] = images.map{$0.cgImage!}
        
        guard let url = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("\(NSUUID().uuidString).gif") else {
            print("url化失敗")
            return nil
        }
        guard let destination = CGImageDestinationCreateWithURL(url as CFURL, kUTTypeGIF, charaImages.count, nil) else {
            print("CGImageDestinationの作成に失敗")
            return nil
        }
        
        let properties = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFLoopCount as String: 0]]
        CGImageDestinationSetProperties(destination, properties as CFDictionary)
        
        let frameProperties = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFDelayTime as String: 0.25]]
        for image in charaImages {
            CGImageDestinationAddImage(destination, image, frameProperties as CFDictionary)
        }
        
        if CGImageDestinationFinalize(destination) {
            print("GIF生成が成功")
            return url
        } else {
            print("GIF生成に失敗")
            return nil
        }
    }
}
```
```ViewController.swift
~~~省略~~~
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 一旦コメントアウト
//        imageView.image = UIImage.gif(name: "character")
        
        // まずは素材画像からUIImageを宣言する
        let image = UIImage(named: "sozai.png")
        
        // 素材のimageからgifのURLが生成できたかをnilチェックする
        if let url = image?.createGifURL() {
            // urlが生成されていたらimageViewのimageに代入
            imageView.image = UIImage.gif(url: url.absoluteString)
        }
    }
~~~省略~~~
```

できた！前回のと比較すると一目瞭然:relaxed:

今回
![output-ffmpeg-palette.gif](https://qiita-image-store.s3.amazonaws.com/0/115590/1934e44d-73a6-e40c-c56c-fa34a8fed2ca.gif)

前回
![ede5a1a7-de78-77a8-cb36-ff04249e84e8.gif](https://qiita-image-store.s3.amazonaws.com/0/115590/8077c0ad-d32c-5a99-b1da-7f435384735a.gif)

## 結果
- 素材が神
- 先人が神
    - ドンピシャな記事があるとマジで捗る
- UIImage.gifの引数がurlってラベルなのにString型で分かりにくい:confused:
- 今回の作業ブランチはこちら！
    - https://github.com/nasteng/Dejikore/tree/update-gif-creation

# 余談
- 他のgif画像も表示してみた

![output-ffmpeg-palette.gif](https://qiita-image-store.s3.amazonaws.com/0/115590/c44eaac1-0c2c-5300-8d25-7c4f09ca8530.gif)
- どうやら全てがgifになるわけじゃなかった様子だよ
    - どう使ったらいいか分からないgifもあるよ・・

# 次回 2017/6/25追記
キャラクターをクラス化してViewControllerから分離するよ
http://qiita.com/nasteng/items/ed2a3974b4bafdb5f653

# 最後に
ご指摘・リファクタ大歓迎です:pray: