I have a UIViewController with a standard UITableView and Search bar with Search delegate added. The view has a segmented control in the navigation bar, when the user changes the segmented control I would like to hide the searchBar.
The code I am using is:
- (void)segChange {
if ([segmentedControl selectedSegmentIndex] == 0) {
[[[self searchDisplayController] searchBar] setHidden:YES];
// This does not work:
[[[self searchDisplayController] searchResultsTableView] setContentOffset:CGPointZero animated:NO];
}
else {
[[[self searchDisplayController] searchBar] setHidden:NO];
}
}
The code hides the searchBar fine, but it also leaves a nasty white space at the top of the table view.... any ideas on how to get rid of it???
Thanks
This code solved the problem:
- (void)segChange {
if ([segmentedControl selectedSegmentIndex] == 0) {
[self.myTableView setTableHeaderView:nil];
}
else {
[self.myTableView setTableHeaderView:[[self searchDisplayController] searchBar]];
}
}
Rather than hiding the segmented control try setting its frame to CGRectZero
Related
I have a form and an input field of type date <input type='date'> . When the field is tapped the datepicker comes into view but the web view scrolls up automatically as the picker becomes visible.
I have tried disabling scrolling and bouncing on the web view but that didn't work i.e. webView.scrollView.scrollEnabled = NO;
webView.scrollView.bounces = NO;
Any ideas?
You could try this:
UIView * v = [[webView subviews] lastObject];
if([v isKindOfClass:[UIScrollView class] ]) {
if ([v respondsToSelector:#selector(setScrollEnabled]) {
[v setScrollEnabled:NO];
}
if ([v respondsToSelector:#selector(bounces)]) {
[v setBounces:NO];
}
}
Here you just check if the view responds to those methods and, if yes, call them.
[self showMenu:view forCell:cell animated:NO];
How do you override this method in Three20 so that it won't do a sliding animation and it won't remove the contents of your TableViewCell?
I just want to show menu like what Facebook App does when showing the Comment and Like Menu
Well if ever, there's still someone out there having this problem, what I did was to override both of these methods, and somehow, it's working fine..
- (void)showMenu:(UIView *)view forCell:(UITableViewCell *)cell animated:(BOOL)animated
{
[self hideMenu:NO];
_menuView = [view retain];
_menuCell = [cell retain];
[_menuCell.contentView addSubview:_menuView];
}
- (void)hideMenu:(BOOL)animated {
if (_menuView) {
[_menuView removeFromSuperview];
TT_RELEASE_SAFELY(_menuView);
TT_RELEASE_SAFELY(_menuCell);
}
}
When a user has tapped in a search bar, hasn't entered any text, and the screen has been darkened (but the tableview has not appeared yet), I would like to put a button on the screen to allow the user to navigate away to some other functionality. Is that possible?
Thanks!
This is quite tricky. The _dimmingView is private to the searchDisplayController and it goes above all subviews. What you can do is cover it with your custom view every time it appears ([searchString length] == 0 and DidBeginSearch)
(tempView's frame is set for UISearchBar placed on table's tableViewHeader)
- (void)viewDidLoad {
tempView = [[UIView alloc] initWith...];
// tempView setup
...
}
- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller {
[tempView setFrame:CGRectMake(0, self.searchDisplayController.searchBar.frame.size.height, 320, self.searchDisplayController.searchResultsTableView.frame.size.height)];
[self.searchDisplayController.searchContentsController.view addSubview:tempView];
...
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
if ([searchString length] == 0)
[self.searchDisplayController.searchContentsController.view addSubview:tempView];
else
[tempView removeFromSuperview];
...
}
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller {
if (tempView && tempView.superview)
[tempView removeFromSuperview];
...
}
Notes: I tried creating a new instance on DidBeginSearch and releasing it on DidEndSearch, and it worked only for the 1st call! Weird...
Hope this helps
Right, to begin my question, here's some screenies of the problem already solved by the Spotify app:
Spotify's Step 1: Standard UISearchBar not in editing mode.
Spotify's Step 2: UISearchBar now in editing mode. Search term entered. Cancel button slides in from the right, and the clear button (grey x) appears.
Spotify's Step 3: Cancel button pressed; keyboard slides out and the search bar is no longer in editing mode. Search term remains and the grey x button is now hidden.
At present, the following code fires off when my cancel button is pressed:
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
[searchBar resignFirstResponder];
[searchBar setShowsCancelButton:NO animated:YES];
}
Which results in:
My Step 3: Search bar now not in editing mode. Cancel button and keyboard has slid out. Search term remains but so does the grey x.
So, my question is this: given that -resignFirstResponder (and -endEditing:, FYI) does not hide the grey x button when a search bar has had text entered into it, how does one hide it?
Thanks again, friends.
The problem is that UISearchBar doesn't expose it's text field, and manages the properties on the text field itself. Sometimes, the values of the properties aren't what you want.
For instance, in my own app, I wanted the keyboard style for my search bar to use the transparent alert style.
My solution was to walk through the subviews of the search bar until you find the text field. You should then be able to set the clearButtonMode property, using something like UITextFieldViewModeWhileEditing as a parameter.
This should make it so that the clear button is only shown while the text field is editing.
You want to do this on viewDidLoad or something early, so it's set before you start using it (but after the search bar is initialised.
for (UIView *subview in searchBar.subviews)
{
if ([subview conformsToProtocol:#protocol(UITextInputTraits)])
{
[(UITextField *)subview setClearButtonMode:UITextFieldViewModeWhileEditing];
}
}
Looks like iOS 7 changed the view hierarchy of UISearchBar, and the text box is deeper in the view (The above solution didn't work for me). However, modifying the above solution to traverse the whole hierarchy works:
[self configureSearchBarView:[self searchBar]];
- (void)configureSearchBarView:(UIView*)view {
for (UIView *subview in [view subviews]){
[self configureSearchBarView:subview];
}
if ([view conformsToProtocol:#protocol(UITextInputTraits)]) {
[(UITextField *)view setClearButtonMode:UITextFieldViewModeWhileEditing];
}
}
I'm building upon the previous answers because I started seeing crashes on iOS 7.1 unless I made the following change. I added an additional call to respondsToSelector for each view to make sure that setClearButtonMode: can be called. I observed an instance of UISearchBar getting passed in, which seems to conform to the UITextInputTraits protocol yet does not have the setClearButtonMode: selector, so a crash occurred. An instance of UISearchBarTextField also gets passed in and is the actual object for which to call setClearButtonMode:.
- (void)removeClearButtonFromView:(UIView *)view
{
if (!view)
{
return;
}
for (UIView *subview in view.subviews)
{
[self removeClearButtonFromView:subview];
}
if ([view conformsToProtocol:#protocol(UITextInputTraits)])
{
UITextField *textView = (UITextField *)view;
if ([textView respondsToSelector:#selector(setClearButtonMode:)])
{
[textView setClearButtonMode:UITextFieldViewModeNever];
}
}
}
You need to get the textField of the Search Bar
UITextField *textField = [searchBar valueForKey:#"_searchField"];
textField.clearButtonMode = UITextFieldViewModeNever;
use in - (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar method.
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
UITextField *textField = [searchBar valueForKey:#"_searchField"];
textField.clearButtonMode = UITextFieldViewModeNever;
}
A better way to do this in iOS7 is:
[[UITextField appearanceWhenContainedIn:[UISearchBar class], nil] setClearButtonMode:UITextFieldViewModeWhileEditing];
To expand on Jadariens answer if you never want the grey x to appear you need to use the following
for (UIView *subview in searchBar.subviews)
{
if ([subview conformsToProtocol:#protocol(UITextInputTraits)])
{
[(UITextField *)subview setClearButtonMode:UITextFieldViewModeNever];
}
}
Accepted answer does not work on iOS7+, here is the modified version as a Swift extension
extension UIView {
class func removeClearButton(svs: [UIView]) {
for sv in svs {
if let tv = sv as? UITextField where sv.conformsToProtocol(UITextInputTraits) {
tv.clearButtonMode = .Never
return
} else {
UIView.removeClearButton(sv.subviews)
}
}
}
}
Usage
UIView.removeClearButton(searchBar.subviews)
Hers is a category I wrote that does this
Category
#implementation UISearchBar (Additions)
- (void)setClearButtonMode:(UITextFieldViewMode)viewMode {
UITextField *textField = [self findTextFieldInView:self];
[textField setClearButtonMode:viewMode];
}
- (UITextField *)findTextFieldInView:(UIView *)view {
for (UIView *subview in view.subviews) {
if ([subview isKindOfClass:[UITextField class]] ||
[subview.class isSubclassOfClass:[UITextField class]]) {
return (UITextField *)subview;
}
UITextField *textField = [self findTextFieldInView:subview];
if (textField) {
return textField;
}
}
return nil;
}
#end
Usage
[searchBar setClearButtonMode:UITextFieldViewModeWhileEditing];
There's a better way than any of the answers here, and you don't have to use private APIs or traverse subviews to do it.
UISearchBar has a built-in API for doing this:
[UISearchBar setImage:forSearchBarIcon:state]
The SearchBar icon key you want is UISearchBarIconClear, and you want the UIControlStateNormal state. Then give it a clear image for the image, and you're done.
So, it should look like this:
[searchBar setImage:clearImage forSearchBarIcon:UISearchBarIconClear state:UIControlStateNormal];
For the (x) icon in searchBar. You can use below delegate method.
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
searchBar.showsCancelButton = YES;
}
for (UIView *subview in _search_bar.subviews)
{
NSLog(#"%#",subview.subviews);
for (UIView *subview11 in subview.subviews)
{
if ([subview11 conformsToProtocol:#protocol(UITextInputTraits)])
{
[(UITextField *)subview11 setClearButtonMode:UITextFieldViewModeNever];
}
}
}
Usually, a UISearchDisplayController, when activated, dims the tableView and focuses the searchBar. As soon as you enter text into the searchBar, it creates a searchResultsTableView that displays between the searchBar and the keyboard. The searchDisplayController's delegate gets called when this second UITableView is loaded/shown/hidden/unloaded. Usually it shows live search results or autocompletion entries while typing.
In my app, I want to search a webservice and I don't want to call the webservice for each letter the user enters. Therefore, I want to entirely disable the searchResultsTableView and keep the dimmed black overlay while he enters text. I would then trigger the search (with a loading screen) once he hits the search button.
Just returning zero rows for the searchResultsTableView doesn't look nice since it displays an empty searchResultsTableView with a "no results" message. I tried to hide the table when it appears (searchDisplayController:didLoadSearchResultsTableView:) which works, but the blacked dimmed overlay is also hidden so that the underlying tableView is completely visible again.
Any ideas besides recreating the UISearchDisplayController functionality from scratch?
here is a little trick that i just figured out
and also you have to return 0 results while editing searchstring
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
savedSearchTerm = searchString;
[controller.searchResultsTableView setBackgroundColor:[UIColor colorWithWhite:0.0 alpha:0.8]];
[controller.searchResultsTableView setRowHeight:800];
[controller.searchResultsTableView setScrollEnabled:NO];
return NO;
}
- (void)searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView
{
// undo the changes above to prevent artefacts reported below by mclin
}
i think you'll figure out what to do next
Have you tried this:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(_lookup:) object:nil];
[self performSelector:#selector(_lookup:) withObject:txt afterDelay:0.20];
This way, if the user types another char within 1/5sec, you only make one web call.
Nothing of the above seemed to work well in the end, so I came up with the following (you have to call removeTableHeader when you are ready to display your results):
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar {
[self setTableHeader];
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
[self setTableHeader];
}
- (void)setTableHeader {
UIView *headerView = [[UIView alloc] initWithFrame:self.searchDisplayController.searchResultsTableView.frame];
headerView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.8];
[self.searchDisplayController.searchResultsTableView setBackgroundColor:[UIColor clearColor]];
[self.searchDisplayController.searchResultsTableView setScrollEnabled:NO];
[self.searchDisplayController.searchResultsTableView setTableHeaderView:headerView];
[headerView release];
}
- (void)removeTableHeader {
[self.searchDisplayController.searchResultsTableView setBackgroundColor:[UIColor whiteColor]];
[self.searchDisplayController.searchResultsTableView setScrollEnabled:YES];
[self.searchDisplayController.searchResultsTableView setTableHeaderView:nil];
}
Obviously, it make the table transparent, adds a black/translucent table header with the same size as the table, and disables scrolling on the table so you cannot get above or past the header. As a bonus, you could add something to the header view ('please wait...' or an activity indicator).
Had the same problem as you, I handled it by a) setting the alpha of the searchResultsTableView to 0 when beginning searching, and then by b) adding/removing the overlayView to the viewController's view. Works like a charm for me.
#interface MyViewController()
//...
#property(nonatomic, retain) UIView *overlayView;
//...
#end
#implementation MyViewController
#synthesize overlayView = _overlayView;
//...
- (void)viewDidLoad
{
//...
//define your overlayView
_overlayView = [[UIView alloc] initWithFrame:CGRectMake(0, 44, 320, 480)];
_overlayView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.8];
}
//hide the searchResultsTableView
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
{
self.searchDisplayController.searchResultsTableView.alpha = 0.0f;
}
//when ending the search, hide the overlayView
- (void) searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
[_overlayView removeFromSuperview];
}
//depending on what the user has inputed, add or remove the overlayView to the view of the current viewController
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
if ([searchString length]>0)
{
[self.view addSubview:_overlayView];
}
else
{
[_overlayView removeFromSuperview];
}
return NO;
}
#end
it should be sufficient to implement the following method in your UISearchDisplayDelegate (which usually is your custom UITableViewController subclass)
- (BOOL) searchDisplayController: (UISearchDisplayController *) controller shouldReloadTableForSearchString: (NSString *) searchString
{
[self startMyCustomWebserviceSearchAsBackgroundProcessForString: searchString]; //starts new NSThread
return NO;
}
have you tried this?
Based on user182820's code below is my version. I hide the UISearchDisplayController's table view. When a character is entered in the search box I place a 'dimmed view' so it looks like UISearchDisplayController's 'dimmed view' never went away and then remove it when the search is finished. If you enter some characters and press cancel, the table view briefly goes all white and I don't know how to get around this.
- (void)viewDidLoad {
...
tableViewMask=[UIView new];
tableViewMask.backgroundColor = [UIColor blackColor];
tableViewMask.alpha = 0.8;
}
- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller{
tableViewMask.frame=CGRectMake(self.tableView.frame.origin.x, self.tableView.frame.origin.y+controller.searchBar.frame.size.height, self.tableView.frame.size.width, self.tableView.frame.size.height-controller.searchBar.frame.size.height);
controller.searchResultsTableView.hidden=YES;
}
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller{
[tableViewMask removeFromSuperview];
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString{
if (searchString.length==0)
[tableViewMask removeFromSuperview];
else
[self.tableView addSubview:tableViewMask];
[searchText autorelease];
searchText=[searchString retain];
return NO;
}
What about just doing it as simple as this:
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
self.searchDisplayController.searchResultsTableView.hidden=YES;
return YES;
}
Works fine for me..
I foud a better way, since there is a bug with the "best answer" - the separator and the "No Results" will be shown when scrolling the black background table.
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
controller.searchResultsTableView.backgroundColor = [UIColor blackColor];
controller.searchResultsTableView.alpha = 0.8;
controller.searchResultsTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
for(UIView *subview in tableView.subviews) {
if([subview isKindOfClass:UILabel.class]) {
subview.hidden = YES;
}
}
return NO;
}
- (void) searchBarSearchButtonClicked:(UISearchBar *)searchBar {
self.searchDisplayController.searchResultsTableView.backgroundColor = [UIColor whiteColor];
self.searchDisplayController.searchResultsTableView.alpha = 1;
self.searchDisplayController.searchResultsTableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
for(UIView *subview in tableView.subviews) {
if([subview isKindOfClass:UILabel.class]) {
subview.hidden = NO;
}
}
// search results and reload data ....
}
All of the existing answers are overly complicated. You can get away by just hiding the results table view immediately.
-(void)searchDisplayController:(UISearchDisplayController *)controller didShowSearchResultsTableView:(UITableView *)tableView
{
tableView.hidden = YES;
}
I think I found a better implementation for this problem. All the previous answers correctly show a dimmed view identical to what the UITableView looks like before a search, but each solution lacks the functionality to tap the area in order to cancel the search.
For that reason I think this code works better.
First of all, create a BOOL such as searchButtonTapped to indicate whether the search button was tapped. By default it is NO.
Then:
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
if (!searchButtonTapped) {
// To prevent results from being shown and to show an identical look to how the tableview looks before a search
[controller.searchResultsTableView setBackgroundColor:[UIColor clearColor]];
[controller.searchResultsTableView setRowHeight:160];
self.searchDisplayController.searchResultsTableView.scrollEnabled = NO;
} else {
// Restore original settings
[controller.searchResultsTableView setBackgroundColor:[UIColor whiteColor]];
[controller.searchResultsTableView setRowHeight:44];
self.searchDisplayController.searchResultsTableView.scrollEnabled = YES;
}
return YES;
}
This should be clear now based on the other answers. Make sure to also restore the original settings when the user taps on the Search button.
Furthermore, in the cellForIndexPath method add:
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.contentView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.8];
In order to create the same dimmed view that is shown before text is entered. Make sure you apply these properties to the right cell, i.e., check which UITableView is active and that the user has not tapped the Search button.
Then, crucially, in didSelectRowAtIndexPath:
if (tableView == self.searchDisplayController.searchResultsTableView) {
if (searchButtonTapped) {
// Code for when the user select a row after actually having performed a search
{
else
[self.searchDisplayController setActive:NO animated:YES];
Now the user can tap the dimmed area, which will not result in a visible selection of a UITableViewCell, but instead cancels the search.