Полноэкранный режим: размер AVPlayerLayer не изменяется при изменении размера родительского представления. - PullRequest
15 голосов
/ 29 февраля 2012

Я занимаюсь разработкой видеоплеера с API AVPlayer от AV Foundation в MonoTouch (но решение в target-c тоже может быть неплохим).Я пытаюсь реализовать полноэкранный режим.

Для отображения видеокадров у меня есть UIView (назовем его представлением воспроизведения), где я добавил AVPlayerLayer в качестве подпредставления слоя представления воспроизведения:

UIView *playbackView = ...
AVPlayerLayer *playerLayer = ...
[playbackView.layer addSublayer:playerLayer];

свойства слоя установленынапример:

playerLayer.needsDisplayOnBoundsChange = YES;
playbackView.layer.needsDisplayOnBoundsChange = YES;

, чтобы быть уверенным, что они изменятся в размере при изменении размера просмотра.Первоначально представление воспроизведения имеет координаты (0, 0, 320, 180) (отображаются только в верхней части пользовательского интерфейса).Затем я делаю полноэкранную анимацию, устанавливая размер кадра просмотра при просмотре как размер окна:

playbackView.frame = playbackView.window.bounds;

Это работает нормально.Представление воспроизведения теперь заполняет все окно (установите цвет фона, чтобы увидеть его).Но мой AVPlayerLayer по-прежнему остается в верхней части представления, как ранее в не полноэкранном режиме.

Почему слой AVPlayer не изменяется в соответствии с новым размером представления воспроизведения?Нужно ли создавать анимацию на AVPlayerLayer?Обновление с setNeedsLayout, layoutSubviews (кажется, что-то не меняет ...)?Удалить AVPlayerLayer и добавить его снова?

Ответы [ 3 ]

27 голосов
/ 10 апреля 2012

Не добавлять в качестве подслоя.Просто используйте слой PlayView в качестве AVPlayerLayer, например:

+ (Class)layerClass {
    return [AVPlayerLayer class];
}
- (AVPlayer*)player {
    return [(AVPlayerLayer *)[self layer] player];
}
- (void)setPlayer:(AVPlayer *)player {
    [(AVPlayerLayer *)[self layer] setPlayer:player];
}

См. Руководство по программированию AV Foundation - Вид проигрывателя

6 голосов
/ 22 апреля 2015

Если вы добавляете AVPlayerLayer в качестве подслоя, вам необходимо обновить его фрейм

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    self.avPlayerLayer.frame = self.movieContainerView.bounds;
}
0 голосов
/ 14 марта 2017

Есть лучший способ добиться этого. Вот AVPlayerLayer -обратное представление, которое пытается сохранить соотношение сторон видео. Просто используйте AutoLayout как обычно.

PlayerView.h:

@interface PlayerView : UIView

@property (strong, nonatomic) AVPlayerLayer *layer;

@end

PlayerView.m:

@interface PlayerView ()

@property (strong, nonatomic) NSLayoutConstraint *aspectConstraint;

@end

@implementation PlayerView

@dynamic layer;

+ (Class)layerClass {
    return [AVPlayerLayer class];
}

- (instancetype)init {
    self = [super init];
    if (self) {
        self.userInteractionEnabled = NO;

        [self addObserver:self forKeyPath:@"layer.videoRect" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil];
    }
    return self;
}

- (void)dealloc {
    [self removeObserver:self forKeyPath:@"layer.videoRect"];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"layer.videoRect"]) {
        CGSize size = [change[NSKeyValueChangeNewKey] CGRectValue].size;
        if (change[NSKeyValueChangeNewKey] && size.width && size.height) {
            self.aspectConstraint.active = NO;
            self.aspectConstraint = [self.widthAnchor constraintEqualToAnchor:self.heightAnchor multiplier:size.width / size.height];
            self.aspectConstraint.priority = UILayoutPriorityDefaultHigh;
            self.aspectConstraint.active = YES;
        }
        else {
            self.aspectConstraint.active = NO;
        }
    }
}

@end

И то же решение на Swift:

import UIKit
import AVFoundation

class PlayerView : UIView {

    override var layer: AVPlayerLayer {
        return super.layer as! AVPlayerLayer
    }

    override class var layerClass: AnyClass {
        return AVPlayerLayer.self
    }

    var aspectConstraint: NSLayoutConstraint?

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.isUserInteractionEnabled = false
        self.addObserver(self, forKeyPath:"layer.videoRect", options:[.initial, .new], context:nil)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.isUserInteractionEnabled = false
        self.addObserver(self, forKeyPath:"layer.videoRect", options:[.initial, .new], context:nil)
    }

    deinit {
        self.removeObserver(self, forKeyPath: "layer.videoRect")
    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if (keyPath == "layer.videoRect") {
            if let rect = change?[.newKey] as? NSValue {
                let size = rect.cgRectValue.size
                if (size.width > 0 && size.height > 0) {
                    self.aspectConstraint?.isActive = false
                    self.aspectConstraint = self.widthAnchor.constraint(equalTo: self.heightAnchor, multiplier:size.width / size.height)
                    self.aspectConstraint?.priority = UILayoutPriorityDefaultHigh
                    self.aspectConstraint?.isActive = true
                }
                else {
                    self.aspectConstraint?.isActive = false
                }
            }
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...