aboutsummaryrefslogtreecommitdiff
path: root/Sources
diff options
context:
space:
mode:
authorleoz <leoz@yahoo.com>2024-01-22 21:14:12 -0500
committerleoz <leoz@yahoo.com>2024-01-22 21:14:12 -0500
commit0156c27aabfd82e6450604a8f32c74a58bcff8f9 (patch)
tree11e5a08d1791edd1bb9d93959d4effe406360d13 /Sources
parent7b9aa6b60e0914a5fba44fa2ba78dfaa566924e4 (diff)
Crop image to circle
Diffstat (limited to 'Sources')
-rw-r--r--Sources/SwiftyCrop/Models/CropViewModel.swift87
-rw-r--r--Sources/SwiftyCrop/View/CropView.swift10
2 files changed, 88 insertions, 9 deletions
diff --git a/Sources/SwiftyCrop/Models/CropViewModel.swift b/Sources/SwiftyCrop/Models/CropViewModel.swift
index e27f1ad..761478a 100644
--- a/Sources/SwiftyCrop/Models/CropViewModel.swift
+++ b/Sources/SwiftyCrop/Models/CropViewModel.swift
@@ -40,15 +40,91 @@ class CropViewModel: ObservableObject {
}
/**
- Crops the image to the part that is dragged/zoomed inside the view. Cropped image will **always** be a square, no matter what mask shape is used.
+ Crops the image to the part that is dragged/zoomed inside the view. Cropped image will be a square.
- Parameters:
- image: The UIImage to crop
- Returns: A cropped UIImage if the cropping operation is successful; otherwise nil.
*/
- func crop(_ image: UIImage) -> UIImage? {
+ func cropToSquare(_ image: UIImage) -> UIImage? {
guard let orientedImage = image.correctlyOriented else {
return nil
}
+
+ let cropRect = calculateCropRect(orientedImage)
+
+ guard let cgImage = orientedImage.cgImage,
+ let result = cgImage.cropping(to: cropRect) else {
+ return nil
+ }
+
+ return UIImage(cgImage: result)
+ }
+
+ /**
+ Crops the image to the part that is dragged/zoomed inside the view. Cropped image will be a circle.
+ - Parameters:
+ - image: The UIImage to crop
+ - Returns: A cropped UIImage if the cropping operation is successful; otherwise nil.
+ */
+ func cropToCircle(_ image: UIImage) -> UIImage? {
+ guard let orientedImage = image.correctlyOriented else {
+ return nil
+ }
+
+ let cropRect = calculateCropRect(orientedImage)
+
+ // A circular crop results in some transparency in the
+ // cropped image, so set opaque to false to ensure the
+ // cropped image does not include a background fill
+ let imageRendererFormat = orientedImage.imageRendererFormat
+ imageRendererFormat.opaque = false
+
+ // UIGraphicsImageRenderer().image provides a block
+ // interface to draw into in a new UIImage
+ let circleCroppedImage = UIGraphicsImageRenderer(
+ // The cropRect.size is the size of
+ // the resulting circleCroppedImage
+ size: cropRect.size,
+ format: imageRendererFormat).image { context in
+
+ // The drawRect is the cropRect starting at (0,0)
+ let drawRect = CGRect(
+ origin: .zero,
+ size: cropRect.size
+ )
+
+ // addClip on a UIBezierPath will clip all contents
+ // outside of the UIBezierPath drawn after addClip
+ // is called, in this case, drawRect is a circle so
+ // the UIBezierPath clips drawing to the circle
+ UIBezierPath(ovalIn: drawRect).addClip()
+
+ // The drawImageRect is offsets the image’s bounds
+ // such that the circular clip is at the center of
+ // the image
+ let drawImageRect = CGRect(
+ origin: CGPoint(
+ x: -cropRect.origin.x,
+ y: -cropRect.origin.y
+ ),
+ size: orientedImage.size
+ )
+
+ // Draws the orientedImage inside of the
+ // circular clip
+ orientedImage.draw(in: drawImageRect)
+ }
+
+ return circleCroppedImage
+ }
+
+ /**
+ Calculates the rectangle to crop.
+ - Parameters:
+ - image: The UIImage to calculate the rectangle to crop for
+ - Returns: A CGRect representing the rectangle to crop.
+ */
+ private func calculateCropRect(_ orientedImage: UIImage) -> CGRect {
// The relation factor of the originals image width/height and the width/height of the image displayed in the view (initial)
let factor = min((orientedImage.size.width / imageSizeInView.width), (orientedImage.size.height / imageSizeInView.height))
let centerInOriginalImage = CGPoint(x: orientedImage.size.width / 2, y: orientedImage.size.height / 2)
@@ -73,12 +149,7 @@ class CropViewModel: ObservableObject {
height: cropRectDimension
)
- guard let cgImage = orientedImage.cgImage,
- let result = cgImage.cropping(to: cropRect) else {
- return nil
- }
-
- return UIImage(cgImage: result)
+ return cropRect
}
}
diff --git a/Sources/SwiftyCrop/View/CropView.swift b/Sources/SwiftyCrop/View/CropView.swift
index 3cb453a..05b1a66 100644
--- a/Sources/SwiftyCrop/View/CropView.swift
+++ b/Sources/SwiftyCrop/View/CropView.swift
@@ -107,7 +107,7 @@ struct CropView: View {
Spacer()
Button {
- onComplete(viewModel.crop(image))
+ onComplete(cropImage())
dismiss()
} label: {
Text("save_button", tableName: localizableTableName, bundle: .module)
@@ -120,6 +120,14 @@ struct CropView: View {
.background(.black)
}
+ private func cropImage() -> UIImage? {
+ if maskShape == .circle {
+ viewModel.cropToCircle(image)
+ } else {
+ viewModel.cropToSquare(image)
+ }
+ }
+
private struct MaskShapeView: View {
let maskShape: MaskShape