Search code examples
iosswiftuicollectionviewtablelayoutuicollectionviewlayout

Is there a way to stop UICollectionView from reusing cells?


I am trying to create a table in Swift 4, which contains both UITextFields and UILabels.

As I have many rows in the UICollectionView, scrolling is required.

However, when I scroll, the reuse function messes up the layout.

Is there another option than UICollectionView? In Android, where I have created a similar app, TableLayout renders all cells and doesn't cause any errors while scrolling.

Desired layout

enter image description here

Layout after scrolling down and then up again

enter image description here

I use this method for reuse:

    override func collectionView(_ collectionView: UICollectionView, 
                                 cellForItemAt indexPath: IndexPath) -> 
                                 UICollectionViewCell {

    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! EUPCell

    cell.row = indexPath.section
    cell.column = indexPath.row
    //print("row: \(cell.row) column: \(cell.column)")
    CellCreation.make(self, indexPath, self.textfields, cell)

    return cell
}

static func make(_ view: EUPViewController,
                 _ indexPath: IndexPath,
                 _ textfields: TextFields,
                 _ cell: EUPCell) {

    let row = indexPath.section
    let column = indexPath.row

    if (row == 0 && column == 0) {
        CellCreation.makeLabel("Datum", cell)
    } else if (row == 0 && column == 1) {
        CellCreation.makeLabel("Skift", cell)
    } else if (row == 0 && column == 2) {
        CellCreation.makeLabel("EUP Operatör", cell)
    } else if (row == 0 && column == 3) {
        CellCreation.makeLabel("Kund", cell)
    } else if (row == 0 && column == 4) {
        CellCreation.makeLabel("Kontaktperson", cell)
    } else if (row == 0 && column == 5) {
        CellCreation.makeLabel("Artikel", cell)
    } else if (row == 0 && column == 6) {
        CellCreation.makeLabel("Plats", cell)
    } else if (row == 1 && (column >= 0 && column <= 6)) {
        CellCreation.makeInput(view, cell, textfields, false)
    } else if (row == 2 && column == 0) {
        CellCreation.makeLabel("Artikel nr", cell)
    } else if (row == 2 && column == 1) {
        CellCreation.makeLabel("Kolli nr", cell)
    } else if (row == 2 && column == 2) {
        CellCreation.makeLabel("FS nr", cell)
    } else if (row == 2 && column == 3) {
        CellCreation.makeLabel("Övrigt", cell)
    } else if (row == 2 && column == 4) {
        CellCreation.makeLabel("Antal i pall", cell)
    } else if (row == 2 && column == 5) {
        CellCreation.makeLabel("Antal OK", cell)
    } else if (row == 2 && column == 6) {
        CellCreation.makeLabel("Antal NOK", cell)
    } else if (row == 2 && column == 7) {
        CellCreation.makeLabel("Åtgärdade", cell)
    } else if (row == 2 && column == 8) {
        CellCreation.makeLabel("Utsorterade", cell)
    } else if ((row >= 3 && row <= 32) && (column >= 0 && column <= 8)) {
        columnCheck(view, column, cell, textfields)
    }
}

/**
 Uses a numeric keyboard for all UITextFields, except for those in
 the "Övrigt" column. This is equivalent to the fourth column
 of the table.
 */
static func columnCheck(_ view: EUPViewController,
                        _ column: Int,
                        _ cell: EUPCell,
                        _ textfields: TextFields) {

    if (column == 3) {
        CellCreation.makeInput(view, cell, textfields, false)
    } else {
        CellCreation.makeInput(view, cell, textfields, true)
    }
}

/**
 Creates an UILabel in the Cell. The font size
 is larger on an iPad.
 */
static func makeLabel(_ text: String, _ cell: EUPCell) {
    let label: UILabel
    let desiredFontSize: CGFloat

    label = UILabel(frame: CGRect(x: 0, y: 0, width: cell.frame.width, height: cell.frame.height))

    if (UIDevice.modelName.contains("iPhone")) {
        desiredFontSize = 8.0
    } else {
        desiredFontSize = 13.0
    }

    let font = UIFont(name: desiredFont, size: desiredFontSize)
    label.font = font

    label.textAlignment = .center

    label.text = text
    cell.addSubview(label)
}

/**
 Creates an UITextField in the given Cell.
 */
static func makeInput(_ view: EUPViewController,
                      _ cell: EUPCell,
                      _ textfields: TextFields,
                      _ isNumeric: Bool) {

    let textField = UITextField(frame: CGRect(x: 0, y: 0, width: cell.frame.width, height: cell.frame.height))

    let desiredFontSize: CGFloat

    if (UIDevice.modelName.contains("iPad")) {
        desiredFontSize = 14.0
    } else {
        desiredFontSize = 13.0
    }

    let font = UIFont(name: desiredFont, size: desiredFontSize)
    textField.font = font

    textField.delegate = view

    textField.borderStyle = .roundedRect
    textField.autocorrectionType = .no
    textField.textAlignment = .left

    textField.contentVerticalAlignment = .center

    if (isNumeric) {
        textField.keyboardType = .asciiCapableNumberPad
    } else {
        textField.keyboardType = UIKeyboardType.default
    }

    textfields.add(textField)

    cell.addSubview(textField)
}

Solution

    • To avoid this mess you need to use two different types of cell.

      1. For Label as a header.
      2. For TextField as an input.
    • Now you need to return 2 in numberOfSectionsInCollectionView

    enter image description here

    • Now set your cell size in sizeForItem. Make sure you added proper conditions for each cell.

    • In cellForItemAt,

      if indexPath.section == 0  {
          if indexPath.row == 0  {
          // dequeue label Cell
          }
          // dequeue Textfield Cell            
      }
      else {
          if indexPath.row == 0  {
          // dequeue label Cell
          }
          // dequeue Textfield Cell    
      }
      

    Note: To properly manage the data of textfield you need to do this -> Swift UICollectionView Textfields