The purpose is to implement a fixed search bar just like Contacts in iOS7.
I have a view controller called SearchViewController inherited from UIViewController.
And I add a searchBar and a tableView as its navigationController.view's subView.
But since searchBar and tableView are separated, when I start to search, no dim effect on tableView and result table view is shown in correctly.
I just want it behaves just like Contacts app.
Here is my code:
SearchViewController.h
#import <UIKit/UIKit.h>
#class UWTabBarController;
#class InfoSessionModel;
#interface SearchViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate, UISearchDisplayDelegate>
SearchViewController.m
#import "SearchViewController.h"
#interface SearchViewController ()
#property (nonatomic, strong) UISearchBar *searchBar;
#property (nonatomic, strong) UISearchDisplayController *searchController;
#property (nonatomic, strong) UITableView *tableView;
#property (nonatomic, strong) NSArray *data;
#end
#implementation SearchViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// initiate search bar
NSInteger statusBarHeight = 20;
NSInteger navigationBarHeight = self.navigationController.navigationBar.frame.size.height;
_searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, statusBarHeight + navigationBarHeight, 320, 44)];
// _searchBar.tintColor = [UIColor clearColor];
_searchBar.delegate = self;
_searchBar.barStyle = UIBarStyleDefault;
//NSMutableArray *scopeTitles = [[NSMutableArray alloc] initWithObjects:#"Employer", #"Program", #"Note", nil];
_searchBar.scopeButtonTitles = [[NSArray alloc] initWithObjects:#"Employer", #"Program", #"Note", nil];//[#"Employer|Program|Note" componentsSeparatedByString:#"|"];
// initiate search bar controller
_searchController = [[UISearchDisplayController alloc] initWithSearchBar:_searchBar contentsController:self];
_searchController.delegate = self;
_searchController.searchResultsDataSource = self;
_searchController.searchResultsDelegate = self;
// initiate table view
_tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, statusBarHeight + navigationBarHeight, 320, [UIScreen mainScreen].bounds.size.height - statusBarHeight - navigationBarHeight)];
[_tableView setContentInset:UIEdgeInsetsMake(_searchBar.frame.size.height, 0, _tabBarController.tabBar.frame.size.height, 0)];
_tableView.delegate = self;
_tableView.dataSource = self;
[_tableView registerClass:[InfoSessionCell class] forCellReuseIdentifier:#"InfoSessionCell"];
[_tableView registerClass:[LoadingCell class] forCellReuseIdentifier:#"LoadingCell"];
[self.navigationController.view addSubview:_tableView];
[self.navigationController.view addSubview:_searchBar];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (_searchController.searchResultsTableView == tableView) {
return 1;
}
else {
return [data count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (_searchController.searchResultsTableView == tableView) {
static NSString *cellIdentifier = #"LoadingCell";
LoadingCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[LoadingCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.loadingLabel.text = #"Test cell for search result";
return cell;
}
else {
//... configure cell and return cell
return cell;
}
}
}
#pragma mark - UISearchDisplayController Delegate Methods
// hasn't been implemented
#pragma mark - UISearchBar Delegate Methods
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar {
//move the search bar up to the correct location
[UIView animateWithDuration:.3
animations:^{
searchBar.frame = CGRectMake(searchBar.frame.origin.x,
20,// status bar's height
searchBar.frame.size.width,
searchBar.frame.size.height);
}
completion:^(BOOL finished){
//whatever else you may need to do
}];
return YES;
}
- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar {
//move the search bar down to the correct location
[UIView animateWithDuration:.25
animations:^{
NSInteger statusBarHeight = 20;
NSInteger navigationBarHeight = self.navigationController.navigationBar.frame.size.height;
searchBar.frame = CGRectMake(_searchBar.frame.origin.x,
statusBarHeight + navigationBarHeight,
_searchBar.frame.size.width,
_searchBar.frame.size.height);
}
completion:^(BOOL finished){
//whatever else you may need to do
}];
return YES;
}
This is the effects of my code:
All right, my problem is caused by my misunderstanding of views and navigationController's views.
In view did load method:
before is:
[self.navigationController.view addSubview:_tableView];
[self.navigationController.view addSubview:_searchBar];
but should be:
[self.view addSubview:_tableView];
[self.view addSubview:_searchBar];
In this way, the original table view and result table view will show correctly.
And other things are moving search bar up and down, these things should be done through UISearchBar delegate protocol methods and UISearchDisplayController delegate protocol methods.
This is a right way to implement a fixed searchBar and tableView bellow it.
Related
I'm working on an iOS app and I created an UIViewController where I put my components and it works fine .
Now I'm trying to create a Custom UIView and to put my WebView and my SearchController into . I spent a lot of time without success .
Here is my .m file and I hope some one can help me with :
#import "HomeViewController.h"
#define widthtScreen [UIScreen mainScreen].bounds.size.width
#define heightScreen [UIScreen mainScreen].bounds.size.height
#interface HomeViewController () <UISearchResultsUpdating,UISearchBarDelegate,UIBarPositioningDelegate,UITableViewDataSource,UITableViewDelegate,MapWebViewDelegate>
#property(strong,nonatomic) MapWebView *webView;
#property (nonatomic) UIButton *btnGeolocate;
#property (nonatomic, strong) UISearchController *searchController;
#end
#implementation HomeViewController{
NSMutableArray *placesList;
BOOL isSearching;
}
-(void)loadView
{
[super loadView];
self.webView = [[MapWebView alloc] initWithFrame:CGRectMake(0, -100, widthtScreen, heightScreen+100)];
self.webView.mapWebViewDelegate = self;
[self.view addSubview:self.webView];
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.navigationItem.hidesBackButton = YES;
mainDelegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];
placesList = [[NSMutableArray alloc] init];
[self initializeSearchController];
mainDelegate.webView = self.webView;
self.btnGeolocate = [[UIButton alloc] initWithFrame:CGRectMake(self.view.frame.size.width-75,550,60,60)];
self.btnGeolocate.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
[self.btnGeolocate setBackgroundImage:[UIImage imageNamed:#"geo.png"]
forState:UIControlStateNormal];
[self.btnGeolocate addTarget:self action:#selector(btnGeolocatePressed:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.btnGeolocate];
mainDelegate.btnZoomIn = [[UIButton alloc] initWithFrame:CGRectMake(self.view.frame.size.width-80,620,30,30)];
mainDelegate.btnZoomIn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
[mainDelegate.btnZoomIn setBackgroundColor:[UIColor blackColor]];
[mainDelegate.btnZoomIn addTarget:self action:#selector(btnZoomInPressed:) forControlEvents:UIControlEventTouchUpInside];
mainDelegate.btnZoomIn.tag=1;
UIImage *btnImage = [UIImage imageNamed:#"plus.png"];
[mainDelegate.btnZoomIn setImage:btnImage forState:UIControlStateNormal];
[self.view addSubview:mainDelegate.btnZoomIn];
mainDelegate.btnZoomOut = [[UIButton alloc] initWithFrame:CGRectMake(self.view.frame.size.width-40,620,30,30)];
mainDelegate.btnZoomOut.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
[mainDelegate.btnZoomOut setBackgroundColor:[UIColor blackColor]];
[mainDelegate.btnZoomOut addTarget:self action:#selector(btnZoomOutPressed:) forControlEvents:UIControlEventTouchUpInside];
mainDelegate.btnZoomOut.tag=1;
UIImage *btnImage2 = [UIImage imageNamed:#"minus.png"];
[mainDelegate.btnZoomOut setImage:btnImage2 forState:UIControlStateNormal];
[self.view addSubview:mainDelegate.btnZoomOut];
}
- (BOOL)slideNavigationControllerShouldDisplayLeftMenu
{
return YES;
}
- (BOOL)slideNavigationControllerShouldDisplayRightMenu
{
return YES;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"number :%lu",(unsigned long)[placesList count]);
return [placesList count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
SuggestResultObject *sro = [SuggestResultObject new];
sro = [placesList objectAtIndex:indexPath.row];
cell.textLabel.text = sro.textPlace;
return cell;
}
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar{
self.navigationItem.leftBarButtonItem = nil;
self.navigationItem.rightBarButtonItem =nil;
return true;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.navigationItem.leftBarButtonItem = mainDelegate.leftBarButtonItem;
self.navigationItem.rightBarButtonItem = mainDelegate.rightBarButtonItem;
SuggestResultObject *sro = [SuggestResultObject new];
sro = [placesList objectAtIndex:indexPath.row];
self.searchController.active = false;
NSString *function = [[NSString alloc] initWithFormat: #"MobileManager.getInstance().moveToLocation(\"%#\",\"%#\")", sro.latPlace,sro.lonPlace];
[_webView evaluateJavaScript:function completionHandler:nil];
}
- (void)jsRun:(NSString *) searchText {
dispatch_async(dispatch_get_main_queue(), ^{
NSString *function = [[NSString alloc] initWithFormat: #"MobileManager.getInstance().setSuggest(\"%#\")", searchText];
[_webView evaluateJavaScript:function completionHandler:nil];
});
}
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
isSearching = YES;
}
- (void)initializeSearchController {
UITableViewController *searchResultsController = [[UITableViewController alloc] initWithStyle:UITableViewStylePlain];
searchResultsController.tableView.dataSource = self;
searchResultsController.tableView.delegate = self;
self.searchController = [[UISearchController alloc] initWithSearchResultsController:searchResultsController];
self.definesPresentationContext = YES;
self.searchController.hidesNavigationBarDuringPresentation = false;
self.searchController.accessibilityElementsHidden= true;
self.searchController.dimsBackgroundDuringPresentation = true;
self.searchController.searchBar.frame = CGRectMake(self.searchController.searchBar.frame.origin.x, self.searchController.searchBar.frame.origin.y, self.searchController.searchBar.frame.size.width, 44.0);
self.navigationItem.titleView = self.searchController.searchBar;
self.searchController.searchResultsUpdater = self;
self.searchController.searchBar.delegate = self;
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
self.navigationItem.leftBarButtonItem = mainDelegate.leftBarButtonItem;
self.navigationItem.rightBarButtonItem = mainDelegate.rightBarButtonItem;
NSLog(#"Cancel clicked");
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
[placesList removeAllObjects];
}
-(void)updateSearchResultsForSearchController:(UISearchController *)searchController {
[placesList removeAllObjects];
if([searchController.searchBar.text length] != 0) {
isSearching = YES;
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[self jsRun:searchController.searchBar.text];
}
else {
isSearching = NO;
[((UITableViewController *)self.searchController.searchResultsController).tableView reloadData];
}
}
-(void) btnGeolocatePressed : (id) sender{
}
-(void) btnZoomInPressed : (id) sender{
[_webView evaluateJavaScript:#"MobileManager.getInstance().zoomIn();" completionHandler:nil];
}
-(void) btnZoomOutPressed : (id) sender{
[_webView evaluateJavaScript:#"MobileManager.getInstance().zoomOut();" completionHandler:nil];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)searchResult:(NSArray*)dataArray{
SuggestResultObject *sro = [SuggestResultObject new];
sro.textPlace = [dataArray objectAtIndex:0];
sro.lonPlace = [dataArray objectAtIndex:1];
sro.latPlace = [dataArray objectAtIndex:2];
[placesList addObject:sro];
[((UITableViewController *)self.searchController.searchResultsController).tableView reloadData];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
}
#end
Screenshots (Not sure how to scale images on stackoverflow.. OSX takes images in Retina format and they're fairly large! Sorry in advance!):
http://i.imgur.com/15qxDpc.png
http://i.imgur.com/QHduP07.png
How it works? Create a UIView and two sub-views: UITableView and
UIWebView. Constrain them properly.
Create the UISearchController with a nil SearchResultsController
as the parameter to the init method.
This lets the search controller know that the results will be
displayed in the current controller/view.
Next we setup the delegates for the UISearchController and create
the function for filtering.
Now that we have the view created, we need a UIViewController to
test it. We can either add the search bar to the tableView header OR
to the NavigationController if there is one..
Below, I have chosen to add it to the UINavigationController and I
told the UISearchController to NOT HIDE the navigation bar on
presentation.
That way, the results are displayed in the current view without hiding
the navigation bar.
You can then use the webview which is hidden and offscreen to do
whatever javascript searches you are using it for..
However, a better idea would be to use JSContext to execute
Javascript instead of a UIWebView. The advantage of the UIWebView
is that you can parse HTML and modify DOM which the JSContext
doesn't allow.
Anyway..
Here is the code I wrote for a UIView that contains a
UISearchController and a UIWebView.. and then to add it to a
UIViewController that is embedded in a UINavigationController.
//
// SearchView.h
// StackOverflow
//
// Created by Brandon T on 2016-06-26.
// Copyright © 2016 XIO. All rights reserved.
//
#import <UIKit/UIKit.h>
#class SearchView;
#protocol SearchViewDelegate <UISearchBarDelegate>
- (void)didSelectRowAtIndexPath:(SearchView *)searchView tableView:(UITableView *)tableView indexPath:(NSIndexPath *)indexPath;
#end
#interface SearchView : UIView
#property (nonatomic, weak) id<SearchViewDelegate> delegate;
- (UISearchBar *)getSearchBar;
- (UIWebView *)getWebView;
#end
//
// SearchView.m
// StackOverflow
//
// Created by Brandon T on 2016-06-26.
// Copyright © 2016 XIO. All rights reserved.
//
#import "SearchView.h"
#define kTableViewCellIdentifier #"kTableViewCellIdentifier"
#interface SearchView() <UITableViewDelegate, UITableViewDataSource, UISearchResultsUpdating>
#property (nonatomic, strong) UIWebView *webView;
#property (nonatomic, strong) UISearchController *searchController;
#property (nonatomic, strong) UITableView *tableView;
#property (nonatomic, strong) NSArray *dataSource;
#property (nonatomic, strong) NSArray *searchResults;
#end
#implementation SearchView
- (instancetype)init {
if (self = [super init]) {
[self setupData];
[self initControls];
[self themeControls];
[self registerCells];
[self doLayout];
}
return self;
}
- (UISearchBar *)getSearchBar {
return _searchController.searchBar;
}
- (UIWebView *)getWebView {
return _webView;
}
- (void)setDelegate:(id<SearchViewDelegate>)delegate {
_delegate = delegate;
_searchController.searchBar.delegate = delegate;
}
- (void)setupData {
//Begin fake data
_dataSource = #[#"Cat", #"Dog", #"Bird", #"Parrot", #"Rabbit", #"Racoon", #"Rat", #"Hamster", #"Pig", #"Cow"];
//End fake data
_searchResults = [_dataSource copy];
}
- (void)initControls {
_webView = [[UIWebView alloc] init];
_searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
_tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
}
- (void)themeControls {
[_webView setHidden:YES];
[_tableView setDelegate:self];
[_tableView setDataSource:self];
_searchController.searchResultsUpdater = self;
_searchController.dimsBackgroundDuringPresentation = false;
_searchController.definesPresentationContext = true;
_searchController.hidesNavigationBarDuringPresentation = false;
}
- (void)registerCells {
[_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:kTableViewCellIdentifier];
}
- (void)doLayout {
[self addSubview:_webView];
[self addSubview:_tableView];
NSDictionary *views = #{#"webView":_webView, #"tableView": _tableView};
NSMutableArray *constraints = [[NSMutableArray alloc] init];
[constraints addObject:[NSString stringWithFormat:#"H:|-(%d)-[webView]-(%d)-|", 0, 0]];
[constraints addObject:[NSString stringWithFormat:#"H:|-(%d)-[tableView]-(%d)-|", 0, 0]];
[constraints addObject:[NSString stringWithFormat:#"V:|-(%d)-[webView(%d)]-(%d)-[tableView]-(%d)-|", -100, 100, 0, 0]];
for (NSString *constraint in constraints) {
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:constraint options:0 metrics:nil views:views]];
}
for (UIView *view in self.subviews) {
[view setTranslatesAutoresizingMaskIntoConstraints:NO];
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _searchController.active && _searchController.searchBar.text.length > 0 ? [_searchResults count] : [_dataSource count];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 50;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kTableViewCellIdentifier forIndexPath:indexPath];
if (_searchController.active && _searchController.searchBar.text.length > 0) {
cell.textLabel.text = _searchResults[indexPath.row];
}
else {
cell.textLabel.text = _dataSource[indexPath.row];
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (self.delegate && [self.delegate respondsToSelector:#selector(didSelectRowAtIndexPath:tableView:indexPath:)]) {
[self.delegate didSelectRowAtIndexPath:self tableView:tableView indexPath:indexPath];
}
}
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController {
[self filterResults:searchController.searchBar.text scope:nil];
}
- (void)filterResults:(NSString *)searchText scope:(NSString *)scope {
_searchResults = [_dataSource filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id _Nonnull evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
NSString *object = [evaluatedObject uppercaseString];
return [object rangeOfString:[searchText uppercaseString]].location != NSNotFound;
}]];
[_tableView reloadData];
}
#end
Then I tested it with the below UIViewController which is embedded in a UINavigationController..
//
// ViewController.m
// StackOverflow
//
// Created by Brandon T on 2016-06-26.
// Copyright © 2016 XIO. All rights reserved.
//
#import "ViewController.h"
#import "SearchView.h"
#interface ViewController ()<SearchViewDelegate>
#property (nonatomic, strong) SearchView *searchView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self initControls];
[self themeControls];
[self doLayout];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)initControls {
_searchView = [[SearchView alloc] init];
}
- (void)themeControls {
self.edgesForExtendedLayout = UIRectEdgeNone;
self.navigationItem.titleView = [_searchView getSearchBar];
[_searchView setDelegate:self];
}
- (void)doLayout {
[self.view addSubview:_searchView];
NSDictionary *views = #{#"searchView":_searchView};
NSMutableArray *constraints = [[NSMutableArray alloc] init];
[constraints addObject:[NSString stringWithFormat:#"H:|-%d-[searchView]-%d-|", 0, 0]];
[constraints addObject:[NSString stringWithFormat:#"V:|-%d-[searchView]-%d-|", 0, 0]];
for (NSString *constraint in constraints) {
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:constraint options:0 metrics:nil views:views]];
}
for (UIView *view in self.view.subviews) {
[view setTranslatesAutoresizingMaskIntoConstraints:NO];
}
}
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
self.navigationItem.leftBarButtonItems = nil;
self.navigationItem.rightBarButtonItems = nil;
}
- (void)didSelectRowAtIndexPath:(SearchView *)searchView tableView:(UITableView *)tableView indexPath:(NSIndexPath *)indexPath {
[[searchView getWebView] stringByEvaluatingJavaScriptFromString:#"SomeJavascriptHere"];
}
#end
I am making my own drop down menu because I am not able to find an open source one that does exactly what I need.
I have implemented the dropdown as a UIView and am adding it to the superview of the button that is tapped in order to show it.
Code:
ViewController.m
#import "ViewController.h"
#import "MenuView.h"
#interface ViewController () <MenuViewDelegate>
#property (weak, nonatomic) IBOutlet UIView *fakeHeader;
#property (nonatomic, strong) MenuView *menuView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)btnTapped:(id)sender {
NSArray *array = #[#"Item 1", #"Item 2", #"Item 3", #"Item 4"];
NSArray *imgArray = nil;
if (self.menuView == nil) {
self.menuView = [[MenuView alloc] showDropDownWith:sender txtArr:array imgArr:imgArray direction:#"down" delegate:self];
self.menuView.delegate = self;
} else {
[self.menuView hideDropDown:sender];
self.menuView = nil;
}
}
- (void) menuDelegateMethod:(MenuView *)sender {
self.menuView = nil;
}
#end
MenuView.h
#import <UIKit/UIKit.h>
#class MenuView;
#protocol MenuViewDelegate
- (void)menuDelegateMethod:(MenuView *)sender;
#end
#interface MenuView : UIView
#property (nonatomic, retain) id <MenuViewDelegate> delegate;
- (id)showDropDownWith:(UIButton *)button txtArr:(NSArray *)txtArr imgArr:(NSArray *)imgArr direction:(NSString *)direction delegate:(id)delegate;
- (void)hideDropDown:(UIButton *)button;
#end
MenuView.m
#import "MenuView.h"
#import "QuartzCore/QuartzCore.h"
#interface MenuView () <UITableViewDataSource, UITableViewDelegate>
#property (nonatomic, strong) UITableView *table;
#property (nonatomic, strong) UIButton *btnSender;
#property (nonatomic, retain) NSString *animationDirection;
#property (nonatomic, retain) NSArray *list;
#property (nonatomic, retain) NSArray *imageList;
#end
#implementation MenuView
- (id)showDropDownWith:(UIButton *)button txtArr:(NSArray *)txtArr imgArr:(NSArray *)imgArr direction:(NSString *)direction delegate:(id)delegate {
CGFloat width = [UIScreen mainScreen].bounds.size.width;
CGFloat origin = [UIScreen mainScreen].bounds.origin.x;
CGFloat realHeight = 40 * txtArr.count;
self.btnSender = button;
self.animationDirection = direction;
self.table = (UITableView *)[super init];
if (self) {
// Initialization code
CGRect btn = button.frame;
self.list = [NSArray arrayWithArray:txtArr];
self.imageList = [NSArray arrayWithArray:imgArr];
if ([direction isEqualToString:#"up"]) {
self.frame = CGRectMake(origin, (btn.origin.y - btn.size.height) , width, 0);
self.layer.shadowOffset = CGSizeMake(0, 1);
} else if ([direction isEqualToString:#"down"]) {
self.frame = CGRectMake(origin, (btn.origin.y + btn.size.height + 10), width, 0);
self.layer.shadowOffset = CGSizeMake(0, 1);
}
self.layer.masksToBounds = YES;
self.layer.shadowOpacity = 0.2;
self.table = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, width, 0)];
self.table.delegate = delegate;
self.table.dataSource = self;
self.table.backgroundColor = [UIColor colorWithRed:0.239 green:0.239 blue:0.239 alpha:1];
self.table.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
self.table.separatorColor = [UIColor lightGrayColor];
self.table.backgroundColor = [UIColor whiteColor];
self.table.userInteractionEnabled = YES;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
if ([direction isEqualToString:#"up"]) {
self.frame = CGRectMake(origin, (btn.origin.y - realHeight), width, realHeight);
} else if([direction isEqualToString:#"down"]) {
self.frame = CGRectMake(origin, (btn.origin.y + btn.size.height + 10), width, realHeight);
}
self.table.frame = CGRectMake(0, 0, width, realHeight);
[UIView commitAnimations];
[button.superview addSubview:self];
self.table.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
[self addSubview:self.table];
}
return self;
}
- (void)hideDropDown:(UIButton *)button {
CGRect btn = button.frame;
CGFloat width = [UIScreen mainScreen].bounds.size.width;
CGFloat origin = [UIScreen mainScreen].bounds.origin.x;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
if ([self.animationDirection isEqualToString:#"up"]) {
self.frame = CGRectMake(origin, btn.origin.y, width, 0);
} else if ([self.animationDirection isEqualToString:#"down"]) {
self.frame = CGRectMake(origin, (btn.origin.y + btn.size.height + 10), width, 0);
}
self.table.frame = CGRectMake(0, 0, width, 0);
[UIView commitAnimations];
}
#pragma mark - Table View DataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.list count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.textLabel.font = [UIFont systemFontOfSize:15];
cell.textLabel.textAlignment = NSTextAlignmentLeft;
}
cell.textLabel.text = [self.list objectAtIndex:indexPath.row];
cell.backgroundColor = [UIColor colorWithRed:48.0f/255.0f green:48.0f/255.0f blue:48.0f/255.0f alpha:1.0f];
cell.textLabel.textColor = [UIColor lightTextColor];
return cell;
}
#pragma mark - Table View Delegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 40;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self hideDropDown:self.btnSender];
[self myDelegate];
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
// Remove seperator inset
if ([cell respondsToSelector:#selector(setSeparatorInset:)]) {
[cell setSeparatorInset:UIEdgeInsetsZero];
}
// Prevent the cell from inheriting the Table View's margin settings
if ([cell respondsToSelector:#selector(setPreservesSuperviewLayoutMargins:)]) {
[cell setPreservesSuperviewLayoutMargins:NO];
}
// Explictly set your cell's layout margins
if ([cell respondsToSelector:#selector(setLayoutMargins:)]) {
[cell setLayoutMargins:UIEdgeInsetsZero];
}
}
#pragma mark - View Delegate
- (void)myDelegate {
[self.delegate menuDelegateMethod:self];
}
#end
Shows up perfectly but the didSelect method is never called.
I don't have any views over the top of it that would be stealing the touch events.
It seems that UIViews might not be able to be UITableviewDelegates. If that's true I don't know why, when I make the calling view controller the delegate, it still fails to didSelect.
NOTE: I am aware of the animation faux pas by not using newer methods. This is based on old example code. I will update the animation after I get this issue worked out.
Questions:
Is it true that UIViews can not be UITableView Delegates?
If so, how does one make a calling view controller the delegate for the table view that resides in the UIView? Other than the process of setting it up as a UITableViewDelegate and assigning the calling view controller as the delegate at the time of the creation of the table.
Did I miss something in the way I set this up that steals the cell taps so that didSelect does not get called, either in the view or the viewController?
Thanks for the help.
Agree with #Piotr that the menu must be the table's delegate, so replace self.table.delegate = delegate; with self.table.delegate = self; in MenuView.m.
But additionally, MenuView.m never invokes its delegate, which it should upon selection in the tableview....
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self hideDropDown:self.btnSender];
// remove this, since it does nothing
//[self myDelegate];
// replace it with
[self.myDelegate menuDelegateMethod:self];
}
That last line says, tell the delegate of the menu that something happened.
Another problem is that the menu doesn't really tell the delegate what happened. Certainly the delegate will be interested in which item is selected. Consider changing the protocol to something like:
#protocol MenuViewDelegate
- (void)menuView:(MenuView *)sender didSelectOptionAtIndex:(NSInteger)index;
#end
// calling it
[self.myDelegate menuView:self didSelectOptionAtIndex:indexPath.row];
Another alternative is to hand back the selected string to the delegate. This can be found in the tableview's datasource at the indexPath.row.
Finally, its good practice to not-retain your delegate since the customer of the Menu might retain it, resulting in a retain cycle. Instead, declare the delegate:
// notice "weak"
#property (nonatomic, weak) id <MenuViewDelegate> delegate;
As I can see, you are passing ViewController (it is MenuViewDelegate) to showDropDownWith method, and then use it as table delegate. This is not correct.
You should pass self there (same as with data source), because you want MenuView to be delegate of table, not ViewController, right?
self.table.delegate = self;
I am using UISearchbar in tableview controller in storyboard.
And searchbar returnKeyType is UIReturnKeySearch.
Its working fine with iOS7 but returnKeyType is not working with iOS8.
in iOS8, return key appears every time in keyboard.
I tried to set returnkeytype in viewDidLoad method of controller too.
What I need to do to set returnKeyType = UIReturnKeySearch in iOS8?
I think you can go with your hard codded logic for right now.
I will update if I will get better solution for your problem.
-(void)viewDidLoad {
[self setReturnKeyTypeSearchForView:searchBar];
}
-(void)setReturnKeyTypeSearchForView:(UIView *)view
{
for (id subView in view.subviews) {
if ([subView isKindOfClass:[UITextField class]]) {
[subView setReturnKeyType:UIReturnKeySearch];
}
else {
[self setReturnKeyTypeSearchForView:subView];
}
}
if ([view isKindOfClass:[UITextField class]]) {
[(UITextField *)view setReturnKeyType:UIReturnKeySearch];
}
}
Try making IBOutlet of your SearchBar
#property (weak, nonatomic) IBOutlet UISearchBar *searchBar;
and add the below line code to your viewDidLoad Method
// if u want Done return key and change accordingly.
_searchBar.returnKeyType = UIReturnKeyDone;
SearchViewController.h
//
#import <UIKit/UIKit.h>
#interface SearchViewController : UIViewController
<UISearchBarDelegate, UITableViewDataSource> {
NSMutableArray *tableData;
UIView *disableViewOverlay;
UITableView *theTableView;
UISearchBar *theSearchBar;
}
#property(retain) NSMutableArray *tableData;
#property(retain) UIView *disableViewOverlay;
#property (nonatomic, retain) IBOutlet UITableView *theTableView;
#property (nonatomic, retain) IBOutlet UISearchBar *theSearchBar;
- (void)searchBar:(UISearchBar *)searchBar activate:(BOOL) active;
#end
SearchViewController.m
//
#import "SearchViewController.h"
#implementation SearchViewController
#synthesize tableData;
#synthesize disableViewOverlay;
#synthesize theSearchBar;
#synthesize theTableView;
// Initialize tableData and disabledViewOverlay
- (void)viewDidLoad {
[super viewDidLoad];
self.tableData =[[NSMutableArray alloc]init];
self.disableViewOverlay = [[UIView alloc]
initWithFrame:CGRectMake(0.0f,44.0f,320.0f,416.0f)];
self.disableViewOverlay.backgroundColor=[UIColor blackColor];
self.disableViewOverlay.alpha = 0;
}
// Since this view is only for searching give the UISearchBar
// focus right away
- (void)viewDidAppear:(BOOL)animated {
[self.theSearchBar becomeFirstResponder];
[super viewDidAppear:animated];
}
#pragma mark -
#pragma mark UISearchBarDelegate Methods
- (void)searchBar:(UISearchBar *)searchBar
textDidChange:(NSString *)searchText {
// We don't want to do anything until the user clicks
// the 'Search' button.
// If you wanted to display results as the user types
// you would do that here.
}
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
// searchBarTextDidBeginEditing is called whenever
// focus is given to the UISearchBar
// call our activate method so that we can do some
// additional things when the UISearchBar shows.
[self searchBar:searchBar activate:YES];
}
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
// searchBarTextDidEndEditing is fired whenever the
// UISearchBar loses focus
// We don't need to do anything here.
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
// Clear the search text
// Deactivate the UISearchBar
searchBar.text=#"";
[self searchBar:searchBar activate:NO];
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
// Do the search and show the results in tableview
// Deactivate the UISearchBar
// You'll probably want to do this on another thread
// SomeService is just a dummy class representing some
// api that you are using to do the search
NSArray *results = [SomeService doSearch:searchBar.text];
[self searchBar:searchBar activate:NO];
[self.tableData removeAllObjects];
[self.tableData addObjectsFromArray:results];
[self.theTableView reloadData];
}
// We call this when we want to activate/deactivate the UISearchBar
// Depending on active (YES/NO) we disable/enable selection and
// scrolling on the UITableView
// Show/Hide the UISearchBar Cancel button
// Fade the screen In/Out with the disableViewOverlay and
// simple Animations
- (void)searchBar:(UISearchBar *)searchBar activate:(BOOL) active{
self.theTableView.allowsSelection = !active;
self.theTableView.scrollEnabled = !active;
if (!active) {
[disableViewOverlay removeFromSuperview];
[searchBar resignFirstResponder];
} else {
self.disableViewOverlay.alpha = 0;
[self.view addSubview:self.disableViewOverlay];
[UIView beginAnimations:#"FadeIn" context:nil];
[UIView setAnimationDuration:0.5];
self.disableViewOverlay.alpha = 0.6;
[UIView commitAnimations];
// probably not needed if you have a details view since you
// will go there on selection
NSIndexPath *selected = [self.theTableView
indexPathForSelectedRow];
if (selected) {
[self.theTableView deselectRowAtIndexPath:selected
animated:NO];
}
}
[searchBar setShowsCancelButton:active animated:YES];
}
#pragma mark -
#pragma mark UITableViewDataSource Methods
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return [tableData count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"SearchResult";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier] autorelease];
}
id *data = [self.tableData objectAtIndex:indexPath.row];
cell.textLabel.text = data.name;
return cell;
}
#pragma mark -
#pragma mark Memory Management Methods
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[theTableView release], theTableView = nil;
[theSearchBar release], theSearchBar = nil;
[tableData dealloc];
[disableViewOverlay dealloc];
[super dealloc];
}
#end
Building a SearchView with UISearchBar and UITableView
this might helps you :)
I'm not sure if I understood your question correctly. You want to have "search" button instead of "return" button, right? There is a new SearchController in ios 8, give it a try:
YourTableViewController.h
#interface YourTableViewController : UITableViewController<UISearchResultsUpdating>
#end
And now the implementation:
YourTableViewController.m
- (void)viewDidLoad {
// initializing with the same controller as presenting
UISearchController *searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
searchController.searchResultsUpdater = self;
searchController.searchBar.frame = CGRectMake(searchController.searchBar.frame.origin.x, searchController.searchBar.frame.origin.y, searchController.searchBar.frame.size.width, 44.0f);
searchController.dimsBackgroundDuringPresentation = NO;
searchController.searchBar.delegate = self;
searchController.searchBar.returnKeyType = UIReturnKeySearch; //should be search by default.. you can change to whatever you want.
// adding searchBar into HeaderView
self.tableView.tableHeaderView = searchController.searchBar;
// just to be able to present results on the same controller
self.definesPresentationContext = YES;
}
You also have to implement method from UISearchResultsUpdating protocol:
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController {
// you can leave it blank
}
EDIT: If it is not what you were looking for please comment, so I can update my answer accordingly
try this in viewDidLoad:
UITextField *txfSearchField = [yourSearchbar valueForKey:#"_searchField"];
if([txfSearchField conformsToProtocol:#protocol(UITextInputTraits)]) {
[txfSearchField setReturnKeyType:UIReturnKeyDefault];
}
First thing to note is that I don't use xibs or storyboards, so everything needs to be done programatically.
I have a UITableView and I need too add a custom static view to the bottom of the screen when the table view is open (it'll function as a toolbar). I can't add it as a direct subview of the tableview because then it scrolls with the table. Also worth mentioning is that all of this is sitting inside of a UINavigationController.
Anyone know how I can get around this?
You can add UIToolbar to the bottom of the screen with UIBarButtonItems and add UITableView to the top of the view controller with height equal tables view height - toolbar height.
Here is a implementation subclassing UIViewController and adding UITableView and UITabBarController to it.
#interface RKViewController ()<UITableViewDataSource, UITableViewDelegate>
#property (nonatomic, strong) UITableView *tableView;
#property (nonatomic, strong) UITabBar *tabBar;
#property (nonatomic, strong) UITabBarController *tabBarController;
#end
#implementation RKViewController
-(id)init
{
self = [super init];
if (self) {
self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 500)];
self.tableView.dataSource = self;
self.tableView.delegate = self;
self.tabBarController = [[UITabBarController alloc] init];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
UIViewController *vc1 = [[UIViewController alloc] init];
vc1.tabBarItem.title = #"Item 1";
UIViewController *vc2 = [[UIViewController alloc] init];
vc2.tabBarItem.title = #"Item 2";
self.tabBarController.viewControllers = [NSArray arrayWithObjects:vc1,vc2, nil];
[self.view addSubview:self.tableView];
[self.view addSubview:self.tabBarController.view];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 10;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
return [UITableViewCell new];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#end
I have a root view controller which composes a search bar on the top and a child table view controller on the bottom. I used composition instead of assigning the search bar to the table view's header for these reasons:
I didn't want the index to overlap with the search bar (like Contacts app).
I wanted the search bar to be sticky. That is, it doesn't move when I scroll the table view (again like the Contacts app).
My table view already had a header.
Since the search bar is in the root view controller, I instantiate my search display controller in the root view controller also. There are two problems with the search UI for which I seek advice:
The translucent gray overlay does not cover the entire child table view. It leaves the top portion of the header and the index visible.
Likewise, the search results table does not cover the entirety of the child table view. I know how to manually change the frame of this results table view, but doing so only fixes just that ... the gray translucent overlay's frame is not linked to the results table view frame. Their is no property to access the overlay.
1) Idle
2) Enter Search Bar
3) Start Typing
#import "ContactsRootViewController.h"
#import "ContactsViewController.h"
#import "UIView+position.h"
#import "User.h"
#import "UserCellView.h"
#import "UserViewController.h"
#interface ContactsRootViewController ()
#property(nonatomic, strong) UISearchBar* searchBar;
#property(nonatomic, strong) ContactsViewController* contactsViewController;
#property(nonatomic, strong) UISearchDisplayController* searchController;
#property(nonatomic, strong) NSMutableArray* matchedUsers;
#end
#implementation ContactsRootViewController
#pragma mark UIViewController
- (NSString*)title
{
return #"Contacts";
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.matchedUsers = [NSMutableArray array];
self.searchBar = [[UISearchBar alloc] init];
self.searchBar.placeholder = #"Search";
[self.searchBar sizeToFit];
[self.view addSubview:self.searchBar];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if (self.contactsViewController == nil) {
self.contactsViewController = [[ContactsViewController alloc] init];
[self addChildViewController:self.contactsViewController];
self.contactsViewController.view.frame = CGRectMake(
0.0,
self.searchBar.bottomY,
self.view.frame.size.width,
self.view.frame.size.height - self.searchBar.bottomY
);
self.contactsViewController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
[self.view addSubview:self.contactsViewController.view];
[self.contactsViewController didMoveToParentViewController:self];
self.searchController = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self.contactsViewController];
self.searchController.delegate = self;
self.searchController.searchResultsDataSource = self;
self.searchController.searchResultsDelegate = self;
}
}
#pragma mark UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.matchedUsers.count;
}
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString* identifier = #"contactsRootViewUserCell";
UserCellView* cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[UserCellView alloc] initWithIdentifier:identifier];
}
cell.user = [self.matchedUsers objectAtIndex:indexPath.row];
return cell;
}
#pragma mark UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.navigationController pushViewController:[[UserViewController alloc] initWithUser:[self.matchedUsers objectAtIndex:indexPath.row]] animated:YES];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return [UserCellView height];
}
#pragma mark UISearchDisplayControllerDelegate
- (BOOL)searchDisplayController:(UISearchDisplayController*)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self.matchedUsers removeAllObjects];
searchString = [searchString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if (searchString.length > 0) {
for (User* user in self.contactsViewController.allUsers) {
NSRange match = [user.userDisplayName rangeOfString:searchString options:NSCaseInsensitiveSearch];
if (match.location != NSNotFound) {
[self.matchedUsers addObject:user];
}
}
}
return YES;
}
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller
{
[self.searchBar resignFirstResponder];
}
#end
I re-implemented UISearchDisplayController, calling my implementation SearchController. It does the same thing and has similar delegate callbacks, but the frame of the search results can be controlled by the programmer.
Header
#import <Foundation/Foundation.h>
#class SearchController;
#protocol SearchControllerDelegate <NSObject>
#required
- (BOOL)searchController:(SearchController*)controller shouldReloadTableForSearchString:(NSString*)searchText;
#optional
- (void)searchController:(SearchController*)controller didShowSearchResultsTableView:(UITableView*)tableView;
- (void)searchController:(SearchController *)controller didHideSearchResultsTableView:(UITableView *)tableView;
- (void)searchControllerDidBeginSearch:(SearchController*)controller;
- (void)searchControllerDidEndSearch:(SearchController*)controller;
#end
#interface SearchController : UIViewController <UISearchBarDelegate>
#property(nonatomic, weak) NSObject<SearchControllerDelegate>* delegate;
#property(nonatomic, weak) NSObject<UITableViewDataSource>* searchResultsDataSource;
#property(nonatomic, weak) NSObject<UITableViewDelegate>* searchResultsDelegate;
#property(nonatomic, strong, readonly) UITableView* searchResultsTableView;
- (id)initWithSearchBar:(UISearchBar*)searchBar;
#end
Implementation
#import "SearchController.h"
#import "UIView+position.h"
#interface SearchController ()
#property(nonatomic, strong) UISearchBar* searchBar;
#property(nonatomic, strong) UIButton* searchResultsVeil;
#property(nonatomic, strong, readwrite) UITableView* searchResultsTableView;
#property(nonatomic, assign) BOOL searchResultsTableViewHidden;
- (void)didTapSearchResultsVeil;
- (void)hideSearchResults;
#end
#implementation SearchController
#pragma mark UIViewController
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.searchResultsTableView deselectRowAtIndexPath:[self.searchResultsTableView indexPathForSelectedRow] animated:YES];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.userInteractionEnabled = NO;
}
#pragma mark SearchController ()
- (void)hideSearchResults
{
self.searchBar.text = nil;
[self.searchResultsTableView reloadData];
self.searchResultsTableViewHidden = YES;
[self.searchBar resignFirstResponder];
}
- (void)didTapSearchResultsVeil
{
[self hideSearchResults];
}
- (void)setSearchResultsTableViewHidden:(BOOL)searchResultsTableViewHidden
{
if (self.searchResultsTableView != nil) {
if (self.searchResultsTableView.hidden && !searchResultsTableViewHidden) {
self.searchResultsTableView.hidden = searchResultsTableViewHidden;
if ([self.delegate respondsToSelector:#selector(searchController:didShowSearchResultsTableView:)]) {
[self.delegate searchController:self didShowSearchResultsTableView:self.searchResultsTableView];
}
} else if (!self.searchResultsTableView.hidden && searchResultsTableViewHidden) {
self.searchResultsTableView.hidden = searchResultsTableViewHidden;
if ([self.delegate respondsToSelector:#selector(searchController:didHideSearchResultsTableView:)]) {
[self.delegate searchController:self didHideSearchResultsTableView:self.searchResultsTableView];
}
}
}
}
- (BOOL)searchResultsTableViewHidden
{
return self.searchResultsTableView == nil || self.searchResultsTableView.hidden;
}
#pragma mark SearchController
- (id)initWithSearchBar:(UISearchBar *)searchBar
{
if (self = [super init]) {
self.searchBar = searchBar;
self.searchBar.delegate = self;
}
return self;
}
- (void)setSearchResultsDataSource:(NSObject<UITableViewDataSource> *)searchResultsDataSource
{
_searchResultsDataSource = searchResultsDataSource;
if (self.searchResultsTableView != nil) {
self.searchResultsTableView.dataSource = searchResultsDataSource;
}
}
- (void)setSearchResultsDelegate:(NSObject<UITableViewDelegate> *)searchResultsDelegate
{
_searchResultsDelegate = searchResultsDelegate;
if (self.searchResultsTableView != nil) {
self.searchResultsTableView.delegate = searchResultsDelegate;
}
}
#pragma mark UISearchBarDelegate
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
if ([self.delegate searchController:self shouldReloadTableForSearchString:searchText]) {
[self.searchResultsTableView reloadData];
self.searchResultsTableViewHidden = [self.searchResultsTableView.dataSource tableView:self.searchResultsTableView numberOfRowsInSection:0] == 0;
}
}
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
[searchBar setShowsCancelButton:YES animated:YES];
if (self.searchResultsVeil == nil) {
self.searchResultsVeil = [[UIButton alloc] initWithFrame:self.view.bounds];
self.searchResultsVeil.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.6];
self.searchResultsVeil.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.searchResultsVeil addTarget:self action:#selector(didTapSearchResultsVeil) forControlEvents:UIControlEventTouchUpInside];
self.searchResultsTableView = [[UITableView alloc] initWithFrame:self.searchResultsVeil.bounds style:UITableViewStylePlain];
self.searchResultsTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
if ([self.searchResultsTableView respondsToSelector:#selector(setSeparatorInset:)]) {
self.searchResultsTableView.separatorInset = UIEdgeInsetsMake(
0.0,
self.searchResultsTableView.width,
0.0,
0.0
);
}
self.searchResultsTableViewHidden = YES;
if (self.searchResultsDataSource != nil) {
self.searchResultsTableView.dataSource = self.searchResultsDataSource;
}
if (self.searchResultsDelegate != nil) {
self.searchResultsTableView.delegate = self.searchResultsDelegate;
}
[self.view addSubview:self.searchResultsVeil];
[self.searchResultsVeil addSubview:self.searchResultsTableView];
}
self.view.userInteractionEnabled = YES;
self.searchResultsVeil.hidden = NO;
if ([self.delegate respondsToSelector:#selector(searchControllerDidBeginSearch:)]) {
[self.delegate searchControllerDidBeginSearch:self];
}
}
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
{
[searchBar setShowsCancelButton:NO animated:YES];
self.view.userInteractionEnabled = NO;
self.searchResultsVeil.hidden = YES;
if ([self.delegate respondsToSelector:#selector(searchControllerDidEndSearch:)]) {
[self.delegate searchControllerDidEndSearch:self];
}
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
{
[self hideSearchResults];
}
#end
Usage
self.searchController = [[SearchController alloc] initWithSearchBar:self.searchBar];
self.searchController.delegate = self;
self.searchController.searchResultsDataSource = self;
self.searchController.searchResultsDelegate = self;
[self addChildViewController:self.searchController];
self.searchController.view.frame = CGRectMake(
self.searchBar.x,
self.searchBar.bottomY,
self.searchBar.width,
self.view.height - self.searchBar.bottomY
);
[self.view addSubview:self.searchController.view];
[self.searchController didMoveToParentViewController:self];
It looks like your view controller does not define a presentation context. I had a similar problem and was able to resolve it by setting
self.definesPresentationContext = YES;
in viewDidLoad. According to the documentation this property is
A Boolean value that indicates whether this view controller's view is covered when the view controller or one of its descendants presents a view controller.