今回のゴール
- UIImageViewにgifを描画して動かす
- UIImageView自体を1秒間に少しずつ左右に動かす
今回のキーワード
- UIImageView
- gif
- UIButton
- CGAffineTransform
- Timer
やったこと
新規プロジェクトを作成するよ
- 省略
- アプリ名は適当にデジモン+艦これで「Dejikore」にしたよ
画面を横のみを許可する
- デジモン = 横画面というイメージなので、まずは画面を横画面表示のみ許可するようにするよ
Main.storyboardにUIImageViewを置いてViewControllerと紐づけるよ
描画する用のgifを用意して、imageViewに紐づける
今回使う素材
素材はクローバーラボ株式会社様ご提供の素材を利用させていただきます
ありがとうございます
http://cloverlab.jp/
http://yurudora.com/tkool/
- 素材を用意したらMacのプロジェクトフォルダ側にコピーしてからXcodeにD&Dしてファイルを追加するよ
- 追加したらインスペクタからimageViewのimageとして追加してビルドしてみるよ
そのままじゃgifアニメが動かないのでググるよ
- 検索ワード「UIImageView gif」
- どうやらそのまま突っ込むだけじゃgifアニメは動いてくれない様子
- こちらの記事を参考にダウンロード・UIImage+gif.swiftをプロジェクトに追加して動かしてみるよ
- UIImage.gifのurlは追加した画像と同じファイル名を記述するよ
- ビルド!
- 動いた
ViewController.swift
class ViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
imageView.image = UIImage.gif(name: "character")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
このimageViewをButtonタップしたら動かすようにするよ
viewController.swift
class ViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
imageView.image = UIImage.gif(name: "character")
}
@IBAction func move(_ sender: UIButton) {
// キャラ画像のframeを変数に格納
var frame = imageView.frame
// x方向に50ほど移動量を追加して変数に格納
let moveX = frame.origin.x + 50
// moveXを使ってframeを更新
frame = CGRect(x: moveX, y: frame.origin.y, width: imageView.frame.width, height: imageView.frame.height)
// imageViewのframeに代入
imageView.frame = frame
}
~~~省略~~~
画面内で動くように修正するよ
- 画像が右向きなのか左向きなのかを判定するフラグ「isLeft」を追加するよ
- 左を向いていればマイナス方向(←)に、逆ならプラス方向(→)に動くようにするよ
ViewController.swift
class ViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView!
private var isLeft: Bool = true // キャラが左を向いているかどうかのフラグ
override func viewDidLoad() {
super.viewDidLoad()
imageView.image = UIImage.gif(name: "character")
}
@IBAction func move(_ sender: UIButton) {
// キャラ画像のframeを変数に格納
var frame = imageView.frame
// 移動量を定義して変数に格納
let moveX: CGFloat = 50.0
// 画面の左端(x座標が0)に到達したら
if frame.origin.x < 0 {
// 右を向くことを表すためにフラグをfalseにする
isLeft = false
// もしキャラのx座標が右端(画面の最大x座標 - imageViewのサイズ分)に到達したら
} else if frame.origin.x > view.frame.maxX - imageView.frame.width {
// 左を向くことを表すためにフラグをtrueにする
isLeft = true
}
// 左を向いていれば左に動かすためにx軸の値をマイナス、逆ならプラスする
isLeft ? (frame.origin.x -= moveX) : (frame.origin.x += moveX)
// imageViewのframeに代入
imageView.frame = frame
}
imageViewをどうやれば反転させられるかをググるよ
- 検索ワード「UIImageView 反転」
- UIViewを反転させるの記事を参考にCGAffinetransformを使ってUIImageViewを反転させるよ
- isLeftの値が変わる時にCGAffineTransformでx軸の値を変えてあげればよさげだよ
ViewController.swift
~~~省略~~~
if frame.origin.x < 0 {
// 右を向いてることを表すためにフラグをfalseにする
isLeft = false
// 右を向いたら反転させる
imageView.transform = CGAffineTransform(scaleX: -1 , y: 1)
// もしキャラのx座標が右端(画面の最大x座標 - imageViewのサイズ分)に到達したら
} else if frame.origin.x > view.frame.maxX - imageView.frame.width {
// 左を向いていることを表すためにフラグをtrueにする
isLeft = true
// 左を向いたら正しい方向にする
imageView.transform = CGAffineTransform(scaleX: -1 , y: 1)
~~~省略~~~
これでもよさげだけど、なんとなくプロパティ監視(willSet)とか使ってみるよ
ViewController.swift
~~~省略~~~
private var isLeft: Bool = true {
willSet(newValue) {
// 新しい値がtrueだったら正しい方向に、falseだったら反転させる
if newValue {
imageView.transform = CGAffineTransform(scaleX: 1 , y: 1)
} else {
imageView.transform = CGAffineTransform(scaleX: -1 , y: 1)
}
}
}
~~~省略~~~
- うまいこと動いた!
ボタンをポチポチするのめんどくさいからTimerを使って一定間隔ごとに実行するようにするよ
- まずIBOutletされているmoveの接続を外してただのfunctionに修正するよ
- ViewControllerにstartTimerという名前の関数を作ってButtonとIBOutlet接続するよ
- ViewControllerのプロパティとしてTimerを宣言するよ
- startTimer内でtimerを初期化、timer.fire()でタイマーを動かすよ
- startTimerではタイマーが動いていればタイマーを止めて、そうでなければ再生成して動かしているよ
- 再生成するのはinvalidateするとタイマー自体が破棄されてしまうためだよ
- https://developer.apple.com/documentation/foundation/timer
- ついでにタップした時にボタンのタイトルを変えるようにしているよ
ViewController.swift
class ViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView!
private var timer = Timer() // Timerの宣言
~~~省略~~~
func move(_ timer: Timer) {
// キャラ画像のframeを変数に格納
var frame = imageView.frame
// 移動量を定義して変数に格納
let moveX: CGFloat = 50.0
// 画面の左端(x座標が0)に到達したら
if frame.origin.x < 0 {
// 右を向いてることを表すためにフラグをfalseにする
isLeft = false
// もしキャラのx座標が右端(画面の最大x座標 - imageViewのサイズ分)に到達したら
} else if frame.origin.x > view.frame.maxX - imageView.frame.width {
// 左を向いていることを表すためにフラグをtrueにする
isLeft = true
}
// 左を向いていれば左に動かすためにx軸の値をマイナス、逆ならプラスする
isLeft ? (frame.origin.x -= moveX) : (frame.origin.x += moveX)
// imageViewのframeに代入
imageView.frame = frame
}
@IBAction func startTimer(_ sender: UIButton) {
// タイマーが動いていれば
if timer.isValid {
// タイマーを止める
timer.invalidate()
sender.setTitle("start", for: .normal)
} else {
sender.setTitle("stop", for: .normal)
// そうでなければ動かす
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(move(_:)), userInfo: nil, repeats: true)
timer.fire()
}
}
結果
疲れた!とりあえず今回の目標は達成!!
今日の作業ブランチはこちら
https://github.com/nasteng/Dejikore/tree/update-gif-moving
2017/6/22追記
次回
素材画像から一部を切り取ってgif化する
http://qiita.com/nasteng/items/a46315145abaa626adee
最後に
ご指摘・リファクタ大歓迎です!