2012-02-29 10 views
13

Pracuję nad odtwarzaczem wideo z interfejsem AVPlayer API od AV Foundation w MonoTouch (ale rozwiązanie w obiektyw-c też może być miłe). Próbuję zaimplementować tryb pełnoekranowy.Pełny ekran: AVPlayerLayer nie zmienia rozmiaru podczas zmiany rozmiaru nadrzędnego widoku.

Aby wyświetlić klatki wideo, Mam UIView (nazwijmy go zobaczyć odtwarzanie), gdzie I dodaje AVPlayerLayer jako podrzędny warstwy widoku odtwarzania:

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

właściwości warstw są ustawione tak:

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

dla pewności, że zostaną one zmienione, jeśli zmieni się rozmiar widoku odtwarzania. Początkowo widok odtwarzania ma współrzędne (0, 0, 320, 180) (wyświetlane tylko w górnej części interfejsu użytkownika). Następnie wykonuję animację pełnoekranową, ustawiając rozmiar klatki odtwarzania jako rozmiar okna:

playbackView.frame = playbackView.window.bounds; 

Działa dobrze. Widok odtwarzania wypełnia teraz całe okno (ustaw kolor tła, aby go zobaczyć). Ale moja AVPlayerLayer nadal pozostaje na górze widoku, tak jak poprzednio, w trybie innym niż tryb pełnoekranowy.

Dlaczego warstwa AVPlayer nie jest zmieniana zgodnie z nowym rozmiarem widoku odtwarzania? Czy muszę również zrobić animację na AVPlayerLayer? Odświeżenie za pomocą setNeedsLayout, layoutSubviews (wydaje się, że coś nie zmienia ...)? Usunąć AVPlayerLayer i dodać go ponownie?

+0

Jednym z rozwiązań jest również rozmiar ramki warstwy według ramka odtwarzania podczas animacji (playerLayer.frame = playbackView.frame). Ale jeśli to zrobimy, warstwa nie będzie animowana, ale bezpośrednio wyświetlana z ostatecznym rozmiarem. Jak możemy animować również tę zmianę? Używaj animacji rdzenia zamiast animacji UIView? – nicolas

Odpowiedz

25

Nie dodawaj jako podwarstwa. Wystarczy użyć warstwę playbackView jako AVPlayerLayer, tak:

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

Zobacz AV Foundation Programming Guide - gracz View

+0

Co, jeśli chcesz dodać jako podwarstwę? Na przykład za pomocą widoku hostowanego w warstwie, który zawiera wiele podwarstw, w tym AVPlayerLayer. – Dalmazio

+3

Apple zaktualizował swój przykład AVPlayera za pomocą wersji, która demonstruje, jak zaimplementować to za pomocą metody swift: https://developer.apple.com/library/prerelease/ios/samplecode/AVFoundationSimplePlayer-iOS/Listings/Swift_AVFoundationSimplePlayer_iOS_PlayerView_swift.html#//apple_ref/ doc/uid/TP40016103-Swift_AVFoundationSimplePlayer_iOS_PlayerView_swift-DontLinkElementID_14 – morgman

+0

Każdy pomysł jak to zrobić z NSView?NSView nie ma "layerClass" – iluvcapra

6

W przypadku dodawania AVPlayerLayer jako podwarstwy, musisz zaktualizować jego rama

- (void)viewDidLayoutSubviews { 
    [super viewDidLayoutSubviews]; 

    self.avPlayerLayer.frame = self.movieContainerView.bounds; 
} 
+0

zły pomysł. Ponieważ jest graczem, za każdym razem, gdy czas wskaźnika jest aktualizowany, ta funkcja jest wysłana – jose920405

+0

wow, co za zła odpowiedź, jestem zakorkowana! nie rób tego – mahieddine

+1

Dobra odpowiedź, bardzo mi pomogła – Genevios

0

Jest lepszy sposób na osiągnięcie tego. Oto widok z tyłu, który próbuje zachować proporcje wideo. Po prostu użyj AutoLayout jak zwykle.

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 

I tego samego roztworu na 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 
       } 
      } 
     } 
    } 
} 
Powiązane problemy