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.


    [Share] A skeleton for making and testing variable height cells for a ScrollView

    Pythonista
    cells variable share
    5
    29
    18392
    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.
    • Phuket2
      Phuket2 @JonB last edited by

      @JonB , sorry I hope you are ok I come back here now you solved the problem.
      I have been sided tracked as usual. Trying to learn everything and learning nothing 😱
      Ok, to the point.
      I just cut up your example to represent how I would like to use the variable height cells. So far ok. It looks very promising. Meaning that getting all the smarts from ui.TableView.

      At least in my mind to get this to work the way I think it should work, I had to create a parallel list to store the cells heights to be sure I recover it correctly when your swizzled callback is called.

      The reason for my post is maybe I am missing something easy, and can recover the height in a more simple way rather than creating the list.

      The other thing is you say:

      t_o=objc_util.ObjCInstance(t)
      t_o.estimatedRowHeight=44
      

      Is optional. It does not appear it is. Without this seems to create all the cells at once rather than requesting as they come into view. Also crashes without that code.

      #!python3
      import ui
      from random import randint
      
      def make_cell():
      	cell = ui.TableViewCell()
      	
      	h = randint(44, 90)
      	cell.height = h
      	cell.text_label.text = 'cell height - ' + str(h)
      	return cell
      	
      class MyTableViewDataSource (object):
      	def __init__(self):
      		self.row_heights = None
      	
      	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
      		num_rows = 500
      		if not self.row_heights:
      			self.row_heights = [44] * num_rows
      		return num_rows
      
      	def tableview_cell_for_row(self, tableview, section, row):
      		# Create and return a cell for the given section/row
      		cell = make_cell()
      		self.row_heights[row] = cell.height
      		return cell
      		print('in get cell', str(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
      		
      	def tableview_height_for_section_row(self, tv,section,row):
      		print('height -', str(self.row_heights[row]))
      		return self.row_heights[row]
      		#return 10+(row/5)**2 if row<50 else 10+((100-row)/5)**2
      
      import tableview_rowheight, ui, objc_util
      # create a tableview and delegate and datasource, per usual
      #tableview_rowheight.setup_tableview_swizzle(False)
      t=ui.TableView(frame=(0,0,200,576))
      d= MyTableViewDataSource() #ui.ListDataSource([str(x) for x in range(100)])
      t.data_source=t.delegate=d
      
      # here i will just create height that grows then shrinks again
      #def tableview_height_for_section_row(tv,section,row):
      	#return 10+(row/5)**2 if row<50 else 10+((100-row)/5)**2
      
      #d.tableview_height_for_section_row=tableview_height_for_section_row
      
      # this is optional, but speeds up initial display and scrolling
      # set to nominal or average height
      t_o=objc_util.ObjCInstance(t)
      t_o.estimatedRowHeight=44
      
      t.present('sheet')
      
      1 Reply Last reply Reply Quote 0
      • cook
        cook last edited by

        I wonder if this sort of thing is possible? I don't have any idea because my objc level is 0.1 but it looks very simple compared to other things! What do you think @JonB @Phuket2

        http://www.appcoda.com/self-sizing-cells/

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

          The trick would be getting AutoLayout to work via objc_util. I have seen various autoLayout properties, but have not tried getting them to work.

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

            @JonB where have you seen auto layout properties?

            Anyway if we can get this to work it seems quite promising and not much code either! (Hopefully)

            I know how to set the estimated row height. When I tried the UITableViewAutomaticDimension ...first of all, I don't know what that is!!! Or how to get it!

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

              UIViews have a autoresizingMask property, and an addConstraint() method, together these define autolayout, I think. @Webmaster4o, this would be a good addition to ui2, as it gives more powerful layout.

              autoresizingMask takes an integer bitmask, which you add up the options you want:

              UIViewAutoresizingNone                 = 0
              UIViewAutoresizingFlexibleLeftMargin   = 1 << 0
              UIViewAutoresizingFlexibleWidth        = 1 << 1
              UIViewAutoresizingFlexibleRightMargin  = 1 << 2
              UIViewAutoresizingFlexibleTopMargin    = 1 << 3
              UIViewAutoresizingFlexibleHeight       = 1 << 4
              UIViewAutoresizingFlexibleBottomMargin = 1 << 5
              
              ObjCInstance(v).autoresizeMask=UIViewAutoResizingFlexibleWidth+UIViewAutoResizingWidth
              

              it should be possible to apply these to tableviewcells, and to the tableview itself..

              UITableViewAutomaticDimension is -1.0, you set the rowheight to this value.

              1 Reply Last reply Reply Quote 1
              • cook
                cook last edited by

                @jonb ...hmm... This is way over my head. I've tried this but it doesn't work.

                import ui
                from objc_util import *
                
                
                class TableData(object):
                	def __init__(self):
                		self.data = [{'value': str(i), 'height': i+40} for i in range(10)]
                	
                	def tableview_number_of_rows(self, tableview, section):
                		return len(self.data)
                
                	def tableview_cell_for_row(self, tableview, section, row):
                		cell = ui.TableViewCell()
                		label = ui.Label()
                		label.text = self.data[row]['value']
                		label.number_of_lines = 0
                		label_objc = ObjCInstance(label)
                		UIViewAutoresizingFlexibleTopMargin = 1 << 5
                		UIViewAutoresizingFlexibleBottomMargin = 1 << 5
                		UIViewAutoresizingFlexibleHeight = 1 << 5
                		UIViewAutoresizingFlexibleLeftMargin = 1 << 5
                		label_objc.autoresizeMask = UIViewAutoresizingFlexibleTopMargin + UIViewAutoresizingFlexibleBottomMargin + UIViewAutoresizingFlexibleHeight + UIViewAutoresizingFlexibleLeftMargin
                		label.height = self.data[row]['height']
                		cell.content_view.add_subview(label)
                		return cell
                
                
                tv = ui.TableView()
                tv.data_source = TableData()
                tv_objc = ObjCInstance(tv)
                tv_objc.rowHeight = -1.0 #UITableViewAutomaticDimension
                tv_objc.estimatedRowHeight = 40.0
                tv.present()
                
                

                ... Scratching my head :)

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

                  @cook You have repeated 1 << 5 four times but that is not what @JonB did above. He is flipping different bits while you are flipping the same bit multiple times.

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

                    @ccc ... I have no clue what flipping bits do! :)

                    But even if I apparently flip the bits differently I don't get any bit closer !

                    I'm in deep water...drowning...and the objc piranhas are coming.

                    Adjusted code ..maybe a step closer ...(?):

                    import ui
                    from objc_util import *
                    
                    
                    class TableData(object):
                    	def __init__(self):
                    		self.data = [{'value': str(i), 'height': i+40} for i in range(10)]
                    	
                    	def tableview_number_of_rows(self, tableview, section):
                    		return len(self.data)
                    
                    	def tableview_cell_for_row(self, tableview, section, row):
                    		cell = ui.TableViewCell()
                    		label = ui.Label()
                    		label.text = self.data[row]['value']
                    		label.number_of_lines = 0
                    		label_objc = ObjCInstance(label)
                    		UIViewAutoresizingNone = 0
                    		UIViewAutoresizingFlexibleLeftMargin = 1 << 0
                    		UIViewAutoresizingFlexibleWidth = 1 << 1
                    		UIViewAutoresizingFlexibleRightMargin = 1 << 2
                    		UIViewAutoresizingFlexibleTopMargin = 1 << 3
                    		UIViewAutoresizingFlexibleHeight = 1 << 4
                    		UIViewAutoresizingFlexibleBottomMargin = 1 << 5
                    
                    		label_objc.autoresizeMask = UIViewAutoresizingFlexibleWidth + UIViewAutoresizingFlexibleTopMargin + UIViewAutoresizingFlexibleBottomMargin + UIViewAutoresizingFlexibleHeight
                    		label.height = self.data[row]['height']
                    		cell.content_view.add_subview(label)
                    		return cell
                    
                    
                    tv = ui.TableView()
                    tv.data_source = TableData()
                    tv_objc = ObjCInstance(tv)
                    tv_objc.rowHeight = -1.0 #UITableViewAutomaticDimension
                    tv_objc.estimatedRowHeight = 40.0
                    tv.present()
                    
                    1 Reply Last reply Reply Quote 0
                    • JonB
                      JonB last edited by JonB

                      Turns out the textlabel already has constraints built in, so all you have to do is:
                      set number_of_lines=0
                      set tableview.row_height=-1
                      set ObjCInstance(tv).estimatedHeight to something nonzero

                      import ui, faker, random
                      from objc_util import *
                      
                      f=faker.Faker()
                      items=[f.text(random.randint(10,200)) for i in range(20)]
                      
                      class MyTableViewDataSource (object):
                      
                      	def tableview_number_of_rows(self, tableview, section):
                      		# Return the number of rows in the section
                      		return 20
                      
                      	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 = items[row]
                      		cell.text_label.number_of_lines=0
                      		return cell
                      
                      
                      v=ui.TableView()
                      v.frame=(0,0,320,576)
                      v.row_height=-1
                      v.data_source=MyTableViewDataSource()
                      ObjCInstance(v).estimatedRowHeight=44
                      v.present('sheet')
                      

                      Basically we get auto resizing text cells with basically three added lines of code!
                      @omz -- providing estimatedRowHeight access in ui.TableView would make this more accessible.

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

                        @jonb incredible. Will give it a shot.

                        Thanks for helping (...doing all the work) figure this out. I think it's really useful and also a very easy approach!

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

                          @jonb now... Do you think this is possible to do for a view that is added to the content_view of a cell?
                          Will that require flipping bits?

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

                            turns out autoresizingmask might be the same as flex... addConstraints would be what we want for content_view. I have not tried it yet.

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

                              I am trying to put images into the rows. This did not work to set a row height.

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