2017-08-01 79 views
1

我想在我的代碼中記錄我的UICollectionView的索引的變量,但我無法讓它工作。經過一些故障排除之後,我將代碼歸結爲以下內容,如果將其粘貼到空的viewController中,應該可以工作,因爲不涉及任何故事板。動畫GIF說明了這個問題。最初,我的變量「selectedItem」等於反映data = [0,1,2,3]的UICollectionView單元格文本,但是當我向右滑動時,它立即變爲關閉狀態。然後,它停留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的大小使其一次只顯示一個項目,那麼您可以使用委託方法'willDisplay:forItemAt:'來跟蹤屏幕上的項目。 – Paulw11

+0

糾正我,如果我錯了,但CollectionView不應該是聰明的,因爲它提供了一個建議的偏移量,當你滾動。如果多個項目可以同時出現在屏幕上,只需使用'willDisplay:forItemAt'就不夠了。您可以在滾動發生後從偏移量中推斷當前索引。 –

回答

0

什麼尼基塔Gaydukov說的是真的,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會在每次滾動時都觸發。這在我看來是浪費CPU,因爲我只是想知道索引停止滾動時。這就是爲什麼我假設 scrollViewDidEndDecelerating是更好的解決方案。我錯過了什麼嗎? –

+0

我錯過了只想知道實際結束滾動時的索引的部分,因此,scrollViewDidEndDecelerating會實現該目的。 – zero

1

跟蹤在「cellForItemAt」所選擇的項目是不是一個好主意。我建議你在UIScrollViewDelegate的scrollViewDidScroll委託方法中跟蹤它。 像這樣的東西應該工作:

func scrollViewDidScroll(_ scrollView: UIScrollView) { 
    let currentPage = cView.contentOffset.x/self.view.bounds.width 
} 
+0

當我使用scrollViewDidScroll(如您所建議的),包括更新文本顯示的行(indexLabel.text =「seletedItem = \(selectedItem)」)時,隨着我逐頁滾動顯示,文本顯示得很亂。這使我得出結論,即使它在每次滾動完成時都給出了正確的答案,但您的方法在CPU方面非常「昂貴」。所以相反,我嘗試使用scrollViewDidEndDecelerating,當我做了我只看到每個滾動結束時的文本顯示。我正確地認爲scrollViewDidEndDecelerating是一個更好的解決方案嗎? –

+0

@TonyM它不是很昂貴的CPU。但是,如果在滾動完成後只需要知道所選項目,則只需要知道scrollViewDidEndDecelerating即可。我提供了一個通用的解決方案,可以在任何情況下工作(例如,如果您想知道所選項目的「分數」,某些自定義滾動效果或某物)。 –