KAinone
@KAinone

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

UICollectionView 無限スクロール

Q&A

Closed

解決したいこと

こちらのサイトを参考にカルーセルを作っています。サイトの中盤にisInfinity定数が定義されています。サイト内にある下記のコードのように、isInfinity というBool型の定数だけでなぜ無限スクロールのオンオフができるのか理解ができません。どういった仕組みなのか教えてほしいです。よろしくお願いします。


let isInfinity = true

引用元

import UIKit

class CarouselView: UICollectionView {

    let cellIdentifier = "carousel"
    let pageCount = 5
    let colors:[UIColor] = [.blue,.yellow,.red,.green,.gray]
    let isInfinity = true
    var cellItemsWidth: CGFloat = 0.0
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
        super.init(frame: frame, collectionViewLayout: layout)
        self.delegate = self
        self.dataSource = self
        self.register(CarouselCell.self, forCellWithReuseIdentifier: cellIdentifier)
    }
    
    convenience init(frame: CGRect) {
        let layout = UICollectionViewFlowLayout()
        layout.itemSize = CGSize(width: 200, height: frame.height / 2)
        layout.scrollDirection = .horizontal
        
        self.init(frame: frame, collectionViewLayout: layout)
        
        // 水平方向のスクロールバーを非表示にする
        self.showsHorizontalScrollIndicator = false
        self.backgroundColor = UIColor.white
    }
    
}

extension CarouselView: UICollectionViewDelegate {
    
}

extension CarouselView: UICollectionViewDataSource {
    
    // セクションごとのセル数
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return isInfinity ? pageCount * 3 : pageCount
    }
    
    // セルの設定
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell:CarouselCell = dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as! CarouselCell
        
        configureCell(cell: cell, indexPath: indexPath)
        
        return cell
    }
    
    func configureCell(cell: CarouselCell,indexPath: IndexPath) {
        // indexを修正する
        let fixedIndex = isInfinity ? indexPath.row % pageCount : indexPath.row
        cell.contentView.backgroundColor = colors[fixedIndex]
    }
    
}

extension CarouselView: UIScrollViewDelegate {
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        
        if isInfinity {
            if cellItemsWidth == 0.0 {
                cellItemsWidth = floor(scrollView.contentSize.width / 3.0) // 表示したい要素群のwidthを計算
            }
            
            if (scrollView.contentOffset.x <= 0.0) || (scrollView.contentOffset.x > cellItemsWidth * 2.0) { // スクロールした位置がしきい値を超えたら中央に戻す
                scrollView.contentOffset.x = cellItemsWidth
            }
        }
    }
    
}
0

3Answer

コメントの追記を試してみました。

//もしフラグがtrueの場合
if isInfinity {
    //且つ表示したい要素群のwidthはまだ計算していない場合
    if cellItemsWidth == 0.0 {
// 表示したい要素群のwidthを計算
        cellItemsWidth = floor(scrollView.contentSize.width / 3.0) 
    }
    //scrollViewのスクロールの横位置は一番左、あるいは要素群の幅の2倍より超える場合、
    if (scrollView.contentOffset.x <= 0.0) || (scrollView.contentOffset.x > cellItemsWidth * 2.0) { 
// スクロールした位置がしきい値を超えたら中央に戻す
        scrollView.contentOffset.x = cellItemsWidth
    }
}
0Like

Comments

  1. @KAinone

    Questioner

    わかりやすくご説明いただきありがとうございます。
    どういった処理がされているかはわかったのですが、なぜ true の要素しか持っていない isinfinty でisInfinity ? pageCount * 3 : pageCountif isInfinityのような条件式が成り立つのかがわかりません。初心者の私には何がtrueだった場合なのだろう?と思ってしまいます。上手く言語化できず恐縮ですが、その点を詳しく教えていただけないでしょうか?

  2. //UICollectionViewのデータリソース(サブクラスかと思う)
    extension CarouselView: UICollectionViewDataSource {
        // ページ何行を戻す
        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            //フラグfalseの場合、ページ5行。
            //フラグtrueの場合、ページ15行。
            return isInfinity ? pageCount * 3 : pageCount
        }
        // セルの設定
        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell:CarouselCell = dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as! CarouselCell
            configureCell(cell: cell, indexPath: indexPath)
            return cell
        }
        func configureCell(cell: CarouselCell,indexPath: IndexPath) {
            // indexを修正する
            //フラグfalseの場合、行ごとに色を設定する[.blue,.yellow,.red,.green,.gray]
            //つまり1ページ固定5行でそれぞれ自分の色があるとの意味だと思う。
            //フラグtrueの場合、5の剰余で色を設定する。
            //つまり1ページ固定15行、3回分で色を設定するでしょう。
            let fixedIndex = isInfinity ? indexPath.row % pageCount : indexPath.row
            cell.contentView.backgroundColor = colors[fixedIndex]
        }
        
    }
    

    image.png
    なぜpageCount * 3というと、5行のままだったら、スクロールがでないからだと思います。

  3. @KAinone

    Questioner

    ご丁寧に解説していただき感謝いたします。
    より一層理解が深まりました。

    回答と致しましては、@hoshi-takanori 様のコメントより、管理の為だと結論づけました。

    ご協力ありがとうございました!

isInfinity というBool型の定数だけでなぜ無限スクロールのオンオフができるのか理解ができません

コードを見ると、「定数の宣言」だけではなくて、その定数を判定して処理を変えていますね。
その処理の違いにより、振る舞いが変わったということです。

スクロールするたびに、スクロール領域の幅を広げているイメージではないでしょうか。

0Like

Comments

  1. @KAinone

    Questioner

    ご回答いただきありがとうございます。
    if isInfinityといったコードで定数を判定して処理をしていると思いますが、UIと結びつきがない状態で isInfinty の Bool が何をトリガーに変わるのかがわかりません。

    例えば、isInfinity ? pageCount * 3 : pageCountであれば、宣言されている true にしかなり得ないと思うのですが、isInfinity をどう判定すれば false になるのでしょうか。

  2. 宣言されている true にしかなり得ないと思うのですが

    その通りです。
    isInfinity = trueとすれば無限スクロールして、isInfinity = falseとすれば無限でない(有限?)スクロールの処理をします。

  3. @KAinone

    Questioner

    引用元のコードではlet isInfinity = trueと宣言し、後にif isInfintyisInfinity ? pageCount * 3 : pageCountとありますが、trueと決めているにも関わらず定数を判定する構文を使う理由がわからないのですが、何か意味があるのでしょうか?

  4. 先ほど答えた通り、isInfinity = falseと定義した時に、無限でない(有限?)スクロールの処理を行うためです。通じてません?

    プログラムの先頭にある定数let isInfinity = true or falseで、無限スクロールと無限でないスクロールのどちらにも対応するようにしてある、ということです。

  5. @KAinone

    Questioner

    それはわかっています。
    先頭で true or false に定義されているので、isInfinity ? pageCount * 3 : pageCountで pageCount が return されることがないのに、定数の中身を判定する構文はなぜ必要なのか聞きたかったのです。先頭で true を宣言していても何かしらのトリガーに結びついたメソッド内で false に再定義することがある場合を見込んでということでしょうか?

    @nak435 様にはいつも迅速にご回答いただき非常に助かっており感謝しています。
    しかし、私は初心者でわからないことがたくさんあるのでここで有識者の方々にお教えを乞うているのであって、あなたが当たり前だと思っていることでも私に通じないことがあります。なので「通じます?」などといった言葉は慎んでください。また、そのようなことを言わずにいられないような精神状態であれば、人に教える立場としては不適格かと思われますのでご回答いただかなくて結構です。コミュニティガイドラインをもう一度ご確認の上、引き続きご協力よろしくお願いいたします。

  6. 先頭で true を宣言していても何かしらのトリガーに結びついたメソッド内で false に再定義することがある場合を見込んでということでしょうか?

    いいえ、true/falseを設定するのはプログラムの先頭の定数で のみです。

    @KAinone さんに分かってもらいたくて、どういう説明なら分かってもらえるかを確かめたくて、「通じてません?」と尋ねました。そのように受け止められたことは不本意であり大変残念ですが、当Q&Aに対する以降の回答は一切控えることにします。お役に立てずすみませんでした。

let isInfinity = true

これは定数によって挙動を切り替えるプログラミング上のテクニックですね。

参考にした記事では、最初に無限スクロールではない CarouselView クラスを作り、それをもとに無限スクロールするように書き換える、という流れになっています。このようなプログラムの書き換えを行う際、元のコードを直接書き換えてしまうと、書き換えの途中では正常に動かなくなり、元の CarouselView の挙動を確認したくなった時に困ります。

そこで、isInfinity という定数を定義し、値が false なら元の挙動にすることで、いつでもソースを一箇所書き換えるだけで元の挙動に戻せるようにしています。

0Like

Comments

  1. @KAinone

    Questioner

    ご回答ありがとうございます。
    なるほどです!求めていた答えでした!

    つまり、最初から無限スクロールと決めていれば、return pageCount * 3と書いても良くて、return isInfinity ? pageCount * 3 : pageCountにしても最終的にlet isInfinity = trueを定義してしまえばプログラム内でruturn pageCountが呼ばれることはないが、変更を加えることを考慮して一箇所を書き変えればいいようにしておけば管理が楽になるということですね。

    私には思いつかない上級テクニックということで納得です。
    ありがとうございました。

Your answer might help someone💌