@@ -48,51 +48,75 @@ public final class GreedyPlayerView: UIView {
4848
4949 // MARK: - Properties
5050
51- private lazy var renderer = VideoRenderActor ( )
51+ typealias DisplayLinkFactory = (
52+ _ target: Any ,
53+ _ selector: Selector
54+ ) -> DisplayLinkProtocol
5255
53- private let renderView = BackedRenderView ( )
54- private var playerItemObserver : AnyCancellable ?
56+ private var displayLink : DisplayLinkProtocol ?
57+ private let renderer : VideoRendererProtocol
58+ private let renderView : RenderViewProtocol
5559
56- private lazy var displayLink = CADisplayLink (
57- target: self ,
58- selector: #selector( displayLinkDidRefresh ( link: ) )
59- )
60+ private let displayLinkFactory : DisplayLinkFactory
61+ private var playerItemObserver : AnyCancellable ?
6062
6163 // MARK: - Lifecycle
6264
63- public override init ( frame: CGRect ) {
65+ init (
66+ frame: CGRect = . zero,
67+ renderer: VideoRendererProtocol ,
68+ renderView: RenderViewProtocol ,
69+ displayLinkFactory: @escaping DisplayLinkFactory
70+ ) {
71+ self . renderer = renderer
72+ self . renderView = renderView
73+ self . displayLinkFactory = displayLinkFactory
6474 super. init ( frame: frame)
75+
6576 renderView. configure ( in: self )
6677 }
6778
79+ public override convenience init ( frame: CGRect ) {
80+ self . init (
81+ frame: frame,
82+ renderer: VideoRenderer ( ) ,
83+ renderView: BackedRenderView ( ) ,
84+ displayLinkFactory: { target, selector in
85+ CADisplayLink ( target: target, selector: selector)
86+ }
87+ )
88+ }
89+
6890 required init ? ( coder: NSCoder ) {
6991 fatalError ( " init(coder:) has not been implemented " )
7092 }
7193
72- public override func willMove( toSuperview newSuperview: UIView ? ) {
73- if newSuperview == nil {
74- dismantle ( )
75- }
76- }
94+ public override func didMoveToWindow( ) {
95+ super. didMoveToWindow ( )
7796
78- public override func didMoveToSuperview( ) {
79- super. didMoveToSuperview ( )
80- initializeDisplayLink ( )
97+ if window == nil {
98+ pauseRendering ( )
99+ } else {
100+ resumeRendering ( )
101+ }
81102 }
82103
83104 // MARK: - Private Methods
84105
85- private func dismantle( ) {
86- displayLink. invalidate ( )
87- playerItemObserver? . cancel ( )
106+ private func resumeRendering( ) {
107+ if displayLink == nil {
108+ displayLink = displayLinkFactory ( self , #selector( displayLinkDidRefresh) )
109+ displayLink? . add ( to: . current, forMode: . common)
110+ }
111+ displayLink? . isPaused = ( player? . currentItem == nil )
88112 }
89113
90- private func initializeDisplayLink ( ) {
91- displayLink. add ( to : . current , forMode : . common )
92- displayLink. isPaused = true
114+ private func pauseRendering ( ) {
115+ displayLink? . invalidate ( )
116+ displayLink = nil
93117 }
94118
95- @objc private func displayLinkDidRefresh( link : CADisplayLink ) {
119+ @objc private func displayLinkDidRefresh( ) {
96120 guard let player else { return }
97121 let itemTime = player. currentTime ( )
98122
@@ -104,17 +128,24 @@ public final class GreedyPlayerView: UIView {
104128 }
105129 }
106130
131+ private func attachAndResumeIfNeeded( _ item: AVPlayerItem ) async {
132+ await renderer. attach ( to: item)
133+ if window != nil {
134+ displayLink? . isPaused = false
135+ }
136+ }
137+
107138 private func addPlayerItemObserver( ) {
108- guard let player else { return }
139+ guard let player else {
140+ displayLink? . isPaused = true
141+ return
142+ }
109143
110144 playerItemObserver = player. publisher ( for: \. currentItem)
111145 . compactMap { $0 }
112146 . sink { [ weak self] item in
113147 guard let self else { return }
114- Task {
115- await self . renderer. attach ( to: item)
116- self . displayLink. isPaused = false
117- }
148+ Task { await attachAndResumeIfNeeded ( item) }
118149 }
119150 }
120151}
0 commit comments