UIView display order issue?
The problem
When drawing a custom movie player controller on-screen (iOS 4), I use an AVPlayerLayer
to display. I wish to composite some overlays on top of the player (ad banners, player controls, etc). At the moment, this works absolutely fine with the following hierarchy:
UIView(self.view)\ | UIView(controlsView)\ * | UIView [some movie player controls] | UIView [some more movie player controls] *
The AVPlayerLayer
is a sublayer of self.view
. On player load, I add the layer to self.view
first and call bringSubviewToFront:
on the controlsView
, and then detect taps on it and programmatically show/fadeout on a timer/whatever. No issue here.
Now that I have support for dynamically fetching and displaying advertising content, I would like to set up the following hierarchy:
UIView(self.view)\ | UIImageView (ad banner) | UIImageView (another ad banner) | ... | UIView(controlsView)\ * | UIView [some movie player controls] | UIView [some more movie player controls] *
At player load, what I then do is attach to my AVPlayer
instance some addBoundaryTimeObserverForTimes:queue:usingBlock:
calls to fade the ad banners in and out at appropriate times. As I do this, I add the UIImageView
s to self.view
as subviews with no explicit ordering (hence they should be drawn on top of everything else), and setHidden:YES
on them.
I've NSLog
'd and these fade blocks are correctly being executed, setting view.alpha = 1.0f
and view.alpha = 0.0f
for show and hide respectively on the ad banners, but nothing is appearing at all, even when explicitly setting the UIImage
displayed by the view to a known working local image and the frame to (0, 0, 320, 480)
, plus setting an autoresizingMask
of
UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleRightMargin;
The UIViewContentMode
is default (should be UIViewContentModeScaleToFill
).
The 'solution' I've gone with 开发者_JAVA技巧for the problem
This doesn't actually address the issue at all, but I'm now using 0.1% opacity as a replacement for 0% opacity. Effectively hides the element without causing issues, and fadeins/fadeouts are working well.
Troubleshooting
To make sure I wasn't drawing the movie over the top of all non-controls views, I moved the AVPlayerLayer
to an additional view, videoView
:
UIView(self.view)\ | UIView(videoView) | UIImageView (ad banner) | UIImageView (another ad banner) | ... | UIView(controlsView)\ * | UIView [some movie player controls] | UIView [some more movie player controls] *
I explicitly call sendSubviewToBack:videoView
and bringSubviewToFront:controlsView
, but still no love. I can see the overlay ads on-screen if I don't call setHidden:YES
when I first add them to the view hierarchy. The kicker is this: the ad also disappears at the correct time (in response to overlayImage.alpha = 0.0f
in my animation blocks), so I know my animation blocks are fine.
Here is a code snippet to do with adding the ad banners to my movie player's view:
for(MyCustomAdBannerClass *overlay in overlays)
{
UIImageView *overlayImage =
[[UIImageView alloc] initWithImage:
[UIImage initWithData:
[NSData dataWithContentsOfURL:[overlay contentURL]]]];
UIViewAutoresizing autoresizingFlags =
UIViewAutoresizingNone;
CGFloat x, y, width, height;
width = [overlay width];
height = [overlay height];
// <snip> set x, y, autoresizing values of the above vars
[overlayImage setFrame:CGRectMake(x, y, width, height)];
[overlayImage setAutoresizingMask:autoresizingFlags];
[self.view addSubview:overlayImage];
}
for(UIView *subview in self.view.subviews)
{
if(subview == controlsView)
[self.view bringSubviewToFront:subview];
else if(subview == videoView)
[self.view sendSubviewToBack:subview];
else
[subview setHidden:YES];
}
Here is the code I use for adding the overlay appear/disappear callbacks (overlay
is an object with information about when to show the overlay and how long for, overlayImage
is the UIImageView
containing the actual ad banner):
[self.player
addBoundaryTimeObserverForTimes:[NSArray arrayWithObject:
[overlay startTime]]
queue:dispatch_get_main_queue()
usingBlock:^{
[UIView
animateWithDuration:0.7
animations:^{
overlayImage.alpha = 1.0f;
dispatch_time_t popTime =
dispatch_time(DISPATCH_TIME_NOW,
[overlay duration] * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(),
^(void){
[UIView
animateWithDuration:0.7
animations:^{
overlayImage.alpha = 0.0f;
[overlayImage removeFromSuperview];
}];
}];
}];
}];
As I said, that overlayImage.alpha = 0.0f;
call is being executed correctly.
NOTE: Just tried setting overlay.alpha = 0.5f;
instead of 0.0f
when the views are first added. The overlay FADES IN properly now. So there is something funky going on with making the view 100% invisible which prevents it from being shown again at a later date. No crashes, so it's not just referencing dealloced memory.
Attempted workaround
I tried sending all the ad overlays behind videoView
and then when each one was to be shown I called
[overlayImage removeFromSuperview];
[self.view insertSubview:overlayImage aboveView:videoView];
// dispatch hide animation
Still doesn't show them, but still fades properly when I omit setHidden:YES
.
Note that I am now using 0.001f
instead of 0.0f
and there are no display issues. I'm still keen to figure out why it does this though.
精彩评论