I have multiple view controllers in my application.
While downloading data from server, I can start a UIActivityIndicatorView.
But the problem is for three different view controllers I need to create three different UIActivityIndicatorView.
I want to initialize UIActivityIndicatorView only once in some place so I can display or hide it. No need to create or initialize UIActivityIndicatorView as many as view controllers.
How can I do this? Any help.
Add your loading indicator on window in your appdelgate like this
First initialize your loading view
-(void)InitializeLoadingIndictor
{
loadingView = [[UIImageView alloc]initWithFrame:CGRectMake(300, 400, 150, 150)];
[loadingView setBackgroundColor:[UIColor blackColor]];
[loadingView setAlpha:0.6];
[loadingView setHidden:YES];
[loadingView.layer setCornerRadius:10];
[_window addSubview:loadingView];
loadingIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[loadingIndicator setFrame:CGRectMake(50, 50, 50, 50)];
[loadingView addSubview:loadingIndicator];
}
Then create two methods two hide and show it
+ (void)showLoading
{
[loadingView setHidden:NO];
[loadingIndicator startAnimating];
}
Hide it
+ (void)hideLoading
{
[loadingView setHidden:YES];
}
Then in your view controller, call it as
[AppDelegate showLoading];
And to hide it when your are done
[AppDelegate hideLoading];
You can use a custom UIAlertView to show your indicator.
I have made two custom files using which you can show indicator without initializing it again and again.
files are:
//header file..
#import <UIKit/UIKit.h>
#interface ProgressIndicator : UIAlertView
{
UIActivityIndicatorView *activityIndicator;
}
#end
//implementation file
#import "ProgressIndicator.h"
#implementation ProgressIndicator
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
activityIndicator=[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
// Initialization code
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
[self addSubview:activityIndicator];
[activityIndicator startAnimating];
// Drawing code
}
#end
//using this files in your controller..
#import "ProgressIndicator.h"
ProgressIndicator *progressIndicator=[[ProgressIndicator alloc] initWithTitle:#"Loading" message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles:nil];
[progressIndicator show];
Please refer this https://github.com/jdg/MBProgressHUD link. You will get custom Activity indicator for all the views.
Thanks guys for the answers. Below code works for me.
AppDelegate.m
-(void)StartLoading
{
loader = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
loader.frame = CGRectMake(50, 14, 20, 20);
[loader startAnimating];
[self.window addSubview:loader];
[self.window bringSubviewToFront:loader];
}
-(void)EndLoading
{
for(UIView *subview in [self.window subviews])
{
if(subview==loader)
[subview removeFromSuperview];
}
}
Related
I am trying to display an activity indicator in full screen so the user cannot press any button in the screen till the activity indicator is turned off as the alert view process. I have called the [activityView startAnimating] but I can push the buttons in the back. Is there way to prevent that?
Thanks from now.
You can use MBProgressHUD for such loading indictators. It's a great MIT-licensed class that extends well if you want to customize it further.
you can try this
UIAlertView *alert= [[[UIAlertView alloc] initWithTitle:#"Loading\nPlease Wait..." message:nil delegate:self cancelButtonTitle:nil otherButtonTitles: nil] autorelease];
[alert show];
UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
indicator.center = CGPointMake(150, 100);
[indicator startAnimating];
[alert addSubview:indicator];
[indicator release];
and add this line where you want remove your alert
[alert dismissWithClickedButtonIndex:0 animated:YES];
I'll suggest a best way will be displaying an alertView with activity indicator on the screen.
You can use the following code for this:
declare property for UIAlertView like:
#property (nonatomic, strong) UIAlertView *sendAlert;
self.sendAlert = [[UIAlertView alloc] initWithTitle:#"Loading" message:#"" delegate:nil cancelButtonTitle:nil otherButtonTitles:nil];
UIActivityIndicatorView *act = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
act setFrame:CGRectMake(115, 60, 50, 50)];
[act startAnimating];
[sendAlert addSubview:act];
act = nil;
[sendAlert show];
When you want to remove alert you can use:
[sendAlert dismissWithClickedButtonIndex:0 animated:YES];
sendAlert = nil;
Another alternative, you can add the activity indicator to your view itself and set the userInteraction of backbutton to false. When you finish the task set to True. But It won't be a nice way.
hey for your this requirement use the bellow code which you can access in your every view use..
add this bellow code and object in AppDelegate.h file like bellow..
UIView *activityView;
UIView *loadingView;
UILabel *lblLoad;
and paste this bellow code in AppDelegate.m file
#pragma mark - Loading View
-(void) showLoadingView {
//NSLog(#"show loading view called");
if (loadingView == nil)
{
loadingView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 60.0, 320.0, 420.0)];
loadingView.opaque = NO;
loadingView.backgroundColor = [UIColor darkGrayColor];
loadingView.alpha = 0.5;
UIView *subloadview=[[UIView alloc] initWithFrame:CGRectMake(84.0, 190.0,150.0 ,50.0)];
subloadview.backgroundColor=[UIColor blackColor];
subloadview.opaque=NO;
subloadview.alpha=0.8;
subloadview.layer.masksToBounds = YES;
subloadview.layer.cornerRadius = 6.0;
lblLoad=[[UILabel alloc]initWithFrame:CGRectMake(50.0, 7.0,80.0, 33.0)];
lblLoad.text=#"LoadingView";
lblLoad.backgroundColor=[UIColor clearColor];
lblLoad.textColor=[UIColor whiteColor];
[subloadview addSubview:lblLoad];
UIActivityIndicatorView *spinningWheel = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(10.0, 11.0, 25.0, 25.0)];
[spinningWheel startAnimating];
spinningWheel.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhite;
[subloadview addSubview:spinningWheel];
[loadingView addSubview:subloadview];
[spinningWheel release];
}
[self.window addSubview:loadingView];
//[[UIApplication sharedApplication] registerForRemoteNotificationTypes: UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert];
}
-(void) hideLoadingView {
if (loadingView) {
[loadingView removeFromSuperview];
[loadingView release];
loadingView = nil;
}
}
and call this method when you want in any class , just like bellow..
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate showLoadingView];
Add a UIView above your current view when activity indicator starts :
UIView *overlayView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
overlayView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];
[self.navigationController.view addSubview:overlayView];
I'm trying to add a "loading" indicator in the center of my screen after each tab is selected and as the initial tab is loaded. I have found many posts showing the code to accomplish this but it doesn't seem to work with the way my webview app is setup.
I am a novice and have written all of my code from different tutorials, so If you could please explain where to put the code that would be helpful.
Here is my current .h file
#import <UIKit/UIKit.h>
#interface ViewController1 : UIViewController
{
IBOutlet UIWebView *myWebView;
}
#end
Here is my current .m file
#import "ViewController1.h"
#interface ViewController1 ()
#end
#implementation ViewController1
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = NSLocalizedString(#"Home", #"Home");
self.tabBarItem.image = [UIImage imageNamed:#"birdhouse"];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
{
UIImage *navigationBarBackground = [[UIImage imageNamed:#"navbar.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0)];
[[UINavigationBar appearance] setBackgroundImage:navigationBarBackground forBarMetrics:UIBarMetricsDefault];
}
// Do any additional setup after loading the view from its nib.
NSURL *myURL = [NSURL URLWithString:#"http://jbirdmedia.org/rcwc/iphone/home.html"];
NSURLRequest *myRequest = [NSURLRequest requestWithURL:myURL];
[myWebView loadRequest:myRequest];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
I've taken out all the code from my failed attempts so this code is currently my working app for viewcontroller1 and I have 4 ViewControllers for my 4 tabs.
Thanks,
Jason
Well your code right now doesn't mention anything about an UIActivityIndicatorView. In your viewDidLoad method you need something like this:
UIActivityIndicatorView *activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
activityView.center = myWebView.center;
[myWebView addSubview: activityView];
[activityView startAnimating];
This is very rough and not tested but it should look something like this:
UIView *loadingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 150, 150)];
loadingView.center = self.view.center;
[loadingView setBackgroundColor:[UIColor blackColor]];
[loadingView setAlpha:0.75f];
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
activityIndicator.frame = CGRectMake(0,0,40,40);
activityIndicator.center = loadingView.center;
[loadingView addSubview:activityIndicator];
[activityIndicator startAnimating];
UILabel *loadingLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 100, loadingView.bounds.size.width, 50)];
[loadingLabel setText:#"Loading"];
[loadingLabel setBackgroundColor:[UIColor clearColor]];
[loadingLabel setTextAlignment:NSTextAlignmentCenter];
[loadingLabel setTextColor:[UIColor whiteColor]];
[loadingView addSubview:loadingLabel];
[self.webView addSubview:loadingView];
You'll need to adapt for the way you actually want the views to look, but that should get you pointed in the right direction.
So, here's the situation (this is all in the simulator):
create a view controller. it owns an MPMoviePlayerController, and adds the player's view as a subview to its own.
set this vc as the window's rootViewController.
also in this vc's view is a button which will launch a modal view controller (which obscures the movie) after pausing the movie player.
click the button, launch the modal VC, then dismiss it.
notice that the paused image of the movie player is gone (it is just black).
click "play" to resume playing the movie
crash
Now, change your build target from iOS 4.3 to iOS 5.0 and it works flawlessly.
What's really bothering me is that this didn't cause a crash under 4.3 a month ago. In fact, I have an app in the store right now for which this works just fine: http://itunes.apple.com/us/app/es-musica-free/id474811522?mt=8
So, I've tried to boil this down to a minimal example which reproduces the problem.
Create a vanilla xcode project.
in your AppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
self.window.backgroundColor = [UIColor blackColor];
[self.window makeKeyAndVisible];
UIViewController *vc = [[DemoMoviePlayerViewController alloc] init];
[self.window setRootViewController:vc];
[vc release];
vc = nil;
return YES;
}
DemoMoviePlayerViewController is pretty simple. here's the h file:
#import
#import
#interface DemoMoviePlayerViewController : UIViewController
{
MPMoviePlayerController *moviePlayer_;
}
#end
and the m file:
#import "DemoMoviePlayerViewController.h"
#interface DemoMoviePlayerViewController (InternalMethods)
- (void)_buttonPressed:(UIButton*)button;
#end
#implementation DemoMoviePlayerViewController
- (id)init
{
self = [super init];
if (self == nil)
{
return nil;
}
[self setWantsFullScreenLayout:YES];
NSURL *url = [NSURL URLWithString:#"http://once.unicornmedia.com/now/od/auto/0116d5af-bdc1-456d-a3ac-e3cbe25bac98/c762a821-e583-420c-b798-7b8c66736027/6c0e77e0-6dc4-48d5-9197-26e59271e8dd/content.once"];
moviePlayer_ = [[MPMoviePlayerController alloc] initWithContentURL:url];
return self;
}
- (void)dealloc
{
[moviePlayer_ stop];
[moviePlayer_ release];
moviePlayer_ = nil;
[super dealloc];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
[[self view] setBackgroundColor:[UIColor redColor]]; // debug
[[moviePlayer_ view] setFrame:[[self view] bounds]];
[[moviePlayer_ view] setAutoresizingMask:(UIViewAutoresizingFlexibleWidth
|UIViewAutoresizingFlexibleHeight)];
[moviePlayer_ setControlStyle:MPMovieControlStyleFullscreen];
[[moviePlayer_ view] setBackgroundColor:[UIColor greenColor]]; // debug
[[self view] addSubview:[moviePlayer_ view]];
// add a button to launch share kit
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setTitle:#"launch modal" forState:UIControlStateNormal];
[button addTarget:self
action:#selector(_buttonPressed:)
forControlEvents:UIControlEventTouchUpInside];
[[moviePlayer_ view] addSubview:button];
[button setFrame:CGRectMake(20, 100, 200, 40)];
[moviePlayer_ setShouldAutoplay:YES];
[moviePlayer_ play];
}
#pragma mark - internal methods -
- (void)_dismissModal
{
[self dismissModalViewControllerAnimated:YES];
}
- (void)_buttonPressed:(UIButton*)button
{
if ([moviePlayer_ currentPlaybackRate] == 1.0)
{
[moviePlayer_ setShouldAutoplay:NO];
[moviePlayer_ pause];
}
[self performSelector:#selector(_dismissModal) withObject:nil afterDelay:3.0];
UIViewController *vc = [[UIViewController alloc] init];
[[vc view] setBackgroundColor:[UIColor blueColor]];
[self presentModalViewController:vc animated:YES];
// yeah I know, vc is leaked...
}
#end
So apparently, somehow the MPMoviePlayerController is somehow able to detect which its view gets obscured, despite the fact that my view controller isn't doing anything fancy in viewWillDisappear or viewDidDisappear.
Again, what really bothers me is that, to the best of my knowledge, this worked just fine under 4.3 about a month ago.
I have a big problem since a few days that I can't solve.
First I have a login view controller with this code :
#implementation MMConnectionViewController
#synthesize login, password, activityIndicator, mainVC;
- (BOOL)textFieldShouldReturn:(UITextField *)aTextField
{
[aTextField resignFirstResponder];
[self performSelectorOnMainThread:#selector(startRolling) withObject:nil waitUntilDone:NO];
[NSThread detachNewThreadSelector:#selector(connect) toTarget:self withObject:nil];
return YES;
}
- (void)viewWillAppear:(BOOL)flag {
[super viewWillAppear:flag];
[login becomeFirstResponder];
login.keyboardAppearance = UIKeyboardAppearanceAlert;
password.keyboardAppearance = UIKeyboardAppearanceAlert;
[self setTitle:#"Monaco Marine"];
UIBarButtonItem *backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Logout"
style:UIBarButtonItemStyleBordered
target:nil
action:nil];
self.navigationItem.backBarButtonItem = backBarButtonItem;
[[UIApplication sharedApplication] setStatusBarStyle: UIStatusBarStyleBlackOpaque];
[backBarButtonItem release];
}
- (void)connect {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
mainVC = [[MMMainViewController alloc] initWithLogin:login.text password:password.text connectPass:#"1" navigationController:self.navigationController nibName:#"MMMainViewController" bundle:nil];
if (mainVC) {
[self performSelectorOnMainThread:#selector(dataLoadingFinished) withObject:nil waitUntilDone:YES];
}
[pool release];
}
- (void)dataLoadingFinished {
self.stopRolling;
[self.navigationController pushViewController:mainVC animated:YES];
}
- (void)showAlertWithMessage:(NSString *)message {
self.stopRolling;
NSLog(#"%#",message);
UIAlertView *warning = [[UIAlertView alloc] initWithTitle:#"Connection Failed" message:[NSString stringWithFormat:#"%#",message] delegate:self cancelButtonTitle:#"Retry" otherButtonTitles:nil];
[warning show];
[warning release];
}
- (void)startRolling {
[activityIndicator startAnimating];
}
- (void)stopRolling {
[activityIndicator stopAnimating];
}
- (void)viewDidLoad {
[login becomeFirstResponder];
}
- (void)dealloc {
[login release],login=nil;
[password release],password=nil;
[activityIndicator release],activityIndicator=nil;
[super dealloc];
}
After, there's MMMainViewController with this code :
#implementation MMMainViewController
#synthesize login, password, connectPass, navigationController, accountVC;
- (void)viewDidLoad {
// Set a title for each view controller. These will also be names of each tab
accountVC.title = #"Account";
accountVC.tabBarItem.image = [UIImage imageNamed:#"icon_user.png"];
self.view.frame = CGRectMake(0, 0, 320, 480);
// Set each tab to show an appropriate view controller
[self setViewControllers:
[NSArray arrayWithObjects:accountVC, nil]];
[navigationController setNavigationBarHidden:NO animated:NO];
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:#"Menu"
style:UIBarButtonItemStyleBordered
target:nil
action:nil];
self.navigationItem.backBarButtonItem = backButton;
[backButton release];
[self setTitle:#"Menu"];
}
// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
- (id)initWithLogin:(NSString *)l password:(NSString *)p connectPass:(NSString *)c navigationController:(UINavigationController *)navController nibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
UIView *contentView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
contentView.backgroundColor = [UIColor whiteColor];
self.view = contentView;
[contentView release];
login = l;
password = p;
connectPass = c;
navigationController = navController;
if (!accountVC)
accountVC = [MMAccountViewController alloc];
[self.accountVC
initWithNibName:#"MMAccountViewController" bundle:nil];
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
return self;
}
- (void)dealloc {
[connectPass release];
[login release];
[password release];
[super dealloc];
}
The layer MMAccountViewController loaded from MMMainViewController is a basic view controller with nothing in it.
Now the problem is that sometimes when load my tabbed view controller and I go back to the login view controller, the screen freezes and bug with message (NSZombieEnabled = YES) :
*** -[CALayer retain]: message sent to deallocated instance 0xd0199d0
This is all I have and I really can't see where I'm wrong.
Some more idea?
Thanks for those who help me!
You are overreleasing something somewhere. You might want to run your application in Instruments to check where it may be happening (XCode: Run->Run With Performance Tool->Leaks will give you the setup that you need afaik). I can't see anything in your code, but you said yourself that you use "roughly" this code, so it may not be in this part of your program.
Update:
I still can't see that you overrelease something somewhere... I'm pretty sure the problem is not within this part of the code. If you haven't found the problem yet, you may want to try and create a test case, that is, a small program that tries to mimic this programs behavior and reproduce the bug. If you can reproduce it within a small program, I'll take a look at that.
I was also encounter the same problem, and the problem was that I was assigning a UIButton to the rightNavigationItem and release that button instance, I just remove the release code and started working.
All of the examples I've seen on here and other sites involved creating a UIActivityIndicatorView and loading it with something like:
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc]
initWithCustomView:myActivityIndicatorView
target:nil
action:nil]
autorelease];
However, that just creates a plain activity indicator in the navigation bar. What I want to do is have a button that looks just like the normal UIBarButtonSystemItem buttons but with an activity indicator instead of one of the default images. I've tried doing initWithImage and initWithTitle with nil images or titles and then adding the activity indicator as a subview, but that doesn't work.
Any ideas?
My Solution is to create a subclass of UIButton:
in SOActivityButton.h:
#interface SOActivityButton : UIButton
{
UIActivityIndicatorView* activityIndicator;
}
#end
in SOActivityButton.m:
#implementation SOActivityButton
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
CGRect innerFrame = CGRectInset(frame, 8.0f, 8.0f);
activityIndicator = [[UIActivityIndicatorView alloc]
initWithFrame:innerFrame];
activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhite;
[self addSubview:activityIndicator];
}
return self;
}
- (void)dealloc
{
[activityIndicator release], activityIndicator = nil;
[super dealloc];
}
- (void) startAnimating
{
[activityIndicator startAnimating];
}
- (void) stopAnimating
{
[activityIndicator stopAnimating];
}
#end
Then to use it:
SOActivityButton* activityButton = [[SOActivityButton alloc]
initWithFrame:CGRectMake(0.0f, 0.0f, 32.0f, 32.0f)];
[activityButton setImage:[UIImage imageNamed:#"button-background.png"]
forState:UIControlStateNormal];
[activityButton addTarget:self action:#selector(myAction:)
forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *activityBarButtonItem = [[UIBarButtonItem alloc]
initWithCustomView:activityButton];
[activityButton release];
self.navigationItem.rightBarButtonItem = activityBarButtonItem;
[activityBarButtonItem release];
You will need to find or create a button-background.png. The PSD here should have one.
have you tried creating a UIButton in the button bar and then adding an activity indicator as a subView of the UIButton?
I have this working and it is very simple. Just place the activity indicator where you want it with IB, but make sure it's lower in the list than the bar you want it on, and is at the "top level" (not a subview of anything else). Then control it in code via an outlet.
Here's something that can help:
activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
[activityIndicator startAnimating];
UIBarButtonItem *activityItem = [[UIBarButtonItem alloc] initWithCustomView:activityIndicator];
[activityIndicator release];
self.navigationItem.rightBarButtonItem = activityItem;
[activityItem release];
[activityIndicator startAnimating];