MijickCamera/Sources/Internal/UI/MCamera/MCamera.swift

182 lines
6.2 KiB
Swift
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// MCamera.swift of MijickCamera
//
// Created by Tomasz Kurylik. Sending from Kraków!
// - Mail: tomasz.kurylik@mijick.com
// - GitHub: https://github.com/FulcrumOne
// - Medium: https://medium.com/@mijick
//
// Copyright ©2024 Mijick. All rights reserved.
import SwiftUI
/**
A view that displays a camera with state-specific screens.
By default, it includes three screens that change depending on the status of the camera; **Error Screen**, **Camera Screen** and **Captured Media Screen**.
Handles issues related to asking for permissions, and if permissions are not granted, it displays the **Error Screen**.
Optionally shows the **Captured Media Screen**, which is displayed after the user captures an image or video.
# Customization
All of the MCamera's default settings can be changed during initialisation.
- important: To start a camera session, simply call the ``startSession()`` method. For more details, see the **Usage** section.
## Camera Screens
Use one of the methods below to change the default screens:
- ``setCameraScreen(_:)``
- ``setCapturedMediaScreen(_:)``
- ``setErrorScreen(_:)``
- tip: To disable displaying captured media, call the ``setCapturedMediaScreen(_:)`` method with a nil value.
## Actions after capturing media
Use one of the methods below to set actions that will be called after capturing media:
- ``onImageCaptured(_:)``
- ``onVideoCaptured(_:)``
- note: If there is no **Captured Media Screen**, the action is called immediately after the media is captured, otherwise it is triggered after the user accepts the captured media in the **Captured Media Screen**.
## Camera Configuration
To change the initial camera settings, use the following methods:
- ``setCameraOutputType(_:)``
- ``setCameraPosition(_:)``
- ``setAudioAvailability(_:)``
- ``setZoomFactor(_:)``
- ``setFlashMode(_:)``
- ``setLightMode(_:)``
- ``setResolution(_:)``
- ``setFrameRate(_:)``
- ``setCameraExposureDuration(_:)``
- ``setCameraTargetBias(_:)``
- ``setCameraISO(_:)``
- ``setCameraExposureMode(_:)``
- ``setCameraHDRMode(_:)``
- ``setCameraFilters(_:)``
- ``setMirrorOutput(_:)``
- ``setGridVisibility(_:)``
- ``setFocusImage(_:)``
- ``setFocusImageColor(_:)``
- ``setFocusImageSize(_:)``
- important: Note that if you try to set a value that exceeds the camera's capabilities, the camera will automatically set the closest possible value and show you which value has been set.
## Other
There are other methods that you can use to customize your experience:
- ``setCloseMCameraAction(_:)``
- ``lockCameraInPortraitOrientation(_:)``
# Usage
```swift
struct ContentView: View {
var body: some View {
MCamera()
.setCameraFilters([.init(name: "CISepiaTone")!])
.setCameraPosition(.back)
.setCameraOutputType(.video)
.setAudioAvailability(false)
.setResolution(.hd4K3840x2160)
.setFrameRate(30)
.setZoomFactor(1.2)
.setCameraISO(3)
.setCameraTargetBias(1.2)
.setLightMode(.on)
.setFlashMode(.auto)
// MUST BE CALLED!
.startSession()
}
}
```
*/
public struct MCamera: View {
@ObservedObject var manager: CameraManager
@Namespace var namespace
var config: Config = .init()
public var body: some View { if config.isCameraConfigured {
ZStack(content: createContent)
.onDisappear(perform: onDisappear)
.onChange(of: manager.attributes.capturedMedia, perform: onCapturedMediaChange)
}}
}
private extension MCamera {
@ViewBuilder func createContent() -> some View {
if let error = manager.attributes.error { createErrorScreen(error) }
else if let capturedMedia = manager.attributes.capturedMedia, config.capturedMediaScreen != nil { createCapturedMediaScreen(capturedMedia) }
else { createCameraScreen() }
}
}
private extension MCamera {
func createErrorScreen(_ error: MCameraError) -> some View {
config.errorScreen(error, config.closeMCameraAction).erased()
}
func createCapturedMediaScreen(_ media: MCameraMedia) -> some View {
config.capturedMediaScreen?(media, namespace, onCapturedMediaRejected, onCapturedMediaAccepted)
.erased()
.onAppear(perform: onCaptureMediaScreenAppear)
}
func createCameraScreen() -> some View {
config.cameraScreen(manager, namespace, config.closeMCameraAction)
.erased()
.onAppear(perform: onCameraAppear)
.onDisappear(perform: onCameraDisappear)
}
}
// MARK: - ACTIONS
// MARK: MCamera
private extension MCamera {
func onDisappear() {
lockScreenOrientation(nil)
manager.cancel()
}
func onCapturedMediaChange(_ capturedMedia: MCameraMedia?) {
guard let capturedMedia, config.capturedMediaScreen == nil else { return }
notifyUserOfMediaCaptured(capturedMedia)
}
}
private extension MCamera {
func lockScreenOrientation(_ orientation: UIInterfaceOrientationMask?) {
config.appDelegate?.orientationLock = orientation ?? .all
UINavigationController.attemptRotationToDeviceOrientation()
}
func notifyUserOfMediaCaptured(_ capturedMedia: MCameraMedia) {
if let image = capturedMedia.getImage() { config.imageCapturedAction(image, .init(mCamera: self)) }
else if let video = capturedMedia.getVideo() { config.videoCapturedAction(video, .init(mCamera: self)) }
}
}
// MARK: Camera Screen
private extension MCamera {
func onCameraAppear() { Task {
do {
try await manager.setup()
lockScreenOrientation(.portrait)
} catch { print("(MijickCamera) ERROR DURING SETUP: \(error)") }
}}
func onCameraDisappear() {
manager.cancel()
}
}
// MARK: Captured Media Screen
private extension MCamera {
func onCaptureMediaScreenAppear() {
lockScreenOrientation(nil)
}
func onCapturedMediaRejected() {
manager.setCapturedMedia(nil)
}
func onCapturedMediaAccepted() {
guard let capturedMedia = manager.attributes.capturedMedia else { return }
notifyUserOfMediaCaptured(capturedMedia)
}
}