UIView显示顺序问题?
问题
在屏幕上绘制自定义电影播放器控制器 (iOS 4) 时,我使用 AVPlayerLayer
进行显示。我希望在播放器顶部合成一些叠加层(广告横幅、播放器控件等)。目前,这对于以下层次结构绝对可以正常工作:
UIView(self.view)\ | UIView(controlsView)\ * | UIView [some movie player controls] | UIView [some more movie player controls] *
AVPlayerLayer
是 self.view
的子层。在播放器加载时,我首先将图层添加到 self.view
并在 controlsView
上调用 bringSubviewToFront:
,然后以编程方式检测对其的点击在计时器上显示/淡出/其他。这里没有问题。
现在我支持动态获取和显示广告内容,我想设置以下层次结构:
UIView(self.view)\ | UIImageView (ad banner) | UIImageView (another ad banner) | ... | UIView(controlsView)\ * | UIView [some movie player controls] | UIView [some more movie player controls] *
在播放器加载时,我要做的是将一些 addBoundaryTimeObserverForTimes 附加到我的
调用以在适当的时间淡入和淡出广告横幅。当我这样做时,我将 AVPlayer
实例: queue:usingBlock:UIImageView
作为子视图添加到 self.view
中,没有明确的顺序(因此它们应该绘制在其他所有内容之上),并且 setHidden:YES
对它们进行设置。
我已经 NSLog
'd 并且这些淡入淡出块正在正确执行,设置 view.alpha = 1.0f
和 view.alpha = 0.0f
分别在广告横幅上显示和隐藏,但即使将视图显示的 UIImage
显式设置为已知的工作本地图像并将框架设置为 (0, 0, 320, 480)
,并设置 UIViewContentMode
的 autoresizingMask
UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleRightMargin;
为默认值(应为 UIViewContentModeScaleToFill
)。
我针对该问题采用的“解决方案”
这实际上根本不能解决问题,但我现在使用 0.1% 不透明度来替代 0% 不透明度。有效隐藏元素而不会引起问题,并且淡入/淡出运行良好。
疑难解答
为了确保我没有在所有非控件视图的顶部绘制影片,我将 AVPlayerLayer
移动到了另一个视图 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] *
我明确调用 sendSubviewToBack:videoView
和 bringSubviewToFront:controlsView
,但仍然没有爱。如果我在第一次将重叠式广告添加到视图层次结构时不调用 setHidden:YES
,我就可以在屏幕上看到重叠式广告。关键是:广告也会在正确的时间消失(响应我的动画块中的 overlayImage.alpha = 0.0f
),所以我知道我的动画块是美好的。
下面是一个将广告横幅添加到我的电影播放器视图中的代码片段:
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];
}
这是我用于添加叠加出现/消失回调的代码(overlay
是一个对象,其中包含有关何时显示的信息叠加层及其持续时间,overlayImage
是包含实际广告横幅的 UIImageView
):
[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];
}];
}];
}];
}];
正如我所说,overlayImage.alpha = 0.0f;
调用正在正确执行。
注意:刚刚尝试在首次添加视图时设置 overlay.alpha = 0.5f;
而不是 0.0f
。叠加层现在可以正确淡入。因此,让视图 100% 不可见会发生一些奇怪的事情,这会阻止它在以后再次显示。没有崩溃,所以它不仅仅是引用已释放的内存。
尝试的解决方法
我尝试将所有广告叠加层发送到videoView
后面,然后当要显示每个广告叠加层时,我调用
[overlayImage removeFromSuperview];
[self.view insertSubview:overlayImage aboveView:videoView];
// dispatch hide animation
仍然不显示它们,但当我省略时仍然会正常淡出setHidden:是
。
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 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
.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
请注意,我现在使用
0.001f
而不是0.0f
,并且没有显示问题。我仍然很想弄清楚它为什么这样做。Note that I am now using
0.001f
instead of0.0f
and there are no display issues. I'm still keen to figure out why it does this though.