瀏覽代碼

Clean up PiP mode for iOS

j8
Daniel Ornelas 7 年之前
父節點
當前提交
fd44721bac

+ 2
- 0
ios/example-pip-app/src/Info.plist 查看文件

@@ -51,6 +51,8 @@
51 51
 		<key>NSAllowsArbitraryLoads</key>
52 52
 		<true/>
53 53
 	</dict>
54
+	<key>UIViewControllerBasedStatusBarAppearance</key>
55
+	<false/>
54 56
 	<key>UISupportedInterfaceOrientations~ipad</key>
55 57
 	<array>
56 58
 		<string>UIInterfaceOrientationPortrait</string>

+ 60
- 6
ios/example-pip-app/src/ViewController.swift 查看文件

@@ -21,19 +21,73 @@ class ViewController: UIViewController {
21 21
 
22 22
     @IBOutlet weak var videoButton: UIButton?
23 23
     
24
-    private var jitsiMeetCoordinator: JitsiMeetPresentationCoordinator?
24
+    fileprivate var pipViewCoordinator: PiPViewCoordinator?
25
+    fileprivate var jitsiMeetView: JitsiMeetView?
25 26
     
26 27
     override func viewDidLoad() {
27 28
         super.viewDidLoad()
28 29
     }
29 30
     
31
+    override func viewWillTransition(to size: CGSize,
32
+                                     with coordinator: UIViewControllerTransitionCoordinator) {
33
+        super.viewWillTransition(to: size, with: coordinator)
34
+        
35
+        let rect = CGRect(origin: CGPoint.zero, size: size)
36
+        pipViewCoordinator?.resetBounds(bounds: rect)
37
+    }
38
+    
30 39
     // MARK: - Actions
31 40
     
32 41
     @IBAction func openJitsiMeet(sender: Any?) {
33
-        let jitsiMeetCoordinator = JitsiMeetPresentationCoordinator()
34
-        self.jitsiMeetCoordinator = jitsiMeetCoordinator
35
-        jitsiMeetCoordinator.jitsiMeetView.welcomePageEnabled = true
36
-        jitsiMeetCoordinator.jitsiMeetView.load(nil)
37
-        jitsiMeetCoordinator.show()
42
+        cleanUp()
43
+        
44
+        // create and configure jitsimeet view
45
+        let jitsiMeetView = JitsiMeetView()
46
+        jitsiMeetView.welcomePageEnabled = true
47
+        jitsiMeetView.pictureInPictureEnabled = true
48
+        jitsiMeetView.load(nil)
49
+        jitsiMeetView.delegate = self
50
+        self.jitsiMeetView = jitsiMeetView
51
+        
52
+        // Enable jitsimeet view to be a view that can be displayed
53
+        // on top of all the things, and let the coordinator to manage
54
+        // the view state and interactions
55
+        pipViewCoordinator = PiPViewCoordinator(withView: jitsiMeetView)
56
+        pipViewCoordinator?.configureAsStickyView(withParentView: view)
57
+        
58
+        // animate in
59
+        jitsiMeetView.alpha = 0
60
+        pipViewCoordinator?.show()
61
+    }
62
+    
63
+    fileprivate func cleanUp() {
64
+        jitsiMeetView?.removeFromSuperview()
65
+        jitsiMeetView = nil
66
+        pipViewCoordinator = nil
67
+    }
68
+}
69
+
70
+extension ViewController: JitsiMeetViewDelegate {
71
+    
72
+    func conferenceFailed(_ data: [AnyHashable : Any]!) {
73
+        hideJitsiMeetViewAndCleanUp()
74
+    }
75
+    
76
+    func conferenceLeft(_ data: [AnyHashable : Any]!) {
77
+        hideJitsiMeetViewAndCleanUp()
78
+    }
79
+    
80
+    func enterPicture(inPicture data: [AnyHashable : Any]!) {
81
+        DispatchQueue.main.async {
82
+            self.pipViewCoordinator?.enterPictureInPicture()
83
+        }
84
+    }
85
+    
86
+    private func hideJitsiMeetViewAndCleanUp() {
87
+        DispatchQueue.main.async {
88
+            self.pipViewCoordinator?.hide() { _ in
89
+                self.cleanUp()
90
+            }
91
+        }
38 92
     }
39 93
 }

+ 4
- 12
ios/sdk/sdk.xcodeproj/project.pbxproj 查看文件

@@ -31,10 +31,8 @@
31 31
 		75635B0B20751D6D00F29C9F /* left.wav in Resources */ = {isa = PBXBuildFile; fileRef = 75635B0920751D6D00F29C9F /* left.wav */; };
32 32
 		C6245F5D2053091D0040BE68 /* image-resize@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C6245F5B2053091D0040BE68 /* image-resize@2x.png */; };
33 33
 		C6245F5E2053091D0040BE68 /* image-resize@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = C6245F5C2053091D0040BE68 /* image-resize@3x.png */; };
34
-		C6A3425F204EF76800E062DD /* PiPWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425C204EF76800E062DD /* PiPWindow.swift */; };
35
-		C6A34260204EF76800E062DD /* JitsiMeetPresentationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425D204EF76800E062DD /* JitsiMeetPresentationCoordinator.swift */; };
36 34
 		C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425E204EF76800E062DD /* DragGestureController.swift */; };
37
-		C6A3426D204F1C3300E062DD /* JitsiMeetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3426C204F1C3300E062DD /* JitsiMeetViewController.swift */; };
35
+		C6CC49AF207412CF000DFA42 /* PiPViewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */; };
38 36
 		C6F99C15204DB63E0001F710 /* JitsiMeetView+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */; };
39 37
 /* End PBXBuildFile section */
40 38
 
@@ -68,10 +66,8 @@
68 66
 		9C77CA3CC919B081F1A52982 /* Pods-JitsiMeet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.release.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.release.xcconfig"; sourceTree = "<group>"; };
69 67
 		C6245F5B2053091D0040BE68 /* image-resize@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "image-resize@2x.png"; path = "src/picture-in-picture/image-resize@2x.png"; sourceTree = "<group>"; };
70 68
 		C6245F5C2053091D0040BE68 /* image-resize@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "image-resize@3x.png"; path = "src/picture-in-picture/image-resize@3x.png"; sourceTree = "<group>"; };
71
-		C6A3425C204EF76800E062DD /* PiPWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PiPWindow.swift; sourceTree = "<group>"; };
72
-		C6A3425D204EF76800E062DD /* JitsiMeetPresentationCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JitsiMeetPresentationCoordinator.swift; sourceTree = "<group>"; };
73 69
 		C6A3425E204EF76800E062DD /* DragGestureController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DragGestureController.swift; sourceTree = "<group>"; };
74
-		C6A3426C204F1C3300E062DD /* JitsiMeetViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JitsiMeetViewController.swift; sourceTree = "<group>"; };
70
+		C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PiPViewCoordinator.swift; sourceTree = "<group>"; };
75 71
 		C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetView+Private.h"; sourceTree = "<group>"; };
76 72
 /* End PBXFileReference section */
77 73
 
@@ -171,9 +167,7 @@
171 167
 			isa = PBXGroup;
172 168
 			children = (
173 169
 				C6A3425E204EF76800E062DD /* DragGestureController.swift */,
174
-				C6A3425D204EF76800E062DD /* JitsiMeetPresentationCoordinator.swift */,
175
-				C6A3426C204F1C3300E062DD /* JitsiMeetViewController.swift */,
176
-				C6A3425C204EF76800E062DD /* PiPWindow.swift */,
170
+				C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */,
177 171
 			);
178 172
 			path = "picture-in-picture";
179 173
 			sourceTree = "<group>";
@@ -345,14 +339,12 @@
345 339
 			buildActionMask = 2147483647;
346 340
 			files = (
347 341
 				0BB9AD7B1F5EC8F4001C08DB /* CallKit.m in Sources */,
348
-				C6A34260204EF76800E062DD /* JitsiMeetPresentationCoordinator.swift in Sources */,
349 342
 				0BB9AD7D1F60356D001C08DB /* AppInfo.m in Sources */,
350 343
 				0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */,
351 344
 				0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */,
352 345
 				0BCA49601EC4B6C600B793EE /* POSIX.m in Sources */,
353 346
 				0B7C2CFD200F51D60060D076 /* LaunchOptions.m in Sources */,
354
-				C6A3426D204F1C3300E062DD /* JitsiMeetViewController.swift in Sources */,
355
-				C6A3425F204EF76800E062DD /* PiPWindow.swift in Sources */,
347
+				C6CC49AF207412CF000DFA42 /* PiPViewCoordinator.swift in Sources */,
356 348
 				0BCA495F1EC4B6C600B793EE /* AudioMode.m in Sources */,
357 349
 				0B44A0191F902126009D1D64 /* MPVolumeViewManager.m in Sources */,
358 350
 				0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */,

+ 0
- 107
ios/sdk/src/picture-in-picture/JitsiMeetPresentationCoordinator.swift 查看文件

@@ -1,107 +0,0 @@
1
-/*
2
- * Copyright @ 2017-present Atlassian Pty Ltd
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- *     http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
-
17
-import Foundation
18
-
19
-/// Coordinates the presentation of JitsiMeetViewController inside of
20
-/// an external window that can be resized and dragged with custom PiP mode
21
-open class JitsiMeetPresentationCoordinator: NSObject {
22
-    
23
-    public let meetViewController: JitsiMeetViewController
24
-    public let meetWindow: PiPWindow
25
-    
26
-    public var isInPiP: Bool {
27
-        get {
28
-            return meetWindow.isInPiP
29
-        }
30
-    }
31
-    
32
-    public var jitsiMeetView: JitsiMeetView {
33
-        get {
34
-            return meetViewController.jitsiMeetView
35
-        }
36
-    }
37
-    
38
-    public init(meetViewController: JitsiMeetViewController? = nil,
39
-                meetWindow: PiPWindow? = nil) {
40
-        self.meetViewController = meetViewController ?? JitsiMeetViewController()
41
-        self.meetWindow = meetWindow ?? PiPWindow(frame: UIScreen.main.bounds)
42
-        
43
-        super.init()
44
-        
45
-        configureMeetWindow()
46
-        configureMeetViewController()
47
-    }
48
-    
49
-    /// Show window with jitsi meet and perform a completion closure
50
-    open func show(completion: CompletionAction? = nil) {
51
-        meetWindow.show(completion: completion)
52
-    }
53
-    
54
-    /// Hide window with jitsi meet and perform a completion closure
55
-    open func hide(completion: CompletionAction? = nil) {
56
-        meetWindow.hide(completion: completion)
57
-    }
58
-    
59
-    open func cleanUp() {
60
-        // TODO: more clean up work on this
61
-        
62
-        meetWindow.isHidden = true
63
-        meetWindow.stopDragGesture()
64
-    }
65
-    
66
-    deinit {
67
-        cleanUp()
68
-    }
69
-    
70
-    // MARK: - helpers
71
-    
72
-    private func configureMeetViewController() {
73
-        meetViewController.jitsiMeetView.pictureInPictureEnabled = true
74
-        meetViewController.delegate = self
75
-    }
76
-    
77
-    private func configureMeetWindow() {
78
-        meetWindow.backgroundColor = .clear
79
-        meetWindow.windowLevel = UIWindowLevelStatusBar + 100
80
-        meetWindow.rootViewController = self.meetViewController
81
-    }
82
-}
83
-
84
-extension JitsiMeetPresentationCoordinator: JitsiMeetViewControllerDelegate {
85
-    
86
-    open func performPresentationUpdate(to: JitsiMeetPresentationUpdate) {
87
-        switch to {
88
-        case .enterPictureInPicture:
89
-            meetWindow.enterPictureInPicture()
90
-        case .sizeChange:
91
-            // resize to full screen if rotation happens
92
-            if meetWindow.isInPiP {
93
-                meetWindow.exitPictureInPicture()
94
-            }
95
-        }
96
-    }
97
-    
98
-    open func conferenceStarted() {
99
-        if meetWindow.isHidden {
100
-            meetWindow.show()
101
-        }
102
-    }
103
-    
104
-    open func conferenceEnded(didFail: Bool) {
105
-        cleanUp()
106
-    }
107
-}

+ 0
- 108
ios/sdk/src/picture-in-picture/JitsiMeetViewController.swift 查看文件

@@ -1,108 +0,0 @@
1
-/*
2
- * Copyright @ 2017-present Atlassian Pty Ltd
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- *     http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
-
17
-public enum JitsiMeetPresentationUpdate {
18
-    
19
-    /// The conference wants to enter Picture-in-Picture
20
-    case enterPictureInPicture
21
-    
22
-    /// A screen size change (usually screen rotation)
23
-    case sizeChange
24
-}
25
-
26
-public protocol JitsiMeetViewControllerDelegate: class {
27
-    
28
-    /// Notifies a change of the conference presentation style.
29
-    ///
30
-    /// - Parameter to: The presentation state that will be changed to
31
-    func performPresentationUpdate(to: JitsiMeetPresentationUpdate)
32
-    
33
-    /// The conference started
34
-    func conferenceStarted()
35
-    
36
-    /// The conference ended
37
-    ///
38
-    /// - Parameter didFail: The reason of ending the conference
39
-    func conferenceEnded(didFail: Bool)
40
-}
41
-
42
-/// Wrapper ViewController of a JitsiMeetView
43
-///
44
-/// Is suggested to override this class and implement some customization
45
-/// on how to handle the JitsiMeetView delegate events
46
-open class JitsiMeetViewController: UIViewController {
47
-    
48
-    open weak var delegate: JitsiMeetViewControllerDelegate?
49
-    
50
-    private(set) var jitsiMeetView: JitsiMeetView = JitsiMeetView()
51
-    
52
-    override open func loadView() {
53
-        super.loadView()
54
-        self.view = jitsiMeetView
55
-    }
56
-    
57
-    open override func viewDidLoad() {
58
-        super.viewDidLoad()
59
-        
60
-        jitsiMeetView.delegate = self
61
-    }
62
- 
63
-    open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
64
-        super.viewWillTransition(to: size, with: coordinator)
65
-        delegate?.performPresentationUpdate(to: .sizeChange)
66
-    }
67
-}
68
-
69
-extension JitsiMeetViewController: JitsiMeetViewDelegate {
70
-    
71
-    open func conferenceWillJoin(_ data: [AnyHashable : Any]!) {
72
-        // do something
73
-    }
74
-    
75
-    open func conferenceJoined(_ data: [AnyHashable : Any]!) {
76
-        DispatchQueue.main.async {
77
-            self.delegate?.conferenceStarted()
78
-        }
79
-    }
80
-    
81
-    open func conferenceWillLeave(_ data: [AnyHashable : Any]!) {
82
-        // do something
83
-    }
84
-    
85
-    open func conferenceLeft(_ data: [AnyHashable : Any]!) {
86
-        DispatchQueue.main.async {
87
-            self.delegate?.conferenceEnded(didFail: false)
88
-        }
89
-    }
90
-    
91
-    open func conferenceFailed(_ data: [AnyHashable : Any]!) {
92
-        DispatchQueue.main.async {
93
-            self.delegate?.conferenceEnded(didFail: true)
94
-        }
95
-    }
96
-    
97
-    open func loadConfigError(_ data: [AnyHashable : Any]!) {
98
-        DispatchQueue.main.async {
99
-            self.delegate?.conferenceEnded(didFail: true)
100
-        }
101
-    }
102
-    
103
-    open func enterPicture(inPicture data: [AnyHashable : Any]!) {
104
-        DispatchQueue.main.async {
105
-           self.delegate?.performPresentationUpdate(to: .enterPictureInPicture)
106
-        }
107
-    }
108
-}

ios/sdk/src/picture-in-picture/PiPWindow.swift → ios/sdk/src/picture-in-picture/PiPViewCoordinator.swift 查看文件

@@ -14,14 +14,15 @@
14 14
  * limitations under the License.
15 15
  */
16 16
 
17
-/// Alias defining a completion closure that returns a Bool
18
-public typealias CompletionAction = (Bool) -> Void
17
+public typealias AnimationCompletion = (Bool) -> Void
19 18
 
20
-/// A window that allows its root view controller to be presented
21
-/// in full screen or in a custom Picture in Picture mode
22
-open class PiPWindow: UIWindow {
19
+/// Coordinates the view state of a specified view to allow
20
+/// to be presented in full screen or in a custom Picture in Picture mode.
21
+/// This object will also provide the drag and tap interactions of the view
22
+/// when is presented in Picure in Picture mode.
23
+public class PiPViewCoordinator {
23 24
     
24
-    /// Limits the boundries of root view position on screen when minimized
25
+    /// Limits the boundries of view position on screen when minimized
25 26
     public var dragBoundInsets: UIEdgeInsets = UIEdgeInsets(top: 25,
26 27
                                                             left: 5,
27 28
                                                             bottom: 5,
@@ -31,10 +32,10 @@ open class PiPWindow: UIWindow {
31 32
         }
32 33
     }
33 34
     
34
-    /// The size ratio for root view controller view when in PiP mode
35
+    /// The size ratio of the view when in PiP mode
35 36
     public var pipSizeRatio: CGFloat = {
36 37
         let deviceIdiom = UIScreen.main.traitCollection.userInterfaceIdiom
37
-        switch (deviceIdiom) {
38
+        switch deviceIdiom {
38 39
         case .pad:
39 40
             return 0.25
40 41
         case .phone:
@@ -44,50 +45,62 @@ open class PiPWindow: UIWindow {
44 45
         }
45 46
     }()
46 47
     
47
-    /// The PiP state of this contents of the window
48
-    private(set) var isInPiP: Bool = false
48
+    private(set) var isInPiP: Bool = false // true if view is in PiP mode
49 49
     
50
-    private let dragController: DragGestureController = DragGestureController()
50
+    private(set) var view: UIView
51
+    private var currentBounds: CGRect = CGRect.zero
51 52
     
52
-    /// Used when in PiP mode to enable/disable exit PiP UI
53 53
     private var tapGestureRecognizer: UITapGestureRecognizer?
54 54
     private var exitPiPButton: UIButton?
55 55
     
56
-    /// Help out to bubble up the gesture detection outside of the rootVC frame
57
-    open override func point(inside point: CGPoint,
58
-                             with event: UIEvent?) -> Bool {
59
-        guard let vc = rootViewController else {
60
-            return super.point(inside: point, with: event)
61
-        }
62
-        return vc.view.frame.contains(point)
56
+    private let dragController: DragGestureController = DragGestureController()
57
+    
58
+    public init(withView view: UIView) {
59
+        self.view = view
60
+    }
61
+    
62
+    /// Configure the view to be always on top of all the contents
63
+    /// of the provided parent view.
64
+    /// If a parentView is not provided it will try to use the main window
65
+    public func configureAsStickyView(withParentView parentView: UIView? = nil) {
66
+        guard
67
+            let parentView = parentView ?? UIApplication.shared.keyWindow
68
+            else { return }
69
+        
70
+        parentView.addSubview(view)
71
+        currentBounds = parentView.bounds
72
+        view.frame = currentBounds
73
+        view.layer.zPosition = CGFloat(Float.greatestFiniteMagnitude)
63 74
     }
64 75
     
65
-    /// animate in the window
66
-    open func show(completion: CompletionAction? = nil) {
67
-        if self.isHidden || self.alpha < 1 {
68
-            self.isHidden = false
69
-            self.alpha = 0
76
+    /// Show view with fade in animation
77
+    public func show(completion: AnimationCompletion? = nil) {
78
+        if view.isHidden || view.alpha < 1 {
79
+            view.isHidden = false
80
+            view.alpha = 0
70 81
             
71
-            animateTransition(animations: {
72
-                self.alpha = 1
82
+            animateTransition(animations: { [weak self] in
83
+                self?.view.alpha = 1
73 84
             }, completion: completion)
74 85
         }
75 86
     }
76 87
     
77
-    /// animate out the window
78
-    open func hide(completion: CompletionAction? = nil) {
79
-        if !self.isHidden || self.alpha > 0 {
80
-            animateTransition(animations: {
81
-                self.alpha = 1
88
+    /// Hide view with fade out animation
89
+    public func hide(completion: AnimationCompletion? = nil) {
90
+        if view.isHidden || view.alpha > 0 {
91
+            animateTransition(animations: { [weak self] in
92
+                self?.view.alpha = 0
93
+                self?.view.isHidden = true
82 94
             }, completion: completion)
83 95
         }
84 96
     }
85 97
     
86
-    /// Resize the root view to PiP mode
87
-    open func enterPictureInPicture() {
88
-        guard let view = rootViewController?.view else { return }
98
+    /// Resize view to and change state to custom PictureInPicture mode
99
+    /// This will resize view, add a  gesture to enable user to "drag" view
100
+    /// around screen, and add a button of top of the view to be able to exit mode
101
+    public func enterPictureInPicture() {
89 102
         isInPiP = true
90
-        animateRootViewChange()
103
+        animateViewChange()
91 104
         dragController.startDragListener(inView: view)
92 105
         dragController.insets = dragBoundInsets
93 106
         
@@ -99,10 +112,11 @@ open class PiPWindow: UIWindow {
99 112
         view.addGestureRecognizer(tapGestureRecognizer)
100 113
     }
101 114
     
102
-    /// Resize the root view to full screen
103
-    open func exitPictureInPicture() {
115
+    /// Exit Picture in picture mode, this will resize view, remove
116
+    /// exit pip button, and disable the drag gesture
117
+    @objc public func exitPictureInPicture() {
104 118
         isInPiP = false
105
-        animateRootViewChange()
119
+        animateViewChange()
106 120
         dragController.stopDragListener()
107 121
         
108 122
         // hide PiP UI
@@ -115,6 +129,13 @@ open class PiPWindow: UIWindow {
115 129
         tapGestureRecognizer = nil
116 130
     }
117 131
     
132
+    /// Reset view to provide bounds, use this method on rotation or
133
+    /// screen size changes
134
+    public func resetBounds(bounds: CGRect) {
135
+        currentBounds = bounds
136
+        exitPictureInPicture()
137
+    }
138
+    
118 139
     /// Stop the dragging gesture of the root view
119 140
     public func stopDragGesture() {
120 141
         dragController.stopDragListener()
@@ -132,41 +153,14 @@ open class PiPWindow: UIWindow {
132 153
         button.backgroundColor = .gray
133 154
         button.layer.cornerRadius = size.width / 2
134 155
         button.frame = CGRect(origin: CGPoint.zero, size: size)
135
-        if let view = rootViewController?.view {
136
-            button.center = view.convert(view.center, from:view.superview)
137
-        }
156
+        button.center = view.convert(view.center, from: view.superview)
138 157
         button.addTarget(target, action: action, for: .touchUpInside)
139 158
         return button
140 159
     }
141 160
     
142
-    // MARK: - Manage presentation switching
143
-    
144
-    private func animateRootViewChange() {
145
-        UIView.animate(withDuration: 0.25) {
146
-            self.rootViewController?.view.frame = self.changeRootViewRect()
147
-            self.rootViewController?.view.setNeedsLayout()
148
-        }
149
-    }
150
-    
151
-    private func changeRootViewRect() -> CGRect {
152
-        guard isInPiP else {
153
-            return self.bounds
154
-        }
155
-        
156
-        // resize to suggested ratio and position to the bottom right
157
-        let adjustedBounds = UIEdgeInsetsInsetRect(self.bounds, dragBoundInsets)
158
-        let size = CGSize(width: bounds.size.width * pipSizeRatio,
159
-                          height: bounds.size.height * pipSizeRatio)
160
-        let x: CGFloat = adjustedBounds.maxX - size.width
161
-        let y: CGFloat = adjustedBounds.maxY - size.height
162
-        return CGRect(x: x, y: y, width: size.width, height: size.height)
163
-    }
164
-    
165
-    // MARK: - Exit PiP
161
+    // MARK: - Interactions
166 162
     
167 163
     @objc private func toggleExitPiP() {
168
-        guard let view = rootViewController?.view else { return }
169
-        
170 164
         if exitPiPButton == nil {
171 165
             // show button
172 166
             let exitSelector = #selector(exitPictureInPicture)
@@ -182,18 +176,40 @@ open class PiPWindow: UIWindow {
182 176
         }
183 177
     }
184 178
     
185
-    @objc private func exitPiP() {
186
-        exitPictureInPicture()
179
+    // MARK: - Size calculation
180
+    
181
+    private func animateViewChange() {
182
+        UIView.animate(withDuration: 0.25) {
183
+            self.view.frame = self.changeViewRect()
184
+            self.view.setNeedsLayout()
185
+        }
186
+    }
187
+    
188
+    private func changeViewRect() -> CGRect {
189
+        let bounds = currentBounds
190
+        
191
+        guard isInPiP else {
192
+            return bounds
193
+        }
194
+        
195
+        // resize to suggested ratio and position to the bottom right
196
+        let adjustedBounds = UIEdgeInsetsInsetRect(bounds, dragBoundInsets)
197
+        let size = CGSize(width: bounds.size.width * pipSizeRatio,
198
+                          height: bounds.size.height * pipSizeRatio)
199
+        let x: CGFloat = adjustedBounds.maxX - size.width
200
+        let y: CGFloat = adjustedBounds.maxY - size.height
201
+        return CGRect(x: x, y: y, width: size.width, height: size.height)
187 202
     }
188 203
     
189
-    // MARK: - Animation transition
204
+    // MARK: - Animation helpers
190 205
     
191 206
     private func animateTransition(animations: @escaping () -> Void,
192
-                                   completion: CompletionAction?) {
207
+                                   completion: AnimationCompletion?) {
193 208
         UIView.animate(withDuration: 0.1,
194 209
                        delay: 0,
195 210
                        options: .beginFromCurrentState,
196 211
                        animations: animations,
197 212
                        completion: completion)
198 213
     }
214
+    
199 215
 }

Loading…
取消
儲存