I have a UIRefreshControl, and it works fine when you use it, but it hangs, it never goes away. I've tried
[self.refreshControl endRefreshing];
This solution was from a similar question, but it has not addressed mine.
Even with this, the refreshControl continues spinning, not dismissing. Why is this?
In my viewDidLoad:
[NSThread detachNewThreadSelector:#selector(refreshFeed) toTarget:self withObject:nil];
refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self
action:#selector(refreshFeed)
forControlEvents:UIControlEventValueChanged];
[self.tableView addSubview:refreshControl];
refreshFeed method...
-(void)refreshFeed
//load those feeds
{
RSSLoader* rss = [[RSSLoader alloc] init];
[rss fetchRssWithURL:feedURL
complete:^(NSString *title, NSArray *results) {
dispatch_queue_t downloadQueue = dispatch_queue_create("downloader",NULL);
dispatch_async(downloadQueue, ^{
_objects = results;
//completed fetching the RSS
dispatch_async(dispatch_get_main_queue(), ^{
[self.refreshControl endRefreshing];
[self.tableView reloadData];
});
});
}];
}
Can you try changing this line
[self.tableView addSubview:refreshControl];
with
[self setRefreshControl:refreshControl];
Related
I have reviewed many posts regarding this issue with no success. I have two table view controllers, a source and a destination, both in a navigation controller. When a user taps a cell in the source table view controller, an object is instantiated which makes a web service call that could take several seconds depending on network speed. Once this web service call is made, the segue is executed and navigation moves from the source to the destination. I want an activity indicator to show when this web service call is made. Nothing I have tried for the past several days has worked, even posts marked as successful on this forum. So, there must be something... Here is my code thus far:
Source table view controller header:
#property (strong, nonatomic) UIActivityIndicatorView *activityIndicator;
Source table view controller implementation:
#synthesize activityIndicator;
-(void)loadView {
[super loadView];
self.activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[self.view addSubview:self.activityIndicator];
self.activityIndicator.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2);
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
NSString *stateAbbr;
if([segue.identifier isEqualToString:#"sgShowStateDetail"]){
DetailTableViewController *detailVC = [segue destinationViewController];
NSIndexPath *path = [self.tableView indexPathForSelectedRow];
NSArray *tempArray = (NSArray*)[groupedStates objectAtIndex:path.section];
NSString *key = [tempArray objectAtIndex:path.row];
stateAbbr = [statesDict objectForKey:key];
[detailVC setStateIdentifier:stateAbbr];
// begin activity indicator here
[self.view bringSubviewToFront:activityIndicator];
[activityIndicator startAnimating];
gauges = [[GaugeList alloc] initWithStateIdentifier:stateAbbr andType:nil];
[activityIndicator stopAnimating];
[detailVC setStateGauges:gauges];
// end activity indicator here
}
}
GaugeList is the object that performs the web service call.
Everything works as expected, except there is never an activity indicator. There are no errors. What am I doing wrong? Thanks!
I think you might be using the wrong pattern. The call to the web service has to happen in the background, while the activity indicator has to be shown and hidden on the main thread. Thus
__block NSArray *gauges;
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
gauges = [[GaugeList alloc] initWithStateIdentifier:stateAbbr andType:nil];
dispatch_async(dispatch_get_main_queue(), ^{
[activityIndicator stopAnimating];
[detailVC setStateGauges:gauges];
};
};
Here is the code using the method tableView: didSelectRowAtIndexPath: rather than prepareForSegue:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.view bringSubviewToFront:activityIndicator];
[activityIndicator startAnimating];
dispatch_async(dispatch_queue_create("", nil), ^ {
NSString *stateAbbr;
DetailTableViewController *detailVC = [self.storyboard instantiateViewControllerWithIdentifier:#"DetailTableViewController"];
NSArray *tempArray = (NSArray*)[groupedStates objectAtIndex:indexPath.section];
NSString *key = [tempArray objectAtIndex:indexPath.row];
stateAbbr = [statesDict objectForKey:key];
[detailVC setStateIdentifier:stateAbbr];
gauges = [[GaugeList alloc] initWithStateIdentifier:stateAbbr andType:nil];
[detailVC setStateGauges:gauges];
dispatch_async(dispatch_get_main_queue(), ^{
[activityIndicator stopAnimating];
[self.navigationController pushViewController:detailVC animated:YES];
});
});
}
I can't refresh my TVC. It crashes when I drag down to refresh. The icon is there, but then it quits. I'm sure its something simple. There are similar questions out there which have not been considered.
Taken from my viewDidLoad method body:
refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self
action:#selector(refreshInvoked:forState:)
forControlEvents:UIControlEventValueChanged];
[self.tableView addSubview:refreshControl];
refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:title
attributes:#{NSFontAttributeName:[UIFont fontWithName:#"Helvetica" size:13.0]}];
[self refreshFeed];
Which refers to:
-(void)refreshFeed
{
RSSLoader* rss = [[RSSLoader alloc] init];
[rss fetchRssWithURL:feedURL
complete:^(NSString *title, NSArray *results) {
dispatch_queue_t downloadQueue = dispatch_queue_create("downloader",NULL);
dispatch_async(downloadQueue, ^{
_objects = results;
[self.tableView reloadData];
//completed fetching the RSS
dispatch_async(dispatch_get_main_queue(), ^{
// [(HeaderView*)self.tableView.tableHeaderView setText:title];
// [(ArticleItem*)self.tableView.]
});
});
}];
}
UIRefreshControl is not meant to be added as subview... doing so will get you some trouble and you need to unregister its target on VC dealloc... else you might get some issues, when the UIRefreshControl calls your dead VC (as it doesn't keep a weak or strong reference to your VC)
Change your action method to:
[refreshControl addTarget:self
action:#selector(refreshFeed)
forControlEvents:UIControlEventValueChanged];
Looks like you were pointing to refreshInvoked:forState: which was not present in self.
In here said that using MBProgressHUD is easy.
But I can't make it.
Here my code:
- (IBAction)save{
HUD = [[MBProgressHUD alloc] initWithView:self.navigationController.view];
[self.navigationController.view addSubview:HUD];
HUD.delegate = self;
[HUD show:YES];
NSString *title = [page stringByEvaluatingJavaScriptFromString:#"document.title"];
SavePageAs *savePage = [[SavePageAs alloc] initWithUrl:self.site directory:title];
[savePage save];
[HUD hide:YES];
}
The progress indicator is not showing during savePage save method run, but shows after the page completely saved (the indicator is shown for less than 1 second).
I also tried this way:
- (IBAction)save {
HUD = [[MBProgressHUD alloc] initWithView:self.navigationController.view];
[self.navigationController.view addSubview:HUD];
HUD.delegate = self;
HUD.labelText = #"Saving...";
[HUD showWhileExecuting:#selector(performFetchOnMainThread) onTarget:self withObject:nil animated:YES];
}
- (void) savingPage{
NSString *title = [page stringByEvaluatingJavaScriptFromString:#"document.title"];
SavePageAs *savePage = [[SavePageAs alloc] initWithUrl:self.site directory:title];
[savePage save];
}
-(void) performFetchOnMainThread {
[self performSelectorOnMainThread:#selector(savingPage) withObject:nil waitUntilDone:YES];
}
But still no luck. Anyone let me know where I'm lack here?
P.S: savePage save is saving all website contents to local directory. I wish once the saving is complete the progressHUD disappear.
Thanks
Try allocation of HUD on the viewWillAppear: instead of -(IBAction)save because sometimes allocation takes up the whole time and by the time it allocates whole task is finished.
Copy Following links to viewWillAppear: and remove them from -(IBAction)save
HUD = [[MBProgressHUD alloc] initWithView:self.navigationController.view];
[self.navigationController.view addSubview:HUD];
HUD.delegate = self;
HUD.labelText = #"Saving...";
EDIT: Keep allocations on viewWillAppear: and change code as shown below:
- (IBAction)save {
[NSThread detachNewThreadSelector:#selector(showHUD) withObject:nil];
[self performSelectorOnMainThread:#selector(savingPage) withObject:nil waitUntilDone:YES];
}
- (void) savingPage{
NSString *title = [page stringByEvaluatingJavaScriptFromString:#"document.title"];
SavePageAs *savePage = [[SavePageAs alloc] initWithUrl:self.site directory:title];
[savePage save];
[HUD hide:YES];
}
-(void)showHUD {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[HUD show:YES];
[pool release];
}
What this will do is create a separate thread to display HUD as main thread is being engaged by savingPage method.
If this too doesn't work, then just change waitUntilDone:YES to waitUntilDone:NO
Note: As per Apple documentation
Important If you use Automatic Reference Counting (ARC), you cannot
use autorelease pools directly. Instead, you use #autoreleasepool
blocks instead. For example, in place of:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init;
// Code benefitting from a local autorelease pool.
[pool release];
you would write:
#autoreleasepool {
// Code benefitting from a local autorelease pool.
}
#autoreleasepool blocks are more efficient than using an instance of
NSAutoreleasePool directly; you can also use them even if you do not
use ARC.
Hope this helps.
check out this code
- (IBAction)save{
HUD = [[MBProgressHUD alloc] initWithView:self.view];
HUD.graceTime = .1;
HUD.navigationBar = self.navigationController.navigationBar;
HUD.labelText = #"Saving";
HUD.delegate = self;
[self.view addSubview:HUD];
[HUD showWhileExecuting:#selector(savingPage) onTarget:self withObject:nil animated:YES];
}
- (void) savingPage{
NSString *title = [page stringByEvaluatingJavaScriptFromString:#"document.title"];
SavePageAs *savePage = [[SavePageAs alloc] initWithUrl:self.site directory:title];
[savePage save];
}
when saveingpage method will be completed MBProgressHUD delegate will be called so implement this delegate method in your class,This will remove HUD from your view
-(void)hudWasHidden
{
[HUD removeFromSuperview];
}
Try changing the HUD allocation lines to
HUD = [MBProgressHUD showHUDAddedTo: self.navigationcontroller.view animated:YES];
HUD.labelText....
And then in your saving method you can have
[MBProgressHUD hideHUDForView:self.navigationcontroller.view animated:NO];
I have a refresh button on my navigationbar
buttonItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:#selector(buttonItemClicked)];
self.navigationItem.rightBarButtonItem = buttonItem;
-(void)buttonItemClicked{
NSLog(#"buttonItemclicked");
myView.labelName.text = nil;
myView.otherLabelName.text = nil;
[spinner startAnimating]
[spinnerView setHidden:NO];
[self requestAPI];
[spinner stopAnimating];
[spinnerView setHidden:YES];
}
If I go in and out of the view, it works fine. But when I call the same methods in buttonItemClicked, it doesn´t work. I also tried calling the view methods inside my action method, but that doesn´t work either.
What I´m trying to do is set my labels to nil, add my UIActivityIndicatorView and remove it after the labels are set again.
I have already tried [self.view setNeedsDisplay];
The refresh it self works, but the animations doesn´t work.
Any suggestions?
The animation is not working because you call startAnimating and stopAnimating (and setHidden) in the same method.
The render start at the end of the method call.
You need to set
[spinner stopAnimating];
[spinnerView setHidden:YES];
in requestAPI.
Edit:
Using Grand Central Dispatch. Like:
- (void)buttonItemClicked {
myView.labelName.text = nil;
myView.otherLabelName.text = nil;
[spinner startAnimating]
[spinnerView setHidden:NO];
[self requestAPI];
}
- (void)requestAPI {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSURL *url = [NSURL URLWithString:#"http://example.com"];
NSData *data = [NSData dataWithContentsOfURL:url];
NSString *stringResult = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
dispatch_async(dispatch_get_main_queue(), ^{
[spinner stopAnimating];
[spinnerView setHidden:YES];
myView.labelName.text = stringResult;
});
});
}
Try [myView setsNeedToDisplay];.
I have a refresh button on my navigationbar
buttonItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:#selector(buttonItemClicked)];
self.navigationItem.rightBarButtonItem = buttonItem;
-(void)buttonItemClicked{
NSLog(#"buttonItemclicked");
myView.labelName.text = nil;
myView.otherLabelName.text = nil;
[spinner startAnimating]
[spinnerView setHidden:NO];
[self requestAPI];
[spinner stopAnimating];
[spinnerView setHidden:YES];
}
If I go in and out of the view, it works fine. But when I call the same methods in buttonItemClicked, it doesn´t work. I also tried calling the view methods inside my action method, but that doesn´t work either.
What I´m trying to do is set my labels to nil, add my UIActivityIndicatorView and remove it after the labels are set again.
I have already tried [self.view setNeedsDisplay];
The refresh it self works, but the animations doesn´t work.
Any suggestions?
The animation is not working because you call startAnimating and stopAnimating (and setHidden) in the same method.
The render start at the end of the method call.
You need to set
[spinner stopAnimating];
[spinnerView setHidden:YES];
in requestAPI.
Edit:
Using Grand Central Dispatch. Like:
- (void)buttonItemClicked {
myView.labelName.text = nil;
myView.otherLabelName.text = nil;
[spinner startAnimating]
[spinnerView setHidden:NO];
[self requestAPI];
}
- (void)requestAPI {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSURL *url = [NSURL URLWithString:#"http://example.com"];
NSData *data = [NSData dataWithContentsOfURL:url];
NSString *stringResult = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
dispatch_async(dispatch_get_main_queue(), ^{
[spinner stopAnimating];
[spinnerView setHidden:YES];
myView.labelName.text = stringResult;
});
});
}
Try [myView setsNeedToDisplay];.