0

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)
}
  • Yeah use `UIScrollView`. BTW what mess does `UICollectionView` created? – dahiya_boy Mar 14 '19 at 12:34
  • could you please post the widget tree setup and complete screen layout. – Rathna Kumaran Mar 14 '19 at 12:34
  • dahiya_boy: Thanks! I'll try that! The UICollectionView renders UITextFields on the rows where I only want to have UILabels. – Eric Groseclos Wikås Mar 14 '19 at 12:37
  • 1
    Likely that you implement the reusing method wrong. – J. Doe Mar 14 '19 at 12:39
  • 2
    Don't try to avoid the Reuse Mecanism. It's an important memory/CPU optmization. Android use a similar system with its CycleView. You have to understand it and use it correctly instead. – Larme Mar 14 '19 at 13:01
  • Showing the code of `CellCreation.make(self, indexPath, self.textfields, cell)` could be a good start to help you to spot the mistakes. – Larme Mar 14 '19 at 13:02
  • 1
    Look for writing better code rather than looking for better UI – vadian Mar 14 '19 at 13:21
  • Calling `addSubview()` each time in cellForRow is clearly not a good idea. Why don't you have a Custom UICollectionViewCell with already the UITextField, another one with only the UILabel, with all the settings needed, call them accordingly. – Larme Mar 14 '19 at 14:15
  • 1
    According to the code *the reuse function* **doesn't** *mess up the layout*, **you** do. – vadian Mar 14 '19 at 14:37

1 Answers1

4
  • 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

dahiya_boy
  • 9,298
  • 1
  • 30
  • 51