iOS: Is there a way to determine when an UIView did appear, after calling view.hidden = NO
I have an UIView with 8 to 10 large images (each loaded into an UIImageView) as subviews. Those images are aligned next to each other, so that they compose a real long horizontal image. This view is put into an UIScrollView, so that the user can scroll along this long horizontal image.
Loading all those images (from the web) takes qu开发者_如何转开发ite some time, so I decided to put that loading code into a NSOperation subclass. Inside the operation's main method each ImageView gets added to the container view.
The view is hidden during this process and instead there is a small rotating image (pretty much like an UIActivityIndicatorView). Here is some code:
CategoryViewController.m
- (id)initWithFrame:(CGRect)frame
{
self = [super init];
if(self)
{
// do some unrelated stuff
[...]
loadIndicator = [[UIImageView alloc] initWithImage:img];
[loadIndicator setFrame:CGRectMake((frame.size.width - width) / 2, (frame.size.height - height) / 2, width, height)];
[loadIndicator setHidden:YES];
// this is the view which will contain those large images
backgroundView = [[UIView alloc] init];
backgroundView.hidden = YES;
scrollView = [[UIScrollView alloc] initWithFrame:frame];
[scrollView setDelegate:(id<UIScrollViewDelegate>)self];
[scrollView addSubview:backgroundView];
[self.view setFrame:frame];
}
return self;
}
- (void)viewDidLoad
{
// do some unrelated stuff
[...]
[self.view addSubview:loadIndicator];
[self.view addSubview:scrollView];
// setup a custom activity indicator
loadIndicator.hidden = NO;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
loadIndicatorRotation = 0;
rotationTimer = [NSTimer scheduledTimerWithTimeInterval: 0.01 target: self selector:@selector(updateRotation:) userInfo: nil repeats: YES];
[UIView commitAnimations];
// setup the operation to load all background images
operationQueue = [[NSOperationQueue alloc] init];
LoadBackgroundImagesOperation* operation = [[LoadBackgroundImagesOperation alloc] initWithImages:bgImageNames View:backgroundView Frame:frame];
// register to recieve the finish notification
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onFinishedLoadingBgImages) name:@"BgImagesFinishedLoading" object:operation];
// Add the operation to the queue
[operationQueue addOperation:operation];
[operation release];
[super viewDidLoad];
}
main method of LoadBackgroundImagesOperation.m
- (void)main
{
int left = 0;
int width, height;
int i;
for(i = 0; i < imageNames.count; i++)
{
if([self isCancelled])
{
[[NSNotificationCenter defaultCenter] postNotificationName:@"BgImagesCancelled" object:self];
return;
}
// code to retrieve the url of current image
[...]
NSData* data = [[NSData alloc] initWithContentsOfURL:imgURL];
UIImage* image = [[UIImage alloc] initWithData:data];
[data release];
double scalingFactor = frame.size.height / image.size.height;
width = image.size.width * scalingFactor;
height = image.size.height * scalingFactor;
UIImageView* imgView = [[UIImageView alloc] initWithImage:image];
[image release];
[imgView setFrame:CGRectMake(left, 0, width, height)];
[background addSubview:imgView];
[imgView release];
left += width;
[background setFrame:CGRectMake(0, 0, left, height)];
}
[[NSNotificationCenter defaultCenter] postNotificationName:@"BgImagesFinishedLoading" object:self];
}
When the operation is finished I hide the rotating image and show the view, which contains the large images.
(back in CategoryViewController.m again)
- (void) onFinishedLoadingBgImages
{
[scrollView setContentSize:backgroundView.frame.size];
backgroundView.hidden = NO;
loadIndicator.hidden = YES;
if(operationQueue != nil)
{
[operationQueue cancelAllOperations];
[operationQueue release];
operationQueue = nil;
}
[rotationTimer invalidate];
rotationTimer = nil;
}
The problem is that it takes around 2 seconds for the simulator and up to 5 seconds on the real device untill the images are actually visible after calling backgroundView.hidden = NO
I would just continue showing the activity indicator during that time, but I don't know how to detect that backgroundview
did appear in order to hide the indicator again.
I tried to put backgroundview
in its own UIViewController and notify my parent view controller when viewDidAppear is called, but that doesn't work. (I read that in nested view controllers I would have to call viewDidAppear manually, but that is not an option, because i don't know when it did appear in the first place).
Also, I can't just do the dirty trick and change the order of backgroundView
and loadIndicator
to hide it, because most of the images have some transparency in them, so the user would still see the indicator.
So I really hope, that any of you guys have a suggestion on how to detect when backgroundview
is actually visible.
Thanks, Chris
Oh, and sorry if some of the code is somewhat unrelated to the problem. Just wanted to give you the big picture of what I am doing.
What you could do is make a UIViewController subclass, and make that the owner of this view, and in that method you could try putting code you need in the viewDidLoad method and see if that is more accurate, however i think that the real issue is the imageViews you use are taking their time loading the images, not the UIView. Therefore, the UIView is visible, however the imageViews have not loaded their images yet so you don't see anything.
If you're showing a lot of large images in a scroll view, you should really consider tiling them so that parts of the images can be loaded separately and you don't have to keep several large images in memory all at the same time. Take a look at CATiledLayer and the PhotoScroller sample code.
精彩评论