Ios – How to align 2 buttons to left & right border of a UIToolbar

iosobjective cswift

I have been experimenting a bit with Cocoa and Swift (I am a noob) and ran into a problem I cannot seem to solve:

Let's assume I create a UIToolbar and add two buttons to it: 'Cancel' and 'Accept'. I would like to align the Cancel button to the left edge of the UIToolbar (that works, of course, by default) and the Accept button to the right edge. Is there some easy way how to achieve the right alignment? This is what I have currently:

let toolbar = UIToolbar(frame: CGRectMake(0, 0, w, 35))
toolbar.backgroundColor = UIColor(red: 0.75, green: 0.75, blue: 0.75, alpha: 0.95)
let acceptButton = UIBarButtonItem(title: "Accept", style: UIBarButtonItemStyle.Done, target: self, action: "buttonPressed:")
let cancelButton = UIBarButtonItem(title: "Cancel", style: UIBarButtonItemStyle.Plain, target: self, action: "buttonPressed:")
let space = UIBarButtonItem(customView: UIView(frame: CGRectMake(0, 0, 50, 10))) // <- MEH! this is not dynamic!
toolbar.items = [acceptButton, space, cancelButton]
view.addSubview(toolbar)

I am using another UIBarButtonItem to create the space but this would not be dynamic (and many answers here on SO suggest this solution – no good). So I was expecting I would get the UIBarButtonItems' frames but I can't seem to make this work either, they have no .frame property. So in another SO answer this was the suggestion:

UIView *view= (UIView *)[self.toolbar.subviews objectAtIndex:0]; // 0 for the first item

This doesn't work either, seems like no direct subview-child is a UIBarButtonItem (and so it returns the width as 375).

Setting tags is a no go…:

let cancelButton.tag = 1
let acceptButton.tag = 0
let acceptButtonFrame = toolbar.viewWithTag(0)!.frame
let cancelButtonFrame = toolbar.viewWithTag(1)!.frame
println(acceptButtonFrame.width)
println(cancelButtonFrame.width)

Yields in:

375.0

fatal error: unexpectedly found nil while unwrapping an Optional value

(lldb)

Is there some way? :-/ Any help appreciated.

Best Answer

Yes, just add a flexible space in between the two of them.

let toolbar = UIToolbar(frame: CGRectMake(0, 0, w, 35))
toolbar.backgroundColor = UIColor(red: 0.75, green: 0.75, blue: 0.75, alpha: 0.95)
let acceptButton = UIBarButtonItem(title: "Accept", style: UIBarButtonItemStyle.Done, target: self, action: "buttonPressed:")
let cancelButton = UIBarButtonItem(title: "Cancel", style: UIBarButtonItemStyle.Plain, target: self, action: "buttonPressed:")
let flexibleSpace = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: nil, action: nil);
toolbar.items = [acceptButton, flexibleSpace, cancelButton]

Not sure if that is the correct syntax, but the class is still UIBarButtonItem. The only difference is the systemItem is a UIBarButtonSytemItem.FlexibleSpace.

Comments:

So in another SO answer this was the suggestion:

UIView *view= (UIView *)[self.toolbar.subviews objectAtIndex:0]; // 0 for the first item

This doesn't work either, seems like no direct subview-child is a UIBarButtonItem (and so it returns the width as 375).

The reason that doesn't work is because [self.toolbar.subviews objectAtIndex:0] is Objective-C code, not Swift code.