diff --git a/ios/example-pip-app/src/Info.plist b/ios/example-pip-app/src/Info.plist
index 8a99dee664..ff67a0ed65 100644
--- a/ios/example-pip-app/src/Info.plist
+++ b/ios/example-pip-app/src/Info.plist
@@ -51,6 +51,8 @@
NSAllowsArbitraryLoads
+ UIViewControllerBasedStatusBarAppearance
+
UISupportedInterfaceOrientations~ipad
UIInterfaceOrientationPortrait
diff --git a/ios/example-pip-app/src/ViewController.swift b/ios/example-pip-app/src/ViewController.swift
index af4f03983b..7d608f4f23 100644
--- a/ios/example-pip-app/src/ViewController.swift
+++ b/ios/example-pip-app/src/ViewController.swift
@@ -21,19 +21,73 @@ class ViewController: UIViewController {
@IBOutlet weak var videoButton: UIButton?
- private var jitsiMeetCoordinator: JitsiMeetPresentationCoordinator?
+ fileprivate var pipViewCoordinator: PiPViewCoordinator?
+ fileprivate var jitsiMeetView: JitsiMeetView?
override func viewDidLoad() {
super.viewDidLoad()
}
+ override func viewWillTransition(to size: CGSize,
+ with coordinator: UIViewControllerTransitionCoordinator) {
+ super.viewWillTransition(to: size, with: coordinator)
+
+ let rect = CGRect(origin: CGPoint.zero, size: size)
+ pipViewCoordinator?.resetBounds(bounds: rect)
+ }
+
// MARK: - Actions
@IBAction func openJitsiMeet(sender: Any?) {
- let jitsiMeetCoordinator = JitsiMeetPresentationCoordinator()
- self.jitsiMeetCoordinator = jitsiMeetCoordinator
- jitsiMeetCoordinator.jitsiMeetView.welcomePageEnabled = true
- jitsiMeetCoordinator.jitsiMeetView.load(nil)
- jitsiMeetCoordinator.show()
+ cleanUp()
+
+ // create and configure jitsimeet view
+ let jitsiMeetView = JitsiMeetView()
+ jitsiMeetView.welcomePageEnabled = true
+ jitsiMeetView.pictureInPictureEnabled = true
+ jitsiMeetView.load(nil)
+ jitsiMeetView.delegate = self
+ self.jitsiMeetView = jitsiMeetView
+
+ // Enable jitsimeet view to be a view that can be displayed
+ // on top of all the things, and let the coordinator to manage
+ // the view state and interactions
+ pipViewCoordinator = PiPViewCoordinator(withView: jitsiMeetView)
+ pipViewCoordinator?.configureAsStickyView(withParentView: view)
+
+ // animate in
+ jitsiMeetView.alpha = 0
+ pipViewCoordinator?.show()
+ }
+
+ fileprivate func cleanUp() {
+ jitsiMeetView?.removeFromSuperview()
+ jitsiMeetView = nil
+ pipViewCoordinator = nil
+ }
+}
+
+extension ViewController: JitsiMeetViewDelegate {
+
+ func conferenceFailed(_ data: [AnyHashable : Any]!) {
+ hideJitsiMeetViewAndCleanUp()
+ }
+
+ func conferenceLeft(_ data: [AnyHashable : Any]!) {
+ hideJitsiMeetViewAndCleanUp()
+ }
+
+ func enterPicture(inPicture data: [AnyHashable : Any]!) {
+ DispatchQueue.main.async {
+ self.pipViewCoordinator?.enterPictureInPicture()
+ }
+ }
+
+ private func hideJitsiMeetViewAndCleanUp() {
+ DispatchQueue.main.async {
+ self.pipViewCoordinator?.hide() { _ in
+ self.cleanUp()
+ }
+ }
}
}
diff --git a/ios/sdk/sdk.xcodeproj/project.pbxproj b/ios/sdk/sdk.xcodeproj/project.pbxproj
index b0d098d879..e5b4b731d0 100644
--- a/ios/sdk/sdk.xcodeproj/project.pbxproj
+++ b/ios/sdk/sdk.xcodeproj/project.pbxproj
@@ -31,10 +31,8 @@
75635B0B20751D6D00F29C9F /* left.wav in Resources */ = {isa = PBXBuildFile; fileRef = 75635B0920751D6D00F29C9F /* left.wav */; };
C6245F5D2053091D0040BE68 /* image-resize@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C6245F5B2053091D0040BE68 /* image-resize@2x.png */; };
C6245F5E2053091D0040BE68 /* image-resize@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = C6245F5C2053091D0040BE68 /* image-resize@3x.png */; };
- C6A3425F204EF76800E062DD /* PiPWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425C204EF76800E062DD /* PiPWindow.swift */; };
- C6A34260204EF76800E062DD /* JitsiMeetPresentationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425D204EF76800E062DD /* JitsiMeetPresentationCoordinator.swift */; };
C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425E204EF76800E062DD /* DragGestureController.swift */; };
- C6A3426D204F1C3300E062DD /* JitsiMeetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3426C204F1C3300E062DD /* JitsiMeetViewController.swift */; };
+ C6CC49AF207412CF000DFA42 /* PiPViewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */; };
C6F99C15204DB63E0001F710 /* JitsiMeetView+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */; };
/* End PBXBuildFile section */
@@ -68,10 +66,8 @@
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 = ""; };
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 = ""; };
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 = ""; };
- C6A3425C204EF76800E062DD /* PiPWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PiPWindow.swift; sourceTree = ""; };
- C6A3425D204EF76800E062DD /* JitsiMeetPresentationCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JitsiMeetPresentationCoordinator.swift; sourceTree = ""; };
C6A3425E204EF76800E062DD /* DragGestureController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DragGestureController.swift; sourceTree = ""; };
- C6A3426C204F1C3300E062DD /* JitsiMeetViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JitsiMeetViewController.swift; sourceTree = ""; };
+ C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PiPViewCoordinator.swift; sourceTree = ""; };
C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetView+Private.h"; sourceTree = ""; };
/* End PBXFileReference section */
@@ -171,9 +167,7 @@
isa = PBXGroup;
children = (
C6A3425E204EF76800E062DD /* DragGestureController.swift */,
- C6A3425D204EF76800E062DD /* JitsiMeetPresentationCoordinator.swift */,
- C6A3426C204F1C3300E062DD /* JitsiMeetViewController.swift */,
- C6A3425C204EF76800E062DD /* PiPWindow.swift */,
+ C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */,
);
path = "picture-in-picture";
sourceTree = "";
@@ -345,14 +339,12 @@
buildActionMask = 2147483647;
files = (
0BB9AD7B1F5EC8F4001C08DB /* CallKit.m in Sources */,
- C6A34260204EF76800E062DD /* JitsiMeetPresentationCoordinator.swift in Sources */,
0BB9AD7D1F60356D001C08DB /* AppInfo.m in Sources */,
0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */,
0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */,
0BCA49601EC4B6C600B793EE /* POSIX.m in Sources */,
0B7C2CFD200F51D60060D076 /* LaunchOptions.m in Sources */,
- C6A3426D204F1C3300E062DD /* JitsiMeetViewController.swift in Sources */,
- C6A3425F204EF76800E062DD /* PiPWindow.swift in Sources */,
+ C6CC49AF207412CF000DFA42 /* PiPViewCoordinator.swift in Sources */,
0BCA495F1EC4B6C600B793EE /* AudioMode.m in Sources */,
0B44A0191F902126009D1D64 /* MPVolumeViewManager.m in Sources */,
0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */,
diff --git a/ios/sdk/src/picture-in-picture/JitsiMeetPresentationCoordinator.swift b/ios/sdk/src/picture-in-picture/JitsiMeetPresentationCoordinator.swift
deleted file mode 100644
index ad8ac92c9b..0000000000
--- a/ios/sdk/src/picture-in-picture/JitsiMeetPresentationCoordinator.swift
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright @ 2017-present Atlassian Pty Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import Foundation
-
-/// Coordinates the presentation of JitsiMeetViewController inside of
-/// an external window that can be resized and dragged with custom PiP mode
-open class JitsiMeetPresentationCoordinator: NSObject {
-
- public let meetViewController: JitsiMeetViewController
- public let meetWindow: PiPWindow
-
- public var isInPiP: Bool {
- get {
- return meetWindow.isInPiP
- }
- }
-
- public var jitsiMeetView: JitsiMeetView {
- get {
- return meetViewController.jitsiMeetView
- }
- }
-
- public init(meetViewController: JitsiMeetViewController? = nil,
- meetWindow: PiPWindow? = nil) {
- self.meetViewController = meetViewController ?? JitsiMeetViewController()
- self.meetWindow = meetWindow ?? PiPWindow(frame: UIScreen.main.bounds)
-
- super.init()
-
- configureMeetWindow()
- configureMeetViewController()
- }
-
- /// Show window with jitsi meet and perform a completion closure
- open func show(completion: CompletionAction? = nil) {
- meetWindow.show(completion: completion)
- }
-
- /// Hide window with jitsi meet and perform a completion closure
- open func hide(completion: CompletionAction? = nil) {
- meetWindow.hide(completion: completion)
- }
-
- open func cleanUp() {
- // TODO: more clean up work on this
-
- meetWindow.isHidden = true
- meetWindow.stopDragGesture()
- }
-
- deinit {
- cleanUp()
- }
-
- // MARK: - helpers
-
- private func configureMeetViewController() {
- meetViewController.jitsiMeetView.pictureInPictureEnabled = true
- meetViewController.delegate = self
- }
-
- private func configureMeetWindow() {
- meetWindow.backgroundColor = .clear
- meetWindow.windowLevel = UIWindowLevelStatusBar + 100
- meetWindow.rootViewController = self.meetViewController
- }
-}
-
-extension JitsiMeetPresentationCoordinator: JitsiMeetViewControllerDelegate {
-
- open func performPresentationUpdate(to: JitsiMeetPresentationUpdate) {
- switch to {
- case .enterPictureInPicture:
- meetWindow.enterPictureInPicture()
- case .sizeChange:
- // resize to full screen if rotation happens
- if meetWindow.isInPiP {
- meetWindow.exitPictureInPicture()
- }
- }
- }
-
- open func conferenceStarted() {
- if meetWindow.isHidden {
- meetWindow.show()
- }
- }
-
- open func conferenceEnded(didFail: Bool) {
- cleanUp()
- }
-}
diff --git a/ios/sdk/src/picture-in-picture/JitsiMeetViewController.swift b/ios/sdk/src/picture-in-picture/JitsiMeetViewController.swift
deleted file mode 100644
index 6e2b952be5..0000000000
--- a/ios/sdk/src/picture-in-picture/JitsiMeetViewController.swift
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright @ 2017-present Atlassian Pty Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-public enum JitsiMeetPresentationUpdate {
-
- /// The conference wants to enter Picture-in-Picture
- case enterPictureInPicture
-
- /// A screen size change (usually screen rotation)
- case sizeChange
-}
-
-public protocol JitsiMeetViewControllerDelegate: class {
-
- /// Notifies a change of the conference presentation style.
- ///
- /// - Parameter to: The presentation state that will be changed to
- func performPresentationUpdate(to: JitsiMeetPresentationUpdate)
-
- /// The conference started
- func conferenceStarted()
-
- /// The conference ended
- ///
- /// - Parameter didFail: The reason of ending the conference
- func conferenceEnded(didFail: Bool)
-}
-
-/// Wrapper ViewController of a JitsiMeetView
-///
-/// Is suggested to override this class and implement some customization
-/// on how to handle the JitsiMeetView delegate events
-open class JitsiMeetViewController: UIViewController {
-
- open weak var delegate: JitsiMeetViewControllerDelegate?
-
- private(set) var jitsiMeetView: JitsiMeetView = JitsiMeetView()
-
- override open func loadView() {
- super.loadView()
- self.view = jitsiMeetView
- }
-
- open override func viewDidLoad() {
- super.viewDidLoad()
-
- jitsiMeetView.delegate = self
- }
-
- open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
- super.viewWillTransition(to: size, with: coordinator)
- delegate?.performPresentationUpdate(to: .sizeChange)
- }
-}
-
-extension JitsiMeetViewController: JitsiMeetViewDelegate {
-
- open func conferenceWillJoin(_ data: [AnyHashable : Any]!) {
- // do something
- }
-
- open func conferenceJoined(_ data: [AnyHashable : Any]!) {
- DispatchQueue.main.async {
- self.delegate?.conferenceStarted()
- }
- }
-
- open func conferenceWillLeave(_ data: [AnyHashable : Any]!) {
- // do something
- }
-
- open func conferenceLeft(_ data: [AnyHashable : Any]!) {
- DispatchQueue.main.async {
- self.delegate?.conferenceEnded(didFail: false)
- }
- }
-
- open func conferenceFailed(_ data: [AnyHashable : Any]!) {
- DispatchQueue.main.async {
- self.delegate?.conferenceEnded(didFail: true)
- }
- }
-
- open func loadConfigError(_ data: [AnyHashable : Any]!) {
- DispatchQueue.main.async {
- self.delegate?.conferenceEnded(didFail: true)
- }
- }
-
- open func enterPicture(inPicture data: [AnyHashable : Any]!) {
- DispatchQueue.main.async {
- self.delegate?.performPresentationUpdate(to: .enterPictureInPicture)
- }
- }
-}
diff --git a/ios/sdk/src/picture-in-picture/PiPWindow.swift b/ios/sdk/src/picture-in-picture/PiPViewCoordinator.swift
similarity index 60%
rename from ios/sdk/src/picture-in-picture/PiPWindow.swift
rename to ios/sdk/src/picture-in-picture/PiPViewCoordinator.swift
index c5c518fc97..2514fd1224 100644
--- a/ios/sdk/src/picture-in-picture/PiPWindow.swift
+++ b/ios/sdk/src/picture-in-picture/PiPViewCoordinator.swift
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-/// Alias defining a completion closure that returns a Bool
-public typealias CompletionAction = (Bool) -> Void
+public typealias AnimationCompletion = (Bool) -> Void
-/// A window that allows its root view controller to be presented
-/// in full screen or in a custom Picture in Picture mode
-open class PiPWindow: UIWindow {
+/// Coordinates the view state of a specified view to allow
+/// to be presented in full screen or in a custom Picture in Picture mode.
+/// This object will also provide the drag and tap interactions of the view
+/// when is presented in Picure in Picture mode.
+public class PiPViewCoordinator {
- /// Limits the boundries of root view position on screen when minimized
+ /// Limits the boundries of view position on screen when minimized
public var dragBoundInsets: UIEdgeInsets = UIEdgeInsets(top: 25,
left: 5,
bottom: 5,
@@ -31,10 +32,10 @@ open class PiPWindow: UIWindow {
}
}
- /// The size ratio for root view controller view when in PiP mode
+ /// The size ratio of the view when in PiP mode
public var pipSizeRatio: CGFloat = {
let deviceIdiom = UIScreen.main.traitCollection.userInterfaceIdiom
- switch (deviceIdiom) {
+ switch deviceIdiom {
case .pad:
return 0.25
case .phone:
@@ -44,50 +45,62 @@ open class PiPWindow: UIWindow {
}
}()
- /// The PiP state of this contents of the window
- private(set) var isInPiP: Bool = false
+ private(set) var isInPiP: Bool = false // true if view is in PiP mode
- private let dragController: DragGestureController = DragGestureController()
+ private(set) var view: UIView
+ private var currentBounds: CGRect = CGRect.zero
- /// Used when in PiP mode to enable/disable exit PiP UI
private var tapGestureRecognizer: UITapGestureRecognizer?
private var exitPiPButton: UIButton?
- /// Help out to bubble up the gesture detection outside of the rootVC frame
- open override func point(inside point: CGPoint,
- with event: UIEvent?) -> Bool {
- guard let vc = rootViewController else {
- return super.point(inside: point, with: event)
- }
- return vc.view.frame.contains(point)
+ private let dragController: DragGestureController = DragGestureController()
+
+ public init(withView view: UIView) {
+ self.view = view
+ }
+
+ /// Configure the view to be always on top of all the contents
+ /// of the provided parent view.
+ /// If a parentView is not provided it will try to use the main window
+ public func configureAsStickyView(withParentView parentView: UIView? = nil) {
+ guard
+ let parentView = parentView ?? UIApplication.shared.keyWindow
+ else { return }
+
+ parentView.addSubview(view)
+ currentBounds = parentView.bounds
+ view.frame = currentBounds
+ view.layer.zPosition = CGFloat(Float.greatestFiniteMagnitude)
}
- /// animate in the window
- open func show(completion: CompletionAction? = nil) {
- if self.isHidden || self.alpha < 1 {
- self.isHidden = false
- self.alpha = 0
+ /// Show view with fade in animation
+ public func show(completion: AnimationCompletion? = nil) {
+ if view.isHidden || view.alpha < 1 {
+ view.isHidden = false
+ view.alpha = 0
- animateTransition(animations: {
- self.alpha = 1
+ animateTransition(animations: { [weak self] in
+ self?.view.alpha = 1
}, completion: completion)
}
}
- /// animate out the window
- open func hide(completion: CompletionAction? = nil) {
- if !self.isHidden || self.alpha > 0 {
- animateTransition(animations: {
- self.alpha = 1
+ /// Hide view with fade out animation
+ public func hide(completion: AnimationCompletion? = nil) {
+ if view.isHidden || view.alpha > 0 {
+ animateTransition(animations: { [weak self] in
+ self?.view.alpha = 0
+ self?.view.isHidden = true
}, completion: completion)
}
}
- /// Resize the root view to PiP mode
- open func enterPictureInPicture() {
- guard let view = rootViewController?.view else { return }
+ /// Resize view to and change state to custom PictureInPicture mode
+ /// This will resize view, add a gesture to enable user to "drag" view
+ /// around screen, and add a button of top of the view to be able to exit mode
+ public func enterPictureInPicture() {
isInPiP = true
- animateRootViewChange()
+ animateViewChange()
dragController.startDragListener(inView: view)
dragController.insets = dragBoundInsets
@@ -99,10 +112,11 @@ open class PiPWindow: UIWindow {
view.addGestureRecognizer(tapGestureRecognizer)
}
- /// Resize the root view to full screen
- open func exitPictureInPicture() {
+ /// Exit Picture in picture mode, this will resize view, remove
+ /// exit pip button, and disable the drag gesture
+ @objc public func exitPictureInPicture() {
isInPiP = false
- animateRootViewChange()
+ animateViewChange()
dragController.stopDragListener()
// hide PiP UI
@@ -115,6 +129,13 @@ open class PiPWindow: UIWindow {
tapGestureRecognizer = nil
}
+ /// Reset view to provide bounds, use this method on rotation or
+ /// screen size changes
+ public func resetBounds(bounds: CGRect) {
+ currentBounds = bounds
+ exitPictureInPicture()
+ }
+
/// Stop the dragging gesture of the root view
public func stopDragGesture() {
dragController.stopDragListener()
@@ -132,41 +153,14 @@ open class PiPWindow: UIWindow {
button.backgroundColor = .gray
button.layer.cornerRadius = size.width / 2
button.frame = CGRect(origin: CGPoint.zero, size: size)
- if let view = rootViewController?.view {
- button.center = view.convert(view.center, from:view.superview)
- }
+ button.center = view.convert(view.center, from: view.superview)
button.addTarget(target, action: action, for: .touchUpInside)
return button
}
- // MARK: - Manage presentation switching
-
- private func animateRootViewChange() {
- UIView.animate(withDuration: 0.25) {
- self.rootViewController?.view.frame = self.changeRootViewRect()
- self.rootViewController?.view.setNeedsLayout()
- }
- }
-
- private func changeRootViewRect() -> CGRect {
- guard isInPiP else {
- return self.bounds
- }
-
- // resize to suggested ratio and position to the bottom right
- let adjustedBounds = UIEdgeInsetsInsetRect(self.bounds, dragBoundInsets)
- let size = CGSize(width: bounds.size.width * pipSizeRatio,
- height: bounds.size.height * pipSizeRatio)
- let x: CGFloat = adjustedBounds.maxX - size.width
- let y: CGFloat = adjustedBounds.maxY - size.height
- return CGRect(x: x, y: y, width: size.width, height: size.height)
- }
-
- // MARK: - Exit PiP
+ // MARK: - Interactions
@objc private func toggleExitPiP() {
- guard let view = rootViewController?.view else { return }
-
if exitPiPButton == nil {
// show button
let exitSelector = #selector(exitPictureInPicture)
@@ -182,18 +176,40 @@ open class PiPWindow: UIWindow {
}
}
- @objc private func exitPiP() {
- exitPictureInPicture()
+ // MARK: - Size calculation
+
+ private func animateViewChange() {
+ UIView.animate(withDuration: 0.25) {
+ self.view.frame = self.changeViewRect()
+ self.view.setNeedsLayout()
+ }
+ }
+
+ private func changeViewRect() -> CGRect {
+ let bounds = currentBounds
+
+ guard isInPiP else {
+ return bounds
+ }
+
+ // resize to suggested ratio and position to the bottom right
+ let adjustedBounds = UIEdgeInsetsInsetRect(bounds, dragBoundInsets)
+ let size = CGSize(width: bounds.size.width * pipSizeRatio,
+ height: bounds.size.height * pipSizeRatio)
+ let x: CGFloat = adjustedBounds.maxX - size.width
+ let y: CGFloat = adjustedBounds.maxY - size.height
+ return CGRect(x: x, y: y, width: size.width, height: size.height)
}
- // MARK: - Animation transition
+ // MARK: - Animation helpers
private func animateTransition(animations: @escaping () -> Void,
- completion: CompletionAction?) {
+ completion: AnimationCompletion?) {
UIView.animate(withDuration: 0.1,
delay: 0,
options: .beginFromCurrentState,
animations: animations,
completion: completion)
}
+
}