IOS/Objective-C: Methods to start and end uiactivityindicatorview - ios

I am trying to create methods that I can reuse to start and stop spinning wheels during different lengthy activities such as syncing a table with a server.
My start method:
-(void) startSpinner {
UIActivityIndicatorView *activityIndicator;
activityIndicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
activityIndicator.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
activityIndicator.center = self.view.center;
[self.view addSubview: activityIndicator];
[activityIndicator startAnimating];
}
and end method
-(void)endSpinner:(UIActivityIndicatorView *) spinner forTable:(UITableView *)tableView {
[tableView reloadData];
[spinner stopAnimating];
tableView.userInteractionEnabled = YES;
}
The problem I'm running into is the end method does not recognize the uiactivityidicator created in the start method.
Should I be saving this in a property? Or how can I grab the spinner from a different method in order to save it.
I'd like to set this up in reusable code as I have many tableviews in different view controllers where I would like to include this code. The tableviews already have properties but do I have to set up a uiactivityindicator property in every view controller where I want to include a spinner?
Thanks for any insights.

Declare a property in AppDelegate:
#property (strong,nonatomic) MyActivityIndicator *activity;
and initialise it:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
_activity=[[MyActivityIndicator alloc]init];
}
and then declare the following methods in appDelegate:
- (void)showActivity
{
dispatch_async(dispatch_get_main_queue(), ^{
[_window addSubview:_activity];
[_activity startAnimating];
});
}
- (void)hideActivity
{
dispatch_async(dispatch_get_main_queue(), ^{
//also remove activity from window
[_activity stopAnimating];
});
}
you can call these two methods from any class:
[(AppDelegate*)[UIApplication sharedApplication].delegate showActivity];

Related

Disabling selection in the background when activity indicator is present

I am adding activity indicator on top of the view and wish to disable the selections in the background when the activity indicator is on. Also for some reason, my activity indicator is still spins for about 30-45 seconds(depending on the network speed) after the data is displayed on the table view. I have created a category for activity indicator.
Activity Indicator category code:
- (UIView *)overlayView {
return objc_getAssociatedObject(self, OverlayViewKey);
}
- (void)setOverlayView:(UIView *)overlayView {
objc_setAssociatedObject(self, OverlayViewKey, overlayView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)showActivityIndicatorForView:(UIView *)view {
self.overlayView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];
self.center = self.overlayView.center;
[view setUserInteractionEnabled:NO];
[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
[self.overlayView setUserInteractionEnabled:NO];
[self startAnimating];
[self.overlayView addSubview:self];
[view addSubview:self.overlayView];
[view bringSubviewToFront:self.overlayView];
self.hidesWhenStopped = YES;
self.hidden = NO;
}
- (void)hideActivityIndicatorForView:(UIView *)view {
[self stopAnimating];
[self.overlayView setUserInteractionEnabled:YES];
[self.overlayView removeFromSuperview];
[[UIApplication sharedApplication] endIgnoringInteractionEvents];
[view setUserInteractionEnabled:YES];
}
Usages in table view controller:
#interface MyTableViewController()
#property (nonatomic, strong) UIActivityIndicatorView *activityIndicator;
#end
#implementation MyTableViewController
- (id) initWithSomething:(NSString *)something {
self = [super init];
if (self) {
self.activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
self.activityIndicator.overlayView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self getDataServiceRequest];
[self.activityIndicator showActivityIndicatorForView:self.navigationController.view];
}
- (void)requestCompletionCallBack sender:(ServiceAPI *)sender {
// Do something here with the data
[self.activityIndicator hideActivityIndicatorForView:self.navigationController.view];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
#end
What am I doing wrong here? Why am I still able to select the data in the background when the activity indicator is on and even after disabling the user interaction.
Move your call to hideActivityIndicatorForView to inside the call to dispatch_async(dispatch_get_main_queue(). It's a UI call, and needs to be done on the main thread.
As for how to disable other actions on your view controller, you have a few options. One simple thing I've done is the put the activity indicator inside a view that's pinned to the whole screen, set to opaque=false, and with a color that's black with an alpha setting of 0.5. That way the content underneath is visible but the user can't click on it. You need to add an outlet to your "coveringView" and show-hide it instead of showing/hiding the activity indicator view.
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
fix it
[self performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];

Continue loading view after method loads from external URL

I have a method to retrieve data from an external url, load it into an array from JSON format, and populate a UITableView. It works fine, but there is no indication to the user that something is happening while the data is downloaded.
- (void)viewDidLoad
{
[super viewDidLoad];
[self retrieveDataC];
}
Here is the code that I tried for viewDidLoad which adds a spinner animation while downloading. I'm attempting to put retrieveDataC on a background thread and when it completes, I would like the view to continue executing as though I didn't implement the multi-threading in the example above.
- (void)viewDidLoad
{
[super viewDidLoad];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
spinner.center = CGPointMake(160, 240);
[self.view addSubview:spinner];
[spinner startAnimating];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self retrieveDataC];
dispatch_async(dispatch_get_main_queue(), ^{
[spinner stopAnimating];
});
});
}
The loading spinner displays correctly for a brief moment, however after the process is done I'm left with a blank table as though I have not called [self retrieveDataC] to begin with. Any suggestions, advice? Am I setting up the background process correctly?
Thank you
EDIT:
Here's what ended up working -
- (void)viewDidLoad
{
[super viewDidLoad];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
spinner.center = CGPointMake(160, 240);
[self.view addSubview:spinner];
[spinner startAnimating];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self retrieveDataC];
dispatch_async(dispatch_get_main_queue(), ^{
[spinner stopAnimating];
[self.collectionView reloadData];
});
});
}
Do you call [self.tableView reloadData] after you received the data?

Display activity indicator only after clicking the button

hi I’m trying view the activity indicator by clicking the button. I have tried but i having some issues. If i drag the activity indicator to my storyboard but its visible while running my app i want to display while clicking the button. in my app I’m giving the photo upload option. So if they click the upload button i want to view the activity indicator . ..
this is code i have tired but its not working.
this is my h file where i have implemented the activity indicator and the button..
- (IBAction)click:(id)sender;
#property (strong, nonatomic) IBOutlet UIActivityIndicatorView *spinner;
this is code which i have used in my m file...
-(void)temp
{
spinner = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
spinner.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
[spinner setCenter:CGPointMake(480/2.0, 128.0/2)]; // (mid of screen) I do this because I'm in landscape mode
[self.view addSubview:spinner];
[spinner startAnimating];
[spinner release];
}
and im calling this function in my button..
- (IBAction)click:(id)sender {
[self temp];
}
and also im have some other issues i saw the code here the problem is
[yourView addSubview:spinner];
in yourview i give my uiviewcontroller name but its giving error i have changed to
[self.view addSubview:spinner];
pls tell me where im doing wrong and what is the right way to make it.. thanks
Man I got the problem...I think you are adding white activity indicator on white view that's why it is not being display but it is there for sure so...use some other style UIActivityIndicatorViewStyleGray
-(void)temp
{
spinner = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
spinner.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
[spinner setCenter:CGPointMake(480/2.0, 128.0/2)]; // (mid of screen) I do this because I'm in landscape mode
[self.view addSubview:spinner];
[spinner startAnimating];
[spinner release];
}
First of all since you already have an IBOutlet for the spinner so do not allocate a new one in temp method. Modify your temp method to look like this:
-(void)temp
{
spinner.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
[spinner setCenter:CGPointMake(480/2.0, 128.0/2)]; // (mid of screen) I do this because I'm in landscape mode
[self.view addSubview:spinner];
[spinner startAnimating];
}
I f you are using interface builder like Storyboard or XIB then just set the frame and evrything there itself. And if you want to change the property of it at runtime just use the IBOutlet you have used.
You need not to instantiate again just use its getter and setter to use it accordingly. So here you can do.
- (IBAction)click:(id)sender
{
[self.spinner startAnimating];
}
and synthesize spinner before using it.
And if you want to use it programmatically delete it from XIB and remove all references corresponding to it. And declare in .h file
UIActivityIndicatorView *spinner;
or you can make a property
#property(nonatomic, strong)UIActivityIndicatorView *spinner;
and just use your own code.
Assuming your spinner connected to your view.
In your viewDidLoad you can setup your view, then it'll show when you tap the button:
- (void)viewDidLoad
{
[super viewDidLoad];
self.spinner = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
self.spinner.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
[self.spinner setCenter:CGPointMake(480/2.0, 128.0/2)]; // (mid of screen) I do this because I'm in landscape mode
self.spinner.hidden = YES;
}
- (IBAction)click:(id)sender
{
self.spinner.hidden = NO;
[self.spinner startAnimating];
// Additionally you can show spinner in top bar
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}
- (void)stopSpinner
{
if (self.spinner.isAnimating) {
self.spinner.hidden = YES;
[self.spinner stopAnimating];
// And hide it when you don't need it anymore
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
}
I suggest you see this answer: How to get orientation-dependent height and width of the screen? to re position your spinner.

Objective C- UIActivityIndicatorView not animating

I am having some trouble getting my UIActivityIndicatorView to start animating. Here is my setup:
In my viewDidLoad in my view controller I have:
- (void)viewDidLoad{
schoolList = NO;
_activityIndicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[_activityIndicator startAnimating];
[NSThread detachNewThreadSelector: #selector(getSchoolList) toTarget: self withObject: nil];
[self performSelector:#selector(updateUI) withObject:nil afterDelay:20.0];
[super viewDidLoad];
}
The selector getSchoolList communicates with a server to retrieve a list of schools in a given state. Then, the selector updateUI is called to populate my UIPickerView with the list. In my updateUI selector I have:
-(void)updateUI {
_schools = [_server returnData];
if(!(_schools == nil)) {
NSLog(#"update the UI");
}
else
NSLog(#"Error:Show re-load button");
[_activityIndicator stopAnimating];
}
When I run this code, my UIActivityIndicatorView shows up, but does not animate. Can someone explain the proper way to animate my UIActivityIndicatorView? Any help is much appreciated.
You need to add the UIActivityIndicatorView to your view in viewDidLoad like this:
- (void)viewDidLoad {
schoolList = NO;
_activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[self addSubview:_activityIndicator];
[_activityIndicator startAnimating];
[NSThread detachNewThreadSelector: #selector(getSchoolList) toTarget: self withObject: nil];
[self performSelector:#selector(updateUI) withObject:nil afterDelay:20.0];
[super viewDidLoad];
}
EDIT
If _activityIndicator is a properly connected IBOutlet to a UIActivityIndicatorView, you should only need to check the 'animating' box. There would be no need to alloc/init another UIActivityIndicatorView.
Breakpoint the update function, but I don't see where you add that as a view to the hierarchy. I think you're looking at a different indicator view in the program.

UIActivityIndicatorView does not show

I have an app with a table view controller in which a user selects a US state, a web service is called and data is displayed for that state in the destination table view controller. Since the web service can take some time to complete I want an activity indicator. Since there will be no temporary data to display, I need this to be processed synchronously. So my task is pretty simple: start the activity indicator, call the web service, and after it completes, stop the activity indicator.
I am obviously doing something wrong and no activity indicator ever displays.
Here is the code from my destination table view controller's viewDidAppear method:
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[self.tableView bringSubviewToFront:spinner];
spinner.hidesWhenStopped = YES;
spinner.hidden = NO;
[spinner startAnimating];
stateGauges = [[GaugeList alloc] initWithStateIdentifier:stateIdentifier andType:nil];
[self.tableView reloadData];
[spinner stopAnimating];
}
Header:
#property (strong, nonatomic) UIActivityIndicatorView *spinner;
GaugeList is the object which makes the web service call.
Can someone tell me how to get an activity indicator view to appear? Thanks!
You forgot to add spinner on table view. Your code should look as follows:
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
spiner.center = //set some center
[self.tableView addSubview: spinner];
[self.tableView bringSubviewToFront:spinner];
spinner.hidesWhenStopped = YES;
spinner.hidden = NO;
[spinner startAnimating];
stateGauges = [[GaugeList alloc] initWithStateIdentifier:stateIdentifier andType:nil];
[self.tableView reloadData];
[spinner stopAnimating];
}
Also you send requests to a web service in main thread. This is bad practice. I would suggest something like following:
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
spiner.center = //set some center
[self.tableView addSubview: spinner];
[self.tableView bringSubviewToFront:spinner];
spinner.hidesWhenStopped = YES;
spinner.hidden = NO;
[spinner startAnimating];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
stateGauges = [[GaugeList alloc] initWithStateIdentifier:stateIdentifier andType:nil];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
[spinner stopAnimating];
});
});
}
At first you should add activity indicator to some view to show it. But you can not add it to UITableView, because UITableView is subclass of UIScrollView and you will see floating activity indicator. The best way in your case is to add activity indicator to navigation bar, etc. Or if you want to disable table view you should write something like this:
- (void)viewDidLoad {
[super viewDidLoad];
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
UIView *dummyView = [[UIView alloc] init];
dummyView.frame = self.tableView.bounds;
dummyView.alpha = 0.5f;
dummyView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
dummyView.userInteractionEnabled = YES;
dummyView.backgroundColor = [UIColor blackColor];
[dummyView addSubview:activityIndicator];
activityIndicator.center = dummyView.center;
[self.tableView addSubview:dummyView];
}
Try using self.spinner instead of using spinner.

Resources