I don't know how to explain but... If you understand me when I say a "loading circle" perfect, I just want to do this
// Start loading in the middle of the screen frozing all interaction
for (int c = 0; c < ([barcos count] - 1); c++)
{
NSArray *datos = [[barcos objectAtIndex:c] componentsSeparatedByString:#";"];
NSString *nombreImagen = [datos objectAtIndex:2];
NSURL *accesoFtp = [NSURL URLWithString:[NSString stringWithFormat:#"%#/%#",urlFtp,nombreImagen]];
NSData *imagen = [NSData dataWithContentsOfURL:accesoFtp];
[imagen writeToFile:[[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent:[NSString stringWithFormat:#"Barcos/%#",nombreImagen]]] atomically:NO];
}
// Stop frozing all interaction and remove the loading circle
Probabbly I have to add a thread or something but I don't know how to do what I want exactly I hope you can help me, again. Thanks.
EDIT:
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;
dispatch_queue_t queue = dispatch_get_global_queue(0,0);
dispatch_async(queue, ^{
for (int c = 0; c < ([barcos count] - 1); c++)
{
NSArray *datos = [[barcos objectAtIndex:c] componentsSeparatedByString:#";"];
NSString *nombreImagen = [datos objectAtIndex:2];
NSURL *accesoFtp = [NSURL URLWithString:[NSString stringWithFormat:#"%#/%#",urlFtp,nombreImagen]];
NSData *imagen = [NSData dataWithContentsOfURL:accesoFtp];
[imagen writeToFile:[[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent:[NSString stringWithFormat:#"Barcos/%#",nombreImagen]]] atomically:NO];
}
dispatch_sync(dispatch_get_main_queue(), ^{
activityIndicator.stopAnimating;
});
});
Two things
1.- The activity indicator is too small but works, if I can to it bigger or same size but make darker the background would be better (Thanks!)
2.- I have a warning with startAnimating and stopAnimating "Property access result unused - getters should not be used for side effects"
Thanks =)
It can be done using MBProgressHUD
Also check this code:
//Show your activity indicator here
dispatch_queue_t queue = dispatch_get_global_queue(0,0);
dispatch_async(queue, ^{
for (int c = 0; c < ([barcos count] - 1); c++)
{
NSArray *datos = [[barcos objectAtIndex:c] componentsSeparatedByString:#";"];
NSString *nombreImagen = [datos objectAtIndex:2];
NSURL *accesoFtp = [NSURL URLWithString:[NSString stringWithFormat:#"%#/%#",urlFtp,nombreImagen]];
NSData *imagen = [NSData dataWithContentsOfURL:accesoFtp];
[imagen writeToFile:[[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent:[NSString stringWithFormat:#"Barcos/%#",nombreImagen]]] atomically:NO];
}
dispatch_sync(dispatch_get_main_queue(), ^{
//hide that activity indicator here
});
});
EDIT:
Never call methods like:
activityIndicator.startAnimating;
activityIndicator.stopAnimating;
These are used for calling setters and getters.
Change it to:
[activityIndicator startAnimating];
[activityIndicator stopAnimating];
You can use a UIActivityIndicatorView.
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];
Also, like shown by Midhun MP, you may have to use asynchronism to, for example, load data while your indicator is showing.
May be you can try Activity Indicator.
You can try using svprogresshud
You can alter the style of activityindicator,
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActionSheetStyleDefault];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActionSheetStyleBlackOpaque];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActionSheetStyleBlackTranslucent];
use any of these styles.
And This link can help to solve your second issue
xCode "Property access results unused - getters should not be used for side effects"
SOLUTION:
instead of using activityIndicator.startAnimating; and activityIndicator.stopAnimating
use [activityIndicator startAnimating]; and [activityIndicator stopAnimating];
For changing the size of the activity indicator:
activityIndicator.frame = CGRectMake(0.0, 0.0, 80.0, 80.0);
activityIndicator.transform = CGAffineTransformMakeScale(1.75, 1.75);
By the way, if you are populating a tableview, you may consider enabling the user interaction at the same time you launch the activity indicator:
[activityIndicator startAnimating];
tableView.userInteractionEnabled = NO;
and restoring it after:
[self.tableView reloadData];
[activityIndicator stopAnimating];
tableView.userInteractionEnabled = YES;
Related
Right now I'm allocating and initializing three UIImageViews that take up the entire screen and are stacked in the viewDidLoad method. Its actually taking some time to do this. Is there a way to do this automatically so the view just has them before its even loaded? like an init method that would speed this up?
- (void)viewDidLoad {
[super viewDidLoad];
self.mySubviews = [[NSMutableArray alloc] init];
self.videoCounterTags = [[NSMutableArray alloc] init];
int c = (int)[self.scenes count];
c--;
NSLog(#"int c = %d", c);
self.myCounter = [NSNumber numberWithInt:c];
for (int i=0; i<=c; i++) {
//create imageView
UIImageView *imageView =[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)];
[imageView setUserInteractionEnabled:YES]; // <--- This is very important
imageView.tag = i; // <--- Add tag to track this subview in the view stack
[self.view addSubview:imageView];
NSLog(#"added image view %d", i);
//get scene object
PFObject *sceneObject = self.scenes[i];
//get the PFFile and filetype
PFFile *file = [sceneObject objectForKey:#"file"];
NSString *fileType = [sceneObject objectForKey:#"fileType"];
//check the filetype
if ([fileType isEqual: #"image"])
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
//get image
NSURL *imageFileUrl = [[NSURL alloc] initWithString:file.url];
NSData *imageData = [NSData dataWithContentsOfURL:imageFileUrl];
dispatch_async(dispatch_get_main_queue(), ^{
imageView.image = [UIImage imageWithData:imageData];
});
});
}
//its a video
else
{
// the video player
NSURL *fileUrl = [NSURL URLWithString:file.url];
self.avPlayer = [AVPlayer playerWithURL:fileUrl];
self.avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
self.avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
//self.avPlayerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[self.avPlayer currentItem]];
CGRect screenRect = [[UIScreen mainScreen] bounds];
self.avPlayerLayer.frame = CGRectMake(0, 0, screenRect.size.width, screenRect.size.height);
[imageView.layer addSublayer:self.avPlayerLayer];
NSNumber *tag = [NSNumber numberWithInt:i+1];
NSLog(#"tag = %#", tag);
[self.videoCounterTags addObject:tag];
}
}
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(viewTapped:)];
// dummyScreen is just a see through view that sits on top of my image stack and holds my tap gesture recognizer
[self.view bringSubviewToFront:self.dummyScreen];
[self.dummyScreen addGestureRecognizer:tapGesture];
}
The problem is this line:
dispatch_async(dispatch_get_global_queue...
That moves you onto a background thread, and thus there is no telling when the code will be executed. Hence the delay.
If these are local files (i.e., the URL is the file URL of an image file in your app bundle), there is no need for any dispatch_async in your code. Remove all of that and do everything on the main thread. That way, it will happen as fast as possible.
If these are remote files (i.e., you have to do networking to get hold of them), then there's probably nothing you can do to speed things up; networking takes time, and viewDidLoad is just about as early as you can possibly be notified that it's time to get hold of the images.
I'm trying to change a button in the navigation bar to an activity indicator instantly when it was pressed. I tryed this code, but the button change only when the IBaction process end:
- (BOOL)connected {
Reachability *reachability = [Reachability reachabilityForInternetConnection];
NetworkStatus networkStatus = [reachability currentReachabilityStatus];
return networkStatus != NotReachable;
}
- (IBAction)scaricaDatabase:(id)sender {
if (![self connected]) {
_navTitle.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Non connesso" style:UIBarButtonItemStylePlain target:nil action:nil];
} else {
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
UIBarButtonItem * barButton = [[UIBarButtonItem alloc] initWithCustomView:activityIndicator];
activityIndicator.color = [UIColor blackColor];
// Set to Left or Right
[activityIndicator startAnimating];
[[self navTitle] setLeftBarButtonItem:barButton];
NSString *dropboxHttpsUrl = #"https://dl.dropboxusercontent.com/u/68843065/eventiMedievale.sql";
NSData *dbFile = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:dropboxHttpsUrl]];
if(dbFile) {
NSLog(#"%lu", (unsigned long)[dbFile length]);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *appFile = [documentsDirectory stringByAppendingPathComponent:#"eventiMedievaleUpd.sql"];
[dbFile writeToFile:appFile atomically:YES];
NSLog(#"%#", appFile);
_navTitle.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Aggiornato" style:UIBarButtonItemStylePlain target:nil action:nil];
}else{
NSLog(#"File non scaricato");
}
}
I need to show the activity indicator when the process is downloading the sql file, but for now I only get the update of the navigation bar at the end of this process.
Could someone help me?
Thanks :)
I would try placing the NSData download portion into a background thread using Grand Central Dispatch. This should go within your IBAction
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
UIBarButtonItem * barButton = [[UIBarButtonItem alloc] initWithCustomView:activityIndicator];
activityIndicator.color = [UIColor blackColor];
// Set to Left or Right
[activityIndicator startAnimating];
[[self navTitle] setLeftBarButtonItem:barButton];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ // 1
//load the data here
dispatch_async(dispatch_get_main_queue(), ^(void) {
//remove the activity indicator
});
});
I'm using a collection view and trying to transition from loading the data synchronously to loading it asynchronously.
I know that the following currently works (it takes a while to load, but all the cells appear correctly when it's done):
// load projectData in main thread
NSData * projectData = [NSData dataWithContentsOfURL:userUrl];
[self performSelectorOnMainThread:#selector(fetchProjects:)withObject:projectData waitUntilDone:YES];
I rewrote it to do everything asynchronously:
// load project data asynchronously
dispatch_async(bgQueue, ^{
UIView *loadingAnimation = loadingCircle;
loadingAnimation.tag = 15;
[self.collectionView addSubview:loadingAnimation];
[loadingCircle startAnimating];
NSData * projectData = [NSData dataWithContentsOfURL:userUrl];
[self performSelector:#selector(fetchProjects:) withObject:projectData];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"finished with loading projects");
UIView *viewToRemove = [self.view viewWithTag:15];
[viewToRemove removeFromSuperview];
[self.collectionView reloadData];
});
});
When I run the app after loading the data asynchronously, the view appears empty (the cells have no content), but when I scroll, some of the cells begin to appear.
Is there anything else I need to call besides reloadData to get my collection cells to appear properly?
Here is my fetchProjects:
// get JSON data of projects
- (void)fetchProjects:(NSData *)responseData {
NSError * error;
NSDictionary * json = [NSJSONSerialization
JSONObjectWithData:responseData
options:kNilOptions
error:&error]; // get dictionary from json data
NSDictionary * data = [json objectForKey:#"data"]; // get data in array
NSArray * projects = [data objectForKey:#"projects"];
NSDictionary * mostRecentProject = [projects objectAtIndex:0];
mostRecentProjectID = [mostRecentProject objectForKey:#"id"];
for (NSDictionary *currentProject in projects)
{
[projectIDs addObject: [currentProject objectForKey:#"id"]];
NSString *projectTitle = [currentProject objectForKey:#"title"];
NSString *trimmedProjectTitle = [projectTitle stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
id delegate = [[UIApplication sharedApplication] delegate];
self.managedObjectContext = [delegate managedObjectContext];
Project *newProject = (Project *) [NSEntityDescription insertNewObjectForEntityForName:#"Project" inManagedObjectContext:[self managedObjectContext]];
CustomLabel *cellLabel=[[CustomLabel alloc]init];
cellLabel.text = trimmedProjectTitle;
NSLog(#"fetchprojects:%#",projectTitle);
[titles addObject:projectTitle];
CGSize maxLabelSize = CGSizeMake(screenWidth/2 - 30,100);
CustomLabel *titleLabel = [[CustomLabel alloc]init];
[titleLabel setNumberOfLines:0];
titleLabel.text = projectTitle;
CGSize expectedLabelSize = [titleLabel.text sizeWithFont:titleLabel.font constrainedToSize:maxLabelSize lineBreakMode:NSLineBreakByWordWrapping];
CGRect labelFrame = (CGRectMake(0, 0, screenWidth/2 - 30, 0));
labelFrame.origin.x = 0;
labelFrame.origin.y = screenWidth/2 - 70 - expectedLabelSize.height;
labelFrame.size.height = expectedLabelSize.height;
titleLabel.frame = labelFrame;
titleLabel.backgroundColor = [[UIColor blackColor]colorWithAlphaComponent:0.5f];
titleLabel.textColor =[UIColor whiteColor];
[titleLabel setFont: [UIFont fontWithName: #"HelveticaNeue" size:12]];
//NSLog(#"%#", titleLabel.text);
UIImageView *imagePreview = [[UIImageView alloc] initWithFrame:CGRectMake(7.5, 10, screenWidth/2 -30, screenWidth/2 -70)];
imagePreview.contentMode= UIViewContentModeScaleAspectFill;
imagePreview.clipsToBounds = YES;
[imagePreview setImage:[UIImage imageNamed:#"blank.png"]];
[imagePreview addSubview:titleLabel];
[imagePreview.subviews[0] setClipsToBounds:YES];
[projectContainers addObject: imagePreview];
}
}
You're doing a lot of UI work on a background thread which you really shouldn't do. From what I can see, the only line that really needs to be run on a background thread is this one:
NSData * projectData = [NSData dataWithContentsOfURL:userUrl];
The rest looks like it deals with setting up and displaying your UI and some CoreData stuff; all of that needs to be run on the main thread. The easiest way to do that and keep everything running in the right order would be something like this:
// NOTE: If you're sure you're already on the main thread here, you don't need the dispatch, but it's not going to hurt to leave it in.
dispatch_async(dispatch_get_main_queue(), ^{
UIView *loadingAnimation = loadingCircle;
loadingAnimation.tag = 15;
[self.collectionView addSubview:loadingAnimation];
[loadingCircle startAnimating];
});
dispatch_async(bgQueue, ^{
NSData * projectData = [NSData dataWithContentsOfURL:userUrl];
dispatch_async(dispatch_get_main_queue(), ^{
[self fetchProjects:projectData];
NSLog(#"finished with loading projects");
UIView *viewToRemove = [self.view viewWithTag:15];
[viewToRemove removeFromSuperview];
[self.collectionView reloadData];
});
});
Note: I also changed [self performSelector:#selector(fetchProjects:) withObject:projectData] to [self fetchProjects:projectData]; you don't really need to go through performSelector: there.
I'm a noobie in the Objective-C language, and I have a little problem.
In fact, I have 2 TableViews, and when I go from one to the other I parse some XML from the internet. The parsing is doing well, but I wanted to add an UIActivityIndicatorView between those 2 views to tell to the user that something is loading.
So, to do that, I wanted to do the parsing in another thread and show the UIActivityIndicatorView in the main thread. So here's my code :
- (void)viewDidLoad
{
[super viewDidLoad];
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;
dispatch_queue_t queue = dispatch_get_global_queue(0,0);
dispatch_async(queue, ^{
NSError *error = nil;
// we will put parsed data in an a array
titles = [[NSMutableArray alloc] init];
urls = [[NSMutableArray alloc] init];
CXMLDocument *rssParser = [[CXMLDocument alloc] initWithContentsOfURL:[NSURL URLWithString:_emissionSelectionnee] options:0 error:&error];
NSArray *nodes = NULL;
nodes = [rssParser nodesForXPath:#"//rss/channel/item/title" error:nil];
for (CXMLElement *title in nodes) {
[titles addObject:[title stringValue]];
}
nodes = NULL;
nodes = [rssParser nodesForXPath:#"//rss/channel/item/enclosure" error:nil];
for (CXMLElement *url in nodes) {
[urls addObject:[[url attributeForName:#"url"] stringValue]];
}
dispatch_sync(dispatch_get_main_queue(), ^{
activityIndicator.stopAnimating;
});
}
}
So now, the UIActivityIndicator shows up, but the cells are empty.. When I do not use the dispatch_queue_t, it works well..
Does someone have an idea?
Thank you in advance!
You need to reload your Table view (in the same block where you hide the activity indicator):
[self.tableView reloadData]
I am using GCD to download the header image for the UITableView.
When I use dispatch_async, the image does not show up at all, and when I use dispatch_sync, it still a synchronous download. How do I fix this ?
eventDetailsTable = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height) style:UITableViewStyleGrouped];
eventDetailsTable.dataSource = self;
eventDetailsTable.delegate = self;
[self.view addSubview:eventDetailsTable];
NSString *headerImageUrl = [NSString stringWithFormat:#"%#%#", [currentEvent objectForKey:#"baseurl"], [currentEvent objectForKey:#"sessionimage"]];
NSURL *headerImageURL = [NSURL URLWithString:headerImageUrl];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSData *imageData = [[NSData alloc] initWithContentsOfURL:headerImageURL];
UIImage *headerImage = [UIImage imageWithData:imageData];
UIImageView *headerImageView = [[UIImageView alloc] initWithImage:headerImage];
eventDetailsTable.tableHeaderView = headerImageView;
});
When you update UI, you must do it on the main thread. So here is solution:
dispatch_async(global_queue, ^{
//Do your work
dispatch_async(dispatch_get_main_queue(), ^{
//Update UI
});
});