I have the following error. 'indexPath' undeclared (first use in this function).
Code.
didSelectRowAtIndexPath
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleWhiteLarge];
cell.accessoryView = spinner;
[spinner startAnimating];
[spinner release];
[self performSelector:#selector(pushDetailView:) withObject:tableView afterDelay:0.1];
}
pushDetailView
- (void)pushDetailView:(UITableView *)tableView {
// Push the detail view here
[tableView deselectRowAtIndexPath:indexPath animated:YES];
//load the clicked cell.
DetailsImageCell *cell = (DetailsImageCell *)[tableView cellForRowAtIndexPath:indexPath];
//init the controller.
AlertsDetailsView *controller = nil;
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad){
controller = [[AlertsDetailsView alloc] initWithNibName:#"DetailsView_iPad" bundle:nil];
} else {
controller = [[AlertsDetailsView alloc] initWithNibName:#"DetailsView" bundle:nil];
}
//set the ID and call JSON in the controller.
[controller setID:[cell getID]];
//show the view.
[self.navigationController pushViewController:controller animated:YES];
}
I think it's because I'm not parsing the indexPath value from didSelectRowAtIndexPath to pushDetailView but I don't know how to approach this.
Could someone please advise?
Thanks.
The problem is that you pushDetailView: method has no indexPath variable on it's scope.
Instead of
- (void)pushDetailView:(UITableView *)tableView {
You should make your method like this:
- (void)pushDetailView:(UITableView *)tableView andIndexPath: (NSIndexPath*) indexPath {
and then indexPath would be declared on the method scope.
Then, inside your didSelectRowAtIndexPath method, replace
[self performSelector:#selector(pushDetailView:) withObject:tableView afterDelay:0.1];
for the code bellow:
double delayInSeconds = 0.1;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self pushDetailView: tableView andIndexPath: indexPath];
});
This uses GCD to execute the code after a delay, instead of the performSelector: withObject :afterDelay: and here is a nice post explaining why sometimes is better to opt for this aproach
Since you need to provide two arguments and perform after a delay package both arguments in an NSDictionary and pass it:
NSDictionary *arguments = [NSDictionary dictionaryWithObjectsAndKeys:
tableView, #"tableView", indexPath, #"indexPath", nil];
[self performSelector:#selector(pushDetailView:) withObject:arguments afterDelay:0.1];
...
- (void)pushDetailView:(NSDictionary *)arguments {
UITableView *tableView = [arguments objectForKey:#"tableView"];
NSIndexPath *indexPath = [arguments objectForKey:#"indexPath"];
...
Or as #Felipe suggests, use GCD.
Related
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
I can successfully push only next view in my iPhone app. However, cause the next view retrieves data to populate the UITableViews, sometimes waiting time could be a few seconds or slightly longer depending on connection.
During this time, the user may think the app has frozen etc. So, to counter this problem, I think implementing UIActivityIndicators would be a good way to let the user know that the app is working.
Can someone tell me where I could implement this?
Thanks.
pushDetailView Method
- (void)pushDetailView {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
//load the clicked cell.
DetailsImageCell *cell = (DetailsImageCell *)[tableView cellForRowAtIndexPath:indexPath];
//init the controller.
AlertsDetailsView *controller = nil;
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad){
controller = [[AlertsDetailsView alloc] initWithNibName:#"DetailsView_iPad" bundle:nil];
} else {
controller = [[AlertsDetailsView alloc] initWithNibName:#"DetailsView" bundle:nil];
}
//set the ID and call JSON in the controller.
[controller setID:[cell getID]];
//show the view.
[self.navigationController pushViewController:controller animated:YES];
You can implement this in didSelectRowAtIndexPath: method itself.
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleWhiteLarge];
cell.accessoryView = spinner;
[spinner startAnimating];
[spinner release];
This will show the activity indicator at the right side of the cell which will make the user feel that something is loading.
Edit: If you set the accessoryView and write the loading code in the same method, the UI will get updated only when all the operations over. A workaround for this is to just set the activityIndicator in the didSelectRowAtIndexPath: method and call the view controller pushing code with some delay.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleWhiteLarge];
cell.accessoryView = spinner;
[spinner startAnimating];
[spinner release];
[self performSelector:#selector(pushDetailView:) withObject:tableView afterDelay:0.1];
}
- (void)pushDetailView:(UITableView *)tableView {
// Push the detail view here
}
How do make the individual cells go to different View Controller. Right now, using the method im using, if i click the first cell, it takes me to a view controller where i have a pic. now if i go back and click the second cell, it takes me to the same view controller. How do i change this so that all the other individual cells go to individual view controller? what do i add or what do i configure?
here my code:
// 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...
NSUInteger row = [indexPath row];
cell.textLabel.text = [breakdownArray objectAtIndex:row];
bookDetailViewController.title = [NSString stringWithFormat:#"%#" , [breakdownArray objectAtIndex:row]];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic may go here. Create and push another view controller.
/*
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:#"<#Nib name#>" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
*/
NSInteger row = [indexPath row];
if (self.bookDetailViewController == nil); {
BookDetailViewController *aBookDetail = [[BookDetailViewController alloc] initWithNibName:#"BookDetailView" bundle:nil];
self.bookDetailViewController = aBookDetail;
[aBookDetail release];
}
bookDetailViewController.title = [NSString stringWithFormat:#"%#" , [breakdownArray objectAtIndex:row]];
Books2010AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
[delegate.breakdownNavController pushViewController:bookDetailViewController animated:YES];
}
One fairly easy way is in your tableView:didSelectRowAtIndexPath: method. You can check the index path parameter and execute different code paths. Something like:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath )indexPath {
switch(indexPath.row) {
case 1:
[self.navigationController pushViewController:someViewController animated:YES];
break;
case 2:
[self.navigationController pushViewController:someOtherViewController animated:YES];
break;
}
}
Like most design choices, this is just an example of one possible approach. That's the best I can do in this general case.
I would create a new view controller each time instead of using the same one and just changing its properties, more like the example that you have commented out in tableView:didSelectRowAtIndexPath:. This will only work if you don't access your controller from anywhere other than these methods.
First: Remove bookDetailViewController.title = [NSString stringWithFormat:#"%#" , [breakdownArray objectAtIndex:row]]; from your tableView:cellForRowAtIndexPath: method (you should do this anyway, since it is just taking more processor time)
Second: Remove the bookDetailController property from your class. You won't need it anymore.
Third: Implement tableView:didSelectRowAtIndexPath: as follows:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)path {
BookDetailViewController *bookDetailViewController = [[BookDetailViewController alloc] initWithNibName:#"BookDetailView" bundle:nil];
NSInteger row = path.row;
bookDetailViewController.title = [NSString stringWithFormat:#"%#" , [breakdownArray objectAtIndex:row]];
//Any other setup on view controller
Books2010AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
[delegate.breakdownNavController pushViewController:bookDetailViewController animated:YES];
[bookDetailViewController release];
}
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];
I am new to iPhone development, so please pardon my ignorance.
I call a UIViewController from a UIViewController. The first UIViewController is a list of items, and the second is the detail for each of the items.
ListViewController (first UIViewController):
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// initialize a AddViewController
selItem = indexPath.row;
selIndexPath = indexPath;
AddViewController *controller = [[AddViewController alloc] init];
// give controller the data to display
// show the AddViewController
[controller setData:[list objectAtIndex:indexPath.row]];
controller.delegate = self;
[self presentModalViewController:controller animated:YES];
[controller release]; // release the controller AddViewController
} // end method tableView:didSelectRowAtIndexPath:
In the 'AddViewController' I have a segment control as both the left and right button bars. On the left, I have 'Exit' and 'Delete'. On the right I have 'Add' and 'Save'. The outlets that the segment controls are linked to are:
AddViewController (second UIViewController):
- (IBAction)delExitSegment:sender
{
// The segmented control was clicked, handle it here
UISegmentedControl *segmentedControl = (UISegmentedControl *)sender;
switch (segmentedControl.selectedSegmentIndex) {
case 0: // Exit Button
[delegate addViewControllerDidExit:self];
break;
case 1: // Delete Button
[delegate addViewControllerDelItem:self];
break;
}
}
- (IBAction)segmentAction:sender
{
// The segmented control was clicked, handle it here
UISegmentedControl *segCtl = (UISegmentedControl *)sender;
switch (segCtl.selectedSegmentIndex) {
case 0: // Add Button
if (currentCell != nil)
[data setValue:currentCell.textField.text forKey:currentCell.label.text];
[delegate addViewControllerDidFinishAdding:self];
break;
case 1: // Save Button
[delegate addViewControllerUpdate:self];
break;
}
}
When I come back to my first view controller, I have:
ListViewController:
- (void)addViewControllerDidFinishAdding:(AddViewController *)controller
{
NSDictionary *item = [controller values];
if (item != nil)
{
[list addObject:item];
}
[self dismissModalViewControllerAnimated:YES];
[list writeToFile:itemFilePath atomically:NO];
[self calcTotal];
[self.tableView reloadData];
}
- (void)addViewControllerUpdate:(AddViewController *)controller
{
NSDictionary *item = [controller values];
if (item != nil)
{
[list replaceObjectAtIndex:selItem withObject:item];
}
[self dismissModalViewControllerAnimated:YES];
[list writeToFile:itemFilePath atomically:NO];
[self calcTotal];
[self.tableView reloadData];
}
- (void)addViewControllerDidExit:(AddViewController *)controller
{
[self dismissModalViewControllerAnimated:YES];
[self.tableView reloadData];
}
- (void)addViewControllerDelItem:(AddViewController *)controller
{
NSDictionary *item = [controller values];
if (item != nil)
{
[list removeObjectAtIndex:selItem];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:selIndexPath] withRowAnimation:UITableViewRowAnimationFade];
[list writeToFile:itemFilePath atomically:NO];
}
[self dismissModalViewControllerAnimated:YES];
[self.tableView reloadData];
}
If I exit or delete an item from AddViewController, I have no problems. When I try and add or save, then my application will crash. The debugger crashes at dismissModalViewControllerAnimated. I can't see what's different between the two segment controls.
Any ideas what I may have wrong?
I just discovered that neither the left or right buttons work when I attempt to edit a cell. The editing cell in AddViewController is as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = #"EditableCell";
EditableCell *cell = (EditableCell *)[table dequeueReusableCellWithIdentifier:identifier];
if (cell == nil)
{
cell = [[EditableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
}
NSString *key = [fields objectAtIndex:indexPath.row + indexPath.section * 3];
[cell setLabelText:key];
cell.textField.text = [data valueForKey:key];
if (indexPath.section == 0 && (indexPath.row == 1 || indexPath.row == 3))
cell.textField.keyboardType = UIKeyboardTypeNumberPad;
if (indexPath.section == 1)
cell.textField.keyboardType = UIKeyboardTypeNumberPad;
cell.editing = NO;
cell.delegate = self;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
Here is the problem:
[self dismissModalViewControllerAnimated:YES];
[self.tableView reloadData];
When you dismiss the view the value of self become unknown, accessing the tableview will definitely crash.
If you want to refresh the tableView just use [self.tableView reloadData] and don't dismiss the view.