I'm a beginning iOS developer and I've been stuck on an issue for quite some time now.
Background: I have a single viewcontroller with a tableview. It holds 4 dynamic prototype cells: prototypecell 1 has an UITextField and a couple of labels, prototypecell 2 has an image, prototypecell 3 and 4 only have a single label. (Note: prototype cell 4 is based on an array in the object and can have 1 or more cells)
Upon opening the screen, only the first cell with the UITextField should be visible. When an user enters a number (max. 3 digits) in this UITextField, the number has to be compared to check if it is an existing number in an array of objects. If this number proves correct, 2 things will have to happen:
- The labels in the first cell will need to change layout (colour, font, size, ...). And the first cell changes rowHeight.
- The other 3 cells will have to appear and show data that corresponds to the number.
If the number is wrong, no extra cells appear and the first cell updates to tell the user it was incorrect.
Problem: I'm having issues with animating the way the 3 cells appear and disappear based on the numberinput in the first cell. More specifically the method "toggleCellsVisibility" and how it relates to the creation of the cells.
I tried multiple combinations of: beginUpdates(), endUpdates(), reloadData(), reloadRowsAtIndexPath(), DispatchQueue.main.async {} and UIView.Animate(). But the only way my code works is if I implement the toggleCellsVisibility method as below, which does not give me any animation options.
I seem to be doing something wrong with the creation and reloading of the data. I think this is related to the use of the global variables indexOfObject and tempObject which hold dummy data when the screen is loaded, and are then shown and overridden with the actual data when a correct number is given.
Summary: Could I somehow insert/create the 3 cells when a correct number is inputted in the first cell, while also animating this change with this particular setup?
Additional: Can I show/hide the extra cells by not using the heightForRowAtIndexPath? It would be better if the cells could self-size.
I selected and simplified the relevant pieces of code:
1) TableView Class:
class TableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, updateUITableView {
var objects: [Object] = [
name: "test1", number: "test2", image: "imageName1", color: UIColor.black, array: ["test3", "test4"],
[name: "test1", number: "test2", image: "imageName2", color: UIColor.white, array: ["test3", "test4", "test5"]
//...
]
var cellsRowHeight = false
var indexOfObject: Int = 0
var tempObject = Object[name: "test1", number: "test2", color: UIColor.blue, image: "imageName3", array: ["test3"]]
@IBOutlet var tableView: UITableView!
// MARK: - Table view data source
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let numberOfRows = 3 + tempObject.subArray.count
return numberOfRows
}
//START - Input received from textfield in first cell
func checkNumberInput(senderCell: SearchInputTableViewCell, number: String) {
// step 1: Check if number exists
let match = checkNumberMatch(numberInput: number)
// step 2: Change lay-out of input cell
senderCell.changeLayout(numberMatch: match.0, name: match.1?.name, color: match.1?.color)
// step 3: Show/hide cells
toggleCellsVisibility(numberMatch: match.0)
}
//STEP 1: Check if the number corresponds to an existing number
func checkNumberMatch(numberInput: String) -> (Bool, Object?) {
var returnObject: Object?
var tuple = (false, returnObject)
for (index, object) in objects.enumerated() where object.number == numberInput {
returnObject = object
tuple = (true, returnObject)
tempObject = object
indexOfObject = index
}
return tuple
}
//STEP 3 = !!PROBLEM!!
func toggleCellsVisibility(numberMatch: Bool) {
cellsRowHeight = numberMatch
// if/else because of additional future implementation
if numberMatch == true { //Cells appear
DispatchQueue.main.async {
self.tableView?.reloadData()
}
} else { //Cells disappear
DispatchQueue.main.async {
self.tableView?.reloadData()
}
}
}
//STEP 4:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
switch (indexPath.row) {
case 0 where !cellsRowHeight:
return 187
case 0 where cellsRowHeight:
return 170
case 1 where !cellsRowHeight:
return 0
case 1 where cellsRowHeight:
return 170
case 2 where !cellsRowHeight:
return 0
case 2 where cellsRowHeight:
return 54
case 3...200 where !cellsRowHeight:
return 0
case 3...200 where cellsRowHeight:
return 44
default:
return 44
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cellIdentifier = "InputCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! InputCell
cell.delegate = self
return cell
}
else if indexPath.row == 1 {
let cellIdentifier = "Cell2"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! Cell2
cell.image?.image = UIImage(named: objects[indexOfObject].image)
return cell
}
else if indexPath.row == 2 {
let cellIdentifier = "Cell3"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! Cell3
cell.name?.text = objects[indexOfObject].name
return cell
}
else {
let cellIdentifier = "Cell4"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! Cell4
cell.name?.text = objects[indexOfObject].subArray[(indexPath as IndexPath).row - 3]
return cell
}
}}
2) Cell Class:
protocol updateUITableView: class {
func checkNumberInput(senderCell: SearchInputTableViewCell, number: String)
}
class InputCell: UITableViewCell, UITextFieldDelegate {
var delegate: updateUITableView?
@IBOutlet var textField: UITextField!
@IBOutlet var nameLabel: UILabel!
//... and other labels
func changeLayout(numberMatch: Bool, name: String?, color: UIColor?) {
if numberMatch == true {
//lay-out changes to labels
}
else {
//lay-out changes to labels
}
}
//Set maximum character limit in textField and dismiss keyboard when character limit (3) is reached.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let currentCharacterCount = textField.text?.characters.count ?? 0
let newLength = currentCharacterCount + string.characters.count - range.length
if (newLength == 3) {
textField.text = (textField.text! as NSString).replacingCharacters(in: range, with: string)
//Get text from textField
let numberInput: String? = textField.text! //Get number if 3 characters entered
if numberInput != nil {
delegate?.checkNumberInput(senderCell: self, number: numberInput!)
}
textField.resignFirstResponder()
if (range.length + range.location > currentCharacterCount) {
return false
} else {
return true
}
}
return true
}
}
class Cell2: UITableViewCell {
@IBOutlet var image: UIImageView!
}
class Cell3: UITableViewCell {
@IBOutlet var name: UILabel!
}
class Cell4: UITableViewCell {
@IBOutlet var name: UILabel!
}
Any help would be immensely appreciated! Thank you!