mirror of
https://github.com/laosb/CropImage.git
synced 2025-04-30 15:41:08 +00:00
Working version.
This commit is contained in:
parent
645e4d6b2c
commit
45c2b0ac58
1 changed files with 104 additions and 14 deletions
|
@ -6,15 +6,62 @@
|
|||
//
|
||||
|
||||
import SwiftUI
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
#endif
|
||||
|
||||
public struct CropImageView: View {
|
||||
public enum RenderError: Error {
|
||||
case imageRendererReturnedNil
|
||||
}
|
||||
|
||||
var image: PlatformImage
|
||||
var targetSize: CGSize
|
||||
var onCrop: (PlatformImage) -> Void
|
||||
var targetScale: CGFloat = 1
|
||||
var onCrop: (Result<PlatformImage, Error>) -> Void
|
||||
|
||||
@State private var offset: CGSize = .zero
|
||||
@State private var scale: CGFloat = 1
|
||||
|
||||
@MainActor
|
||||
func crop() throws -> PlatformImage {
|
||||
let snapshotView = MoveAndScalableImageView(offset: $offset, scale: $scale, image: image)
|
||||
.frame(width: targetSize.width, height: targetSize.height)
|
||||
if #available(iOS 16.0, macOS 13.0, *) {
|
||||
let renderer = ImageRenderer(content: snapshotView)
|
||||
renderer.scale = targetScale
|
||||
#if os(iOS)
|
||||
if let image = renderer.uiImage {
|
||||
return image
|
||||
} else {
|
||||
throw RenderError.imageRendererReturnedNil
|
||||
}
|
||||
#elseif os(macOS)
|
||||
if let image = renderer.nsImage {
|
||||
return image
|
||||
} else {
|
||||
throw RenderError.imageRendererReturnedNil
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
#if os(macOS)
|
||||
fatalError("Cropping is not supported on macOS versions before Ventrura 13.0.")
|
||||
#elseif os(iOS)
|
||||
let window = UIWindow(frame: CGRect(origin: .zero, size: targetSize))
|
||||
let hosting = UIHostingController(rootView: snapshotView)
|
||||
hosting.view.frame = window.frame
|
||||
window.addSubview(hosting.view)
|
||||
window.makeKeyAndVisible()
|
||||
UIGraphicsBeginImageContextWithOptions(hosting.view.bounds.size, false, targetScale)
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
hosting.view.layer.render(in: context)
|
||||
let image = UIGraphicsGetImageFromCurrentImageContext()!
|
||||
UIGraphicsEndImageContext()
|
||||
return image
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
ZStack {
|
||||
MoveAndScalableImageView(offset: $offset, scale: $scale, image: image)
|
||||
|
@ -22,36 +69,79 @@ public struct CropImageView: View {
|
|||
.fill(style: FillStyle(eoFill: true))
|
||||
.foregroundColor(.black.opacity(0.6))
|
||||
.allowsHitTesting(false)
|
||||
VStack {
|
||||
Spacer()
|
||||
HStack {
|
||||
Spacer()
|
||||
Button { Task {
|
||||
do {
|
||||
onCrop(.success(try crop()))
|
||||
} catch {
|
||||
onCrop(.failure(error))
|
||||
}
|
||||
} } label: {
|
||||
Label("Crop", systemImage: "checkmark.circle.fill")
|
||||
.font(.title2)
|
||||
.foregroundColor(.accentColor)
|
||||
.labelStyle(.iconOnly)
|
||||
.padding(1)
|
||||
.background(
|
||||
Circle().fill(.white)
|
||||
)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CropImageView_Previews: PreviewProvider {
|
||||
struct PreviewView: View {
|
||||
@State private var croppedImage: PlatformImage? = nil
|
||||
@State private var targetSize: CGSize = .init(width: 100, height: 100)
|
||||
@State private var result: Result<PlatformImage, Error>? = nil
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
CropImageView(
|
||||
image: .init(contentsOfFile: "/Users/laosb/Downloads/png.png")!,
|
||||
targetSize: .init(width: 100, height: 100)
|
||||
) { _ in
|
||||
|
||||
}
|
||||
if let croppedImage {
|
||||
#if os(macOS)
|
||||
Image(nsImage: croppedImage)
|
||||
#elseif os(iOS)
|
||||
Image(uiImage: croppedImage)
|
||||
#endif
|
||||
} else {
|
||||
Text("Press \(Image(systemName: "checkmark.circle.fill")) to crop.")
|
||||
targetSize: targetSize
|
||||
) { result = $0 }
|
||||
Form {
|
||||
Section {
|
||||
TextField("Width", value: $targetSize.width, formatter: NumberFormatter())
|
||||
TextField("Height", value: $targetSize.height, formatter: NumberFormatter())
|
||||
} header: { Text("Crop Target Size") }
|
||||
Section {
|
||||
if let result {
|
||||
switch result {
|
||||
case let .success(croppedImage):
|
||||
#if os(macOS)
|
||||
Image(nsImage: croppedImage)
|
||||
#elseif os(iOS)
|
||||
Image(uiImage: croppedImage)
|
||||
#endif
|
||||
case let .failure(error):
|
||||
Text(error.localizedDescription)
|
||||
.foregroundColor(.red)
|
||||
}
|
||||
} else {
|
||||
Text("Press \(Image(systemName: "checkmark.circle.fill")) to crop.")
|
||||
}
|
||||
} header: { Text("Result") }
|
||||
}
|
||||
#if os(macOS)
|
||||
.formStyle(.grouped)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static var previews: some View {
|
||||
PreviewView()
|
||||
#if os(macOS)
|
||||
.frame(minHeight: 750)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue