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 ;)
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:
process your data in your data structure (calculating time differences, etc)
put your "cell layout" code inside your cell class(es), not inside cellForRowAt
don't add/remove subviews inside cellForRowAt
use auto-layout / constraints rather than calculating sizes
and, what I think would really help you...
use multiple cell classes
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.
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:
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"
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.intermediateStops.removeAll()
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.arrivalStop.predictedTime == nil {
cell.timeMiddleLabel.text = timeFormatHHMM.string(from: tempPublicLeg.plannedArrivalTime)
cell.timeMiddleLabel.textColor = UIColor.label
} else if tempPublicLeg.arrivalStop.plannedTime == tempPublicLeg.arrivalStop.predictedTime {
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 (here is first and every other cell)
//Location cell
//Show Time
if arrayIndex == 0 {
//first cell
sideLineType = "start"
cell.timeTopLabel.isHidden = true
cell.timeMiddleLabel.isHidden = false
cell.timeBottomLabel.isHidden = true
if resultLegArray[selectedIndex][0][arrayIndex] is PublicLeg {
print("PublicLeg")
var tempPublicLeg = resultLegArray[selectedIndex][0][arrayIndex] as! PublicLeg
if tempPublicLeg.departureStop.predictedTime == nil {
cell.timeMiddleLabel.textColor = .label
cell.timeMiddleLabel.text = timeFormatHHMM.string(from: tempPublicLeg.departureStop.plannedTime)
} else if tempPublicLeg.departureStop.plannedTime == tempPublicLeg.departureStop.predictedTime {
cell.timeMiddleLabel.text = timeFormatHHMM.string(from: tempPublicLeg.departureStop.predictedTime!)
cell.timeMiddleLabel.textColor = UIColor.systemGreen
} else {
let timeDifference = tempPublicLeg.departureStop.plannedTime.distance(to: (tempPublicLeg.departureStop.predictedTime ?? tempPublicLeg.departureStop.plannedTime)!)
cell.timeMiddleLabel.text = timeFormatHHMM.string(from: tempPublicLeg.departureStop.predictedTime!)
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][arrayIndex] as! IndividualLeg
sideColor = UIColor.lightGray
cell.timeMiddleLabel.text = timeFormatHHMM.string(from: tempIndLeg.departureTime)
cell.timeMiddleLabel.textColor = UIColor.label
}
} else {//MARK: sideLineType Middle eg. every cell besiddes first and last
sideLineType = "middle"
cell.timeTopLabel.isHidden = false
cell.timeMiddleLabel.isHidden = true
cell.timeBottomLabel.isHidden = false
if resultLegArray[selectedIndex][0][arrayIndex] is PublicLeg {
print("PublicLeg")
var tempPublicLeg = resultLegArray[selectedIndex][0][arrayIndex] as! PublicLeg
if tempPublicLeg.departureStop.predictedTime == nil {
cell.timeBottomLabel.textColor = .label
cell.timeBottomLabel.text = timeFormatHHMM.string(from: tempPublicLeg.departureStop.plannedTime)
} else if tempPublicLeg.departureStop.plannedTime == tempPublicLeg.departureStop.predictedTime {
cell.timeBottomLabel.text = timeFormatHHMM.string(from: tempPublicLeg.departureStop.predictedTime!)
cell.timeBottomLabel.textColor = UIColor.systemGreen
} else {
let timeDifference = tempPublicLeg.departureStop.plannedTime.distance(to: tempPublicLeg.departureStop.predictedTime! )
cell.timeBottomLabel.text = timeFormatHHMM.string(from: tempPublicLeg.departureStop.predictedTime!)
cell.timeBottomLabel.textColor = UIColor.systemRed
if timeDifference.stringFromTimeIntervalOnlyNumber().contains("-") == true {
cell.timeBottomLabel.textColor = UIColor.systemBlue
}
}
sideBottomColor = UIColor(argb: tempPublicLeg.line.style.backgroundColor)
} else {
print("IndividualLeg")
var tempIndLeg = resultLegArray[selectedIndex][0][arrayIndex] as! IndividualLeg
sideBottomColor = UIColor.lightGray
cell.timeBottomLabel.text = timeFormatHHMM.string(from: tempIndLeg.departureTime)
cell.timeBottomLabel.textColor = UIColor.label
}
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)
if tempPublicLeg.arrivalStop.predictedTime == nil {
cell.timeTopLabel.textColor = .label
cell.timeTopLabel.text = timeFormatHHMM.string(from: tempPublicLeg.arrivalStop.plannedTime)
} else if tempPublicLeg.arrivalStop.plannedTime == tempPublicLeg.arrivalStop.predictedTime {
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
}
}
} else {
print("IndividualLeg")
var tempIndLeg = resultLegArray[selectedIndex][0][arrayIndex-1] as! IndividualLeg
sideTopColor = UIColor.lightGray
cell.timeTopLabel.text = timeFormatHHMM.string(from: tempIndLeg.arrivalTime)
cell.timeTopLabel.textColor = UIColor.label
}
}
}
//Even index
} else {
//Info cell (Vehicle or Walk)
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 = detailVerbindungIntermediateStopsTableView(frame: CGRect(x: 0, y: cell.frame.height, width: cell.frame.width, height: .zero))
intermediateTableView.translatesAutoresizingMaskIntoConstraints = false
intermediateTableView.register(detailVerbindungIntermediateStopTableViewCell.self, forCellReuseIdentifier: "detailVerbindungIntermediateStopTableViewCell")
intermediateTableView.dataSource = cell
intermediateTableView.delegate = cell
intermediateTableView.estimatedRowHeight = 20
intermediateTableView.rowHeight = 20
intermediateTableView.separatorInset.left = 96
cell.contentView.addSubview(intermediateTableView)
cell.intermediateStops = tempPublicLeg.intermediateStops
cell.sideColor = sideColor
let bottomConstraint = NSLayoutConstraint(item: intermediateTableView, attribute: .top, relatedBy: .equal, toItem: cell.destinationLabel, attribute: .bottom, multiplier: 1.0, constant: 0)
let leadingConstraint = NSLayoutConstraint(item: intermediateTableView, attribute: .leading, relatedBy: .equal, toItem: cell.contentView, attribute: .leading, multiplier: 1.0, constant: 0)
let trailingConstraint = NSLayoutConstraint(item: intermediateTableView, attribute: .trailing, relatedBy: .equal, toItem: cell.contentView, attribute: .trailing, multiplier: 1.0, constant: 0)
let heightConstraint = NSLayoutConstraint(item: intermediateTableView, attribute: .height, relatedBy: .greaterThanOrEqual, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 20)
cell.contentView.addConstraints([bottomConstraint, leadingConstraint, trailingConstraint, heightConstraint])
} 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
}
This is my didSelectRowAt method:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row / 2 >= resultLegArray[selectedIndex][0].count {
return
}
if resultLegArray[selectedIndex][0][indexPath.row / 2] is PublicLeg && indexPath.row % 2 != 0 {
if expandedRowIndex == indexPath.row {
expandedRowIndex = -1
} else {
expandedRowIndex = indexPath.row
}
tableView.reloadRows(at: [indexPath], with: .automatic)
} else {
tableView.deselectRow(at: indexPath, animated: false)
}
}
And this my custom tableView Cell that is displayed inside the main cells:
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 20
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 20
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return intermediateStops.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "detailVerbindungIntermediateStopTableViewCell", for: indexPath) as! detailVerbindungIntermediateStopTableViewCell
cell.stopLabel.text = intermediateStops[indexPath.row].location.name
if let time = intermediateStops[indexPath.row].departure?.time {
cell.timeLabel.text = timeFormatHHMM.string(from: time)
} else {
cell.timeLabel.text = ""
}
let sideLineMainView = UIView(frame: CGRect(x: 0, y: 0, width: 6, height: 20))
sideLineMainView.backgroundColor = sideColor
cell.lineImageView.addSubview(sideLineMainView)
cell.rightLabel.isHidden = true
if intermediateStops[indexPath.row].departure?.predictedTime == nil {
//Keine Live
cell.timeLabel.textColor = UIColor.label
if let time = intermediateStops[indexPath.row].departure?.plannedTime {
cell.timeLabel.text = timeFormatHHMM.string(from: time)
} else {
cell.timeLabel.text = ""
} } else {
//Live
if intermediateStops[indexPath.row].departure?.predictedTime == intermediateStops[indexPath.row].departure?.plannedTime {
//Pünktlich
cell.timeLabel.textColor = UIColor.systemGreen
cell.timeLabel.text = timeFormatHHMM.string(from: intermediateStops[indexPath.row].departure!.plannedTime)
} else {
//Außerfahrplanmäßige Zeit
let timeDifference = intermediateStops[indexPath.row].departure?.predictedTime?.timeIntervalSince(intermediateStops[indexPath.row].departure!.plannedTime)
let delayAttString = NSMutableAttributedString()
var delayString = NSAttributedString()
var timeString = NSAttributedString()
let firstKlammer = NSAttributedString(string: "(", attributes: [NSAttributedString.Key.foregroundColor: UIColor.label])
let secondKlammer = NSAttributedString(string: ")", attributes: [NSAttributedString.Key.foregroundColor: UIColor.label])
if timeDifference!.stringFromTimeIntervalWithText().contains("-") {
delayString = NSAttributedString(string: timeDifference!.stringFromTimeIntervalOnlyNumber(), attributes: [NSAttributedString.Key.foregroundColor: UIColor.systemBlue])
timeString = NSAttributedString(string: timeFormatHHMM.string(from: (intermediateStops[indexPath.row].departure?.predictedTime)!), attributes: [NSAttributedString.Key.foregroundColor: UIColor.systemBlue])
cell.timeLabel.textColor = .systemBlue
} else {
delayString = NSAttributedString(string: "+\(timeDifference!.stringFromTimeIntervalOnlyNumber())", attributes: [NSAttributedString.Key.foregroundColor: UIColor.systemRed])
timeString = NSAttributedString(string: timeFormatHHMM.string(from: (intermediateStops[indexPath.row].departure?.predictedTime)!), attributes: [NSAttributedString.Key.foregroundColor: UIColor.systemRed])
cell.timeLabel.textColor = .systemRed
}
delayAttString.append(firstKlammer)
delayAttString.append(delayString)
delayAttString.append(secondKlammer)
cell.timeLabel.text = timeFormatHHMM.string(from: (intermediateStops[indexPath.row].departure?.predictedTime)!)
cell.rightLabel.isHidden = false
cell.rightLabel.attributedText = delayAttString
}
}
return cell
}
Sorry for that much code, I appreciate your help and hope I gave you the right information.
Thanks!
Related
I have a collection view cell which loads images. On each cell I allow users to long press which pops up buttons with icons which can allow users to click on. Upon clicking on the buttons I show the type of icon they clicked on as a form of rating. My issue now is currently if I scroll down and comes up to the top the button on the image which I displayed after the user has clicked is no longer there. How do I keep the icons stacked to the image even if I scroll
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reUseMoviesCellID, for: indexPath) as! MoviesCollectionCell
cell.btnSmallGoodRate.id = self.movieObj[indexPath.row].id ?? ""
cell.btnSmallOkRate.id = self.movieObj[indexPath.row].id ?? ""
cell.btnSmallHateRate.id = self.movieObj[indexPath.row].id ?? ""
cell.btnSmallLoveRate.id = self.movieObj[indexPath.row].id ?? ""
cell.tapHandler = {
Commons.userGlobalRate += 1
self.lblRated.text = "\(Commons.userGlobalRate) rated"
self.ratedView.backgroundColor = .ratedGoldColour
if Commons.progressBarValue < 1 {
Commons.progressBarValue += 0.2
self.ratedTrackBarView.setProgress(Commons.progressBarValue, animated: true)
}
}
cell.configure(with: movieObj[indexPath.row])
let ids = ["id" : movieObj[indexPath.row].id!]
excludedDictionary.append(ids)
return cell
}
class MoviesCollectionCell: UICollectionViewCell {
var tapHandler: (()->())?
let movieImage: UIImageView = {
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
image.clipsToBounds = true
image.contentMode = .scaleAspectFill
image.layer.cornerRadius = 10
return image
}()
let movieOverlay: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .black.withAlphaComponent(0.3)
view.alpha = 0
return view
}()
let btnRate: UIImageView = {
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
image.clipsToBounds = true
image.contentMode = .scaleAspectFit
image.alpha = 0
return image
}()
let btnSmallGoodRate: ButtonRating = {
let btn = ButtonRating()
btn.translatesAutoresizingMaskIntoConstraints = false
btn.setImage(UIImage(named: "goodRate"), for: .normal)
btn.addTarget(self, action: #selector(goodItem(_:)), for: .touchUpInside)
btn.backgroundColor = .black.withAlphaComponent(0.4)
btn.layer.cornerRadius = 30
btn.alpha = 0
return btn
}()
let removeOverlay: UIButton = {
let btn = UIButton()
btn.translatesAutoresizingMaskIntoConstraints = false
btn.setImage(UIImage(systemName: "xmark"), for: .normal)
btn.tintColor = .black
btn.contentMode = .scaleAspectFit
btn.backgroundColor = .white.withAlphaComponent(0.7)
btn.layer.cornerRadius = 10
btn.contentEdgeInsets = UIEdgeInsets(top: 4, left: 4, bottom: 4, right: 4)
btn.addTarget(self, action: #selector(dismissOverlay), for: .touchUpInside)
btn.alpha = 0
return btn
}()
let btnSmallHateRate: ButtonRating = {
let btn = ButtonRating()
btn.translatesAutoresizingMaskIntoConstraints = false
btn.clipsToBounds = true
btn.contentMode = .scaleAspectFit
btn.setImage(UIImage(named: "HateIt"), for: .normal)
btn.alpha = 0
btn.addTarget(self, action: #selector(hateItem(_:)), for: .touchUpInside)
btn.backgroundColor = .black.withAlphaComponent(0.4)
btn.layer.cornerRadius = 30
return btn
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(movieImage)
movieImage.addSubview(btnRate)
movieImage.addSubview(movieOverlay)
contentView.addSubview(btnSmallGoodRate)
contentView.addSubview(btnSmallHateRate)
contentView.addSubview(removeOverlay)
let directFullPreviewer = UILongPressGestureRecognizer(target: self, action: #selector(directFullPreviewLongPressAction))
addGestureRecognizer(directFullPreviewer)
btnRate.tintColor = .white
btnRate.layer.shadowColor = UIColor.black.cgColor
btnRate.layer.shadowOffset = CGSize(width: 1.0, height: 2.0)
btnRate.layer.shadowRadius = 2
btnRate.layer.shadowOpacity = 0.8
btnRate.layer.masksToBounds = false
}
override func prepareForReuse() {
super.prepareForReuse()
movieImage.image = nil
btnRate.image = nil
}
func configure(with res: Responder){
movieImage.sd_setImage(with: URL(string: res.packShot?.thumbnail ?? ""), placeholderImage: UIImage(named: "ImagePlaceholder"))
}
#objc func directFullPreviewLongPressAction(g: UILongPressGestureRecognizer)
{
if g.state == UIGestureRecognizer.State.began
{
movieOverlay.alpha = 1
btnSmallGoodRate.alpha = 1
btnSmallHateRate.alpha = 1
removeOverlay.alpha = 1
btnHidden.alpha = 1
}
}
#objc func dismissOverlay(){
movieOverlay.alpha = 0
btnSmallHateRate.alpha = 0
btnSmallGoodRate.alpha = 0
removeOverlay.alpha = 0
btnHidden.alpha = 0
}
#objc func hateItem(_ sender: UIButton){
guard let gestureVariable = sender as? ButtonRating else{
return
}
let movieID = gestureVariable.id
print("Hate movie ID ", movieID)
btnSmallOkRate.alpha = 0
btnSmallHateRate.alpha = 0
btnSmallGoodRate.alpha = 0
btnSmallLoveRate.alpha = 0
removeOverlay.alpha = 0
btnRate.alpha = 1
btnRate.image = UIImage(named: "HateIt")
tapHandler?()
}
#objc func goodItem(_ sender: UIButton){
guard let gestureVariable = sender as? ButtonRating else{
return
}
let movieID = gestureVariable.id
print("Good movie ID ", movieID)
btnSmallOkRate.alpha = 0
btnSmallHateRate.alpha = 0
btnSmallGoodRate.alpha = 0
btnSmallLoveRate.alpha = 0
removeOverlay.alpha = 0
btnRate.alpha = 1
btnRate.image = UIImage(named: "goodRate")
tapHandler?()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Cell will dequeueReusableCell after it disapear from scrollview, so u should
set the bool value to reduction.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reUseMoviesCellID, for: indexPath) as! MoviesCollectionCell
cell.btnSmallGoodRate.id = self.movieObj[indexPath.row].id ?? ""
cell.btnSmallOkRate.id = self.movieObj[indexPath.row].id ?? ""
cell.btnSmallHateRate.id = self.movieObj[indexPath.row].id ?? ""
cell.btnSmallLoveRate.id = self.movieObj[indexPath.row].id ?? ""
cell.tapHandler = {
cell.isTap = true
Commons.userGlobalRate += 1
self.lblRated.text = "\(Commons.userGlobalRate) rated"
self.ratedView.backgroundColor = .ratedGoldColour
if Commons.progressBarValue < 1 {
Commons.progressBarValue += 0.2
self.ratedTrackBarView.setProgress(Commons.progressBarValue, animated: true)
}
}
if cell.isTap{
self.lblRated.text = "\(Commons.userGlobalRate) rated"
self.ratedView.backgroundColor = .ratedGoldColour
if Commons.progressBarValue < 1 {
Commons.progressBarValue += 0.2
self.ratedTrackBarView.setProgress(Commons.progressBarValue, animated: true)
}
}
cell.configure(with: movieObj[indexPath.row])
let ids = ["id" : movieObj[indexPath.row].id!]
excludedDictionary.append(ids)
return cell
}
class MoviesCollectionCell: UICollectionViewCell {
var tapHandler: (()->())?
var isTap = false
}
You need to maintain press action inside "movieObj". Because the cell is always reusable.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reUseMoviesCellID, for: indexPath) as! MoviesCollectionCell
cell.btnSmallGoodRate.id = self.movieObj[indexPath.row].id ?? ""
cell.btnSmallOkRate.id = self.movieObj[indexPath.row].id ?? ""
cell.btnSmallHateRate.id = self.movieObj[indexPath.row].id ?? ""
cell.btnSmallLoveRate.id = self.movieObj[indexPath.row].id ?? ""
cell.tapHandler = {
movieObj[indexPath.row].isTapped = true
Commons.userGlobalRate += 1
self.lblRated.text = "\(Commons.userGlobalRate) rated"
self.ratedView.backgroundColor = .ratedGoldColour
if Commons.progressBarValue < 1 {
Commons.progressBarValue += 0.2
self.ratedTrackBarView.setProgress(Commons.progressBarValue, animated: true)
}
}
if movieObj[indexPath.row].isTapped {
self.lblRated.text = "\(Commons.userGlobalRate) rated"
self.ratedView.backgroundColor = .ratedGoldColour
if Commons.progressBarValue < 1 {
Commons.progressBarValue += 0.2
self.ratedTrackBarView.setProgress(Commons.progressBarValue, animated: true)
}
}
}
I have made my app completely Programatic UI. With iOS 15 I am having a weird issue however with previous iOS versions its working fine as expected. So I have couple of buttons on top of the view, while clicking each button the below table view will be reloaded. When I click on one particular button the table view cell is not showing anything but the space is occupied. At the same time if I press any other button and comeback to the same button everything looks perfect. Kindly check the below code and shed some light.
ModeOfPaymentTableViewCell
class ModeOfPaymentTableViewCell: UITableViewCell{
class var identifier: String { return String.className(self) }
let addressDescriptionTextView:UILabel = {
let textView = UILabel()
textView.translatesAutoresizingMaskIntoConstraints = false
textView.textAlignment = .natural
textView.textColor = .gray
textView.font = K.FONT.FONT17
textView.isUserInteractionEnabled = false
textView.numberOfLines = 0
return textView
}()
let otherLabel:UILabel = {
let lbl = UILabel()
lbl.translatesAutoresizingMaskIntoConstraints = false
lbl.textColor = UIColor.black
lbl.textAlignment = .natural
lbl.font = K.FONT.FONT17
return lbl
}()
let indicatorImage:UIImageView = {
let imageview = UIImageView()
imageview.translatesAutoresizingMaskIntoConstraints = false
imageview.image = #imageLiteral(resourceName: "Back")
return imageview
}()
let cardImage : UIImageView = {
let imageview = UIImageView()
imageview.translatesAutoresizingMaskIntoConstraints = false
imageview.contentMode = UIView.ContentMode.scaleAspectFit
return imageview
}()
var primaryData = [String](){
didSet{
setUpDescriptionTextView()
}
}
let shimmer = ShimmerCellWithOutImage()
var showShimmer = Bool(){
didSet{
shimmer.isHidden = !showShimmer
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
shimmer.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(shimmer)
self.addSubview(addressDescriptionTextView)
self.addSubview(otherLabel)
self.addSubview(cardImage)
self.addSubview(indicatorImage)
self.borderColor = UIColor.groupTableViewBackground
self.borderWidth = 1
self.backgroundColor = .white
self.selectionStyle = .none
setUpUI()
if Language.type == "en" || Language.type == "en-SA"{
otherLabel.textAlignment = .right
indicatorImage.image = #imageLiteral(resourceName: "Next")
}
else if Language.type == "ar" || Language.type == "ar-SA" {
otherLabel.textAlignment = .left
indicatorImage.image = #imageLiteral(resourceName: "Back")
}
else{
otherLabel.textAlignment = .right
indicatorImage.image = #imageLiteral(resourceName: "Next")
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("failed to load the frame")
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override public func layoutSubviews() {
super.layoutSubviews()
shimmer.shimmerView.addShimmerAnimation()
}
func setUpDescriptionTextView(){
let leftSideValue = primaryData[0].components(separatedBy: ",")
let attributedText = NSMutableAttributedString()
attributedText.append(NSAttributedString(
string: "\n" + leftSideValue[0], attributes:
[NSAttributedString.Key.foregroundColor :
UIColor(hex:K.COLOR.GRAYTEXTCOLOR),NSAttributedString.Key.font:K.FONT.BOLDFONT12!]))
attributedText.append(NSAttributedString(
string: "\n" + "\(leftSideValue[1])" + "\n", attributes: [NSAttributedString.Key.foregroundColor : UIColor.black,NSAttributedString.Key.font:K.FONT.FONT17!]))
addressDescriptionTextView.attributedText = attributedText
if primaryData[1] != " 0.0 " && primaryData[1] != ""{
// otherLabel.set(image: UIImage.directionIndicator()!, with: primaryData[1])
let data = primaryData[1];
if data == "VISA" || data == "MADA" || data == "MASTER"{
if data == "VISA" {
cardImage.image = #imageLiteral(resourceName: "visa")
}else if data == "MADA"{
cardImage.image = #imageLiteral(resourceName: "mada")
}else if data == "MASTER"{
cardImage.image = #imageLiteral(resourceName: "mascard")
}else{
cardImage.image = #imageLiteral(resourceName: "CARDS")
}
otherLabel.isHidden = true
cardImage.isHidden = false
}else{
otherLabel.text = data
otherLabel.isHidden = false
cardImage.isHidden = true
}
otherLabel.tintColor = UIColor(hex:K.COLOR.BLUE)
}else{
}
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func setUpUI() {
[shimmer.topAnchor.constraint(equalTo: topAnchor,constant: 2),
shimmer.leadingAnchor.constraint(equalTo:leadingAnchor, constant: 0),
shimmer.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0),
shimmer.bottomAnchor.constraint(equalTo: bottomAnchor,constant:-2)].forEach{$0.isActive = true}
[addressDescriptionTextView.topAnchor.constraint(equalTo: topAnchor,constant: 0),
addressDescriptionTextView.leadingAnchor.constraint(equalTo:leadingAnchor, constant: 10),
addressDescriptionTextView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -UIScreen.main.bounds.width/2),
addressDescriptionTextView.bottomAnchor.constraint(equalTo: bottomAnchor,constant:0)].forEach{$0.isActive = true}
[otherLabel.topAnchor.constraint(equalTo: topAnchor,constant: 0),
otherLabel.leadingAnchor.constraint(equalTo: addressDescriptionTextView.trailingAnchor, constant: 2),
otherLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -25),
otherLabel.bottomAnchor.constraint(equalTo: bottomAnchor,constant:0)].forEach{$0.isActive = true}
[cardImage.topAnchor.constraint(equalTo: topAnchor,constant: 0),
// cardImage.leadingAnchor.constraint(equalTo: addressDescriptionTextView.trailingAnchor, constant: 2),
cardImage.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -25),
cardImage.bottomAnchor.constraint(equalTo: bottomAnchor,constant:0)].forEach{$0.isActive = true}
[indicatorImage.centerYAnchor.constraint(equalTo: centerYAnchor),
indicatorImage.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10),
indicatorImage.heightAnchor.constraint(equalToConstant: 15),
indicatorImage.widthAnchor.constraint(equalToConstant: 10)].forEach{$0.isActive = true}
}
}
EwalletDetailsViewController
Here the issue happens under case Reload:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
tableView.separatorStyle = .none
switch selectedType {
case "Activity":
let cell:generalActivityTableViewCell = tableView.dequeueReusableCell(
withIdentifier: generalActivityTableViewCell.identifier,
for: indexPath) as! generalActivityTableViewCell
let date = convertDateStringToFormatedDateString(date: self.activityData?.data?[indexPath.row].date_time ?? "")
cell.activityDescriptionDetails = "\(self.activityData?.data?[indexPath.row].title ?? "") & \(self.activityData?.data?[indexPath.row].desc ?? "")"
cell.activityValueDetails = "\(date) & \(self.activityData?.data?[indexPath.row].amount ?? "")"
return cell
case "Transfer":
if indexPath.row == 1{
if selectedEwalletForTransfer?.ewallet_number ?? "" == ""{
let cell:OtherAccountViewCell = tableView.dequeueReusableCell(
withIdentifier: OtherAccountViewCell.identifier,
for: indexPath) as! OtherAccountViewCell
return cell
}else{
let cell:ModeOfPaymentTableViewCell = tableView.dequeueReusableCell(
withIdentifier: ModeOfPaymentTableViewCell.identifier,
for: indexPath) as! ModeOfPaymentTableViewCell
cell.selectionStyle = .none
cell.showShimmer = false
cell.backgroundColor = UIColor.red
if selectedEwalletForTransfer?.ewallet_title == ""{
cell.primaryData = ["\("To Ewallet Account".localized())"+","+"\(selectedEwalletForTransfer?.ewallet_number ?? "")", "\(selectedEwalletForTransfer?.currency_label ?? "") "+"\(selectedEwalletForTransfer?.balance ?? "") " ]
}else{
cell.primaryData = ["\("To Ewallet Account".localized())"+","+"\(selectedEwalletForTransfer?.ewallet_number ?? "") "+"-"+" \(selectedEwalletForTransfer?.ewallet_title ?? "")" , "\(selectedEwalletForTransfer?.currency_label ?? "") "+"\(selectedEwalletForTransfer?.balance ?? "") " ]
}
return cell
}
}else{
let cell:ReloadPickerCell = tableView.dequeueReusableCell(
withIdentifier: ReloadPickerCell.identifier,
for: indexPath) as! ReloadPickerCell
cell.currencyPicker.delegate = self
cell.currencyPicker.dataSource = self
cell.amountPicker.delegate = self
cell.amountPicker.dataSource = self
cell.amountLabel.isHidden = false
cell.amountLabel.delegate = self
cell.selecteTitleLabel.text = "Enter Amount to transfer".localized()
return cell
}
case "Reload":
if indexPath.row == 1{
let cell:ModeOfPaymentTableViewCell = tableView.dequeueReusableCell(
withIdentifier: ModeOfPaymentTableViewCell.identifier,
for: indexPath) as! ModeOfPaymentTableViewCell
cell.selectionStyle = .none
cell.showShimmer = false
if self.selectedCard != nil{
cell.primaryData = ["\("CARD INFO".localized())"+","+"\(self.selectedCard?.card_masked_number ?? "")", "\(self.selectedCard?.card_brand ?? "")" ]
}else{
cell.primaryData = ["\("CARD INFO".localized())"+","+"Click here to add your credit card".localized(), "" ]
}
cell.layoutSubviews()
return cell
}else{
let cell:ReloadPickerCell = tableView.dequeueReusableCell(
withIdentifier: ReloadPickerCell.identifier,
for: indexPath) as! ReloadPickerCell
cell.currencyPicker.delegate = self
cell.currencyPicker.dataSource = self
cell.amountPicker.delegate = self
cell.amountPicker.dataSource = self
cell.amountLabel.isHidden = true
cell.selecteTitleLabel.text = "ENTER AMOUNT TO RELOAD".localized()
return cell
}
case "Share":
let cell:SharedTableViewCell = tableView.dequeueReusableCell(
withIdentifier: SharedTableViewCell.identifier,
for: indexPath) as! SharedTableViewCell
cell.showShimmer = false
let date = convertDateStringToFormatedDateString(date: self.shareData?.data?[indexPath.row].shared_date ?? "")
cell.primaryData = ["\(self.shareData?.data?[indexPath.row].shared_email ?? "")" + "," + "\(self.shareData?.data?[indexPath.row].user_name ?? "")","\(date)","\(self.shareData?.data?[indexPath.row].profile_pic ?? "")"]
return cell
default:
return UITableViewCell()
}
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
switch selectedType {
case "Activity":
return UITableView.automaticDimension
case "Transfer":
if indexPath.row == 1{
if selectedEwalletForTransfer?.ewallet_number ?? "" == ""{
return 120
}else{
return UITableView.automaticDimension
}
}else{
return 110
}
case "Reload":
if indexPath.row == 1{
if self.cardData?.data?.count == 0{
return UITableView.automaticDimension
}else{
return UITableView.automaticDimension
}
}else{
return 110
}
case "Share":
return UITableView.automaticDimension
default:
return 0
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
switch selectedType {
case "Activity":
return UITableView.automaticDimension
case "Transfer":
if indexPath.row == 1{
if selectedEwalletForTransfer?.ewallet_number ?? "" == ""{
return 120
}else{
return UITableView.automaticDimension
}
}else{
return 110
}
case "Reload":
if indexPath.row == 1{
if self.cardData?.data?.count == 0 || self.cardData?.data == nil && OPPPaymentProvider.deviceSupportsApplePay() && self.selectedEwallet?.currency_code == "SAR" {
return UITableView.automaticDimension
}else{
return UITableView.automaticDimension
}
}else{
return 110
}
case "Share":
return UITableView.automaticDimension
default:
return 0
}
}
My problem is that UITableView lags quite a lot while scrolling.
This is what I am trying to achieve
Starting from the top I have a simple section header with only one checkbox and one UILabel. Under this header, you can see a custom cell with only one UILabel aligned to the center. This custom cell works like another header for the data that would be shown below (Basically a 3D array). Under these "headers" are custom cells that contain one multiline UILabel and under this label is a container for a variable amount of lines containing a checkbox and an UILabel. On the right side of the cell is also a button (blue/white arrow).
So this means the content is shown like this:
Section header (containing day and date)
Custom UITableViewCell = header (containing some header information)
Custom UITableViewCell (containing data to be shown)
Here is my code:
cellForRowAt:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let (isHeader, headerNumber, semiResult) = checkIfIsHeader(section: indexPath.section, row: indexPath.row)
let row = indexPath.row
if isHeader {
let chod = objednavkaDny[indexPath.section].chody[headerNumber+1]
let cell = tableView.dequeueReusableCell(withIdentifier: cellHeaderReuseIdentifier, for: indexPath) as! ObjednavkyHeaderTableViewCell
cell.titleLabel.text = chod.popisPoradiJidla
cell.selectionStyle = .none
return cell
}else{
let chod = objednavkaDny[indexPath.section].chody[headerNumber]
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as! ObjednavkyTableViewCell
cell.updateData(objednavka: chod.objednavky[row-semiResult], canSetAmount: self.typDialogu == 3)
return cell
}
}
checkIfIsHeader:
func checkIfIsHeader(section: Int, row: Int) -> (Bool, Int, Int){
if let cachedResult = checkIfHeaderCache[section]?[row] {
return (cachedResult[0] == 1, cachedResult[1], cachedResult[2])
}
var isHeader = false
var semiResult = 0
var headerNumber = -1
for (index, chod) in objednavkaDny[section].chody.enumerated() {
let sum = chod.objednavky.count
if row == semiResult {
isHeader = true
break
}else if row < semiResult {
semiResult -= objednavkaDny[section].chody[index-1].objednavky.count
break
}else {
headerNumber += 1
semiResult += 1
if index != objednavkaDny[section].chody.count - 1 {
semiResult += sum
}
}
}
checkIfHeaderCache[section] = [Int:[Int]]()
checkIfHeaderCache[section]![row] = [isHeader ? 1 : 0, headerNumber, semiResult]
return (isHeader, headerNumber, semiResult)
}
and the main cell that shows the data:
class ObjednavkyTableViewCell: UITableViewCell {
lazy var numberTextField: ObjednavkyTextField = {
let textField = ObjednavkyTextField()
textField.translatesAutoresizingMaskIntoConstraints = false
return textField
}()
let mealLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = .black
label.textAlignment = .left
label.font = UIFont(name: ".SFUIText", size: 15)
label.numberOfLines = 0
label.backgroundColor = .white
label.isOpaque = true
return label
}()
lazy var detailsButton: UIButton = {
let button = UIButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
button.setImage(UIImage(named: "arrow-right")?.withRenderingMode(.alwaysTemplate), for: .normal)
button.imageView?.tintColor = UIColor.custom.blue.classicBlue
button.imageView?.contentMode = .scaleAspectFit
button.contentHorizontalAlignment = .right
button.imageEdgeInsets = UIEdgeInsetsMake(10, 0, 10, 0)
button.addTarget(self, action: #selector(detailsButtonPressed), for: .touchUpInside)
button.backgroundColor = .white
button.isOpaque = true
return button
}()
let pricesContainerView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .white
view.isOpaque = true
return view
}()
var canSetAmount = false {
didSet {
canSetAmount ? showNumberTextField() : hideNumberTextField()
}
}
var shouldShowPrices = false {
didSet {
shouldShowPrices ? showPricesContainerView() : hidePricesContainerView()
}
}
var pricesContainerHeight: CGFloat = 0
private let priceViewHeight: CGFloat = 30
var mealLabelLeadingConstraint: NSLayoutConstraint?
var mealLabelBottomConstraint: NSLayoutConstraint?
var pricesContainerViewHeightConstraint: NSLayoutConstraint?
var pricesContainerViewBottomConstraint: NSLayoutConstraint?
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.selectionStyle = .none
setupView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#objc func detailsButtonPressed() {
}
func updateData(objednavka: Objednavka, canSetAmount: Bool) {
self.canSetAmount = canSetAmount
if let popisJidla = objednavka.popisJidla, popisJidla != "", popisJidla != " " {
self.mealLabel.text = popisJidla
}else{
self.mealLabel.text = objednavka.nazevJidelnicku
}
if objednavka.objects.count > 1 {
shouldShowPrices = true
setPricesStackView(with: objednavka.objects)
checkIfSelected(objects: objednavka.objects)
}else{
shouldShowPrices = false
self.numberTextField.text = String(objednavka.objects[0].pocet)
//setSelected(objednavka.objects[0].pocet > 0, animated: false)
objednavka.objects[0].pocet > 0 ? setSelectedStyle() : setDeselectedStyle()
}
}
//---------------
func checkIfSelected(objects: [ObjednavkaObject]) {
var didChangeSelection = false
for object in objects { // Checks wether cell should be selected or not
if object.pocet > 0 {
setSelected(true, animated: false)
setSelectedStyle()
didChangeSelection = true
break
}
}
if !didChangeSelection {
setSelected(false, animated: false)
setDeselectedStyle()
}
}
//--------------
func showNumberTextField() {
numberTextField.isHidden = false
mealLabelLeadingConstraint?.isActive = false
mealLabelLeadingConstraint = mealLabel.leadingAnchor.constraint(equalTo: numberTextField.trailingAnchor, constant: 10)
mealLabelLeadingConstraint?.isActive = true
}
func hideNumberTextField() {
numberTextField.isHidden = true
mealLabelLeadingConstraint?.isActive = false
mealLabelLeadingConstraint = mealLabel.leadingAnchor.constraint(equalTo: readableContentGuide.leadingAnchor, constant: 0)
mealLabelLeadingConstraint?.isActive = true
}
func showPricesContainerView() {
hideNumberTextField()
pricesContainerView.isHidden = false
mealLabelBottomConstraint?.isActive = false
pricesContainerViewBottomConstraint?.isActive = true
}
func hidePricesContainerView() {
pricesContainerView.isHidden = true
pricesContainerViewBottomConstraint?.isActive = false
mealLabelBottomConstraint?.isActive = true
}
//--------------
func setSelectedStyle() {
self.backgroundColor = UIColor.custom.blue.classicBlue
mealLabel.textColor = .white
mealLabel.backgroundColor = UIColor.custom.blue.classicBlue
for subview in pricesContainerView.subviews where subview is ObjednavkyPriceView {
let priceView = (subview as! ObjednavkyPriceView)
priceView.titleLabel.textColor = .white
priceView.checkBox.backgroundColor = UIColor.custom.blue.classicBlue
priceView.titleLabel.backgroundColor = UIColor.custom.blue.classicBlue
priceView.backgroundColor = UIColor.custom.blue.classicBlue
}
pricesContainerView.backgroundColor = UIColor.custom.blue.classicBlue
detailsButton.imageView?.tintColor = .white
detailsButton.backgroundColor = UIColor.custom.blue.classicBlue
}
func setDeselectedStyle() {
self.backgroundColor = .white
mealLabel.textColor = .black
mealLabel.backgroundColor = .white
for subview in pricesContainerView.subviews where subview is ObjednavkyPriceView {
let priceView = (subview as! ObjednavkyPriceView)
priceView.titleLabel.textColor = .black
priceView.checkBox.backgroundColor = .white
priceView.titleLabel.backgroundColor = .white
priceView.backgroundColor = .white
}
pricesContainerView.backgroundColor = .white
detailsButton.imageView?.tintColor = UIColor.custom.blue.classicBlue
detailsButton.backgroundColor = .white
}
//-----------------
func setPricesStackView(with objects: [ObjednavkaObject]) {
let subviews = pricesContainerView.subviews
var subviewsToDelete = subviews.count
for (index, object) in objects.enumerated() {
subviewsToDelete -= 1
if subviews.count - 1 >= index {
let priceView = subviews[index] as! ObjednavkyPriceView
priceView.titleLabel.text = object.popisProduktu // + " " + NSNumber(value: object.cena).getFormattedString(currencySymbol: "Kč") // TODO: currencySymbol
priceView.canSetAmount = canSetAmount
priceView.count = object.pocet
priceView.canOrder = (object.nelzeObj == nil || object.nelzeObj == "")
}else {
let priceView = ObjednavkyPriceView(frame: CGRect(x: 0, y: CGFloat(index) * priceViewHeight + CGFloat(index * 5), width: pricesContainerView.frame.width, height: priceViewHeight))
pricesContainerView.addSubview(priceView)
priceView.titleLabel.text = object.popisProduktu // + " " + NSNumber(value: object.cena).getFormattedString(currencySymbol: "Kč") // TODO: currencySymbol
priceView.numberTextField.delegate = self
priceView.canSetAmount = canSetAmount
priceView.canOrder = (object.nelzeObj == nil || object.nelzeObj == "")
priceView.count = object.pocet
pricesContainerHeight += ((index == 0) ? 30 : 35)
}
}
if subviewsToDelete > 0 { // Deletes unwanted subviews
for _ in 0..<subviewsToDelete {
pricesContainerView.subviews.last?.removeFromSuperview()
pricesContainerHeight -= pricesContainerHeight + 5
}
}
if pricesContainerHeight < 0 {
pricesContainerHeight = 0
}
pricesContainerViewHeightConstraint?.constant = pricesContainerHeight
}
func setupView() {
self.layer.shouldRasterize = true
self.layer.rasterizationScale = UIScreen.main.scale
self.backgroundColor = .white
contentView.addSubview(numberTextField)
contentView.addSubview(mealLabel)
contentView.addSubview(detailsButton)
contentView.addSubview(pricesContainerView)
setupConstraints()
}
func setupConstraints() {
numberTextField.anchor(leading: readableContentGuide.leadingAnchor, size: CGSize(width: 30, height: 30))
numberTextField.centerYAnchor.constraint(equalTo: mealLabel.centerYAnchor).isActive = true
detailsButton.anchor(trailing: readableContentGuide.trailingAnchor, size: CGSize(width: 30, height: 30))
detailsButton.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
mealLabel.anchor(top: contentView.topAnchor, trailing: detailsButton.leadingAnchor, padding: .init(top: 10, left: 0, bottom: 0, right: -10))
mealLabelBottomConstraint = mealLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10)
mealLabelBottomConstraint?.priority = UILayoutPriority(rawValue: 999)
pricesContainerView.anchor(top: mealLabel.bottomAnchor, leading: readableContentGuide.leadingAnchor, trailing: detailsButton.leadingAnchor, padding: .init(top: 10, left: 0, bottom: 0, right: -10))
pricesContainerViewBottomConstraint = pricesContainerView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10)
pricesContainerViewBottomConstraint?.priority = UILayoutPriority(rawValue: 999)
pricesContainerViewHeightConstraint = pricesContainerView.heightAnchor.constraint(equalToConstant: 0)
pricesContainerViewHeightConstraint?.priority = UILayoutPriority(rawValue: 999)
pricesContainerViewHeightConstraint?.isActive = true
}
}
To conclude how it is done:
tableView.rowHeight is set to UITableViewAutomaticDymension
inside cellForRowAt I get the data from an array and give it to the
cell
all the cells are set up in code using constraints
all the views have set isOpaque = true
heights of the cells are cached
cells are set to rasterize
I also noticed that it lags at certain scroll levels and that sometimes it works just fine and sometimes it lags a lot.
Despite all of the optimization I have done, the tableView still lags while scrolling.
Here is a screenshot from Instruments
Any tip how to improve the scrolling performance is highly appreciated!
(I might have forgotten to include some code/information so feel free to ask me in the comments.)
I can't tell you where the lag happens exactly but when we are talking about lagging during scrolling, it's related to your cellForRowAt delegate method. What happends is that too many things are going on within this method and it's called for every cells that are displaying & going to display. I see that your are trying to cache the result by checkIfHeaderCache but still, there is a for loop at the very beginning to determine header cell.
Suggestions:
I don't know where you get data (objednavkaDny) from but right after you get the data, do a full loop through and determin cell type one by one, and save the result some where base on your design. During this loading time, you can show some loading message on the screen. Then, within the cellForRow method, you should be just simply using things like
if (isHeader) {
render header cell
} else {
render other cell
}
Bottom line:
cellForRow method is not designed to handle heavy calculations, and it will slow down the scrolling if you do so. This method is for assigning values to the cached table view cell only and that's the only thing it is good at.
I'm new in Swift, and I have an issue with the iPhone X.
I followed this tutorial: https://www.youtube.com/watch?v=FDay6ocBlnE&index=8&list=PL0dzCUj1L5JEfHqwjBV0XFb9qx9cGXwkq in order to create a Chat App.
My problem is that the textField is fixed to the bottom, and that is not good for the iPhone X.
I really don't know how I can change this, given that I'm more familiar with storyboard and here, the collectionViewController is entirely programmatically. I searched a lot of other tutorials but I found nothing to help.
This is my code:
The bottom view (with the textfield):
class ChatInputContainerView: UIView, UITextFieldDelegate {
// ...
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .red
// ...
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The CollectionViewController:
class ChatLogController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
// ...
lazy var inputContainerView: ChatInputContainerView = {
// I can't change the y value (it changes nothing)
let chatInputContainerView = ChatInputContainerView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 54))
chatInputContainerView.chatLogController = self
return chatInputContainerView
}()
override var inputAccessoryView: UIView? {
get {
return inputContainerView
}
}
override var canBecomeFirstResponder : Bool {
return true
}
}
Update
Here's the entire code:
import UIKit
import UserNotifications
class ChatLogController: UICollectionViewController, UITextFieldDelegate, UICollectionViewDelegateFlowLayout, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var user: Userm? {
didSet {
navigationItem.title = user?.username
loadMessages()
}
}
var messages = [Message]()
func loadMessages() {
guard let toId = user?.id else {
return
}
Api.Message.observeUserDiscussion(toId: toId) { (message) in
self.messages.append(message)
DispatchQueue.main.async(execute: {
self.collectionView?.reloadData()
//scroll to the last index
let indexPath = IndexPath(item: self.messages.count - 1, section: 0)
self.collectionView?.scrollToItem(at: indexPath, at: .bottom, animated: true)
})
}
}
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
UNUserNotificationCenter.current().getNotificationSettings { (settings) in
print("Notification settings: \(settings)")
guard settings.authorizationStatus == .authorized else { return }
//Not authorised
UIApplication.shared.registerForRemoteNotifications()
}
navigationItem.backBarButtonItem = UIBarButtonItem(title: " ", style: .plain, target: nil, action: nil)
collectionView?.contentInset = UIEdgeInsets(top: 8, left: 0, bottom: 20, right: 0)
// collectionView?.scrollIndicatorInsets = UIEdgeInsets(top: 0, left: 0, bottom: 50, right: 0)
collectionView?.alwaysBounceVertical = true
collectionView?.backgroundColor = UIColor.white
collectionView?.register(ChatMessageCell.self, forCellWithReuseIdentifier: cellId)
collectionView?.keyboardDismissMode = .interactive
arrowBackButton(greyBack)
let image = UIImage(named: "iconProfilCog")
navigationItem.rightBarButtonItem = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(handleParamsMessage))
navigationItem.rightBarButtonItem?.tintColor = UIColor(red: 203/255, green: 203/255, blue: 203/255, alpha: 1)
setupKeyboardObservers()
emptyTextField()
}
func emptyTextField() {
self.inputContainerView.inputTextField.text = ""
self.inputContainerView.sendButton.isEnabled = false
self.inputContainerView.sendButton.alpha = 0.8
}
override func viewDidLayoutSubviews() {
inputContainerView.inputTextField.roundCorners([.topLeft,.bottomLeft], radius: 10)
inputContainerView.backgroundSendButtonView.roundCorners([.topRight,.bottomRight], radius: 22)
}
lazy var inputContainerView: ChatInputContainerView = {
let chatInputContainerView = ChatInputContainerView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 54))
chatInputContainerView.chatLogController = self
return chatInputContainerView
}()
func handleParamsMessage() {
print("params")
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let detailMessage = storyboard.instantiateViewController(withIdentifier: "MessageDetailTableViewController") as! MessageDetailTableViewController
if let user = user {
detailMessage.userId = user.id!
self.navigationController?.pushViewController(detailMessage, animated: true)
}
}
func handleUploadTap() {
let imagePickerController = UIImagePickerController()
imagePickerController.allowsEditing = true
imagePickerController.delegate = self
//imagePickerController.mediaTypes = [kUTTypeImage as String, kUTTypeMovie as String]
present(imagePickerController, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
// if let videoUrl = info[UIImagePickerControllerMediaURL] as? URL {
// //we selected a video
// handleVideoSelectedForUrl(videoUrl)
// } else {
// //we selected an image
handleImageSelectedForInfo(info as [String : AnyObject])
// }
dismiss(animated: true, completion: nil)
}
fileprivate func handleImageSelectedForInfo(_ info: [String: AnyObject]) {
var selectedImageFromPicker: UIImage?
if let editedImage = info["UIImagePickerControllerEditedImage"] as? UIImage {
selectedImageFromPicker = editedImage
} else if let originalImage = info["UIImagePickerControllerOriginalImage"] as? UIImage {
selectedImageFromPicker = originalImage
}
if let selectedImage = selectedImageFromPicker {
HelperService.uploadMessagePictureToDatabase(selectedImage, completion: { (imageUrl) in
self.sendMessageWithImageUrl(imageUrl, image: selectedImage)
})
}
}
override var inputAccessoryView: UIView? {
get {
return inputContainerView
}
}
override var canBecomeFirstResponder : Bool {
return true
}
func setupKeyboardObservers() {
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardDidShow), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
NotificationCenter.default.removeObserver(self)
}
func handleKeyboardDidShow() {
if messages.count > 0 {
let indexPath = IndexPath(item: messages.count - 1, section: 0)
collectionView?.scrollToItem(at: indexPath, at: .top, animated: true)
}
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return messages.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! ChatMessageCell
cell.chatLogController = self
let message = messages[indexPath.item]
cell.textView.text = message.text
setupCell(cell, message: message)
//lets modify the bubbleView's width somehow???
// cell.bubbleWidthAnchor?.constant = estimateFrameForText(message.text!).width + 25
if let text = message.text {
//a text message
cell.bubbleWidthAnchor?.constant = estimateFrameForText(text).width + 25
cell.textView.isHidden = false
} else if message.imageUrl != nil {
//fall in here if its an image message
cell.bubbleWidthAnchor?.constant = 200
cell.textView.isHidden = true
}
return cell
}
fileprivate func setupCell(_ cell: ChatMessageCell, message: Message) {
if let profileImageUrl = self.user?.profileImageUrl {
let photoUrl = URL(string: profileImageUrl)
cell.profileImageView.sd_setImage(with: photoUrl)
}
if message.fromId == Api.User.CURRENT_USER?.uid {
//outgoing blue
cell.bubbleView.backgroundColor = ChatMessageCell.blueColor
cell.textView.textColor = UIColor.white
cell.profileImageView.isHidden = true
cell.tailImageView.isHidden = true
cell.bubbleViewRightAnchor?.isActive = true
cell.bubbleViewLeftAnchor?.isActive = false
} else {
//incoming gray
cell.bubbleView.backgroundColor = UIColor(red: 243/255, green: 243/255, blue: 243/255, alpha: 1)
cell.textView.textColor = UIColor(red: 70/255, green: 70/255, blue: 70/255, alpha: 1)
cell.profileImageView.isHidden = false
cell.tailImageView.isHidden = false
cell.bubbleViewRightAnchor?.isActive = false
cell.bubbleViewLeftAnchor?.isActive = true
}
if let messageImageUrl = message.imageUrl {
let photoUrl = URL(string: messageImageUrl)
cell.messageImageView.sd_setImage(with: photoUrl)
cell.messageImageView.isHidden = false
// cell.bubbleView.backgroundColor = UIColor(red: 243/255, green: 243/255, blue: 243/255, alpha: 1)
} else {
cell.messageImageView.isHidden = true
}
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
collectionView?.collectionViewLayout.invalidateLayout()
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
var height: CGFloat = 80
let message = messages[indexPath.item]
if let text = message.text {
height = estimateFrameForText(text).height + 18
} else if let imageWidth = message.imageWidth?.floatValue, let imageHeight = message.imageHeight?.floatValue {
// h1 / w1 = h2 / w2
// solve for h1
// h1 = h2 / w2 * w1
height = CGFloat(imageHeight / imageWidth * 200)
}
let width = UIScreen.main.bounds.width
return CGSize(width: width, height: height)
}
fileprivate func estimateFrameForText(_ text: String) -> CGRect {
let size = CGSize(width: 200, height: 1000)
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
return NSString(string: text).boundingRect(with: size, options: options, attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 15, weight: .medium)], context: nil)
}
var containerViewBottomAnchor: NSLayoutConstraint?
func handleSend() {
self.inputContainerView.sendButton.isEnabled = false
let properties = ["text": inputContainerView.inputTextField.text!]
sendMessageWithPropertiesFIR(properties as [String : AnyObject])
}
fileprivate func sendMessageWithImageUrl(_ imageUrl: String, image: UIImage) {
let properties: [String: AnyObject] = ["imageUrl": imageUrl as AnyObject, "imageWidth": image.size.width as AnyObject, "imageHeight": image.size.height as AnyObject]
sendMessageWithPropertiesFIR(properties)
}
func sendMessageWithPropertiesFIR(_ properties: [String: AnyObject]) {
print(properties["text"])
var messageText = ""
if properties["text"] != nil {
messageText = properties["text"] as! String
} else {
messageText = "A envoyé une photo"
}
Api.Message.sendMessageWithProperties(toId: user!.id!, properties: properties) {
Api.Message.isUserMuted(userId: self.user!.id!, completion: { (isMuted) in
if !isMuted {
Api.UserToken.observeUserToken(withUser: self.user!.id!, completion: { (token) in
if let token = token {
Api.User.observeCurrentUser(completion: { (user) in
Api.Notification.sendNotifPush(token: token, message: "\(user.username!): \(messageText)")
})
}
})
}
})
self.emptyTextField()
}
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
handleSend()
return true
}
var startingFrame: CGRect?
var blackBackgroundView: UIView?
var startingImageView: UIImageView?
//my custom zooming logic
func performZoomInForStartingImageView(_ startingImageView: UIImageView) {
self.startingImageView = startingImageView
self.startingImageView?.isHidden = true
self.inputContainerView.inputTextField.resignFirstResponder()
startingFrame = startingImageView.superview?.convert(startingImageView.frame, to: nil)
let zoomingImageView = UIImageView(frame: startingFrame!)
zoomingImageView.backgroundColor = UIColor.red
zoomingImageView.image = startingImageView.image
zoomingImageView.isUserInteractionEnabled = true
zoomingImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleZoomOut)))
if let keyWindow = UIApplication.shared.keyWindow {
blackBackgroundView = UIView(frame: keyWindow.frame)
blackBackgroundView?.backgroundColor = UIColor.black
blackBackgroundView?.alpha = 0
keyWindow.addSubview(blackBackgroundView!)
keyWindow.addSubview(zoomingImageView)
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
// self.inputContainerView.inputTextField.resignFirstResponder()
self.blackBackgroundView?.alpha = 1
self.inputContainerView.alpha = 0
// math?
// h2 / w1 = h1 / w1
// h2 = h1 / w1 * w1
let height = self.startingFrame!.height / self.startingFrame!.width * keyWindow.frame.width
zoomingImageView.frame = CGRect(x: 0, y: 0, width: keyWindow.frame.width, height: height)
zoomingImageView.center = keyWindow.center
}, completion: { (completed) in
// do nothing
})
}
}
func handleZoomOut(_ tapGesture: UITapGestureRecognizer) {
if let zoomOutImageView = tapGesture.view {
//need to animate back out to controller
zoomOutImageView.layer.cornerRadius = 8
zoomOutImageView.clipsToBounds = true
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.startingFrame = self.startingImageView?.superview?.convert((self.startingImageView?.frame)!, to: nil)
zoomOutImageView.frame = self.startingFrame!
self.blackBackgroundView?.alpha = 0
self.inputContainerView.alpha = 1
}, completion: { (completed) in
zoomOutImageView.removeFromSuperview()
self.startingImageView?.isHidden = false
})
}
}
}
The View:
import UIKit
class ChatInputContainerView: UIView, UITextFieldDelegate {
weak var chatLogController: ChatLogController? {
didSet {
sendButton.addTarget(chatLogController, action: #selector(ChatLogController.handleSend), for: .touchUpInside)
uploadImageView.addGestureRecognizer(UITapGestureRecognizer(target: chatLogController, action: #selector(ChatLogController.handleUploadTap)))
inputTextField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
}
}
let inputColor = UIColor(red: 243/255, green: 243/255, blue: 243/255, alpha: 1)
lazy var inputTextField: UITextField = {
let textField = UITextField()
textField.placeholder = "Entrer un message..."
textField.translatesAutoresizingMaskIntoConstraints = false
textField.delegate = self
textField.backgroundColor = inputColor
// textField.roundCorners([.topLeft,.bottomLeft], radius: 10)
textField.clipsToBounds = true
return textField
}()
let uploadImageView: UIImageView = {
let uploadImageView = UIImageView()
uploadImageView.isUserInteractionEnabled = true
uploadImageView.image = UIImage(named: "pinImage")
uploadImageView.translatesAutoresizingMaskIntoConstraints = false
return uploadImageView
}()
lazy var backgroundSendButtonView: UIView = {
let backgroundSendButtonView = UIView()
backgroundSendButtonView.backgroundColor = inputColor
backgroundSendButtonView.translatesAutoresizingMaskIntoConstraints = false
return backgroundSendButtonView
}()
let sendButton = UIButton(type: .system)
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .red
addSubview(uploadImageView)
// sendButton.setTitle("Send", for: UIControlState())
sendButton.setImage(UIImage(named: "planeChat"), for: .normal)
sendButton.backgroundColor = UIColor.white
sendButton.tintColor = UIColor(red: 82/255, green: 121/255, blue: 179/255, alpha: 1)
sendButton.layer.cornerRadius = 20
sendButton.clipsToBounds = true
sendButton.translatesAutoresizingMaskIntoConstraints = false
//what is handleSend?
addSubview(sendButton)
addSubview(self.inputTextField)
//x,y,w,h
// A enlever après
self.inputTextField.leftAnchor.constraint(equalTo: uploadImageView.rightAnchor, constant: 12).isActive = true
//self.inputTextField.leftAnchor.constraint(equalTo: leftAnchor, constant: 8).isActive = true
//self.inputTextField.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
self.inputTextField.topAnchor.constraint(equalTo: topAnchor, constant: 4).isActive = true
self.inputTextField.rightAnchor.constraint(equalTo: sendButton.leftAnchor, constant: -4).isActive = true
self.inputTextField.heightAnchor.constraint(equalToConstant: 48).isActive = true
//x,y,w,h
sendButton.rightAnchor.constraint(equalTo: rightAnchor, constant: -8).isActive = true
sendButton.centerYAnchor.constraint(equalTo: inputTextField.centerYAnchor).isActive = true
sendButton.widthAnchor.constraint(equalToConstant: 38).isActive = true
sendButton.heightAnchor.constraint(equalToConstant: 38).isActive = true
//x,y,w,h
uploadImageView.leftAnchor.constraint(equalTo: leftAnchor, constant: 18).isActive = true
uploadImageView.centerYAnchor.constraint(equalTo: inputTextField.centerYAnchor).isActive = true
uploadImageView.widthAnchor.constraint(equalToConstant: 18).isActive = true
uploadImageView.heightAnchor.constraint(equalToConstant: 20).isActive = true
//l//et backgroundSendButtonView = UIView()
//addSubview(backgroundSendButtonView)
// backgroundSendButtonView.roundCorners([.topRight,.bottomRight], radius: 24)
insertSubview(backgroundSendButtonView, belowSubview: sendButton)
backgroundSendButtonView.rightAnchor.constraint(equalTo: rightAnchor, constant: -4).isActive = true
backgroundSendButtonView.centerYAnchor.constraint(equalTo: inputTextField.centerYAnchor).isActive = true
//backgroundSendButtonView.widthAnchor.constraint(equalToConstant: 30).isActive = true
backgroundSendButtonView.leftAnchor.constraint(equalTo: inputTextField.rightAnchor).isActive = true
backgroundSendButtonView.heightAnchor.constraint(equalTo: inputTextField.heightAnchor).isActive = true
//x,y,w,h
// let separatorLineView = UIView()
// separatorLineView.backgroundColor = UIColor(red: 220/255, green: 220/255, blue: 220/255, alpha: 1)
// separatorLineView.translatesAutoresizingMaskIntoConstraints = false
// addSubview(separatorLineView)
// //x,y,w,h
// separatorLineView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
// separatorLineView.topAnchor.constraint(equalTo: topAnchor).isActive = true
// separatorLineView.widthAnchor.constraint(equalTo: widthAnchor).isActive = true
// separatorLineView.heightAnchor.constraint(equalToConstant: 1).isActive = true
let gradientView = UIView()
let colorTop = UIColor.clear.cgColor
let colorBottom = UIColor(red: 0, green: 0, blue: 0, alpha: 0.05).cgColor
gradientView.translatesAutoresizingMaskIntoConstraints = false
addSubview(gradientView)
//x,y,w,h
gradientView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
gradientView.topAnchor.constraint(equalTo: topAnchor, constant: -25).isActive = true
gradientView.widthAnchor.constraint(equalTo: widthAnchor).isActive = true
gradientView.heightAnchor.constraint(equalToConstant: 25).isActive = true
gradientView.backgroundColor = UIColor.clear
let gradientBackground = CAGradientLayer()
gradientBackground.colors = [ colorTop, colorBottom]
gradientBackground.locations = [0.0, 1.0]
var backgroundLayer = CALayer()
backgroundLayer = gradientBackground
let width = UIScreen.main.bounds.size.width
backgroundLayer.frame = CGRect(x: 0, y: 0, width: width, height: 25)
print(backgroundLayer.frame)
print(gradientView.bounds)
gradientView.layer.insertSublayer(backgroundLayer, at: 0)
}
func setGradient(_ view: UIView, colorTop: CGColor, colorBottom: CGColor) {
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
chatLogController?.handleSend()
return true
}
#objc func textFieldDidChange(_ textField: UITextField) {
if textField == self.inputTextField {
if self.inputTextField.text!.isEmpty {
disableButton()
} else {
// sendButton.setTitleColor(typoGreyButton, for: .normal)
self.sendButton.isEnabled = true
self.sendButton.alpha = 1
}
}
}
func disableButton(){
//sendButton.setTitleColor(smoothGray, for: .normal)
sendButton.isEnabled = false
self.sendButton.alpha = 0.8
}
func emptyTextField() {
self.inputTextField.text = ""
disableButton()
}
// func textViewDidChange(_ textView: UITextView) {
// print(textView)
// if textView == self.inputContainerView.inputTextField {
// if (self.inputContainerView.inputTextField.text?.isEmpty)! {
// disableButton()
// } else {
// // sendButton.setTitleColor(typoGreyButton, for: .normal)
// self.inputContainerView.sendButton.isEnabled = true
// }
// }
//
// }
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
If you just want to anchor to the bottom safe area, you can do that anywhere in your view controller:
if #available(iOS 11.0, *) {
someView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
} else {
someView.bottomAnchor.constraint(equalTo: bottomLayoutGuide.topAnchor).isActive = true
}
If, however, you want to use it as a constant (i.e. subtract the length of the bottom safe area from a value), you need to do that in a later lifecycle method like viewDidLayoutSubviews():
if #available(iOS 11.0, *) {
someView.bottomAnchor.constraint(equalTo: anotherView.bottomAnchor, constant: -view.safeAreaInsets.bottom).isActive = true
} else {
someView.bottomAnchor.constraint(equalTo: anotherView.bottomAnchor, constant: -bottomLayoutGuide.length).isActive = true
}
iOS 11 revamped their safe area API so make sure that you support pre-iOS-11 devices as I did in these examples.
I also just noticed that you've set the view's frame explicitly in its intializer. You typically don't want to set a view's frame like that if you're using auto layout (constraints). Therefore, I would suggest not setting the view's frame like you did and instead using constraints to do it.
I am creating a dynamic uilabel in CellforRowat for particuklar index i.e indexpath.row=0
But When I Scroll down I am seeing that UI Label on 4th Index as well ....
Why is it coming on 3rd index if I have already added a condition in Cellforroat as:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// var cell:ActivityMainTableViewCell?
if(CheckFlag == false){
//CheckFlag = true
}
else{
CheckFlag = false
}
// cell = tableView.dequeueReusableCell(withIdentifier: "Cell")! as? ActivityMainTableViewCell
let cell:ActivityMainTableViewCell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! ActivityMainTableViewCell
/// let cell = tableView.cellForRow(at: indexPath) as! ActivityMainTableViewCell
///let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ActivityMainTableViewCell
/// cell.viewheight.constant = 0
cell.imgViewFirst.layer.cornerRadius = (cell.imgViewFirst.frame.size.width) / 2
cell.imgViewFirst.layer.borderWidth = 3.0
cell.imgViewFirst.layer.borderColor = UIColor.white.cgColor
cell.imgViewFirst.clipsToBounds = true
cell.ImageViewThird.layer.cornerRadius = (cell.ImageViewThird.frame.size.width) / 2
cell.ImageViewThird.layer.borderWidth = 3.0
cell.ImageViewThird.layer.borderColor = UIColor.white.cgColor
cell.ImageViewThird.clipsToBounds = true
cell.ImageViewSecond.layer.cornerRadius = (cell.ImageViewSecond.frame.size.width) / 2
cell.ImageViewSecond.layer.borderWidth = 3.0
cell.ImageViewSecond.layer.borderColor = UIColor.white.cgColor
cell.ImageViewSecond.clipsToBounds = true
print(indexPath)
print(indexPath.row)
print(dicArray)
cell.DateOfMeeting.text = dicArray[indexPath.row]["Date"] as? String
cell.CompanyInMeetiong.text = dicArray[indexPath.row]["Company"] as? String
cell.lblNews.text = dicArray[indexPath.row]["News"] as? String
cell.lblCommonKey.text = dicArray[indexPath.row]["CommonKey"] as? String
print(SectordicArray)
cell.imgViewFirst.isHidden = true
cell.lblCommonKey.isHidden = true
cell.btnOthers.tag = indexPath.row
//for i in 0..<section.count {
print(cell.lblCommonKey.text)
print(indexPath.row)
RowHeight = 110.0
CellHeight = 140.0
isFirstTime = true
// print(AllCompanydicArray)
if(indexPath.row == 0){
let Data = AllCompanydicArray[indexPath.row]
print(Data)
for i in 0..<Data.count {
print(Data[i])
if(isFirstTime == false){
RowHeight = RowHeight + 30.0
CellHeight = CellHeight + 30.0
}
else{
isFirstTime = false
}
let Company = (Data[i] as NSDictionary).value(forKey: "Company") as! [String]
let Contacts = (Data[i] as NSDictionary).value(forKey: "Participants") as! [String]
// section.append(Company)
//items.append(Contacts)
for k in 0..<Company.count {
let newLabel = UILabel(frame: CGRect(x: 10.0, y: RowHeight, width:cell.frame.width, height: 30.0))
newLabel.text = Company[k]
newLabel.font = UIFont(name:"HelveticaNeue-Bold", size: 13)
cell.addSubview(newLabel)
}
for j in 0..<Contacts.count {
RowHeight = RowHeight + 30.0
CellHeight = CellHeight + 30.0
var
newLabel = UILabel(frame: CGRect(x: 10.0, y: RowHeight, width:30, height: 30.0))
newLabel.text = CaptionName(Name: Contacts[j])
//"RC"
newLabel.font = UIFont(name:"HelveticaNeue-Bold", size: 11)
newLabel.textAlignment = .center
newLabel.backgroundColor = getColorByHex(rgbHexValue: 0x002366, alpha: 0.9)// UIColor(red: 0.0, green: 14.0, blue: 40.0, alpha: 100.0)
newLabel.layer.cornerRadius = (newLabel.frame.size.width) / 2
newLabel.layer.borderWidth = 1.0
newLabel.clipsToBounds = true
newLabel.textColor = UIColor.white
newLabel.layer.borderColor = UIColor.white.cgColor
cell.addSubview(newLabel)
newLabel = UILabel(frame: CGRect(x: 50, y: RowHeight, width:cell.frame.width-30, height: 30.0))
newLabel.text = Contacts[j]//"Raghav Chopra"
newLabel.font = UIFont(name:"HelveticaNeue", size: 12)
cell.addSubview(newLabel)
}
}
}
else{
}
return cell
}
Please let me know how can i avoid uilabel on 4th index.