How to preserve the order of method execution in iOS programming? - ios

So currently I am working on a camera app in iOS. In general, when "Capture" button is clicked on the screen, it will do the following:
Display UILabel "Saving.." on the screen
[camManager captureStillImage] //capturing the image
Remove UILabel "Saving.." from the screen
The problem was, the "Saving.." label never appear on the screen. But, when I remove step 3, the label will actually appear on the screen, but after capturing the image.
So based on my understanding, this was caused either because step 2 was executed too fast or by multithreading such that these steps are not guaranteed to execute in the order as I wrote them. Is this correct?
If so, how can I guarantee that this label appear right before capturing and disappear immediately after capturing?
Code
- (IBAction)captureImage:(id)sender {
[self showLabel];
[manager captureMultipleImg];
[self hideLabel];
}

You're blocking the main thread.
The main thread is responsible for UI stuff. When you're doing a long operation like [manager captureMultipleImg]; probably is, the UI will not get updated. You need to use multi-threading in cases like this.
You can use GCD here:
- (IBAction)captureImage:(id)sender {
[self showLabel];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
[manager captureMultipleImg];
// Dispatch back on main for UI stuff
dispatch_async(dispatch_get_main_queue(), ^{
[self hideLabel];
});
});
}

Related

iOS UI elements won't update while in the method called

I'm doing a pretty simple iOS/ObjC program. Click a button, it's loops thru a for..next and displays counters and pics.
for (int i=0; i <= numberOfExercises; i++) {
exerciseName = [exercises objectAtIndex:j][0];
[self.lastLabel setText:exerciseName];
[self.lastLabel setNeedsDisplay];
usleep(1000000);
NSLog(#"Count: %d Name:%#", i, exerciseName);
}
However, it's not updating the actual textfield on the screen.
I've tried everything I know of and there's just something I can't see.
(IBAction)btnExerciseClicked:(id)sender {
for (int i=0; i <= numberOfExercises; i++) {
exerciseName = [exercises objectAtIndex:j][0];
exerciseImageName = [exercises objectAtIndex:j][1];
exerciseImageName = [exerciseImageName stringByAppendingString:#".jpg"];
self.exerciseImage.image = [UIImage imageNamed:exerciseImageName];
[self.ExerciseName setText:exerciseName];
[self.ExerciseName setNeedsDisplay];
[self.exerciseImage setNeedsDisplay];
}
I've put the block in there:
double delayInSeconds = 0.1;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
NSLog(#"inside the block");
[self.ExerciseName setText:exerciseName];
[self.ExerciseName setNeedsDisplay];
[self.exerciseImage setNeedsDisplay];
});
And I've done a direct assign before the loop starts:
exerciseName = [exercises objectAtIndex:5][0];
[self.ExerciseName setText:exerciseName];
[self.ExerciseName setNeedsDisplay];
NSLog(#"Name: %#", self.ExerciseName.text);
I've even written a separate method for it:
-(void) myCycleDisplay: (NSString *) imageName
nameOfExercise: (NSString *) exerciseName
voiceOver: (BOOL) useVoiceOver
countDown: (BOOL) useCountDown
beep: (BOOL) useBeep{
[self.ExerciseName setText:exerciseName];
[self.ExerciseName setNeedsDisplay];
self.exerciseImage.image = [UIImage imageNamed:imageName];
}
And after all that, STILL no display until AFTER the method is done. Put in a delay: usleep(1000000);
I've confirmed that the data is in the element. I've setup the element in code and in IB.
The app is pretty simple, a button is pressed on the screen, data is loaded, an array is walked and display items are updated based on elements in the array.
The data is in the textfield, this has been confirmed. It even display them, but only after the method is exited.
Once the method is exited, the text/pic are displayed properly.
So, as a test, I made the for..next loop run twice and clicked the button over and over. Sure enough it displayed properly AFTER leaving the method.
I can't get it to update the display while IN the method. (this also includes the slider).
Why do I have to exit a method to get the display to update?
I think the easiest solution to your problem is to dump the whole for-loop into another queue.
dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
for (int i=0; i <= numberOfExercises; i++) {
NSString *exerciseName = [exercises objectAtIndex:i][0];
dispatch_async(dispatch_get_main_queue(), ^{
[self.lastLabel setText:exerciseName];
[self.lastLabel setNeedsDisplay];
});
usleep(1000000);
NSLog(#"Count: %d Name:%#", i, exerciseName);
}
});
There are three additional changes.
Changed [exercises objectAtIndex:j] to [exercises objectAtIndex:i]
I think this was a mistake on your part
Made exerciseName local to the block.
I could have made the declaration of exerciseName __block, but it's easier to just make the whole thing local.
Wrapped setting the label in dispatch_async(dispatch_get_main_queue(), ^{ … });
UI updates must be made on the main queue.
Note, this is a bad solution. You should rethink your approach entirely. I would move this logic into it's own separate class, then use notifications to get the UI to update.
When you call usleep(1000000) you are blocking the main queue. UI updates happen on the main queue, but you're blocking that queue, so they don't happen. Also, UI updates in general don't happen until you finish the current pass through the event loop-- from the user tapping your button, through your method doing whatever it needs to do, continuing until your method finishes. Then UIKit updates the UI. You need to let your method finish because that's how UIKit works.
I'm not exactly sure what you're trying to do. If you want to update your UI at intervals, look into NSTimer.
I hope I understood the problem well enough to give a relevant answer and I apologize if I didn't. Here's how I see it:
There is a set of exercises that have to be done one after another;
Exercises take certain amount of time;
The name of the exercise and a relevant image are displayed when exercise starts.
The current implementation does precisely that: display is updated and the system sleeps for a certain amount of time before updating the display again.
The problem with that approach is that UIKit frameworks that is commonly used when doing interactive things on iOS devices is what could be called "indirect". For instance, when .text property of a label is updated, text is not drawn, instead, the system is notified that display should be updated. UIKit periodically checks whether updates are requested and if they are, the display is redrawn.
All of this is done on the "main queue" by sequentially executing blocks of code added to it (as a side note, this is to unlike the way Javascript work in a browser). This means that as long as some block of code is executed, everything else of the main queue will not be and the app will stop being interactive and updating display. Thus, the way to use UIKit is by quickly notifying it of necessary changes and finishing the function. Consequently, one never does long calculations or "sleeps" on on the main queue.
In context of this question, this leaves a problem of timed updates: we want to update the UI after some time has expired. There are 2 solution that come to mind:
The "typical" one would to be use the NSTimer class;
If constant updates to the UI are necessary, there is a CADisplayLink class and a convenient [UIScreen displayLinkWithTarget:selector:] method to create one on, say, [UIScreen mainScreen]. What it does is, it calls the selector in time for screen updates (currently, that's about 60 times per second but you can configure it to be called less frequently). It is ideal for things like count-down timers and frequently updating UI elements such as progress bars.
(I've switched to Swift and the latest API so the method names might not be exactly right in Objective-C).
Please let me know if I misunderstood the question or if you need sample code or further clarification.
Good luck!

Stop or completely erase iOS Global Dispatch Queue

So I have read several posts on here about the queueing system but I cannot seem to figure out how to do what I am looking for. Currently I am going to a page and loading images using a loop, and each image uses async dispatch seen here.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
//Load Image Code Goes Here
dispatch_async(dispatch_get_main_queue(), ^{
//Display Image Code Goes Here After Loading.
});
});
And this works perfectly, however I need to be able to destroy this queue or wait until it is finished before doing anything else. Basically certain pages have dozens and dozens of images, so they all start loading, then I go to a totally separate area in the app and do a completely different image loading (1-2 images) and it will take almost a minute because it is still waiting for the other images to load. Is there a way to destroy my old queue or suspend? I have seen a people say "you can but it will corrupt the incoming data" which is fine because the image would just re download upon a new page load. Any ideas?
A slightly different approach is to only dispatch a few images to start and then dispatch another when any of the previous requests finishes. Here's what the code looks like
- (IBAction)startButtonPressed
{
self.nextImageNumber = 1;
for ( int i = 0; i < 2; i++ )
[self performSelectorOnMainThread:#selector(getImage) withObject:nil waitUntilDone:NO];
}
- (void)getImage
{
if ( self.view.window && self.nextImageNumber <= 6 )
{
int n = self.nextImageNumber++;
NSLog( #"Requesting image %d", n );
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"http://images.apple.com/v/iphone-5s/gallery/a/images/download/photo_%d.jpg", n]];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:url]];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog( #"Received image %d", n );
[self updateImage:image forView:n];
[self performSelectorOnMainThread:#selector(getImage) withObject:nil waitUntilDone:NO];
});
});
}
}
The images being downloaded are named "photo_1.jpg" through "photo_6.jpg". The process is started by requesting the first two photos. When one of those finishes, the next request is dispatched, and so on until all 6 photos are retrieved. The key line of code is
if ( self.view.window && self.nextImageNumber <= 6 )
The first part of the if checks whether the view controller is still on the screen. When the user navigates away from the view controller, self.view.window will be set to nil. So navigating away from the view controller breaks the chain of downloads.
The second part of the if checks whether all of the downloads are finished. This is easy to do since the filenames contain a number. For random filenames, you could fill an NSArray with the URLs and then index through the array until you reach the end.
I started the chain with 2 downloads because there are only 6 images to download at that URL. Depending on the image sizes and number of images, you might want to start by dispatching more. The tradeoff is to maximize bandwidth usage (by starting with more) versus minimizing cancellation time (by starting with less).
if you are using an NSURLConnection, you should maybe keep references to them outside of the blocks, then if you move away from the screen call [downloadConnection cancel] on each of them

Image View is Causing Awkward Hang?

I've been working on this for a couple days now (off & on) and I'm not exactly sure why this isn't working, so I'm askin you pros at SOF for some insight.
NewsItem.m
On my first view controller, I'm reading from a JSON feed which has 10+ items. Each item is represented by a NewsItem view which allows for a title, body copy, and a small image. The UIImageView has an IBOutlet called imageView. I'm loading the image for my imageView asynchronously. When the image is loaded, I'm dispatching a notification called IMAGE_LOADED. This notification is only picked up on the the NewsItemArticle
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//this will start the image loading in bg
dispatch_async(concurrentQueue, ^{
NSData *image = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:self.imageURL]];
//this will set the image when loading is finished
dispatch_async(dispatch_get_main_queue(), ^{
[self.imageView setAlpha:0.0];
self.image = [UIImage imageWithData:image];
[self.imageView setImage:self.image];
[UIView animateWithDuration:0.5 animations:^{
[self.imageView setAlpha:1.0];
}];
if(self)
[[NSNotificationCenter defaultCenter] postNotificationName:IMAGE_LOADED object:self];
});
});
NewsItemArticle.m
When a user taps on a NewsItemView then I load a new controller which is a scroll view of several NewsItemArticle views inside a scrollview. A NewsItemArticle will listen for IMAGE_LOADED and if it is decided the current notification has an image for this particular article, it will use the same image for it's own reference like so:
- (void)handleImageLoaded:(NSNotification *)note
{
if([note.object isEqual:self.cell]) {
// this next line is hanging the app. not sure why.
[self.imageView setImage:self.cell.image];
[self.activityViewIndicator removeFromSuperview];
}
}
So essentially:
I'm using an asynchronous load on my first image reference
I'm using notifications to let other parts of the app know and image was loaded
The app hangs when the existing image is reference to a second UIImageView
If I comment out the suspect line, the app never hangs. As it its, my app hangs until all the images are loaded. My thoughts are:
This is a network threading conflict (not likely)
This is a GPU threading conflict (perhaps during a resize to the container view's size?)
Has anyone seen anything like this before?
For lazy loading of table view images there are few good options available. Can make use of them in your design to save time and avoid efforts to reinvent the wheel.
1. Apple lazy loading code --link
2. SDWebImage --link
SDWebImage will provide you a completion handler/block where you can use the notification mechanism to notify other modules of your application.
Cheers!
Amar.

Display output in infinite loop iOS

I am new to iOS and I need some feedback about how I can display the output on Iphone screen in an infinite loop.
When the button is pressed, the application goes in an endless loop and creates some outputs. I would like to display these outputs on Iphone screen. Although I get those values from printf on debug screen, nothing shows on Iphone screen. If I would try this function without the infinite loop, I would be able to see the output on screen, however the application must run in infinite loop.
Since I am quite new to programming, does the application need to be run in multi-thread mode?
The code is below:
-(IBAction)button:(UIButton *)sender{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int output = -1;
while (pool) {
//output is received
if((output != -1) ) {
printf("%i \n", output); // the output is seen on debug screen
self.display.text = [NSString stringWithFormat:#"%i",output]; // the outputs is not shown on iphone
//[pool release];
}
}
}
You're not changing the value of output, so if it enter the loop it will never change.
The app is already multi-threaded, it cannot be otherwise - you're probably talking about background tasks (http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html
Whether you should be on a different thread depends on how controlled the loop is as, any heavy work, or large large numbers should be taken off of the main thread with something similar to:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//My background stuff
dispatch_async(dispatch_get_main_queue(), ^{
//My ui code
});
});
Regarding threading, GCD is quite simple to use: (https://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html) - there are plenty of tutorials around for GCD
I think issue is fast changing text not let us see output.
Try this and tell me know what you are getting.
self.display.text = [NSString stringWithFormat:#"%#%i",self.display.text, output];

UIView created in a GCD global queue odd behavior after added as a subview

I'm creating some UIViews and caching them for reasons that aren't really important to the question at hand.
After I add the view X as a subview to Y one of X's subviews does not appear. If I wait 20-30 seconds it suddenly appears.
Here's how I am creating the views and adding them to the cache. These views are not yet added to the ui, that happens later.
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for(int i = 0; i < 10;i++){
MyUIView *cTemp = [[MyUIView alloc] initWithFrame:CGRectZero];
[self addViewToCahce:cTemp forKey:#"key"];
}
});
but if I remove the dispatch_async it appears as it should. Anyone know what is going on here or how to prevent this unusual behavior?
Never modify UI outside of the main thread. Cocoa, like most UI frameworks, is not multithreaded. Try the following instead:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// Do whatever processing you want to do here
dispatch_async(dispatch_get_main_queue(), ^{
for(int i = 0; i < 10;i++){
MyUIView *cTemp = [[MyUIView alloc] initWithFrame:CGRectZero];
[self addViewToCahce:cTemp forKey:#"key"];
}
});
});
As for why you experience the behavior you describe, I can only speculate. I would not trust the constructor to UIView to be thread safe. If you need to create your views in another thread, I would suggest refactoring the code a bit, if possible.

Resources