Setting table's rows programmatically

Hello Claude31,


I hope that's you who resumes my last question. I tried to follow your advice:

=> Create a new thread, copy your new post there, possibly giving a link to the first thread if needed

=> Unmark your post and mark correct the real answer (post dated Mar 22, 2020 5:56 AM)

=> Select each piece of code and hit <>


Here the problem I want to post again:


The problem we discussed is solved. Ok. Unfortunately, a new row selection problem has arised in a broader context. To understand, I need to describe this context a bit more than already done. There are the adapted lines of code you proposed:


  r_RowSelection = codeSystemTableView.indexPathsForSelectedRows!
  let selectedRows = r_RowSelection.map() { $0.row}
  r_RowSelectionDictionary[r_TemplateTerm] = selectedRows
  r_SelectedRowIndexes = IndexSet(selectedRows)


Everything is ok.


The broader context: The codeSystemTableView should be able to display more that one table - not simultaneously within one and the same view, but successively. This is done by operating a button that iterates through the available tables. This button calls a function that runs the following code:


  DispatchQueue.main.async
  {
  self.codeSystemTableView.reloadData()
  }


That’s ok too. The different tables can be displayed and its rows can be selected or unselected. Understandably, the selection on a particular table vanishes between the iterations, i.e. after reloadData(). Therefore user cannot check the selection state of the different tables by iterating through the tables. To tackle this problem the selections on the different tables is layed down in a dictionary as shown in the code lines above. To restore the selections the dictionary’s selectedRows are cast into an IndexSet as you proposed:


  r_SelectedRowIndexes = IndexSet(selectedRows)


An IndexSet is needed to set the table’s row selection state by means of this function according to Xcode’s manual:


  func selectRowIndexes(_ indexes: IndexSet, byExtendingSelection extend: Bool)
  Sets the row selection using indexes extending the selection if specified.


Now, the new lines of code look like these:

  r_RowSelection = codeSystemTableView.indexPathsForSelectedRows!
  let selectedRows = r_RowSelection.map() { $0.row}
  r_RowSelectionDictionary[r_TemplateTerm] = selectedRows

  r_SelectedRowIndexes = IndexSet(selectedRows)
  codeSystemTableView.selectRowIndexes(r_SelectedRowIndexes, YES)



1. Problem. The last line throws an error:

Value of type 'UITableView' has no member 'selectRowIndexes' contradictory to what Xcode’s documents say.


2. Problem. Printing r_RowSelection, selectRowIndexes, r_RowSelectionDictionary shows:

r_RowSelection is [[0, 0], [0, 2], [0, 4], [0, 6]])

r_SelectedRowIndexes is 4 indexes

r_RowSelectionDictionary is ["Template 1": [0, 2, 4, 6]]


The r_RowSelectionDictionary contains one item. That’s ok because until now only one table has been activated. Iterating to the next table shows the following printout.

r_RowSelection is [[0, 0]])

r_SelectedRowIndexes is 1 indexes

r_RowSelectionDictionary is ["Template 1": [0, 2, 4, 6], "Template 2": [0]]


That’s ok too. Now, two tables have been activated and some items selected. But what is wrong or irritating, the printed r_SelectedRowIndexes does not show the IndexSet but the number of items in the IndexSet, i.e. 4 indexes resp. 1 indexes:

r_SelectedRowIndexes is 4 indexes

r_SelectedRowIndexes is 1 indexes


Some additional information to understand the code:

The tables are represented by the keys "Template 1" and "Template 2". These keys refer to the appropriate tables. That’s ok too. For testing and demonstration there are two tables only. The number of tables is unlimited, at least theoretically.


Do you have an idea how to solve these problems?

Again, thank you very much for your help and please apologize my ignorance of the forum's rules

Now I hope I have done everything correctly


Best regards

gefa

Answered by Claude31 in 412056022

I wrote my answers in bold and your questions in italic, to make them easier to see in context.


Ok. Unfortunately, a new row selection problem has arised in a broader context. To understand, I need to describe this context a bit more than already done. There are the adapted lines of code you proposed:


  r_RowSelection = codeSystemTableView.indexPathsForSelectedRows!
  let selectedRows = r_RowSelection.map() { $0.row}
  r_RowSelectionDictionary[r_TemplateTerm] = selectedRows

  r_SelectedRowIndexes = IndexSet(selectedRows)

Everything is ok.



The broader context: The codeSystemTableView should be able to display more that one table - not simultaneously within one and the same view, but successively. This is done by operating a button that iterates through the available tables. This button calls a function that runs the following code:


  DispatchQueue.main.async
  {
  self.codeSystemTableView.reloadData()
  }


That’s ok too. The different tables can be displayed and its rows can be selected or unselected. Understandably, the selection on a particular table vanishes between the iterations, i.e. after reloadData(). Therefore user cannot check the selection state of the different tables by iterating through the tables.


How do you switch between tables just by reloading ?

Does the cellForRowAt select between the different tables datasources and keep track of which is the active table ?

-> Could you show this function cellForRowAt

Another way, if you want to stay inside the same VC would be to declare several tableViews and hide all except the active one. No need to reload, selections would be maintained…


To tackle this problem the selections on the different tables is layed down in a dictionary as shown in the code lines above. To restore the selections the dictionary’s selectedRows are cast into an IndexSet as you proposed:


  r_SelectedRowIndexes = IndexSet(selectedRows)

selectedRows is not a dictionary, but an array. Only one common to all tables ?

Creating several tables would avoid this.


An IndexSet is needed to set the table’s row selection state by means of this function according to Xcode’s manual:

  func selectRowIndexes(_ indexes: IndexSet, byExtendingSelection extend: Bool)

Sets the row selection using indexes extending the selection if specified.


Now, the new lines of code look like these:

  r_RowSelection = codeSystemTableView.indexPathsForSelectedRows!
  let selectedRows = r_RowSelection.map() { $0.row}
  r_RowSelectionDictionary[r_TemplateTerm] = selectedRows

  r_SelectedRowIndexes = IndexSet(selectedRows)
  codeSystemTableView.selectRowIndexes(r_SelectedRowIndexes, YES)


1. Problem. The last line throws an error:

Value of type 'UITableView' has no member 'selectRowIndexes' contradictory to what Xcode’s documents say.


This is a Cocoa func (NSTableView), not CocoaTouch (UITableView)

The correct call is:

tableView.selectRow(at: indexPath, animated: true, scrollPosition: .bottom)


2. Problem. Printing r_RowSelection, selectRowIndexes, r_RowSelectionDictionary shows:

r_RowSelection is [[0, 0], [0, 2], [0, 4], [0, 6]])

r_SelectedRowIndexes is 4 indexes

r_RowSelectionDictionary is ["Template 1": [0, 2, 4, 6]]


That means you have selected rows 0, 2, 4, 6. Exact ?

r_RowSelectionDictionary is ["Template 1": [0, 2, 4, 6]] [0, 2, 4, 6] est un Set, non ordered.


The r_RowSelectionDictionary contains one item. That’s ok because until now only one table has been activated. Iterating to the next table shows the following printout.

r_RowSelection is [[0, 0]])

r_SelectedRowIndexes is 1 indexes

r_RowSelectionDictionary is ["Template 1": [0, 2, 4, 6], "Template 2": [0]]



That’s ok too. Now, two tables have been ativated and some items selected. But what is wrong or irritating, the r_SelectedRowIndexes does not show the IndexSet but the number of items in the IndexSet, i.e. 4 indexes resp. 1 indexes:

r_SelectedRowIndexes is 4 indexes

r_SelectedRowIndexes is 1 indexes


I do not understand your point : r_SelectedRowIndexes does not show the IndexSet but the number of items in the IndexSet, i.e. 4

What is your print statement ? What do you get exactly in the log ?

running the following code:

let mySet : Set<Int> = [0, 2, 4, 6]

print(mySet)


gives a set of items, in any order:

[4, 2, 0, 6]


Some additional information to understand the code:

The tables are represented by the keys "Template 1" and "Template 2". These keys refer to the appropriate tables. That’s ok too. For testing and demonstration there are two tables only. The number of tables is unrestricted, at least theoretically.

Accepted Answer

I wrote my answers in bold and your questions in italic, to make them easier to see in context.


Ok. Unfortunately, a new row selection problem has arised in a broader context. To understand, I need to describe this context a bit more than already done. There are the adapted lines of code you proposed:


  r_RowSelection = codeSystemTableView.indexPathsForSelectedRows!
  let selectedRows = r_RowSelection.map() { $0.row}
  r_RowSelectionDictionary[r_TemplateTerm] = selectedRows

  r_SelectedRowIndexes = IndexSet(selectedRows)

Everything is ok.



The broader context: The codeSystemTableView should be able to display more that one table - not simultaneously within one and the same view, but successively. This is done by operating a button that iterates through the available tables. This button calls a function that runs the following code:


  DispatchQueue.main.async
  {
  self.codeSystemTableView.reloadData()
  }


That’s ok too. The different tables can be displayed and its rows can be selected or unselected. Understandably, the selection on a particular table vanishes between the iterations, i.e. after reloadData(). Therefore user cannot check the selection state of the different tables by iterating through the tables.


How do you switch between tables just by reloading ?

Does the cellForRowAt select between the different tables datasources and keep track of which is the active table ?

-> Could you show this function cellForRowAt

Another way, if you want to stay inside the same VC would be to declare several tableViews and hide all except the active one. No need to reload, selections would be maintained…


To tackle this problem the selections on the different tables is layed down in a dictionary as shown in the code lines above. To restore the selections the dictionary’s selectedRows are cast into an IndexSet as you proposed:


  r_SelectedRowIndexes = IndexSet(selectedRows)

selectedRows is not a dictionary, but an array. Only one common to all tables ?

Creating several tables would avoid this.


An IndexSet is needed to set the table’s row selection state by means of this function according to Xcode’s manual:

  func selectRowIndexes(_ indexes: IndexSet, byExtendingSelection extend: Bool)

Sets the row selection using indexes extending the selection if specified.


Now, the new lines of code look like these:

  r_RowSelection = codeSystemTableView.indexPathsForSelectedRows!
  let selectedRows = r_RowSelection.map() { $0.row}
  r_RowSelectionDictionary[r_TemplateTerm] = selectedRows

  r_SelectedRowIndexes = IndexSet(selectedRows)
  codeSystemTableView.selectRowIndexes(r_SelectedRowIndexes, YES)


1. Problem. The last line throws an error:

Value of type 'UITableView' has no member 'selectRowIndexes' contradictory to what Xcode’s documents say.


This is a Cocoa func (NSTableView), not CocoaTouch (UITableView)

The correct call is:

tableView.selectRow(at: indexPath, animated: true, scrollPosition: .bottom)


2. Problem. Printing r_RowSelection, selectRowIndexes, r_RowSelectionDictionary shows:

r_RowSelection is [[0, 0], [0, 2], [0, 4], [0, 6]])

r_SelectedRowIndexes is 4 indexes

r_RowSelectionDictionary is ["Template 1": [0, 2, 4, 6]]


That means you have selected rows 0, 2, 4, 6. Exact ?

r_RowSelectionDictionary is ["Template 1": [0, 2, 4, 6]] [0, 2, 4, 6] est un Set, non ordered.


The r_RowSelectionDictionary contains one item. That’s ok because until now only one table has been activated. Iterating to the next table shows the following printout.

r_RowSelection is [[0, 0]])

r_SelectedRowIndexes is 1 indexes

r_RowSelectionDictionary is ["Template 1": [0, 2, 4, 6], "Template 2": [0]]



That’s ok too. Now, two tables have been ativated and some items selected. But what is wrong or irritating, the r_SelectedRowIndexes does not show the IndexSet but the number of items in the IndexSet, i.e. 4 indexes resp. 1 indexes:

r_SelectedRowIndexes is 4 indexes

r_SelectedRowIndexes is 1 indexes


I do not understand your point : r_SelectedRowIndexes does not show the IndexSet but the number of items in the IndexSet, i.e. 4

What is your print statement ? What do you get exactly in the log ?

running the following code:

let mySet : Set<Int> = [0, 2, 4, 6]

print(mySet)


gives a set of items, in any order:

[4, 2, 0, 6]


Some additional information to understand the code:

The tables are represented by the keys "Template 1" and "Template 2". These keys refer to the appropriate tables. That’s ok too. For testing and demonstration there are two tables only. The number of tables is unrestricted, at least theoretically.

First of all, you should not address a specific person as an opening post in a thread.

This is not a place for private chat. It is a place to share experience among various developers.


You should better include a link to your previous thread, to make all the readers understand these are related.

How to implement 'var selectedRowIndexes: IndexSet { get }'


1. Problem. The last line throws an error:

Value of type 'UITableView' has no member 'selectRowIndexes' contradictory to what Xcode’s documents say.


As already commented what you have found is a method for NSTableView, not for UITableView.

You may need to call func selectRow(at: IndexPath?, animated: Bool, scrollPosition: UITableView.ScrollPosition) multiple times.


Something like this

        for indexPath in selectedIndexPaths {
            codeSystemTableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
        }


So, this is not true:

An IndexSet is needed to set the table’s row selection state by means of this function according to Xcode’s manual:


You should better store Array of IndexPath directly into your `r_RowSelectionDictionary`.

    var r_RowSelectionDictionary: [String: [IndexPath]] = [:]

    //...
        r_RowSelection = codeSystemTableView.indexPathsForSelectedRows!
        r_RowSelectionDictionary[r_TemplateTerm] = r_RowSelection

    //...
        if let selectedIndexPaths = r_RowSelectionDictionary[r_TemplateTerm] {
            for indexPath in selectedIndexPaths {
                codeSystemTableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
            }
        }


Seems you have gone in the wrong direction, based on the unavailable method.

With these changes your Problem 2 disappears.


---

I'm not sure this is the best strategy to keep selected state among multiple data models.

You may keep selected states in your data model design, in some cases that would be easier to manage.

First of all: Please apologize that I addressed my reply to a specific person as an opening post in a thread. I didn't know that. I use the Forum very seldom. I will come back after I have studied the details of your reply.

There are several proposal to solve my problem. I will step through them one after another. Two comments are equal and refer to

tableView.selectRow(at:, animated:, scrollPosition:)

To avoid double feedback I continue at the comment of Claude31

I do understand and I tried

tableView.selectRow(at:, animated:, scrollPosition:)

Now, the code looks like this:


  r_RowSelection = codeSystemTableView.indexPathsForSelectedRows!
        let selectedRows = r_RowSelection.map() { $0.row}
  r_RowSelectionDictionary[r_TemplateTerm] = selectedRows

  r_SelectedRowIndexes = IndexSet(selectedRows)

  codeSystemTableView.selectRow(at: r_RowSelection,
    animated: true,
    scrollPosition: .bottom)


Line 07 generates an error for r_RowSelection:

Cannot convert value of type '[IndexPath]' to expected argument type 'IndexPath?'


r_RowSelection is defined like this:

  var r_RowSelection : [IndexPath] = []

Next I changed the definition into:

  var r_RowSelection : IndexPath? = []

Now,

selectRow(at:animated:scrollPosition) is silenced

But three new error meassages appear.


For code line

  r_RowSelection = codeSystemTableView.indexPathsForSelectedRows!

the error message is:

Cannot assign value of type '[IndexPath]' to type 'IndexPath?'


For code line

  r_RowSelectionDictionary[r_TemplateTerm] = selectedRows

the error message, referring to selectedRows (red underscore), is:

Cannot assign value of type 'Int?' to type '[Int]?'


For code line:

  r_SelectedRowIndexes = IndexSet(selectedRows)

the error message is:

Argument labels '(_:)' do not match any available overloads


I tried several changes, without success. How to solve the problem?

When you tst, don't hesitate to print logs to understand what's happening (or use the debugger with breakpoints and inspect variables with po command).

Setting table's rows programmatically
 
 
Q