

カメラロールから選んだ画像をimageViewに描写している状態で、ボタンをタップするとセピア色や白黒にフィルターをかける機能を実装しています。  
さらに、ユーザがUISliderを左右にドラッグしてセピア色や白黒の濃淡具合を選択できるような機能を実装したいと思い  
独学でコーディングしたのですが、ここから先がわかりません。

[code]

    import UIKit
    class ViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
        @IBOutlet var mainImageView: UIImageView!
        @IBAction func pickImageFromLibrary() {
            if UIImagePickerController.isSourceTypeAvailable(.PhotoLibrary) {
                let controller = UIImagePickerController()
                controller.delegate = self
                controller.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
                self.presentViewController(controller, animated: true, completion: nil)
            }
        }
        override func viewDidLoad() {
            super.viewDidLoad()
            let slider = UISlider()
            slider.frame = CGRect(x: 80, y: 380, width: 200, height: 40)
            slider.minimumValue = 0.0
            slider.maximumValue = 1.0
            slider.addTarget(self, action: "sepiaFilter:", forControlEvents: UIControlEvents.ValueChanged)
            slider.addTarget(self, action: "monoFilter:", forControlEvents: UIControlEvents.ValueChanged)
            self.view.addSubview(slider)
        }
        func imagePickerController(picker: UIImagePickerController,
        didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) {
            self.mainImageView.image = image
            (self.view.viewWithTag(1) as! UIButton).setImage(image, forState: .Normal)
            (self.view.viewWithTag(2) as! UIButton).setImage(sepiaFilter(image, slider: UISlider()), forState: .Normal)
            (self.view.viewWithTag(3) as! UIButton).setImage(monoFilter(image, slider: UISlider()), forState: .Normal)
            picker.dismissViewControllerAnimated(true, completion: nil)
        }
        func sepiaFilter(image: UIImage, slider: UISlider) -> UIImage {
            let sepiaFilter = CIFilter(name: "CISepiaTone")
            sepiaFilter!.setDefaults()
            let ciImage: CIImage = CIImage(image: image)!
            sepiaFilter!.setValue(ciImage, forKey: kCIInputImageKey)
            sepiaFilter!.setValue(slider.value, forKey: "inputIntensity")
            if let outputImage = sepiaFilter!.valueForKey("outputImage") as? CIImage {
                let manipulatedImage = UIImage(CIImage: outputImage)
                self.mainImageView.image = manipulatedImage
           }
            return mainImageView.image!
        }
        func monoFilter(image: UIImage, slider: UISlider) -> UIImage {
            let monoFilter = CIFilter(name: "CIColorControls")
            monoFilter!.setDefaults()
            let ciImage = CIImage(image: image)!
            monoFilter!.setValue(ciImage, forKey: kCIInputImageKey)
            monoFilter!.setValue(slider.value, forKey: "inputSaturation")
            if let outputImage = monoFilter!.valueForKey("outputImage") as? CIImage {
                let manipulatedImage = UIImage(CIImage: outputImage)
                self.mainImageView.image = manipulatedImage
            }
            return mainImageView.image!
        }
        @IBAction func filterTapped(sender:UIButton) {
            switch sender.tag {
            case 1:
                mainImageView.image = (self.view.viewWithTag(1) as! UIButton).imageView?.image
            case 2:
                mainImageView.image = (self.view.viewWithTag(2) as! UIButton).imageView?.image
            default:
                break
            }
        }
    }
    
[/code]

上記のコードで実機テストをすると、UISliderをドラッグしたときにエラーとなります。  
(ビルド前の、エラーマークは無い状態です)

UISlider作成するときのaddTarget()の括弧内で、action: "sepiaFilter:"と"monoFilter:"  
を混在させているため、エラーになっていると思うのですが、  
文法的にどういった記述をして修正すべきでしょうか。

※追記  
説明不足ですみません。  
デバッグエリアの表示内容など、下記の通りです。  
[![画像の説明をここに入力](https://i.stack.imgur.com/Ii8dh.png)](https://i.stack.imgur.com/Ii8dh.png)

[![画像の説明をここに入力](https://i.stack.imgur.com/frF86.png)](https://i.stack.imgur.com/frF86.png)

[![画像の説明をここに入力](https://i.stack.imgur.com/T4MWg.png)](https://i.stack.imgur.com/T4MWg.png)


CC BY-SA 3.0
2016-07-31T06:02:01.620
27978
2017-03-31T13:38:59.993
2016-12-30T11:13:10.853
76
14329
question
swift swift2
1つのUISliderで複数のフィルターの色相変化をさせるには


(編集メモ)`sepiaFilter`,
`monoFilter`がフィルター用の関数とactionメソッドの両方に使用されていたのに気づかないまま不適切な方法を勧めることになっていたので、修正しました。

* * *

あなたが引用されたエラーメッセージで一番肝心なのはこちらになります。

> **[_picFilter_2016_0725.ViewController sepiaFilter:]: unrecognized selector
> sent to instance 0x1553f930'**

これは「(あなたのアプリの)`ViewController`クラスのインスタンスに`sepiaFilter:`と言う認識されていないselectorが送られた」と言っています。selectorと言うのは、Objective-
C用語ですが、iOSプログラミングでは必須の概念ですのでうっすらくらいには理解しておられると思いますが、要は「`ViewController`クラスには`sepiaFilter:`に対応するメソッドは定義されていないよ」と言っていることになります。

あなたのクラスには、

[code]

    func sepiaFilter(image: UIImage, slider: UISlider) -> UIImage {
        //...
        return mainImageView.image!
    }
    
[/code]

というメソッドがあるわけですが、このメソッドの(Objective-
C表現での)selectorは`sepiaFilter:slider:`と言うものになります。(Swift2までの話でSwift3では変わります。)

単に`sepiaFilter:`と言うセレクタを記述すると、それは引数が1個しかない、

[code]

    func sepiaFilter(param: SomeParamType) -> SomeReturnType {
        //...
        return returnTypeObject
    }
    
[/code]

と言う型式のメソッドを指定したことになります。  
(Objective-Cのselectorでは、戻り値があるかどうか、パラメータや戻り値の型は関係ありませんが、後で述べるtarget-
action用のメソッドでは戻り値を返してはいけません。)

「イベントが発生したら`sepiaFilter:`ってメソッドを呼べって言われたのに、そんなメソッドないじゃんかよぉ！！！」と怒っているので、対処方法はどれかになります。

(1) `action:`引数に既に存在するメソッドの正しいselectorを指定する  
(2) `action:`引数で指定したメソッドを定義してやる  
(3) `action:`引数で指定した形に合うように既存のメソッドを修正してやる  
(4) `action:`引数の指定を修正した上で、そのメソッドを定義してやる

iOSの`UIControl`(`UISlider`もその一種)では、target-actionで指定できるメソッドは、次のどれかの形でなければいけません。

[UIControl Class
Reference](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIControl_Class/index.html#//apple_ref/doc/uid/TP40006779)

> Listing 1 Swift
[code]

>     @IBAction func doSomething()
>     @IBAction func doSomething(sender: UIButton)
>     @IBAction func doSomething(sender: UIButton, forEvent event: UIEvent)
>  
[/code]

(戻り値は省略していないのではなく、戻り値なしというのが指定です。)

この中の2番目の形式が、あなたの指定したselector`sepiaFilter:`に合う引数1つのメソッドですが、
**あなたの`sepiaFilter(_:slider:)`メソッドはフィルター関数としても使われている**ので、そのまま修正してやっても「戻り値なし」の上記の形に合わせることができません。  
ですので、ここでは **actionメソッドをフィルター関数とは別に追加する** 形の上記(2)の対応が必要でしょう。

[code]

    @IBAction func sepiaFilter(sender: UISlider) {
        guard let image = self.mainImageView.image else {
            fatalError("something is wrong with mainImageView...")
        }
        self.mainImageView.image = sepiaFilter(image, slider: sender)
    }
    func sepiaFilter(image: UIImage, slider: UISlider) -> UIImage {
        guard let sepiaFilter = CIFilter(name: "CISepiaTone") else {
            fatalError("something is wrong with CIFilter...")
        }
        sepiaFilter.setDefaults()
        guard let ciImage: CIImage = CIImage(image: image) else {
            print("cannot create CIImage...")
            return image
        }
        sepiaFilter.setValue(ciImage, forKey: kCIInputImageKey)
        sepiaFilter.setValue(slider.value, forKey: "inputIntensity")
        if let outputImage = sepiaFilter.valueForKey("outputImage") as? CIImage {
            let manipulatedImage = UIImage(CIImage: outputImage)
            return manipulatedImage //フィルター関数ではmainImageViewは変更しないで修正後のイメージを戻り値として返す
        }
        return image
    }
    
[/code]

※「私のアプリをクラッシュさせて演算子」("Crash Me
operator"`!`)は出来るだけ使わない方が良いのとボタンイメージ用のフィルター処理で`mainImageView`の方まで変更する必要はないはずなのでフィルター関数の方も若干修正させていただきました。またプログラム中で`addTarget`する場合には`@IBAction`は要らないのですが、フィルター関数側と明示的に区別できるので、そのまま付けておきます。

当然ながら、こちらを修正したら、ほぼ同じ状態になっていた`monoFilter:`の方にも同様の修正をしてやらないと、今度はそちらが実行時エラーになってしまいます。

かなり長文になってしまいましたので、こちらのご質問の方の主題は追記にお示しいただいたエラーを解消することとして、一旦ここで置かせていただきます。


CC BY-SA 3.0
2016-07-31T07:48:01.423
27979
2016-08-01T17:55:06.247
2016-08-01T17:55:06.247
13972
13972
27978
answer