omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular

    Welcome!

    This is the community forum for my apps Pythonista and Editorial.

    For individual support questions, you can also send an email. If you have a very short question or just want to say hello β€” I'm @olemoritz on Twitter.


    Access TableViewCells

    Pythonista
    5
    11
    3495
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Drizzel
      Drizzel last edited by

      Is there an easy way to access the cells in a Tableview? And, more specifically, the currently selected cell?

      For now, I modified the datasource to give every cell an attribute with itβ€˜s section and row, and then append them to an array. But cycling through this list to find the currently selected cell gets a bit inefficient with longer tables...

      stephen 1 Reply Last reply Reply Quote 0
      • Drizzel
        Drizzel last edited by

        And Iβ€˜m confused about how the superview of a cell is itβ€˜s tableview, but the tableview doesn’t have any subviews

        1 Reply Last reply Reply Quote 0
        • stephen
          stephen @Drizzel last edited by

          @Drizzel

          tableview_cell_for_row inside your data_source πŸ€“πŸ˜Ž

          class MyTableViewDataSource (object):
          	 def tableview_number_of_sections(self, tableview):
          		  # Return the number of sections (defaults to 1)
          		  return 1
          
          	 def tableview_number_of_rows(self, tableview, section):
          		  # Return the number of rows in the section
          		  return 0
          
          	 def tableview_cell_for_row(self, tableview, section, row):
          		  # Create and return a cell for the given section/row
          		  cell = ui.TableViewCell()
          		  cell.text_label.text = 'Foo Bar'
          		  return cell
          
          	 def tableview_title_for_header(self, tableview, section):
          		  # Return a title for the given section.
          		  # If this is not implemented, no section headers will be shown.
          		  return 'Some Section'
          
          	 def tableview_can_delete(self, tableview, section, row):
          		  # Return True if the user should be able to delete the given row.
          		  return True
          
          	 def tableview_can_move(self, tableview, section, row):
          		  # Return True if a reordering control should be shown for the given row (in editing mode).
          		  return True
          
          	 def tableview_delete(self, tableview, section, row):
          		  # Called when the user confirms deletion of the given row.
          		  pass
          
          	 def tableview_move_row(self, tableview, from_section, from_row, to_section, to_row):
          		  # Called when the user moves a row with the reordering control (in editing mode).
          		  pass
          
          1 Reply Last reply Reply Quote 0
          • Drizzel
            Drizzel last edited by Drizzel

            @stephen Alright, thanks. I just thought that creating a new cell wasn’t β€želegantβ€œ, if you now what I mean :)

            Edit:
            Iβ€˜m pretty sure I’m missing something, though

            import ui 
            
            tv = ui.TableView(frame = (0,0,300,300))
            tv.data_source = ui.ListDataSource([i for i in range(10)])
            tv.data_source.tableview_cell_for_row(tv, 0, 2).background_color = 'red'
            
            tv.present('sheet')
            
            
            cvp stephen JonB 3 Replies Last reply Reply Quote 0
            • cvp
              cvp @Drizzel last edited by

              @Drizzel dirty but shortest

              import ui 
              def tableview_cell_for_row(tableview, section, row):
              	# Create and return a cell for the given section/row
              	cell = ui.TableViewCell()
              	cell.text_label.text = str(tableview.data_source.items[row])
              	selected_cell = ui.View()
              	selected_cell.bg_color = 'red'
              	cell.selected_background_view = selected_cell
              	return cell
              tv = ui.TableView(frame = (0,0,300,300))
              tv.data_source = ui.ListDataSource(items=[i for i in range(10)])
              tv.data_source.tableview_cell_for_row = tableview_cell_for_row
              tv.present('sheet')
              
              1 Reply Last reply Reply Quote 0
              • stephen
                stephen @Drizzel last edited by

                @Drizzel if you dont mind me asking.. what exactly are you doing?

                1 Reply Last reply Reply Quote 0
                • shinyformica
                  shinyformica last edited by

                  @Drizzel the important bit here is that you aren't really supposed to directly access tableview cells by index, since in all likelihood, they don't exist.

                  This is because the way the underlying control is implemented is designed to only create cells when they need to be displayed, and it keeps only the ones it absolutely needs to keep (with some caching), it even reuses existing cells that have gone out of the visible area, instead of creating new ones.

                  That source.tableview_cell_for_row() is called to generate a cell for a particular index at the moment the view requires it, and it will be destroyed or reused, so you can't really depend on it being "kept" somewhere as a subview you can always access.

                  Now, you can actually use objc_util magic to get a cell at an index path, but the cell will be None if that index is invalid or not visible.

                  So the best thing to do if you want to somehow modify a cell (I'm guessing that's what you want here) is to implement tableview_cell_for_row() and make any modifications there for cells being created. If you need to update a visible cell, just call table.reload() and it will cause all visible cells to be re-created.

                  1 Reply Last reply Reply Quote 2
                  • JonB
                    JonB @Drizzel last edited by

                    @Drizzel the way to think of tableview cells is that you never access them from other code. You populate it inside of cell_for_row -- basically only the cells currently on screen ever actually exist. It is a memory thing -- you could have a 100000 row table, but only 6 tableview cells ever exist at once.

                    Now, it is possible, if you create your own premade list of cells, to just have cell_for_row return the premade cell. Or, you could use the content_view if you have something expensive that you want to reuse, like, say, a webview.

                    stephen 1 Reply Last reply Reply Quote 2
                    • Drizzel
                      Drizzel last edited by Drizzel

                      Thanks, I thought TableViewCells would always exist, nevermind if they’re on screen or not. That explains why this example doesn’t work properly. I’m going to try the suggestion of @JonB

                      
                      import ui
                      
                      class MyTableViewDataSource (object):
                      	def __init__(self, data, **kwargs):
                      		#self.__dict__.update((k, v) for k, v in kwargs.items() if k in allowed_keys)
                      		self.__dict__.update(kwargs)
                      		self.items = data	
                      		self.previousCell = False
                      		self.currentCell = False
                      		self.cells = [[] for i in range(len(self.items))]
                      		
                      	def tableview_number_of_sections(self, tableview):
                      		return len(self.items)
                      		
                      	def tableview_number_of_rows(self, tableview, section):
                      		return len(self.items[section])
                      		
                      	def tableview_cell_for_row(self, tableview, section, row):
                      		data = tableview.data_source.items[section][row]
                      		cell = ui.TableViewCell('subtitle')
                      		cell.section, cell.row = section, row
                      		cell.text_label.text = data
                      			
                      		self.cells[section].append(cell)
                      		return cell
                      		
                      	def tableview_title_for_header(self, tableview, section):
                      		# Return a title for the given section.
                      		# If this is not implemented, no section headers will be shown.
                      		return 'section: '+str(section)
                      		
                      	def tableview_can_delete(self, tableview, section, row):
                      		# Return True if the user should be able to delete the given row.
                      		return False
                      		
                      	def tableview_can_move(self, tableview, section, row):
                      		# Return True if a reordering control should be shown for the given row (in editing mode).
                      		return False
                      		
                      	def tableview_delete(self, tableview, section, row):
                      		# Called when the user confirms deletion of the given row.
                      		pass
                      		
                      	def tableview_move_row(self, tableview, from_section, from_row, to_section, to_row):
                      		# Called when the user moves a row with the reordering control (in editing mode).
                      		pass
                      
                      #–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
                      	
                      class BasicTableViewDelegate(object):
                      	def tableview_did_select(self, tableview, section, row):
                      		# Called when a row was selected.
                      		
                      		if tableview.data_source.currentCell:	
                      			if tableview.data_source.previousCell:
                      				tableview.data_source.previousCell.background_color = 'white'
                      				
                      			tableview.data_source.previousCell = tableview.data_source.currentCell
                      			del tableview.data_source.currentCell
                      			tableview.data_source.previousCell.background_color = '#eeeeee'
                      			
                      			
                      		#find current cell
                      		for cell in tableview.data_source.cells[section]: #dont like having to cycle through all cells
                      			if cell.row == row:
                      				tableview.data_source.currentCell = cell
                      				break
                      
                      	def tableview_did_deselect(self, tableview, section, row):
                      		# Called when a row was de-selected (in multiple selection mode).
                      		pass
                      
                      	def tableview_title_for_delete_button(self, tableview, section, row):
                      		# Return the title for the 'swipe-to-***' button.
                      		return 'Delete'
                      
                      if __name__ == '__main__':
                      
                      	section1 = [str(i) for i in range(10)]
                      	section2 = [str(i) for i in range(10)]
                      	
                      	tv = ui.TableView()
                      	tv.data_source = MyTableViewDataSource([section1, section2])
                      	tv.delegate = BasicTableViewDelegate()
                      	
                      	tv.frame = (0, 0, 300, 400)
                      	tv.present('sheet')
                      	
                      	
                      
                      1 Reply Last reply Reply Quote 0
                      • stephen
                        stephen @JonB last edited by stephen

                        @JonB couldnt you use a user defined list which elements are all TableViewCell objects and then inside tableview_cell_for_row return that list at respective row index? giving access to cells even if the cells not presented?

                        
                        class MyTableView(ui.View):
                        	def __init__(self, *args, **kwargs):
                        		self.tv=ui.TableView()
                        		self.cells=list()
                        		self.tv.delegate = self.tv.data_source = self
                        
                        		
                        	def tableview_create_cell(self, **kwargs):
                        		cell = ui.TableViewCell()
                        		
                        		for k, v in kwargs.items():
                        			setattr(cell, k, v)
                        			
                        		self.cells.append(cell)
                        		return cell
                        		
                        	def tableview_did_select(self, tableview, section, row):
                        		pass
                        
                        	def tableview_did_deselect(self, tableview, section, row):
                        		pass
                        
                        	def tableview_title_for_delete_button(self, tableview, section, row):
                        		return 'Delete'
                        		
                        	def tableview_number_of_sections(self, tableview):
                        		return 1
                        
                        	def tableview_number_of_rows(self, tableview, section):
                        		return len(self.cells)
                        
                        	def tableview_cell_for_row(self, tableview, section, row):
                        		return self.cells[row]
                        
                        	def tableview_title_for_header(self, tableview, section):
                        		return ''
                        
                        	def tableview_can_delete(self, tableview, section, row):
                        		return True
                        
                        	def tableview_can_move(self, tableview, section, row):
                        		return True
                        
                        	def tableview_delete(self, tableview, section, row):
                        		self.cells.remove(self.cells[row])
                        
                        	def tableview_move_row(self, tableview, from_section, from_row, to_section, to_row):
                        		pass
                        
                        
                        Drizzel 1 Reply Last reply Reply Quote 0
                        • Drizzel
                          Drizzel @stephen last edited by

                          @stephen That’s how I understood him. And it worked out really well, after some minor adaptions

                          1 Reply Last reply Reply Quote 1
                          • First post
                            Last post
                          Powered by NodeBB Forums | Contributors