2017-08-01 15 views
1

私のコードでUICollectionViewのインデックスを追跡する変数が必要ですが、動作させることができません。いくつかのトラブルシューティングの後、私はストーリーボードが関係していないので、空のviewControllerに貼り付けると動作するはずです。アニメーションGIFはその問題を示しています。最初は私の変数 "selectedItem"は、data = [0,1,2,3]を反映するUICollectionViewセルのテキストと同じですが、右にスワイプするとすぐに1だけオフになります。次に、再び一致する最後のセルまで1だけオフになります。逆に進むとパターンが繰り返されます。任意の助けてくれてありがとう -UICollectionViewのインデックスを追跡する方法

ViewController

import UIKit 

class CodeCollView2: UIViewController, UICollectionViewDataSource,UICollectionViewDelegate, UICollectionViewDelegateFlowLayout { 
    var data = [0,1,2,3] //["0", "1", "2", "3" ] 
    let cellId = "cellId2" 

    var selectedItem = 0 

    lazy var cView: UICollectionView = { 
    let layout = UICollectionViewFlowLayout() 
    layout.scrollDirection = .horizontal 
    layout.minimumLineSpacing = 0 
    let cv = UICollectionView(frame: .zero, collectionViewLayout: layout) 
    cv.isPagingEnabled = true 
    cv.dataSource = self 
    cv.delegate = self 
    return cv 
    }() 

    var indexLabel: UILabel = { 
    let label = UILabel() 
    label.text = "" 
    label.font = UIFont.systemFont(ofSize: 30) 
    return label 
    }() 

    override func viewDidLoad() { 
    super.viewDidLoad() 
    setupViews() 
    } 

    func setupViews() { 
    cView.register(CCell2.self, forCellWithReuseIdentifier: cellId) 
    view.addSubview(cView) 
    cView.translatesAutoresizingMaskIntoConstraints = false 
    cView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true 
    cView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true 
    cView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true 
    cView.heightAnchor.constraint(equalToConstant: 200).isActive = true 
    view.addSubview(indexLabel) 
    indexLabel.translatesAutoresizingMaskIntoConstraints = false 
    indexLabel.bottomAnchor.constraint(equalTo: cView.topAnchor).isActive = true 
    indexLabel.centerXAnchor.constraint(equalTo: cView.centerXAnchor).isActive = true 
    } 

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 
    return data.count 
    } 

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! CCell2 
    selectedItem = indexPath.item 
    indexLabel.text = "seletedItem = \(selectedItem)" 
    cell.itemValue = data[selectedItem] 
    return cell 
    } 

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 
    return collectionView.frame.size 
    } 
} 

//============== CVCell ================== 
class CCell2: UICollectionViewCell { 

    var itemValue: Int? { 
    didSet { 
     if let val = itemValue { 
     itemLabel.text = "\(val)" 
     } 
    } 
    } 

    var itemLabel: UILabel = { 
    let label = UILabel() 
    label.font = UIFont.systemFont(ofSize: 100) 
    return label 
    }() 

    override init(frame: CGRect) { 
    super.init(frame: frame) 
    backgroundColor = .lightGray 
    addSubview(itemLabel) 
    itemLabel.translatesAutoresizingMaskIntoConstraints = false 
    itemLabel.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true 
    itemLabel.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true 
    } 

    required init?(coder aDecoder: NSCoder) { 
    fatalError("init(coder:) has not been implemented") 
    } 
} 
+0

あなたはcellForItemAt 'に頼ることはできません:'であること特定の順序で呼び出されます。 collectionViewが一度に1つのアイテムしか表示しないようにサイズが設定されている場合は、おそらくデリゲートメソッド 'willDisplay:forItemAt:'を使用して、オンスクリーンアイテムを追跡できます。 – Paulw11

+0

私が間違っている場合は私を修正しますが、CollectionViewは、スクロール時に提案されたオフセットを与えるという点でスマートではないと考えられています。同時に 'willDisplay:forItemAt'を使うだけでは、複数の項目が同時に画面に現れることはありません。スクロールが発生した後、そのオフセットから現在のインデックスを推測することができます。 –

答えて

0

cellForItemAtは、セルが示されようとしているときに呼び出され、どのセルが中心にあるかを決めるのに使うべきではありません。

scrollViewDidScroll

は、あなたが中心に持っているセルのトラッキングの正しい方法で、あなたはこのようなものでオンになっているものをインデックス印刷することができます。

func scrollViewDidScroll(_ scrollView:UIScrollView) 
    { 
     let midX:CGFloat = scrollView.bounds.midX 
     let midY:CGFloat = scrollView.bounds.midY 
     let point:CGPoint = CGPoint(x:midX, y:midY) 

     guard 

      let indexPath:IndexPath = collectionView.indexPathForItem(at:point) 

     else 
     { 
      return 
     } 

     let currentPage:Int = indexPath.item 
     indexLabel.text = "seletedItem = \(currentPage)" 
    } 
+0

私はNikita Gaydukovのソリューションのコメントは、scrollViewDidScrollが1回のスクロール移動ごとに発生するため、ソリューションにも当てはまると思います。それは私がインデックスをスクロールを停止するときに知りたいと思っているので、CPUの無駄であるように思えます。だからこそ私は scrollViewDidEndDeceleratingが良い解決策であると仮定している。何か不足していますか? –

+0

実際にスクロールが終了したときのインデックスを知りたいだけなので、scrollViewDidEndDeceleratingはその目的を果たします。 – zero

1

「cellForItemAt」で選択した項目の追跡は良いアイデアではありません。私はUIScrollViewDelegateのscrollViewDidScrollデリゲートメソッドでそれを追跡することをお勧めします。このような 何か作業をする必要があります:あなたはそれだけのビットを参照し、前のものに戻っても、

ニキータGaydukovが真であると言う何
func scrollViewDidScroll(_ scrollView: UIScrollView) { 
    let currentPage = cView.contentOffset.x/self.view.bounds.width 
} 
+0

テキスト表示(indexLabel.text = "seletedItem = \(selectedItem)")を更新する行を含めたscrollViewDidScrollを使用したとき、ページ間でスクロールするとテキストが大きく表示されました。これにより、スクロールが完了するたびに正しい答えが得られたにもかかわらず、あなたのアプローチがCPUに非常に「高価」だったと結論づけました。代わりに私はscrollViewDidEndDeceleratingを使ってみました。そして、私がしたとき、私は各スクロールの終わりにテキスト表示を見ただけでした。私はscrollViewDidEndDeceleratingがより良い解決策であると仮定して正しいですか? –

+0

@TonyMそれはあまりCPU高価ではありません。しかし、scrollViewDidEndDeceleratingよりもスクロールが完了した後にのみ、選択した項目を知る必要がある場合は、移動する方法です。私はどのような場合でもうまくいく一般的な解決方法を提供しました(たとえば、選択したアイテムの一部、カスタムスクロールエフェクトなどを知りたければ)。 –

関連する問題