【Swift】UICollectionViewでチェックマークをつける

1日1個しか進まない呪いにでもかかってるのかな・・・?

これはもう3日4日ぐらい悩みました・・・。別の作業をして若干諦め気味だったけれどようやくできました。よかった。

 

UICollectionViewにチェックマークをつける。要は写真アプリと一緒なことがしたいのですが、最後のセルにしかマークが付かなかったり、連続でONにすることはできるがOFFにすることはできなかったり。

とにかく説明していきます〜。UICollectionViewの使い方云々は割愛。

 

全体の流れ

  1. 空の配列を用意
  2. タップしたセルのindexPathを配列にいれる
  3. コレクションビューをリロードする
  4. リロードした際に分岐する。indexPathが配列内にある場合はチェックマークを表示させる
  5. もう一度タップされたら配列からindexPathを取り除く

 

セルをタップした時のアクション

まず、空の配列を用意します。

var checkArray:NSMutableArray = []

次にセルをタップしたときのアクションを設定します。

 func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
    if checkArray.containsObject(indexPath){
          checkArray.removeObject(indexPath)
     }else{
          checkArray.addObject(indexPath)
     }
     myCollectionView.reloadData()
}

 

collectionView:didSelectItemAtIndexPath:メソッド内で配列にタップされたセルの行列情報を入れておきます。

最初にタップされた際(on)には配列は空なので、elseを通りindexPathを配列にいれます。

次にタップされた際(off)には配列からindexPathを取り除きます。

 

分岐をしてチェックマークをつける

セルを作成します。

var checkBtnView:UIImageView!
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell",forIndexPath:indexPath)
        for subview in cell.contentView.subviews{
            subview.removeFromSuperview()
        }
        if checkArray.containsObject(indexPath){
            checkBtnView = UIImageView()
            let checkImage = UIImage(named:"check.png")! as UIImage
            checkBtnView!.frame = CGRectMake(0,0,20,20)
            checkBtnView!.layer.position = CGPoint(x: cell.layer.frame.width - 15.0, y: cell.layer.frame.height - 15.0)
            checkBtnView!.image = checkImage
            cell.contentView.addSubview(checkBtnView!)
        }else{
            print("何も表示しません")
        }
}

 

チェックボタンの画像は自分で作成しておきます。

collectionView:cellForItemAtIndexPath:メソッド内でセルを作成します。

セルのsubviewを初期化し、先ほど作成した配列の中にindexPathがあればチェックマークをセルに表示させます。

indexPathがなければ何も表示しません。

 

最初にcellのsubviewを初期化するのを忘れずに。詳しくはこちらの記事(UICollectionViewで二重に表示されるのを防ぐ)

 

これで完了のはずです〜

チェックマークをすべて消す場合はこんな感じです。

checkArray.removeAllObjects()
myCollectionView.reloadData()

 

 

失敗談・・・

こちらの記事を参考して最初はこのようにして場合分けしていました

func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
        var cell = self.cellForItemAtIndexPath(indexPath)

        cell?.tag += 1
        if cell!.tag % 2 == 0 {
            cell?.selected = false
        }

        if cell?.selected == true {
            cell?.backgroundColor = UIColor.redColor()
        } else if cell?.selected == false {
            cell?.backgroundColor = UIColor.clearColor()
        }
    }

 

もしかしたら途中で書き換えてしまったのかもしれませんが、cellのtag内にはindexPath.rowが入っています。(最初はちゃんと動いたと思ったんですが・・・)

これはタグが2で割り切れればfalseを返し、割り切れなければtrueを返します。そうすることで1回タップで背景を赤色に、2回目は消す、3回目は赤・・・という風にon/offができています。

 

しかし私のタグにはindexPath.rowが入っている。。。0番目のところ(偶数番目)は上手くいくのですが1番目(奇数番目)は上手く動きません。

それでも最初は上手く動いてたので(ように見えてただけかもれしませんが)よくわからない・・・

 

とりあえず上手くチェックマークをつけることに成功しましたが、何故失敗したのかも考えていかないとですね(´・ω・`)

 

2018/04/14追記 Swift4に対応しました。
どうもお久しぶりです。

少しだけ手順を変更して。かつての私よ、毎度リロードするのはイクナイ気がしますよ。

  1. 空の配列を用意
  2. タップした時にセルのindexPathが配列内にあるかをチェック
  3. あれば配列内から削除・なければ配列に入れる
  4. タップしたセルのチェックマークをオンオフする
あとはcollectionView(_:cellForItemAt:)内にてindexPathが配列内に含まれているかどうかを確認する必要があります。 これがないとスクロールした時に前の情報に戻ってしまうので。

タップしたら背景色を青にするよっていうコレクションビューを作ってみました。Swift4です。

タップした時に配列内のindexPathのindexを探して、そのindexが見つかれば削除、なければ配列に入れるって処理をしています。
        //位置を探す
        if let index = checkArray.index(where: { (index) -> Bool in
            return index == indexPath
        }){
            //見つかったら配列から削除する
            checkArray.remove(at: index)
            cell.contentView.backgroundColor = defaultColor //セルの色をデフォルトに戻す
        }else {
            //配列内にないので追加する
            checkArray.append(indexPath)
            cell.contentView.backgroundColor = checkColor   //背景に色をつける
        }
の部分です。 containsで調べてもいいんですが、removeするには何番目に入っているのか知る必要があるのでこんな感じにしてみました。 本記事のcellForItemAtのところ、見てられなくて流石にツライ。そのなかでaddSubviewしないでねって、ついさっき【Swift】UICollectionViewで二重に表示されるのを防ぐに書いたばっかりだよ! 今修正したコードもいつか見てらんねーってなる日が来るのかな。 それではでは。あでゅー

Comments...

コメントは認証制です。詳しくは下記の注意をお読みください。お気軽にコメントお願いします!

Write a Comment

コメント時の注意

「Twitter」「Facebook」「Google+」「WordPress」のいずれかのアカウントをお持ちの方は各アカウントと連携することでコメントできます。 コメントしたことはSNSに流れませんので、アカウントをお持ちの方はこちらの方法でコメントを投稿して下さると嬉しいです。 アカウントをお持ちでない方はメールアドレスで投稿することができます。 初回コメント時は承認後に表示されます。

Related Memo...

記事を書くほどでもないけれどメモっておきたいこと

テスト投稿。

例えばiphone7 の画面サイズ

750 × 1334
半分375 × 667

iOS

UINavigationController + UIScrollView の組み合わせで使っている時に謎の余白ができる時

UINavigationController + UIScrollView の組み合わせで使っていて、UIScrollView 上に AutoLayout で上下左右0で View を設置しているのに、30px程度上にずれてしまうとき。

`navigationController.navigationBar.isTranslucent = false` にすると直るかもしれない。

ScrollView上のコンテンツとNavigationBarの重なっているところが透過していたら多分これで直せるはず。

通常のターゲットではちゃんと動いているのに、iOSSnapshotTestCase を用いたテストでだけこの対応が必要なのよくわからないけれど。。。

iOS

UITableView.RowAnimation の .none はアニメーションするよ

UITableView.RowAnimation の .none はアニメーションがnoneなわけじゃなく、デフォルトの設定を使うよという意味らしい。

The inserted or deleted rows use the default animations.

なのでアニメーションしちゃう。今更の気づき。

 

iOS
more