ラベル アニメ の投稿を表示しています。 すべての投稿を表示
ラベル アニメ の投稿を表示しています。 すべての投稿を表示

2017年6月7日水曜日

画面キャプチャ方法

//余計なUIをキャプチャしないように隠す
hogeButton.isHidden = true
hogeLabel.isHidden = true
CATransaction.flush() //画面更新
        
//viewと同じサイズのコンテキスト(オフスクリーンバッファ)を作成
let rect = self.view.bounds
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
//スクリーンをコンテキストに描画 = スクリーンキャプチャ
self.view.drawHierarchy(in: view.bounds, afterScreenUpdates: false)

//スクリーンがキャプチャされたコンテキストからUIImageを作成
let capturedImage : UIImage = UIGraphicsGetImageFromCurrentImageContext()!
//コンテキストの扱いを終了
UIGraphicsEndImageContext()
        
//隠したUIを表示させる
hogeButton.isHidden = false
hogeLabel.isHidden = false

アニメの途中が表示されない問題

Context(オフスクリーンバッファみたいなもん)を作ってそこに画像をdrawしたりrenderしたりしてたんだけど、UIViewアニメの途中をキャプろうとしても、なぜかアニメ中のオブジェクトが実際に表示されている位置でキャプチャされてくれない現象が。

詳しいことはわからんのだが、アニメのメソッドで指定してる最後の移動位置でキャプチャされちゃうみたい。
つまり、AからBの位置に5秒間で移動するアニメを実行中、3秒目でキャプチャしてもなぜかキャプチャ画像だけはBの位置に来てる。(画面上ではまだ途中を動いてるというのに)

renderやdrawじゃなくdrawHierarchy

それを回避するため、drawHierarchy()メソッドを使うのがいいようだ。
ただし、afterScreenUpdatesがtrueの場合は同じような問題が発生するので、falseにしなければいけない。

直前のhiddenが効かない新たな問題

でもfalseにすると、写したくないUI部品をキャプチャ動作の直前でhiddenしても、hiddenされなくて写り込んでしまう。
UI部品をhiddenした直後にsetNeedsDisplay()とかやってもダメ。これは画面更新の予約だけで、実際に更新されるのはOS次第なんだそうな。

よくわからんがflush()で解決

そこで調べたところ、部品をhiddenした直後に CATransaction.flush() をしたところうまくいった。
一瞬部品がhiddenされ、スクリーンキャプチャされた後にまた部品が表示される。
CATransactionはCALayerのアニメを使うためのクラスだそうで、flush()はRunLoop終わりで自動的に呼ばれるものを強制的に(?)実行させるものらしい。
flashじゃなくてflushな。顔を赤らめるとか、興奮、感情の芽生え、トイレの貯水槽とかの意味がある。なんだよそれ!?w
正直言ってUIViewのメソッドじゃないのにうまくいくのは納得いかないんだけど、結果オーライでいいや。

2015年10月19日月曜日

UIViewのアニメーション方法いろいろ

種類があるようなので、気づいたら追記する形でまとめていく。

UIView.beginAnimations()

beginAnimations()とcommitAnimations()ではさむタイプ
メソッド前にアニメ前の状態を設定しておき、Durationの秒数かけてbeginAnimationsとcommitAnimationsの間に書かれた(AnimationCurveやDurationは設定なので別として)状態にアニメーションする。
centerなどの位置や、alphaチャネルも変化できる。
print文はアニメが始まってすぐに表示される。

        image.center = CGPointMake(0, 0)

        UIView.beginAnimations("imageMove", context: nil)
        UIView.setAnimationCurve(UIViewAnimationCurve.Linear)
        UIView.setAnimationDuration(10.0)
        image.center = CGPointMake(-50, 100)
        UIView.commitAnimations()
        print("アニメ終わり")

UIView.animationWithDuration()

クロージャを使うタイプ
メソッド前にアニメ前の状態を設定しておき、Durationの秒数かけて最初のクロージャに書かれた状態にアニメーションする。
centerなどの位置や、alphaチャネルも変化できる。
print文はアニメが終了後に表示される。

        image.center = CGPointMake(0, 0)
        
        UIView.animateWithDuration(10.0,
            delay: 0,
            options: UIViewAnimationOptions.CurveLinear,
            animations: { () -> Void in
            self.image.center = CGPointMake(-50, 100)
            }) { (Bool) -> Void in
                print("アニメ終わり")

        }

UIImageViewのパラパラアニメ

各フレームのコマを配列に用意しておき、それをstartAnimating()で開始する。
RepeatCountは繰り返しの回数で、0の場合無限に繰り返し。
途中で終了する際はanimeImage.stopAnimating()。
終了後は先に設定しておいたUIImage(ここでは"normalImage")になる。
前述のUIViewのアニメーションと組み合わせ、パラパラアニメしながら位置や大きさを変化などもできる。

                //最初にアニメ終了後に表示するimageを設定しておく
                animeImage.image = UIImage(named: "normalImage")
                //アニメのコマ設定
                let imageArray:[UIImage] = [
                    UIImage(named: "animeImage1")!,
                    UIImage(named: "animeImage2")!,
                    UIImage(named: "animeImage3")!,
                    UIImage(named: "animeImage4")!
                ]
                animeImage.animationImages = imageArray
                animeImage.animationDuration = 2.0
                animeImage.animationRepeatCount = 1
                animeImage.startAnimating()