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.
Spacing of ButtonItem's
-
Is there a way to influence the spacing of
ButtonItem
's in a title row bar? They seem to be pretty far apart. In the iPhone layout of my app I have four (one left and three right) of them and now there's hardly any room left for the title itself. Thanks a lot! -
Not really... It's possible to get something similar by using
objc_util
though. This way, you can create aButtonItem
that uses a custom view instead of an image/title, and you could add multiple buttons with custom spacing/size to that view...A Button as a custom view of a ButtonItem doesn't behave completely like a normal ButtonItem with an image though. For example, the touch target of regular ButtonItems is much larger (taps don't need to be as precise).
Anyway, here's a little demo of what I mean. The spacing is very tight in this example, but it's easy to change.
import ui from objc_util import * v = ui.View(frame=(0, 0, 400, 400), name='Demo') v.background_color = 'white' btn_images = [ui.Image.named(n) for n in ['iob:beaker_32', 'iob:beer_32', 'iob:coffee_32']] btn_container = ui.View(frame=(0, 0, len(btn_images)*32, 44)) for i, img in enumerate(btn_images): btn = ui.Button(image=img) btn.frame = (i*32, 0, 32, 44) btn_container.add_subview(btn) btn_item = ui.ButtonItem() btn_item_objc = ObjCInstance(btn_item) btn_item_objc.customView = ObjCInstance(btn_container) v.right_button_items = [btn_item] v.present('sheet')
-
@omz Hi there! Thanks a lot for offering this sample code snippet. I'm currently turning it into a utility class. Unfortunately, there seems to be an issue with the
action
method of theButtonItem
's: they are never called. I can see the icons being pressed but nothing else happens. Do I need a little more ObjC wizadry for this? Thanks! I appreciate your help! -
You'd have to use the
action
of the individual buttons that are added to the container. -
-
The first problem is that you cannot pass an
action
as a keyword argument to theui.Button
constructor. It's a bit unfortunate that this is silently ignored instead of raising an exception... but you have to assign theaction
attribute separately.Unfortunately, your code will crash after you do so. The reason for this is that the container view (and with it, the buttons) get garbage-collected because there are no references to them anymore after
get_condensed_list
returns. The underlying (ObjC) views still exist, but the Python objects are gone, which leads to garbage pointers and crashes... In short, you have to keep a reference to thebtn_container
view somehow. I would suggest that you simply assign it as an attribute ofv
(something likev.button_container = btn_container
). This requires some refactoring of yourget_condensed_list
method. This should work:# coding: utf-8 # This file is part of https://github.com/marcus67/rechtschreibung import ui from objc_util import * DEFAULT_X_SPACING = 8 DEFAULT_HEIGHT = 44 class ButtonItemCondenser (object): def __init__(self, button_item_list, x_spacing=DEFAULT_X_SPACING): self.button_item_list = button_item_list self.x_spacing = x_spacing def get_condensed_list(self): # see https://forum.omz-software.com/topic/2724/spacing-of-buttonitem-s i = 0 x = 0 btn_container = ui.View(name='test') for button_item in self.button_item_list: btn = ui.Button(image=button_item.image, action=button_item.action) #button_item.action(btn_container) width = button_item.image.size[0] btn.frame = (x, 0, width, DEFAULT_HEIGHT) x = x + width + self.x_spacing btn_container.add_subview(btn) i = i + 1 x = x - self.x_spacing btn_container.frame = (0, 0, x , DEFAULT_HEIGHT) btn_item = ui.ButtonItem() btn_item_objc = ObjCInstance(btn_item) btn_item_objc.customView = ObjCInstance(btn_container) return [btn_item] def handle_action(sender): #print "handle_action: sender.name=%s" % sender.name print str(sender) #def handle_action(): # print "handle_action" def test(): icon_names = [ 'iob:beaker_32', 'iob:beer_32', 'iob:bag_32' ] button_item_list = map(lambda name : ui.ButtonItem(image=ui.Image.named(name), action=handle_action), icon_names) condenser = ButtonItemCondenser(button_item_list) v = ui.View(frame=(0, 0, 400, 400), name='Demo') v.background_color = 'white' condensed_list = condenser.get_condensed_list() normal_item = ui.ButtonItem(image=ui.Image.named('iob:checkmark_32'), action=handle_action) condensed_list.append(normal_item) v.right_button_items = condensed_list v.present('sheet') if __name__ == '__main__': test()
-
-
-
@marcus67 A simple one-line fix would be to add a reference to the
condenser
object to the view before you present it:# ... v.condenser = condenser v.present('sheet') # ...
This way, you can make sure that the objects you reference in
condenser
don't get garbage-collected as long as the view is on screen... -
@omz Thanks a lot for your help! The latest change did it for me.
-
@omz: That's what the title bar looks like now! :-)