22064
swift及びプログラミング初心者です。  
スタンプカメラアプリでカメラロールに保存したUIImageをTwitter投稿させる機能の実装方法で悩んでいます。  
撮影ボタンを押すとクロージャー内部で画像を合成させて、最終的にcapturedImage2というUIImageが生成させました。  
(カメラロールには画像が保存できています)  
以下、クロージャー内部のコードです。

```

    let myAVConnection = myImageOutPut.connectionWithMediaType(AVMediaTypeVideo)
    myImageOutPut.captureStillImageAsynchronouslyFromConnection(myAVConnection, completionHandler: 
        {(imageDataBuffer, error) -> Void in
            ・・・・
            ・・・・
            ・省略・
            ・・・・
            ・・・・
            let capturedImage2: UIImage = UIGraphicsGetImageFromCurrentImageContext()
            UIImageWriteToSavedPhotosAlbum(capturedImage2, self, nil, nil)
            UIGraphicsEndImageContext()
        })
    }
    
```

クロージャ内部で生成したcapturedImage2をtwitterに投稿したいため、Socialフレームワークを用いて以下の様なコードを記述したいと思っています。

```

    @IBAction func postTwitter(sender: AnyObject) {
        let twVC:SLComposeViewController = SLComposeViewController(forServiceType: SLServiceTypeTwitter)!
        let sendImage = rarara
        twVC.addImage(sendImage)
        self.presentViewController(twVC, animated: true, completion: nil)
    }
    
```

上記のコードで  
`let sendImage = rarara`  
右辺のrararaの部分をクロージャ内部で生成したUIImageであるcapturedImage2としたいのですが、記述方法が分かりません。  
クロージャ内部で生成したものをクロージャ外部で使いたい場合、どういったコーディングをすべきなのでしょうか。
CC BY-SA 3.0
2016-02-14T05:41:28.743
22063
2016-02-14T10:04:48.200
2016-02-14T08:18:16.370
7362
14329
question
swift xcode xcode7 swift2
クロージャー内部で生成させたUIImageを、クロージャー外部で使うにはどうしたらいいですか？
_クロージャ内部で生成したものをクロージャ外部で使いたい場合、どういったコーディングをすべきなのでしょうか。_  
クロージャー内部で生成したものはクロージャー内部で使う、と言うのが(とりわけそのクロージャーが非同期処理の完了ハンドラであるなら)コーディングの基本です。

```

    let myAVConnection = myImageOutPut.connectionWithMediaType(AVMediaTypeVideo)
    myImageOutPut.captureStillImageAsynchronouslyFromConnection(myAVConnection, completionHandler: {(imageDataBuffer, error) -> Void in
        //省略...
        let capturedImage2: UIImage = UIGraphicsGetImageFromCurrentImageContext()
        UIImageWriteToSavedPhotosAlbum(capturedImage2, self, nil, nil)
        UIGraphicsEndImageContext()
        //ここでは、captureImage2に確実に必要なUIImageが入っているのだから、それを使うコードはここに書く
        dispatch_async(dispatch_get_main_queue()) {
            let twVC:SLComposeViewController = SLComposeViewController(forServiceType: SLServiceTypeTwitter)!
            let sendImage = capturedImage2
            twVC.addImage(sendImage)
            self.presentViewController(twVC, animated: true, completion: nil)
        }
    })
    
```

どうしても別メソッドとしたいので、あればクロージャーの内部からそのメソッドを呼べば良いだけです。(ついでに少しでも短くするために末尾クロージャー用の省略表記を使わせてもらいます。)

```

        let myAVConnection = myImageOutPut.connectionWithMediaType(AVMediaTypeVideo)
        myImageOutPut.captureStillImageAsynchronouslyFromConnection(myAVConnection) {imageDataBuffer, error in
            //省略...
            let capturedImage2: UIImage = UIGraphicsGetImageFromCurrentImageContext()
            UIImageWriteToSavedPhotosAlbum(capturedImage2, self, nil, nil)
            UIGraphicsEndImageContext()
            //ここでは、captureImage2に確実に必要なUIImageが入っているのだから、それを使うコードはここに書く
            dispatch_async(dispatch_get_main_queue()) {
                self.sendToTwitter(capturedImage2)
            }
        }
    
```

(上のメソッドとは別のメソッドに。)

```

    func sendToTwitter(rarara: UIImage) {
        let twVC:SLComposeViewController = SLComposeViewController(forServiceType: SLServiceTypeTwitter)!
        let sendImage = rarara
        twVC.addImage(sendImage)
        self.presentViewController(twVC, animated: true, completion: nil)
    }
    
```

撮影ボタンを押したときに投稿するのであれば、postTwitter()メソッドのように@IBActionを想定したメソッドにする必要はないように思います。

細部の動作までは未確認ですので、それでは要件に合う動作とならないとか、実行時に変なエラーが出るとかいった可能性もあるかと思いますが、その場合はコメント等で内容を詳しくお知らせください。

(補足)captureStillImageAsynchronouslyFromConnection:completionHandler:の完了ハンドラはメインスレッドで呼ばれるとは限らないのでコード例を一部修正しました。

(追記)コメントに頂いたように「撮影ボタンを押してTwitterに投稿したい人のみTwitter投稿ボタンを押して投稿してい‌​ただく仕様」と言うことであれば例えばこのようにコーディング出来る、と言うのをあげておきます。ポイントは、

  * 撮影されたイメージをどこか適当な場所に保持しておく
  * イメージの準備ができたことを通知して、画面(UI)に反映させる

の2点でしょうか。

```

    //撮影が完了したイメージをインスタンスプロパティとして保持しておくようにする
    var postedImage: UIImage? = nil
    //ツイッターボタン(初期状態はenabledをfalseにしておく)
    @IBOutlet weak var twitterButton: UIButton!
    
```

ここでは、イメージを保持する場所は該当ViewControllerのインスタンスプロパティ、イメージの準備ができていることはツイッターボタンのenabledプロパティに反映させる、といった仕様を仮定しておきます。画面の構成によってはイメージを保存する場所は、AppDelegateの方が良いかもしれませんし、専用のデータ保持用クラスを定義してしまう方が良い場合もあります。

撮影時の処理は以下のようになるでしょう。

```

        let myAVConnection = myImageOutPut.connectionWithMediaType(AVMediaTypeVideo)
        myImageOutPut.captureStillImageAsynchronouslyFromConnection(myAVConnection) {imageDataBuffer, error in
            //省略...
            let capturedImage2: UIImage = UIGraphicsGetImageFromCurrentImageContext()
            UIImageWriteToSavedPhotosAlbum(capturedImage2, self, nil, nil)
            UIGraphicsEndImageContext()
            //ここでは、captureImage2に確実に必要なUIImageが入っているのだから、それを使うコードはここに書く
            dispatch_async(dispatch_get_main_queue()) {
                //取得したイメージをどこかに保存
                self.postedImage = capturedImage2
                //self.postedImageの準備ができたことを何らかの方法で知らせる
                self.twitterButton.enabled = true
            }
        }
    
```

ここでは、ツイッターボタンが押されたら、もうそのイメージは利用できないよということにしておきます。

```

    @IBAction func postTwitter(sender: UIButton) {
        let twVC:SLComposeViewController = SLComposeViewController(forServiceType: SLServiceTypeTwitter)!
        //次のイメージが撮影されるまでボタンを使えないようにする
        sender.enabled = false
        if let sendImage = postedImage {
            postedImage = nil
            twVC.addImage(sendImage)
            self.presentViewController(twVC, animated: true, completion: nil)
        } else {
            //ボタンのenabledが正しく制御されていればここは通らないはずだが、念のためログくらいは出力する
            NSLog("Twitter button pressed while postedImage == nil")
        }
    }
    
```

いかがでしょうか。何かお気付きの点がありましたら、またご連絡ください。
CC BY-SA 3.0
2016-02-14T06:28:23.513
22064
2016-02-14T10:04:48.200
2016-02-14T10:04:48.200
13972
13972
22063
answer