MILLEN BOX

音楽好きの元組み込みソフトエンジニアによるプログラミング(主にiOSアプリ開発)の勉強の記録

タッチする毎に画像を生成。生成後コントロール可能。(UIImageView, swift 1.2, viewDidLayoutSubviews)

手から沢山のトランプが出現するマジックをアプリケーションで表現したくなりました。 Githubは以下です。

github.com

自分ポイント1

プロジェクトを確認頂くとわかると思うのですが、imageを指定していないviewを予め用意しています。このviewをタッチ時にカード画像へ変化させようという算段です。

class ViewController: UIViewController {

    @IBOutlet var imageCurrectCardView: UIImageView!    //Magic開始範囲 & 生成中のカードView
    ...
}

自分ポイント2

特定範囲をタッチ時にカードを生成します。ポイント1でも述べた通り、imageを指定していないviewを予め用意して、その範囲をタッチしたら、そのviewをカード画像に変化させてます。後ほど使い回すために、cardViewを見える化する関数displayImageCardViewWithPointも作成。

        override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
        let touch = touches.first as! UITouch
        touchPoint = touch.locationInView(self.view)            //タッチ座標を取得
        let tag = touch.view.tag
        startTouchPoint = touchPoint
        
        if tag == 999 {     //imageCurrectCardViewをタッチした場合のみ以下の処理を行う
            let size = CGSizeMake(100, 100)                                         //サイズ格納用変数size作成
            imageCurrectCardView.contentMode = UIViewContentMode.ScaleAspectFit     //画像の表示方法を設定
            displayImageCardViewWithPoint(  touchPoint!,
                                            size: size,
                                            imgName: "card.png",
                                            imageView: imageCurrectCardView,
                                            isControlImageCardView: true)           //cardViewを見える化関数を実行
        }
        
    }

//cardViewを見える化する関数
    func displayImageCardViewWithPoint(point: CGPoint, size: CGSize, imgName: String, imageView: UIImageView, isControlImageCardView :Bool) {
        //, imgName: String, contentMode: String, isControlImageCurrectCardView :Bool
        imageView.frame.size = size                             //sizeをカードviewに適応
        imageView.center = point                                //pointをviewに適応
        imageView.image = UIImage(named: imgName)               //imgName画像を表示
        isControlImageCurrectCardView = isControlImageCardView  //imageCurrectCardViewが操作中 or not
        self.view.bringSubviewToFront(imageView)                //imageView最前面に
    }

自分ポイント3

viewDidLayoutSubviewsを作成し、内部でごにょごにょしてます。これをしとかないと1回目のタッチ時の画像の大きさと座標が意図したものではないものなります。Auto LayoutをDisableにすると、この処理は必要ありませんが、今後の勉強のためにAuto Layout Enable状態で実装してみました。

AutoLayoutがEnableではview.frameの決定はviewDidLayoutSubviewsのタイミングで行われる(らしい)。view更新時、touchesBeganの後に呼ばれているっぽいです。

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        if isControlImageCurrectCardView != false {     //imageCurrectCardViewの操作中でなければ
            var size = CGSizeMake(100, 100)             //サイズ格納用変数size作成
            imageCurrectCardView.frame.size = size      //sizeをカードviewに適応
            imageCurrectCardView.center = touchPoint!   //タッチ座標をカードviewに適応
        } else {
            //Do nothing
        }
    }

自分ポイント4

touchesEnded時、カードを生成したviewを初期状態に戻します。見えているカードviewについては、内部的には新たに作成します。複数の画像が生成されることを考慮して配列にUIImageViewを格納。タグ付けを行い、後ほどコントロールすることを考慮済。

    override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
        let touch = touches.first as! UITouch
        touchPoint = touch.locationInView(self.view)    //タッチ座標を取得
        let tag = touch.view.tag
        
        if tag == 999 {     //imageCurrectCardViewをタッチした場合のみ以下の処理を行う
            //1. imageCurrectCardViewを初期状態に戻す
            let initialPoint = CGPointMake(187.5, 334.0)       //center座標格納変数point作成 (初期値に戻す)
            let initialSize = CGSizeMake(157, 210)             //サイズ格納用変数size作成 (初期値に戻す)
            imageCurrectCardView.contentMode = UIViewContentMode.ScaleToFill        //contentModeを変更
            displayImageCardViewWithPoint(  initialPoint,
                                            size: initialSize,
                                            imgName: "",
                                            imageView: imageCurrectCardView,
                                            isControlImageCardView: false)          //cardViewを見える化(コレは厳密に言うと見えない化...)関数を実行


            //2. imageCurrectCardViewをimageOldCardViewArrayに格納する(ようなイメージの処理)
            var img = UIImage(named: "")
            imageOldCardViewArray.append(UIImageView(image:img))                    //imageOldCardViewArrayに要素を追加
            let count = imageOldCardViewArray.count                                 //imageOldCardViewArrayの数を保存
            let size = CGSizeMake(100, 100)                                                 //サイズ格納用変数size作成
            
            imageOldCardViewArray[count - 1].contentMode = UIViewContentMode.ScaleAspectFit //contentModeを変更
            imageOldCardViewArray[count - 1].tag = count                                    //配列番号でタグ付け
            displayImageCardViewWithPoint(  touchPoint!,
                                            size: size,
                                            imgName: "card.png",
                                            imageView: imageOldCardViewArray[count - 1],
                                            isControlImageCardView: false)                  //cardViewを見える化関数を実行
            self.view.addSubview(imageOldCardViewArray[count - 1])                          //画像生成
            
            imageOldCardViewArray[count - 1].userInteractionEnabled = true                  //タッチ操作を有効に
        } else {
            //Do nothing
            
        }
        
    }

自分ポイント5

生成したカードは全て後からでもコントロール可能にしています。

    override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
        let touch = touches.first as! UITouch
        touchPoint = touch.locationInView(self.view)            //タッチ座標を取得
        let tag = touch.view.tag
        startTouchPoint = touchPoint
        
        if tag == 999 { 
            ....
        } else if tag > 0 {
            startImagePoint = imageOldCardViewArray[tag - 1].center                 //タッチしたカードのスタート位置を保存
            self.view.bringSubviewToFront(imageOldCardViewArray[tag - 1])           //imageOldCardViewArrayを最前面に
        } else {
            //Do nothing
        }
        
    }

自分ポイント6

ボタンを押したら、カード画像と配列を消去します。画像の消去にはremoveFromSuperview, 配列の削除にはremoveAll()を使用します。今回はアニメーションを使用して画像を見えなくしたのでremoveFromSuperviewは使用していません。何故使用しなくてOKなのかは、はっきり言って理解していません...。

    @IBAction func pressClean(sender: AnyObject) {
        for (index, val) in enumerate(imageOldCardViewArray) {
            UIView.animateWithDuration(2, animations: { () -> Void in
                self.imageOldCardViewArray[index].layer.opacity = 0.0
            })
            //self.imageOldCardViewArray[index].removeFromSuperview()
        }
        
        imageOldCardViewArray.removeAll()
        
    }

自分の中では、もっと簡単に作成できると思ったのですが、かなり時間がかかりました。(3日くらい...)

もっとおしゃれに書けるようになりたいなぁ...。