Проблема
При рисовании пользовательского контроллера видеоплеера на экране (iOS 4) я использую AVPlayerLayer
для отображения. Я хочу наложить некоторые оверлеи поверх плеера (рекламные баннеры, элементы управления плеером и т. д.). На данный момент это прекрасно работает со следующей иерархией:
UIView(self.view)\ | UIView(controlsView)\ * | UIView [some movie player controls] | UIView [some more movie player controls] *
AVPlayerLayer
является подуровнем self.view
. При загрузке проигрывателя я сначала добавляю слой в self.view
и вызываю bringSubviewToFront:
в controlsView
, а затем обнаруживаю нажатия на него и программно отображаю/затухаю по таймеру/что угодно. Здесь нет проблем.
Теперь, когда у меня есть поддержка динамического получения и отображения рекламного контента, я хотел бы настроить следующую иерархию:
UIView(self.view)\ | UIImageView (ad banner) | UIImageView (another ad banner) | ... | UIView(controlsView)\ * | UIView [some movie player controls] | UIView [some more movie player controls] *
При загрузке игрока я затем присоединяю к моему AVPlayer
экземпляру несколько addBoundaryTimeObserverForTimes:queue:usingBlock:
вызовов, чтобы рекламные баннеры появлялись и исчезали в соответствующее время. Когда я это делаю, я добавляю UIImageView
s к self.view
как подпредставления без явного порядка (поэтому они должны быть нарисованы поверх всего остального) и setHidden:YES
на них.
Я сделал NSLog
, и эти блоки затухания выполняются правильно, устанавливая view.alpha = 1.0f
и view.alpha = 0.0f
для отображения и скрытия соответственно на рекламных баннерах, но вообще ничего не появляется, даже при явной установке UIImage
, отображаемого представлением, на известное рабочее локальное изображение и кадр на (0, 0, 320, 480)
плюс установка autoresizingMask
из
UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleRightMargin;
UIViewContentMode
по умолчанию (должно быть 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:YES
.
setAlpha:0.0f
вместоsetHidden:YES
, поведение идентично (ничего не отображается), а затем, когда я пропускаю вызовsetAlpha:0.0f
, он отображается с самого начала, а затем исчезает в нужное время. - person   schedule 02.06.2011