-2

Currently i have an uitableview with a custom cell. It contains an uilabel and uibutton - data source for which is fetched from two separate arrays. The uibutton function is to append the corresponding array with lines for the uilabel and insert them in the uitableview as well as add a new cell with another uibutton below.

There is one conditional - once the question array value is nil, the answer uibutton is shown.

The issue is - because the uibutton name is fetched as the last value from the array, once i scroll all the button titles are being re-written to match the latest one.

Here is the flow. enter image description here

Here is my code

 func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return numberofsections
// is always equal to zero
} 


func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

return someTagsArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell     {

    var cell:TblCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! TblCell

        cell.lblCarName.text = someTagsArray[indexPath.row]
        cell.act1.setTitle(answersdict.last, forState:UIControlState.Normal)


return cell
}
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {

var cell:TblCell = cell as! TblCell

    if let text = cell.lblCarName.text where text.isEmpty {
        cell.act1.hidden = false
    } else {
        println("Failed")
    }
}

Is there any way to store those uibutton values or prevent the cell from being reused completely.

Some more data on my question\answer model I have a dictionary with the nested array called linesmain for questions

linesmain = ["first":["question 1", "question 2", "question 3"], "answer 1": ["question 1", "question 2", "question 3"], "answer 2": ["question 1", "question 2", "question 3"], "answer 3":["question 1", "question 2", "question 3"]]

and a similar dictionary for answers

answersmain = ["first": ["answer 1"], "answer 1": ["answer 2"], "answer 2": ["answer 3"], "answer 3":["answer 4"]]

i keep it that way because i might increase the amount of answers in the future.

Then i have two arrays for the cells to append. One appends - the questions (someTagsArray), another one - the answers (answersdict). On launch the "first" questions and answers are loaded, then depending on the uibutton currentTitle in the newly appended cell.

Thank you.

David Robertson
  • 1,561
  • 4
  • 19
  • 41
  • Please show your `tableView:numberOfSections` and `tableView:numberOfRowsForSection:` code, so we can see how your questions and answers are mapped to (sections and) rows. –  Aug 31 '15 at 15:45
  • @PetahChristian doing it. as well as adding info on my model. – David Robertson Aug 31 '15 at 15:52
  • You'll need to show `someTagsArray` so we can see how questions and answers get added to that array. Because what you'll need to do is pull the answer text from `someTagsArray[indexPath.row]` and assign it to the answer button for that row. –  Aug 31 '15 at 16:11
  • @PetahChristian they look like this `someTagsArray = [question 1, question 2, question 3]; answersdict = [answer1]` after the arrays are appended they look like this `someTagsArray = [question1, question 2, question 3, question 1, question 2, question 3]; answersdict = [answer1, answer 2]` e.t.c. – David Robertson Aug 31 '15 at 16:15

2 Answers2

2

No, there's no way to prevent a cell from being reused. Reuse keeps memory requirements low, and scrolling fast, as the table only keeps around enough cells to ensure it can show the rows that are visible.

Cells will constantly be reused as you scroll back and forth. This is normal, expected, and isn't meant to be circumvented.

You're going to have to store those answer values in your model in a way that you can lookup the answer by indexPath row.

Change answersdict.last to use some method that takes a row, and returns the answer text for that row.

If you're not sure how to do that, you'll need to post more code showing how your model stores your question and answer data.

Update

In general, there are a few things I'd do differently, that you might want to consider.

  1. A tableView cell is selectable. Instead of adding buttons to the cell, you could just let the user selecting (tapping) a cell accomplish the work of the button event.

  2. As the answer to your original question suggested, you'd be better off dividing the data into sections, with each section containing the questions and answer for that section:

    Section 0
        Question 1 // row 0 of section 0
        Question 2 // row 1 of section 0
        Question 3
        Answer 1
    Section 1
        Question 1 // row 0 of section 1
        Question 2 // row 1 of section 1
        and so forth...

Your someTagsArray would be better off stored like [[question 1, question 2, question 3],[question 1, question 2, question 3], ...]. You see how each set of questions is in its own (section) array?

Then you can get the text for each question by querying someTagsArray[indexPath.section][indexPath.row] and the text for each answer by querying answersArray[indexPath.section].

func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return someTagsArray.count
} 

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return someTagsArray[section].count + 1 // number of Questions + 1 for the answer
}

Update 2:

Do you see the example of how the data is broken down into sections? Let's look at the counts for the first section, which is section 0:

someTagsArray[0].count // 3 questions in section 0
answersArray[0] // the one answer for section 0

The count of questions and answers aren't equal. As you pointed out, that would make no sense.

So, now that we see there are 3 questions and 1 answer for section 0, we know that section 0 has 4 rows.

The first three rows are for the questions. The last row is for the answer.

So, in tableView:cellForRowAtIndexPath, you're going to have to examine indexPath.row, and determine whether it is a question row or the answer row. It should be fairly easy to do, and I'll leave that as an exercise.

Community
  • 1
  • 1
  • Not sure how to do that. Please review i've added the requested info. – David Robertson Aug 31 '15 at 15:55
  • Done. I've added an example showing how to use sections and rows to group each set of questions and answers. –  Aug 31 '15 at 16:32
  • i'll try the codes out. thank you for your great help. – David Robertson Aug 31 '15 at 16:37
  • created the 2d array, set the `numberOfsections`, `numberOfRowsInSection` and `cellForRowAtIndexPath` codes. but i have an "array out of range" error which leads to this code `cell.lblCarName.text = someTagsArray[indexPath.section][indexPath.row]` (( – David Robertson Sep 01 '15 at 15:22
  • currently there are two arrays - one 2d "someTagsArray" another one simple array "answersArray". their object count is equal. – David Robertson Sep 01 '15 at 15:24
  • if i remove the `uibutton indexPath.row code` - and remove +1 from `numberOfRowsInSection` - the app works fine, but without the answer cells ( – David Robertson Sep 01 '15 at 15:30
  • to make the app work you have to make the amount of objects inside both someTagsArray[section] and answersArray equal. gosh( this doesn't make any sense – David Robertson Sep 01 '15 at 15:51
  • Don't remove the +1. It's where the answer goes. And no, the count of questions and answers aren't equal. Read the last update for an explanation. –  Sep 01 '15 at 17:58
  • am i right? my 2d questions array count is 1 (not considering nested array values) and my answers array is also one. i don't understand why it says - out of range – David Robertson Sep 01 '15 at 20:45
  • `questions.count` and `answers.count` will be equal, because they represent the number of sections. `questions[0].count` is the number of questions in that section. To index a question, it's `question[section][row]`. If you are out of range, it is a coding error. You can use the Xcode debugger to see which index is out of range, and determine the error you made in the code. If you're still stuck, edit your question and post the problematic code. But you'll be better off learning how to figure it out on your own, not by copying the right answer someone gives you. –  Sep 01 '15 at 21:08
  • i think i was able to pull it off by implementing separate instances of cell - `if (indexPath.row < questions[section].count) { var cell1: ... } else { var cell2: ... here we load the uibutton title from answers array`. it launches well, but because earlier on i was using `insertRowsAtIndexPaths` for cell insertion - i'm completely lost how to do it with sections. – David Robertson Sep 02 '15 at 10:10
  • That is the proper way how to configure a cell, whether or not it uses sections. You always had the need to determine whether a row was for a question or an answer. Sections just make the code a bit simpler/clearer to understand. As for insertRows, you shouldn't be completely lost. When you insert a row, the dataSource (value returned from numberOfRows) must agree with the change. So, instead of always returning *numberOfQuestions* + 1, you would return *numberOfQuestions* + *numberOfAnswers*. The logic is the same, whether or not you used sections. –  Sep 02 '15 at 12:57
  • That is a great idea. I'll try implementing it in the code. Thank you again. – David Robertson Sep 02 '15 at 14:48
  • i've managed to pull of the insert rows method. the app works fine until (and here is another issue) i slide it upwards and previous cells are reused. all of a sudden - it crashes with error "array index of range". very strange – David Robertson Sep 03 '15 at 15:47
  • If you are "lost" because you can't understand why "strange" things happen, you need to find tutorials about using the debugger, or about arrays and indexing. Learn how to use the Xcode debugger. Identify the line with the error. Figure out why the index is out of range. Fix the problem. In other words, master the basics so you can start to identify, understand, and fix mistakes in your code. –  Sep 03 '15 at 16:01
2

There is a problem with your approach if the number of rows in your table exceeds the number that can fit on screen. In that case, the cells that scroll off-screen will be re-used, and the contents of the button will be lost. If you can be sure that this will never happen, use the separate datasource array for button title.

You have to create a separate array with titles of buttons and you can use them against indexPath.row. Hope this helps

iAhmed
  • 6,556
  • 2
  • 25
  • 31