As long as the application is running i need to change the status icon once in 2 sec., i think in my case i have to do it in separate thread.
This is my scenario.
I have 2 NSObject initialised in my MainMenu.nib. One of them is main controller AppDelegate and other one ChangeStatusBar to change the icon.
I have an IBOutlet in my AppDeligate pointing to ChangeStatusBar object. From AppDeligate i am calling one method of ChangeStatusBar which uses performSelectorInBackground with run loop at achieve this. Which is not working. My method to change icon is getting called but image is not changing during AppDeligate wait.
My Appdeligate can go on unconditional wait, even in such case i want StatusItem image changing. Ex: say you have scanf in AppDeligate or you are calling an API that can return after sum time. Thanks in Advance. Here is my code.
// AppDelegate.m
-(void)applicationDidFinishLaunching:(NSNotification*)notification
{
[statusItem initStatusItem:true]; //StatusItem is the IBOutlet to ChangeStatusBar object
}
//ChangeStatusBar.m
-(void)initStatusItem:(BOOL) flag
{
statusItem = [[[NSStatusBar systemStatusBar]
statusItemWithLength:NSVariableStatusItemLength]retain] ;
[statusItem setMenu:statusMenu];
[statusItem setImage:[NSImage imageNamed:#"lk0.png"]];
[self performSelectorInBackground:#selector(timerStart) withObject:nil];
}
-(void) timerStart
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
[[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(changeImages)
userInfo:nil repeats:YES] retain];
[runLoop run];
[pool release];
}
///Change image here
-(void)changeImages
{
[statusItem setImage:[NSImage imageNamed:[NSString stringWithFormat:#"lk%d.png",NoImages]]];`
}
Related
I'm missing something about how UIActivityIndicatorView and NSTimer work together.
I've added this UIActivityIndicatorView in Interface Builder with the following settings:
The UIWebView is instantiated as self.webV and the UIActivityIndicatorView as self.indicator.
I have the following code in the implementation file:
-(void)viewDidLoad
{
[super viewDidLoad];
//Create UIWebView.
if (!self.webV)
{
self.webV = [[UIWebView alloc] init];
}
self.webV.delegate = self;
//Load web page.
NSString *baseURLString = #"myURL.com";
NSString *urlString = [baseURLString stringByAppendingPathComponent:#"myURL.com"];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0/2.0 target:self selector:#selector(timerLoad) userInfo:nil repeats:YES];
[self connectWithURL:urlString andBaseURLString:baseURLString];
}
-(void)timerLoad
{
if (!self.webV.loading)
{
[self.indicator stopAnimating];
}
else
{
[self.indicator startAnimating];
}
}
But when the UIWebView loads, no activity indicator shows up. What am I doing wrong or leaving out?
Thanks for the help, folks.
I'm really not sure on what the behaviour of the UIActivityIndicatorView is supposed to be if you repeatedly call start/stop on it. I am reasonably sure it isn't meant to be used that way :)
So, even though your question is specific to NSTimer and UIActivityIndicatorView, it may be helpful to understand that you should approach your solution differently.
Instead of using a timer that repeatedly calls [self.indicator startAnimating] every half-second, you should use the webview delegate methods to toggle the UIActivityIndicatorView on and off.
-(void)viewDidLoad
{
[super viewDidLoad];
//Create UIWebView.
if (!self.webV)
{
self.webV = [[UIWebView alloc] init];
}
self.webV.delegate = self;
//Load web page.
NSString *baseURLString = #"myURL.com";
NSString *urlString = [baseURLString stringByAppendingPathComponent:#"myURL.com"];
//self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0/2.0 target:self selector:#selector(timerLoad) userInfo:nil repeats:YES];
[self connectWithURL:urlString andBaseURLString:baseURLString];
}
- (void)webViewDidStartLoad:(UIWebView *)webView{
//start animating
[self.indicator startAnimating];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView{
//stop animating
[self.indicator stopAnimating];
}
There could be several reasons. It could load so fast that the loading is already done, or it never loaded at all because something is wrong with the URL.
This isn't the cause of your problem, but you never invalidate your timer, which you should.
I was also going to make the point that you should use delegate methods instead of a timer, but pdriegen beat me to it.
I'm still new to iOS development but I've ran into a problem that I can't solve and I've tried looking online but can't find anything yet.
I'm using a UIImagePickerController to pick and image and I'm using it in the App Delegate. When an image is returned, in the imagePickerController:didFinishPickingMediaWithInfo method, I want to make a new navigation controller with a view controller and put it over the "app".
Here is how I'm doing it:
CustomNavigationController *customNavigationController = [[CustomNavigationController alloc] init];
PhotoViewController *photoViewController = [[PhotoViewController alloc] initWithNibName:#"PhotoViewController" bundle:[NSBundle mainBundle]];
[customNavigationController pushViewController:photoViewController animated:NO];
[customNavigationController.view setFrame:[[UIScreen mainScreen] applicationFrame]];
[photoViewController release];
[self.window addSubview:customNavigationController.view];
//Call method of photoViewController.
[[customNavigationController.viewControllers objectAtIndex:0] addPhotoFromData:info];
[self.tabBarController dismissViewControllerAnimated:YES completion:nil]; //Get rid of UIImagePickerController
However in the photoViewController, I don't have access to any instance variables synthesized and loaded in viewDidLoad. They all return to null. There is also a tableView and calls to reload the tableView do not actually cause the tableView to respond.
Here is some of the code from photoViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.photoCount = 0;
self.timestamp = [[NSDate date] timeIntervalSince1970];
self.photos = [[NSMutableArray alloc] initWithCapacity:5];
self.photosData = [[NSMutableArray alloc] initWithCapacity:5];
self.uploadingCount = 0;
UINib *customCell = [UINib nibWithNibName:#"CustomTableCell" bundle:[NSBundle mainBundle]];
[self.tableView registerNib:customCell forCellReuseIdentifier:#"customCell"];
self.tableView.separatorColor = [UIColor clearColor];
NSLog(#"%#", self);
}
and also the addPhotoFromData method:
- (void)addPhotoFromData:(NSDictionary *)info
{
[self.photos addObject:info];
NSLog(#"%#", self.photos); //Doesn't add "info" and does not return anything when later called from other methods.
[self.tableView reloadData]; //Doesn't work
Everything was working before I add in the UINavigationController. I'm completely lost.
EDIT: After some more debugging attempts, I have discovered that the view is not loaded when addPhotoFromData is called. viewDidLoad is called afterwords. Is there a way to delay method calls?
Any reason you can't have the PhotoViewController call addPhotoFromData: inside the viewDidLoad method? You can always access properties of the app delegate from the view controller:
YourAppDelegateType * delegate = [UIApplication sharedApplication].delegate;
[self addPhotoFromData:delegate.info];
I've been trying to figure this out for 2 days now, and before anyone posts another stackoverflow question, I've read them all and none of them cover my problem exactly:
I have a CoreData app that updates dynamically. Now during the update I want an UIAlertView to pop up saying that an update is being downloaded.
So here's the important code:
AppDelegate:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[myUpdater checkForUpdatesInContext:self.managedObjectContext];
}
_
Updater Class:
- (void)checkForUpdatesInContext:(NSManagedObjectContext *)myManagedObjectContext
{
[self loadUpdateTime];
NSLog(#"Update start");
NSDate *now = [NSDate dateWithTimeIntervalSinceNow:[[NSTimeZone localTimeZone] secondsFromGMT]];
if ([now timeIntervalSinceDate:updateTime] < UPDATE_TIME_INTERVAL)
{
return;
}
[self showAlertViewWithTitle:#"Update"];
... //updating process
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
NSLog (#"Update done");
}
- (void) showAlertViewWithTitle:(NSString *)title
{
self.alertView = [[UIAlertView alloc] initWithTitle:title message:#"Daten werden aktualisiert..." delegate:self cancelButtonTitle:nil otherButtonTitles:nil];
... //design the alertView
[self.alertView show];
NSLog (#"AlertView shows");
}
So here is what happens when I run this:
Launch image shows
NSLog "Update starts" fires
NSLog "AlertView shows" fires
Screen dims but no AlertView is shown
Update is running
NSLog "Update done" fires
Launch image goes away and TabBarController shows up
UIAlertView shows up and is dismissed right away and the dimmed screen returns to normal
What I would like to have happen:
Launch image
TabBarController shows up
Screen dims and UIAlertView shows
Update is running
UIAlertView gets dismissed and dimmed screen returns to normal
I know it's something with the UI Thread and the main Thread and stuff.. But I tried every combination it seems but still not the expected result. Please help :)
EDIT:
HighlightsViewController Class:
- (void)viewDidLoad
{
[super viewDidLoad];
self.updater = [[Updater alloc] init];
[updater checkForUpdatesInContext:self.managedObjectContext];
... // other setup stuff nothing worth mentioning
}
Is this the right place to call [super viewDidLoad]? Because it still doesn't work like this, still the update is being done while the Launch Image is showing on the screen. :-(( I'm about to give this one up..
Here you go, in this prototype things work exactly how you want them to.
Header:
#import <UIKit/UIKit.h>
#interface AlertViewProtoViewController : UIViewController
{
}
- (void) showAlertViewWithTitle:(NSString *)title;
- (void) checkForUpdatesInContext;
- (void) update;
- (void)someMethod;
- (void)someOtherMethod;
#end
#import "AlertViewProtoViewController.h"
Class:
#implementation AlertViewProtoViewController
UIAlertView *alertView;
bool updateDone;
UILabel *test;
bool timershizzle;
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = [UIColor yellowColor];
UILabel *test = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
test.backgroundColor = [UIColor blueColor];
[self.view addSubview:test];
[self performSelector:#selector(checkForUpdatesInContext) withObject:nil afterDelay:0.0];
}
- (void)update
{
//NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; //commented for auto ref counting
NSLog(#"update start");
//your update stuff
NSLog(#"update end");
updateDone = YES;
//[pool release];
}
- (void)checkForUpdatesInContext//:(NSManagedObjectContext *)myManagedObjectContext
{
//[self loadUpdateTime];
NSLog(#"Update start");
NSDate *now = [NSDate dateWithTimeIntervalSinceNow:[[NSTimeZone localTimeZone] secondsFromGMT]];
// if ([now timeIntervalSinceDate:updateTime] < UPDATE_TIME_INTERVAL)
// {
// return;
// }
[self showAlertViewWithTitle:#"Update"];
//[self setManagedObjectContext:myManagedObjectContext];
[self performSelector:#selector(someMethod) withObject:nil afterDelay:0.0];
[self performSelector:#selector(someOtherMethod) withObject:nil afterDelay:0.0];
}
-(void)someOtherMethod
{
while (!updateDone) {
// NSLog(#"waiting...");
}
[alertView dismissWithClickedButtonIndex:0 animated:YES];
NSLog (#"Update done");
self.view.backgroundColor = [UIColor greenColor];
}
-(void)someMethod
{
[self performSelectorInBackground:#selector(update) withObject:nil];
}
- (void) showAlertViewWithTitle:(NSString *)title
{
alertView = [[UIAlertView alloc] initWithTitle:title message:#"Daten werden aktualisiert..." delegate:self cancelButtonTitle:nil otherButtonTitles:nil];
alertView.frame = CGRectMake(100, 100, 200, 200);
alertView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:alertView];
[self.view setNeedsDisplay];
NSLog (#"AlertView shows");
}
#end
You should adjust were needed for your own purposes but it works.
You are starting a background thread and then dismissing the alert immediately. I would suggest that you might use an NSNotification, posted from the background task, and received in whichever controller starts the alert, triggering a method that dismissed the alert.
I find the UIAlertView interface unsuitable for this type of user notice, and prefer to use a semi-transparent overlay view with a UIActivityIndicatorView, plus an informing message for the user.
You are doing a:
- (void)applicationDidBecomeActive:(UIApplication *)application
Isn't it so that the alertview you want to show needs a view to be loaded which isn't active yet at this point? See: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIAlertView_Class/UIAlertView/UIAlertView.html
Similar question? UIAlertView starts to show, screen dims, but it doesn't pop up until it's too late!
I'm trying to add a spinning activity indicator (UIActivityIndicatorView) to my app while it parses data from the internet. I have an IBOutlet (spinner) connected to a UIActivityIndicatorView in IB. Initially I had it set up like this:
-
(void) function {
self.spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleWhite];
self.spinner.hidesWhenStopped = YES;
[spinner startAnimating];
//parse data from internet
[spinner stopAnimating];}
But the spinner wouldn't spin. I read that it had something to do with everything being on the same thread. So I tried this:
- (void) newFunction {
self.spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleWhite];
self.spinner.hidesWhenStopped = YES;
[spinner startAnimating];
[NSThread detachNewThreadSelector: #selector(function) toTarget: self withObject: nil];
[spinner stopAnimating];}
But still no luck. Any ideas? Thanks.
Your newFunction: method should look like this:
- (void) newFunction {
self.spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
self.spinner.hidesWhenStopped = YES;
[NSThread detachNewThreadSelector: #selector(function) toTarget: self withObject: nil];
}
And your function method should look like this:
- (void) function {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self.spinner performSelectorOnMainThread:#selector(startAnimating) withObject:nil waitUntilDone:NO];
//...
[self.spinner performSelectorOnMainThread:#selector(stopAnimating) withObject:nil waitUntilDone:NO];
[pool drain];
}
you should not intitialize indicator again .please replace your code with this.
-(void) function {
[spinner startAnimating];
[self performSelector:#selector(newfunction) withObject:nil afterDelay:3.0];
}
- (void) newfunction {
[spinner stopAnimating];
}
Thanks.
Just see that the "//parse data from internet " is synchronous or asynchronous. Asynchronous would mean that a separate thread would start from that point on, and the current function execution will continue without delay.
In your second example, you are explicitly making separate thread, which means that #selector(function) will happen on a separate thread, and the next statement [spinner stopAnimating] is executed immediately. So, it seems like spinner is not spinning at all.
Moreover, make sure you start and stop the activity indicator on main thread only.
I was wondering if anyone knew the best way of getting in a UITableView, where we have images on the left hand side, for the images to appear with UIActivity indicator gradually rather than not being there and appearing all of a sudden. Ive seen that on a few apps and wanted to know how to do it.
I haven't done this myself, but my idea would be to create my own UIView subclass that handles showing the UIActivity indicator while the image is loading. Then add instances of that view to your table cells instead of plain UIImageViews.
Update:
OK, as I said before, I haven't done this myself. The following is just typed into the browser, and I'm not sure if you'd run into any issues with threading, but it should give you an idea as to how to approach this:
#interface IndicatorImageView : UIView {
UIImageView *imageView;
UIActivityIndicatorView *indicator;
NSString *imageName;
NSURL *imageURL;
}
#property (nonatomic, copy) NSString *imageName;
#property (nonatomic, retain) NSURL* imageURL;
#end
#implementation IndicatorImageView
#synthesize imageName;
#synthesize imageURL;
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
imageView = [[UIImageView alloc] initWithFrame:self.bounds];
[self addSubview:imageView];
indicator = [[UIActivityIndicatorView alloc] initWithFrame:self.bounds];
[self addSubview:indicator];
}
return self;
}
- (void)setImageName:(NSString *)anImageName {
if (imageName == anImageName) {
return;
}
[imageName release];
imageName = [anImageName copy];
// optionally remove the old image while we're updating
//imageView.image = nil;
[indicator startAnimating];
[self performSelectorInBackground:#selector(updateImageViewUsingImageName) withObject:nil];
}
- (void)setImageURL:(NSURL *)anImageURL {
if (imageURL == anImageURL) {
return;
}
[imageURL release];
imageURL = [anImageURL copy];
// optionally remove the old image while we're updating
//imageView.image = nil;
[indicator startAnimating];
[self performSelectorInBackground:#selector(updateImageViewUsingImageURL) withObject:nil];
}
- (void)updateImageViewUsingImageName {
NSAutoreleasepool *pool = [[NSAutoreleasePool alloc] init];
imageView.image = [UIImage imageNamed:imageName];
[indicator performSelectorOnMainThread:#selector(stopAnimating) withObject:nil];
[pool release];
}
- (void)updateImageViewUsingImageURL {
NSAutoreleasepool *pool = [[NSAutoreleasePool alloc] init];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
imageView.image = [UIImage imageWithData:imageData];
[indicator performSelectorOnMainThread:#selector(stopAnimating) withObject:nil];
[pool release];
}
#end
When you set the imageName property, the image will be loaded in the background while the UIActivityIndicatorView is animating. Once the image is loaded and set on the UIImageView, we stop animating the activity indicator.
If I can remember correctly something like that was shown in stanford lectures cs193, iPhone Application Programming, when loading profile pictures from twitter accounts.
Have a look for the appropriate presentation (I can't remember which one it was), look at notes that talk about table view then have a look at itunesu video of the lecture.
http://www.stanford.edu/class/cs193p/cgi-bin/index.php
Subclass UIImage and add the UIActivityIndicator as a subview. Add some methods to the class for setting the image source, and start/stop the UIActivityIndicator appropriately.