I am working on an app that needs to flip through 300 or so images in sequence, with a 2 second delay between images.
These images are barcodes that are generated on the fly during the display. They are not stored images.
The display is in a navigation controller and I would like the user to be able to click the 'back' button without the app crashing from a selector being sent to an instance that no longer exists.
I know that it is possible to animate a UIImageView, but I don't want to create an large array of images because of memory issues.
I'd like to do this in a loop where I generate the barcode, display the image, delay 2 seconds, and then repeat with the next image.
The following code works, but crashes if you click the 'back' button, with a 'message sent to deallocated instance' error.
NSSet *dataSet = [self fetchDataSet];
for (MyBarCode *data in dataSet) {
// display barcode in UIImageView
[self updateBarCodeImage:data ];
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow: 2.0]];
}
There doesn't seem to be a way to cancel this timer, or I could do that in viewWillDisapear.
What's the right way to do this?
Please don't just point me to the animationImages examples. I've already seen all of them and -- again -- I don't want to have to hold all these images in memory during the animation. If there's a way to generate the images on the fly during animation, now that would be interesting.
I think something like this should work:
__weak ViewController *bSelf = self;
NSSet *dataSet = [self fetchDataSet];
dispatch_queue_t myQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
dispatch_async(myQueue, ^{
__strong ViewController *sSelf = bSelf;
for (BoardingPass *data in dataSet) {
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[sSelf updateBarCodeImage:data]
});
}
});
UPDATED:
Well I am not sure what exactly you are looking for. If you are talking about a simple animation than yes, Cocos 2D would be overkill. In that case I suggest doing this tutorial which gave me a lot of ideas on a project I was working on some time back:
http://www.raywenderlich.com/2454/how-to-use-uiview-animation-tutorial
Related
I am writing an ios app, that has multiple UIViewcontrollers. They all have UITableViews that are filled with data, that is acquired from different API's. But the problem that I am facing is that, when I tap on a cell, the the app won't navigate to the next page, until the data for that page is acquired. This make the app look, mighty slow. I need some way to navigate to next page, where I can put some spinner animation to let the user know that it is acquiring data(atleast). I don't want the user to think that the app has crashed, or something(it stays in the same page for solid 7-10 seconds)
Thanks in advance
you should call all the api in the background so it wont effect the main thread, use the queue for call api in background queue like below.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_async(queue, ^{
//call apis here
});
My solution to this problem is,You can call the API using dispatch queue,So that it will not affect other functionalities.
Please follow these simple steps
ViewController1 = VC1
ViewController1 = VC2
in VC1
[self.navigationController pushViewController:VC2 animated:YES];
in VC2
#implementation {
BOOL hasData;
}
-(void)viewDidLoad {
hasData = NO;
[self getAndShowData];
}
-(void)getAndShowData {
// Start Showing Spinner
// load your data from server and
// on success
1.) hasData = YES;
2.) Call reload tableview
// Remove spinner
}
- (NSInteger)numberOfRowsInSection:(NSInteger)section {
if(hasData == NO) return 0;
return actual number of rows.
}
}
I amy trying to make simple progress bar in SpriteKit. To simplify the example I will use SKLabelNode and it's text property, which will indicate the progress.
Here is the code( GameScene.m ):
#import "GameScene.h"
#interface GameScene ()
typedef void (^CompletitionHandler)(void);
#property (nonatomic,strong)SKLabelNode* progressBar;
#end
#implementation GameScene
-(void)didMoveToView:(SKView *)view {
/* Setup your scene here */
self.progressBar = [SKLabelNode labelNodeWithFontNamed:#"Chalkduster"];
self.progressBar.fontColor = [SKColor redColor];
self.progressBar.fontSize = 24.0;
self.progressBar.position = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame));
[self addChild:self.progressBar];
[self loadSceneAssetsWithCompletionHandler:^{
[self setupScene:view];
}];
}
- (void)loadSceneAssetsWithCompletionHandler:(CompletitionHandler)handler {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// Load the shared assets in the background.
//This part simulates bunch of assets(textures, emitters etc) loading in background
for (int i = 0; i <= 100; i++) {
[NSThread sleepForTimeInterval:0.01];
float progressValue = (float)i;
dispatch_async(dispatch_get_main_queue(), ^{
self.progressBar.text = [NSString stringWithFormat:#"%1.f", progressValue];
});
}
if (!handler) {
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
// Call the completion handler back on the main queue.
handler();
});
});
}
Now I am trying to find an obvious way, if there is any, to update the progress based on percentage of data loaded from background queue.
The problem is that I want to show percentage of loaded data from 0 to 100. But I don't know how to tell what would be 1% of bunch of textures, emitters and nodes, or I can say, I don't know how to update a progress bar after %1 is loaded. This is because I am working with different kind of objects. Is there any way to check the state of certain background queue to see how much stuff is left to be executed(loaded)?
Anybody has any idea, or suggestion ?
If you are looking to get an update on, for example, how much is left to load for a texture atlas then the answer is you can't.
You can however keep a "load items" counter and update that once an asset is loaded. For example, you have 13 texture atlases to load and some sound files, both of those have completion capability in their loading methods.
- (void)preloadWithCompletionHandler:(void (^)(void))completionHandler
playSoundFileNamed:(NSString *)soundFile
waitForCompletion:(BOOL)wait
Every time an asset finishes loading, update your counter. To be honest though, I am not sure if this is really necessary as loading usually happens very quickly. Display a generic "loading" message for a few seconds is probably your best (and easiest) option.
I have a UITableView (TV) with several sections, each section has an NSArray that serves as the dataSource (no CoreData, no images). When the user opens the TV, my app does some intensive calculations to generate the dataSource arrays. In some cases, the calculations can take some time, and what happens then is that the section headers show first, after which the cells appear, which doesn't like good, I think.
I'm already using GCD to do the calculations:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear: animated];
[MBProgressHUD showHUDForView: self.view animated: YES];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self.model generateData];
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView: self.view animated: YES];
[self.tableView reloadData];
});
});
}
Besides trying to optimize the calculations, is there anything else I could do to make this look smoother? For instance, is there a way for the section headers not to appear until the calculations are done?
UPDATE:
So in the end, my solution turned out to be different. To generate my data I am now using a dispatch_group, and calculate theNSArray for each section in andispatch_group_async block, so they run concurrently. This already was an improvement in speed. Furthermore, I start the calculation already in the UIViewController from which the user opens the TV. Therefore, the data is available almost instantly when the TV opens, and all sections load smoothly.
Here is a code snippet for completeness:
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) ^{
[self.model generateArray1];
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) ^{
[self.model generateArray2];
});
//... etc for each section
// make sure that everything is done before moving on
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
If you return nil from tableView:titleForHeaderInSection: then the header won't be shown, so add a small amount of conditional logic which checks if the data is loaded yet and either returns nil (if not loaded) or the section title (if it is loaded).
I am navigating by clicking a button to a viewcontroller where I am loading webview,but after clicking the button it is taking some time,how to navigate faster and load webview faster,please help.I have only the following code in second viewcontroller.
-(void)viewWillAppear:(BOOL)animated{
self.navigationController.navigationBarHidden=YES;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSURLRequest *request=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://myurl"]];
dispatch_async(dispatch_get_main_queue(), ^{
[self.wb loadRequest:request] ;
});
});
}
Try this code
-(void)viewWillAppear:(BOOL)animated{
self.navigationController.navigationBarHidden=YES;
dispatch_queue_t jsonParsingQueue = dispatch_queue_create("jsonParsingQueue", NULL);
// execute a task on that queue asynchronously
dispatch_async(jsonParsingQueue, ^{
NSURLRequest *request=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://myurl"]];
dispatch_async(dispatch_get_main_queue(), ^{
[self.wb loadRequest:request] ;
});
});
}
If I understand your question, there isn't much you can do to make if faster. That request speed is based on internet speed (Over which you don't have much of a control).
Also the request already happens asynchronously, so there's no need to do that yourself.
You are combining two things as you navigate to your webview
loading and displaying a view
Retrieving data from the internet
You can only directly influence the first one, the second one is well beyond your control.
By performing the asynchronous NSURLRequest from within the viewWillAppear method, you are telling iOS to delay showing the view until the internet has given you all the data it needs.
A better approach is to configure all the visual elements of your new view, display that view in the interface, and then AFTER the new view is visible, perform your NSURLRequest.
Adding a UIActivityIndicator may also help your users realize that your app was snappy and responsive, and the delay they are experiencing is from the internet.
Perhaps the easiest way to fix this would be to move your code over to
- (void)viewDidLoad {}
It takes a long to start up the application I write on an iPhone. I hope to display a waiting progress bar to indicate the percentage of the startup progress. Can anyone help me? I am new to the Development of IOS.
You can't.
The application needs to be started before you can show any dynamic content.
After your first ViewController is loaded you can of course show a progress bar and do loading.
Before that, you can only show a static image Default.png & Default#2x.png.
If you delay the heavy loading to after your -[ViewController viewDidLoad]-method is finished you can show any kind of GUI while doing the heavy work.
Edit:
This example will detach a new thread in your viewDidLoad and do some heavy work (in this case sleep for 1 second) and update a UIProgressView and log the progress as it sleeps 10 times, incrementing the progressview with 0.1 each time.
- (void)viewDidLoad
{
// .....
[NSThread detachNewThreadSelector:#selector(workerBee) toTarget:self withObject:nil];
}
- (void)workerBee
{
NSAutoreleasePool* workerPool = [[NSAutoreleasePool alloc] init];
// Put your image loading between this line and [workerpool release]
for (float i = 0; i<1; i+= 0.1)
{
[self performSelectorInBackground:#selector(updateProgress:) withObject:[NSNumber numberWithFloat:i]];
sleep(1000);
}
[workerPool release];
}
- (void)updateProgress:(NSNumber*)number
{
NSLog("Progress is now: %#", number);
[progressView setProgress:[number floatValue]];
}
Place a loading bar in the first screen and start your computation-heavy code in another thread after displaying it.