i am facing a very strange bug in my tableView. When changing the row height, the content gets mixed up very strangely (see the GIF).
Here is the code for my tableView, I think I have all labels, imageViews etc. properly resetted at the beginning. I added the Task @ MainAnchor method but it didnt change anything.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "detailVerbindungTableViewCell", for: indexPath) as! detailVerbindungTableViewCell
Task { @MainActor in
for view in cell.contentView.subviews {
if let label = view as? LineHalfTriangleView {
label.removeFromSuperview()
}
}
for view in cell.sideLineView.subviews {
cell.sideLineView.willRemoveSubview(view)
}
var arrayIndex = indexPath.row / 2
print(arrayIndex)
let middleSeperator = UIView(frame: CGRect(x: 0, y: cell.contentView.frame.height / 2, width: cell.contentView.frame.width, height: 1))
middleSeperator.backgroundColor = UIColor.systemBlue
var sideLineType = "end"
let timeFormatHHMM = DateFormatter()
timeFormatHHMM.timeStyle = .short
var sideColor = UIColor.clear
var sideTopColor = UIColor.clear
var sideBottomColor = UIColor.clear
cell.devLabel.text = "(arrayIndex)"
cell.devLabel.isHidden = !UserDefaults.standard.bool(forKey: "devDetailVerbIndex")
cell.lineNumberLabel.text = ""
cell.lineNumberLabel.textColor = .label
cell.lineNumberLabel.backgroundColor = .clear
cell.destinationLabel.text = ""
cell.timeBottomLabel.text = ""
cell.timeMiddleLabel.text = ""
cell.timeTopLabel.text = ""
cell.constDestToNumber.constant = 8
cell.constDestToStrich.constant = 58
cell.constDestToNumber.isActive = true
cell.destinationLabel.font = UIFont.systemFont(ofSize: cell.destinationLabel.font.pointSize)
cell.timeTopLabel.textColor = UIColor.label
cell.timeMiddleLabel.textColor = UIColor.label
cell.timeBottomLabel.textColor = UIColor.label
if indexPath.row % 2 == 0 {
//Location cell
cell.contentView.backgroundColor = UIColor.systemBackground
cell.constDestToNumber.isActive = false
cell.constDestToStrich.constant = 8
cell.destinationLabel.font = UIFont.systemFont(ofSize: cell.destinationLabel.font.pointSize, weight: .semibold)
if arrayIndex == resultLegArray[selectedIndex][0].count {
cell.destinationLabel.text = resultLegArray[selectedIndex][0].last?.arrival.name
} else {
cell.destinationLabel.text = resultLegArray[selectedIndex][0][arrayIndex].departure.name
}
if arrayIndex == resultLegArray[selectedIndex][0].count {
//Location cell
//Show Time
//Last cell
sideLineType = "end"
cell.timeTopLabel.isHidden = true
cell.timeMiddleLabel.isHidden = false
cell.timeBottomLabel.isHidden = true
if resultLegArray[selectedIndex][0].last is PublicLeg {
print("PublicLeg")
var tempPublicLeg = resultLegArray[selectedIndex][0].last as! PublicLeg
if tempPublicLeg.arrivalTime == tempPublicLeg.plannedArrivalTime {
cell.timeMiddleLabel.text = timeFormatHHMM.string(from: tempPublicLeg.plannedArrivalTime)
cell.timeMiddleLabel.textColor = UIColor.systemGreen
} else {
let timeDifference = tempPublicLeg.plannedArrivalTime.distance(to: tempPublicLeg.arrivalTime )
cell.timeMiddleLabel.text = timeFormatHHMM.string(from: tempPublicLeg.arrivalTime)
cell.timeMiddleLabel.textColor = UIColor.systemRed
if timeDifference.stringFromTimeIntervalOnlyNumber().contains("-") == true {
cell.timeMiddleLabel.textColor = UIColor.systemBlue
}
}
sideColor = UIColor(argb: tempPublicLeg.line.style.backgroundColor)
} else {
print("IndividualLeg")
var tempIndLeg = resultLegArray[selectedIndex][0].last as! IndividualLeg
cell.timeMiddleLabel.text = timeFormatHHMM.string(from: tempIndLeg.arrivalTime)
cell.timeMiddleLabel.textColor = UIColor.label
sideColor = UIColor.lightGray
}
} else {
//Not last cell
//Location cell
//Show Time
if arrayIndex == 0 {
sideLineType = "start"
cell.timeTopLabel.isHidden = true
cell.timeMiddleLabel.isHidden = false
cell.timeBottomLabel.isHidden = true
if resultLegArray[selectedIndex][0].first?.departureTime == resultLegArray[selectedIndex][0].first?.plannedDepartureTime {
cell.timeMiddleLabel.text = timeFormatHHMM.string(from: resultLegArray[selectedIndex][0].first!.plannedDepartureTime)
cell.timeMiddleLabel.textColor = UIColor.systemGreen
} else {
let timeDifference = resultLegArray[selectedIndex][0].first?.plannedDepartureTime.distance(to: (resultLegArray[selectedIndex][0].first?.departureTime ?? resultLegArray[selectedIndex][0].first?.plannedDepartureTime)!)
cell.timeMiddleLabel.text = timeFormatHHMM.string(from: resultLegArray[selectedIndex][0].first!.departureTime)
cell.timeMiddleLabel.textColor = UIColor.systemRed
if timeDifference?.stringFromTimeIntervalOnlyNumber().contains("-") == true {
cell.timeMiddleLabel.textColor = UIColor.systemBlue
}
}
//MARK: Location middle Side Color
if resultLegArray[selectedIndex][0][arrayIndex] is PublicLeg {
print("PublicLeg")
var tempPublicLeg = resultLegArray[selectedIndex][0][arrayIndex] as! PublicLeg
sideColor = UIColor(argb: tempPublicLeg.line.style.backgroundColor)
} else {
print("IndividualLeg")
var tempIndLeg = resultLegArray[selectedIndex][0][arrayIndex] as! IndividualLeg
sideColor = UIColor.lightGray
}
} else {//MARK: sideLineType Middle
sideLineType = "middle"
cell.timeTopLabel.isHidden = false
cell.timeMiddleLabel.isHidden = true
cell.timeBottomLabel.isHidden = false
if resultLegArray[selectedIndex][0][arrayIndex].departureTime == resultLegArray[selectedIndex][0][arrayIndex].plannedDepartureTime {
cell.timeBottomLabel.text = timeFormatHHMM.string(from: resultLegArray[selectedIndex][0][arrayIndex].departureTime)
cell.timeBottomLabel.textColor = UIColor.systemGreen
} else {
let timeDifference = resultLegArray[selectedIndex][0][arrayIndex].plannedDepartureTime.distance(to: resultLegArray[selectedIndex][0][arrayIndex].departureTime )
cell.timeBottomLabel.text = timeFormatHHMM.string(from: resultLegArray[selectedIndex][0][arrayIndex].departureTime)
cell.timeBottomLabel.textColor = UIColor.systemRed
if timeDifference.stringFromTimeIntervalOnlyNumber().contains("-") == true {
cell.timeBottomLabel.textColor = UIColor.systemBlue
}
}
if resultLegArray[selectedIndex][0][arrayIndex-1].arrivalTime == resultLegArray[selectedIndex][0][arrayIndex-1].plannedArrivalTime {
cell.timeTopLabel.text = timeFormatHHMM.string(from: resultLegArray[selectedIndex][0][arrayIndex-1].arrivalTime)
cell.timeTopLabel.textColor = UIColor.systemGreen
} else {
let timeDifference = resultLegArray[selectedIndex][0][arrayIndex-1].plannedArrivalTime.distance(to: resultLegArray[selectedIndex][0][arrayIndex-1].arrivalTime )
cell.timeTopLabel.text = timeFormatHHMM.string(from: resultLegArray[selectedIndex][0][arrayIndex-1].arrivalTime)
cell.timeTopLabel.textColor = UIColor.systemRed
if timeDifference.stringFromTimeIntervalOnlyNumber().contains("-") == true {
cell.timeTopLabel.textColor = UIColor.systemBlue
}
}
if resultLegArray[selectedIndex][0][arrayIndex] is PublicLeg {
print("PublicLeg")
var tempPublicLeg = resultLegArray[selectedIndex][0][arrayIndex] as! PublicLeg
sideBottomColor = UIColor(argb: tempPublicLeg.line.style.backgroundColor)
} else {
print("IndividualLeg")
var tempIndLeg = resultLegArray[selectedIndex][0][arrayIndex] as! IndividualLeg
sideBottomColor = UIColor.lightGray
}
if resultLegArray[selectedIndex][0][arrayIndex-1] is PublicLeg { //Line before current
print("PublicLeg")
var tempPublicLeg = resultLegArray[selectedIndex][0][arrayIndex-1] as! PublicLeg
sideTopColor = UIColor(argb: tempPublicLeg.line.style.backgroundColor)
} else {
print("IndividualLeg")
var tempIndLeg = resultLegArray[selectedIndex][0][arrayIndex-1] as! IndividualLeg
sideTopColor = UIColor.lightGray
}
}
}
//Even index
} else {
//Info cell
cell.timeTopLabel.isHidden = true
cell.timeMiddleLabel.isHidden = true
cell.timeBottomLabel.isHidden = true
sideLineType = "static"
cell.backgroundColor = UIColor.systemBackground
if resultLegArray[selectedIndex][0][arrayIndex] is PublicLeg {
//Fahrzeug
print("PublicLeg")
var tempPublicLeg = resultLegArray[selectedIndex][0][arrayIndex] as! PublicLeg
cell.lineNumberLabel.text = tempPublicLeg.line.label ?? ""
cell.destinationLabel.text = tempPublicLeg.destination?.name
if tempPublicLeg.line.style.backgroundColor2 == nil || tempPublicLeg.line.style.backgroundColor2 == 0 {
cell.lineNumberLabel.backgroundColorC = tempPublicLeg.line.style.backgroundColor
} else {
cell.lineNumberLabel.backgroundColorC = UInt32(UIColor.clear.hexa)
let backgroundLineHalfHalf = LineHalfTriangleView(frame: cell.lineNumberLabel.frame)
backgroundLineHalfHalf.topColor = tempPublicLeg.line.style.backgroundColor
backgroundLineHalfHalf.bottomColor = tempPublicLeg.line.style.backgroundColor2
backgroundLineHalfHalf.borderColor = tempPublicLeg.line.style.borderColor
cell.contentView.addSubview(backgroundLineHalfHalf)
cell.contentView.sendSubviewToBack(backgroundLineHalfHalf)
}
cell.lineNumberLabel.foregroundColor = tempPublicLeg.line.style.foregroundColor
cell.lineNumberLabel.roundCorners(corners: .allCorners, radius: 0)
//MARK: Info PublicLeg Time
if tempPublicLeg.departureTime == tempPublicLeg.plannedDepartureTime {
} else {
let timeDifference = tempPublicLeg.plannedDepartureTime.distance(to: tempPublicLeg.departureTime )
cell.timeTopLabel.text = timeDifference.stringFromTimeIntervalWithText()
cell.timeTopLabel.textColor = UIColor.systemRed
cell.timeTopLabel.isHidden = false
cell.timeTopLabel.text = "+ (timeDifference.stringFromTimeIntervalOnlyNumber())"
if cell.timeTopLabel.text?.contains("-") == true {
cell.timeTopLabel.text = cell.timeTopLabel.text?.replacingOccurrences(of: "+ ", with: "")
cell.timeTopLabel.text = cell.timeTopLabel.text?.replacingOccurrences(of: "-", with: "- ")
cell.timeTopLabel.textColor = UIColor.systemBlue
}
}
if tempPublicLeg.arrivalTime == tempPublicLeg.plannedArrivalTime {
} else {
let timeDifference = tempPublicLeg.plannedArrivalTime.distance(to: tempPublicLeg.arrivalTime )
cell.timeBottomLabel.text = timeDifference.stringFromTimeIntervalWithText()
cell.timeBottomLabel.textColor = UIColor.systemRed
cell.timeBottomLabel.isHidden = false
cell.timeBottomLabel.text = "+ (timeDifference.stringFromTimeIntervalOnlyNumber())"
if cell.timeBottomLabel.text?.contains("-") == true {
cell.timeBottomLabel.text = cell.timeBottomLabel.text?.replacingOccurrences(of: "+ ", with: "")
cell.timeBottomLabel.text = cell.timeBottomLabel.text?.replacingOccurrences(of: "-", with: "- ")
cell.timeBottomLabel.textColor = UIColor.systemBlue
}
}
if cell.timeTopLabel.text == cell.timeBottomLabel.text {
cell.timeTopLabel.isHidden = true
cell.timeBottomLabel.isHidden = true
cell.timeMiddleLabel.isHidden = false
cell.timeMiddleLabel.text = cell.timeTopLabel.text
cell.timeMiddleLabel.textColor = cell.timeTopLabel.textColor
}
sideColor = UIColor(argb: tempPublicLeg.line.style.backgroundColor)
cell.lineNumberLabel.shape = tempPublicLeg.line.style.shape
//Expandable Cell
let intermediateTableView = UITableView(frame: CGRect(x: 0, y: cell.frame.height, width: cell.frame.width, height: 30))
intermediateTableView.register(detailVerbindungIntermediateStopTableViewCell.self, forCellReuseIdentifier: "detailVerbindungIntermediateStopTableViewCell")
intermediateTableView.dataSource = cell
intermediateTableView.delegate = cell
cell.contentView.addSubview(intermediateTableView)
} else {
//Walk
print("IndividualLeg")
var tempIndLeg = resultLegArray[selectedIndex][0][arrayIndex] as! IndividualLeg
cell.destinationLabel.text = "Fußweg: (tempIndLeg.departure.getDistanceText(CLLocation(latitude: CLLocationDegrees(tempIndLeg.arrival.coord?.lat ?? 0)/1000000, longitude: CLLocationDegrees(tempIndLeg.arrival.coord?.lon ?? 0)/1000000)))"
let config = UIImage.SymbolConfiguration(paletteColors: [.label, .lightGray])
let walkIconImgView = UIImageView(frame: CGRect(x: 96, y: 24, width: 42, height: 42))
walkIconImgView.contentMode = .scaleAspectFit
walkIconImgView.image = UIImage(systemName: "figure.walk.diamond")!.applyingSymbolConfiguration(config)
cell.addSubview(walkIconImgView)
walkIconImgView.isHidden = true
let imageAttachment = NSTextAttachment()
imageAttachment.image = UIImage(systemName: "figure.walk", withConfiguration: config)
let fullString = NSMutableAttributedString(string: "")
fullString.append(NSAttributedString(attachment: imageAttachment))
cell.lineNumberLabel.attributedText = fullString
sideColor = UIColor.lightGray
}
}
switch sideLineType {
case "middle": // ⎡ Comes from bottom to top
// Create the ⏐ UIView
let leftView = UIView()
leftView.frame = CGRect(x: 0, y: cell.sideLineView.frame.height / 2 + 7, width: 6, height: cell.sideLineView.frame.height / 2)
leftView.backgroundColor = sideBottomColor
// Create the ⎯ UIView
let rightView = UIView()
rightView.frame = CGRect(x: 0, y: cell.sideLineView.frame.height / 2 + 7, width: cell.sideLineView.frame.width, height: 6)
rightView.backgroundColor = sideBottomColor
// Add the subviews to the container view
cell.sideLineView.addSubview(leftView)
cell.sideLineView.addSubview(rightView)
// ⎣ Comes from top to bottom
// Create the ⏐ UIView
let topLeftView = UIView()
topLeftView.frame = CGRect(x: 0, y: 0, width: 6, height: cell.sideLineView.frame.height / 2 - 11)
topLeftView.backgroundColor = sideTopColor
// Create the ⎯ UIView
let topRightView = UIView()
topRightView.frame = CGRect(x: 0, y: cell.sideLineView.frame.height / 2 - 11, width: cell.sideLineView.frame.width, height: 6)
topRightView.backgroundColor = sideTopColor
// Add the subviews to the container view
cell.sideLineView.addSubview(topLeftView)
cell.sideLineView.addSubview(topRightView)
case "start": // ⎡
let sideLineMainView = UIView(frame: CGRect(x: 0, y: cell.sideLineView.frame.height / 2 - 3, width: 6, height: cell.sideLineView.frame.height))
sideLineMainView.backgroundColor = sideColor
cell.sideLineView.addSubview(sideLineMainView)
let sideLineSideView = UIView(frame: CGRect(x: 0, y: cell.sideLineView.frame.height / 2 - 3, width: cell.sideLineView.frame.width, height: 6))
sideLineSideView.backgroundColor = sideColor
cell.sideLineView.addSubview(sideLineSideView)
case "end": // ⎣
let sideLineMainView = UIView(frame: CGRect(x: 0, y: 0, width: 6, height: cell.sideLineView.frame.height / 2 + 3))
sideLineMainView.backgroundColor = sideColor
cell.sideLineView.addSubview(sideLineMainView)
let sideLineSideView = UIView(frame: CGRect(x: 0, y: cell.sideLineView.frame.height / 2 - 3, width: cell.sideLineView.frame.width, height: 6))
sideLineSideView.backgroundColor = sideColor
cell.sideLineView.addSubview(sideLineSideView)
case "static": // ⎥
let sideLineMainView = UIView(frame: CGRect(x: 0, y: 0, width: 6, height: cell.sideLineView.frame.height))
sideLineMainView.backgroundColor = sideColor
cell.sideLineView.addSubview(sideLineMainView)
default: break
}
}
return cell
}
And with the following method, I change the height:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if expandedRowIndex == indexPath.row {
expandedRowIndex = -1
shouldExpanded = false
} else {
expandedRowIndex = indexPath.row
shouldExpanded = true
}
tableView.reloadRows(at: [indexPath], with: .automatic)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == expandedRowIndex && shouldExpanded == true {
return 91 //Expanded
}
return 71 //Not expanded
}
Thanks in advance and sorry for my bad English 😉
2
Answers
Update:
Thank you very much for you comments, I appreciate it! To the last comment, this is how my cells should look like/look now:
The data I get comes from a private API Server of the Deutsche Bahn. I've updated my code and now this is my cellForRowAt method:
This is my didSelectRowAt method:
And this my custom tableView Cell that is displayed inside the main cells:
Sorry for that much code, I appreciate your help and hope I gave you the right information. Thanks!
We’re missing a lot of information – data structures, sample data, etc. – so I can’t copy/paste/run your code to figure out exactly what’s wrong.
However, I would strongly suggest:
cellForRowAt
cellForRowAt
and, what I think would really help you…
For example, instead of ONE cell class that needs add/remove subviews for every instance, use four classes (I don’t know what your ultimate needs will be, so you might need more). I’ve added vertical space between the cells to make it clear:
Now, in each cell’s
init
process, create and layout only the UI elements that cell will need.Then, in
cellForRowAt
, dequeue and configure the appropriate class.Here’s how it would look without the inter-cell spacing:
If that’s not quite clear, or if you’re still having trouble… if you put together a minimal reproducible example (post it somewhere like GitHub) that includes your data structures and some sample data and I can help you find the issue.