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
    18394
    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 last edited by

      Not sure this is useful to anyone or not. I want to make my own cell type so to speak. Want to try different things with different objects etc. I thought this would be a good way to start out for me. I have a bunch of useless cell classes in there now just to illustrate it working.

      # Phuket - Pythonista Forum
      # cell building test bed, python 3.xxx
      import ui
      
      from random import randint, choice
      
      
      class SimpleCell(ui.View):
      	def __init__(self, *args, **kwargs):
      		self.set_attrs(**kwargs)
      	
      	def set_attrs(self, **kwargs):
      		for k, v in kwargs.items():
      			if hasattr(self, k):
      				setattr(self, k, v)
      				
      	def add_label(self, text):
      		lb = ui.Label()
      		lb.text = text
      		lb.font = ('Arial Rounded MT Bold', 32)
      		lb.size_to_fit()
      		lb.center = self.center
      		self.add_subview(lb)
      	
      	
      class ACell(SimpleCell):
      	def __init__(self, *args, **kwargs):
      		SimpleCell.__init__(self, *args, **kwargs)
      		self.add_label('ACell')
      		
      		
      class ACellGreen(SimpleCell):
      	def __init__(self, *args, **kwargs):
      		SimpleCell.__init__(self, *args, **kwargs)
      		self.bg_color = 'green'
      		self.add_label('Green')
      		
      		
      class ACellRandom(SimpleCell):
      	def __init__(self, *args, **kwargs):
      		SimpleCell.__init__(self, *args, **kwargs)
      		self.bg_color = (randint(0, 255) / 100.,
      								randint(0, 255) / 100.,
      								randint(0, 255) / 100.)
      		self.add_label('Random')
      		
      		
      class ACellRed(SimpleCell):
      	def __init__(self, *args, **kwargs):
      		SimpleCell.__init__(self, *args, **kwargs)
      		self.add_label('Red')
      		self.bg_color = 'red'
      		
      		
      class ACellPurple(SimpleCell):
      	def __init__(self, *args, **kwargs):
      		SimpleCell.__init__(self, *args, **kwargs)
      		self.add_label('Purple')
      		self.bg_color = 'purple'
      		
      
      class DisplayCells(ui.View):
      	tm = 20				# top margin
      	vgap = 10			# vertical gap between cells
      	
      	def __init__(self, *args, **kwargs):
      		ui.View.__init__(self, *args, **kwargs)
      		self.sv = None
      		self.cells = []
      		self.height_cache = self.tm
      		
      		self.make_view()
      		
      	def make_view(self):
      		sv = ui.ScrollView(name='sv')
      		sv.frame = self.bounds
      		sv.flex = 'wh'
      		self.add_subview(sv)
      		self.sv = sv
      		
      	def add_cell(self, cell):
      		self.cells.append(cell)
      		cell.y = self.height_cache
      		self.height_cache += (cell.height + self.vgap)
      		self.sv.content_size = (0, self.height_cache)
      		self.sv.add_subview(cell)
      		
      if __name__ == '__main__':
      	# a list of cell classes selected randomly for testing
      	_cells = [ACell, ACellGreen, ACellRandom, ACellRed, ACellPurple]
      	w = 600
      	h = 800
      	
      	f = (0, 0, w, h)
      	
      	dc = DisplayCells(frame=f, bg_color='white')
      	
      	for r in range(300):
      		cell = choice(_cells)(width=f[2],
      								height=randint(40, 200),  bg_color='pink')
      		dc.add_cell(cell)
      	
      	dc.present('sheet')
      	```
      1 Reply Last reply Reply Quote 2
      • JonB
        JonB last edited by

        I have not tried this yet, but objc does allow variable height rows
        I believe -1 is the value for the auto dimension, but you will also need to implement a delegate.

        Phuket2 1 Reply Last reply Reply Quote 4
        • Phuket2
          Phuket2 @JonB last edited by

          @JonB , thanks. Yes, @cook sent me an article about it
          http://www.cimgf.com/2009/09/23/uitableviewcell-dynamic-height/

          It would be great if someone (not me) could make a robust way to enable ui.TableView to handle this. So much better and get a lot for free using TableView.
          I still struggle with basic Python, so no sense in me diving into objc yet.

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

            I have started something... should be totally possible to swizzle the PA3 tableView class to work like the other delegate methods. I have to first upgrade my swizzle method to handle the case when the method to add does not already exist, as is the case here.

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

              @JonB, well I don't exactly understand all your msg. But I know the intent.
              Really, I think this will be great. TableView is so nice, but still very limited with fixed height rows. I have been so focused on python I have really missed something by not looking at objective C. I didn't understand before how close the correlation was before between Pythonista's ui objects and the objective C equivalents. As I was reading today, It sort of dawned on me. What ever magic happens for the Xcode template, it can't be a full rewrite.
              I come across an article today about apples bad mvc coding practices. And it talked a lot about a tag. I was really laughing. All my bitching before about a tag... I had no idea they actually had one in objective c classes. They say it's a very bad mvc pattern. But for sure a old HyperCard programmer is on the team.
              Anyway, I see to understand Pythonista better I need to understand apples framework better. Swift or objective c. But just to understand the correlation between to two.

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

                https://github.com/jsbain/objc_hacks.git

                see the textview_rowheight script.

                note you will also need the latest version of swizzle.py in that same repo.

                importing textview_rowheight will install the swizzle, and after that any tableview can use a new delegate method tableview_height_for_section_row(tv, section, row) similar to the way the other tv delegate methods work. if pusing in a custom class, obviously you would also have a self argument.

                As a simple example:

                import textview_rowheight, ui, objc_util
                # create a tableview and delegate and datasource, per usual
                t=ui.TableView(frame=(0,0,200,576))
                d=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')
                

                edit: for the moment this only works with the python3 interpreter.

                Phuket2 1 Reply Last reply Reply Quote 1
                • Phuket2
                  Phuket2 @JonB last edited by

                  @JonB , I got it running. The numbers are not drawing in the cells and the empty rows heights are all the same height.
                  I did some print statements here and there to see if I could work out what was going wrong. I assume it must be something basic as it at least runs. But I really don't understand what's going on. I mean I get the gist of it, but there are objc stuff I don't really get.

                  @JonB after doing it, do you feel it's a solid solution for me to try and understand, or do you think I would be better to work on my skeleton. Really, the TableView is such an integral part to the ui, that to use it, I really would have to get into it and understand it. It would be horrible having problems with TableViews and having little idea why. I guess another thing, is it something you see that could break easily in the future?
                  Sorry to ask all the questions, but I'm genuinely exited about this. But maybe the effort is better but into making a sister class TableView that uses as much of TableViews built in functionality as possible.
                  Hmmm, I am not sure

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

                    i was not careful with how i handled cleared globals. change the setup_tableview_swizzle() to setup_tableview_swizzle(1), should do it.
                    With a little polish, I don't see why some objc in the background isn't a good long term solution. but pure python solution may also be more flexible (like your virtualcell class)

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

                      @JonB , I did the change. I updated as you said. I got the same result

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

                        I updated
                        https://github.com/jsbain/objc_hacks/blob/master/tableview_rowheight.py
                        and
                        https://github.com/jsbain/objc_hacks/blob/master/rowheight_example.py

                        or, in stash just use

                        git clone https://github.com/jsbain/objc_hacks.git obj_hacks
                        

                        , in which case you can get updates by cd obj_hacks, then git pull. Then run the rowheight_example script.

                        I need to handle swizzling in the presence of global clearing. What happens is even though the swizzled method implementation has been retained, its dependencies in the closure are not. I probably need to write the callback to import everything it needs in the def rather than in the module -- or, I need to ensure that dependencies get added to the pythonista_startup module so they don't get cleared.

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

                          @JonB l sorry to be a pain. But still does not work. Exactly same as last time. I am definitely using the right version. I used the stash cmd and cloned your git directory. I also restarted Pythonista. Running in 3.5 and have the latest beta from test flight. I tried running from both files.

                          I get a very strange artifact on the first line. It's been there from the first version I tried. In the pic below, the screen shot is with me pulling down the list so you can see it clearly. Not sure if this is a clue or not. But of course should not be there.

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

                            At a loss here... did you restart pythonista after installing the new script? I am about to upload a new one, which will catch errors and print out if an error is caught. maybe try in pythonista2 as well? It works for me in both betas though. I tried turning off animations as you do, and that works as well, so I am at a loss.... try this new version of tableview_rowheight and see if it spits out errors.

                            it should look like this

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

                              @JonB , hmmmm. Tried the new version. Same Same and not different. I did all you say like restart Pythonista etc. I have even restarted my ipad. I also tried running under 2.7 in the beta 3 app and running in the beta 2.7 app. Each time I see the same result. Also NO errors printed to the console. I seen your error catching statements in the last version. I get exactly the same as the previous pic I posted. Only one caveat to what I said before. I thought I was on the latest 3.x beta. I was not. It had failed to install yesterday. I didn't realise. I can't update it at the moment. There seems to be a global problem with some apple services. I think that's why I can't update right now. But I am only one behind. I will update as soon as I can. Again, I have been trying. Seems weird. You wouldn't think it was a hardware issue. Well, I would think so.
                              I also tried it (only py 2.7 beta) without disabling the animations in the pythonista_startup , with a forced restart of course. Same result.
                              I wish I could be more help.

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

                                @Phuket2 If you interested in getting to the bottom of this, lets move the discussion over here
                                https://github.com/jsbain/objc_hacks/issues/5
                                I have a set of tests for you to run to help get to the bottom of what is happening...

                                Phuket2 2 Replies Last reply Reply Quote 0
                                • Phuket2
                                  Phuket2 @JonB last edited by

                                  @JonB , thanks will do.

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

                                    Just to anyone that had been looking at this thread @JonB resolved the issue I was having with his code ousted here to sizzle. He also mentions in a seperate post. Issues to do with different size data types returned for 32bit and 64bit devices. Anyway it's fixed now. The code is at his git hub repository listed above.
                                    @JonB, thanks for your perseverance. I guess some good info come out of it.

                                    1 Reply Last reply Reply Quote 0
                                    • 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
                                            • First post
                                              Last post
                                            Powered by NodeBB Forums | Contributors