I have two UICollectionView objects, that both have different source and delegate. I would like to achieve a "Photos app'esque" look with the transition, using UseLayoutToLayoutNavigationTransitions.
It doesn't work though. When I call the UseLayoutToLayoutNavigationTransitions it changes the layout, but not the content.
First picture is first collection view. A series of categories and the people contained in them.
Second picture is what I'd like the animation to end up in. A series of people within a certain category.
Last picture is what happens right now. Categories just get rearranged.
Have a look at http://www.objc.io/issue-12/collectionview-animations.html in the
Transitions Between UICollectionViewController Instances
section.
it basically shows you that you'll have to change the datasource and delegate manually by implementation of the navigation controller delegate methods:
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if ([viewController isKindOfClass:[FJDetailViewController class]]) {
FJDetailViewController *dvc = (FJDetailViewController*)viewController;
dvc.collectionView.dataSource = dvc;
dvc.collectionView.delegate = dvc;
[dvc.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:_selectedItem inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:NO];
}
else if (viewController == self){
self.collectionView.dataSource = self;
self.collectionView.delegate = self;
}
}
Your problem is that during the transition iOS will change the datasource. See my answer to this question How to use useLayoutToLayoutNavigationTransitions in UICollectionView?
You can use the same pattern described there:
use UseLayoutToLayoutNavigationTransitions to get the layout changes
observe when the transition is done
set the datasource to the one you need at that point
Related
I found a really good walk through of how to pass string values back from a ViewController to a calling ViewController and got it working perfectly. The example is really very good.
https://www.youtube.com/watch?v=YVikeoR3gYg
That said, the technique for passing back content seems relatively straight forward now that I have seen it, even if it's not that intuitive.
The example code however only includes two controllers. When I replicated the code using a much more detailed Storyboard, the code simply doesn't work. In my test app, I even embedded the calling Controller inside a NavigationController to see whether this would have an affect, but it still continued to work fine.
In my application, the ViewController is embedded within a NavigationController that is called via a SWRevealController segue class. I don't know if this is important or relevant but I am mentioning it.
I then call a CollectionViewController to choose an icon that should be passed back to the calling ViewController.
When I select the icon, I correctly identify the icon and pop
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
selectedIcon = [placeIcons objectAtIndex:indexPath.row];
NSLog(#"In IconCollectionViewControlled - selected %#", selectedIcon);
NSString *itemToPassBack = #"12345"; // Just testing any old string here...
// [self.delegate passBackIcon:selectedIcon]; // commenting out while testing
[self.delegate passBackIcon:itemToPassBack];
[self.navigationController popViewControllerAnimated:YES];
}
I get a correct trace suggesting that the right icon is selected. I would then expect that the text '12345' would be passed back to the calling Controller.
In my calling Controller, I have the following:
- (void)passBackIcon:(NSString *)iconName {
NSLog(#"Icon to use is %#", iconName);
}
But this just isn't being called at all (or at least I'm not seeing the NSLog being shown. It's just being ignored.
The delegate is being correctly declared as far as I can tell.
Am I missing something?
assuming you are working with segues, in the method prepareSegue you should setting the delegate
for Example :
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"YOUR_SEGUE_IDENTIFIER"] ) {
DestinationVc *vc = (DestinationVc *)segue.destinationViewController;
[vc setDelegate:self];
}
}
Hope it works for you
I've found this to be the easiest way to pass string and other information around using a tableView.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
ViewControllerYouWantToPassToo *result = [self.storyboard instantiateViewControllerWithIdentifier:#"NameOfTheViewController"];
result.stringName = #"12345" // String Name is a NSString property you set up in the ViewController you want to pass too
[self.navigationController pushViewController:result animated:YES];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
I suggest you wrapping your delegate in a check to see that it is valid and that it has adopted the respective method (if optional).
if(self.delegate && [self.delegate respondsToSelector:#selector(passBackIcon:)]){
[self.delegate passBackIcon:itemToPassBack];
}else{
NSLog(#"Your delegate is not setup correctly");
}
If it enters the else, you have not set the delegate properly..ie you likely never did
self.delegate = SomeInstanceOfAClassThatAdoptsYourDelegate;
I am currently trying to add some search-functionality to my app, but there are some problems, when configuring this. I have a tab-based application with 3 tableviews and I want to add one searchbar for all tableviews. But it seems not that easy to set this up.
The first problem that occurs is that inside the Storyboard-Editor I only can add a separate searchbar to every tableview, but it is not possible to add searchbar to the tabBarController itself. So that the same searchbar is visible over all 3 tableviews.
The Second Problem then is, if I get this some how working, I have to setup a searchDisplayController with 3 different tableviews, but i can initialize a searchDisplayController with just one tableview.
What is the best approach of searching 3 different categories with one searchbar on an iPhone and are there any tutorials out there ? I was also looking at some other apps like facebook, and they are also searching just inside one tableview.
You can use three search display controllers as follows: have each vc on each tab implement a method (and declare it publicly) like this:
- (void)searchFor:(NSString *)string {
[self.searchDisplayController setActive:YES];
self.searchDisplayController.searchBar.text = string;
}
Each of those should implement this method:
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
// do the search logic for my table
// set a badge on my tab indicating how many results I found
// then perform it on the other vcs:
NSMutableArray *otherVCs = [[self.tabBarController viewControllers] mutableCopy];
[otherVCs removeObject:self];
for (MyViewController *otherVC in otherVCs) {
[otherVC searchFor:searchString];
}
}
You might need to make sure all your other tab vcs are loaded (if you launch the app, visit just one tab and try this, the other vcs might not be ready. To do that, just insert this line in the loop to force the view to load: (void)[otherVC view];
(note - this answer presumes ARC)
there is a little Problem with this solution. It causes an infinite loop. The solution is that we have to proof if the view is visible or not.
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self setUpFilteredEndlessScrollingWithUrl:kGetAllTours];
[self loadFilteredDataWithPath:kGetAllTours];
if (self.isViewLoaded && self.view.window) {
NSMutableArray *otherVCs = [[self.tabBarController viewControllers] mutableCopy];
[otherVCs removeObject:self];
for (BaseTableViewController *otherVC in otherVCs) {
[otherVC searchFor:searchString];
}
}
return YES;
}
I have a UIPageViewController and I just can not figure out how to know to what direction the user turned the page so i can set the page count appropriately.
Thanks
Shani
As Hejazi said
After a gesture-driven transition completes this delegate method
is called:
pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted:
The part that should be clarified is that completed will be YES if the page was fully turned and will be NO if the page did not actually turn. The NO case happens, for example, when the user just pulls up the corner of the page and then puts it back down without flipping the page.
This is the concept you will want to implement:
- (void)pageViewController:(UIPageViewController *)pvc didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
{
// If the page did not turn
if (!completed)
{
// You do nothing because whatever page you thought
// the book was on before the gesture started is still the correct page
return;
}
// This is where you would know the page number changed and handle it appropriately
// [self sendPageChangeNotification:YES];
}
After a gesture-driven transition completes this delegate method is called:
pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted:
So by comparing the previousViewControllers parameter and pageViewController.viewControllers you can know the direction.
The 'Page-Based Application' template provide these 2 methods :
- (NSUInteger)indexOfViewController:(DataViewController *)viewController;
a method for finding index given a view controller
- (DataViewController *)viewControllerAtIndex:(NSUInteger)index
a method for instantiating a view controller given an index.
For making the correct animation, you need to know the index of your current view controller. The page-based template methods are perfect fit for that. Then, you simply compare your 'jump to' index and your 'current' index.
Here's some code to get the idea :
- (void)jumpToPage:(NSInteger)page {
// find current index
DataViewController *currentViewController = (DataViewController *)[self.pageViewController.viewControllers lastObject];
NSUInteger index = [self indexOfViewController:currentViewController];
// choosing the correct direction
// if the 'current' is smaller than the 'jump to' page, then choose forward
// vice versa
UIPageViewControllerNavigationDirection direction;
if (index < page) {
direction = UIPageViewControllerNavigationDirectionForward;
} else {
direction = UIPageViewControllerNavigationDirectionReverse;
}
// choose view controllers according to the orientation
NSArray *viewControllers;
if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation)) {
DataViewController *rightViewController = [self viewControllerAtIndex:page];
viewControllers = [NSArray arrayWithObject:rightViewController];
} else {
DataViewController *rightViewController = [self viewControllerAtIndex:page];
DataViewController *leftViewController = [self viewControllerAtIndex:page-1];
viewControllers = [NSArray arrayWithObjects:leftViewController, rightViewController, nil];
}
// fire the method which actually trigger the animation
[self.pageViewController setViewControllers:viewControllers
direction:direction
animated:YES
completion:nil];
}
You could add a "pageIndex" property to your view controllers that serve as the pages. IOW, when you create the view controllers for viewControllerBeforeViewController & viewControllerAfterViewController (or when you call setViewControllers), store a pageIndex in those view controllers that you can then reference whenever you need to know the index.
great answers for page controller handling. I found that the view controller that was added as a page will call viewWillAppear as the user slides the page into view and will also call viewDidAppear upon completion.
When the user turns a page the UIPageViewController's setViewControllers: method will be called. This method receives an argument of type UIPageViewControllerNavigationDirection that will give you the information you need.
I want to show some default content when the user taps the Searchbar, but before any text is entered.
I have a solution working using settext:
- (void) searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller {
[searchDisplayController.searchBar setText:#" "];
}
This works but it's not elegant and the hint text in the Searchbar disappears.
Is there a better way to preload data to the SearchResultTableView in a UISearchDisplayController, without having to implement the whole UISearch functionality yourself in a custom controller?
For a demo of the desired effect, look at Safari's search box, if you tap it, the search interface opens with previous searches showing.
OK, I have it. Preload your dataSource with some data and do the following hack (nothing illegal) and you'll get the tableView to show up. Note that there may be some clean-up to do, but this will get your default data to display.
In viewDidLoad of the view controller than owns the UISearchBar:
[super viewDidLoad];
[self.searchDisplayController.searchResultsTableView reloadData];
// whatever you named your search bar - mine is property named searchBar
CGRect testFrame = CGRectMake(0, 20, self.searchBar.frame.size.width, 100);
self.searchDisplayController.searchResultsTableView.frame = testFrame;
[self.searchBar.superview addSubview:self.searchDisplayController.searchResultsTableView];
Here's what's happening:
The UISearchBar doesn't want to show the searchResultsTableView until you start editing. If you touch the tableView (e.g. reloadData) it will load and be there, sitting in memory with frame = CGRectZero. You give it a proper frame and then add it to the superview of the searchBar, et voila.
I verified this is correct by displaying the superview of the searchBar and the superview of the tableView after a proper load of the tableView - they have the same superview. So, I go back and add the tableView to the superview early, and sure enough it shows up. For me, it showed up dimmed (maybe another view above it? alpha set lower?), but was still clickable. I didn't go any further, but that definitely gets you your tableView displaying without any user interaction.
Enjoy,
Damien
I found a much better solution to this issue, and it seems to work perfectly on iOS 6 and 7. While it is still a hack, its a much cleaner and future proof hack than the above. The other solutions do not work consistently and prevent some UISearchDisplayDelegate methods from ever firing! Further I had complex insetting issues which I could not resolve with the above methods. The main issue with the other solutions is that they seriously confuse the internals of the UISearchDisplayController. My solution is based on the observation that UISearchDisplayContoller is a UISearchbarDelegate and that the automatic undimming & showing of results table can be triggered by simulating a keypress in the search field! So:
- (void) searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller
{
if ([controller respondsToSelector: #selector(searchBar:textDidChange:)])
[(id<UISearchBarDelegate>)controller searchBar: controller.searchBar textDidChange: #" "];
}
This code is future proof against crashing by checking it responds to the UISearchbarDelegate method, and sends space #" " to trick the UISearchDisplayController into thinking user has typed a letter.
Now if the user types something and then erases it, the table will dim again. The other solutions try to work around this by doing something in the searchDisplayController:didHideSearchResultsTableView: method. But this doesn't make sense to me, as surely when you cancel the search it will need to truly hide your results table and you may need to run code in this case. My solution for this part is to subclass (note you could probably use a Method Swizzled Category to make it work everywhere if needed in your project):
// privately declare protocol to suppress compiler warning
#interface UISearchDisplayController (Super) <UISearchBarDelegate>
#end
// subclass to change behavior
#interface GMSearchDisplayController : UISearchDisplayController
#end
#implementation GMSearchDisplayController
- (void) searchBar: (UISearchBar *) searchBar textDidChange: (NSString *) searchString
{
if (searchString.length == 0)
searchString = #" ";
if ([super respondsToSelector: #selector(searchBar:textDidChange:)])
[super searchBar: searchBar textDidChange: searchString];
}
#end
This code works by intercepting the textDidChange delegate method and changing nil or empty strings in to space string #" " preventing the normal hiding/dimming that occurs on an empty search bar. If you are using this second bit of code, then you could modify the first bit to pass a nil instead of #" " as this second bit will do the needed conversion to #" " for you.
In my own project, I needed to handle the case that user does type a space, so instead of #" " above I used a defined token:
// arbitrary token used internally
#define SEARCH_PRELOAD_CONDITIONAL #"_#preresults#_"
And then handle it internally by converting it back to nil string:
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
if ([searchString isEqualToString: SEARCH_PRELOAD_CONDITIONAL])
searchString = nil;
}
Enjoy! :)
Working solution for iOS 7:
// When the tableView is hidden, put it back
- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller {
[self.searchDisplayController.searchResultsTableView reloadData];
controller.searchResultsTableView.hidden = NO;
// Also, remove the dark overlay
for (UIView *v in [[controller.searchResultsTableView superview] subviews]) {
// This is somewhat hacky..
if (v.alpha < 1) {
[v setHidden:YES];
}
}
}
-(void)searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView {
[self.searchDisplayController.searchResultsTableView reloadData];
if (self.searchDisplayController.active == YES) {
tableView.hidden = NO;
}
}
I have the solution. Insert these three methods.You have to have the tableview preloaded with the data in order this to work. I have this codes working in my code so it has to work for you as long as you have already preloaded the data and you are using search display controller.
- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller
{
CGRect testFrame = CGRectMake(0, self.notesSearchBar.frame.size.height, self.notesSearchBar.frame.size.width, self.view.frame.size.height - self.notesSearchBar.frame.size.height);
self.searchDisplayController.searchResultsTableView.frame = testFrame;
[self.notesSearchBar.superview addSubview:self.searchDisplayController.searchResultsTableView];
// [self.view addSubview:self.searchDisplayController.searchResultsTableView];
controller.searchResultsTableView.hidden = NO;
}
-(void) searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView
{
CGRect testFrame = CGRectMake(0, self.notesSearchBar.frame.size.height, self.notesSearchBar.frame.size.width, self.view.frame.size.height - self.notesSearchBar.frame.size.height);
self.searchDisplayController.searchResultsTableView.frame = testFrame;
[self.notesSearchBar.superview addSubview:self.searchDisplayController.searchResultsTableView];
// [self.view addSubview:self.searchDisplayController.searchResultsTableView];
controller.searchResultsTableView.hidden = NO;
}
-(void) searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
controller.searchResultsTableView.hidden = YES;
}
This works in iOS 8:
- (void)searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView
{
self.searchDisplayController.searchResultsTableView.hidden = NO;
}
- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller
{
self.searchDisplayController.searchResultsTableView.hidden = NO;
[self.searchDisplayController.searchResultsTableView.superview.superview bringSubviewToFront:self.searchDisplayController.searchResultsTableView.superview];
CGRect frame = self.searchDisplayController.searchResultsTableView.frame;
self.searchDisplayController.searchResultsTableView.frame = CGRectMake(frame.origin.x, 64, frame.size.width, frame.size.height);
}
However, I did not like this hack and replaced the searchDisplayController by my own implementation (UISearchBar with UITableView).
I tested the following in iOS 9 with UISearchController.
By default, whenever there is no text in the UISearchBar, the table shown will be your original table view (referred to as originalTableView from here on out). This may or may not have a black see-through layer depending on the value of dimsBackgroundDuringPresentation.
Once the user enters any text into the UISearchBar, the contents of the new table view will be shown (I will refer to this as the searchTableView).
Note that in both of these solutions, I'm implementing the UISearchController in a manner similar to how Apple does so in this example; namely, there are two separate UITableViewController's in charge of showing the data.
Solution 1: Complicated implementation, smoother animation
For a smoother animation:
Implement the UISearchControllerDelegate's willPresentSearchController and willDismissSearchController methods.
In willPresent, update your originalTableView's data to get ready to display whatever zero text data you want, then call reloadData().
In willDismiss, revert the process to show the original contents and call reloadData().
Example:
// here tableView refers to originalTableView
extension ViewController: UISearchControllerDelegate {
func willPresentSearchController(searchController: UISearchController) {
data = ["A", "B", "C"]
tableView.reloadData()
}
func willDismissSearchController(searchController: UISearchController) {
data = ["a", "b", "c"]
tableView.reloadData()
}
}
In this fictional example, the data is displayed as a,b,c when the user is not focused on the UISearchBar, but is displayed as A,B,C once the user taps on the UISearchBar.
Solution 2: Quick to implement, choppy animation
Instead of implementing the logic for the search controller in both originalTableView and searchTableView, you can use this solution to simply show the searchTableView even though it is hidden by default.
Most likely, you are already implementing the UISearchResultsUpdating protocol. By simply calling tableView.hidden = false in that method, you should get the behavior you are after; albeit with a less than stellar animation.
// here tableView refers to searchTableView
extension SearchViewController: UISearchResultsUpdating {
func updateSearchResultsForSearchController(searchController: UISearchController) {
tableView.hidden = false
filterData(text: searchController.searchBar.text!)
tableView.reloadData()
}
}
Why don't you have a look at the TableSearch project that comes in Xcode? It more or less address what you need. It shows a table right at the beginning and shades it out when search field is tapped. Check TableSearch in Documentation and API reference in Xcode 4 under help.
Also take a look at this It almost does what you need with a UISearchBar and a UITableView. I'd prefer that approach instead. Tweak it.
And to address your needs further, I'd suggest using two datasources. One holding all the contents (this is what you would show at the very beginning) and one to hold the filtered search results. Now go ahead and empty the first datasource and fill it up with the contents of the second one to store & dim-show the results of the previous search. Proceed this way. Use this approach in the TableSearch code for your task.
Here's a way to pre-populate the searchController.
// 1. Save previous search results as soon as user enters text
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
// save searchString
return YES;
}
// 2. load previous search string at startup
- (void)viewDidLoad
{
// create data source for table view
// searchData may be a NSMutableArray property defined in your interface
self.searchData = // search your master list for searchString
// or create your own list using any search criteria you want
// or you may act depending on user preference
if(!showPreviousSearch)
{
// [searchData removeAllObjects];
}
}
// 3. provide correct data source when asked
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
...
}
NSMutableArray *dataSource = nil;
if (tableView == [[self searchDisplayController] searchResultsTableView])
{
dataSource = self.searchData;
}
else
{
dataSource = self.masterData;
}
...
return cell;
}
// Hope this helps.
Thanks
I am writing a SplitView iPad app. Inside the DetailViewController, there's a little view that contains a UITableView and a UISearchBar and its controller. This view does not represent the whole screen space reserved for the DetailViewController. Actually, it uses just half of it. There's an UIImageView on the other half.
And this is where trouble comes in: every time I use the search bar, the displaycontroller (I assume) dims everything present inside the DetailViewController, including the image view. That is not consistent with what someone would expect when running the app. Is there any way to set the frame to be dimmed? Or at least disable dimming for good?
Thanks in advance.
You are correct that it is the UISearchDisplayController that is managing the "dimming" effect that you're seeing.
What the UISearchDisplayController is doing is adding a UIControl as a subview to the view of the searchContentsController (a property of UISearchDisplayController), which is likely your detail-view controller. This UIControl is just an alpha'd view with a gray background. It seems to have a touch-up-inside event handler that ends searching when tapped.
To constrain the dimming effect to your sub-view of the detail-view, you need to do three things. (I'm assuming your detail-view-controller is defined via a xib. If not, these steps can be done in code too.)
1) add a new UIViewController to your detail-view-controller xib. Attach this new view-controller to an IBOutlet of your detail-view-controller. In my example I call this "_searchAreaViewController". This is important, even if you wont ever access the view controller (but remember, you'll have to release it at some point)
#interface DetailViewController : UIViewController <UIPopoverControllerDelegate, UISplitViewControllerDelegate, UITableViewDelegate, UITableViewDataSource> {
UIPopoverController *popoverController;
UIToolbar *toolbar;
id detailItem;
UILabel *detailDescriptionLabel;
IBOutlet UIViewController* _searchAreaViewController;
}
2) make the containing view for your search area the view of this new view-controller. To do this, use Interface Builder to set a new referencing outlet for this view by dragging the outlet to the searchAreaViewController and selecting the "view" outlet. You must have a containing view - it should be a subview of your detail-view, and it should contain the UISearchBar and likely your UITableView.
3) make the searchContentsController property of the UISearchDisplayController refer to this new view controller instead of the detail-view-controller. This can only be done via Interface Builder as the property is read-only (IB has some magic to make this work?) If you need to do this step via code you'll have to subclass the UISearchDisplayController and return the correct value from a property override of "searchContentsController".
I made a sample app to demonstrate this and the only line of code I had to add to the SplitView template was the one listed in step 1 above. Everything else was just adding the views/controllers and connecting them properly in IB.
good luck!
iOS 8+
[[UIView appearanceWhenContainedInInstancesOfClasses:#[NSClassFromString(#"UISearchDisplayControllerContainerView")]] setHidden:YES];
iOS 7
[View appearanceWhenContainedIn:NSClassFromString(#"UISearchDisplayControllerContainerView"), nil] setHidden:YES];
I know, that UISearchDisplayController is deprecated for now, but if you still need to use it, you can solve your issue with one line of code perfectly. Add it to viewDidLoad method.
Could you clarify what you mean by "use the search bar" and "dims everything present"? I interpret what you wrote in such a way that the keyboard pops up when you are about to enter text in the text field of the search bar. And that at this point the detail view is dimmed out, preventing user interaction.
The cause is that the search bar implements a modal dialog which prevents user interaction with the view as long as the keyboard is shown. Unfortunately, there doesn't seem to be any way to configure the search bar to prevent this behavior. On the other hand I am not sure that the user won't expect this behavior since search bars are modal consistently and behave like this in general under iOS.
I have tried two work-arounds:
1.) There is a property of the UIViewController called modalPresentationStyle which produces exactly the behavior you describe if it has the value UIModalPresentationFormSheet ("All uncovered areas are dimmed to prevent the user from interacting with them.", see the Apple documentation). But setting this property to a different values does not change the result (at least for me it didn't work).
2.) You would need to write your own non-modal search bar replacement since a standard UITextField is non-modal and thus does not dim out any other UI elements. This approach works, but you might need a little more work to make it look like a "regular" search bar. But, again, since this search bar behaves differently from the modal normal search bars in iOS this might not really be what the users expect.
I know I am late and this is a horrible idea here, but 'setHidden:No' did not work for me.
-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
BOOL hasBeenremoved = NO;
hasBeenremoved = [[[[NSThread mainThread] threadDictionary] objectForKey:#"hasBeenremoved"] boolValue];
if (hasBeenremoved)
{
UIView* dimmingView = nil;
dimmingView = [[[NSThread mainThread] threadDictionary] objectForKey:#"dimmingView"];
UIView* dimmingViewSuperView = nil;
dimmingViewSuperView = [[[NSThread mainThread] threadDictionary] objectForKey:#"dimmingViewSuperView"];
[dimmingViewSuperView addSubview:dimmingView];
[[[NSThread mainThread] threadDictionary] setObject:#NO forKey:#"hasBeenremoved"];
}
if ([searchText length] == 0 || [searchText isEqualToString:#""] )
{
[searchBar becomeFirstResponder];
[[[self primarySearchDisplayController] searchResultsTableView] reloadData];
[[[self primarySearchDisplayController] searchResultsTableView] setHidden:NO];
for( UIView *subview in self.view.subviews )
{
if([subview isMemberOfClass:[UIControl class]] ||
([[[subview class] description] isEqualToString:#"UISearchDisplayControllerContainerView"]))
{
for(UIView *subView2 in subview.subviews)
{
for(UIView *subView3 in subView2.subviews)
{
if (subView3.alpha < 1)
{
if ([[[subView3 class] description] isEqualToString:#"_UISearchDisplayControllerDimmingView"])
{
[[[NSThread mainThread] threadDictionary] setObject:subView3 forKey:#"dimmingView"];
[[[NSThread mainThread] threadDictionary] setObject:subView3.superview forKey:#"dimmingViewSuperView"];
[[[NSThread mainThread] threadDictionary] setObject:#YES forKey:#"hasBeenremoved"];
[subView3 removeFromSuperview];
}
}
}
}
}
}
}
}