I have UITableViewController with a UISearchBar as the tableHeaderView of its tableView. I also have a UISearchDisplayController initialized with that UISearchBar as its searchBar and that UITableViewController as its contentsController. So far so good, everything almost works.
The problem is that the UITableView has cells which have their accessoryType set to UITableViewCellAccessoryDetailDisclosureButton. Here's what happens:
To start, everything looks as it should:
The user taps inside the UISearchBar.
The UISearchDisplayController creates the dark overlay on top of the main table, and makes the index (as in, sectionIndexTitlesForTableView) of the main table disappear.
Suppose the user at this point hides the keyboard (by pressing the iPad "hide keyboard" button on the standard keyboard)
Since the user hasn't typed anything into the UISearchBar yet, we can still see the main table, albeit underneath the dark overlay added by the UISearchDisplayController.
The hiding of the keyboard exposes more of the main table, causing the main table to load more cells.
Now here's the problem: Since these cells are loaded while the index of the main table is hidden, the disclosure button is shown too far too the right (at least, compared to the other cells).
Moreover, when the user now cancels the search, those cells may not be reloaded causing the disclosure button to be shown underneath the index (which is now visible again).
I'm at a loss on how to work around this; the only option I can think of is to find the UIView that corresponds to the disclosure button and manually move it, but that seems incredibly hacky, if only because even finding that UIView requires a nasty hack. Any suggestions on how to fix this in a nicer way would be much appreciated!
Minimal runnable example
Below is a minimal example. Just start a new XCode project, enable ARC, iPad only, and replace the contents of the AppDelegate with the below. Note that for the sake of the minimal example I force the main table to reload its cells in searchDisplayController:willShowSearchResultsTableView, otherwise the main table will cache its cells and the problem won't show (in my actual application the main table is reloading its cells for others reasons, I'm not completely sure why -- but of course it should be fine for the main table to reload cells at any time.)
To see the problem happening, run the code, type something in the search box (you will see "Search result 0 .. 5") and then cancel the search. The disclosure buttons of the main table are now shown underneath, rather than beside, the index.
Below is just the code:
#interface AppDelegate ()
#property (nonatomic, strong) UITableViewController* mainTableController;
#property (nonatomic, strong) UISearchDisplayController* searchDisplay;
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
UITableViewController* tableViewController = [[UITableViewController alloc] initWithStyle:UITableViewStylePlain];
UITableView* tableView = [tableViewController tableView];
[tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
[tableView setDataSource:self];
[self setMainTableController:tableViewController];
UISearchBar* searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 0, 44)]; // Width set automatically
[tableView setTableHeaderView:searchBar];
UISearchDisplayController* searchDisplay = [[UISearchDisplayController alloc] initWithSearchBar:searchBar
contentsController:tableViewController];
[searchDisplay setSearchResultsDataSource:self];
[searchDisplay setDelegate:self];
[self setSearchDisplay:searchDisplay];
[[self window] setRootViewController:tableViewController];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if (tableView != [[self searchDisplay] searchResultsTableView]) {
return 26;
} else {
return 1;
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView != [[self searchDisplay] searchResultsTableView]) {
return 10;
} else {
return 5;
}
}
- (void)searchDisplayController:(UISearchDisplayController *)controller willShowSearchResultsTableView:(UITableView *)tableView {
// The problem arises only if the main table view needs to reload its data
// In this minimal example, we force this to happen
[[[self mainTableController] tableView] reloadData];
[tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"SearchCell"];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView != [[self searchDisplay] searchResultsTableView]) {
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
[[cell textLabel] setText:[NSString stringWithFormat:#"%c%d", 'A' + [indexPath section], [indexPath row]]];
[cell setAccessoryType:UITableViewCellAccessoryDetailDisclosureButton];
return cell;
} else {
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:#"SearchCell" forIndexPath:indexPath];
[[cell textLabel] setText:[NSString stringWithFormat:#"Search result %d", [indexPath row]]];
[cell setAccessoryType:UITableViewCellAccessoryDetailDisclosureButton];
return cell;
}
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
if (tableView != [[self searchDisplay] searchResultsTableView]) {
return [NSArray arrayWithObjects:#"A", #"B", #"C", #"D", #"E", #"F", #"G", #"H", #"I", #"J", #"K", #"L", #"M", #"N", #"O", #"P", #"Q", #"R", #"S", #"T", #"U", #"V", #"W", #"X", #"Y", #"Z", nil];
} else {
return nil;
}
}
If you are looking to mimic the way Apple's own apps seem to behave under these circumstances, then the correct course of action would be the cause the detail disclosure buttons to all move to the right when starting the search and then to all move back again once the search is complete.
I have achieved this myself in your example by calling reloadData on your main table view in two UISearchDisplayDelegate methods which I added to your code:
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
{
[[[self mainTableController] tableView] reloadData];
}
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
[[[self mainTableController] tableView] reloadData];
}
This will force your detail disclosure views to be repositioned to take account of the visibility of the table view index, keeping the position of all disclosure indicators consistent with each other whether in search mode or not.
Update
I've toyed around with reloading the table view in other UISearchDisplayDelegate methods including:
- (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView
- (void)searchDisplayController:(UISearchDisplayController *)controller willUnloadSearchResultsTableView:(UITableView *)tableView
But these produce a rather jarring effect with the positions of the detail disclosure buttons jumping around abruptly so I'd recommend the willBeginSearch and willEndSearch methods as previously stated.
The easiest, and possibly cleanest, way that I can think of is to tell your viewcontroller (or view) to listen for keyboard events. Then when the keyboard is minimized you resign the first responder of the search bar and reload your tableview(if it doesn't already reload it properly).
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
//Your code here
// register for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:self.window];
// register for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:self.window];
}
return self;
}
-(void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name: UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name: UIKeyboardWillHideNotification object:nil];
}
And then add these two functions and do what you need in them.
- (void)keyboardWillHide:(NSNotification *)aNotification
{
//searchbar resignFirstResponder
//Tableview reload (if needed)
}
- (void)keyboardWillShow:(NSNotification *)aNotification
{
//Don't really need this one for your needs
}
Since you are resigning first responder in the keyboardwillhide function (before the keyboard starts moving) your tableview cells should reload properly without you having to reload them again.
The problem is not restricted to tables with section index titles. I had a similar problem with section header titles. If you add
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if (tableView != [[self searchDisplay] searchResultsTableView]) {
return [NSString stringWithFormat:#"Section %c", 'A' + section];
} else {
return nil;
}
}
to your "Minimal runnable example" program, then you will observe that the section header titles appear also in the search table view as soon as the main table view is reloaded. (The same problem was reported here: Removing Header Titles from a UITableView in Search Mode.)
The only solution I know of is to avoid all updates to the main table view as long as the search display controller is active ([self.searchDisplay isActive] == YES) and reload the main table view only when the search table is unloaded:
- (void)searchDisplayController:(UISearchDisplayController *)controller willUnloadSearchResultsTableView:(UITableView *)tableView {
[[[self mainTableController] tableView] reloadData];
}
Related
I am currently having a hard time with a problem I have.
I created a View with 2 containers inside on is a UITableView and the other shows informations considering the first one selection.
Everything seems to work find except when I swipe for deleting (actually I don't try to remove the row, but just to reset the information contained in the object - a list of image - and refresh the view to show that the reset was done) on the left container.
At that time the process delete what has to be deleted and all (refresh also the UI) but sometimes after this event I can't select any object in my list any more, like if the table was not User Interaction enabled any more (Event aren't received any more I tried with break points). But the rest of the view is still working (I can still click on the right container's buttons etc). Moreover, if I click on my button to open the camera, when I get back the view is working perfectly again. Is there any ways that my refresh get stuck in some way ?
Xcode doesn't give me any stop so the program keeps on working.
I don't really know what code to show (I didn't put all the code but all I thought was important) :
#implementation PJTableViewController
{
NSIndexPath *selectedRow;
NSMutableArray *tableData;
}
#synthesize attachmentShow, attachments, listPJ;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reloadTableView)
name:#"reloadLeftContainer"
object:nil];
selectedRow = [NSIndexPath indexPathForRow:0 inSection:0];
if( attachments != nil )
{
tableData = attachments.mutableCopy;
}
[listPJ setScrollEnabled:YES];
}
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self selectCurrentRow];
}
// Selection
-(void) selectCurrentRow
{
NSIndexPath *indexPath = selectedRow;
[listPJ selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
[self tableView:listPJ didSelectRowAtIndexPath:indexPath];
}
//Reload
-(void) reloadTableView
{
[listPJ performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
[self selectCurrentRow];
}
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
selectedRow = [tableView indexPathForSelectedRow];
NSDictionary *dictionary = tableData[indexPath.section];
Attachment *att = (Attachment*)[dictionary objectForKey:[dictionary allKeys][0]][indexPath.row];
if( att )
[attachmentShow setAttachment:att];
}
-(BOOL) tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *dictionary = tableData[indexPath.section];
Attachment *att = (Attachment*)[dictionary objectForKey:[dictionary allKeys][0]][indexPath.row];
if( att.attachmentsPath.count == 0)
return NO;
return YES;
}
-(void) tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *dictionary = tableData[indexPath.section];
Attachment *att = (Attachment*)[dictionary objectForKey:[dictionary allKeys][0]][indexPath.row];
// Reset Attachment
if( att.attachmentsPath.count > 0)
{
[att resetAttachment];
[attachmentShow setAttachment:att];
[self reloadTableView];
}
}
[att resetAttachment]; This line allow me to reset the attachment content.
[attachmentShow setAttachment:att]; This line allow me to set the update the right container with the new informations.
Use -reloadRowsAtIndexPaths:withRowAnimation:. Never reload the entire tableView when you're updating specific things.
So I have a UIViewController that I'm presenting over the current view modally. This view controller consists only of a UITableView (for selecting items), and a navigation bar at the top which has a Cancel button in case you don't want to select anything.
The whole thing works just fine in almost any situation. Selecting an item works, pressing the cancel button dismisses the view, everything is fine. However, there's one case that causes the app to crash: when you swipe left on an item in the table view to reveal the delete button, then press the cancel button at the top to dismiss the view, the app crashes and it doesn't say anything about the cause of the crash in the console output. Here's the code for the view controller I'm presenting modally:
.h:
#import <UIKit/UIKit.h>
#import "common.h"
#interface LoadViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
- (IBAction)lCancelButton:(id)sender;
#property (weak, nonatomic) IBOutlet UITableView *lTableView;
#end
.m:
#import "LoadViewController.h"
#interface LoadViewController () {
NSMutableArray* sampleCounts;
NSArray* tableData;
}
#end
#implementation LoadViewController
#synthesize lTableView = _lTableView; // This is the table view itself
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Deleting an item when the delete button is pressed
[self.lTableView beginUpdates];
// Deleting it from the table view first
[self.lTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
// ... then from the arrays as well
[sampleCounts removeObjectAtIndex:indexPath.item];
// (making a mutable copy here so I can delete stuff from it)
NSMutableArray* tmp = [tableData mutableCopy];
[tmp removeObjectAtIndex:indexPath.item];
tableData = tmp;
[self.lTableView endUpdates];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
self.lTableView.allowsMultipleSelectionDuringEditing = NO;
sampleCounts = [NSMutableArray array];
tableData = [NSMutableArray array];
// I'm filling up both arrays with the appropriate data here...
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (IBAction)lCancelButton:(id)sender {
// Dismiss the view controller when Cancel is pressed
[self dismissViewControllerAnimated:YES completion:nil];
}
-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.tableData.count;
}
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// If an item gets selected, store the name of it (I use a class called 'common' for storing data like this), then dismiss the view
[common setItemName:[tableData objectAtIndex:indexPath.item]];
[self dismissViewControllerAnimated:YES completion:nil];
}
-(UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString* ID = #"ID";
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
}
cell.textLabel.text = [self.tableData objectAtIndex:[indexPath row]];
cell.detailTextLabel.text = [sampleCounts objectAtIndex:[indexPath row]];
return cell;
}
#end
So as you can see, it's very simple. I have the item names and the descriptions of them in two separate arrays (both arrays hold NSString*s), I fill up the table view with that information, and that's it. The rest is pretty straight forward.
So does anyone have any idea why the app crashes when I swipe on an item to reveal the delete button, and then dismiss the view controller by pressing Cancel? In every other case, everything works perfectly fine. It only crashes when I press Cancel whenever a delete button is visible.
Try the following:
- (void)viewWillDisappear:(BOOL)animated
{
self.lTableView.editing = NO;
}
What I can think of is about your mode.
Probably is you are in the editing mode.
So, try to go back to the normal mode before dismissing.
[self.tableView setEditing:No];
In my application I have a TableView full of cells. Everything works just fine -- When I tap on a cell, it calls tableView:DidSelectRowAtIndexPath: right away and executes a segue, bringing me to the next screen. I also have a search bar, using UISearchDisplayController, allowing users to search through the items in the tableView. When I type some text into the search bar, the cells that match the search display in the table.
Now, my problem is when I tap on one of the cells displayed in this search results table... On one initial tap, the table view does not respond in any way. If the tap is held just briefly, the cell turns gray, as if it were selected, however tableView:DidSelectRowAtIndexPath: is still not called, and the cell turns back to normal after releasing the tap. But if I do a long press for a few seconds, then tableView:DidSelectRowAtIndexPath: is finally called, and I am brought to the correct screen.
Has anyone encountered this problem before? As far as I know I have implemented my UISearchDisplayController the same exact way as I always have, and have never had this problem.
Thank You, and let me know if I can give any additional information that may be helpful
EDIT
I am not certain wherein the problem lies exactly, so I'm not sure which methods to show, but here is some code...
I am bringing up the search bar upon clicking an icon in the UINavigationBar, then removing it from the superview once the editing has finished.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSArray *contentArray;
if (tableView == self.tableView) {
contentArray = self.postArray;
} else if (tableView == self.searchDisplayController.searchResultsTableView) {
contentArray = self.searchResults;
}
// This is pretty hackish, but it wasn't working before for some reason. So I send the PFObject I want as the sender
[self performSegueWithIdentifier:#"ShowPostDetails" sender:contentArray[indexPath.row]];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"PostCell";
PostTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[PostTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
[self updateSubviewsForCell:cell inTableView:tableView atIndexPath:indexPath];
/*
if (tableView == self.searchDisplayController.searchResultsTableView) {
[cell addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(cellTapped:)]];
} */
return cell;
}
#pragma mark - Search Bar Delegate
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"text contains[c] %#", searchText];
self.searchResults = [self.postArray filteredArrayUsingPredicate:resultPredicate];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
{
[self.view addSubview:self.searchDisplayController.searchBar];
self.searchDisplayController.searchBar.center = CGPointMake(self.view.window.center.x, 42);
}
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
[self.searchDisplayController.searchBar removeFromSuperview];
}
I found my problem...
The specific view controller that was experiencing this problem is subclassed from the view controller containing these delegate methods, and contains a UITextField for entering information. I watch for a keyboardDidAppear notification, and when it appears I add a UITapGestureRecognizer to the view to close the keyboard by resigning first responder of the UITextField when the view is tapped. When I added this I had not yet implemented the search feature, so I knew the only reason the keyboard would pop up is for the UITextField. The problem is that this extra TapGestureRecognizer added when the keyboard popped up for the search bar prevented the TapGestureRecognizer built into the UITableView cell from firing. I was looking in the wrong spot for the problem.
To fix the problem I simply made a check that the UITextField is indeed the first responder before adding the gesture recognizer. Now all works as it is supposed to.
So for anyone else experiencing a similar problem, I'd say go back and make sure you don't have any other UIGestureRecognizers that might be conflicting with the gesture recognizers of your tableView.
Thanks to everyone who commented. Helped lead me to where the problem was.
I have tried to implement a search bar but I have not had any luck dealing with this problem. I would really appreciate any help that can be provided. I've a big project in which I've a table view, and I want to implement a search bar over it and see the real time filtering. I do not use Storyboard but I'm using XIB. I've added the following protocols:
<UITableViewDelegate,UITableViewDataSource,UISearchBarDelegate,UISearchDisplayDelegate>
I've declared 2 arrays in #interface , the first for the whole elements and the second one for the filtered ones:
NSArray* OldList;
NSArray* filteredList;
Then I've setted the number of rows and the number of sections and then:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
myClassCell *cell = [tableView dequeueReusableCellWithIdentifier:MYCELLCLASS];
if (cell == nil)
{
cell = [myClassCell newFromNib];
}
NSMutableDictionary* elem = nil;
if (tableView == self.searchDisplayController.searchResultsTableView)
{
elem = [filteredList objectAtIndex:indexPath.row];
if ([elem count]+1 > indexPath.row)
[cell showValues:elem];
else
[cell showValues:nil];
}
else
{
elem = [OldList objectAtIndex:indexPath.row];
if ([elem count]+1 > indexPath.row)
[cell showValues:elem];
else
[cell showValues:nil];
}
return cell;
}
-(void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"name contains[c] %#", searchText];
filteredist = [OldList filteredArrayUsingPredicate:resultPredicate];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
return YES;
}
At this point, I haven't done any changes to the xib, no links and no other stuffs. If I compile I get my table, but obviously if I try to search something, nothing works. Moreover if I scroll down to the end of the table the app crashes. The real problem is that I can't see the search bar working. Could someone help me please?
Well you're on the right track... it is exactly to do with the connection of your controller class to your controller xib.
When you want to initialise a Search Bar and Search Display Controller into a UITableView, you are effectively adding a second table view that, when activated, must be managed by code in your UITableViewController class in the same manner as any UITableView.
I have used these SO questions/answers to check my own answer - I recommend you take a look:
Creating a UISearchDisplayController programmatically
Gray UISearchBar w/matching scope bar programmatically
I have read the Apple Documentation. I recommend you do the same to help you understand this.
First Step:
You will need to set data source and delegate methods for both table views when you run your controller class.
Before you do any of this, include this property...
#property (nonatomic, strong) UISearchDisplayController *searchController;
The following code describes how to initialise and set the appropriate properties for a UISearchBar and a UISearchDisplayController. If you are programmatically creating a UITableViewController in code you will also need to set the data source and delegate for it (not shown to keep the code easy to read).
You have two options here - which one you choose depends on your code and what you wish to achieve - either set these in your init/awakeFromNib methods, or set these in one of your table view controller (TVC) lifecycle methods.
Option One - Init
(Note1: Paul Hegarty's extraordinary iTunesU lectures taught me to init/awake a class as follows - in this way you are covered for both scenarios - you call init or it can awakeFromNib.)
- (void)setup {
// Provide initialisation code here!!!
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectZero];
[searchBar sizeToFit];
[searchBar setDelegate:self];
[self setSearchController:[[UISearchDisplayController alloc] initWithSearchBar:searchBar
contentsController:self]];
[self.searchController setSearchResultsDataSource:self];
[self.searchController setSearchResultsDelegate:self];
[self.searchController setDelegate:self];
}
- (void)awakeFromNib {
[self setup];
}
- (id)initWithStyle:(UITableViewStyle)style {
self = [super initWithStyle:style];
if (self) {
[self setup];
}
return self;
}
OR
Option Two - TVC Lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectZero];
[searchBar sizeToFit];
[searchBar setDelegate:self];
[self setSearchController:[[UISearchDisplayController alloc] initWithSearchBar:searchBar
contentsController:self]];
[self.searchController setSearchResultsDataSource:self];
[self.searchController setSearchResultsDelegate:self];
[self.searchController setDelegate:self];
[self.tableView setTableHeaderView:self.searchController.searchBar]; // see Note2
...< other code as required >...
}
Note2: Regardless of which of these options you choose, you will need to place the following line of code in your viewDidLoad method...
[self.tableView setTableHeaderView:self.searchController.searchBar]; // (or just searchBar)
Second Step:
Notes:
The table view that represents your complete data set (OldList) can be called using self.tableView (PS convention is to start each variable with lower case - so change your property name from OldList to oldList).
The table view that represents the filtered data set (filteredList) can be called using self.searchController.searchResultsTableView.
While you have prepared your tableView:cellForRowAtIndexPath: data source method, I suspect you have other data source (and maybe delegate) methods that need to be informed of which table view is the current table view, before they are able to function properly and provide you with a fully operational search results table view and search function.
For example:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if (tableView == self.searchController.searchResultsTableView)
return 1;
return [[self.oldList sections] count];;
}
and:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == self.searchController.searchResultsTableView)
return [self.filteredList count];
return [self.oldList count];
}
Note that there may be other data source (and maybe delegate) methods that need to be informed of which table view is the current table view... I will leave it to you to determine which of these methods are to be modified, and the corresponding code necessary to adjust the table view.
Third Step:
You will be required to register a nib and reuse identifier for your search results table view.
I prefer to create a separate nib file (called "TableViewCellSearch.xib") that contains one table view cell, with the reuse identifier "SearchCell", and then place the code to register this nib and reuse identifier in the following UISearchDisplayController delegate method.
It is worth noting that this code is just as effective after the code block examples above in init/awakeFromNib/viewDidLoad.
- (void)searchDisplayController:(UISearchDisplayController *)controller willShowSearchResultsTableView:(UITableView *)tableView {
static NSString *cellIdentifierSearch = #"SearchCell";
UINib *nib = [UINib nibWithNibName:#"TableViewCellSearch" bundle:nil];
[self.searchController.searchResultsTableView registerNib:nib forCellReuseIdentifier:cellIdentifierSearch];
}
Try these suggestions.
Hope this helps.
I have a feature in my app where the user can change the color scheme of the app. The app uses a Split View Controller, with a MainTable and DetailView table. Everything works fine except for the MainTable. What is failing is that the MainTable reloadData method is not causing the cells to be redrawn.
It should be noted that I am changing globalHighContrast and sending the notification from a UIModalPresentationFormSheet viewController, so the tables are kind of visible on the screen while the viewController is active.
I am triggering the screen update from a notification, like this:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reloadAllTables)
name:#"contrastModeChanged"
object:nil];
Then, to make sure that I call reloadData on the main thread, I am handling the notification like this:
-(void)reloadAllTables{
[self performSelectorOnMainThread:#selector(doReloadAllTables) withObject:nil waitUntilDone:NO];
}
-(void)doReloadAllTables{
[self showIcon];
if( globalHighContrast ){
theTable.backgroundColor = [Colors lightBkgColor];
self.view.backgroundColor = [Colors lightBkgColor];
} else {
theTable.backgroundColor = [Colors darkBkgColor];
self.view.backgroundColor = [Colors darkBkgColor];
}
[detailViewController configureView:currentMainMenu];
[detailViewController.subTable reloadData];
[theTable reloadData];
// desperate try to force it to work
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:currentMainMenu inSection:0];
[self tableView:theTable didSelectRowAtIndexPath:indexPath];
}
Both reloadAllTables and doReloadAllTables are being called, but
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
is not being called.
As soon as I tap a cell on the MainTable it does update correctly to the new color scheme.
Also, there is a desperate attempt to workaround this by trying to simulate the MainTable touch, but that doesn't work either.
You can try to put code for updating you scheme in -(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath method...