Flashing UILabel as a - iphone

I'm trying to use a UIImage as a button which gives the impression of it being turned on then off again within about half a second. This works fine if I switch it on but if I want to switch it off again it doesn't switch on at all. I have a short loop in there to prevent it switching on and off so fast I can't see it but it doesn't switch on at all. I've tried it with and without the [flashingButton release]. Is there something I'm misunderstanding here? Can I addSubview and removeFromSuperView at the same time even with a short delay?
if ( some conditional statements in here .......) {
UIImage *estimateButton1 = [UIImage imageNamed:#"FlashingButton.png"];
flashingButton = [[UIImageView alloc] initWithImage:flashingButton1];
flashingButton.frame = CGRectMake (146,8,165,30);
[self.view addSubview:flashingButton];
// [flashingButton release];
// short loop in here to delay urning the button off
[self.flashingButton removeFromSuperview];

User interface drawing doesn't happen until later in the main run loop. Your call to addSubview adds flashingButton to self.view but doesn't draw it. Your short loop blocks the main run loop, so it still doesn't get to the drawing part. And then, you remove the button before the main run loop gets to draw it.
A solution is to let the main run loop continue after you've added the flashing button (so it will get drawn), but create a timer that will remove that button in the future. You can use performSelector:withObject:afterDelay: to do this.
[self.flashingButton performSelector:#selector(removeFromSuperview) withObject:nil afterDelay:0.5f];
You can read about run loops in "Threading Programming Guide" and about how drawing gets done in "View Programming Guide for iOS."

Looping within the main thread will just hang the program temporarily and prevent any drawing from taking place. Instead, use an NSTimer.

Related

Delay between viewDidLoad and viewDidAppear with animated UIImageView

I have a controller that uses an animated UIImageView to display a sequence of 30 512 x 512 frames. When I run the application the view quickly does the following.
- (void)viewDidLoad {
[super viewDidLoad];
[[self imageView] setAnimationImages:[[self dataModel] framesForLOOP]];
[[self imageView] setAnimationDuration:2.5];
[[self imageView] setAnimationRepeatCount:1];
[[self imageView] startAnimating];
NSLog(#"MARKER_001");
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(#"MARKER_002");
}
This all works fine but what I am trying to work out is that after viewDidLoad: is called there is a 2 second delay before viewDidAppear: is called (between MARKER_001 and MARKER_002).
I was thinking there might be a delay setting up the frames NSArray or after calling setAnimationImages: or maybe after startAnimating.
Is there anyway to reduce/remove this delay, I would prefer to preload the animation at startup and take a hit there rather than having the delay when the viewController loads as it makes the button that fires the segue to instantiate the new controller feel laggy.
Just a few ideas:
reduce the pain - do you need 30x512x512?
distribute the pain - load the first image on viewWillAppear, kick off an operation to load the others and update the animation images as new images are ready (can supply code e.g. if needed)
move the pain - prepare the array of UIImages in app init.
dig deeper - let's have a look at the framesForLoop method, maybe there's some more opportunity to reduce/distribute/move the pain in there.
Note that even if you call startAnimating in viewDidLoad, the animation won't start there. You can't get an animation running in a controller which has not been not displayed yet.
You should call the startAnimating in viewDidAppear instead.
Two seconds delay between these two methods is not anything strange. The delay can be much longer or shorter, depending on what happens inside your application.
Measuring the time between two methods which are not connected doesn't make sense. What about measuring how much time the individual methods take?
If anything is laggy, you should probably paste all the code that happens between the user action and the moment when everything is displayed and the lag happens.

Total mystery - iOS methods execute after random delay

I have the variant of the following codes, triggered by a user input.
NSLog(#"WHY YOU iOS!!!");
[scanButton setSelected:YES];
[overlayImageView setImage:image];
[overlayView setHidden:YES];
The thing is that the above routine executes with seemingly random delay from five seconds to more than a minute.
Sometimes, they don't execute at all, except, mysteriously, the NSLog method. If I set a breakpoint at the NSLog line, it just executes immediately and I can see "WHY YOU ..." printed out in the console. However, even though the debugger runs smoothly past the below three lines without errors or warnings, the effect of the the next three lines takes place after random delay, or infinite delay. What's wrong? How could it be possible that they have random delay?
I suspected that UI part of Cocoa SDK suspends due to my poor program design or whatever, but even during the random delay, other buttons and functionalities work so fine, while the effect of setImage takes place suddenly in 47.2 seconds. What's wrong with iOS, or with me?
What confounds me more is that the above exact routine works fine without delay if the image parameter (UIImage*) comes from a different source. If that's the case, we can attribute the cause of the problem to the image, however, why do scanButton and overlayView also respond with the same delay, even though they have nothing to do with the image? Total mystery to me.
I have no idea on whats going on, as there is almost no hint in the code that you've provided, but why you dont try something like this:
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"WHY YOU iOS!!!");
[scanButton setSelected:YES];
[overlayImageView setImage:image];
[overlayView setHidden:YES];
[overlayImageView setNeedsDisplay];
[overlayImageView setNeedsLayout];
[scanButton setNeedsDisplay];
[scanButton setNeedsLayout];
[overlayView setNeedsDisplay];
[overlayView setNeedsLayout];
});
Basically ensure always to update de UI in the main thread (in case that you're using a background thread) and set the UI controls as needed to be redrawn.

How to know/test when an animation has finished playing

I am starting an animation via a button press. Here is the code for the button:
-(IBAction)startAnimation:(id)sender{
stick.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"photo 1.JPG"],
[UIImage imageNamed:#"photo 2.JPG"],
[UIImage imageNamed:#"photo 3.JPG"],
[UIImage imageNamed:#"photo 4.JPG"],
[UIImage imageNamed:#"photo 5.JPG"],nil];
[stick setAnimationRepeatCount:200];
stick.animationDuration = 0.5;
[stick startAnimating];
}
and when the animation is done, i want to have a button appear, to play another animation on the screen. How can i test or see when my animation is done playing? Thanks in advance!
You haven't told us what stick is, but it looks like a UIImageView. You can only call isAnimating to check whether the animation is still running but you don't get any notification and there's no delegate either. You can calculate the stop time (200 * 0.5) and thus set up a timer (add a little safety margin). This won't be 100% correct but it might be "good enough".
If you called your animation using blocks, you could use a completion block.
Here is a tutorial you can refer to which shows a couple different ways to call animation routines on iOS. This tutorial also shows what you can do for completion if you don't feel like using blocks (namely UIView's setAnimationDidStopSelector method).

UIImageView only displaying intermittently

I'm using the following code to display a page number, when the user switches pages in my iPad app. The number is supposed to show up in a nice transparent gray pane (similar to the "Build Succeeded" message in XCode). However, the image view only shows up about 5% of the time. (I haven't added the code to put a number in the pane yet). The NSLog() message appears every time. I've also set a breakpoint and stepped through, and the UIImageView code is getting called. Sadly, I can't step into the framework to see what it's doing.
The variables used are all locals or constants, so I doubt it has anything to do with a race condition. Also, I can wait minutes between clicks and not get an image, or press the button rapidly and get multiple stacked images (they're transparent, so it's easy to notice a stack).
I tried running a setNeedsDisplay on self.view after the addSubview: (even though I'm pretty sure addSubview: does it), but that didn't do anything.
Calling displayPageNumber: directly, without threading, doesn't make it appear consistently either.
Any ideas?
#define PageDisplayTime 0.5
#define PageDisplayImageName #"PageIndicator.png"
#define PageDisplayImage [UIImage imageNamed: PageDisplayImageName]
...
[NSThread detachNewThreadSelector: #selector(displayPageNumber:)
toTarget: self withObject: index];
...
- (void) displayPageNumber: (NSNumber*) _pageIndex
{
NSLog(#"Page Number: '%d'.", [_pageIndex integerValue] + 1);
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
UIImageView* glassPaneView = [[UIImageView alloc] initWithImage: PageDisplayImage];
glassPaneView.center = CGPointMake(300.0, 300.0);
[self.view addSubview: glassPaneView];
[NSThread sleepForTimeInterval: PageDisplayTime];
[glassPaneView removeFromSuperview];
[glassPaneView release];
[pool release];
}
AFAIK all UI coding must be done on the main thread. doing it on the background thread will at best have unpredictable results, at worse will crash.
Calling the method directly (thus in the main thread) doesn't work because the sleep just stops the current thread, so it is not able to do anything including drawing until you remove the view, thus preventing it from showing.
You have to refactor the method into multiple methods called on the main thread. The first just adds the subview and sets a timer, the timer handling method then removes the subview.
I figured out the issue. In case others run into this, here it is:
The addSubview: / removeFromSuperview pair with the sleepForTimeInterval: between them are basically useless. I knew sleeping blocked the thread, but apparently this thread needs to be unblocked to update the view. I assumed (incorrectly it appears) that the view update happened on the main thread.
Here's my replacement solution:
unhide the subview in the main thread and call detachNewThreadSelector: with a message that sleeps for X seconds and then hides the subview.
And I'm going to add an NSLocked counter that gets incremented on unhide, and decremented in the hider message, but only hides when the counter is down to 0. This will allow repeated activations to extend the duration.

UIImageView causes lag first time they are displayed

I have a couple of UIViews with UIImageViews on them. When I display a view the first time, there is a noticeable lag (0.5 second or so) before it is shown. When shown again everything works.
I have managed to work around this by adding all views and then removing them. Like this:
[self performSelectorOnMainThread:#selector(addAndRemoveAllViews)
withObject:nil
waitUntilDone:NO];
The function loops over all views and [addSubview:view] followed by [view removeFromSuperview]. This seems to trigger the images to load while not being shown.
Questions:
This all feels like a workaround to me. Is there a better approach to handle these types of things?
Is it possible to get a callback or something when it's completed? I want to run an animation when done.
Update:
Images are created from NSData like this:
[[UIImageView alloc] initWithImage:[UIImage imageWithData:rawData]];
It seems very likely the image is larger than the image view. If possible shrink it down to the size the image view needs.
The initial delay is loading the image from storage, after that you are using a cached version which is why it is faster.