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.
Swipe TableViewCell to show multiple actions
-
What is handler2? Also, maybe we need to retain_global, since you define the block inside a function.
-
@JonB hanler2 for delete tab but not yet modified nor used...I did not tap delete
-
@JonB ok, I move the block outside main(), no more segmentation error, but
Traceback (most recent call last): File "_ctypes/callbacks.c", line 234, in 'calling callback function' File "/private/var/mobile/Containers/Shared/AppGroup/1B829014-77B3-4446-9B65-034BDDC46F49/Pythonista3/Documents/MesTests/UITableViewController user swipe.py", line 132, in handler blk.invoke(comp,True) NameError: name 'comp' is not defined
-
@cvp ok, removing the blk.invoke doesn't crash... Maybe we have the block signature wrong.
-
Ahh, by printing arguments, I realized that I forgot that blocks have hidden arguments pointing to themselves.
class _block_descriptor (Structure): _fields_ = [('reserved', c_ulong), ('size', c_ulong), ('copy_helper', c_void_p), ('dispose_helper', c_void_p), ('signature', c_char_p)] InvokeFuncType = ctypes.CFUNCTYPE(None, *[c_void_p, ctypes.c_bool]) class _block_literal(Structure): _fields_ = [('isa', c_void_p), ('flags', c_int), ('reserved', c_int), ('invoke', InvokeFuncType), ('descriptor', _block_descriptor)] def handler(_blk, _action, _sourceView, _comp): print(ObjCInstance (_action)) print(ObjCInstance(_sourceView)) print(ObjCInstance(_comp)) blk=_block_literal.from_address(_comp) print(blk.descriptor.signature) blk.invoke(_comp, True) handler_block=ObjCBlock(handler,restype=None,argtypes=[c_void_p, c_void_p,c_void_p, c_void_p])
-
…. But now I get a crash whenever I press enter at the console again. I think something in the VC needs to be retained?
-
@JonB With new code, I get
Traceback (most recent call last): File "_ctypes/callbacks.c", line 234, in 'calling callback function' TypeError: handler() missing 1 required positional argument: '_comp'
-
@JonB my error, I forgot to add a c_void_p
No more segmentation error
Log is
<UIContextualAction: 0x2856e4e60: style=0, title=@Ryubai's' action 😅, backgroundColor=UIExtendedSRGBColorSpace 0 0 1 0.5> <UISwipeActionStandardButton: 0x115808080; frame = (81 0; 120 43.5); anchorPoint = (0, 0.5); opaque = NO; autoresize = W+H; tintColor = UIExtendedGrayColorSpace 1 1; layer = <CALayer: 0x28360d860>> <__NSStackBlock__: 0x16bb0c608> b'5\xf0\xb2\xdc\x01'
-
Strange... I don't get a crash right away, but if I close the VC and type anything in the console, I get a seg fault. I moved everything into the main code, deleted the auto releases, and tried dismissing the VC from within the handler, but same result. Maybe I broke something else elsewhere.
Well ... If it works, good! Note you can use the action argument's title property to figure out which button was tapped, if you don't want to have a different handler for each button.
-
@JonB If I start Pythonista and I run the script a first time, the 2nd button is absent. Then, I close the program and relaunch it, the 2nd button appears. That's also strange
And if I relaunch it a third time, I have a segmentation error
-
@JonB said
Referring back to a time when I was smarter
I can't believe that, smarter than now....Is that really possible 😉
-
This post is deleted! -
One answer might be to simple remove the blk.invoke line.
I will experiment a bit more this weekend
-
Thanks for all the help! And sorry for taking a while to get back to you, I’ve been really busy these past few days. I tried your code and both actions are showing up for me every time, however Pythonista is still crashing from segmentation faults whenever I tap either action (whether or not I remove the blk.invoke). Thanks again and I agree that it’s hard to imagine a time when @JonB was smarter!
-
@colint could you try this little script which does not use ObjectiveC but marvelous gestures module of @mikael
Swipe left a row and test "delete" and "yours edit" buttons
It is not perfect, for instance, you can swipe left several rows, but this could be protected easily by some extra lines of code. Only to show what is possible without ObjectiveC
You could even replace the swipe by a tap or a double tap on the row.
import console import ui import gestures class MyTableView(ui.View): def __init__(self,w,h): self.width = w self.height = h tbl = ui.TableView() tbl.frame = (0,0,w,h) tbl.row_height = 50 tbl.data_source = ui.ListDataSource(items=['a','b','c','d','e']) tbl.delegate = self tbl.data_source.tableview_cell_for_row = self.tableview_cell_for_row tbl.data_source.tableview_can_delete = self.tableview_can_delete tbl.background_color = (0,0,0,0) self.add_subview(tbl) def but_action(self,sender): v = sender.superview tbl = v.tv row = v.row for sv in v.subviews: v.remove_subview(sv) if sender.title == 'delete': b = console.alert('delete row',str(row), 'confirm', 'cancel', hide_cancel_button=True) if b == 1: del tbl.data_source.items[row] tbl.reload() elif sender.title == 'yours edit': t = tbl.data_source.items[row] t = console.input_alert('enter text of row', str(row), t, 'ok', hide_cancel_button=True) tbl.data_source.items[row] = t v.text_label.text = t def LEFT_swipe(self,data): v = data.view w = v.width bdel = ui.Button() bdel.frame = (w,0,w/4,v.height) bdel.background_color = 'red' bdel.tint_color = 'white' bdel.title = 'delete' bdel.action = self.but_action v.add_subview(bdel) byou = ui.Button() byou.frame = (bdel.x+bdel.width,0,w/4,v.height) byou.background_color = 'green' byou.tint_color = 'white' byou.title = 'yours edit' byou.action = self.but_action v.add_subview(byou) def animation(): bdel.x = w/2 byou.x = bdel.x + bdel.width ui.animate(animation, duration=0.3) def RIGHT_swipe(self,data): v = data.view for sv in v.subviews: v.remove_subview(sv) def tableview_cell_for_row(self, tableview, section, row): # Create and return a cell for the given section/row cell = ui.TableViewCell() cell.tv = tableview cell.row = row cell.selectable = False cell.text_label.text = tableview.data_source.items[row] gestures.swipe(cell,self.LEFT_swipe, direction=gestures.LEFT) gestures.swipe(cell,self.RIGHT_swipe, direction=gestures.RIGHT) return cell def tableview_can_delete(self, tableview, section, row): # Return True if the user should be able to delete the given row. return False # to be sure that standard delete button is not displayed def main(): # Hide script w,h = ui.get_screen_size() mi = min(w,h)*0.9 my_back = MyTableView(mi,mi) my_back.background_color='white' my_back.name = 'Test for @colint' my_back.present('sheet',hide_title_bar=False) my_back.wait_modal() # Protect against import if __name__ == '__main__': main()
-
@colint feed-back hoped, please
-
@cvp Thanks for the script, the gestures module seems really neat! I tried out your code and the actions showed up nicely, the only thing is that for some reason, the script freezes when I press either of the buttons. I have had some issues with console.alert that I think started after some iOS update, so it may just be that that is causing the problem.
-
@colint strange, I don't have any freeze. I know that sometimes console.alert needs to be decorated by @ui.in_background, but here, I don't have the problem. Anyway, the challenge was to have several buttons when swiping, isn'it?
-
Strange, it must be just a problem on my end then. When I put ui.inbackground it doesn’t freeze, but the alerts show up behind the presented view, so I can only see them once I close the window. Anyway, like you said, as long as both actions show up then this is a good alternative way to do it! The only thing I’m wondering about now is if there is a way to get the same behaviour from the original solution, where a long swipe to the left would perform one of the actions. Thanks again for the help!
-
@colint said
long swipe
In the gestures module, the swipe function has min_distance and max_distance but they do not seem to work or you can't have two different swipes in the same direction.
Anyway, these parameters seem to belong to a private API, I think, and so it would be not allowed to be used.I try to get the width of the swipe gesture and to process differently for a short or a long swipe but, actually, without success.
Edit : I think that should be possible without using gestures but the standard touch_began/moved/ended methods where you can continuously compare finger movement from its begin to deduce if the swipe is short or long.
I could try but not this Sunday, normally tomorrow