Starkus
Starkus

Reputation: 157

Get indexPath from UICollectionViewController to UICollectionViewCell subclass

in my view controller I Am loading a custom CollectionViewCell with subclass. Based on the position of a cell's indexpath I want to format the text labels differently. I.e. first row has only one cell with bigger text, whereas the second has two cell with smaller text.

How can I access the indexpath from my UICollectionView in my UICollectionViewCell subclass? I tried a delegate protocol but this always returns nil.

Code below and Thanks so much!

Markus

UICollectionViewController:

import UIKit

protocol WorkoutDataViewControllerCVDataSource: AnyObject {

func workoutType(for workoutDataViewControllerCV: WorkoutDataViewControllerCV) -> WorkoutType
func workoutDistance(for workoutDataViewControllerCV: WorkoutDataViewControllerCV) -> Double
func workoutDuration(for workoutDataViewControllerCV: WorkoutDataViewControllerCV) -> Double
func workoutInstantVelocity(for workoutDataViewControllerCV: WorkoutDataViewControllerCV) -> Double
}

final class WorkoutDataViewControllerCV: UIViewController {

@IBOutlet weak var collectionView: UICollectionView!

weak var dataSource: WorkoutDataViewControllerCVDataSource!

private lazy var velocityFormatter = VelocityFormatter(dataSource: self, delegate: self)
private lazy var averageVelocityFormatter = VelocityFormatter(dataSource: self, delegate: self)

override func viewDidLoad() {
    super.viewDidLoad()
    self.collectionView.register(MeasurementCollectionViewCell.preferredNib, forCellWithReuseIdentifier: MeasurementCollectionViewCell.preferredReuseIdentifier)
}

 }

   // MARK: - Managing UICollectionView

 extension WorkoutDataViewControllerCV: UICollectionViewDataSource {

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

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Measurement Cell", for: indexPath)
    return cell
     }

    }

   extension WorkoutDataViewControllerCV: UICollectionViewDelegateFlowLayout {

func collectionView(_ collectionView: UICollectionView,
                    layout collectionViewLayout: UICollectionViewLayout,
                    sizeForItemAt indexPath: IndexPath) -> CGSize {

    let availableWidth = self.view.frame.width

    switch indexPath.row {
    case 0: return CGSize(width: availableWidth, height: 150)
    case 1: return CGSize(width: availableWidth/2.1, height: 150)
    case 2: return CGSize(width: availableWidth/2.1, height: 150)
    case 3: return CGSize(width: availableWidth, height: 150)
    default:
        return CGSize(width: availableWidth/2.1, height: 150)
    }
}
}

   // MARK: - Managing VelocityFormatter

   extension WorkoutDataViewControllerCV: VelocityFormatterDataSource     {

func duration(for velocityFormatter: VelocityFormatter) -> Double {
    return dataSource.workoutDuration(for: self)
}

func distance(for velocityFormatter: VelocityFormatter) -> Double {
    return dataSource.workoutDistance(for: self)
}

func instantVelocity(for velocityFormatter: VelocityFormatter) -> Double {
    return dataSource.workoutInstantVelocity(for: self)
}
}

UICollectionViewCell.swift

    import UIKit

  final class MeasurementCollectionViewCell: UICollectionViewCell {

@IBOutlet private var measurementPropertyLabel: UILabel!
@IBOutlet private var measurementValueLabel: UILabel!
@IBOutlet private var measurementUnitLabel: UILabel!

static let preferredReuseIdentifier = "Measurement Cell"
static let preferredNib = UINib(nibName: "MeasurementCollectionViewCell", bundle: nil)

override func awakeFromNib() {
    super.awakeFromNib()
    updateMeasurement(property: "Speed", value: "100", unit: "km/h")

    //measurementValueLabel.font = measurementValueLabel.font.monospacedDigitFont
}

func updateMeasurement(property: String, value: String, unit: String?) {
    measurementPropertyLabel.text = property
    measurementValueLabel.text = value
    measurementUnitLabel.text = unit
       }

    }

Upvotes: 2

Views: 2290

Answers (3)

Ratul Sharker
Ratul Sharker

Reputation: 8013

Pretty straight forward way would be storing the indexPath into the subclass of UICollectionViewCell class. Assign it while returning from cellForRow at: index path. So now the subclassed collectionviewcell has access to the indexpath of it's own

Upvotes: 1

Sateesh Yemireddi
Sateesh Yemireddi

Reputation: 4399

Get the instance of cell in UICollectionView delegate method collectionView(_, didSelectItemAt _).

extension WorkoutDataViewControllerCV: UICollectionViewDelegate {

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

        if let cell = collectionView.cellForItem(at: indexPath) as? MeasurementCollectionViewCell {
            cell.selectedIndexPath(indexPath)
        }
    }
}

The indexPath will be passed as an argument in method selectedIndexPath to MeasurementCollectionViewCell from above method.

class MeasurementCollectionViewCell: UICollectionViewCell {

    ......
    func selectedIndexPath(_ indexPath: IndexPath) {

        //Do your business here.
    }
}

Upvotes: 2

Callam
Callam

Reputation: 11539

You can use the responder chain to get the collection view of a cell with which you can get the index path. Just add these extensions in a new file called UICollectionViewCell+IndexPath.swift.

extension UIResponder {
    func next<T: UIResponder>(_ type: T.Type) -> T? {
        return next as? T ?? next?.next(type)
    }
}

extension UICollectionViewCell {
    var indexPath: IndexPath? {
        return next(UICollectionView.self)?.indexPath(for: self)
    }
}

Now inside your cell, you can use self.indexPath

Upvotes: 1

Related Questions