UIView显示顺序问题?

发布于 2024-11-11 18:07:56 字数 5300 浏览 3 评论 0原文

问题

在屏幕上绘制自定义电影播放器​​控制器 (iOS 4) 时,我使用 AVPlayerLayer 进行显示。我希望在播放器顶部合成一些叠加层(广告横幅、播放器控件等)。目前,这对于以下层次结构绝对可以正常工作:

UIView(self.view)\
                 | UIView(controlsView)\
                 *                     | UIView [some movie player controls]
                                       | UIView [some more movie player controls]
                                       *

AVPlayerLayerself.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.0fview.alpha = 0.0f分别在广告横幅上显示和隐藏,但即使将视图显示的 UIImage 显式设置为已知的工作本地图像并将框架设置为 (0, 0, 320, 480),并设置 UIViewContentModeautoresizingMask

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:videoViewbringSubviewToFront: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 UIImageViews 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

绻影浮沉 2024-11-18 18:07:56

请注意,我现在使用 0.001f 而不是 0.0f,并且没有显示问题。我仍然很想弄清楚它为什么这样做。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文