I am a beginner to iOS development and am having trouble finding the cause for this issue.
I have a simple app with a Master and DetailView controllers. The Master View contains an UITableView with a SearchController. Selecting any row on the UITableView will transition to the detailview.
The app freezes when I perform the following sequence
Launch app
Pull down the search bar
Enter text
Select a row from the search results
Select back from the detail view
Now the app freezes after the ViewDidLoad method of the MasterView is loaded. I can't find anything in the system.log.
Here's the code from the MasterViewController
- (void)viewWillAppear:(BOOL)animated {
self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 44.0f)];
[self.tableView setContentOffset:CGPointMake(0,40)];
self.tableView.tableHeaderView = self.searchBar;
// Create and configure the search controller
self.searchController = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self];
self.searchController.searchResultsDataSource = self;
self.searchController.searchResultsDelegate = self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *months = #"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec";
feeds = [months componentsSeparatedByString:#","];
self.navigationItem.leftBarButtonItem = self.editButtonItem;
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(insertNewObject:)];
self.navigationItem.rightBarButtonItem = addButton;
self.detailViewController = (DetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"numberOfRowsInSection");
if(tableView == self.tableView)
{
return feeds.count;
}
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF like %#", self.searchBar.text];
self.filteredFeeds = [feeds filteredArrayUsingPredicate:predicate];
return feeds.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
}
cell.textLabel.text = [feeds objectAtIndex:indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
NSDate *object = _objects[indexPath.row];
self.detailViewController.detailItem = object;
}
[self performSegueWithIdentifier: #"showDetail" sender: self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
NSDate *object = _objects[indexPath.row];
[[segue destinationViewController] setDetailItem:object];
}
}
I have uploaded the entire project at the location below.
https://www.dropbox.com/s/yok9vngzv143npa/search.zip
Any kind of assistance is appreciated.
I can not able to run your program because version issue of iOS.
But i seen your code. I suspect on 1 point on following code,
- (void)viewWillAppear:(BOOL)animated {
self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 44.0f)];
[self.tableView setContentOffset:CGPointMake(0,40)];
self.tableView.tableHeaderView = self.searchBar;
// Create and configure the search controller
self.searchController = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self];
self.searchController.searchResultsDataSource = self;
self.searchController.searchResultsDelegate = self;
}
You create/allocate UISearchBar and UISearchDisplayController again and again while you get back from DetailViewController to MasterViewController, these may be cause of your app freezing.
"viewWillAppear" called before the receiver’s view is about to be added to a view hierarchy and before any animations are configured for showing the view.
"viewDidLoad" called after view controller has loaded its view hierarchy into memory.
I suggest you place your one time allocations view code in viewDidLoad method.
For more information go through this doc https://developer.apple.com/library/ios/documentation/uikit/reference/UIViewController_Class/Reference/Reference.html#//apple_ref/occ/instm/UIViewController/viewDidLoad
Related
I've wired a UISearchDisplayController into my app along with a search bar. I've initialized it as so:
self.filteredObjectArray = [NSMutableArray arrayWithCapacity:[self.objectArray count]];
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 43)];
searchBar.delegate = self;
[self.view addSubview:searchBar];
UISearchDisplayController *searchDisplay = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
searchDisplay.delegate = self;
searchDisplay.searchResultsTableView.delegate = self;
searchDisplay.searchResultsDataSource = self;
searchDisplay.searchResultsDelegate = self;
Which is pretty much the way I've seen it in other examples. I've also implemented the appropriate delegate methods, with the important filterContentForSearchText and the shouldReload* methods shown below:
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
[self.filteredObjectArray removeAllObjects]; // First clear the filtered array.
for (XRay *xray in objectArray) {
NSString *combinedLabel = [self combinedLabel:xray];
if ([combinedLabel rangeOfString:searchText options:(NSCaseInsensitiveSearch)].location != NSNotFound) {
[self.filteredObjectArray addObject:xray];
}
}
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString scope:
[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
return YES;
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption
{
[self filterContentForSearchText:self.searchDisplayController.searchBar.text scope:
[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:searchOption]];
return YES;
}
However my search is not working when I give the search text box focus or input any characters. You'll also notice that the greyed out view does not appear over my regular table view.
EDIT
cellForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"XraySearchChoice";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
XRay *xray = nil;
if (tableView == self.searchDisplayController.searchResultsTableView) {
xray = [self.filteredObjectArray objectAtIndex:indexPath.row];
} else {
xray = [self.objectArray objectAtIndex:indexPath.row];
}
cell.textLabel.text = [self combinedLabel:xray];
return cell;
}
I figured out what was wrong and I'm a bit surprised to be honest. Once I set a property on the SearchTableViewController:
#property (nonatomic, strong) UISearchDisplayController *searchDisplay;
And updated my code to use that one, everything worked from there. Here is my initializing code with the new property.
- (void)viewDidLoad
{
self.filteredObjectArray = [NSMutableArray arrayWithCapacity:[self.objectArray count]];
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 43)];
searchBar.delegate = self;
self.tableView.tableHeaderView = searchBar;
self.searchDisplay = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
self.searchDisplay.delegate = self;
self.searchDisplay.searchResultsDataSource = self;
self.searchDisplay.searchResultsDelegate = self;
}
Try with below code it will help you
self.filteredObjectArray = [NSMutableArray arrayWithCapacity:[self.objectArray count]];
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 43)];
searchBar.delegate = self;
//[self.view addSubview:searchBar];
self.theTableView.tableHeaderView = searchBar;//theTableView your tableview object
UISearchDisplayController *searchDisplay = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
self.searchController = searchDisplay;
[searchBar release];
searchDisplay.delegate = self;
searchDisplay.searchResultsDataSource = self;
searchDisplay.searchResultsDelegate = self;
I followed the tutorial here and was wondering how to make the table appear grouped.
ex:
group1 contains Subview One and Subview Two
group2 contains Subview Three
I switched the type in interface builder but that only shows one group.
Thanks,
Adam
Sidenote* I am a completely new at objective c, hence the tutorial.
EDIT
I thought it might be helpful to put up the piece of code
#import "RootViewController.h"
#import "SubViewOneController.h"
#import "SubViewTwoController.h"
#implementation RootViewController
#pragma mark -
#pragma mark View lifecycle
-(void)awakeFromNib {
views = [[NSMutableArray alloc] init];
SubViewOneController *subViewOneController = [[SubViewOneController alloc] init];
SubViewTwoController *subViewTwoController = [[SubViewTwoController alloc] init];
//Subview 1
subViewOneController.title = #"Subview One";
[views addObject:[NSDictionary dictionaryWithObjectsAndKeys:
#"Subview One", #"title",
subViewOneController, #"controller",
nil]];
[subViewOneController release];
//Subview 2
subViewOneController = [[SubViewOneController alloc] init];
subViewOneController.title = #"Subview Two";
[views addObject:[NSDictionary dictionaryWithObjectsAndKeys:
#"Subview Two", #"title",
subViewTwoController, #"controller",
nil]];
[subViewOneController release];
//Subview 3
subViewOneController = [[SubViewOneController alloc] init];
subViewOneController.title = #"Subview Three";
[views addObject:[NSDictionary dictionaryWithObjectsAndKeys:
#"Subview Three", #"title",
subViewOneController, #"controller",
nil]];
[subViewOneController release];
UIBarButtonItem *temporaryBarButtonItem = [[UIBarButtonItem alloc] init];
temporaryBarButtonItem.title = #"Back";
self.navigationItem.backBarButtonItem = temporaryBarButtonItem;
[temporaryBarButtonItem release];
self.title = #"Basic Navigation";
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [views count];
}
//
//I think it goes somewhere in here
//
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewStyleGrouped reuseIdentifier:CellIdentifier];
}
cell.text = [[views objectAtIndex:indexPath.row] objectForKey:#"title"];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UIViewController *targetViewController = [[views objectAtIndex:indexPath.row] objectForKey:#"controller"];
[[self navigationController] pushViewController:targetViewController animated:YES];
}
- (void)dealloc {
[views dealloc];
[super dealloc];
}
#end
Ok so you are correct it does go into your cellForRowAtIndexPath: you will be looking to do something like:
if ([indexPath section] == 0) {
//stuff for first section.
}
if ([indexPath section] == 1) {
//stuff for second section.
}
You will need to also deal with how your are configuring the cell from your array. The sections row numbers start with 0. The same thing with didSelectRowAtIndexPath:
If you don't deal with the section where you set the cell.text you will end up with
Subview One
Subview Two
Subview One
I recommend getting the iPhone Programming (The Big Nerd Ranch Guide)
In Interface Builder, choose your UITableView object and choose Grouped as the style.
If you are programmatically creating it, use the initWithStyle: UITableViewStyleGrouped.
EDIT:
if (section == 0) { /* your first section */ }
if (section == 1) { /* your second section */ }
This is all controlled by your UITableViewDataSource. The numberOfSectionsInTableView: method controls how many groups there are, and tableView:numberOfRowsInSection: controls how many rows are in each group.
Your tableView:cellForRowAtIndexPath: on the UITableViewDelegate gets an NSIndexPath object; indexPath.section tells you which group and indexPath.row tells you which row in the group. The cell you return really has no idea which group it is in or which row in the group it is, it's all controlled by the fact that you return it for a particular indexPath when tableView:cellForRowAtIndexPath: is called.
After retrieving search results from a UISearchBar, the results appear in my tableview correctly, but the view is 'greyed out' (see image below)..Any help on this is appreciated, I can't find a solution the Apple documentation.
This is my code that is fired upon hitting the Search button:
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
isSearchOn = YES;
canSelectRow = YES;
self.tableView.scrollEnabled = YES;
CityLookup *cityLookup = [[CityLookup alloc] findCity:searchBar.text];
if ([cityLookup.citiesList count] > 0) {
tableCities = cityLookup.citiesList;
}
[cityLookup release];
isSearchOn = NO;
self.searchBar.text=#"";
[self.searchBar setShowsCancelButton:NO animated:YES];
[self.searchBar resignFirstResponder];
[self.navigationController setNavigationBarHidden:NO animated:YES];
[self.tableView reloadData];
}
And this is how the table view is being refreshed:
-(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *kCellID = #"cellID";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellID];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kCellID] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
NSString *cellValue = [tableCities objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
return cell;
}
If you are using the full package with a UISearchDisplayController the following line should remove the search interface:
[self.searchDisplayController setActive:NO animated:YES];
If you are not using UISearchDisplayController I would recommend checking it out and see if it doesn't suit your needs.
Remark: There is nothing in your posted code that would remove whatever view you have used to grey out the table view. So if you are not using UISearchDisplayController look in the code that displays the search interface to see what you need to do to remove it.
Your not hiding your search bar when hitting search, try:
[self.navigationController setNavigationBarHidden:YES animated:YES];
below test code causes EXC_BAD_ACCESS when it's scrolled many time.
would somebody like to tell me the problem?
-myview.h---------------------------------------------------
#interface MyView : UINavigationController < UITableViewDataSource, UITableViewDelegate, UINavigationBarDelegate >
{
UITableView* mTableView;
NSMutableArray* data;
}
-myview.m---------------------------------------------------
#implementation MyView
- (void)viewDidLoad
{
[super viewDidLoad];
int th = self.navigationBar.frame.size.height;
int w = self.view.frame.size.width;
int h = self.view.frame.size.height;
mTableView = [[[UITableView alloc] initWithFrame:
CGRectMake(0.0f, th, w, h - th) style:UITableViewStylePlain] retain];
mTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight;
mTableView.dataSource = self;
mTableView.delegate = self;
[mTableView setRowHeight:55];
[self.view addSubview:mTableView];
data = [[[NSMutableArray alloc] init] retain];
for (int i = 0; i < 150; i++)
{
[data addObject:[NSDictionary
dictionaryWithObjects:[NSArray arrayWithObjects:#"good", [UIColor blackColor], nil]
forKeys:[NSArray arrayWithObjects:#"name", #"color", nil]]];
}
}
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 150;
}
- (UITableViewCell *)tableViewCellWithReuseIdentifier:(NSString *)identifier
{
UITableViewCell *cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier] autorelease];
cell.textLabel.text = #"goooood";
return cell;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
cell = [self tableViewCellWithReuseIdentifier:CellIdentifier];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
- (void)dealloc
{
[super dealloc];
}
- (BOOL)canBecomeFirstResponder
{
return YES;
}
#end
You should have a view controller that contains your table view (it could even be a subclass of UITableViewController). I don't believe that UINavigationController is mean't to be subclassed as you have. If you want a navigation controller you should create an instance of CustomTableViewController, containing your tableview, and then use the initWithRootViewController: method of UINavigationController:
CustomTableViewController * ctvc = [[CustomTableViewController alloc] init];
UINavigationController * navigationController = [[UINavigationController alloc] initWithRootViewController:ctvc];
[ctvc release];
// Superview is where you want this added
// probably your window in app did finish launching
[superview addSubview:navigationController.view];
[navigationController release];
I've been pretty good at fixing my Xcode Obj-C errors, but this one has me stumped:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if ([[tableList objectAtIndex:indexPath.row] isEqual:#"Standard"]) {
[StandardSetupViewController *standard = [[StandardSetupViewController alloc] initWithNibName:#"StandardSetupViewController" bundle:nil]];
// ERROR OCCURS HERE:
// error: expected ':' before '*' token
// confused by earlier errors, bailing out
[standard setTitle:#"Standard"];
[self.navigationController pushViewController:standard animated:YES];
[standard release];
}
}
I picked up the code from a video tutorial on YouTube at http://www.youtube.com/watch?v=9ozwoETFei0, then fixed several errors in the coding by checking it against Beginning iPhone Development and sample Apple source code. I double-checked to make sure the error isn't in the #import page.
I'm posting the coding that occurs earlier in the above snippet in case you think the error occurs further up:
#import "RootViewController.h"
#implementation RootViewController
#synthesize fetchedResultsController, managedObjectContext;
- (void)viewDidLoad {
self.title = #"Setting Up";
tableList = [[NSMutableArray alloc] init];
[tableList addObject:#"Standard"];
[tableList release];
[super viewDidLoad];
// Set up the edit and add buttons.
self.navigationItem.leftBarButtonItem = self.editButtonItem;
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(insertNewObject)];
self.navigationItem.rightBarButtonItem = addButton;
[addButton release];
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
// Handle the error...
}
}
- (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 anything that can be recreated in viewDidLoad or on demand.
// e.g. self.myOutlet = nil;
}
#pragma mark Table view methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [tableList count];
}
// Customize the appearance of table view cells.
- (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] autorelease];
}
// Configure the cell.
cell.textLabel.text = [[tableList objectAtIndex:indexPath.row] retain];
[cell setAccessoryType:UITableViewCellAccessoryDetailDisclosureButton];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if ([[tableList objectAtIndex:indexPath.row] isEqual:#"Standard"]) {
[StandardSetupViewController *standard = [[StandardSetupViewController alloc] initWithNibName:#"StandardSetupViewController" bundle:nil]];
// ERROR OCCURS HERE:
// error: expected ':' before '*' token
// confused by earlier errors, bailing out
[standard setTitle:#"Standard"];
[self.navigationController pushViewController:standard animated:YES];
[standard release];
}
}
Thanks!
Steve (who is "confused" and thinking of "bailing out" too!)
You don't wrap your StandardSetupViewController with brackets, that was the problem.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if ([[tableList objectAtIndex:indexPath.row] isEqual:#"Standard"]) {
StandardSetupViewController *standard = [[StandardSetupViewController alloc] initWithNibName:#"StandardSetupViewController" bundle:nil];
[standard setTitle:#"Standard"];
[self.navigationController pushViewController:standard animated:YES];
[standard release];
}
}
Yours:
[StandardSetupViewController *standard = [[StandardSetupViewController alloc] initWithNibName:#"StandardSetupViewController" bundle:nil]];
Should be:
StandardSetupViewController *standard = [[StandardSetupViewController alloc] initWithNibName:#"StandardSetupViewController" bundle:nil];