I have an action that can take a second or two when a row is selected in a UITableViewCell. I want to give the user feedback when they select the cell that I'm doing something. Currently it just shows the tableviewcell highlight. I added a UIActivityIndicatorView to my view. I have it hidden by default. I try to do this in my didSelectRowAtIndexPath:
{
CustomCell *cell = (CustomCell *)[tableView cellForRowAtIndexPath:indexPath];
cell.activityIndicator.hidden = NO;
[cell.activityIndicator startAnimating];
// do long task
[cell.activityIndicator stopAnimating];
cell.activityIndicator.hidden = YES;
}
This code does not show my activityindicator. If I delete the
activityIndicator.hidden = YES;
in the
setCustomObject:(id)newObject
of my CustomCell class, I do see the indicator. It's just static though. I want to hide it until they click on the cell, animate while long task is running, then stop animating and hide again when long task is over. Any thoughts? Thanks!
Try updating the activity indicator in the main thread
dispatch_async(dispatch_get_main_queue(), ^{
cell.activityIndicator.hidden = NO;
[cell.activityIndicator startAnimating];
});
//do long task
dispatch_async(dispatch_get_main_queue(), ^{
cell.activityIndicator.hidden = YES;
[cell.activityIndicator stopAnimating];
});
in the setCustomObject:(id)newObject method, instead of setting it to hidden, try this:
activityIndicator.hidesWhenStopped = YES;
[acitivtyIndicator stopAnimating];
Then in the didSelectRowAtIndexPath method, remove the code that sets "hidden" or not, and just use [activityIndicator startAnimating] or [activityIndicator stopAnimating] to control both animation and whether it's hidden or not.
Related
I want to add a refresh bar button in my view controller
I put in the viewdidload() this code:
UIBarButtonItem *refreshButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:#selector(refreshTable)];
self.navigationItem.leftBarButtonItem = refreshButton;
and the refreshtable function is :
- (void) refreshTable
{
[self.tableView reloadData];
NSLog(#"table is refreshing ....");
}
this is a screenshot of the refresh button:
but it doesn't work, shall i add something else in the refreshtable function?, the table view doesn't refreshed!, the message "table is refreshing ...." appears everytime i click on the refresh button, but the table doesn't load new data!
when refreshing, can I have this icon somewhere?
If I had two table view in my view controller, and I want when I click on the refresh button, just the first table to be reloaded,
I put the same you code that you suggested, but it doesn't work here! should I do something else?
The issue is you added the data fetching logic in your viewDidLoad (according to your comments).
The viewDidLoad method will be called when the view is loading. It' won't be called unless you dismiss the view and present it again(it won't be calles if you call the reloadData it's just for reloading the UITableView).
When you call the reloadData the data source is still with old data, it is not updated.
So implement your refreshTable method like:
- (void) refreshTable
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://....../fastnews.php"]];
[self performSelectorOnMainThread:#selector(fetchedData:) withObject:data waitUntilDone:YES];
});
}
-(void)fetchedData:(NSData *)responseData
{
NSError *error;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
fastResults = [json objectForKey:#"nodes"];
[self.tableView reloadData];
}
try this .
[self.tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:[tableView indexPathsForVisibleRows]
withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
You need to update your dataSourceAaaay in refreshTable method before reload table.
I want to build object and then open a controller with it. Building can take up to 5 seconds and I want to show a message while it processing.
I have the following implementation of didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
messageView.hidden = NO;
// Some methods
Controller *ctrl = [Controller new];
[self.navigationController pushViewController:ctrl animated:YES];
}
Everything is good but there is a problem: messageView appears ONLY when push animation starts. What can I do to fix that?
Similar to Jonathan's answer, delay the push a little to give the messageView time to appear.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
messageView.hidden = NO;
int64_t oneMillisecond = NSEC_PER_MSEC;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, oneMillisecond), dispatch_get_main_queue(), ^(void){
// Some methods
Controller *ctrl = [Controller new];
[self.navigationController pushViewController:ctrl animated:YES];
});
}
It's not displaying because you're blocking the main thread while building the object.
The user interface will not update until you return control to the run loop.
The solution is to build your object on a background thread, the easiest way of doing this is with libdispatch, like so:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
messageView.hidden = NO;
// you may want to disable user interaction while background operations happen
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// Perform your lengthy operations here
Controller *ctrl = [[Controller alloc] init];
dispatch_async(dispatch_get_main_queue(), ^{
[self.navigationController pushViewController:ctrl animated:YES];
}
});
}
If you want a UIAlertView you can use this code:
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Title..." message:#"More?" delegate:nil cancelButtonTitle:nil otherButtonTitles:nil];
[alert show];
After your done you can call this to dismiss:
[alert dismissWithClickedButtonIndex:0 animated:YES];
Can you try with this:
[UIView animateWithDuration:0.5f delay:0.0f options:UIViewAnimationCurveLinear animations:^(void)
{
messageView.hidden = NO;
}
completion:^(BOOL finished)
{
Controller *ctrl = [Controller new];
[self.navigationController pushViewController:ctrl animated:YES];
}];
The view probably isn't redrawn during your didSelectRowAtIndexPath call.
So... I would try running the long running method in a block. Then block the main thread with you messageView animation and have your block post a notification or something to shut it down.
You might want to have some kind of condition for the messageView to shut itself down after a certain time.
I initialize an activity indicator and in a button press action I start it animating and call the next view to display.
-(IBAction) downloadButtonPressed:(id)sender {
NSLog(#"Download Button Pressed");
indicator.hidden = NO;
[indicator startAnimating];
if (addviewcontroller == nil)
addviewcontroller = [[AddViewController alloc]init];
[self.view addSubview:addviewcontroller.view];
[addviewcontroller setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[self presentModalViewController:addviewcontroller animated:YES];
}
When I press the button, the activity indicator doesn't start immediately. It starts when the other view is called. The indicator is displayed for a second, but when the button is pressed it takes some time to load the other view.
I dont know why the indicator shows for a second without starting.
Try this :
-(IBAction) downloadButtonPressed:(id)sender
{
NSLog(#"Download Button Pressed");
indicator.hidden = NO;
[indicator startAnimating];
[self performSelector:#selector(showController) withObject:nil afterDelay:0.1f];
}
- (void)showController {
if (addviewcontroller == nil)
addviewcontroller = [[AddViewController alloc]init];
[self.view addSubview:addviewcontroller.view];
[addviewcontroller setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[self presentModalViewController:addviewcontroller animated:YES];
}
That should do the trick ;-)
EDIT
I just noticed that there is a problem in your code, you are adding your addviewcontroller twice. One by adding it as a subview of the actual view controller, and one by modally presenting another view controller. You should remove one of the statements from this function.
I'm adding 'pull to load more' to a UITableView. I've got the reloading working and I can add more data, but I'm trying to display a UIActivityIndicatorView when the data loads. I've managed to get the current accessory to disappear, but I can't make the actitity indicator draw itself.
This is the code I have in my UITableViewCell subclass:
-(void) toggleLoading:(bool)showLoading {
if(showLoading) {
[self.accessoryView setHidden:true];
isLoading = false;
} else {
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
spinner.frame = CGRectMake(0, 0, 24, 24);
self.accessoryView = spinner;
[spinner startAnimating];
[spinner release];
isLoading = true;
[self.backgroundView setNeedsDisplay];
}
}
The method is called and when the data finishes loading, the correct accessory is shown again. Is there an extra step I need?
Shouldn't you be drawing the spinner in didSelectRowAtIndexPath?
btw I would use:
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
Turned out I was trying to call this from a separate thread (using GCD), hence the lag in updating the UI. The solution was to move the UI calls onto the UI thread.
I have an App using UITableViews and fetching data from a server. I am attempting to put a UIActivityIndicatorView on the Parent UITableView, so it spins while the Child UITableView is loading. I have the UIActivityIndicatorView all hookedup through Interface Builder, etc.
-(void)spinTheSpinner {
NSLog(#"Spin The Spinner");
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[spinner startAnimating];
[NSThread sleepForTimeInterval:9];
[self performSelectorOnMainThread:#selector(doneSpinning) withObject:nil waitUntilDone:NO];
[pool release];
}
-(void)doneSpinning {
NSLog(#"done spinning");
[spinner stopAnimating];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[NSThread detachNewThreadSelector:#selector(spinTheSpinner) toTarget:self withObject:nil];
NSMutableString *tempText = [[NSMutableString alloc] initWithString:[categoryNumber objectAtIndex:indexPath.row]];
Threads *viewController = [[Threads alloc] initWithNibName:#"newTable" bundle:nil tagval:tempText SessionID:PHPSESSID];
[self.navigationController pushViewController:viewController animated:YES];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[viewController release];
}
So, when I push the Child UITableView onto the screen, the UIActivityIndicatorView (spinner) just sits there on the Parent UITableView. If I go to the Child UITableView and then quickly go back to the Parent View, I can catch the UIActivitIndicatorView in the act of spinning. The NSLogs are showing at the correct times - when I first tap the cell in the Parent UITableView, the "Spin The Spinner" appears in the Log. 9 seconds later, I get "done spinning" in the Log, but the spinner never spins unless I pop back to the Parent UITableView in time.
Ideas of why this is not working properly?
Perhaps the sleepForTimeInterval is blocking the animation.
Try removing the sleepForTimeInterval and replace performSelectorOnMainThread with a call to performSelector:withObject:afterDelay:.
let the ui display it outside thread:
[NSObject dispatchUI:^{
[spinner startAnimating];
}];