2017-08-01 1 views
1

Ich möchte eine Variable in meinem Code, der den Index meiner UICollectionView verfolgt, aber ich kann es nicht zum Funktionieren bringen. Nach einiger Fehlersuche habe ich den Code auf den folgenden Code reduziert, der, wenn er in einen leeren viewController eingefügt wird, funktionieren sollte, da kein Storyboard involviert ist. Das animierte gif veranschaulicht das Problem. Anfangs ist meine Variable "selectedItem" gleich dem UICollectionView-Zelle-Text, der die data = [0,1,2,3] widerspiegelt, aber wenn ich dann nach rechts wische, wird sie sofort um 1. Dann bleibt sie um 1 bis zur letzten Zelle, wo sie wieder übereinstimmt. Das Muster wiederholt sich beim Rückwärtsfahren. Vielen Dank für jede Hilfe -Wie UICollectionView-Index verfolgen

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

Sie können nicht auf 'cellForItemAt verlassen:' Sein in einer bestimmten Reihenfolge aufgerufen. Wenn Ihr collectionView so dimensioniert ist, dass immer nur ein einzelnes Objekt angezeigt wird, können Sie wahrscheinlich die Delegate-Methode 'willDisplay: forItemAt:' verwenden, um das Element auf dem Bildschirm zu verfolgen. – Paulw11

+0

Korrigieren Sie mich, wenn ich falsch liege, aber CollectionView ist nicht so intelligent, weil es beim Scrollen einen vorgeschlagenen Versatz gibt. Die Verwendung von 'willDisplay: forItemAt' reicht nicht aus, wenn mehrere Objekte gleichzeitig auf dem Bildschirm erscheinen können. Sie könnten wahrscheinlich den aktuellen Index von diesem Offset ableiten, nachdem ein Scroll aufgetreten ist. –

Antwort

0

Was Nikita Gaydukov sagt wahr ist, cellForItemAt wird aufgerufen, wenn eine Zelle gezeigt werden wird, auch wenn Sie nur ein wenig davon sehen und zum vorherigen zurückgehen, also solltest du nicht entscheiden, welche Zelle im Mittelpunkt steht.

scrollViewDidScroll ist die richtige Art und Weise der Verfolgung, die Zelle, die Sie in der Mitte haben, und Sie können drucken, was Index Sie mit so etwas wie dieses sind:

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

Ich glaube, dass die Kommentare, die ich für die Lösung von Nikita Gaydukov gemacht habe, auch auf Ihre Lösung zutreffen, da scrollViewDidScroll bei jeder einzelnen Scroll-Bewegung ausgelöst wird. Das scheint mir eine Verschwendung der CPU zu sein, da ich nur den Index wissen möchte, wenn er aufhört zu scrollen. Deshalb nehme ich an scrollViewDidEndDecelerating ist die bessere Lösung. Fehle ich etwas? –

+0

Ich habe den Teil vermisst, den Sie nur wissen wollen, auf welchem ​​Index Sie sich befinden, wenn er tatsächlich das Scrollen beendet, also wird scrollViewDidEndDecelerating diesen Zweck erfüllen. – zero

1

Verfolgung des ausgewählten Elements in der 'cellForItemAt' ist keine gute Idee. Ich würde vorschlagen, dass Sie es in der Delegiertenmethode scrollViewDidScroll des UIScrollViewDelegate verfolgen. So etwas sollte funktionieren:

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

Wenn ich scrollViewDidScroll (wie Sie vorgeschlagen haben) einschließlich der Zeile, die die Textanzeige aktualisiert (indexLabel.text = "seletedItem = \ (selectedItem)"), den Text wild angezeigt, wie ich von Seite zu Seite gescrollt. Dies führte mich zu der Schlussfolgerung, dass Ihre Vorgehensweise CPU-mäßig sehr "teuer" war, obwohl sie jedes Mal, wenn die Scroll beendet wurde, die richtige Antwort gab. Also habe ich es stattdessen mit scrollViewDidEndDecelerating versucht und als ich das gemacht habe, habe ich nur die Textanzeige am Ende jedes Scrolls gesehen. Habe ich richtig angenommen, dass scrollViewDidEndDecelerating eine bessere Lösung ist? –

+0

@TonyM es ist nicht sehr CPU teuer. Aber wenn Sie das ausgewählte Element NUR nachdem Scroll beendet ist als scrollViewDidEndDecelerating ist der Weg zu gehen. Ich habe eine allgemeine Lösung zur Verfügung gestellt, die in jedem Fall funktionieren wird (zum Beispiel, wenn Sie einen "Bruchteil" des ausgewählten Elements, für einen benutzerdefinierten Scroll-Effekt oder etwas Ähnliches wissen möchten). –