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"]];
Related
This is a bit of a hack, but trying to get a badge on a toolbaritem. Almost there, but it appears in the back, tried normal methods to get to front with no luck.
deletedCountBadge = [CustomBadge customBadgeWithString:#"0"];
deletedCountBadge.frame = CGRectMake(100,10,25,25);
UIView *view = (UIView *)[bottomToolBar.subviews objectAtIndex:0];
[view addSubview:deletedCountBadge];
Got it to work with a couple of different pointers on the net.
1) If you are moving the toolbar around at all (.frame=) make sure to set the items after the layout
2) Can use negative spacing items to get it to show up where you want.
UIBarButtonItem *deleteBadge = [BarButtonItemBadge barButtonItemBadge:#"1" insideColor:[UIColor blueColor]];
UIBarButtonItem *negativeSeperator = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
negativeSeperator.width = -30;
[bottomToolBarDictionaryButtonItems addObject:deleteBadge];
[bottomToolBarDictionaryButtonItems addObject:negativeSeperator];
// after any layout, of tool bar
[bottomToolBar setItems:bottomToolBarDictionaryButtonItems];
I am trying to display a UISearchBar in place of the button shown on the right side of the UINavigationItem. I am using this code:
- (void)viewDidLoad {
[super viewDidLoad];
UISearchBar *searchBar = [[UISearchBar alloc] init];
UIBarButtonItem *navRight = [[UIBarButtonItem alloc] initWithCustomView:searchBar];
[[self navigationItem] setRightBarButtonItem:navRight];
[searchBar release];
[navRight release];
}
However, the displayed search bar is only a few pixels wide. I need to make it wider.
Am I going about this the wrong way? Is it at all possible?
You need to set the UISearchBar's frame at some point.
e.x.
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 200, 20)];
EDIT: Regarding the button-esque artifact under the UISearchBar
The button-y thing that you see under the UISearchBar is actually a background element of the UISearchBar, not the result of the search bar overlapping a UIBarButtonItem. Since there doesn't seem to be a nice way to hide this (and I hope someone can come along and correct me), I can only point you in the direction of a hack that comes with the usual perils of SDK version dependence, inelegance, and potential for more headaches.
EDIT 2: Another potential solution
You could also set your UISearchBar's frame's height to 44 (the height of the navbar) and then the background should blend nicely with the navbar. Based off this SO post I recently saw.
How do I get a UISegmentedControl that is like the one in the Mail App, so that it is the same colour as UIToolbar buttons (as if both segments were in the selected state).
I want to use the segmented control for exactly the same purpose as Mail.
(on the iPad, so a grey not blue color)
This is code from Apple Sample codes... NavBar and both the images used in the code..
you shoud be able to get exact same view as mail App.
// "Segmented" control to the right
UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] initWithItems:
[NSArray arrayWithObjects:
[UIImage imageNamed:#"up.png"],
[UIImage imageNamed:#"down.png"],
nil]];
[segmentedControl addTarget:self action:#selector(segmentAction:) forControlEvents:UIControlEventValueChanged];
segmentedControl.frame = CGRectMake(0, 0, 90, 30);
segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;
segmentedControl.momentary = YES;
defaultTintColor = [segmentedControl.tintColor retain]; // keep track of this for later
UIBarButtonItem *segmentBarItem = [[UIBarButtonItem alloc] initWithCustomView:segmentedControl];
[segmentedControl release];
self.navigationItem.rightBarButtonItem = segmentBarItem;
[segmentBarItem release];
You seek the tintColor property!
When you use a UISegmentedControl you can change its tint color to any color you can dream up. So, if you added the UISegmentedControl in Interface Builder then you would style it in your - (void)viewWillAppear:(BOOL)animated method as such (assuming you had it hooked up to a #synthesized ivar:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// Set the tintColor to match the navigation bar
self.mySegmentedControl.tintColor = [UIColor colorWithRed:.94 green:.94 blue:.94 alpha:1];
... do whatever else in your viewWillAppear ...
}
Now obviously you will want to play with the red, green, blue, and alpha's that I've put in the sample code above, but you can literally tint the UISegmentedController any color you would like (or make it as transparent as you would like), so it's just a matter of finding the RGBA values that look perfect to you.
Remember that per Apple's docs that the default value of this property is nil (no color). UISegmentedControl uses this property only if the style of the segmented control is UISegmentedControlStyleBar.
Good luck!
I dont know exactly what you mean.. but i believe the "UISegmentedControlStyleBar" as segmentedControlStyle could it be.
segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar
You can set this property in the IB too! (It's the property called "style")
The style I'm looking for is undocumented: it is style 4.
It looks like he up/down control here: http://media.mobilemeandering.com/wp-content/uploads/2010/04/ipad-mail-message-2.png
(not my image btw)
It basically makes all segments look selected, it's intended for momentary pushes, and is effectively multiple tool bar buttons pushed up together.
So it can't be set in IB but must be set in code or manually in the nib/xib file, by opening the nib as a text file.
I'm not sure I exactly understand what you're trying to do, but I'll give it a shot.
The solution is not obvious, you need to use a UISearchDisplayController in order to get a matching UISearchBar and UISegmentedControl.
See the TableSearch sample code for an example.
I have a UIBarButtonItem on a navigation bar. I'd like to make it arrow shaped. By that I mean I want it to be square except for a pointy side. Does anyone know how to do this?
Thanks!
I wrestled with several approaches on this one and finally figured it out using all the answers from several sources. There are a couple of tricks; this snippet will show it all (I was creating a custom rightNavButton, but you can adapt this easily for any UIBarButtonItem):
// Produces a nice "right arrow" style button that mirrors the back arrow
// automatically added by the navController
//
UIImage *buttonImage = [UIImage imageNamed:#"forwardButton.png"];
UIButton *forwardButton = [UIButton buttonWithType:UIButtonTypeCustom];
[forwardButton setBackgroundImage:buttonImage forState:UIControlStateNormal];
[forwardButton setTitle:#"Meter" forState:UIControlStateNormal];
forwardButton.font = [UIFont boldSystemFontOfSize:12];
forwardButton.frame = CGRectMake(0, 0, buttonImage.size.width, buttonImage.size.height);
[forwardButton addTarget:self action:#selector(showMeter)
forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
initWithCustomView:forwardButton];
Note: it turns out that if you try to set the target and action directly on the button item after assigning a custom view using a UIButton that it won't take - you have to set the target and action on the button itself - apparently the navBar uses the button provided verbatim.
This is the image I was using on the opaque black navBar (you can use anything, obviously): http://raretiger.com/images/forwardbutton.png
Make a custom background. See Creating a left-arrow button (like UINavigationBar's "back" style) on a UIToolbar for how.
You may -pushNavigationItem:animated: to make the built-in back button appear, although you cannot assign custom actions to it.
For undocumented methods, you may use
UIButton* backButton = [UIButton buttonWithType:101];
to create a back "button".
You could create an UIImageView or an UIButton with the required image then use:
[[UIBarButtonItem alloc] initWithCustomView: arrowButton];
Hope this helps.
continuing on #Plamen Dragozov's idea: i noticed if you leave the uibutton as is, it would trigger a picture adjustment when touched and that is quite the opposite what a normal navigation back button does. So what I did was to uncheck "highlighted adjusts Image" and it works like a charm.
hope this helps newbies like me.
More elegant solution:
UIBarButtonItem * backButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Title" style:UIBarButtonItemStylePlain target:self action:#selector(back:)];
[backButtonItem setBackgroundImage:[UIImage imageNamed:#"back.png"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
self.navigationItem.leftBarButtonItem = backButtonItem;
My problem was renaming the back button that appears on the pushed view controller. I found a dirty workaround and if you ignore the non-ideal animation problem, you'll get a back button with the title you want.
The trick is to change the title of the first VC in viewWillDisappear and re-set it of course in viewWillAppear
(Needless to say, by default, if there is no leftBarButtonItem set, UINavigationController will show a back button with the title of the VC that pushed the current VC)
In the VC where you push your current VC, do
-(void) viewWillDisappear:(BOOL) animated {
[super viewWillDisappear:animated];
self.title = #"Back";
}
-(void) viewWillAppear:(BOOL) animated {
[super viewWillAppear:animated];
self.title = #"Original Title";
}
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.