人生の恥は書き捨て

プログラムとかいろいろ

NSTimerとUISliderを使ってSwiftで画像スライドショーを作る

やること

画像のスライドショーのようなものを作ります。

スライドバーがついていて、スライドさせると画像が入れ替わります。
また時間とともに画像が入れ替わります。

スライドバーの変化で画像を入れ替えるとともに、
NSTimerを使って時間経過とともに画像を入れ替えます。

完成形

f:id:kazuhei0108:20150126010315p:plain

f:id:kazuhei0108:20150126010328p:plain

コード

ひとまず全コードを貼っておきます。

import UIKit

class ViewController: UIViewController {
    
    let photos = ["写真1.png", "写真2.png", "写真3.png", "写真4.png", "写真5.png", "写真6.png"]
    
    var barHeight: CGFloat = 0.0
    var displayWidth: CGFloat = 0.0
    var displayHeight: CGFloat = 0.0
    var imageView: UIImageView!
    var timer: NSTimer!
    var slider: UISlider!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // statusbarの高さ取得
        barHeight = UIApplication.sharedApplication().statusBarFrame.size.height
        
        // viewのサイズ取得
        displayWidth = self.view.frame.width
        displayHeight = self.view.frame.height
        
        // 一枚目の原稿を読み込む
        let image:UIImage! = UIImage(named: photos[0])
        // 画像のアスペクト比を出して横幅いっぱいに画像を表示する
        let aspect:CGFloat = image.size.height / image.size.width
        imageView = UIImageView(frame: CGRect(x: 0, y: barHeight + ((displayHeight - barHeight - displayWidth * aspect) / 2), width: displayWidth, height: displayWidth * aspect))
        imageView.image = image;
        self.view.addSubview(imageView)
        
        // Sliderを作成する.
        slider = UISlider(frame: CGRectMake(0, 0, displayWidth*0.9, 20))
        slider.layer.position = CGPointMake(displayWidth / 2, displayHeight - 30)
        slider.backgroundColor = UIColor.whiteColor()
        slider.layer.cornerRadius = 10.0
        slider.layer.masksToBounds = false
        
        // 目盛の最小値と最大値を設定する.
        slider.minimumValue = 0
        slider.maximumValue = Float(manuscripts.count) - 1.0
        
        // Sliderの初期位置を設定する.
        slider.value = 0
        
        // Sliderの現在位置より右のTintカラーを変える.
        slider.maximumTrackTintColor = UIColor.grayColor()
        
        // Sliderの現在位置より左のTintカラーを変える.
        slider.minimumTrackTintColor = UIColor.greenColor()
        
        // 値が変化した時
        slider.addTarget(self, action: "onChangeValueSlider:", forControlEvents: UIControlEvents.ValueChanged)
        // 指を付けた時
        slider.addTarget(self, action: "timerStart:", forControlEvents: UIControlEvents.TouchUpInside)
        // 指を離した時
        slider.addTarget(self, action: "timerStop:", forControlEvents: UIControlEvents.TouchDown)
        
        self.view.addSubview(slider)
        
        timerInitialized()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    func timerInitialized () {
        timer = NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: Selector("nextPage"), userInfo: nil, repeats: true)
    }
    
    func timerStart (sender : UISlider) {
        if (!timer.valid) {
            timerInitialized()
        }
    }
    
    func timerStop (sender : UISlider){
        if (timer.valid) {
            timer.invalidate()
        }
    }
    
    // Sliderの値が変わった時に呼ばれるメソッド
    func onChangeValueSlider(sender : UISlider){
        let index = Int(sender.value)
        
        // 再びUIView処理済み画像を設定する.
        imageView.image = UIImage(named: photos[index])
        
        // 再描画をおこなう.
        imageView.setNeedsDisplay()
    }
    
    func nextPage (){
        slider.value++
        onChangeValueMySlider(slider)
    }
}

解説

ざっくりと解説。
Single View Applicationで作っていきます。
StoryBordやAutoLayout等のGUIは使いません。

変数の宣言

    let photos = ["写真1.png", "写真2.png", "写真3.png", "写真4.png", "写真5.png", "写真6.png"]
    
    var barHeight: CGFloat = 0.0
    var displayWidth: CGFloat = 0.0
    var displayHeight: CGFloat = 0.0
    var imageView: UIImageView!
    var timer: NSTimer!
    var slider: UISlider!


一枚目の画像を貼り付けます。
この際、横幅が画面いっぱいになるようにアスペクト比を使って計算しています。

        // statusbarの高さ取得
        barHeight = UIApplication.sharedApplication().statusBarFrame.size.height
        
        // viewのサイズ取得
        displayWidth = self.view.frame.width
        displayHeight = self.view.frame.height
        
        // 一枚目の原稿を読み込む
        let image:UIImage! = UIImage(named: photos[0])
        // 画像のアスペクト比を出して横幅いっぱいに画像を表示する
        let aspect:CGFloat = image.size.height / image.size.width
        imageView = UIImageView(frame: CGRect(x: 0, y: barHeight + ((displayHeight - barHeight - displayWidth * aspect) / 2), width: displayWidth, height: displayWidth * aspect))
        imageView.image = image;
        self.view.addSubview(imageView)

画像をはりつける高さはステータスバーを抜いて、上下の余白が均等になるように計算しています。


次にスライダーを作って、スタイルや初期値を設定します。

        // Sliderを作成する.
        slider = UISlider(frame: CGRectMake(0, 0, displayWidth*0.9, 20))
        slider.layer.position = CGPointMake(displayWidth / 2, displayHeight - 30)
        slider.backgroundColor = UIColor.whiteColor()
        slider.layer.cornerRadius = 10.0
        slider.layer.masksToBounds = false
        
        // 目盛の最小値と最大値を設定する.
        slider.minimumValue = 0
        slider.maximumValue = Float(manuscripts.count) - 1.0
        
        // Sliderの初期位置を設定する.
        slider.value = 0
        
        // Sliderの現在位置より右のTintカラーを変える.
        slider.maximumTrackTintColor = UIColor.grayColor()
        
        // Sliderの現在位置より左のTintカラーを変える.
        slider.minimumTrackTintColor = UIColor.greenColor()


スライダーにイベントを登録します。

        // 値が変化した時
        slider.addTarget(self, action: "onChangeValueSlider:", forControlEvents: UIControlEvents.ValueChanged)
        // 指を付けた時
        slider.addTarget(self, action: "timerStart:", forControlEvents: UIControlEvents.TouchUpInside)
        // 指を離した時
        slider.addTarget(self, action: "timerStop:", forControlEvents: UIControlEvents.TouchDown)

スライダーの値が変化したら、onChangeValueMySliderを呼んで画像を切替えます。

スライダーを操作している時にタイマーが動くと画像が切り替わってしまうので、
UIControlEvents.TouchUpInsideのイベントでtimerを止めて、
UIControlEvents.TouchDownのイベントで再びtimerを動かすようにしています。


タイマーは、2秒に1回nextPageの関数を呼ぶようにして、これで画像を次に進めています。

func timerInitialized () {
        timer = NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: Selector("nextPage"), userInfo: nil, repeats: true)
    }

2秒ごと繰り返すのでrepeats: true
userInfoはselectorに渡す引数を指定しますが、今回は無いのでnilを渡しています。

メモ

addTargetのactionはtargetを追加される側のオブジェクトを引数を取らないとダメっぽい?(ちょっと良くわかってない)

func timerStart (sender : UISlider) {
    if (!timer.valid) {
        timerInitialized()
    }
}

↑の(sender: UISlider)の部分のことです。