the text must be red when the variable beats == "true"
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! InstallmentTableViewCell
if self.switchInstallmentToPay == true {
if let installment = PaymentManager.paymentPlan?.unpaidInstallments![indexPath.row] {
if let id = installment.id, let paymentDue = installment.paymentDue, let description = installment.numberDescription, let method = installment.paymentMethodDescription, let expectedPayment = installment.expectedPayment, let actualPayment = installment.actualPayment, let payable = installment.payable, let late = installment.late {
cell.load(id: id, paymentDue: paymentDue, description: description, method: method, expectedPayment: expectedPayment, actualPayment: actualPayment, payable: payable, late: late)
if installment.payable! {
cell.accessoryType = .checkmark
cell.tintColor = UIColor.lighterGray
cell.isUserInteractionEnabled = true
if installment.late! {
cell.lbDescription.textColor = UIColor.danger // not working
}
}else{
cell.accessoryType = .none
//cell.tintColor = UIColor.lightGray
cell.isUserInteractionEnabled = false
}
}
}
}else{
if let installment = PaymentManager.paymentPlan?.paidInstallments![indexPath.row] {
if let id = installment.id, let paymentDue = installment.paymentDue, let description = installment.numberDescription, let method = installment.paymentMethodDescription, let expectedPayment = installment.expectedPayment, let actualPayment = installment.actualPayment, let payable = installment.payable, let late = installment.late {
cell.load(id: id, paymentDue: paymentDue, description: description, method: method, expectedPayment: expectedPayment, actualPayment: actualPayment, payable: payable, late: late)
cell.accessoryType = .none
cell.isUserInteractionEnabled = false
cell.lbDescription.textColor = UIColor.black // not working
cell.tintColor = UIColor.lighterGray
}
}
}
return cell
}
This code is difficult to read, and there's a lot of redundancy. If you're using a storyboard, I suggest making separate dynamic cells for the paid and unpaid installments. Both cells' class type can stay InstallmentTableViewCell, as you're just duplicating the cells' views, not their logic. The various elements' colors & styles can be set right in the storyboard's cell prototype, and then your tableView(_:cellForRowAt:indexPath) can be simplified to just
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellID = switchInstallmentToPay ? "unpaidCell" : "paidCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath) as! InstallmentTableViewCell
cell.load(...)
return cell
}
I would also recommend changing cell.load() to take an installment argument and setting the cells' properties there instead of cluttering the caller with multiple if lets.
Related
I don't understand how to link the selection of a set of rating stars to each cell and save this value?
Cosmo lib: https://github.com/evgenyneu/Cosmos
My Code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCellTableViewCell
let currentNameItem = gameNameArray[indexPath.row]
cell.gameNameLabel?.text = currentNameItem["Name"] as? String
// MARK: - переменная из словаря - если true ставим галочку - если нет убираем
if (currentNameItem["isCompleted"] as? Bool) == true {
cell.accessoryType = .checkmark
} else {
cell.accessoryType = .none
}
let currentSubNameItem = gameSubNameArray[indexPath.row]
cell.subGameNameLabel?.text = currentSubNameItem["Name"] as? String
let currentScoreItem = gameScoreArray[indexPath.row]
cell.gameScoreValue?.text = currentScoreItem["Name"] as? String
let currentImageItem = gameImageArray[indexPath.row]
guard let url = URL(string: currentImageItem["Name"] as! String) else { return cell }
cell.gameImage.sd_setImage(with: url, completed: nil)
//I can set the rating value
cell.fiveStarRaiting.rating = 5
let currentRaitingItem = raitingArray[indexPath.row] //array where I would like to save the ratings
cell.fiveStarRaiting.rating = currentRaitingItem //here, the values from the rating array should be pulled up by the ide
//allows you to save the rating value at the end of the finger movement gesture
cell.fiveStarRaiting.didFinishTouchingCosmos = { [self] raiting in raitingArray.append(raitingStarValue)}
print(raitingArray.count)
//cell.testButton0.addTarget(self, action: #selector(testFuncButton(sender:)), for: .touchUpInside)
return cell
}
As you know, table view cells are reusable, so when you scrolls and when cells are disappeared then its values are also cleared that's how table view and collection view works.
So if you want to save those ratings then you can go with a local temporary dictionary.
e.g.
var ratingsArray = [Int:Float]()
store your indexpath as key in "ratingsArray" dictionary and set its value as cosmos ratings.
and set cosmos ratings values as prefill in "cellForRowAt" table view method,
if ratingsArray.keys.contains(indexPath.row) {
cell.fiveStarRaiting.rating = 5
}
else {
cell.fiveStarRaiting.rating = 0 // set starts default value here
}
cell.fiveStarRaiting.didFinishTouchingCosmos = { [self] rating in
ratingsArray[indexPath.row] = rating
}
I've got problems when I scroll down in my UITableview. The table shows me cells with old content when the cell is reused.
The Probleme is the following:
Swift wants to reuse an old cell, but doesn't properly clear the old content from the old cell. This leads to cells with old content, although I'm providing new data to the cells.
Architecture of the UITableView if the following:
Each custom cell has their own identifier
Each custom cell is separated in an own class
Screenshots of the problem:
Beginning of the Questionnaire Screen Shot:
The scrolled down table:
The problem here is the "Handedness"-Cell which is showing the cell number 3 (because of the reuse of the cell), which is not right
The numberOfSection-Method
override func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
The numberOfRowsInSection-Method
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(section == 0){
return questionnaireStructure.count
} else {
return 1
}
}
The cellForRowAt-Method
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// first section is the normal Questionnaire
if(indexPath.section == 0){
// current questionnaireStructure
let questStruct:QuestionnaireStructure? = questionnaireStructure[indexPath.row]
// current cell is a "Headline"
if(questStruct?.elementtype == "elements/headlines"){
let cell = tableView.dequeueReusableCell(withIdentifier: "HeadlineStructureCellID", for: indexPath) as! Headline
cell.headline.text = questStruct?.headline
cell.selectionStyle = UITableViewCellSelectionStyle.none
return cell
} else if(questStruct?.elementtype == "elements/texts"){
// current cell is a "texts"
let cell = tableView.dequeueReusableCell(withIdentifier: "TextsStructureCellID", for: indexPath) as! Texts
cell.textsLabel.text = questStruct?.text
cell.selectionStyle = UITableViewCellSelectionStyle.none
return cell
} else if(questStruct?.questiontype == "Slider"){
// currrent cell is a "slider-Question"
let cell = tableView.dequeueReusableCell(withIdentifier: "QuestionSliderStructureCellID", for: indexPath) as! Slider
cell.sliderQuestion.text = questStruct?.question
cell.selectionStyle = UITableViewCellSelectionStyle.none
let values = (questStruct?.values)!
let valueArray = values.array as! [Values]
cell.slider.minimumValue = Float(valueArray[0].min)
cell.slider.maximumValue = Float(valueArray[0].max)
let answers = (questStruct?.answers)!
let answerArray = answers.array as! [Answers]
cell.minLabel.text = answerArray[0].label
cell.maxLabel.text = answerArray[1].label
return cell
} else if(questStruct?.questiontype == "SingleChoice"){
let cell = tableView.dequeueReusableCell(withIdentifier: "QuestionSingleChoiceStructureCellID", for: indexPath) as! SingleChoiceCell
let radioButtonController = SSRadioButtonsController()
radioButtonController.delegate = self
radioButtonController.shouldLetDeSelect = true
cell.radioButtonController = radioButtonController
cell.updateCellData(questStruct: questStruct!, indexInTable: indexPath.row)
return cell
} else if(questStruct?.questiontype == "MultipleChoice"){
let cell = tableView.dequeueReusableCell(withIdentifier: "QuestionMultipleChoiceStructureCellID", for: indexPath) as! MultipleChoiceCell
cell.multQuestionLabel.text = questStruct?.question
cell.questStruct = questStruct
return cell
} else if(questStruct?.questiontype == "YesNoSwitch"){
let cell = tableView.dequeueReusableCell(withIdentifier: "QuestionYesNoSwitchStructureCellID", for: indexPath) as! YesNoSwitch
cell.yesNoQuestion.text = questStruct?.question
cell.selectionStyle = UITableViewCellSelectionStyle.none
return cell
} else if(questStruct?.questiontype == "TextDate"){
let cell = tableView.dequeueReusableCell(withIdentifier: "Datepicker", for: indexPath) as! DatePicker
cell.question.text = questStruct?.question
cell.selectionStyle = UITableViewCellSelectionStyle.none
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "QuestionSingleChoiceStructureCellID", for: indexPath) as! SingleChoiceCell
//cell.singleChoiceLabel.text = questStruct?.question
cell.selectionStyle = UITableViewCellSelectionStyle.none
return cell
}
} else {
//last section is the save button
// show the save button when the Questionnaire is loaded
if(questionnaireStructure.count != 0){
let cell = tableView.dequeueReusableCell(withIdentifier: "SaveStructureCellID", for: indexPath) as! SaveQuestionnaire
cell.selectionStyle = UITableViewCellSelectionStyle.none
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "TextsStructureCellID", for: indexPath) as! Texts
cell.selectionStyle = UITableViewCellSelectionStyle.none
return cell
}
}
}
What I checked:
the data of "questStruct" is providing the latest data
overriding the "prepareForReuse"-Methode without success
Here:
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "QuestionSingleChoiceStructureCellID", for: indexPath) as! SingleChoiceCell
//cell.singleChoiceLabel.text = questStruct?.question
cell.selectionStyle = UITableViewCellSelectionStyle.none
return cell
}
You need to "reset" the cell in case it's being reused. Options are:
write a reset() function in the cell, to clear any assigned data and display "default" content, or
create an empty questStruct and call cell.updateCellData(questStruct: questStruct!, indexInTable: indexPath.row)
Option 1. is probably the easiest and most straight-forward.
Are you sure the data isn't actually duplicated in the questStruct array? If that's not the case then all I can think is that it looks like you have two places where a single choice cell is used. In one of them you set a bunch of data, while in the other one you don't seem to set any data. I'm talking about that last else statement where you have the part where you set singleChoiceLabel.text except it's commented out. If that condition gets hit and it's reusing a cell that was configured for the other singleChoiceStructure branch of the if condition then the information will still be filled out from the previous configuration. It's possible the questionType property of one of your QuestionnaireStructure objects is either spelled incorrectly or just a value you haven't accounted for, which is causing the if statement to hit the else which returns an unconfigured QuestionSingleChoice cell that might still have information from the last time it was used.
I am new to swift . i am doing my project programatically and I load data from api to the tableView and tableView like ios setting page ..
now i need all rows information when click "Add to cart" button. How can i do it?
here is my code sample :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.section {
case 0:
let cell = tableView.dequeueReusableCell(withIdentifier: cartHeaderCell, for: indexPath) as! CartHeaderCell
cell.configureCell(indexPath.item)
return cell
case 1:
let obj = data?[indexPath.row]
var cell = UITableViewCell()
switch obj {
case is Addon:
cell = tableView.dequeueReusableCell(withIdentifier: addonCell, for: indexPath) as! AddonCell
let switchView = UISwitch(frame: .zero)
switchView.setOn(false, animated: true)
cell.accessoryView = switchView
guard let addon = obj as? Addon else {
return cell
}
cell.textLabel?.text = "\(addon.name) + €\(addon.price)"
case is AddonGroup:
cell = tableView.dequeueReusableCell(withIdentifier: addonGroupCell, for: indexPath) as! AddonGroupCell
cell.accessoryType = UITableViewCellAccessoryType.disclosureIndicator
guard let addonGroup = obj as? AddonGroup else {
return cell
}
if let addons = addonGroup.addonList {
cell.detailTextLabel?.text = ""
var selectedAddons = ""
for _addon in addons
{
if _addon.isSelect == true {
selectedAddons = selectedAddons + "\(_addon.name)"
}
}
cell.detailTextLabel?.text = selectedAddons
}
cell.textLabel?.text = addonGroup.name
...........................................
As Fahim was mentioning, you need to set up a data model that records that status of each cell before / during / after the user interaction with each cell. So when the cell goes off screen and then comes back on, it will be presented with the correct state of the model.
Secondly, for the UISwitchViews, you should be instantiating and adding those to the contentView within each cell in order to keep the cellForRow function clean and problem free. The reason leads me into my next point: how to record the status of each UISwitchView after the user has interacted with a UISwitchView. You are going to want to create a protocol and add a delegate within the UICollectionViewCell(that inherits class and the delegate should be a weak var), in order to update the model whenever the UISwitch is tapped.
If you have any more questions i can do my best to help!
I need to get the first cell in my tableView to be a different size from the rest. The rest of my cells are all under the class CustomPFTableViewCell, but the first one is a different cell so its under the class FirstPFTableViewCell, both of which extend from the class PFTableViewCell. Right now, I just used an if depending on the indexPath.row for whether or not the cell was the first cell. When its not it will load data for the cell from Parse.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell {
if(indexPath.row >= 1){
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! CustomPFTableViewCell!
print("Loading Parse Database Files...")
// Extract values from the PFObject to display in the table cell
if let name = object?["Name"] as? String {
cell?.nameTextLabel?.text = name
print("Loading " + name)
}
if let author = object?["authorName"] as? String {
cell?.authorTextLabel?.text = author
}
if let likes = object?["Likes"] as? Int {
let stringVal = String(likes)
cell?.numLikes.text = stringVal
}
if let descrip = object?["Description"] as? String {
cell?.descriptionHolder = descrip
}
let initialThumbnail = UIImage(named: "Unloaded")
cell.customFlag.image = initialThumbnail
if let thumbnail = object?["imageCover"] as? PFFile {
cell.customFlag.file = thumbnail
cell.customFlag.loadInBackground()
}
return cell
}
print("Finished loading!")
let cell = tableView.dequeueReusableCellWithIdentifier("firstCell") as! PFTableViewCell
return cell
}
The end is empty because I'm not sure how to go about changing the one/first cell's size. (In the Interface Builder its set to 60). I guess the most important part in solving this is this:
let cell = tableView.dequeueReusableCellWithIdentifier("firstCell") as! PFTableViewCell
return cell
}
In order to play with the size of the cell you have to implement the UITableViewDelegate function
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.row == 0 {
return firstCellHeight
} else {
return customCellHeight
}
As the title suggests, I am having trouble with my UISearchController displaying the wrong cell prototype for the first cell in the search results.
Background Information: I have two cell prototypes, one without an image (identifier: basicCell) and another with a UIImageView (identifier: imageCell). Cells work perfectly when not searching.
Detailed Description of the Problem: When I click on the search bar everything is fine until I start searching for something. When I do, the first cell always has the imageCell identifier (a gray empty image view is shown denoting the lack of an image), no matter what. NB: Before searching anything, the first cell in the tableview has a custom image... Maybe that's of note?
Anyway I have no idea what I am doing wrong. Would anyone mind helping?
Code:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if (self.resultSearchController.active) {
if hasImageAtIndexPath(indexPath) {
let cell = tableView.dequeueReusableCellWithIdentifier(imageCellIdentifier, forIndexPath: indexPath) as! TimelineTableViewCellImage
let event = filteredTableData[indexPath.row]
cell.content.text = profile.content
cell.name.text = profile.name
//This is the image
cell.attachment.image = profile.image
cell.attachment.layer.cornerRadius = 1
cell.attachment.clipsToBounds = true
return cell
} else {
let cell = tableView.dequeueReusableCellWithIdentifier(basicCellIdentifier, forIndexPath: indexPath) as! TimelineTableViewCell
let event = filteredTableData[indexPath.row]
cell.content.text = profile.content
cell.name.text = profile.name
return cell
}
} else {
if hasImageAtIndexPath(indexPath) {
let cell = tableView.dequeueReusableCellWithIdentifier(imageCellIdentifier, forIndexPath: indexPath) as! TimelineTableViewCellImage
let event = events[indexPath.row]
cell.content.text = profile.content
cell.name.text = profile.name
cell.attachement.image = profile.image
cell.attachment.layer.cornerRadius = 1
cell.attachment.clipsToBounds = true
return cell
} else {
let cell = tableView.dequeueReusableCellWithIdentifier(basicCellIdentifier, forIndexPath: indexPath) as! TimelineTableViewCell
let event = events[indexPath.row]
cell.content.text = profile.content
cell.name.text = profile.name
return cell
}
}
}
And this is my code that checks for an image:
func hasImageAtIndexPath(indexPath:NSIndexPath) -> Bool {
let event = events[indexPath.row]
let imageArray = [event.image]
for eventImage in imageArray {
if eventImage != nil {
return true
}
}
return false
}
You need to have an if-else clause in your hasImageAtIndexPath: function just like you have in your cellForRowAtIndexPath:. If the table view is the search table, then event needs to be defined the same way as you have in cellForRowAtIndexPath:,
func hasImageAtIndexPath(indexPath:NSIndexPath sender:UITableView) -> Bool
if (self.resultSearchController.active){
let event = filteredTableData[indexPath.row]
}else{
let event = events[indexPath.row]
}
let imageArray = [event.image]
for eventImage in imageArray {
if eventImage != nil {
return true
}
}
return false
}