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

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

新規登録して質問してみよう
ただいま回答率
85.50%
Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

2回答

773閲覧

view内にランダムに円をフェードインアウトで出現させたい

okkyu

総合スコア18

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2020/06/30 05:30

view内で円を交互にランダムに繰り返し出現させたいのですが方法がわからず困っております。
下記のコードでfor文を使ってループさせてみたのですが、10回分が同時に出現しただけで思った通りの結果になりませんでした。
何か良い方法はないでしょうか?教えていただけましたら幸いです。

swift

1コード 2 3class ViewController: UIViewController { 4 5 func makeImage(width w: CGFloat,height h: CGFloat) -> UIImage{ 6 let size = CGSize(width: w, height: h) 7 UIGraphicsBeginImageContextWithOptions(size, false, 1.0) //size、opaques、scale 8 let context = UIGraphicsGetCurrentContext() 9 let drawRect = CGRect(x: 0, y: 0, width: w, height: h) 10 let drawPath = UIBezierPath(roundedRect: drawRect, cornerRadius: 100) 11 context?.setFillColor(red: 0, green: 1.0, blue: 1.0, alpha: 1.0) 12 drawPath.fill() 13 context?.setStrokeColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0) 14 drawPath.stroke() 15 let image = UIGraphicsGetImageFromCurrentImageContext() 16 UIGraphicsEndImageContext() 17 18 return image! 19 } 20 21 override func viewDidLoad() { 22 super.viewDidLoad() 23 // Do any additional setup after loading the view. 24 25 let c: CGFloat = 100.0 26 let fw = view.frame.width 27 let fh = view.frame.height 28 29     for _ in 0..<10{ 30 rdmImage(viewFrameWidth: fw, viewFrameHeight: fh, circleSize: c) 31 } 32 } 33 34 func rdmImage(viewFrameWidth w: CGFloat,viewFrameHeight h: CGFloat,circleSize c: CGFloat){ 35 var rdmWid = CGFloat(arc4random_uniform(UInt32(w))) 36 var rdmHei = CGFloat(arc4random_uniform(UInt32(h))) 37 38 if rdmWid<c/2{ 39 rdmWid = c/2 40 }else if rdmWid>w-c/2{ 41 rdmWid = w-c/2 42 } 43 if rdmHei<c/2{ 44 rdmHei = c/2 45 }else if rdmHei>h-c/2{ 46 rdmHei = h-c/2 47 } 48 49 let circleImage = makeImage(width: c, height: c) 50 let circleView = UIImageView(image: circleImage) 51 circleView.center = CGPoint(x: rdmWid, y: rdmHei) 52 circleView.alpha = 0.0 53 self.view.addSubview(circleView) 54 55 UIView.animate( 56 withDuration: 2.0, 57 delay: 0, 58 options: [.curveEaseInOut], 59 animations: {circleView.alpha = 1.0}, 60 completion: {(finished: Bool) in 61 self.fadeOutRemove(circleView) 62 }) 63 } 64 65 func fadeOutRemove(_ view: UIView){ 66 UIView.animate(withDuration: 2.0, delay: 0, options: UIView.AnimationOptions(), animations: { 67 view.alpha = 0.0 68 }, completion: {(finished: Bool) in 69 view.removeFromSuperview() 70 }) 71 } 72} 73

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

for i in 0..<10 { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5*Double(i)) { self.rdmImage(viewFrameWidth: fw, viewFrameHeight: fh, circleSize: c) } }

こんなんじゃダメですかね・・

投稿2020/06/30 14:26

編集2020/07/01 00:22
fugu

総合スコア35

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/06/30 20:32

シンプルでいいと思います。なぜか2回書いてあるけど。
fugu

2020/07/01 00:22

ほんとだ、すいません、修正します。
okkyu

2020/07/01 04:26

ご回答ありがとうございます。参考にさせていただきます。
guest

0

ベストアンサー

asyncAfterは最初に思いついたので、ちょっと別解を考えてみました。

UIView.animate()completion (つまり、クロージャ)に終了後の動作を記述するのはもちろんアリなのですが、それを次々と記述していくと可読性が落ちてしまいそうなので、Core Animation の別の手法に書き換えてみました。

ここでは、DispatchQueueに入れるのではなく、CATransaction.setCompletionBlock で再帰的に rdmImage() を呼び出しています。

また、これは完全に先読みなのですが、もしアニメーションを途中で中断したくなったときに、任意のタイミングで中断できるようにもしてみました。

下記の実装ではボタンを押したタイミングでアニメーションを中断しています。

Swift

1import UIKit 2 3class ViewController: UIViewController { 4 // MARK: アニメーションのレイヤ 5 var animationLayer: CALayer? 6 7 // 中略 8 9 override func viewDidLoad() { 10 super.viewDidLoad() 11 // 中略 12 13 // MARK: 再帰呼び出しするようにメソッドを書き換え。ここでは10回呼び出し 14 recursiveRdmImage(viewFrameWidth: fw, viewFrameHeight: fh, circleSize: c, repeats: 10) 15 } 16 17 func recursiveRdmImage(viewFrameWidth w: CGFloat,viewFrameHeight h: CGFloat,circleSize c: CGFloat, repeats: Int) { 18 19 var rdmWid = CGFloat.random(in: 0...w) 20 var rdmHei = CGFloat.random(in: 0...h) 21 22 23 if rdmWid<c/2{ 24 rdmWid = c/2 25 }else if rdmWid>w-c/2{ 26 rdmWid = w-c/2 27 } 28 if rdmHei<c/2{ 29 rdmHei = c/2 30 }else if rdmHei>h-c/2{ 31 rdmHei = h-c/2 32 } 33 34 let circleImage = makeImage(width: c, height: c) 35 let circleView = UIImageView(image: circleImage) 36 circleView.center = CGPoint(x: rdmWid, y: rdmHei) 37 circleView.alpha = 0.0 38 self.view.addSubview(circleView) 39 40 // MARK: トランザクションとして一括登録 41 CATransaction.begin() 42 43 // MARK: トランザクション終了時の処理 44 CATransaction.setCompletionBlock { 45 circleView.removeFromSuperview() 46 47 if repeats > 0 && self.animationLayer != nil { 48 // MARK: 繰り返しが残っていて、かつanimationLayer != nil の場合は再帰呼び出し 49 self.recursiveRdmImage(viewFrameWidth: w, viewFrameHeight: h, circleSize: c , repeats: repeats - 1) 50 } else { 51 // すべてのアニメーションが終わったので animationLayer を nil にする 52 self.animationLayer = nil 53 } 54 } 55 56 // MARK: 4秒間のアニメーションを frameAnimation としてまとめて登録する 57 // layer には alpha というプロパティはなく、かわりに opacity を使う 58 let animation = CAKeyframeAnimation(keyPath: "opacity") 59 animation.duration = 4.0 60 animation.keyTimes = [0.0, 0.5, 1.0] 61 animation.values = [0.0, 1.0, 0.0] 62 // MARK: アニメーションを登録する。 63 // forkey: は必須ではないが、あとで削除するときにキーが必要なので登録 64 circleView.layer.add(animation, forKey: "rdmImage") 65 66 // MARK: アニメーションを止めるときに使う 67 animationLayer = circleView.layer 68 CATransaction.commit() 69 } 70 71 // MARK: 止めるボタンを押した時の処理 72 @IBAction func stopButton(_ sender: Any) { 73 if animationLayer == nil { 74 print("no animations") 75 return 76 } 77 78 // forkey で指定したアニメーションを止める 79 animationLayer?.removeAnimation(forKey: "rdmImage") 80 animationLayer = nil 81 82 } 83}

投稿2020/06/30 23:42

TsukubaDepot

総合スコア5086

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

okkyu

2020/07/01 04:26

ご回答ありがとうございます。悩んでいたことが解決でき、とても参考になりました。ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問