［Swift］画像をタップした場所のRGB値をとる

# はじめに
今回は、ImageViewに表示した画像のタップした場所からRBG値を取ってみたいと思います。

# デモ動画
![](https://media.giphy.com/media/8JWOL8PooqVMQzFxet/giphy.gif)

# やること
- ImageViewに表示した画像のタプした場所からRGB値を取り、NavigationBarに表示する。

# やらないこと
- 画像の拡大、縮小
- タップした場所の表示

# 環境
- Swift4
- Xcode 9.4.1

# StoryBoard
<img width="1440" alt="スクリーンショット 2018-08-05 21.18.04.png" src="https://qiita-image-store.s3.amazonaws.com/0/200520/5ef609ce-f50a-de03-0e8b-4a67c0507ccf.png">

左上にカメラボタンを配置するためにNavigation Controllerを設置。
それと画像を表示するImageVIewを置きます。その際、

1. ImageVIewの「User Interaction Enabled」にチェックマークをつけましょう。
2. Content Modeを「Aspect Fit」に変更。
3. 「Tap Gesture Recognizer」をImageVIew上に設置。（設置すると下の画像の青い四角のアイコンが追加されます。）

<img width="1166" alt="スクリーンショット 2018-08-05 21.22.56.png" src="https://qiita-image-store.s3.amazonaws.com/0/200520/7dabb367-da40-cda5-e2db-cc190a8df81d.png">



# コード
```.swift

import UIKit

class TapViewController: UIViewController {
    
    @IBOutlet var imageView: UIImageView!
    var image = UIImage()
    //表示されている画像のタップ座標用変数
    var tapPoint = CGPoint(x: 0, y: 0)
    
    override func viewDidLoad() {
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    //imageviewをタップした時に色を判別
    @IBAction func getImageRGB(_ sender: UITapGestureRecognizer) {
        
        guard imageView.image != nil else {return}
        
        //タップした座標の取得
        tapPoint = sender.location(in: imageView)
        
        let cgImage = imageView.image?.cgImage!
        let pixelData = cgImage?.dataProvider!.data
        let data: UnsafePointer = CFDataGetBytePtr(pixelData)
        //1ピクセルのバイト数
        let bytesPerPixel = (cgImage?.bitsPerPixel)! / 8
        //1ラインのバイト数
        let bytesPerRow = (cgImage?.bytesPerRow)!
        print("bytesPerPixel=\(bytesPerPixel) bytesPerRow=\(bytesPerRow)")
        //タップした位置の座標にあたるアドレスを算出
        let pixelAd: Int = Int(tapPoint.y) * bytesPerRow + Int(tapPoint.x) * bytesPerPixel
        //それぞれRGBAの値をとる
        let r = Int( CGFloat(data[pixelAd])) 
        let g = Int( CGFloat(data[pixelAd+1]))
        let b = Int( CGFloat(data[pixelAd+2]))
        let a = CGFloat(Int( CGFloat(data[pixelAd+3])/CGFloat(255.0)*100)) / 100
        
        print([r,g,b,a])
        //navigationbarに結果を表示
        let R = "R:" + String(Int(r))
        let G = " G:" + String(Int(g))
        let B = " B:" + String(Int(b))
        let A = " A:" +  String(format: "%.1f", a)
        navigationItem.title = R + G + B + A
    }
    
    
}


//画像を選択
extension TapViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    
    //画像を選んだ時の処理
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
        
        let selectedImage = info[UIImagePickerControllerOriginalImage] as! UIImage
        //サイズを圧縮する
        // let resizedImage = selectedImage.scale(byFactor: 0.4)
        
        image = selectedImage
        
        var imageHeight = image.size.height
        var imageWidth = image.size.width
        
        let navigationBarHeight = navigationController?.navigationBar.frame.height
        let width = self.view.frame.width
        let height = self.view.frame.height
        let centerX = self.view.center.x
        let centerY = self.view.center.y
        let widthRatio = imageWidth
        let heightRatio = imageHeight
        //画像の大きさに応じてiamgeviewのサイズを変える
        if imageHeight > self.view.frame.height || imageWidth > self.view.frame.width {
            imageWidth = width
            imageHeight = width*heightRatio/widthRatio
            
        } else if imageHeight > self.view.frame.height {
            imageHeight = height
            imageWidth = height*widthRatio/heightRatio
            
        } else if imageWidth > self.view.frame.width {
            imageWidth = width
            imageHeight = width*heightRatio/widthRatio
            
        } else {
        }
        
        imageView.contentMode = UIViewContentMode.scaleAspectFill
        imageView.frame.size = CGSize(width: imageWidth, height: imageHeight)
        //画像がnavigationbarに被らないようにする
        if imageHeight/2 > (height/2 - navigationBarHeight!) {
            imageView.center = CGPoint(x: centerX, y: centerY + navigationBarHeight!)
        } else {
            imageView.center = CGPoint(x: centerX, y: centerY)
        }
        
        imageView.image = image
        
        picker.dismiss(animated: true, completion: nil)
    }
    
    
    
    // 撮影がキャンセルされた時に呼ばれる
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        picker.dismiss(animated: true, completion: nil)
    }
    
    func tappedlibrary() {
        let sourceType:UIImagePickerControllerSourceType =
            UIImagePickerControllerSourceType.photoLibrary
        
        if UIImagePickerController.isSourceTypeAvailable(
            UIImagePickerControllerSourceType.photoLibrary){
            // インスタンスの作成
            let cameraPicker = UIImagePickerController()
            cameraPicker.sourceType = sourceType
            cameraPicker.delegate = self
            self.present(cameraPicker, animated: true, completion: nil)
        }
        else{
            print("error")
            
        }
    }
    
    func tappedcamera() {
        let sourceType:UIImagePickerControllerSourceType =
            UIImagePickerControllerSourceType.camera
        // カメラが利用可能かチェック
        if UIImagePickerController.isSourceTypeAvailable(
            UIImagePickerControllerSourceType.camera){
            // インスタンスの作成
            let cameraPicker = UIImagePickerController()
            cameraPicker.sourceType = sourceType
            cameraPicker.delegate = self
            self.present(cameraPicker, animated: true, completion: nil)
            
        }
        else{
            print("error")
        }
    }
    
    @IBAction func selecteImageButton(_ sender: UITapGestureRecognizer) {
        //アラート表示のために
        let actionSheet = UIAlertController(title: "", message: "写真の選択", preferredStyle: UIAlertControllerStyle.actionSheet)
        
        let tappedcamera = UIAlertAction(title: "カメラで撮影する", style: UIAlertActionStyle.default, handler: {
            (action: UIAlertAction!) in
            self.tappedcamera()
        })
        
        let tappedlibrary = UIAlertAction(title: "ライブラリから選択する", style: UIAlertActionStyle.default, handler: {
            (action: UIAlertAction!) in
            self.tappedlibrary()
        })
        
        let cancel = UIAlertAction(title: "キャンセル", style: UIAlertActionStyle.cancel, handler: {
            (action: UIAlertAction!) in
            print("キャンセル")
        })
        
        actionSheet.addAction(tappedcamera)
        actionSheet.addAction(tappedlibrary)
        actionSheet.addAction(cancel)
        
        present(actionSheet, animated: true, completion: nil)
    }

}
```

## 注意点
今回、画像の縦横比を元の画像に合わせつつ、画像とImageVeiwの大きさが常に一致するようにしている。
理由はタップした座標を取得する際に、ImageViewとImageで座標の原点が変わってしまうからだ。

図のような場合を想定する。
灰色がImageViewで、ライトグリーンがImageだとすると、
星の位置をタップした際に、ImageViewとImageそれぞれ緑色と紫色の矢印のように座標を特定する。

つまり、２つの左上の座標がぴったりあっていないと異なる座標が取得され、意図した場所のRGB値が取れなくなってしまう。

![iPhone 8.png](https://qiita-image-store.s3.amazonaws.com/0/200520/65fa4868-052d-53ce-744e-cb4da069606a.png)


# 関連付け
普通に関連付けすれば大丈夫ですが、１つだけ注意点があります。
ImageViewをタップした時に動作する

```.swift
 @IBAction func getImageRGB(_ sender: UITapGestureRecognizer)
```
は**ImageViewと繋がずに、「Tap Gesture Recognizer」と繋いでください！（画像の青色の四角）**



####### GitHub
https://github.com/freadteam/RGBFromImageViewSample