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_utilthough. This way, you can create a
ButtonItemthat 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
actionmethod of the
ButtonItem'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
actionof the individual buttons that are added to the container.
The first problem is that you cannot pass an
actionas a keyword argument to the
ui.Buttonconstructor. It's a bit unfortunate that this is silently ignored instead of raising an exception... but you have to assign the
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_listreturns. 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 the
btn_containerview somehow. I would suggest that you simply assign it as an attribute of
v.button_container = btn_container). This requires some refactoring of your
get_condensed_listmethod. 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 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()
@JonB Sorry for addressing you directly, but you seem to have an abundance of experience in this area. Do you have any idea what could be wrong in my implementation of @omz's ObjC approach (see my gist link below)? Thanks a lot!
@marcus67 A simple one-line fix would be to add a reference to the
condenserobject 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
condenserdon'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! :-)