diff --git a/Classes/Camera/CameraController.swift b/Classes/Camera/CameraController.swift index ec1a53283..53eeaa493 100644 --- a/Classes/Camera/CameraController.swift +++ b/Classes/Camera/CameraController.swift @@ -173,7 +173,9 @@ open class CameraController: UIViewController, MediaClipsEditorDelegate, CameraP return controller }() private lazy var clipsController: MediaClipsEditorViewController = { - let controller = MediaClipsEditorViewController(showsAddButton: false) + let viewSettings = MediaClipsCollectionView.Settings(showsFadeOutGradient: false) + let collectionSettings = MediaClipsCollectionController.Settings(clipsCollectionViewSettings: viewSettings) + let controller = MediaClipsEditorViewController(showsAddButton: false, collectionSettings: collectionSettings) controller.delegate = self return controller }() @@ -640,6 +642,10 @@ open class CameraController: UIViewController, MediaClipsEditorDelegate, CameraP private func generateHapticFeedback() { feedbackGenerator.notificationOccurred(.success) } + + public var confirmButton: UIView? { + return (presentedViewController as? MultiEditorViewController)?.currentEditor?.editorView.confirmOrPostButton() + } // MARK: - CameraViewDelegate @@ -1091,7 +1097,7 @@ open class CameraController: UIViewController, MediaClipsEditorDelegate, CameraP func cameraPermissionsChanged(hasFullAccess: Bool) { if hasFullAccess { - cameraInputController.setupCaptureSession() + cameraInputController.setupCaptureSession(frameSize: view.frame.size) toggleMediaPicker(visible: true, animated: false) } } diff --git a/Classes/Camera/CameraInputController.swift b/Classes/Camera/CameraInputController.swift index 61a8fb89a..a923ad552 100644 --- a/Classes/Camera/CameraInputController.swift +++ b/Classes/Camera/CameraInputController.swift @@ -183,6 +183,8 @@ final class CameraInputController: UIViewController, CameraRecordingDelegate, AV setupFlash(defaultOption: settings.preferredFlashOption) setupPreviewBlur() + + NotificationCenter.default.addObserver(self, selector: #selector(orientationChanged), name: UIDevice.orientationDidChangeNotification, object: nil) } override func viewWillAppear(_ animated: Bool) { @@ -197,7 +199,7 @@ final class CameraInputController: UIViewController, CameraRecordingDelegate, AV return } - setupCaptureSession() + setupCaptureSession(frameSize: view.frame.size) setupNotifications() } @@ -214,8 +216,7 @@ final class CameraInputController: UIViewController, CameraRecordingDelegate, AV teardownNotifications() } - func setupCaptureSession() { - let frameSize = view.frame.size + func setupCaptureSession(frameSize: CGSize) { previewBlurView.effect = CameraInputController.blurEffect() let hasFullAccess = delegate?.cameraInputControllerHasFullAccess() ?? true guard hasFullAccess else { @@ -283,6 +284,7 @@ final class CameraInputController: UIViewController, CameraRecordingDelegate, AV try configureAudioDataOutput() try configureCurrentOutput() captureSession?.commitConfiguration() + refreshOrientation() } catch { // this can happen if not all permissions were accepted, should not throw an exception captureSession?.commitConfiguration() @@ -290,6 +292,41 @@ final class CameraInputController: UIViewController, CameraRecordingDelegate, AV } } + @objc func orientationChanged() { + refreshOrientation() + } + + private func refreshOrientation() { + captureSession?.connections.forEach({ connection in + connection.videoOrientation = orientation() + }) + } + + override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + super.viewWillTransition(to: size, with: coordinator) + + refreshOrientation() + + stopCaptureSession() + setupCaptureSession(frameSize: CGSize(width: size.width, height: size.height)) + } + + + private func orientation() -> AVCaptureVideoOrientation { + switch UIDevice.current.orientation { + case .portrait: + return .portrait + case .portraitUpsideDown: + return .portraitUpsideDown + case .landscapeLeft: + return .landscapeRight + case .landscapeRight: + return .landscapeLeft + default: + return .portrait + } + } + private func setupGestures() { view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapped(gesture:)))) let doubleTap = UITapGestureRecognizer(target: self, action: #selector(doubleTapped)) diff --git a/Classes/Constants/KanvasEditorDesign.swift b/Classes/Constants/KanvasEditorDesign.swift index c80e0bfd9..c8472e2e6 100644 --- a/Classes/Constants/KanvasEditorDesign.swift +++ b/Classes/Constants/KanvasEditorDesign.swift @@ -186,4 +186,43 @@ public struct KanvasEditorDesign { } ) }() + + public static var storiesDesign: KanvasEditorDesign = { + return KanvasEditorDesign( + isVerticalMenu: false, + checkmarkImage: KanvasImages.shared.editorConfirmImage, + buttonBackgroundColor: .clear, + buttonInvertedBackgroundColor: .clear, + optionBackgroundColor: .clear, + topButtonSize: 49, + topSecondaryButtonSize: 36, + topButtonInterspace: 30, + fakeOptionCellCheckmarkImage: UIImage.imageFromCameraBundle(named: "confirm"), + closeGradientImage: UIImage.imageFromCameraBundle(named: "closeGradient"), + editorViewCloseImage: UIImage.imageFromCameraBundle(named: "whiteCloseIcon"), + editorViewBackImage: UIImage.imageFromCameraBundle(named: "back"), + editorViewButtonTopMargin: 24, + editorViewButtonBottomMargin: Device.belongsToIPhoneXGroup ? 14 : 19.5, + editorViewFakeOptionCellMinSize: 36, + editorViewFakeOptionCellMaxSize: 45, + editorViewCloseButtonSize: 26.5, + editorViewCloseButtonHorizontalMargin: 24, + drawingViewUndoImage: UIImage.imageFromCameraBundle(named: "undo"), + drawingViewEraserSelectedImage: UIImage.imageFromCameraBundle(named: "eraserSelected"), + drawingViewEraserUnselectedImage: UIImage.imageFromCameraBundle(named: "eraserUnselected"), + drawingViewMarkerImage: UIImage.imageFromCameraBundle(named: "marker"), + drawingViewSharpieImage: UIImage.imageFromCameraBundle(named: "sharpie"), + drawingViewPencilImage: UIImage.imageFromCameraBundle(named: "pencil"), + drawingViewEyeDropperImage: UIImage.imageFromCameraBundle(named: "eyeDropper"), + editorTextViewFontImage: UIImage.imageFromCameraBundle(named: "font"), + editorTextViewAlignmentImage: [ + .left: UIImage.imageFromCameraBundle(named: "leftAlignment"), + .center: UIImage.imageFromCameraBundle(named: "centerAlignment"), + .right: UIImage.imageFromCameraBundle(named: "rightAlignment"), + ], + editorTextViewHighlightImage: { selected in + return selected ? UIImage.imageFromCameraBundle(named: "highlightSelected") : UIImage.imageFromCameraBundle(named: "highlightUnselected") + } + ) + }() } diff --git a/Classes/Editor/EditorView.swift b/Classes/Editor/EditorView.swift index 2116b23c5..c7d5c4957 100644 --- a/Classes/Editor/EditorView.swift +++ b/Classes/Editor/EditorView.swift @@ -347,7 +347,7 @@ final class EditorView: UIView, MovableViewCanvasDelegate, MediaPlayerViewDelega let bottomConstraint: NSLayoutConstraint let topConstraint: NSLayoutConstraint - if Device.belongsToIPhoneXGroup { + if Device.belongsToIPhoneXGroup || Device.isIPad { bottomConstraint = playerView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor) topConstraint = playerView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor) } else { diff --git a/Classes/Editor/MultiEditor/MultiEditorViewController.swift b/Classes/Editor/MultiEditor/MultiEditorViewController.swift index 93fe0aba6..22e373b9c 100644 --- a/Classes/Editor/MultiEditor/MultiEditorViewController.swift +++ b/Classes/Editor/MultiEditor/MultiEditorViewController.swift @@ -16,7 +16,9 @@ protocol MultiEditorComposerDelegate: EditorControllerDelegate { class MultiEditorViewController: UIViewController { private lazy var clipsController: MediaClipsEditorViewController = { - let clipsEditor = MediaClipsEditorViewController(showsAddButton: true) + let collectionViewSettings = MediaClipsCollectionView.Settings(showsFadeOutGradient: false) + let collectionSettings = MediaClipsCollectionController.Settings(clipsCollectionViewSettings: collectionViewSettings) + let clipsEditor = MediaClipsEditorViewController(showsAddButton: true, collectionSettings: collectionSettings) clipsEditor.delegate = self clipsEditor.view.backgroundColor = UIColor.black.withAlphaComponent(0.4) return clipsEditor @@ -76,7 +78,7 @@ class MultiEditorViewController: UIViewController { private var exportingEditors: [EditorViewController]? - private weak var currentEditor: EditorViewController? + private(set) weak var currentEditor: EditorViewController? init(settings: CameraSettings, frames: [Frame], diff --git a/Classes/Editor/Shared/ColorCollection/ColorCollectionCell.swift b/Classes/Editor/Shared/ColorCollection/ColorCollectionCell.swift index 00af36f63..b27cb2c66 100644 --- a/Classes/Editor/Shared/ColorCollection/ColorCollectionCell.swift +++ b/Classes/Editor/Shared/ColorCollection/ColorCollectionCell.swift @@ -73,8 +73,8 @@ final class ColorCollectionCell: UICollectionViewCell { circleView.accessibilityIdentifier = "Color Cell View" NSLayoutConstraint.activate([ - circleView.centerXAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.centerXAnchor), - circleView.centerYAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.centerYAnchor), + circleView.centerXAnchor.constraint(equalTo: contentView.layoutMarginsGuide.centerXAnchor), + circleView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), circleView.heightAnchor.constraint(equalToConstant: ColorCollectionCellConstants.circleDiameter), circleView.widthAnchor.constraint(equalToConstant: ColorCollectionCellConstants.circleDiameter) ]) diff --git a/Classes/Editor/Text/EditorTextController.swift b/Classes/Editor/Text/EditorTextController.swift index 153548b50..4add95715 100644 --- a/Classes/Editor/Text/EditorTextController.swift +++ b/Classes/Editor/Text/EditorTextController.swift @@ -285,6 +285,9 @@ final class EditorTextController: UIViewController, EditorTextViewDelegate, Colo // This method is called inside the keyboard animation, // so any UI change made here will be animated. @objc func keyboardWillHide(notification: NSNotification) { + if isHiding == false && textView.alpha != 0 { + didConfirmText() + } textView.moveToolsDown() } @@ -336,13 +339,17 @@ final class EditorTextController: UIViewController, EditorTextViewDelegate, Colo self.textView.startWriting() }) } - + + /// A flag to indicate whether we're in the process of hiding. This is used in `keyboardWillHide` to determine whether to confirm the text. + private var isHiding = false /// Makes the view disappear private func hide() { textView.endWriting() + isHiding = true UIView.animate(withDuration: Constants.animationDuration) { self.textView.alpha = 0 + self.isHiding = false } } diff --git a/Classes/MediaClips/MediaClipsCollectionController.swift b/Classes/MediaClips/MediaClipsCollectionController.swift index e1abd3b85..df82404a7 100644 --- a/Classes/MediaClips/MediaClipsCollectionController.swift +++ b/Classes/MediaClips/MediaClipsCollectionController.swift @@ -34,7 +34,7 @@ private struct MediaClipsCollectionControllerConstants { /// Controller for handling the media clips collection. final class MediaClipsCollectionController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { - private lazy var mediaClipsCollectionView = MediaClipsCollectionView() + private lazy var mediaClipsCollectionView = MediaClipsCollectionView(settings: settings.clipsCollectionViewSettings) private var clips: [MediaClip] private var draggingClipIndex: IndexPath? @@ -42,8 +42,15 @@ final class MediaClipsCollectionController: UIViewController, UICollectionViewDe weak var delegate: MediaClipsCollectionControllerDelegate? - init() { + struct Settings { + let clipsCollectionViewSettings: MediaClipsCollectionView.Settings + } + + private let settings: Settings + + init(settings: Settings) { clips = [] + self.settings = settings super.init(nibName: .none, bundle: .none) } diff --git a/Classes/MediaClips/MediaClipsCollectionView.swift b/Classes/MediaClips/MediaClipsCollectionView.swift index 6d7d9881d..9d3cc1194 100644 --- a/Classes/MediaClips/MediaClipsCollectionView.swift +++ b/Classes/MediaClips/MediaClipsCollectionView.swift @@ -18,9 +18,16 @@ final class MediaClipsCollectionView: UIView { static let height = MediaClipsCollectionViewConstants.height let collectionView: UICollectionView let fadeOutGradient = CAGradientLayer() + + struct Settings { + let showsFadeOutGradient: Bool + } + + private let settings: Settings - init() { + init(settings: Settings) { collectionView = createCollectionView() + self.settings = settings super.init(frame: .zero) @@ -49,7 +56,9 @@ extension MediaClipsCollectionView { private func setUpViews() { collectionView.add(into: self) collectionView.clipsToBounds = false - setFadeOutGradient() + if settings.showsFadeOutGradient { + setFadeOutGradient() + } } private func setFadeOutGradient() { diff --git a/Classes/MediaClips/MediaClipsEditorViewController.swift b/Classes/MediaClips/MediaClipsEditorViewController.swift index f96823468..cb5affff4 100644 --- a/Classes/MediaClips/MediaClipsEditorViewController.swift +++ b/Classes/MediaClips/MediaClipsEditorViewController.swift @@ -50,9 +50,9 @@ final class MediaClipsEditorViewController: UIViewController, MediaClipsCollecti /// This needs to be dynamic because it will be observed @objc private(set) dynamic var hasClips: Bool = false - init(showsAddButton: Bool = false) { + init(showsAddButton: Bool = false, collectionSettings: MediaClipsCollectionController.Settings) { editorView = MediaClipsEditorView(showsAddButton: showsAddButton) - collectionController = MediaClipsCollectionController() + collectionController = MediaClipsCollectionController(settings: collectionSettings) super.init(nibName: .none, bundle: .none) collectionController.delegate = self editorView.delegate = self diff --git a/Classes/Settings/CameraSettings.swift b/Classes/Settings/CameraSettings.swift index ead7f7290..c7f242d78 100644 --- a/Classes/Settings/CameraSettings.swift +++ b/Classes/Settings/CameraSettings.swift @@ -309,6 +309,9 @@ public struct CameraFeatures { /// Animate the movement of control in the editor public var animateEditorControls: Bool = DefaultCameraSettings.animateEditorControls + /// Whether to show a shadow to the sides of the media clips + public var showShadowOverMediaClips: Bool = DefaultCameraSettings.showShadowOverMediaClips + /// The Font Selector button uses the currently selected font for its label public var fontSelectorUsesFont: Bool = DefaultCameraSettings.fontFamilyUsesFont @@ -377,4 +380,5 @@ private struct DefaultCameraSettings { static let gifCameraShouldStartGIFMaker: Bool = false static let fontFamilyUsesFont: Bool = false static let animateEditorControls: Bool = true + static let showShadowOverMediaClips: Bool = true }