Since iOS 8 I'm experiencing a weird issue with a table view/UISearchBar setup and wondered if others have experienced a similar issue or can point to what, if anything, I might be doing wrong. The broad situation:
"The issues" are that if I implement cellForRowAtIndexPath as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = (MyCell *) [self.tableView dequeueReusableCellWithIdentifier:@"MyId" forIndexPath:indexPath];
...
in other words by specifying the path to dequeueReusableCellWithIdentifier, then this results in a BAD_ACCESS or assertion failure in iOS 8 (but not iOS 7). Specifically, either an assertion failure or BAD_ACCESS occurs on calling dequeueReusableCellWithIdentifier under the circumstances mentioned above, i.e. when, with a search active, you segue from one of the cells in the results table to another view and then segue back again.
Now, I can stop the error occurring by just calling:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = (MyCell *) [self.tableView dequeueReusableCellWithIdentifier:@"MyId"];
...
without passing in the indexPath. This then works without an error as such, but on segueing back to the table view with search results, a weird display issue occurs whereby layered underneath the search results, there appear to be the separators of a "ghost" table, almost as though the system is trying to render one table directly on top of another (but cellForRowAtIndexPath isn't being called for each table, only for the search results table as expected).
I get the same problem whether the segue is attached to the cell or table view controller (so in the latter case, I implement didSelectRowAtIndexPath to manually trigger the segue).
So: (a) can anyone point to something I might be doing wrong to cause these issues, or (b) point to a bare-bones working example of a table view controller with UISearchBar where the table cells segue to another view? I'm surprised I'm getting so many issues as implementing a searchable table with detail views must be a common, boring thing that people do all of the time, no?
Sample project exhibiting the iusse: http://www.javamex.com/DL/TableTest.zip
The records in the views are displayed properly. While working on opportunity, quote, order, or invoice records, you might observe that some records are missing. You might not see some records because of an error that occurred due to customization done to the subgrid.
Before you start resolving the issue, take note of the view for which this issue is occurring. Go to Settings > Customizations > Customize the System. Select the Entity > Views and select the view that has this issue. In this example, we're selecting the table Account and view as Accounts Being Followed. Select More Actions > Edit.
The "StudentInformation" view snippet is from "StudentInformation.cshtml". ViewBag is dynamic property that takes advantage of new dynamic features in C# 4.0. It's also used to pass data from a controller to a view. In short, The ViewBag property is simply a wrapper around the ViewData that exposes the ViewData dictionary as a dynamic object.
If you change the default view of the lookup field to a custom view, the field doesn't display products from the custom view. To be able to display products from the selected price list, the lookup always defaults to a system view named Products in Parent Price List.
I Downloaded your code and what i observed is if i change the
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
method as follows it is working fine for me.
Solution1:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [searchResults count];
} else {
return [testData count];
}
}
Please let me know if it works for you and what my suggestion is use "NSPredicate" to filter the array like as follows.
- (void) searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
NSLog(@"Search text did change");
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:@"SELF contains[cd] %@",
searchText];
searchResults = [testData filteredArrayUsingPredicate:resultPredicate];
}
Nlote: Take "searchResults" as NSArray not NSMutableArray.
The above solution is working for me Here is one more approach to solve the problem.
Solution2:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [searchResults count];
} else {
return [testData count];
}
}
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:@"SELF contains[cd] %@",
searchText];
searchResults = [testData filteredArrayUsingPredicate:resultPredicate];
}
// I implemented The following delegate method.
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchString:(NSString *)searchString {
[self filterContentForSearchText:searchString scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
return YES;
}
There's actually nothing wrong with using either method to dequeue your cell for the main tableView
, although the indexPath variant seems to be Apple's preferred option these days.
For the searchResultsTableView
however, avoid specifying the indexPath as it's not necessarily valid for your view controller's tableView
. That is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyTableViewCell *cell;
if ([tableView isEqual:self.searchDisplayController.searchResultsTableView]) {
cell = [self.tableView dequeueReusableCellWithIdentifier:@"MyCell"];
} else {
cell = [self.tableView dequeueReusableCellWithIdentifier:@"MyCell" forIndexPath:indexPath];
}
// configure the cell
}
For this to work correctly, you also need to amend your other UITableViewDataSource
method. Instead of:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return (searchResults) ? (searchResults.count) : (testData.count);
}
Do:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if ([tableView isEqual:self.searchDisplayController.searchResultsTableView]) {
return searchResults.count;
}
return testData.count;
}
Actually there are two tableViews, UISearchResultsTableView
for displaying the result, self.tableView
for source data displaying.
When you search for "One", and clear all search text, there is only one visible cell left in self.tableView
and no visible cell in UISearchResultsTableView
. Actually at this time both tables should have no visible cells left. Then you search for "T", now there should be two cells in UISearchResultsTableView
because "Two" and "Three" match "T", at this time, there is only one visible cell in self.tableView
, which label text is "One",dequeueReusableCellWithIdentifier:forIndexPath
works fine for row 0, for row 1, it crashes. I think the reason is that there is only one visible cell for self.tableView
.
MyTableViewCell *cell;
if (tableView != self.tableView) {
cell = [self.tableView dequeueReusableCellWithIdentifier:@"MyCell"];
} else {
cell = [self.tableView dequeueReusableCellWithIdentifier:@"MyCell" forIndexPath:indexPath];
}
And in textDidChange, add this at the end to clear tableView when searchText is empty string.
if (searchText.length == 0) {
[self.tableView reloadData];
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With