Skip to content

Commit

Permalink
feat: add CropAuxiliaryIndicatorConfig to allow custom CropAuxiliaryI…
Browse files Browse the repository at this point in the history
…ndicator colors (#405)

fix some compile warnings

change the value of minOverLayerUnit

avoid that the cropping masks can not cover the image in case of a small minimumCropBoxSize
  • Loading branch information
guoyingtao authored Jul 20, 2024
1 parent 899b717 commit bc50ce7
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 64 deletions.
28 changes: 14 additions & 14 deletions Example/EmbeddedCropViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class EmbeddedCropViewController: UIViewController {
return
}

cropViewController?.update(image.addFilter(filter: .Mono))
cropViewController?.update(image.addFilter(filter: .mono))
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
Expand Down Expand Up @@ -185,28 +185,28 @@ extension EmbeddedCropViewController: CropViewControllerDelegate {
}
}

enum FilterType : String {
case Chrome = "CIPhotoEffectChrome"
case Fade = "CIPhotoEffectFade"
case Instant = "CIPhotoEffectInstant"
case Mono = "CIPhotoEffectMono"
case Noir = "CIPhotoEffectNoir"
case Process = "CIPhotoEffectProcess"
case Tonal = "CIPhotoEffectTonal"
case Transfer = "CIPhotoEffectTransfer"
enum FilterType: String {
case chrome = "CIPhotoEffectChrome"
case fade = "CIPhotoEffectFade"
case instant = "CIPhotoEffectInstant"
case mono = "CIPhotoEffectMono"
case noir = "CIPhotoEffectNoir"
case process = "CIPhotoEffectProcess"
case tonal = "CIPhotoEffectTonal"
case transfer = "CIPhotoEffectTransfer"
}

extension UIImage {
func addFilter(filter : FilterType) -> UIImage {
func addFilter(filter: FilterType) -> UIImage {
let filter = CIFilter(name: filter.rawValue)
// convert UIImage to CIImage and set as input
// Convert UIImage to CIImage and set as input
let ciInput = CIImage(image: self)
filter?.setValue(ciInput, forKey: "inputImage")
// get output CIImage, render as CGImage first to retain proper UIImage scale
// Get output CIImage, render as CGImage first to retain proper UIImage scale
let ciOutput = filter?.outputImage
let ciContext = CIContext()
let cgImage = ciContext.createCGImage(ciOutput!, from: (ciOutput?.extent)!)
//Return the image
// Return the image
return UIImage(cgImage: cgImage!)
}
}
19 changes: 2 additions & 17 deletions Example/zh-Hans.lproj/Main.storyboard
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="22505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="ipad7_9" orientation="portrait" layout="fullscreen" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22504"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
Expand Down Expand Up @@ -181,21 +181,6 @@
</objects>
<point key="canvasLocation" x="1117.5999999999999" y="5.8470764617691158"/>
</scene>
<!--Crop View Controller-->
<scene sceneID="djG-LZ-hRH">
<objects>
<viewController id="xJq-Ev-XcJ" customClass="CropViewController" customModule="Mantis" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="nLp-HP-0xC">
<rect key="frame" x="0.0" y="0.0" width="744" height="1133"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<viewLayoutGuide key="safeArea" id="0JZ-7h-zil"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="TRU-F3-FYg" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1042" y="819"/>
</scene>
</scenes>
<resources>
<image name="sunflower" width="3648" height="5472"/>
Expand Down
4 changes: 4 additions & 0 deletions Mantis.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
F24DCFE829AB0A7000D8E8C1 /* CropAuxiliaryIndicatorViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F24DCFE729AB0A7000D8E8C1 /* CropAuxiliaryIndicatorViewTests.swift */; };
F24DCFEC29AF2A6000D8E8C1 /* CropAuxiliaryIndicatorView+Accessibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = F24DCFEB29AF2A6000D8E8C1 /* CropAuxiliaryIndicatorView+Accessibility.swift */; };
F251B36E299B9A52006325B0 /* CropWorkbenchViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F251B36D299B9A52006325B0 /* CropWorkbenchViewTests.swift */; };
F28FF64A2C4A492A002F5F06 /* CropAuxiliaryIndicatorConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = F28FF6492C4A492A002F5F06 /* CropAuxiliaryIndicatorConfig.swift */; };
F2AD53412A46DB8B00AE9C87 /* ImageAutoAdjustHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2AD53402A46DB8B00AE9C87 /* ImageAutoAdjustHelper.swift */; };
F2B4FDA42A3B661B00667F22 /* SlideDial.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2B4FDA32A3B661B00667F22 /* SlideDial.swift */; };
F2B4FDA72A3B69B600667F22 /* SlideRulerPositionHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2B4FDA52A3B69B600667F22 /* SlideRulerPositionHelper.swift */; };
Expand Down Expand Up @@ -211,6 +212,7 @@
F24DCFE729AB0A7000D8E8C1 /* CropAuxiliaryIndicatorViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CropAuxiliaryIndicatorViewTests.swift; sourceTree = "<group>"; };
F24DCFEB29AF2A6000D8E8C1 /* CropAuxiliaryIndicatorView+Accessibility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CropAuxiliaryIndicatorView+Accessibility.swift"; sourceTree = "<group>"; };
F251B36D299B9A52006325B0 /* CropWorkbenchViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CropWorkbenchViewTests.swift; sourceTree = "<group>"; };
F28FF6492C4A492A002F5F06 /* CropAuxiliaryIndicatorConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CropAuxiliaryIndicatorConfig.swift; sourceTree = "<group>"; };
F2AD53402A46DB8B00AE9C87 /* ImageAutoAdjustHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageAutoAdjustHelper.swift; sourceTree = "<group>"; };
F2B4FDA32A3B661B00667F22 /* SlideDial.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlideDial.swift; sourceTree = "<group>"; };
F2B4FDA52A3B69B600667F22 /* SlideRulerPositionHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SlideRulerPositionHelper.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -507,6 +509,7 @@
OBJ_36 /* Mantis.swift */,
6659429C2877DAE80096A5DB /* Config.swift */,
663FCF3728866DDA003D3B9E /* CropViewConfig.swift */,
F28FF6492C4A492A002F5F06 /* CropAuxiliaryIndicatorConfig.swift */,
663FCF3828866DDA003D3B9E /* CropToolbarConfig.swift */,
6659429E2877DC8A0096A5DB /* Enum.swift */,
665942A02877DD1C0096A5DB /* CropData.swift */,
Expand Down Expand Up @@ -795,6 +798,7 @@
FEDAAD8625205CC300D95667 /* RatioSelector.swift in Sources */,
OBJ_93 /* CropVisualEffectView.swift in Sources */,
269AF94227437EE400F7FAF6 /* RotationDialConfig.swift in Sources */,
F28FF64A2C4A492A002F5F06 /* CropAuxiliaryIndicatorConfig.swift in Sources */,
OBJ_94 /* RatioOptions.swift in Sources */,
OBJ_95 /* Angle.swift in Sources */,
OBJ_97 /* RotationCalculator.swift in Sources */,
Expand Down
41 changes: 41 additions & 0 deletions Sources/Mantis/CropAuxiliaryIndicatorConfig.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// CropAuxiliaryIndicatorConfig.swift
// Mantis
//
// Created by Yingtao Guo on 7/19/24.
//

import UIKit

public struct CropAuxiliaryIndicatorConfig {
/**
This value is for how easy to drag crop box. The bigger, the easier.
*/
public var cropBoxHotAreaUnit: CGFloat = 32 {
didSet {
assert(cropBoxHotAreaUnit > 0)
}
}

public var disableCropBoxDeformation = false
public var style: CropAuxiliaryIndicatorStyleType = .normal

public var borderNormalColor = UIColor.white

/**
The color of the border when showing which border is touched currently.
*/
public var borderHintColor = UIColor.white

public var cornerHandleColor = UIColor.white
public var edgeLineHandleColor = UIColor.white

public var gridMainColor = UIColor.white

/**
This property is only used when rotating the image
*/
public var gridSecondaryColor = UIColor.lightGray

public init() {}
}
43 changes: 27 additions & 16 deletions Sources/Mantis/CropView/CropAuxiliaryIndicatorView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
import UIKit

final class CropAuxiliaryIndicatorView: UIView, CropAuxiliaryIndicatorViewProtocol {
private var boarderNormalColor = UIColor.white
private var boarderHintColor = UIColor.white
private var borderNormalColor = UIColor.white
private var borderHintColor = UIColor.white
private var cornerHandleColor = UIColor.white
private var edgeLineHandleColor = UIColor.white
private let cornerHandleLength = CGFloat(20.0)
private let edgeLineHandleLength = CGFloat(30.0)
private let handleThickness = CGFloat(3.0)
Expand Down Expand Up @@ -49,16 +51,22 @@ final class CropAuxiliaryIndicatorView: UIView, CropAuxiliaryIndicatorViewProtoc
}
}

init(frame: CGRect,
cropBoxHotAreaUnit: CGFloat,
disableCropBoxDeformation: Bool = false,
style: CropAuxiliaryIndicatorStyleType = .normal) {
init(frame: CGRect, config: CropAuxiliaryIndicatorConfig = CropAuxiliaryIndicatorConfig()) {
super.init(frame: frame)
clipsToBounds = false
backgroundColor = .clear
self.cropBoxHotAreaUnit = cropBoxHotAreaUnit
self.disableCropBoxDeformation = disableCropBoxDeformation
self.style = style

cropBoxHotAreaUnit = config.cropBoxHotAreaUnit
disableCropBoxDeformation = config.disableCropBoxDeformation
style = config.style

borderNormalColor = config.borderNormalColor
borderHintColor = config.borderHintColor
cornerHandleColor = config.cornerHandleColor
edgeLineHandleColor = config.edgeLineHandleColor
gridMainColor = config.gridMainColor
gridSecondaryColor = config.gridSecondaryColor

setup()
}

Expand All @@ -67,15 +75,18 @@ final class CropAuxiliaryIndicatorView: UIView, CropAuxiliaryIndicatorViewProtoc
backgroundColor = .clear
}

private func createNewLine() -> UIView {
private func createNewLine(withNormalColor normalColor: UIColor = .white) -> UIView {
let view = UIView()
view.frame = .zero

if style == .normal {
view.backgroundColor = .white
view.backgroundColor = normalColor
} else {
view.backgroundColor = .clear
}

addSubview(view)

return view
}

Expand All @@ -85,19 +96,19 @@ final class CropAuxiliaryIndicatorView: UIView, CropAuxiliaryIndicatorViewProtoc
borderLine.layer.borderWidth = borderThickness

if style == .normal {
borderLine.layer.borderColor = boarderNormalColor.cgColor
hintLine.backgroundColor = boarderHintColor
borderLine.layer.borderColor = borderNormalColor.cgColor
hintLine.backgroundColor = borderHintColor
} else {
borderLine.layer.borderColor = UIColor.clear.cgColor
hintLine.backgroundColor = .clear
}

for _ in 0..<8 {
cornerHandles.append(createNewLine())
cornerHandles.append(createNewLine(withNormalColor: cornerHandleColor))
}

for _ in 0..<4 {
edgeLineHandles.append(createNewLine())
edgeLineHandles.append(createNewLine(withNormalColor: edgeLineHandleColor))
}

setupAccessibilityHelperViews()
Expand Down Expand Up @@ -177,7 +188,7 @@ final class CropAuxiliaryIndicatorView: UIView, CropAuxiliaryIndicatorViewProtoc
borderLine.layer.backgroundColor = UIColor.clear.cgColor
borderLine.layer.borderWidth = borderThickness
if style == .normal {
borderLine.layer.borderColor = boarderNormalColor.cgColor
borderLine.layer.borderColor = borderNormalColor.cgColor
} else {
borderLine.layer.borderColor = UIColor.clear.cgColor
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/Mantis/CropView/CropView+Touches.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ extension CropView {
return rotationControlView.getTouchTarget(with: pointInRotationControlView)
}

if !cropViewConfig.disableCropBoxDeformation && isHitGridOverlayView(by: newPoint) {
if !cropViewConfig.cropAuxiliaryIndicatorConfig.disableCropBoxDeformation && isHitGridOverlayView(by: newPoint) {
return self
}

Expand All @@ -29,7 +29,7 @@ extension CropView {
}

private func isHitGridOverlayView(by touchPoint: CGPoint) -> Bool {
let hotAreaUnit = cropViewConfig.cropBoxHotAreaUnit
let hotAreaUnit = cropViewConfig.cropAuxiliaryIndicatorConfig.cropBoxHotAreaUnit

return cropAuxiliaryIndicatorView.frame.insetBy(dx: -hotAreaUnit/2, dy: -hotAreaUnit/2).contains(touchPoint)
&& !cropAuxiliaryIndicatorView.frame.insetBy(dx: hotAreaUnit/2, dy: hotAreaUnit/2).contains(touchPoint)
Expand Down
4 changes: 2 additions & 2 deletions Sources/Mantis/CropView/CropView+UIScrollViewDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ extension CropView: UIScrollViewDelegate {
}

func scrollViewDidZoom(_ scrollView: UIScrollView) {
guard scrollView.subviews.count > 0 else {
guard !scrollView.subviews.isEmpty else {
return
}

Expand All @@ -34,7 +34,7 @@ extension CropView: UIScrollViewDelegate {
let offsetX: CGFloat = max((scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5, 0.0)
let offsetY: CGFloat = max((scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5, 0.0)

subView.center = CGPointMake(scrollView.contentSize.width * 0.5 + offsetX, scrollView.contentSize.height * 0.5 + offsetY)
subView.center = CGPoint(x: scrollView.contentSize.width * 0.5 + offsetX, y: scrollView.contentSize.height * 0.5 + offsetY)
}

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
Expand Down
42 changes: 34 additions & 8 deletions Sources/Mantis/CropViewConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,38 @@ public struct CropViewConfig {
/**
This value is for how easy to drag crop box. The bigger, the easier.
*/
public var cropBoxHotAreaUnit: CGFloat = 32 {
didSet {
assert(cropBoxHotAreaUnit > 0)
@available(*, deprecated, message: "Use cropAuxiliaryIndicatorStyle.cropBoxHotAreaUnit instead")
public var cropBoxHotAreaUnit: CGFloat {
get {
cropAuxiliaryIndicatorConfig.cropBoxHotAreaUnit
}
set {
cropAuxiliaryIndicatorConfig.cropBoxHotAreaUnit = newValue
}
}

@available(*, deprecated, message: "Use cropAuxiliaryIndicatorStyle.disableCropBoxDeformation instead")
public var disableCropBoxDeformation: Bool {
get {
cropAuxiliaryIndicatorConfig.disableCropBoxDeformation
}
set {
cropAuxiliaryIndicatorConfig.disableCropBoxDeformation = newValue
}
}

@available(*, deprecated, message: "Use cropAuxiliaryIndicatorConfig.style instead")
public var cropAuxiliaryIndicatorStyle: CropAuxiliaryIndicatorStyleType {
get {
cropAuxiliaryIndicatorConfig.style
}
set {
cropAuxiliaryIndicatorConfig.style = newValue
}
}

public var cropAuxiliaryIndicatorConfig = CropAuxiliaryIndicatorConfig()

public var cropShapeType: CropShapeType = .rect

public var cropBorderWidth: CGFloat = 0 {
Expand All @@ -31,8 +57,6 @@ public struct CropViewConfig {

public var cropMaskVisualEffectType: CropMaskVisualEffectType = .blurDark

public var cropAuxiliaryIndicatorStyle: CropAuxiliaryIndicatorStyleType = .normal

public var presetTransformationType: PresetTransformationType = .none

public var minimumZoomScale: CGFloat = 1 {
Expand Down Expand Up @@ -66,9 +90,11 @@ public struct CropViewConfig {

public var rotateCropBoxFor90DegreeRotation = true

var minimumCropBoxSize: CGFloat = 42

public var disableCropBoxDeformation = false
public var minimumCropBoxSize: CGFloat = 42 {
didSet {
assert(minimumCropBoxSize >= 4)
}
}

public enum BuiltInRotationControlViewType {
case rotationDial(config: RotationDialConfig = .init())
Expand Down
6 changes: 2 additions & 4 deletions Sources/Mantis/Mantis.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,7 @@ private func buildCropView(withImage image: UIImage,
config cropViewConfig: CropViewConfig,
rotationControlView: RotationControlViewProtocol?) -> CropViewProtocol {
let cropAuxiliaryIndicatorView = CropAuxiliaryIndicatorView(frame: .zero,
cropBoxHotAreaUnit: cropViewConfig.cropBoxHotAreaUnit,
disableCropBoxDeformation: cropViewConfig.disableCropBoxDeformation,
style: cropViewConfig.cropAuxiliaryIndicatorStyle)
config: cropViewConfig.cropAuxiliaryIndicatorConfig)
let imageContainer = ImageContainer(image: image)
let cropView = CropView(image: image,
cropViewConfig: cropViewConfig,
Expand All @@ -116,7 +114,7 @@ private func buildCropView(withImage image: UIImage,
private func buildCropViewModel(with cropViewConfig: CropViewConfig) -> CropViewModelProtocol {
CropViewModel(
cropViewPadding: cropViewConfig.padding,
hotAreaUnit: cropViewConfig.cropBoxHotAreaUnit
hotAreaUnit: cropViewConfig.cropAuxiliaryIndicatorConfig.cropBoxHotAreaUnit
)
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/Mantis/MaskBackground/CropMaskProtocal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import UIKit

private let minOverLayerUnit: CGFloat = 30
private let minOverLayerUnit: CGFloat = 4
private let initialFrameLength: CGFloat = 1000

protocol CropMaskProtocol: UIView {
Expand Down

0 comments on commit bc50ce7

Please sign in to comment.