質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.13%

Swiftで画面をスクリーンショットしたい

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,773

daigakusei

score 18

SwiftとSpriteBuilderを使用してアプリを開発しているのですが、画面内をスクリーンショットしてTwitterに投稿するといった機能をつけようと思い、試しに下記のコードをボタンを押したときに起こる関数内に記入したのですが、カメラロールに保存して確認した際に画面が真っ白になって保存されていたため何が原因で真っ白になるのかが分からないため質問させて頂きました。

//キャプチャ取得 変数screenshotにUIImageが保存される
        let layer = UIApplication.sharedApplication().keyWindow?.layer

        let scale = UIScreen.mainScreen().scale
        UIGraphicsBeginImageContextWithOptions(layer!.frame.size, false, scale)  // スクリーンショットの取得開始

        layer!.renderInContext(UIGraphicsGetCurrentContext())
        let screenshot = UIGraphicsGetImageFromCurrentImageContext()  // 描画が行われたスクリーンショットの取得
        UIGraphicsEndImageContext()  // スクリーンショットの取得終了

        UIImageWriteToSavedPhotosAlbum(screenshot, nil, nil, nil)  // アルバムに保存

こちらのコードはhttp://swift-salaryman.com/screencaptureutil.php#を参考にしました。コード内のscreenshotに真っ白の画像が保存されるためそちらの解決方法をどなたか教えてください。宜しくお願いしますm(_ _)m

以下追記しました。メインのコードです。

import Foundation

/**
* メインクラス(タイトル画面)
*/
class MainScene: CCNode {

    /** オプションノード */
    weak var _optionNode: CCNode!
    /** Backボタン */
    weak var _backButton: CCButton!

    /**
    * 画面が生成後に同期で呼び出される。
    */
    func didLoadFromCCB() {
    }

    /**
    * ゲームスタートボタン(TitleSceneへ)
    */
    func _tapGameStart() {
        var scene = CCBReader.loadAsScene("TitleScene")
        CCDirector.sharedDirector().replaceScene(scene, withTransition: CCTransition(fadeWithDuration: 0.7))
    }

    /**
    * オプションボタン
    */
    func _tapOption() {
        _optionNode.visible = true  // オプションノード表示
        _backButton.visible = true  // バックボタン表示

        //キャプチャ取得 変数screenshotにUIImageが保存される
        let layer = UIApplication.sharedApplication().keyWindow?.layer

        let scale = UIScreen.mainScreen().scale  // 画面のサイズ取得
        UIGraphicsBeginImageContextWithOptions(layer!.frame.size, false, scale)  // スクリーンショットの取得開始

        layer!.renderInContext(UIGraphicsGetCurrentContext())
        let screenshot = UIGraphicsGetImageFromCurrentImageContext()  // 描画が行われたスクリーンショットの取得
        UIGraphicsEndImageContext()  // スクリーンショットの取得終了

        UIImageWriteToSavedPhotosAlbum(screenshot, nil, nil, nil)  // アルバムに保存
    }

    /**
    * バックボタン
    */
    func _tapBack() {
        _optionNode.visible = false  // オプションノード非表示
        _backButton.visible = false  // バックボタン非表示
    }
}

今は無理やりオプションを開いた際にスクリーンショットを行っています。ちなみにSpriteBuilderを使用しております。
動作に問題はないのですが、保存される画像が真っ白になるので原因をしりたいです。。。宜しくお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • u39ueda

    2016/01/20 11:49

    下の回答者さんへのコメントを見るにコードの他の部分に問題がありそうです。キャプチャしている部分だけでなく、呼び出し元(ViewControllerなど)のコードも追加してもらえますか?

    キャンセル

  • daigakusei

    2016/01/21 17:50

    コメントありがとうございます!追記しました。よろしくお願いしますm(_ _)m

    キャンセル

回答 3

+1

SpriteKitのSKViewのように、OpenGLを利用するCALayerはrenderInContext()で描画することができません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/01/22 00:23

    コメント下さりありがとうございますm(_ _)m
    renderInContext()で描画はできなかったのですね。。。
    原因を知ることができてよかったです。

    キャンセル

checkベストアンサー

0

すみません。SpriteBuilderだったのですね。見落としてました。

こちらを参考にしてみました。
http://forum.spritebuilder.com/t/taking-screenshots-not-working-as-expected-solved/1550

func screenshotWithStartNode(stNode: CCNode) -> UIImage {
    CCDirector.sharedDirector().nextDeltaTimeZero = true
    let winSize: CGSize = CCDirector.sharedDirector().viewSize()
    let renTxture: CCRenderTexture = CCRenderTexture(width: winSize.width, height:winSize.height, pixelFormat: .RGBA8888, depthStencilFormat: 0)
    renTxture.begin()
    stNode.visit()
    renTxture.end()

    return renTxture.getUIImage()
}

試してはいませんが、これでできないでしょうか?

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/01/22 01:47

    ちょっと状況がよく分からないですが…

    > Could not find member 'RGBA888'
    これはどこか `RGBA8888` にするべきところで `RGBA888` と指定してしまっているとかではないのでしょうか?

    > 'CCRenderTexture!' is not convertible to 'CCRenderTexture'
    ちょっとわかりません。
    Cannot convert value of type 'CGFloat' to expected argument type 'Int32'
    というエラーだったならば、
    let renTxture: CCRenderTexture = CCRenderTexture(width: Int32(winSize.width), height:Int32(winSize.height), pixelFormat: .RGBA8888, depthStencilFormat: 0)
    にすればよさそうに思えますが…

    正直なところ別の質問として投稿し直した方が別の詳しい方が答えてくれるかもしれません。

    キャンセル

  • 2016/01/22 01:59

    何度もありがとうございます。
    すみません、Could not find member RGBA8888 というエラーでした。また、

    let renTxture: CCRenderTexture = CCRenderTexture(width: self.winSize.width, height: selfwinSize.height, pixelFormat: .RGBA8888, depthStencilFormat: 0)

    に変えてみたところエラーの内容が
    'MainScene' does not have a member named 'winSize'
    となりましたが、よく分かりませんでした。

    そうですね、明日やって解決できなかったときは別でまた質問してみようかと思います。
    ひとまず自分はiPhone6なのでそれに合う値を入れて、

    /**
    * スクリーンショット開始のメソッド
    */
    func screenshotWithStartNode(stNode: CCNode) -> UIImage {
    CCDirector.sharedDirector().nextDeltaTimeZero = true
    let winSize: CGSize = CCDirector.sharedDirector().viewSize()
    let renTxture: CCRenderTexture = CCRenderTexture(width: 320, height: 568, pixelFormat: .RGBA8888, depthStencilFormat: 0)
    renTxture.begin()
    stNode.visit()
    renTxture.end()

    return renTxture.getUIImage()
    }
    こちらで動かしてみようと思います。スクリーンショットが実現できて本当に助かりました。こんなに夜遅くまでコメント下さり感謝していますm(_ _)m ありがとうございます!

    キャンセル

  • 2016/01/22 02:04 編集

    /**
    * スクリーンショット開始のメソッド
    */
    func screenshotWithStartNode(stNode: CCNode) -> UIImage {
    CCDirector.sharedDirector().nextDeltaTimeZero = true
    let winSize: CGSize = CCDirector.sharedDirector().viewSize()
    let renTxture: CCRenderTexture = CCRenderTexture(width: Int32(winSize.width), height:Int32(winSize.height), pixelFormat: .RGBA8888, depthStencilFormat: 0)
    renTxture.begin()
    stNode.visit()
    renTxture.end()

    return renTxture.getUIImage()
    }


    u39uedaさんが最後に教えてくださった
    let renTxture: CCRenderTexture = CCRenderTexture(width: Int32(winSize.width), height:Int32(winSize.height), pixelFormat: .RGBA8888, depthStencilFormat: 0)
    こちらのコードを追加してエラー起きることなく実行することができました!!本当にありがとうございましたm(_ _)m

    キャンセル

-1

そのコードをコピペして、画面にボタンを配置して、ボタンが押された時にそのコードを実行するようにしてみたら、正常にスクリーンショット採取できました。
(こちらの確認環境がXcode7.2のためだと思いますが、UIGraphicsGetCurrentContext()は!でアンラップするよう修正する必要がありました。)

ちなみに、ステータスバーの部分はアプリの画面の外でシステムが表示している部分なので、そこは撮れません。
真っ白ということは、ボタンも撮れてないのでしょうか?
それともスクリーンショット採取時にボタンを隠すような制御を入れているのでしょうか?
なにかそういう制御をしているなら、必ず表示されるはずの部品を配置して試してみた方がよいと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/01/19 21:36

    コメントして下さりありがとうございますm(_ _)m
    ボタンも全て映らない真っ白な状態の画像が保存されます。ちなみにmain.Storyboard使用せず、コードの記述のみで行っています。また、xcodeprojではなくxcworkspaceの方で書き込みを行っている状態です。
    ボタンを隠したりなどの制御は入れてないはずなのですが、なぜか白の画像が保存されてしいます(T_T)
    そもそもこちらのコードの意味を一行ずつ理解していないので正しく使えません。。

    キャンセル

  • 2016/01/21 22:06

    「SpriteBuilderを使用」を見落としていました。というかSpriteBuilderのことはあまり詳しく知りませんが、
    ボタン等含め最終的に全てOpenGLで描画されているのであれば、Stripeさんがコメントされている通り、renderInContext()でイメージに変換できないので、その方法ではキャプチャできません。

    キャンセル

  • 2016/01/22 00:28

    こちらこそ詳しいコードを後から追記したため誤解を招いてしまいすみません。。
    OpenGLで描画したものがrenderInContext()を使用してイメージに変換できないということを知りませんでした。コメント下さりありがとうございますm(_ _)m

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.13%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる