Get the width of a UIBarButtonItem - iphone

I'm trying to get the width of a UIBarButtonItem.
This doesn't work:
barbuttonitem.customView.frame.size.width
And this won't work, either:
barbuttonitem.width

What about this:
UIBarButtonItem *item = /*...*/;
UIView *view = [item valueForKey:#"view"];
CGFloat width = view? [view frame].size.width : (CGFloat)0.0;

I had the same problem. After a lot of tries I found something that worked!
In my specific case, I needed to get the width of the first UIBarButtonItem from the navigation controller's toolbar, but you can easily adapt it to your likings:
UIToolbar *toolbar = self.navigationController.toolbar; // or whatever toolbar you need
UIView *view = (UIView *)[toolbar.subviews objectAtIndex:0]; // 0 for the first item
double itemWidth = view.bounds.size.width;
Please note: I had to use this code in viewDidLoad to get a proper value. In the init it returns 0.0
Arthur

In case you are interested in the width on one particular item, the easiest way is to have two IBOutlets: one for the button and the other for the corresponding bar button item. Instead of reading the bar button item width, you will read the button width.
This approach, of course, will not work if you want e.g. to sum the widths in a loop. (By the way, the 1st button starts at x=12 and the distance between two buttons is 10, unless you do something tricky.) Of course, you can have two arrays, but this is just cumbersome.

There are always more than one subview in a toolbar.
In addition to arthurs answer you can iterate over them and check the type of it.
for sv in self.navigationController!.toolbar.subviews {
if sv.isKindOfClass(UIBarButtonItem.self){
let width = sv.bounds.width
}
}

Get directly width in bar button item.
UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"image"] style:UIBarButtonItemStylePlain target:self action:#selector(action:)];
double width = barButtonSetting.image.size.width;

Related

Displaying both a right view and a clear button in UITextField

After adding a right view to a UITextField, I am finding that it refuses to display both the right view and the clear button (having both rightViewMode and clearButtonMode set to UITextFieldViewModeAlways). I see the right view but the clear button is no longer displayed. I made sure that they don't overlap by having overriden clearButtonRectForBounds and clearButtonRectForBounds, to no avail. And if I use the leftView instead of rightView, then no such issue occurs and both the left view and the clear button are displayed.
So although it does not appear to be stated in the documentation, it looks to me like the clear button is only displayed when the right view isn't displayed (and when the text property isn't a blank string). Is this correct and does anyone have a reliable workaround? In the meantime I believe I am stuck with having to create a UIView that overlays my right view on top of a UITextField in order to get what I though I'd be getting from UITextField alone.
you can't display both at the same time , but you can do it like this
UITextField * textfield = [[UITextField alloc] initWithFrame:CGRectMake(10, 100, 300, 40)];
[textfield setBorderStyle:UITextBorderStyleRoundedRect];
UIImageView * imgvw = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"search.jpeg"]];
[imgvw setFrame:CGRectMake(0, 0, 30, 30)];
[textfield setRightView:imgvw];
[textfield setRightViewMode:UITextFieldViewModeUnlessEditing];
[textfield setClearButtonMode:UITextFieldViewModeWhileEditing];
[self.view addSubview:textfield];
Yes you are right. UITextfield has properties like left and right view. If you use clear button it overlaps rightview.
Form Apple doc http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UITextField_Class/Reference/UITextField.html
If your overlay view does not overlap any other sibling views, it receives touch events like any other view. If you specify a control for your view, that control tracks and sends actions as usual. If an overlay view overlaps the clear button, however, the clear button always takes precedence in receiving events. By default, the right overlay view does overlap the clear button.
But like the name says it is a view. So you can create your own view which has 2 buttons on it and set rightview of the textfield. If you wish, you can use delegate methods of the textfield to make your buttons appear and dissapear from the view.
I had the same issue but was easily solved by 'fooling' the UITextField into using the LeftView instead of RightView. This way you can use both clear button and your supposed rightView. All you need to do is subclass UITextField and return the 'right corner' in leftViewRectForBounds and similarly update editingRectForBounds and textRectForBounds. Works perfectly.
I'll back up #strange 's answer (using left view instead of right) with some code:
-(CGRect)leftViewRectForBounds:(CGRect)bounds
{
return CGRectOffset([super leftViewRectForBounds:bounds], bounds.size.width - 30, 0);
}
-(CGRect)clearButtonRectForBounds:(CGRect)bounds
{
return CGRectOffset([super clearButtonRectForBounds:bounds], -30, 0);
}
-(CGRect)editingRectForBounds:(CGRect)bounds
{
CGRect rect = bounds;
rect.origin.x = 10;
rect.size.width -= 60;
return rect;
}
-(CGRect)textRectForBounds:(CGRect)bounds
{
CGRect rect = bounds;
rect.origin.x = 10;
rect.size.width -= 60;
return rect;
}
Note that my right (left) view width is 30.
If you happen to use both left and right view, and clear button, then this solution obviously won't work. In that case you will have to give up using one of those, and use a separate view put next to your UITextField.

Getting the width of a UIBarButtonSystemItem

Is there a way to programmatically get the width of a UIBarButtonSystemItem. The width property always returns 0 for system items. In particular, I want to get the exact width of the editButtonItem property of a UIViewController.
On the iPhone the value is 44 but it is a bit bigger on the iPad and I cannot nail it down.
I got my answer from the link that #JoBu1324 left of a comment.
Here is the code I used.
UIBarButtonItem *item = /*...*/;
UIView *view = [item valueForKey:#"view"];
CGFloat width = view? [view frame].size.width : (CGFloat)0.0;
Obviously UIBarButtonItem doesn't have the frame property. As UIBarButtonItem is a direct subclass of NSObject(which in turn don't have the frame), its impossible to get the frame of UIBarButtonItem from code(documented API's).
You have to rely on some other ways to find the width, as you found 44 is the width of editButtonItem in iPhone ;-)
The UIBarButtonItem has a property width, but it generally is 0 for system buttons. So, I usually use my custom button with my localized string inside the button, or use flexibleSpace UIBarbuttonItem to add a flexible space between left and right buttons. You can calculate the width of string by the method -sizeWithFont of NSString. A flexible space can be obtained by the style UIBarButtonSystemItemFlexibleSpace of UIBarButtonItem.

adding icon between back button & title of navigation bar

I want to put an icon by side of the title of my navigation bar. I'd prefer not to implement it as a custom titleView, because then i'll need to create a custom titleView for each controller i put on the stack (and I have pretty deep nesting). I'm adding currently as an UIImageView to a navigationBar. My problem is to calculate exactly this icon's horizontal position. It depends on the width of the back button, which has each time another title. How do I calculate this back button frame? Googling on it seems doesn't bring any reasonable results.
Thanks in advance
You could calculate the size of a label, with
your text in it and experiment with what the button will add on..
CGSize s = [label sizeWithFont:_font];
I ran into this same issue. My solution was to add the icon as one of the leftBarButtonItems, after a flexible space. This pushes it up against the title.
UIBarButtonItem *space = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil] autorelease];
UIView *icon = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"titleIcon"]] autorelease];
UIBarButtonItem *iconItem = [[[UIBarButtonItem alloc] initWithCustomView:icon] autorelease];
NSArray *items = [NSArray arrayWithObjects: space, iconItem, nil];
self.navigationItem.leftBarButtonItems = items;
self.navigationItem.leftItemsSupplementBackButton = YES;
It's not quite the same as creating a custom titleView, however, in two ways: (1) the title text remains centered if possible, with the icon to its left, rather than centering the title and icon together; and (2) when you push a new view onto the stack, the icon does not slide to the left with the title; instead it fades out, like the other bar button items.
So, depending on your requirements, this solution may or may not work for you. But at least it avoids trying to match the standard title text attributes in a custom title view. (As far as I can discover, there is no automatic way to do that.)

Adding button to left of UISearchBar

I am tearing my hair out on this one. My client wants to add a button to the left of a search bar like the example below:
(source: erik.co.uk)
But I just can't figure out how to do it. Apple don't seem to provide any documented method for adding custom buttons to a UISearchBar, let alone to the left of the search bar.
I've tried hacking around in Interface Builder adding a UIToolbar with a button in it to the left but I cannot find any combination of styles where the two line up properly to give the impression that they are one. There is always what looks like one pixel difference in the vertical alignment as you can see from the picture below:
(source: erik.co.uk)
I've searched around and just can't find the answer, but as we can see from the screenshot it must be possible!
Thank you in advance for your help.
Erik
Use a navigation bar instead of a toolbar. Set the search bar to the navigation bar's title view.
In Interface Builder:
Result:
You can replace the Bookmark image instead, and adjust its offset if necessary.
For example:
[self.searchDisplayController.searchBar setImage:[UIImage imageNamed:#"plus2"] forSearchBarIcon:UISearchBarIconBookmark state:UIControlStateNormal];
[self.searchDisplayController.searchBar setPositionAdjustment:UIOffsetMake(-10, 0) forSearchBarIcon:UISearchBarIconBookmark];
Handle the button event in the delegate method:
- (void)searchBarBookmarkButtonClicked:(UISearchBar *)searchBar
This is how it looks:
The first solution is to use UINavigationBar instead of UIToolbar, as KennyTM noticed. But you may not be satisfied with Navigation bar, like in my case, when I need to use 3 buttons (Navigation bar is allow to use only 2 buttons) - see the left picture. This is how I did it:
Use Toolbar with 3 buttons and Flexible Space Bar Button Item in the place where search bar should be placed.
Put search bar on (not in) the toolbar. To do so in Interface Builder, do not drag & drop the search bar on the toolbar. Instead, put it somewhere nearby and then move it to place using the arrow keys on the keyboard (or by changing X & Y position in Interface Builder).
Search bar left black line under it (see the right picture). To hide it I put one additional view with the height 1px and a white background over it.
It looks a bit dirty for me, so if you have a better solution, let me know.
The easiest solution is to add your SearchBar in TOP of your Toolbar, (not in), I give you the best solution I use in my company eBuildy:
UIBarButtonItem *mySettingsButton = [[UIBarButtonItem alloc] initWithTitle:#"Settings" style:UIBarButtonItemStyleBordered target:self action:#selector(refresh)];
UIBarButtonItem *mySpacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *myRefreshButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:#selector(refresh)];
UIToolbar *myTopToolbar = [[UIToolbar alloc]initWithFrame:CGRectMake(0,0,320,40)];
UISearchBar *mySearchBar = [[UISearchBar alloc]initWithFrame:CGRectMake(70,1,220,40)];
[myTopToolbar setItems:[NSArray arrayWithObjects:mySettingsButton,mySpacer,myRefreshButton, nil] animated:NO];
[self.view addSubview:myTopToolbar];
[self.view addSubview:mySearchBar];
answering an old question here but i was struggling with this one myself recently and found some shortcomings with the other answers for the situation i was trying to address. here's what i did in a subclass of UISearchBar:
first add a UIButton property (here "selectButton"). then override the initWithFrame method and do something similar to the following:
-(id)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame])
{
self.selectButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
self.selectButton.contentEdgeInsets = (UIEdgeInsets){.left=4,.right=4};
[self.selectButton addTarget:self action:#selector(pressedButton:) forControlEvents:UIControlEventTouchUpInside];
self.selectButton.titleLabel.numberOfLines = 1;
self.selectButton.titleLabel.adjustsFontSizeToFitWidth = YES;
self.selectButton.titleLabel.lineBreakMode = UILineBreakModeClip;
[self addSubview:self.selectButton];
[self.selectButton setFrame:CGRectMake(5, 6, 60, 31)];
}
return self;
}
Now you want to override the layout subviews method to resize the searchbar to the appropriate width, depending on whether or not the cancel button is showing. That should look something like this:
-(void)layoutSubviews
{
[super layoutSubviews];
float cancelButtonWidth = 65.0;
UITextField *searchField = [self.subviews objectAtIndex:1];
if (self.showsCancelButton == YES)
[searchField setFrame:CGRectMake(70, 6, self.frame.size.width - 70 - cancelButtonWidth, 31)];
else
[searchField setFrame:CGRectMake(70, 6, self.frame.size.width - 70, 31)];
}
Note that in the above method I added a constant for the cancelButtonWidth. I tried adding code to get the width from [self cancelButton] but that seems only accessible at runtime and doesn't allow the project to compile. In any case this should be a good start for what you need
If you want a custom button on the right, taking place of the Cancel button, just use this code (valid for iOS 9 and up):
[self.searchBar setShowsCancelButton:YES];
[[UIBarButtonItem appearanceWhenContainedIn:[self.searchBar class], nil] setTitle:#""];
[[UIBarButtonItem appearanceWhenContainedIn:[self.searchBar class], nil] setImage:[UIImage imageNamed:#"search"]];

Navigation toolbar rightside two buttons

Does anyone know how to add two system buttons to the top right side of my navigation toolbar? I know that custom buttons can be added, and I really don't understand why the system buttons can't do this too.
And I really need it. I need an add button and an edit button.
Edit to reorder and delete table rows.
Add to add a new row.
I can't use the bottom toolbar because I have a tabbar there.
Could somebody help me out?
Something like this should work (substitute your own images and action methods):
#define ACTIONEDIT 0
#define ACTIONADD 1
...
UISegmentedControl* segmentedControl = [[UISegmentedControl alloc]
initWithItems: [NSArray arrayWithObjects:
[UIImage imageNamed:#"icon-edit.png"],
[UIImage imageNamed:#"icon-add.png"],
nil]
];
[segmentedControl addTarget:self
action:#selector(segmentAction:)
forControlEvents:UIControlEventValueChanged];
segmentedControl.frame = CGRectMake(0, 0, 90, 30);
segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;
segmentedControl.momentary = YES;
[segmentedControl setEnabled:YES forSegmentAtIndex:ACTIONEDIT];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
initWithCustomView:segmentedControl];
...
- (void)segmentAction:(id)sender
{
UISegmentedControl* segCtl = sender;
int action = [segCtl selectedSegmentIndex];
switch (action) {
case ACTIONADD:
[self addToList];
break;
case ACTIONEDIT:
[self editList];
break;
}
}
With the default navigation bar, you can only have three buttons, unless I'm missing something. One on the left, one in the center, and one on the right. Even if you create a smaller button and think you have enough space, the touches will all register to the same button (whichever is linked to the right or left). If you want to get functionality like google's navbars, I would suggest implementing it yourself. It really wouldn't be that difficult, and you would get exactly the functionality that you want. If you decide to do this, I'm sure SO can guide you through difficult parts.
I wonder what would happen if you'd use a custom view for your UINavigationItem:
myViewController.navigationItem.titleView = myCustomView;
I imagine the titleView might expand all the way to the right if you don't have a button there. I'noticed that title text gets more space if there is no right button.
Then you could add a label (for the title) and your two buttons to that custom view.