I am facing problem in UITableView. I want change the label color on some condition. But it does not change on first reload. But when I start scrolling the new reusable cell change the text color.
Here is my code
I tried setNeedDesplay() and layoutIfNeeded() but not working either.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! WalletHistoryCell
let item = array[indexPath.row]
let amountDouble = Float(item.amount ?? "0.0")
cell.labelAmount.text = String.init(format: Constants.AMOUNT_PLACEHOLDER, amountDouble!)
cell.labelDateTime.text = item.dateTime ?? ""
if !(item.details?.isEmpty)!{
cell.labelReason.text = String.init(format: Constants.REASON_PLACEHOLDER, item.details!)
}
if item.debit!{
cell.labelName.text = item.toName ?? ""
cell.labelTo.text = item.toNum ?? ""
cell.labelAmount.textColor = UIColor.appDiscountColor
}else{
cell.labelName.text = item.fromName ?? ""
cell.labelTo.text = item.phoneNumber ?? ""
cell.labelAmount.textColor = UIColor.appHopOrbitColor
}
return cell
}
By default it must work correctly. I.e. the problem not in TableView or TableViewCell. The only reason I can suppose is that tableView.reloadData() calls earlier than array updates. You can recheck this by printing item in cellForRowAt method.
Related
Whenever I scroll in the table view, the cell label's text disappears
which means the tableview data disappears. I tried changing the switch statement to if statement and it still doesn't work. Is my function to change the date format the reason?
Here is my code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "requestsCell", for: indexPath) as! RequestsTableViewCell
let requesten = requestsList[indexPath.row]
switch requesten.type {
case "FLT" :
cell.requestType.text = "Flight"
let reqDateTimeDate = stringToDate(strdate: requesten.reqDate!)
cell.requestDate.text = "\(dateFormatterPrint.string(from: reqDateTimeDate))"
cell.fromDate.isHidden = true
cell.toDate.isHidden = true
case "DOF" :
cell.requestType.text = "Day Off"
let reqDateTimeDate = stringToDate(strdate: requesten.reqDate!)
cell.requestDate.text = "\(dateFormatterPrint.string(from: reqDateTimeDate))"
cell.fromDate.isHidden = true
cell.toDate.isHidden = true
case "VAC" :
cell.requestType.text = "Vacation"
let reqDateFrom = stringToDate(strdate: requesten.dateFrom!)
let reqDateTo = stringToDate(strdate: requesten.dateTo!)
cell.requestDate.isHidden = true
cell.toDate.text = "To: \(dateFormatterPrint.string(from: reqDateTo))"
cell.fromDate.text = "From: \(dateFormatterPrint.string(from: reqDateFrom))"
default:
cell.requestType.text = ""
}
return cell
}
You should set up your code better such that each and every case statement changes the same properties. Or you need to override the prepareForReuse method so that each time the cell is reset to a common state.
Looking at your code - every case statement should assign these properties:
requestType.text
requestDate.text
fromDate.isHidden
fromDate.text
toDate.isHidden
toDate.text
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.
I have a table view that has 100 rows. What I'd like to do is set an image for the row if the index of the row is in my array that I have. What I've found is that it only sets the image for the first number in the array. So, in this example, I have [2,3,4,5] in my array. The result is that it is setting the image for the row where index = 2. Rather than for all the numbers. Any help on this would be greatly appreciated!
var completedWorkoutsArray = [2,3,4,5]
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellReuseIdentifier")! //1.
let text = data[indexPath.row]
cell.textLabel?.text = text
for i in completedWorkoutsArray {
if(indexPath.row == i) {
let image: UIImage = checkmark!
cell.imageView!.image = image
}
else
{
let image: UIImage = redX!
cell.imageView!.image = image
}
}
return cell
}
You want something like this:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellReuseIdentifier")!
let text = data[indexPath.row]
cell.textLabel?.text = text
let image = completedWorkoutsArray.contains(indexPath.row) ? checkmark : redX
cell.imageView.image = image
return cell
}
Your code fails because it loops through all values in the array. It sets the image each and every time. The last image set is the one you see.
My JsonData -
let imagestring : String? = (myData as AnyObject).value(forKey: "Post_mid_image") as? String
if imagestring != nil {
let imageTrueString = "https://www.zdoof.com/" + imagestring!
self.imageStringArray.append(imageTrueString )
}
if let NameString = (myData as AnyObject).value(forKey: "Name") as? String {
self.nameStringArray.append(NameString)
}
When i am trying to set it to the table view cell
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.postLableArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reUse", for: indexPath)
let myImage = cell.viewWithTag(30) as! UIImageView
myImage.clipsToBounds = true
if indexPath.row < imageStringArray.count {
if let myImageString = imageStringArray[indexPath.row] as? String {
let ImageUrl = URL.init(string: myImageString)
myImage.kf.setImage(with: ImageUrl)
}
}
return cell
}
The image is repeating in every cell . Why it is happening ? Please help
As per the response you have given, you can show the image like below:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let dict = myData as AnyObject
let cell = tableView.dequeueReusableCell(withIdentifier: "reUse", for: indexPath)
let myImage = cell.viewWithTag(30) as! UIImageView
if dict["Post_mid_image"] != nil {
let imageUrl = URL.init(string: strImageUrl)
myImage.kf.setImage(with: imageUrl)
} else {
//Set placeholder image showing no image available
}
return cell
}
Problem is with cell re-usablity of table view here ,you have to handle it , you can have SDWebImage library for loading images in cell or you can have your own image cache which caches images with key/values , key as in image url , so dynamically checking image url for item at indexpath and load cached image with that image url as key.
Hope it helps!!
This is happening because of using tableView.dequeueReusableCell(withIdentifier: "reUse", for: indexPath).
Basically whenever you use dequeueReusableCell(withIdentifier:,For:), it will use the same cell for all of data. It means the total number of cell which are on screen are only going to load, for all other cell, it will use same cell with different value.
now consider a scenario that you are having 500 cells in tableview, but we can manage at most 10-15 cells in display, so for all other cells it will use same cells just modify the value of cell.
so what you can do here is, whenever you use if statement, don't forgot to add else too.
because for one scenario if cell's background is set to red, than we need to add else for another scenario, as cells are just repeated.
I am creating an app where it uses custom cells. I also have these UITextView's where if you input a word, that word should then go to one of the four labels I created in the custom cell. I am still coding it however I got an error saying "Error: Index Out of Range".
Here is the code, and I also commented where it is giving that error
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) ->
UITableViewCell{
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! TableView
cell.lbl.text = todolist[indexPath.row]
cell.lbl2.text = todolist2[indexPath.row] // This is the error code
cell.lbl3.text = todolist3[indexPath.row]
cell.lbl4.text = todolist4[indexPath.row]
return cell
}
Here is where I append my texts
#IBAction func ClickedforSelection(sender: AnyObject) {
todolist.append(txt.text!)
todolist2.append(txt1.text!)
todolist3.append(txt2.text!)
todolist4.append(txt3.text!)
self.view.endEditing(true)
txt.text = ""
txt1.text = ""
txt2.text = ""
txt3.text = ""
NSUserDefaults.standardUserDefaults().setObject(todolist, forKey: "list")
NSUserDefaults.standardUserDefaults().setObject(todolist2, forKey: "list2")
NSUserDefaults.standardUserDefaults().setObject(todolist3, forKey: "list3")
NSUserDefaults.standardUserDefaults().setObject(todolist4, forKey: "list4")
Here is my NumberofRowsInSection
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return todolist.count
}
I have a conjecture that it may be the reuse of indexPath.row. Any solution?
If numberOfRowsInSection is returning todolist.count, you are accessing todolist2 in your cell. If todolist has 2 items and todolist2 has 1 item, it will do this because you are trying to access an item in a list that doesn't exist. Put a breakpoint at the first call of cell.lbl.text and check each array (todolist, todolist1, etc...). You should see that todolist2 does not have have a record at whatever "row" it's calling. If that is the case, you should just test it prior to calling it. (verify todolist2.count has enough items in it - or better yet, change the code to not have 4 arrays tracking 1 row (convert to a struct of some type with all 4 values, or something similar).
First, change the following code by commenting out lines:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) ->
UITableViewCell{
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! TableView
cell.lbl.text = todolist[indexPath.row]
// cell.lbl2.text = todolist2[indexPath.row] // This is the error code
// cell.lbl3.text = todolist3[indexPath.row]
// cell.lbl4.text = todolist4[indexPath.row]
return cell
}
And test to verify existing code (should work but of course it will not update the labels.)
Then add code to print the number of items in each array:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) ->
UITableViewCell{
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! TableView
cell.lbl.text = todolist[indexPath.row]
// cell.lbl2.text = todolist2[indexPath.row] // This is the error code
// cell.lbl3.text = todolist3[indexPath.row]
// cell.lbl4.text = todolist4[indexPath.row]
print("Row: \(indexPath.row)")
print("List 1: \(todolist.count)") //this will print to the console
print("List 2: \(todolist2.count)")
print("List 3: \(todolist3.count)")
print("List 4: \(todolist4.count)")
return cell
}
What you will likely see is that they don't have the same number of items, and as soon as it his a "row" that is equal to or greater than the number of items, it will break. Remember that Row's start at Zero, while count starts at 1.
If this is what you find, then there is problem something wrong with the code where you are adding the values to the todolist arrays. If you want to see how to convert that to a struct, I can post that for you.
Converting to struct
The code that is executing when something is clicked:
#IBAction func ClickedforSelection(sender: AnyObject) {
shows that a value is written to each of the 4 todolists every time. While I don't have the full requirements, if this is what you want to do, then you could implement a struct. Put this code in it's own ToDoList.swift file (ideally):
struct ToDoListItem {
var listItem: String?
var list1Item: String?
var list2Item: String?
var list3Item: String?
}
Then replace where you define your todolislt arrays (all 4 of them) with a single:
var listItems = [ToDoListItem]() //creates an array of ToDoListItems and initializes it with no values
Then in the ClickedForSelection function, change it to:
let listItem = ToDoListItem(listItem: txt.text, list1Item: txt1.text, list2Item: txt2.text, list3Item: txt3.text)
listItems.append(listItem) //add it to your array
//todolist.append(txt.text!)
//todolist2.append(txt1.text!)
//todolist3.append(txt2.text!)
//todolist4.append(txt3.text!)
self.view.endEditing(true)
txt.text = ""
txt1.text = ""
txt2.text = ""
txt3.text = ""
// This routine will need to be updated. Leaving that for you to figure out :)
// NSUserDefaults.standardUserDefaults().setObject(todolist, forKey: "list")
// NSUserDefaults.standardUserDefaults().setObject(todolist2, forKey: "list2")
// NSUserDefaults.standardUserDefaults().setObject(todolist3, forKey: "list3")
// NSUserDefaults.standardUserDefaults().setObject(todolist4, forKey: "list4")
...then numberOfRowsInSection changes to:
return listItems.count
...then cellForRowAtIndexPath changes to:
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! TableView
let listItem = listItems[indexPath.row]
cell.lbl.text = listItem.listItem ?? "" // Since listItems.listItem is an optional value, ?? unwraps it safely. If it is nill, it uses "" instead
cell.lbl2.text = listItem.list1Item ?? ""
cell.lbl3.text = listItem.list2Item ?? ""
cell.lbl4.text = listItem.list3Item ?? ""
return cell
Again...I would strongly consider how you are storing a value for a todolist for all 4 lists every time (if it is a todo list app, it seems like this may not be ideal?)