UISearchBar does not display Cancel button when added as subview to UINavigationBar - iphone

I'm following the answer at https://stackoverflow.com/a/14235624/855680 to animate my UISearchBar which starts off with a smaller width, then expands to the full width of the iPhone screen when active. It expands as expected, except that the Cancel button does not appear at all. I've tried calling setShowsCancelButton:animated in both searchBarTextDidBeginEditing: and searchDisplayControllerWillBeginSearch:, but to no avail. What am I missing? Here's my code:
HomeViewController.h
#import <UIKit/UIKit.h>
#interface HomeViewController : UIViewController <UISearchBarDelegate, UISearchDisplayDelegate>
#end
HomeViewController.m
#import "HomeViewController.h"
#interface HomeViewController ()
#property (strong, nonatomic) UISearchDisplayController *sdc;
#property (strong, nonatomic) UISearchBar *searchBar;
#end
#implementation HomeViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Add dummy buttons to navigation bar.
UIBarButtonItem *btn1 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCompose target:self action:nil];
UIBarButtonItem *btn2 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCamera target:self action:nil];
[self.navigationItem setLeftBarButtonItems:#[btn1, btn2] animated:YES];
// Add UISearchBar.
self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(100, 0, 150, 44)];
self.sdc = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self];
self.sdc.delegate = self;
[self.navigationController.navigationBar addSubview:self.searchBar];
}
// From this point onwards, pretty much copy-paste from the StackOverflow answer.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:#selector(adjustFrame:) name:UIKeyboardWillShowNotification object:nil];
[nc addObserver:self selector:#selector(adjustFrame:) name:UIKeyboardWillHideNotification object:nil];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[nc removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
- (void)adjustFrame:(NSNotification *) notification {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
[UIView setAnimationBeginsFromCurrentState:YES];
if ([[notification name] isEqual:UIKeyboardWillHideNotification]) {
// revert back to the normal state.
self.searchBar.frame = CGRectMake (100, 0, 150, 44);
}
else {
//resize search bar
self.searchBar.frame = CGRectMake (0,0,320,self.searchBar.frame.size.height);
}
[UIView commitAnimations];
}
// Try to catch the editing event and display the Cancel button.
// BOTH DON'T WORK.
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
[searchBar setShowsCancelButton:YES animated:YES];
}
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
[controller.searchBar setShowsCancelButton:YES animated:YES];
}
#end

Figured this out myself!
I tried programmatically creating the UISearchBar and the UISearchDisplayController in a new project, but instead of adding the search bar in the navigation bar, I added it to the main view of a view controller. It worked that way, the Cancel button shows whenever I click on the search bar, except that it doesn't resize back to the original frame when I stop editing--but that's for another discussion. Afterwards, I went back to this project and printed out self.searchBar.showsCancelButton after every line of code where I set it to YES, and it turned out the value is, in fact, YES. So it is the UINavigationBar that, for some reason, does not show the UISearchBar's Cancel button. My solution, then, was to create a fake "Cancel" button in the navigation bar's rightBarButtonItem.
On start, the navigation bar looks like this:
Then, when I click on the search bar, I expand it to a width that's just enough to cover the two left bar button items, but leave some space to keep the right bar button item visible. Then, that right bar button item serves as the Cancel button (I just used a system "Add" button for demo's sake).
When I click on "Search" in the keyboard, or on the plus button, the search bar reverts to its old size and the right bar button item disappears. My full code is below:
HomeViewController.h
#import <UIKit/UIKit.h>
#interface HomeViewController : UIViewController <UISearchBarDelegate>
#end
HomeViewController.m
#import "HomeViewController.h"
#interface HomeViewController ()
#property (strong, nonatomic) UISearchBar *searchBar;
#end
#implementation HomeViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Just some buttons that the search bar will overlap when active.
UIBarButtonItem *btn1 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCompose target:self action:nil];
UIBarButtonItem *btn2 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCamera target:self action:nil];
[self.navigationItem setLeftBarButtonItems:#[btn1, btn2] animated:YES];
self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(100, 0, 150, 44)]; self.searchBar.delegate = self;
[self.navigationController.navigationBar addSubview:self.searchBar];
}
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar {
// Set a fake Cancel button.
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(stopEditing)];
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^(){
self.searchBar.frame = CGRectMake(0, 0, 280, 44);
} completion:nil];
// Bring search bar to the front because adding a right bar button
// item somehow puts it behind the UIBarButtonItems.
[self.navigationController.navigationBar bringSubviewToFront:self.searchBar];
return YES;
}
- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar {
// Go back to the old frame.
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^(){
self.searchBar.frame = CGRectMake(100, 0, 150, 44);
} completion:nil];
// Remove the "Cancel" button.
self.navigationItem.rightBarButtonItem = nil;
[self.navigationController.navigationBar bringSubviewToFront:self.searchBar];
return YES;
}
- (void)stopEditing {
[self.searchBar resignFirstResponder];
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
[self stopEditing];
}
#end

You should set delegate of UISearchBar
self.searchBar.delegate = self;
- (void)viewDidLoad {
[super viewDidLoad];
// Add dummy buttons to navigation bar.
UIBarButtonItem *btn1 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCompose target:self action:nil];
UIBarButtonItem *btn2 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCamera target:self action:nil];
[self.navigationItem setLeftBarButtonItems:#[btn1, btn2] animated:YES];
// Add UISearchBar.
self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(100, 0, 150, 44)];
self.sdc = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self];
self.sdc.delegate = self;
self.searchBar.delegate = self;
[self.navigationController.navigationBar addSubview:self.searchBar];
}

Is there any reason why you are putting the search bar in the navigation bar? I think your code should work if your were placing the search bar somewhere in the view, the navigation bar is tricky if you wish to put something in it you generally define views or items to replace the objects in your navbar like the rightbarbuttonitem / leftbarbuttonitem or title
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:infoButton];

Here is the simplest solution for iOS7.
- (void) viewDidLoad
{
self.navigationItem.leftBarButtonItem = LEFT_ITEM;
self.navigationItem.rightBarButtonItem = SEARCH_ICON;
}
- (void)searchIconPressed
{
self.navigationItem.leftBarButtonItem = nil;
self.navigationItem.rightBarButtonItem = nil;
self.navigationItem.titleView = self.searchBar;
UIBarButtonItem* cancel = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:#selector(cancelSearch)];
[self.navigationItem setRightBarButtonItem:cancel animated:YES];
[self.searchBar becomeFirstResponder];
}
Now you will have a search bar with Cancel button also.

Related

Toolbar barbutton icon(pagecurl) not loaded

I am loading a mapview. I have created a barbutton in the bottombar toolbar of the view, setting its identifier as pagecurl. As expected, a barbutton with page curl icon is loaded. From this mapview I move to anotherv view, by clicking on annotations in the mapview. And then I return to the mapview. At that time my pagecurl barbutton icon(pagecurl icon) is not displayed, and my barbutton width is also reduced. I am not able to figure out the problem.
- (void)viewDidLoad
{
[super viewDidLoad];
if(isSingleContactSelected)
{
[self.navigationController.navigationBar setBarStyle:UIBarStyleDefault];
self.navigationItem.leftBarButtonItem = self.cancelButton ;
[self.cancelButton setTarget:self];
[self.cancelButton setAction:#selector(onClose:)];
[addressFieldSearchBar setFrame:CGRectMake(66, 0, 256, 44)];
addressFieldSearchBar.delegate =self;
[self.navigationController.navigationBar setBarStyle:UIBarStyleDefault];
[self.navigationController.navigationBar addSubview:addressFieldSearchBar];
[searchDirectionSegmentedControl addTarget:self action:#selector(segmentAction:) forControlEvents:UIControlEventValueChanged];
UIBarButtonItem *searchDirectionSegmentedButton = [[UIBarButtonItem alloc] initWithCustomView:searchDirectionSegmentedControl];
flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
NSArray *toolbarItems = [NSArray arrayWithObjects: compassButton , flexibleSpace, searchDirectionSegmentedButton, flexibleSpace, pageButton, nil];
[self setToolbarItems:toolbarItems];
self.navigationController.toolbarHidden = NO;
[compassButton release];
[pageButton release];
[searchDirectionSegmentedControl release];
mapView = [[MKMapView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
mapView.delegate=self;
[self.view addSubview:mapView];
}
}
- (void)viewDidUnload{
[super viewDidUnload];
}
-(void) viewWillAppear:(BOOL)animated{
if(isSingleContactSelected){
[self.navigationController.navigationBar setHidden:NO];
[self.navigationController.toolbar setHidden:NO];
[self.navigationController.toolbar setBarStyle:UIBarStyleDefault];
[self.addressFieldSearchBar setHidden:NO];
}
}
-(void) viewWillDisappear:(BOOL)animated{
if(isSingleContactSelected){
[self.addressFieldSearchBar setHidden:YES];
[self.navigationController.toolbar setHidden:YES];
}
}
Though its a long shot, it could be something to do with the setHidden calls in your appear and disappear methods.
[self.navigationController.toolbar setHidden:YES];
[self.navigationController.toolbar setHidden:NO];
The best way to do this using UIViewController's "-setHidesBottomBarWhenPushed:" method.
Maybe try some NSLog() in -viewWillAppear:
// If pageButton is an instance variable
NSLog(#"%#",pageButton);
// Enumerate through all toolbar items.
// Check to see if NSLog output differs after pushing/popping this view controller.
for (UIBarButtonItem *item in [self.navigationController.toolbar.items])
{
NSLog(#"%#",item);
}
setHidesBottomBarWhenPushed method did the trick in this case.

How to refresh UITableViewController in iphone/iPad

-(void)reloadView{
NSLog(#"IN RELOAD VIEW ");
[[self tableView] reloadData];
}
- (void)viewDidLoad {
UIBarButtonItem * barButton = [[UIBarButtonItem alloc] initWithCustomView:activityIndicator];
[self navigationItem].rightBarButtonItem = barButton;
[activityIndicator startAnimating];
[self callStudentsWebService];
[super viewDidLoad];
self.clearsSelectionOnViewWillAppear = NO;
self.contentSizeForViewInPopover = CGSizeMake(320.0, 600.0);
[self setTitle:#"Students"];
self.navigationController.toolbarHidden = NO;
self.navigationController.toolbar.barStyle = UIBarStyleBlack;
UIBarButtonItem *add = [[UIBarButtonItem alloc] initWithTitle:#"Add Student" style:UIBarButtonItemStyleBordered target:self action:#selector(addStudent)];
UIBarButtonItem *flexible = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
UIBarButtonItem *import = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemOrganize target:self action:#selector(addStudent)];
NSArray* toolbarItems = [NSArray arrayWithObjects:add,flexible,import, nil];
self.toolbarItems = toolbarItems;
}
Above is my Code in a Class which is a subclass of UITableViewController.
Now my problem is when I come onto the screen First Time it get the records from web service from the method [self callStudentsWebService] . Now i have a button in uitoolbar in the same view which updates the list . I am calling the reloadView method to refresh the List but it calls the numberOfRowsInSection and viewDidLoad method but does not call cellForRowAtIndexPath method. But when i goto other screen and come back again it does refresh my List from start.
What could be the reason ?? Please help i am new in iPad Development. And i m doing all this in iPad.
Thanks alot
EDITED
#interface StudentsList : UITableViewController {
DetailViewController *detailViewController;
UIToolbar *toolbar;
UIActivityIndicatorView *activityIndicator;
}
May be your data.count is 0? If there is some data it should call cellForRowAtIndexPath after [[self tableView] reloadData];
Do the refresh code in another separate method -(void)refreshData then call this whenever you want such as in -viewDidLoad or on a button tap...
Try to call -(void)reloadView from
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self reloadView];
}

How to add a UIToolbar to a UITableViewController programmatically?

I have opted to use a UITableViewController without a nib. I need a UIToolbar at the bottom with two buttons. What is the simplest way of doing that?
P.S. I know that I can easily use a UIViewController and add a UITableView however I want things to look consistent across the app.
Can someone help?
I saw the following example and I am not sure on its validity:
(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
//Initialize the toolbar
toolbar = [[UIToolbar alloc] init]; toolbar.barStyle = UIBarStyleDefault;
//Set the toolbar to fit the width of the app.
[toolbar sizeToFit];
//Caclulate the height of the toolbar
CGFloat toolbarHeight = [toolbar frame].size.height;
//Get the bounds of the parent view
CGRect rootViewBounds = self.parentViewController.view.bounds;
//Get the height of the parent view.
CGFloat rootViewHeight = CGRectGetHeight(rootViewBounds);
//Get the width of the parent view,
CGFloat rootViewWidth = CGRectGetWidth(rootViewBounds);
//Create a rectangle for the toolbar
CGRect rectArea = CGRectMake(0, rootViewHeight - toolbarHeight, rootViewWidth, toolbarHeight);
//Reposition and resize the receiver
[toolbar setFrame:rectArea];
//Create a button
UIBarButtonItem *infoButton = [[UIBarButtonItem alloc] initWithTitle:#"back"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(info_clicked:)];
[toolbar setItems:[NSArray arrayWithObjects:infoButton,nil]];
//Add the toolbar as a subview to the navigation controller.
[self.navigationController.view addSubview:toolbar];
[[self tableView] reloadData];
}
(void) info_clicked:(id)sender {
[self.navigationController popViewControllerAnimated:YES];
[toolbar removeFromSuperview];
}
The simpler thing to do is to build your project on top of a UINavigationController. It already has a toolbar, it's just hidden by default. You can reveal it by toggling the toolbarHidden property, and your table view controller will be able to use it as long as it's in the navigation controller hierarchy.
In your app delegate, or in the object your app delegate passes control to, create the navigation controller with your UITableViewController as the root view controller:
- ( void )application: (UIApplication *)application
didFinishLaunchingWithOptions: (NSDictionary *)options
{
MyTableViewController *tableViewController;
UINavigationController *navController;
tableViewController = [[ MyTableViewController alloc ]
initWithStyle: UITableViewStylePlain ];
navController = [[ UINavigationController alloc ]
initWithRootViewController: tableViewController ];
[ tableViewController release ];
/* ensure that the toolbar is visible */
navController.toolbarHidden = NO;
self.navigationController = navController;
[ navController release ];
[ self.window addSubview: self.navigationController.view ];
[ self.window makeKeyAndVisible ];
}
Then set the toolbar items in your MyTableViewController object:
- ( void )viewDidLoad
{
UIBarButtonItem *buttonItem;
buttonItem = [[ UIBarButtonItem alloc ] initWithTitle: #"Back"
style: UIBarButtonItemStyleBordered
target: self
action: #selector( goBack: ) ];
self.toolbarItems = [ NSArray arrayWithObject: buttonItem ];
[ buttonItem release ];
/* ... additional setup ... */
}
You also can just check "shows toolbar" option in NavigationController attributes inspector.
Here is a simple example, which may help
UIBarButtonItem *spaceItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *trashItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemTrash target:self action:#selector(deleteMessages)];
UIBarButtonItem *composeItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCompose target:self action:#selector(composeMail)];
NSArray *toolbarItems = [NSMutableArray arrayWithObjects:spaceItem, trashItem,spaceItem,composeItem,nil];
self.navigationController.toolbarHidden = NO;
[self setToolbarItems:toolbarItems];
Thanks,
prodeveloper

Subview above toolbar

I use [self setToolbarItems:items]; to add buttons to toolbar I want to display subview to be above all even teh toolbar I use the following code , but it be behind the tool bar
any suggestion how to make it above
pickerView = [[UIPickerView alloc]initWithFrame:CGRectMake(0,744, 768, 216)];
mytab = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 700, 768, 44)];
pickerView.alpha=0.0;
mytab.alpha=0.0;
pickerView.showsSelectionIndicator = YES;
pickerView.dataSource = self;
pickerView.delegate = self;
[self.view addSubview:pickerView];
[self.view bringSubviewToFront:pickerView];
mytab.tintColor=[UIColor blackColor];
UIBarButtonItem * bt1=[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(_cancel)];
UIBarButtonItem * flx=[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
NSArray *arr=[[NSArray alloc] initWithObjects:flx,bt1,nil];
[mytab setItems:arr];
[self.view addSubview:mytab];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.3];
pickerView.alpha=1.0;
mytab.alpha=1.0;
[UIView commitAnimations];
[pickerView release];
[mytab release];
[bt1 release];
[flx release];
[arr release];
you are adding the tool bar after the picker ... add the tool bar fist and then the picker...
actually i think you should be presenting the picker as a modal view controller's view..
EDIT:
You can see how a modal view controller is displayed in appple's sample codes. Look for presentModalViewController method
SimpleEKDemo
Metronome
I simply subtract 44 from the Y of pickerview and toolbar

How can I create a button with a UIActivityIndicator in my navigation bar with the same style as normal buttons?

All of the examples I've seen on here and other sites involved creating a UIActivityIndicatorView and loading it with something like:
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc]
initWithCustomView:myActivityIndicatorView
target:nil
action:nil]
autorelease];
However, that just creates a plain activity indicator in the navigation bar. What I want to do is have a button that looks just like the normal UIBarButtonSystemItem buttons but with an activity indicator instead of one of the default images. I've tried doing initWithImage and initWithTitle with nil images or titles and then adding the activity indicator as a subview, but that doesn't work.
Any ideas?
My Solution is to create a subclass of UIButton:
in SOActivityButton.h:
#interface SOActivityButton : UIButton
{
UIActivityIndicatorView* activityIndicator;
}
#end
in SOActivityButton.m:
#implementation SOActivityButton
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
CGRect innerFrame = CGRectInset(frame, 8.0f, 8.0f);
activityIndicator = [[UIActivityIndicatorView alloc]
initWithFrame:innerFrame];
activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhite;
[self addSubview:activityIndicator];
}
return self;
}
- (void)dealloc
{
[activityIndicator release], activityIndicator = nil;
[super dealloc];
}
- (void) startAnimating
{
[activityIndicator startAnimating];
}
- (void) stopAnimating
{
[activityIndicator stopAnimating];
}
#end
Then to use it:
SOActivityButton* activityButton = [[SOActivityButton alloc]
initWithFrame:CGRectMake(0.0f, 0.0f, 32.0f, 32.0f)];
[activityButton setImage:[UIImage imageNamed:#"button-background.png"]
forState:UIControlStateNormal];
[activityButton addTarget:self action:#selector(myAction:)
forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *activityBarButtonItem = [[UIBarButtonItem alloc]
initWithCustomView:activityButton];
[activityButton release];
self.navigationItem.rightBarButtonItem = activityBarButtonItem;
[activityBarButtonItem release];
You will need to find or create a button-background.png. The PSD here should have one.
have you tried creating a UIButton in the button bar and then adding an activity indicator as a subView of the UIButton?
I have this working and it is very simple. Just place the activity indicator where you want it with IB, but make sure it's lower in the list than the bar you want it on, and is at the "top level" (not a subview of anything else). Then control it in code via an outlet.
Here's something that can help:
activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
[activityIndicator startAnimating];
UIBarButtonItem *activityItem = [[UIBarButtonItem alloc] initWithCustomView:activityIndicator];
[activityIndicator release];
self.navigationItem.rightBarButtonItem = activityItem;
[activityItem release];
[activityIndicator startAnimating];