jellyflood/Swiftfin/Views/PhotoPickerView/Components/PhotoCropView.swift

162 lines
4.2 KiB
Swift

//
// Swiftfin is subject to the terms of the Mozilla Public
// License, v2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
//
// Copyright (c) 2025 Jellyfin & Jellyfin Contributors
//
import Mantis
import SwiftUI
struct PhotoCropView: View {
// MARK: - State, Observed, & Environment Objects
@StateObject
private var proxy: _PhotoCropView.Proxy = .init()
// MARK: - Image Variable
let isSaving: Bool
let image: UIImage
let cropShape: Mantis.CropShapeType
let presetRatio: Mantis.PresetFixedRatioType
let onSave: (UIImage) -> Void
let onCancel: () -> Void
// MARK: - Body
var body: some View {
_PhotoCropView(
initialImage: image,
cropShape: cropShape,
presetRatio: presetRatio,
proxy: proxy,
onImageCropped: onSave
)
.topBarTrailing {
Button(L10n.rotate, systemImage: "rotate.right") {
proxy.rotate()
}
if isSaving {
Button(L10n.cancel, action: onCancel)
.buttonStyle(.toolbarPill(.red))
} else {
Button(L10n.save) {
proxy.crop()
}
.buttonStyle(.toolbarPill)
}
}
.toolbar {
ToolbarItem(placement: .principal) {
if isSaving {
ProgressView()
} else {
Button(L10n.reset) {
proxy.reset()
}
.foregroundStyle(.yellow)
.disabled(isSaving)
}
}
}
.ignoresSafeArea()
.background {
Color.black
}
}
}
// MARK: - Photo Crop View
private struct _PhotoCropView: UIViewControllerRepresentable {
class Proxy: ObservableObject {
weak var cropViewController: CropViewController?
func crop() {
cropViewController?.crop()
}
func reset() {
cropViewController?.didSelectReset()
}
func rotate() {
cropViewController?.didSelectClockwiseRotate()
}
}
let initialImage: UIImage
let cropShape: Mantis.CropShapeType
let presetRatio: Mantis.PresetFixedRatioType
let proxy: Proxy
let onImageCropped: (UIImage) -> Void
func makeUIViewController(context: Context) -> some UIViewController {
var config = Mantis.Config()
config.cropViewConfig.backgroundColor = .black.withAlphaComponent(0.9)
config.cropViewConfig.cropShapeType = cropShape
config.presetFixedRatioType = presetRatio
config.showAttachedCropToolbar = false
let cropViewController = Mantis.cropViewController(
image: initialImage,
config: config
)
cropViewController.delegate = context.coordinator
context.coordinator.onImageCropped = onImageCropped
proxy.cropViewController = cropViewController
return cropViewController
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator()
}
class Coordinator: CropViewControllerDelegate {
var onImageCropped: ((UIImage) -> Void)?
func cropViewControllerDidCrop(
_ cropViewController: CropViewController,
cropped: UIImage,
transformation: Transformation,
cropInfo: CropInfo
) {
onImageCropped?(cropped)
}
func cropViewControllerDidCancel(
_ cropViewController: CropViewController,
original: UIImage
) {}
func cropViewControllerDidFailToCrop(
_ cropViewController: CropViewController,
original: UIImage
) {}
func cropViewControllerDidBeginResize(
_ cropViewController: CropViewController
) {}
func cropViewControllerDidEndResize(
_ cropViewController: Mantis.CropViewController,
original: UIImage,
cropInfo: Mantis.CropInfo
) {}
}
}