Showing preview only (213K chars total). Download the full file or copy to clipboard to get everything.
Repository: artemkrachulov/AKImageCropperView
Branch: master
Commit: 3436f26c9da5
Files: 47
Total size: 197.2 KB
Directory structure:
gitextract_x4b46xu8/
├── .gitignore
├── AKImageCropperView/
│ ├── AKImageCropperOverlayView.swift
│ ├── AKImageCropperOverlayViewConfiguration.swift
│ ├── AKImageCropperOverlayViewConfigurationCorner.swift
│ ├── AKImageCropperOverlayViewConfigurationEdge.swift
│ ├── AKImageCropperOverlayViewConfigurationGrid.swift
│ ├── AKImageCropperOverlayViewConfigurationOverlay.swift
│ ├── AKImageCropperOverlayViewTouchState.swift
│ ├── AKImageCropperScrollView.swift
│ ├── AKImageCropperView.h
│ ├── AKImageCropperView.swift
│ ├── IC_CGFloatExtension.swift
│ ├── IC_CGPointExtension.swift
│ ├── IC_CGSizeExtensions.swift
│ ├── IC_UIImageExtensions.swift
│ ├── Info.plist
│ └── PrimaryFilledButton.swift
├── AKImageCropperView.podspec
├── AKImageCropperView.xcodeproj/
│ ├── project.pbxproj
│ ├── project.xcworkspace/
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata/
│ │ ├── AKImageCropperDemo.xcscmblueprint
│ │ └── Demo.xcscmblueprint
│ └── xcshareddata/
│ └── xcschemes/
│ └── AKImageCropperView.xcscheme
├── AKImageCropperViewExample/
│ ├── AppDelegate.swift
│ ├── Base.lproj/
│ │ ├── LaunchScreen.xib
│ │ └── Main.storyboard
│ ├── Constants.swift
│ ├── CropperViewController.swift
│ ├── CustomImageCropperOverlayView.swift
│ ├── HomeViewController.swift
│ ├── ImageViewController.swift
│ ├── Images.xcassets/
│ │ ├── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ ├── Attractive-girl.imageset/
│ │ │ └── Contents.json
│ │ ├── Autumn-background.imageset/
│ │ │ └── Contents.json
│ │ ├── Colorful-pillows.imageset/
│ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ ├── Cupcakes.imageset/
│ │ │ └── Contents.json
│ │ ├── Funnel-cake-stand.imageset/
│ │ │ └── Contents.json
│ │ ├── Icons/
│ │ │ ├── Contents.json
│ │ │ ├── overlay.imageset/
│ │ │ │ └── Contents.json
│ │ │ ├── random.imageset/
│ │ │ │ └── Contents.json
│ │ │ └── rotate.imageset/
│ │ │ └── Contents.json
│ │ └── Image-of-earth.imageset/
│ │ └── Contents.json
│ ├── ImagesTableViewController.swift
│ └── Info.plist
├── LICENSE
└── README.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Xcode
.DS_Store
*/build/*
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
profile
*.moved-aside
DerivedData
.idea/
*.hmap
#CocoaPods
Pods
Podfile.lock
================================================
FILE: AKImageCropperView/AKImageCropperOverlayView.swift
================================================
//
// AKImageCropperOverlayView.swift
//
// Created by Artem Krachulov.
// Copyright (c) 2016 Artem Krachulov. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
import UIKit
protocol AKImageCropperOverlayViewDelegate : class {
func cropperOverlayViewDidChangeCropRect(_ view: AKImageCropperOverlayView, _ cropRect: CGRect)
}
open class AKImageCropperOverlayView: UIView {
// MARK: -
// MARK: ** Properties **
/** Configuration structure for the Overlay View appearance and behavior. */
open var configuraiton = AKImageCropperCropViewConfiguration()
/** Crop rectangle */
internal var cropRect: CGRect = .zero
/** Saved crop rectangle state */
fileprivate var touchesBegan: (touch: CGPoint, cropRect: CGRect)!
/** Current active crop area part */
fileprivate var activeCropAreaPart: AKCropAreaPart = .none {
didSet { layoutSubviews() }
}
fileprivate struct AKCropAreaPart: OptionSet {
public let rawValue: Int
public init(rawValue: Int) {
self.rawValue = rawValue
}
static let none = AKCropAreaPart(rawValue: 1 << 0)
static let topEdge = AKCropAreaPart(rawValue: 1 << 1)
static let leftEdge = AKCropAreaPart(rawValue: 1 << 2)
static let bottomEdge = AKCropAreaPart(rawValue: 1 << 3)
static let rightEdge = AKCropAreaPart(rawValue: 1 << 4)
static let all: AKCropAreaPart = [.topEdge, .rightEdge, .bottomEdge, .leftEdge]
static let topLeftCorner: AKCropAreaPart = [.topEdge, .leftEdge]
static let topRightCorner: AKCropAreaPart = [.topEdge, .rightEdge]
static let bottomRightCorner: AKCropAreaPart = [.bottomEdge, .rightEdge]
static let bottomLeftCorner: AKCropAreaPart = [.bottomEdge, .leftEdge]
}
// MARK: Managing the Delegate
weak var delegate: AKImageCropperOverlayViewDelegate?
// MARK: Touch & Parts views
fileprivate var topcropView: UIView!
fileprivate var rightcropView: UIView!
fileprivate var bottomcropView: UIView!
fileprivate var leftcropView: UIView!
fileprivate var topEdgeTouchView: UIView!
fileprivate var topEdgeView: UIView!
fileprivate var rightEdgeTouchView: UIView!
fileprivate var rightEdgeView: UIView!
fileprivate var bottomEdgeTouchView: UIView!
fileprivate var bottomEdgeView: UIView!
fileprivate var leftEdgeTouchView: UIView!
fileprivate var leftEdgeView: UIView!
fileprivate var topLeftCornerTouchView: UIView!
fileprivate var topLeftCornerView: UIView!
fileprivate var topRightCornerTouchView: UIView!
fileprivate var topRightCornerView: UIView!
fileprivate var bottomRightCornerTouchView: UIView!
fileprivate var bottomRightCornerView: UIView!
fileprivate var bottomLeftCornerTouchView: UIView!
fileprivate var bottomLeftCornerView: UIView!
fileprivate var gridView: UIView!
fileprivate var gridViewVerticalLines: [UIView]!
fileprivate var gridViewHorizontalLines: [UIView]!
// MARK: -
// MARK: ** Initialization OBJECTS(VIEWS) & theirs parameters **
/** Parent (main) class to translate some properties and objects. */
weak var cropperView: AKImageCropperView!
fileprivate (set) lazy var overlayView: UIView! = {
let view = UIView()
view.backgroundColor = UIColor.black.withAlphaComponent(0.5)
view.clipsToBounds = true
return view
}()
fileprivate lazy var containerImageView: UIView! = {
let view = UIView()
view.backgroundColor = UIColor.clear
view.clipsToBounds = true
view.isUserInteractionEnabled = false
return view
}()
fileprivate lazy var imageView: UIImageView! = {
let view = UIImageView()
view.backgroundColor = UIColor.clear
return view
}()
open var image: UIImage! {
didSet {
imageView.image = image
}
}
// MARK: - Initialization
/**
Returns an overlay view initialized with the specified configuraiton.
- Parameter configuraiton: Configuration structure for the Overlay View appearance and behavior.
*/
init() {
super.init(frame: .zero)
backgroundColor = .clear
alpha = 0
initialize()
}
public init(configuraiton: AKImageCropperCropViewConfiguration? = nil) {
super.init(frame: CGRect.zero)
if configuraiton != nil {
self.configuraiton = configuraiton!
}
backgroundColor = UIColor.clear
alpha = 0
initialize()
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Draving Crop rect frame
fileprivate func initialize() {
/*
Create views layout.
Step by step
1. OverlayView
*/
addSubview(overlayView)
let blurEffect = UIBlurEffect(style: configuraiton.overlay.blurStyle)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.alpha = self.configuraiton.overlay.blurAlpha
blurEffectView.frame = overlayView.bounds
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
overlayView.addSubview(blurEffectView)
/* 2. Container view ‹‹ Image view */
containerImageView.addSubview(imageView)
addSubview(containerImageView)
/* 3. Crop rectangle */
// Edges
topEdgeTouchView = UIView()
addSubview(topEdgeTouchView)
topEdgeView = UIView()
topEdgeTouchView.addSubview(topEdgeView)
rightEdgeTouchView = UIView()
addSubview(rightEdgeTouchView)
rightEdgeView = UIView()
rightEdgeTouchView.addSubview(rightEdgeView)
bottomEdgeTouchView = UIView()
addSubview(bottomEdgeTouchView)
bottomEdgeView = UIView()
bottomEdgeTouchView.addSubview(bottomEdgeView)
leftEdgeTouchView = UIView()
addSubview(leftEdgeTouchView)
leftEdgeView = UIView()
leftEdgeTouchView.addSubview(leftEdgeView)
if configuraiton.edge.isHidden {
topEdgeView.isHidden = true
rightEdgeView.isHidden = true
bottomEdgeView.isHidden = true
leftEdgeView.isHidden = true
}
// Corners
topLeftCornerTouchView = UIView()
addSubview(topLeftCornerTouchView)
topLeftCornerView = UIView()
topLeftCornerView.layer.addSublayer(CAShapeLayer())
topLeftCornerTouchView.addSubview(topLeftCornerView)
topRightCornerTouchView = UIView()
addSubview(topRightCornerTouchView)
topRightCornerView = UIView()
topRightCornerView.layer.addSublayer(CAShapeLayer())
topRightCornerTouchView.addSubview(topRightCornerView)
bottomRightCornerTouchView = UIView()
addSubview(bottomRightCornerTouchView)
bottomRightCornerView = UIView()
bottomRightCornerView.layer.addSublayer(CAShapeLayer())
bottomRightCornerTouchView.addSubview(bottomRightCornerView)
bottomLeftCornerTouchView = UIView()
addSubview(bottomLeftCornerTouchView)
bottomLeftCornerView = UIView()
bottomLeftCornerView.layer.addSublayer(CAShapeLayer())
bottomLeftCornerTouchView.addSubview(bottomLeftCornerView)
if configuraiton.corner.isHidden {
topLeftCornerView.isHidden = true
topRightCornerView.isHidden = true
bottomRightCornerView.isHidden = true
bottomLeftCornerView.isHidden = true
}
// Grid
gridView = UIView()
gridViewVerticalLines = []
gridViewHorizontalLines = []
for _ in 0..<configuraiton.grid.linesCount.vertical {
let view = UIView()
view.frame.size.width = configuraiton.grid.linesWidth
view.backgroundColor = configuraiton.grid.linesColor
gridViewVerticalLines.append(view)
gridView.addSubview(view)
}
for _ in 0..<configuraiton.grid.linesCount.horizontal {
let view = UIView()
view.frame.size.height = configuraiton.grid.linesWidth
view.backgroundColor = configuraiton.grid.linesColor
gridViewHorizontalLines.append(view)
gridView.addSubview(view)
}
addSubview(gridView)
gridView.isHidden = configuraiton.grid.isHidden
if configuraiton.grid.alwaysShowGrid {
gridView.alpha = 1
} else {
gridView.alpha = 0
}
}
// MARK: - Life cycle
override open func layoutSubviews() {
super.layoutSubviews()
overlayView.frame = frame
containerImageView.frame = cropRect
matchForegroundToBackgroundScrollViewOffset()
matchForegroundToBackgroundScrollViewSize()
topEdgeTouchView.frame = cropAreaTopEdgeFrame
layoutTopEdgeView(topEdgeView,
inTouchView: topEdgeTouchView,
forState: activeCropAreaPart == .topEdge
? .highlighted
: .normal)
rightEdgeTouchView.frame = cropAreaRightEdgeFrame
layoutRightEdgeView(rightEdgeView,
inTouchView: rightEdgeTouchView,
forState: activeCropAreaPart == .rightEdge
? .highlighted
: .normal)
bottomEdgeTouchView.frame = cropAreaBottomEdgeFrame
layoutBottomEdgeView(bottomEdgeView,
inTouchView: bottomEdgeTouchView,
forState: activeCropAreaPart == .bottomEdge
? .highlighted
: .normal)
leftEdgeTouchView.frame = cropAreaLeftEdgeFrame
layoutLeftEdgeView(leftEdgeView,
inTouchView: leftEdgeTouchView,
forState: activeCropAreaPart == .leftEdge
? .highlighted
: .normal)
topLeftCornerTouchView.frame = cropAreaTopLeftCornerFrame
layoutTopLeftCornerView(topLeftCornerView,
inTouchView: topLeftCornerTouchView,
forState: activeCropAreaPart == .topLeftCorner
? .highlighted
: .normal)
topRightCornerTouchView.frame = cropAreaTopRightCornerFrame
layoutTopRightCornerView(topRightCornerView,
inTouchView: topRightCornerTouchView,
forState: activeCropAreaPart == .topRightCorner
? .highlighted
: .normal)
bottomRightCornerTouchView.frame = cropAreaBottomRightCornerFrame
layoutBottomRightCornerView(bottomRightCornerView,
inTouchView: bottomRightCornerTouchView,
forState: activeCropAreaPart == .bottomRightCorner
? .highlighted
: .normal)
bottomLeftCornerTouchView.frame = cropAreaBottomLeftCornerFrame
layoutBottomLeftCornerView(bottomLeftCornerView,
inTouchView: bottomLeftCornerTouchView,
forState: activeCropAreaPart == .bottomLeftCorner
? .highlighted
: .normal)
gridView.frame = cropRect
layoutGridView(gridView, gridViewHorizontalLines: gridViewHorizontalLines, gridViewVerticalLines: gridViewVerticalLines)
}
// MARK: Crop rectangle parts rects
fileprivate var cropAreaTopLeftCornerFrame: CGRect {
return CGRect(
origin: CGPoint(
x: cropRect.origin.x - configuraiton.cornerTouchSize.width / 2,
y: cropRect.origin.y - configuraiton.cornerTouchSize.height / 2),
size: configuraiton.cornerTouchSize)
}
fileprivate var cropAreaTopRightCornerFrame: CGRect {
return CGRect(
origin: CGPoint(
x: cropRect.maxX - configuraiton.cornerTouchSize.width / 2,
y: cropRect.minY - configuraiton.cornerTouchSize.height / 2),
size: configuraiton.cornerTouchSize)
}
fileprivate var cropAreaBottomLeftCornerFrame: CGRect {
return CGRect(
origin: CGPoint(
x: cropRect.origin.x - configuraiton.cornerTouchSize.width / 2,
y: cropRect.maxY - configuraiton.cornerTouchSize.height / 2),
size: configuraiton.cornerTouchSize)
}
fileprivate var cropAreaBottomRightCornerFrame: CGRect {
return CGRect(
origin: CGPoint(
x: cropRect.maxX - configuraiton.cornerTouchSize.width / 2,
y: cropRect.maxY - configuraiton.cornerTouchSize.height / 2),
size: configuraiton.cornerTouchSize)
}
fileprivate var cropAreaTopEdgeFrame: CGRect{
return CGRect(
x : cropAreaTopLeftCornerFrame.maxX,
y : cropRect.origin.y - configuraiton.edgeTouchThickness.horizontal / 2,
width : cropRect.size.width - (cropAreaTopLeftCornerFrame.size.width / 2 + cropAreaTopRightCornerFrame.size.width / 2),
height : configuraiton.edgeTouchThickness.horizontal)
}
fileprivate var cropAreaBottomEdgeFrame: CGRect {
return CGRect(
x : cropAreaBottomLeftCornerFrame.maxX,
y : cropRect.maxY - configuraiton.edgeTouchThickness.horizontal / 2,
width : cropRect.size.width - (cropAreaBottomLeftCornerFrame.size.width / 2 + cropAreaBottomRightCornerFrame.size.width / 2),
height : configuraiton.edgeTouchThickness.horizontal)
}
fileprivate var cropAreaRightEdgeFrame: CGRect {
return CGRect(
x : cropRect.maxX - configuraiton.edgeTouchThickness.vertical / 2,
y : cropAreaTopLeftCornerFrame.maxY,
width : configuraiton.edgeTouchThickness.vertical,
height : cropRect.size.height - (cropAreaTopRightCornerFrame.size.height / 2 + cropAreaBottomRightCornerFrame.size.height / 2))
}
fileprivate var cropAreaLeftEdgeFrame: CGRect {
return CGRect(
x : cropRect.origin.x - configuraiton.edgeTouchThickness.vertical / 2,
y : cropAreaTopLeftCornerFrame.maxY,
width : configuraiton.edgeTouchThickness.vertical,
height : cropRect.size.height - (cropAreaTopLeftCornerFrame.size.height / 2 + cropAreaBottomLeftCornerFrame.size.height / 2))
}
fileprivate func getCropAreaPartContainsPoint(_ point: CGPoint) -> AKCropAreaPart {
if cropAreaTopEdgeFrame.contains(point) {
return .topEdge
} else if cropAreaBottomEdgeFrame.contains(point) {
return .bottomEdge
} else if cropAreaRightEdgeFrame.contains(point) {
return .rightEdge
} else if cropAreaLeftEdgeFrame.contains(point) {
return .leftEdge
} else if cropAreaTopLeftCornerFrame.contains(point) {
return .topLeftCorner
} else if cropAreaTopRightCornerFrame.contains(point) {
return .topRightCorner
} else if cropAreaBottomLeftCornerFrame.contains(point) {
return .bottomLeftCorner
} else if cropAreaBottomRightCornerFrame.contains(point) {
return .bottomRightCorner
} else {
return .none
}
}
// MARK: Other methods
final func showOverlayBlur(_ show: Bool, completion: ((Bool) -> Void)? = nil) {
UIView.animate(withDuration: configuraiton.animation.duration, delay: 0, options: [], animations: {
self.overlayView.subviews.first?.alpha = show ? self.configuraiton.overlay.blurAlpha : 0.0
}, completion: { isComplete in
completion?(isComplete)
})
}
final func showGrid(_ show: Bool, completion: ((Bool) -> Void)? = nil) {
if configuraiton.grid.alwaysShowGrid {
completion?(true)
return
}
let animations: () -> Void = { _ in
self.gridView.alpha = show ? 1 : 0
}
if configuraiton.animation.duration == 0 {
animations()
} else {
UIView.animate(withDuration: configuraiton.animation.duration, delay: 0, options: [], animations: animations, completion: { isComplete in
completion?(isComplete)
})
}
}
/**
Visual representation for top edge view in current user interaction state.
- Parameter view: Top edge view.
- Parameter touchView: Touch area view where added top edge view.
- Parameter state: User interaction state.
*/
open func layoutTopEdgeView(_ view: UIView, inTouchView touchView: UIView, forState state: AKImageCropperCropViewTouchState) {
var color: UIColor
var width: CGFloat
if state == .normal {
color = configuraiton.edge.normalLineColor
width = configuraiton.edge.normalLineWidth
} else {
color = configuraiton.edge.highlightedLineColor
width = configuraiton.edge.highlightedLineWidth
}
view.backgroundColor = color
view.frame = CGRect(
x : touchView.bounds.origin.x - configuraiton.cornerTouchSize.width / 2 - configuraiton.edge.normalLineWidth,
y : touchView.bounds.midY - width,
width : touchView.bounds.size.width + configuraiton.cornerTouchSize.width + configuraiton.edge.normalLineWidth * 2,
height : width)
}
/**
Visual representation for right edge view in current user interaction state.
- Parameter view: Right edge view.
- Parameter touchView: Touch area view where added right edge view.
- Parameter state: User interaction state.
*/
open func layoutRightEdgeView(_ view: UIView, inTouchView touchView: UIView, forState state: AKImageCropperCropViewTouchState) {
var color: UIColor
var width: CGFloat
if state == .normal {
color = configuraiton.edge.normalLineColor
width = configuraiton.edge.normalLineWidth
} else {
color = configuraiton.edge.highlightedLineColor
width = configuraiton.edge.highlightedLineWidth
}
view.backgroundColor = color
view.frame = CGRect(
x : touchView.bounds.midX,
y : touchView.bounds.origin.y - configuraiton.cornerTouchSize.height / 2 - configuraiton.edge.normalLineWidth,
width : width,
height : touchView.bounds.size.height + configuraiton.cornerTouchSize.height + configuraiton.edge.normalLineWidth * 2)
}
/**
Visual representation for bottom edge view in current user interaction state.
- Parameter view: Bottom edge view.
- Parameter touchView: Touch area view where added bottom edge view.
- Parameter state: User interaction state.
*/
open func layoutBottomEdgeView(_ view: UIView, inTouchView touchView: UIView, forState state: AKImageCropperCropViewTouchState) {
var color: UIColor
var width: CGFloat
if state == .normal {
color = configuraiton.edge.normalLineColor
width = configuraiton.edge.normalLineWidth
} else {
color = configuraiton.edge.highlightedLineColor
width = configuraiton.edge.highlightedLineWidth
}
view.backgroundColor = color
view.frame = CGRect(
x : touchView.bounds.origin.x - configuraiton.cornerTouchSize.width / 2 - configuraiton.edge.normalLineWidth,
y : touchView.bounds.midY,
width : touchView.bounds.size.width + configuraiton.cornerTouchSize.width + configuraiton.edge.normalLineWidth * 2,
height : width)
}
/**
Visual representation for left edge view in current user interaction state.
- Parameter view: Left edge view.
- Parameter touchView: Touch area view where added left edge view.
- Parameter state: User interaction state.
*/
open func layoutLeftEdgeView(_ view: UIView, inTouchView touchView: UIView, forState state: AKImageCropperCropViewTouchState) {
var color: UIColor
var width: CGFloat
if state == .normal {
color = configuraiton.edge.normalLineColor
width = configuraiton.edge.normalLineWidth
} else {
color = configuraiton.edge.highlightedLineColor
width = configuraiton.edge.highlightedLineWidth
}
view.backgroundColor = color
view.frame = CGRect(
x : touchView.bounds.midX - width,
y : touchView.bounds.origin.y - configuraiton.cornerTouchSize.height / 2 - configuraiton.edge.normalLineWidth,
width : width,
height : touchView.bounds.size.height + configuraiton.cornerTouchSize.height + configuraiton.edge.normalLineWidth * 2)
}
/**
Visual representation for top left corner view in current user interaction state. Drawing going with added shape layer.
- Parameter view: Top left corner view.
- Parameter touchView: Touch area view where added top left edge view.
- Parameter state: User interaction state.
*/
open func layoutTopLeftCornerView(_ view: UIView, inTouchView touchView: UIView, forState state: AKImageCropperCropViewTouchState) {
var lineWidth: CGFloat
let layer: CAShapeLayer = view.layer.sublayers!.first as! CAShapeLayer
if state == .normal {
layer.fillColor = configuraiton.corner.normalLineColor.cgColor
view.frame.size = configuraiton.corner.normaSize
lineWidth = configuraiton.corner.normalLineWidth
} else {
layer.fillColor = configuraiton.edge.highlightedLineColor.cgColor
view.frame.size = configuraiton.corner.highlightedSize
lineWidth = configuraiton.corner.highlightedLineWidth
}
view.center = CGPoint(x: touchView.bounds.midX, y: touchView.bounds.midY)
let rect = CGRect(origin: CGPoint(x: view.bounds.midX - lineWidth, y: view.bounds.midY - lineWidth), size: view.frame.size)
let substractRect = CGRect(
x : rect.origin.x + lineWidth,
y : rect.origin.y + lineWidth,
width : rect.size.width - lineWidth,
height : rect.size.height - lineWidth)
let path = UIBezierPath(rect: rect)
path.append(UIBezierPath(rect: substractRect).reversing())
layer.path = path.cgPath
}
/**
Visual representation for top right corner view in current user interaction state. Drawing going with added shape layer.
- Parameter view: Top right corner view.
- Parameter touchView: Touch area view where added top right edge view.
- Parameter state: User interaction state.
*/
open func layoutTopRightCornerView(_ view: UIView, inTouchView touchView: UIView, forState state: AKImageCropperCropViewTouchState) {
var lineWidth: CGFloat
let layer: CAShapeLayer = view.layer.sublayers!.first as! CAShapeLayer
if state == .normal {
layer.fillColor = configuraiton.corner.normalLineColor.cgColor
view.frame.size = configuraiton.corner.normaSize
lineWidth = configuraiton.corner.normalLineWidth
} else {
layer.fillColor = configuraiton.edge.highlightedLineColor.cgColor
view.frame.size = configuraiton.corner.highlightedSize
lineWidth = configuraiton.corner.highlightedLineWidth
}
view.center = CGPoint(x: touchView.bounds.midX, y: touchView.bounds.midY)
let rect = CGRect(origin: CGPoint(x: -view.bounds.midX + lineWidth, y: view.bounds.midY - lineWidth), size: view.frame.size)
let substractRect = CGRect(
x : rect.origin.x,
y : rect.origin.y + lineWidth,
width : rect.size.width - lineWidth,
height : rect.size.height - lineWidth)
let path = UIBezierPath(rect: rect)
path.append(UIBezierPath(rect: substractRect).reversing())
layer.path = path.cgPath
}
/**
Visual representation for bottom right corner view in current user interaction state. Drawing going with added shape layer.
- Parameter view: Bottom right corner view.
- Parameter touchView: Touch area view where added bottom right edge view.
- Parameter state: User interaction state.
*/
open func layoutBottomRightCornerView(_ view: UIView, inTouchView touchView: UIView, forState state: AKImageCropperCropViewTouchState) {
var lineWidth: CGFloat
let layer: CAShapeLayer = view.layer.sublayers!.first as! CAShapeLayer
if state == .normal {
layer.fillColor = configuraiton.corner.normalLineColor.cgColor
view.frame.size = configuraiton.corner.normaSize
lineWidth = configuraiton.corner.normalLineWidth
} else {
layer.fillColor = configuraiton.edge.highlightedLineColor.cgColor
view.frame.size = configuraiton.corner.highlightedSize
lineWidth = configuraiton.corner.highlightedLineWidth
}
view.center = CGPoint(x: touchView.bounds.midX, y: touchView.bounds.midY)
let rect = CGRect(origin: CGPoint(x: -view.bounds.midX + lineWidth, y: -view.bounds.midY + lineWidth), size: view.frame.size)
let substractRect = CGRect(
x : rect.origin.x,
y : rect.origin.y,
width : rect.size.width - lineWidth,
height : rect.size.height - lineWidth)
let path = UIBezierPath(rect: rect)
path.append(UIBezierPath(rect: substractRect).reversing())
layer.path = path.cgPath
}
/**
Visual representation for bottom left corner view in current user interaction state. Drawing going with added shape layer.
- Parameter view: Bottom left corner view.
- Parameter touchView: Touch area view where added bottom left edge view.
- Parameter state: User interaction state.
*/
open func layoutBottomLeftCornerView(_ view: UIView, inTouchView touchView: UIView, forState state: AKImageCropperCropViewTouchState) {
var lineWidth: CGFloat
let layer: CAShapeLayer = view.layer.sublayers!.first as! CAShapeLayer
if state == .normal {
layer.fillColor = configuraiton.corner.normalLineColor.cgColor
view.frame.size = configuraiton.corner.normaSize
lineWidth = configuraiton.corner.normalLineWidth
} else {
layer.fillColor = configuraiton.edge.highlightedLineColor.cgColor
view.frame.size = configuraiton.corner.highlightedSize
lineWidth = configuraiton.corner.highlightedLineWidth
}
view.center = CGPoint(x: touchView.bounds.midX, y: touchView.bounds.midY)
let rect = CGRect(origin: CGPoint(x: view.bounds.midX - lineWidth, y: -view.bounds.midY + lineWidth), size: view.frame.size)
let substractRect = CGRect(
x : rect.origin.x + lineWidth,
y : rect.origin.y,
width : rect.size.width - lineWidth,
height : rect.size.height - lineWidth)
let path = UIBezierPath(rect: rect)
path.append(UIBezierPath(rect: substractRect).reversing())
layer.path = path.cgPath
}
/**
Visual representation for grid view.
- Parameter view: Grid view.
- Parameter gridViewHorizontalLines: Horizontal line view`s array.
- Parameter gridViewVerticalLines: Vertical line view`s array.
*/
open func layoutGridView(_ view: UIView, gridViewHorizontalLines: [UIView], gridViewVerticalLines: [UIView]) {
for (i, line) in gridViewHorizontalLines.enumerated() {
line.frame.origin = CGPoint(x: 0, y: view.frame.height * CGFloat(i + 1) / CGFloat(gridViewHorizontalLines.count + 1))
line.frame.size.width = view.frame.width
}
for (i, line) in gridViewVerticalLines.enumerated() {
line.frame.origin = CGPoint(x: view.frame.width * CGFloat(i + 1) / CGFloat(gridViewVerticalLines.count + 1), y: 0)
line.frame.size.height = view.frame.height
}
}
// MARK: - Touches
override open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
/* Save */
touchesBegan = (touch.location(in: self), cropRect)
/* Active part */
activeCropAreaPart = getCropAreaPartContainsPoint(touchesBegan.touch)
}
override open func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
/* GET TRANSLATION POINT */
let point = touch.location(in: self)
let previousPoint = touch.previousLocation(in: self)
let translationPoint = CGPoint(x: point.x - previousPoint.x, y: point.y - previousPoint.y)
/* MOVE FRAME */
let cropRectMaxFrame = cropperView.reversedFrameWithInsets
if activeCropAreaPart.contains(.topEdge) {
cropRect.origin.y += translationPoint.y
cropRect.size.height -= translationPoint.y
let pointInEdge = touchesBegan.touch.y - touchesBegan.cropRect.minY
let minStickPoint = pointInEdge + cropRectMaxFrame.minY
let maxStickPoint = pointInEdge + touchesBegan.cropRect.maxY - configuraiton.minCropRectSize.height
if point.y > maxStickPoint || cropRect.height < configuraiton.minCropRectSize.height {
cropRect.origin.y = touchesBegan.cropRect.maxY - configuraiton.minCropRectSize.height
cropRect.size.height = configuraiton.minCropRectSize.height
}
if point.y < minStickPoint {
cropRect.origin.y = cropRectMaxFrame.minY
cropRect.size.height = touchesBegan.cropRect.maxY - cropRectMaxFrame.minY
}
}
if activeCropAreaPart.contains(.rightEdge) {
cropRect.size.width += translationPoint.x
let pointInEdge = touchesBegan.touch.x - touchesBegan.cropRect.maxX
let minStickPoint = pointInEdge + touchesBegan.cropRect.minX + configuraiton.minCropRectSize.width
let maxStickPoint = pointInEdge + cropRectMaxFrame.maxX
if point.x > maxStickPoint {
cropRect.size.width = cropRectMaxFrame.maxX - cropRect.origin.x
}
if point.x < minStickPoint || cropRect.width < configuraiton.minCropRectSize.width {
cropRect.size.width = configuraiton.minCropRectSize.width
}
}
if activeCropAreaPart.contains(.bottomEdge) {
cropRect.size.height += translationPoint.y
let pointInEdge = touchesBegan.touch.y - touchesBegan.cropRect.maxY
let minStickPoint = pointInEdge + touchesBegan.cropRect.minY + configuraiton.minCropRectSize.height
let maxStickPoint = pointInEdge + cropRectMaxFrame.maxY
if point.y > maxStickPoint {
cropRect.size.height = cropRectMaxFrame.maxY - cropRect.origin.y
}
if point.y < minStickPoint || cropRect.height < configuraiton.minCropRectSize.height {
cropRect.size.height = configuraiton.minCropRectSize.height
}
}
if activeCropAreaPart.contains(.leftEdge) {
cropRect.origin.x += translationPoint.x
cropRect.size.width -= translationPoint.x
let pointInEdge = touchesBegan.touch.x - touchesBegan.cropRect.minX
let minStickPoint = pointInEdge + cropRectMaxFrame.minX
let maxStickPoint = pointInEdge + touchesBegan.cropRect.maxX - configuraiton.minCropRectSize.width
if point.x > maxStickPoint || cropRect.width < configuraiton.minCropRectSize.width {
cropRect.origin.x = touchesBegan.cropRect.maxX - configuraiton.minCropRectSize.width
cropRect.size.width = configuraiton.minCropRectSize.width
}
if point.x < minStickPoint {
cropRect.origin.x = cropRectMaxFrame.minX
cropRect.size.width = touchesBegan.cropRect.maxX - cropRectMaxFrame.minX
}
}
/* Update UI for the crop rectange */
layoutSubviews()
/* Delegates */
delegate?.cropperOverlayViewDidChangeCropRect(self, cropRect)
}
override open func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
/* Active part */
activeCropAreaPart = .none
}
// MARK: - Instance Method
override open func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
guard alpha == 1 else { return cropperView.scrollView }
return self.point(inside: point, with: event) && getCropAreaPartContainsPoint(point) != .none
? self
: cropperView.scrollView
}
// MARK: - Match Foreground To Background
func matchForegroundToBackgroundScrollViewOffset() {
imageView.frame.origin = CGPoint(
x: -(cropperView.scrollView.contentOffset.x + containerImageView.frame.origin.x),
y: -(cropperView.scrollView.contentOffset.y + containerImageView.frame.origin.y))
}
func matchForegroundToBackgroundScrollViewSize() {
imageView.frame.size = cropperView.scrollView.contentSize
}
}
================================================
FILE: AKImageCropperView/AKImageCropperOverlayViewConfiguration.swift
================================================
//
// AKImageCropperCropViewConfiguration.swift
//
// Created by Artem Krachulov.
// Copyright (c) 2016 Artem Krachulov. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
import UIKit
/// Overlay view configuration struct.
public struct AKImageCropperCropViewConfiguration {
public init() {}
// MARK: Crop rectangle
/// Delay before the crop rectangle will scale to fit cropper view frame edges.
public var zoomingToFitDelay: TimeInterval = 1.0
/**
Animation options for layout transitions.
- duration: The duration of the transition animation, measured in seconds.
- options: Specifies the supported animation curves.
*/
public var animation: (duration: TimeInterval, options: UIViewAnimationOptions) = (duration: 0.3, options: .curveEaseInOut)
/// Edges insets for crop rectangle. Static values for programmatically rotation.
public var cropRectInsets = UIEdgeInsetsMake(20, 20, 20, 20)
/// The smallest value for the crop rectangle sizes. Initial value of this property is 60 pixels width and 60 pixels height.
public var minCropRectSize: CGSize = CGSize(width: 60, height: 60)
/// Touch view where will be drawn the corner.
public var cornerTouchSize: CGSize = CGSize(width: 30.0, height: 30.0)
/**
Thickness for edges touch area. This touch view is centered on the edge line.
- vertical: Thickness for vertical edges: Left, Right.
- horizontal: Thickness for horizontal edges: Top, Bottom.
*/
public var edgeTouchThickness: (vertical: CGFloat, horizontal: CGFloat) = (vertical: 20.0, horizontal: 20.0)
// MARK: Visual Appearance
/// Overlay visual configuration.
public var overlay = AKImageCropperCropViewConfigurationOverlay()
/// Edges visual configuration.
public var edge = AKImageCropperCropViewConfigurationEdge()
/// Corners visual configuration.
public var corner = AKImageCropperCropViewConfigurationCorner()
/// Grid visual configuration.
public var grid = AKImageCropperCropViewConfigurationGrid()
}
================================================
FILE: AKImageCropperView/AKImageCropperOverlayViewConfigurationCorner.swift
================================================
//
// AKImageCropperCropViewConfigurationCorner.swift
//
// Created by Artem Krachulov.
// Copyright (c) 2016 Artem Krachulov. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
import UIKit
/// Corners configuration struct.
public struct AKImageCropperCropViewConfigurationCorner {
/// A Boolean value that determines whether the corner view is hidden.
public var isHidden: Bool = false
/// Line width for normal corner state.
public var normalLineWidth: CGFloat = 3.0
/// Line width for highlighted corner state.
public var highlightedLineWidth: CGFloat = 3.0
/// Size for normal corner state.
public var normaSize: CGSize = CGSize(width: 20, height: 20)
/// Size for highlighted corner state.
public var highlightedSize: CGSize = CGSize(width: 30, height: 30)
/// Line color for normal corner state.
public var normalLineColor: UIColor = .white
/// Line color for highlighted corner state.
public var highlightedLineColor: UIColor = .white
}
================================================
FILE: AKImageCropperView/AKImageCropperOverlayViewConfigurationEdge.swift
================================================
//
// AKImageCropperCropViewConfigurationEdge.swift
//
// Created by Artem Krachulov.
// Copyright (c) 2016 Artem Krachulov. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
import UIKit
/// Edges configuration struct.
public struct AKImageCropperCropViewConfigurationEdge {
/// A Boolean value that determines whether the edge view is hidden.
public var isHidden: Bool = false
/// Line width for normal edge state.
public var normalLineWidth: CGFloat = 1.0
/// Line width for highlighted edge state.
public var highlightedLineWidth: CGFloat = 3.0
/// Line color for normal edge state.
public var normalLineColor: UIColor = .white
/// Line color for highlighted edge state.
public var highlightedLineColor: UIColor = .white
}
================================================
FILE: AKImageCropperView/AKImageCropperOverlayViewConfigurationGrid.swift
================================================
//
// AKImageCropperCropViewConfigurationGrid.swift
//
// Created by Artem Krachulov.
// Copyright (c) 2016 Artem Krachulov. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
import UIKit
/// Grid visual configuration struct.
public struct AKImageCropperCropViewConfigurationGrid {
/// A Boolean value that determines whether the edge view is hidden.
public var isHidden: Bool = false
/// Hide grid after user interaction.
public var alwaysShowGrid: Bool = false
/**
The number of vertical and horizontal lines inside the crop rectangle.
- vertical: Vertical lines count.
- horizontal: Horizontal lines count.
*/
public var linesCount: (vertical: Int, horizontal: Int) = (vertical: 2, horizontal: 2)
/// Vertical and horizontal lines width.
public var linesWidth: CGFloat = 1.0
/// Vertical and horizontal lines color.
public var linesColor: UIColor = UIColor.white.withAlphaComponent(0.5)
}
================================================
FILE: AKImageCropperView/AKImageCropperOverlayViewConfigurationOverlay.swift
================================================
//
// AKImageCropperCropViewConfigurationOverlay.swift
//
// Created by Artem Krachulov.
// Copyright (c) 2016 Artem Krachulov. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
import UIKit
/// Overlay configuration struct.
public struct AKImageCropperCropViewConfigurationOverlay {
/// The view’s background color.
public var backgroundColor: UIColor = UIColor.black.withAlphaComponent(0.5)
/// A Boolean value that determines whether the blur effect is enable.
///
/// The blur effect added over overlay view. The effect will disappear before user interaction will start. After manipulations, the effect will revert to the initial state.
public var isBlurEnabled: Bool = true
/// The intensity of the blur effect.
public var blurStyle: UIBlurEffectStyle = .dark
/// The blur effect alpha value.
public var blurAlpha: CGFloat = 0.6
}
================================================
FILE: AKImageCropperView/AKImageCropperOverlayViewTouchState.swift
================================================
//
// AKImageCropperCropViewTouchState.swift
//
// Created by Artem Krachulov.
// Copyright (c) 2016 Artem Krachulov. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
/// User interaction state for edge or corner.
public enum AKImageCropperCropViewTouchState {
/// Default, relase
case normal
/// Press, touch, etc.
case highlighted
}
================================================
FILE: AKImageCropperView/AKImageCropperScrollView.swift
================================================
//
// AKImageCropperScrollView.swift
// AKImageCropperView
//
// Created by Artem Krachulov on 12/17/16.
// Copyright © 2016 Artem Krachulov. All rights reserved.
//
import UIKit
final class AKImageCropperScrollView: UIScrollView {
// MARK: -
// MARK: ** Properties **
/** Return visible rect of an UIScrollView's content */
open var visibleRect: CGRect {
return CGRect(
x : contentInset.left,
y : contentInset.top,
width : frame.size.width - contentInset.left - contentInset.right,
height : frame.size.height - contentInset.top - contentInset.bottom)
}
/** Returns scaled visible rect of an UIScrollView's content */
open var scaledVisibleRect: CGRect {
return CGRect(
x : (contentOffset.x + contentInset.left) / zoomScale,
y : (contentOffset.y + contentInset.top) / zoomScale,
width : visibleRect.size.width / zoomScale,
height : visibleRect.size.height / zoomScale)
}
// MARK: -
// MARK: ** Initialization OBJECTS(VIEWS) & theirs parameters **
fileprivate lazy var imageView: UIImageView! = {
let view = UIImageView()
return view
}()
open var image: UIImage! {
didSet {
/* Prepare scroll view to changing the image */
maximumZoomScale = 1
minimumZoomScale = 1
zoomScale = 1
/* Update an image view */
imageView.image = image
imageView.frame.size = image.size
contentSize = image.size
}
}
// MARK: - Initialization
/** Returns an scroll view initialized object. */
init() {
super.init(frame: .zero)
alwaysBounceVertical = true
alwaysBounceHorizontal = true
showsVerticalScrollIndicator = false
showsHorizontalScrollIndicator = false
addSubview(imageView)
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
================================================
FILE: AKImageCropperView/AKImageCropperView.h
================================================
//
// AKImageCropperView.h
// AKImageCropperView
//
// Created by Artem Krachulov on 12/13/16.
// Copyright © 2016 Artem Krachulov. All rights reserved.
//
#import <UIKit/UIKit.h>
//! Project version number for AKImageCropperView.
FOUNDATION_EXPORT double AKImageCropperViewVersionNumber;
//! Project version string for AKImageCropperView.
FOUNDATION_EXPORT const unsigned char AKImageCropperViewVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <AKImageCropperView/PublicHeader.h>
================================================
FILE: AKImageCropperView/AKImageCropperView.swift
================================================
//
// AKImageCropperView.swift
//
// Created by Artem Krachulov.
// Copyright (c) 2016 Artem Krachulov. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
import UIKit
typealias CGPointPercentage = CGPoint
open class AKImageCropperView: UIView, UIScrollViewDelegate, UIGestureRecognizerDelegate, AKImageCropperOverlayViewDelegate {
/** Current rotation angle */
fileprivate var angle: Double = 0.0
/** Scroll view minimum edge insets (current value) */
fileprivate var minEdgeInsets: UIEdgeInsets = .zero
/** Reversed frame direct to current rotation angle */
fileprivate var reversedRect: CGRect {
return CGRect(
origin : .zero,
size : ((angle / M_PI_2).truncatingRemainder(dividingBy: 2)) == 1
? CGSize(width: frame.size.height, height: frame.size.width)
: frame.size)
}
/** Reversed minimum edge insets direct to current rotation angle */
fileprivate var reversedEdgeInsets: UIEdgeInsets {
var newEdgeInsets: UIEdgeInsets
switch angle {
case M_PI_2:
newEdgeInsets = UIEdgeInsetsMake(
minEdgeInsets.right,
minEdgeInsets.top,
minEdgeInsets.left,
minEdgeInsets.bottom)
case M_PI:
newEdgeInsets = UIEdgeInsetsMake(
minEdgeInsets.bottom,
minEdgeInsets.right,
minEdgeInsets.top,
minEdgeInsets.left)
case M_PI_2 * 3:
newEdgeInsets = UIEdgeInsetsMake(
minEdgeInsets.left,
minEdgeInsets.bottom,
minEdgeInsets.right,
minEdgeInsets.top)
default:
newEdgeInsets = minEdgeInsets
}
return newEdgeInsets
}
/** Reversed frame + edgeInsets direct to current rotation angle */
var reversedFrameWithInsets: CGRect {
return UIEdgeInsetsInsetRect(reversedRect, reversedEdgeInsets)
}
// MARK: -
// MARK: ** Saved properties **
fileprivate struct SavedProperty {
var scaleAspectRatio: CGFloat!
var contentOffset: CGPoint!
var contentOffsetPercentage: CGPoint!
var cropRectSize: CGSize!
init() {
scaleAspectRatio = 1.0
contentOffset = .zero
contentOffsetPercentage = .zero
cropRectSize = .zero
}
mutating func save(scrollView: AKImageCropperScrollView) {
scaleAspectRatio = scrollView.zoomScale / scrollView.minimumZoomScale
contentOffset = CGPoint(
x: scrollView.contentOffset.x + scrollView.contentInset.left,
y: scrollView.contentOffset.y + scrollView.contentInset.top)
let contentSize = CGSize(
width : (scrollView.contentSize.width - scrollView.visibleRect.width).ic_roundTo(precision: 3),
height : (scrollView.contentSize.height - scrollView.visibleRect.height).ic_roundTo(precision: 3))
contentOffsetPercentage = CGPointPercentage(
x: (contentOffset.x > 0 && contentSize.width != 0)
? ic_round(x: contentOffset.x / contentSize.width, multiplier: 0.005)
: 0,
y: (contentOffset.y > 0 && contentSize.height != 0)
? ic_round(x: contentOffset.y / contentSize.height, multiplier: 0.005)
: 0)
cropRectSize = scrollView.visibleRect.size
}
}
/** Saved Scroll View parameters before complex layout animation */
fileprivate var savedProperty = SavedProperty()
// MARK: Accessing the Displayed Images
/** The image displayed in the image cropper view. Default is nil. */
open var image: UIImage? {
didSet {
guard let image = image else {
return
}
scrollView.image = image
overlayView?.image = image
reset()
}
}
/** Cropperd image in the specified crop rectangle */
open var croppedImage: UIImage? {
return image?.ic_imageInRect(scrollView.scaledVisibleRect)?.ic_rotateByAngle(angle)
}
// MARK: States
/** Returns the image edited state flag. */
open var isEdited: Bool {
guard let image = image else {
return false
}
let fitScaleMultiplier = ic_CGSizeFitScaleMultiplier(image.size, relativeToSize: reversedFrameWithInsets.size)
return angle != 0 || fitScaleMultiplier != scrollView.minimumZoomScale || fitScaleMultiplier != scrollView.zoomScale
}
/** Determines the overlay view current state. Default is false. */
open private(set) var isOverlayViewActive: Bool = false
fileprivate var layoutByImage: Bool = true
/** Сompletion blocker. */
fileprivate var isAnimation: Bool = false
// MARK: Managing the Delegate
/** The delegate of the cropper view object. */
weak open var delegate: AKImageCropperViewDelegate?
// MARK: -
// MARK: ** Initialization OBJECTS(VIEWS) & theirs parameters **
// MARK: Rotate view
fileprivate lazy var rotateView: UIView! = {
let view = UIView()
view.clipsToBounds = true
return view
}()
// MARK: Scroll view
var scrollView: AKImageCropperScrollView!
// MARK: Overlay Crop view
open var overlayView: AKImageCropperOverlayView? {
willSet {
if overlayView != nil {
overlayView?.removeFromSuperview()
}
if newValue != nil {
newValue?.delegate = self
newValue?.cropperView = self
rotateView.addSubview(newValue!)
}
layoutSubviews()
}
}
// MARK: - Initializing an Image Cropper View
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initialize()
}
override public init(frame: CGRect) {
super.init(frame: frame)
initialize()
}
/**
Returns an image cropper view initialized with the specified image.
- Parameter image: The initial image to display in the image cropper view.
*/
public init(image: UIImage?) {
super.init(frame:CGRect.zero)
initialize()
self.image = image
}
fileprivate func initialize() {
/*
Create views layout.
Step by step
1. Scroll view ‹‹ Image view
*/
scrollView = AKImageCropperScrollView()
scrollView.delegate = self
rotateView.addSubview(scrollView)
/* 2. Overlay view with crop rectangle */
overlayView = AKImageCropperOverlayView()
/* 3. Rotate view */
addSubview(rotateView)
/**
Add Observers
To controll all parameters changing and pass them to foreground image view
*/
initObservers()
let pressGesture = UILongPressGestureRecognizer(target: self, action: #selector(pressGestureAction(_ :)))
pressGesture.minimumPressDuration = 0
pressGesture.cancelsTouchesInView = false
pressGesture.delegate = self
addGestureRecognizer(pressGesture)
}
@objc fileprivate func pressGestureAction(_ gesture: UITapGestureRecognizer) {
if gesture.state == .began {
beforeInteraction()
} else if gesture.state == .cancelled || gesture.state == .ended {
afterInteraction()
}
}
// MARK: - UIGestureRecognizerDelegate
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
deinit {
removeObservers()
#if AKImageCropperViewDEBUG
print("deinit AKImageCropperView")
#endif
}
// MARK: - Observe Scroll view values
fileprivate func initObservers() {
for forKeyPath in ["contentOffset", "contentSize"] {
scrollView.addObserver(self, forKeyPath: forKeyPath, options: [.new, .old], context: nil)
}
}
open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard let keyPath = keyPath else {
return
}
switch keyPath {
case "contentOffset":
if let _ = change![NSKeyValueChangeKey.newKey] {
overlayView?.matchForegroundToBackgroundScrollViewOffset()
}
case "contentSize":
if let _ = change![NSKeyValueChangeKey.newKey] {
overlayView?.matchForegroundToBackgroundScrollViewSize()
}
default: ()
}
}
fileprivate func removeObservers() {
for forKeyPath in ["contentOffset", "contentSize"] {
scrollView.removeObserver(self, forKeyPath: forKeyPath)
}
}
// MARK: - Life cycle
override open func layoutSubviews() {
super.layoutSubviews()
layoutSubviews(byImage: layoutByImage)
}
func layoutSubviews(byImage: Bool) {
rotateView.frame = CGRect(origin: .zero, size: self.frame.size)
let frame = reversedRect
let reversedFrameWithInsetsSize = reversedFrameWithInsets.size
if byImage {
/* Zoom */
let fitScaleMultiplier = image == nil
? 1
: ic_CGSizeFitScaleMultiplier(image!.size, relativeToSize: reversedFrameWithInsetsSize)
scrollView.maximumZoomScale = fitScaleMultiplier * 1000
scrollView.minimumZoomScale = fitScaleMultiplier
scrollView.zoomScale = fitScaleMultiplier * savedProperty.scaleAspectRatio
} else {
/* Zoom */
let fitScaleMultiplier = ic_CGSizeFitScaleMultiplier(scrollView.visibleRect.size, relativeToSize: reversedFrameWithInsetsSize)
scrollView.maximumZoomScale *= fitScaleMultiplier
scrollView.minimumZoomScale *= fitScaleMultiplier
scrollView.zoomScale *= fitScaleMultiplier
/* Content inset */
var size: CGSize
if overlayView?.alpha == 1 {
size = CGSize(
width : scrollView.visibleRect.size.width * fitScaleMultiplier,
height : scrollView.visibleRect.size.height * fitScaleMultiplier)
} else {
size = ic_CGSizeFits(scrollView.contentSize, minSize: .zero, maxSize: reversedFrameWithInsetsSize)
}
scrollView.contentInset = centeredInsets(from: size, to: reversedFrameWithInsetsSize)
/* Content offset */
let savedFitScaleMultiplier = ic_CGSizeFitScaleMultiplier(savedProperty.cropRectSize, relativeToSize: reversedFrameWithInsetsSize)
var contentOffset = CGPoint(
x: savedProperty.contentOffset.x * savedFitScaleMultiplier,
y: savedProperty.contentOffset.y * savedFitScaleMultiplier)
contentOffset.x -= scrollView.contentInset.left
contentOffset.y -= scrollView.contentInset.top
scrollView.contentOffset = contentOffset
}
scrollView.frame = frame
overlayView?.frame = frame
overlayView?.cropRect = scrollView.visibleRect
overlayView?.layoutSubviews()
}
// MARK: -
// MARK: ** Actions **
// MARK: Overlay actions
/**
Show the overlay view with crop rectangle.
- Parameter duration: The total duration of the animations, measured in seconds. If you specify a negative value or 0, the changes are made without animating them. Default value is 0.
- Parameter options: A mask of options indicating how you want to perform the animations. For a list of valid constants, see UIViewAnimationOptions. Default value is .curveEaseInOut.
- Parameter completion: A block object to be executed when the animation sequence ends. This block has no return value and takes a single Boolean argument that indicates whether or not the animations actually finished before the completion handler was called. If the duration of the animation is 0, this block is performed at the beginning of the next run loop cycle. This parameter may be NULL.
*/
open func showOverlayView(animationDuration duration: TimeInterval = 0, options: UIViewAnimationOptions = .curveEaseInOut, completion: ((Bool) -> Void)? = nil) {
guard let image = image, let overlayView = overlayView, !isOverlayViewActive && !isAnimation else {
return
}
minEdgeInsets = overlayView.configuraiton.cropRectInsets
savedProperty.save(scrollView: scrollView)
cancelZoomingTimer()
let _animations: () -> Void = { _ in
self.layoutSubviews()
// Update scroll view content offsets using active zooming scale and insets.
self.scrollView.contentOffset = self.contentOffset(from: self.savedProperty.contentOffsetPercentage)
}
let _completion: (Bool) -> Void = { isFinished in
// Update zoom relative to crop rext
let fillScaleMultiplier = ic_CGSizeFillScaleMultiplier(image.size, relativeToSize: overlayView.cropRect.size)
self.scrollView.maximumZoomScale = fillScaleMultiplier * 1000
self.scrollView.minimumZoomScale = fillScaleMultiplier
/* */
self.isAnimation = false
self.isOverlayViewActive = true
completion?(isFinished)
}
/* Show */
layoutByImage = false
isAnimation = true
if duration == 0 {
_animations()
overlayView.alpha = 1
_completion(true)
} else {
UIView.animate(withDuration: duration, delay: 0, options: options, animations: _animations, completion: { _ in
UIView.animate(withDuration: duration, delay: 0, options: options, animations: {
overlayView.alpha = 1
}, completion: _completion)
})
}
}
/**
Hide the overlay view with crop rectangle.
- Parameter duration: The total duration of the animations, measured in seconds. If you specify a negative value or 0, the changes are made without animating them. Default value is 0.
- Parameter options: A mask of options indicating how you want to perform the animations. For a list of valid constants, see UIViewAnimationOptions. Default value is .curveEaseInOut.
- Parameter completion: A block object to be executed when the animation sequence ends. This block has no return value and takes a single Boolean argument that indicates whether or not the animations actually finished before the completion handler was called. If the duration of the animation is 0, this block is performed at the beginning of the next run loop cycle. This parameter may be NULL.
*/
open func hideOverlayView(animationDuration duration: TimeInterval = 0, options: UIViewAnimationOptions = .curveEaseInOut, completion: ((Bool) -> Void)? = nil) {
guard let image = image, let overlayView = overlayView, isOverlayViewActive && !isAnimation else {
return
}
minEdgeInsets = .zero
savedProperty.save(scrollView: scrollView)
cancelZoomingTimer()
isAnimation = true
let _animations: () -> Void = { _ in
self.layoutSubviews()
/**
Update scroll view content offsets
using active zooming scale and insets.
*/
self.scrollView.contentOffset = self.contentOffset(from: self.savedProperty.contentOffsetPercentage)
}
let _completion: (Bool) -> Void = { isFinished in
// Update zoom relative to crop rext
let fitScaleMultiplier = ic_CGSizeFitScaleMultiplier(image.size, relativeToSize: self.reversedFrameWithInsets.size)
self.scrollView.maximumZoomScale = fitScaleMultiplier * 1000
self.scrollView.minimumZoomScale = fitScaleMultiplier
/* */
self.isAnimation = false
self.isOverlayViewActive = false
self.layoutByImage = false
completion?(isFinished)
}
if duration == 0 {
overlayView.alpha = 0
_animations()
_completion(true)
} else {
UIView.animate(withDuration: duration, delay: 0, options: options, animations: {
overlayView.alpha = 0
}, completion: { _ in
UIView.animate(withDuration: duration, delay: 0, options: options, animations: _animations, completion: _completion)
})
}
}
// MARK: Rotate
/**
Rotate the image on the angle in multiples of 90 degrees (M_PI_2).
- Parameter angle: Rotation angle. The angle a multiple of 90 degrees (M_PI_2).
- Parameter duration: The total duration of the animations, measured in seconds. If you specify a negative value or 0, the changes are made without animating them. Default value is 0.
- Parameter options: A mask of options indicating how you want to perform the animations. For a list of valid constants, see UIViewAnimationOptions. Default value is .curveEaseInOut.
- Parameter completion: A block object to be executed when the animation sequence ends. This block has no return value and takes a single Boolean argument that indicates whether or not the animations actually finished before the completion handler was called. If the duration of the animation is 0, this block is performed at the beginning of the next run loop cycle. This parameter may be NULL.
*/
open func rotate(_ angle: Double, withDuration duration: TimeInterval = 0, options: UIViewAnimationOptions = .curveEaseInOut, completion: ((Bool) -> Void)? = nil) {
guard angle.truncatingRemainder(dividingBy: M_PI_2) == 0 else {
return
}
self.angle = angle
savedProperty.save(scrollView: scrollView)
let _animations: () -> Void = { _ in
self.rotateView.transform = CGAffineTransform(rotationAngle: CGFloat(angle))
self.layoutSubviews()
}
let _completion: (Bool) -> Void = { isFinished in
completion?(isFinished)
}
if duration == 0 {
_animations()
_completion(true)
} else {
UIView.animate(withDuration: duration, delay: 0, options: options, animations: _animations, completion: _completion)
}
}
// MARK: Reset
/**
Return Cropper view to the initial state.
- Parameter duration: The total duration of the animations, measured in seconds. If you specify a negative value or 0, the changes are made without animating them. Default value is 0.
- Parameter options: A mask of options indicating how you want to perform the animations. For a list of valid constants, see UIViewAnimationOptions. Default value is .curveEaseInOut.
- Parameter completion: A block object to be executed when the animation sequence ends. This block has no return value and takes a single Boolean argument that indicates whether or not the animations actually finished before the completion handler was called. If the duration of the animation is 0, this block is performed at the beginning of the next run loop cycle. This parameter may be NULL.
*/
open func reset(animationDuration duration: TimeInterval = 0, options: UIViewAnimationOptions = .curveEaseInOut, completion: ((Bool) -> Void)? = nil) {
guard !isAnimation else {
return
}
savedProperty = SavedProperty()
angle = 0
cancelZoomingTimer()
let _animations: () -> Void = { _ in
self.rotateView.transform = CGAffineTransform.identity
let _layoutByImage = self.layoutByImage
self.layoutByImage = true
self.layoutSubviews()
self.layoutByImage = _layoutByImage
}
let _completion: (Bool) -> Void = { isFinished in
self.isAnimation = false
completion?(isFinished)
}
isAnimation = true
if duration == 0 {
_animations()
_completion(true)
} else {
UIView.animate(withDuration: duration, delay: 0, options: options, animations: _animations, completion: _completion)
}
}
// MARK: - Edge insets zooming
fileprivate var zoomingTimer: Timer?
fileprivate func startZoomingTimer() {
guard let overlayView = overlayView else {
return
}
cancelZoomingTimer()
zoomingTimer = Timer.scheduledTimer(timeInterval: overlayView.configuraiton.zoomingToFitDelay, target: self, selector: #selector(zoomAction), userInfo: nil, repeats: false)
}
@objc fileprivate func zoomAction() {
guard let overlayView = overlayView else {
return
}
savedProperty.save(scrollView: scrollView)
overlayView.showGrid(false)
overlayView.showOverlayBlur(true)
UIView.animate(withDuration: overlayView.configuraiton.animation.duration, delay: 0, options: overlayView.configuraiton.animation.options, animations: {
self.layoutSubviews()
})
}
fileprivate func cancelZoomingTimer() {
zoomingTimer?.invalidate()
zoomingTimer = nil
}
// MARK: - After interaction actions
fileprivate func beforeInteraction() {
guard let overlayView = overlayView, overlayView.alpha == 1.0 else {
return
}
cancelZoomingTimer()
overlayView.showGrid(true)
overlayView.showOverlayBlur(false)
}
fileprivate func afterInteraction() {
guard let overlayView = overlayView, overlayView.alpha == 1.0 else {
return
}
startZoomingTimer()
savedProperty.save(scrollView: scrollView)
}
// MARK: - Helper methods
private func centeredInsets(from size: CGSize, to relativeSize: CGSize) -> UIEdgeInsets {
let center = ic_CGPointCenters(size, relativeToSize: relativeSize)
// Fix insets direct to orientation
return UIEdgeInsetsMake(
center.y + minEdgeInsets.top,
center.x + minEdgeInsets.left,
center.y + minEdgeInsets.bottom,
center.x + minEdgeInsets.right)
}
private func contentOffset(from savedContentOffsetPercentage: CGPointPercentage) -> CGPoint {
var contentOffset = CGPoint(
x: scrollView.contentInset.left > minEdgeInsets.left
? 0
: ((scrollView.contentSize.width - reversedFrameWithInsets.size.width) * savedContentOffsetPercentage.x),
y: scrollView.contentInset.top > minEdgeInsets.left
? 0
: ((scrollView.contentSize.height - reversedFrameWithInsets.size.height) * savedContentOffsetPercentage.y))
contentOffset.x -= scrollView.contentInset.left
contentOffset.y -= scrollView.contentInset.top
return contentOffset
}
// MARK: - AKImageCropperOverlayViewDelegate
func cropperOverlayViewDidChangeCropRect(_ view: AKImageCropperOverlayView, _ cropRect: CGRect) {
scrollView.contentInset = UIEdgeInsetsMake(
cropRect.origin.y,
cropRect.origin.x,
view.frame.size.height - cropRect.size.height - cropRect.origin.y,
view.frame.size.width - cropRect.size.width - cropRect.origin.x)
if cropRect.size.height > scrollView.contentSize.height || cropRect.size.width > scrollView.contentSize.width {
let fillScaleMultiplier = ic_CGSizeFillScaleMultiplier(scrollView.contentSize, relativeToSize: cropRect.size)
scrollView.maximumZoomScale *= fillScaleMultiplier
scrollView.minimumZoomScale *= fillScaleMultiplier
scrollView.zoomScale *= fillScaleMultiplier
}
}
// MARK: - UIScrollViewDelegate
public func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return scrollView.subviews.first
}
public func scrollViewDidZoom(_ scrollView: UIScrollView) {
guard layoutByImage else {
return
}
let size = ic_CGSizeFits(scrollView.contentSize, minSize: .zero, maxSize: reversedFrameWithInsets.size)
scrollView.contentInset = centeredInsets(from: size, to: reversedFrameWithInsets.size)
}
public func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) {
beforeInteraction()
}
public func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) {
afterInteraction()
}
public func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
beforeInteraction()
}
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
afterInteraction()
}
}
// MARK: - AKImageCropperViewDelegate
public protocol AKImageCropperViewDelegate : class {
/**
Tells the delegate that crop frame was changed.
- Parameter view : The image cropper view.
- Parameter rect : New crop rectangle origin and size.
*/
func imageCropperViewDidChangeCropRect(view: AKImageCropperView, cropRect rect: CGRect)
}
public extension AKImageCropperViewDelegate {
func imageCropperViewDidChangeCropRect(view: AKImageCropperView, cropRect rect: CGRect) {}
}
================================================
FILE: AKImageCropperView/IC_CGFloatExtension.swift
================================================
//
// ic_CGFloatExtension.swift
// AKImageCropperView
//
// Created by Artem Krachulov.
// Copyright (c) 2016 Artem Krachulov. All rights reserved.
//
import UIKit
extension CGFloat {
/** Rounds the value to the nearest with precision. */
public func ic_roundTo(precision: Int) -> CGFloat {
let divisor = pow(10.0, CGFloat(precision))
return (self * divisor).rounded() / divisor
}
}
/** Rounds the value to the nearest with multiplier. */
public func ic_round(x: CGFloat, multiplier: CGFloat) -> CGFloat {
return multiplier * round(x / multiplier)
}
================================================
FILE: AKImageCropperView/IC_CGPointExtension.swift
================================================
//
// IC_CGPointExtension.swift
// AKImageCropperView
//
// Created by Artem Krachulov.
// Copyright (c) 2016 Artem Krachulov. All rights reserved.
//
import Foundation
/** Return centered origin value relative to other size . */
func ic_CGPointCenters(_ size1: CGSize, relativeToSize size2: CGSize) -> CGPoint {
return CGPoint(x: size2.width / 2 - size1.width / 2, y: size2.height / 2 - size1.height / 2)
}
================================================
FILE: AKImageCropperView/IC_CGSizeExtensions.swift
================================================
//
// IC_CGSizeExtensions.swift
// AKImageCropperView
//
// Created by Artem Krachulov.
// Copyright (c) 2016 Artem Krachulov. All rights reserved.
//
import Foundation
/** Returns fill scale value relative to target size with aspect ratio. */
func ic_CGSizeFitScaleMultiplier(_ size1: CGSize, relativeToSize size2: CGSize) -> CGFloat {
guard size1.width != 0 && size1.height != 0 else { return 1.0 }
return min(size2.height / size1.height, size2.width / size1.width)
}
/** Returns fill scale value relative to target size with aspect ratio. */
func ic_CGSizeFillScaleMultiplier(_ size1: CGSize, relativeToSize size2: CGSize) -> CGFloat {
guard size1.width != 0 && size1.height != 0 else { return 1.0 }
return max(size2.height / size1.height, size2.width / size1.width)
}
/** Returns size that fits min and max sizes. */
func ic_CGSizeFits(_ size: CGSize, minSize: CGSize, maxSize: CGSize) -> CGSize {
var size = size
if size.width > maxSize.width {
size.width = maxSize.width
}
if size.height > maxSize.height {
size.height = maxSize.height
}
if size.width < minSize.width {
size.width = minSize.width
}
if size.height < minSize.height {
size.height = minSize.height
}
return size
}
================================================
FILE: AKImageCropperView/IC_UIImageExtensions.swift
================================================
//
// IC_UIImageExtensions.swift
// AKImageCropperView
//
// Created by Artem Krachulov.
// Copyright (c) 2016 Artem Krachulov. All rights reserved.
//
import Foundation
extension UIImage {
/** Returns image cropped from selected rectangle. */
func ic_imageInRect(_ rect: CGRect) -> UIImage? {
UIGraphicsBeginImageContext(rect.size)
// Create the bitmap context
guard let context = UIGraphicsGetCurrentContext() else {
return nil
}
// Sets the clipping path to the intersection of the current clipping path with the area defined by the specified rectangle.
context.clip(to: CGRect(origin: .zero, size: rect.size))
self.draw(in: CGRect(origin: CGPoint(x: -rect.origin.x, y: -rect.origin.y), size: self.size))
// Returns an image based on the contents of the current bitmap-based graphics context.
let contextImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return contextImage
}
/** Returns image rotated by specified angle. */
func ic_rotateByAngle(_ angle: Double) -> UIImage? {
// Calculate the size of the rotated view's containing box for our drawing space
let rotatedViewBox = UIView(frame: CGRect(origin: .zero, size: self.size))
rotatedViewBox.transform = CGAffineTransform(rotationAngle: CGFloat(angle))
let rotatedSize = rotatedViewBox.frame.size
UIGraphicsBeginImageContext(rotatedSize)
// Create the bitmap context
guard let context = UIGraphicsGetCurrentContext() else {
return nil
}
context.translateBy(x: rotatedSize.width / 2.0, y: rotatedSize.height / 2.0)
context.rotate(by: CGFloat(angle))
self.draw(in: CGRect(origin: CGPoint(x: -self.size.width / 2, y: -self.size.height / 2), size: self.size))
// Returns an image based on the contents of the current bitmap-based graphics context.
let contextImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return contextImage
}
}
================================================
FILE: AKImageCropperView/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>2.0.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
================================================
FILE: AKImageCropperView/PrimaryFilledButton.swift
================================================
//
// PrimaryFilledButton.swift
// Visitor
//
// Created by Artem Krachulov on 1/16/17.
// Copyright © 2017 VZPass. All rights reserved.
//
import Foundation
================================================
FILE: AKImageCropperView.podspec
================================================
Pod::Spec.new do |s|
s.name = "AKImageCropperView"
s.version = "2.0.0"
s.homepage = "https://github.com/artemkrachulov/AKImageCropperView"
s.summary = "Responsive image cropper"
s.description = <<-DESC
Image cropping plugin which supported different devices orientation. Easy to set up and configure. Has many settings for flexible integration into your project. Behavior is similar to native iOS photo cropper.
DESC
s.license = { :type => "MIT", :file => "LICENSE" }
s.author = { "Artem Krachulov" => "artem.krachulov@gmail.com" }
# Source Info
s.ios.deployment_target = "8.0"
s.source = {
:git => "https://github.com/artemkrachulov/AKImageCropperView.git",
:tag => 'v'+s.version.to_s
}
s.source_files = "AKImageCropperView/*.{swift}"
s.pod_target_xcconfig = { 'SWIFT_VERSION' => '3.0' }
end
================================================
FILE: AKImageCropperView.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
011028AB1DFFDD24002AA94E /* AKImageCropperView.h in Headers */ = {isa = PBXBuildFile; fileRef = 011028A91DFFDD24002AA94E /* AKImageCropperView.h */; settings = {ATTRIBUTES = (Public, ); }; };
0141AF1D1E052BF6007C5672 /* AKImageCropperScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0141AF1C1E052BF6007C5672 /* AKImageCropperScrollView.swift */; };
0155304A1E28F2C500961922 /* IC_UIImageExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 015530491E28F2C500961922 /* IC_UIImageExtensions.swift */; };
0155304C1E28FDB800961922 /* IC_CGSizeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0155304B1E28FDB800961922 /* IC_CGSizeExtensions.swift */; };
0155304E1E28FEC600961922 /* IC_CGPointExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0155304D1E28FEC600961922 /* IC_CGPointExtension.swift */; };
0166B68F1E03E1560081B751 /* AKImageCropperOverlayViewConfigurationOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0166B68E1E03E1560081B751 /* AKImageCropperOverlayViewConfigurationOverlay.swift */; };
016E5EF21DFFDD8300662D31 /* AKImageCropperOverlayViewTouchState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 016E5EE71DFFDD8300662D31 /* AKImageCropperOverlayViewTouchState.swift */; };
016E5EF31DFFDD8300662D31 /* AKImageCropperOverlayViewConfigurationCorner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 016E5EE81DFFDD8300662D31 /* AKImageCropperOverlayViewConfigurationCorner.swift */; };
016E5EF41DFFDD8300662D31 /* AKImageCropperOverlayViewConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 016E5EE91DFFDD8300662D31 /* AKImageCropperOverlayViewConfiguration.swift */; };
016E5EF61DFFDD8300662D31 /* AKImageCropperOverlayViewConfigurationEdge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 016E5EEB1DFFDD8300662D31 /* AKImageCropperOverlayViewConfigurationEdge.swift */; };
016E5EF71DFFDD8300662D31 /* AKImageCropperOverlayViewConfigurationGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 016E5EEC1DFFDD8300662D31 /* AKImageCropperOverlayViewConfigurationGrid.swift */; };
016E5EF81DFFDD8300662D31 /* AKImageCropperOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 016E5EED1DFFDD8300662D31 /* AKImageCropperOverlayView.swift */; };
016E5EFB1DFFDD8300662D31 /* AKImageCropperView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 016E5EF01DFFDD8300662D31 /* AKImageCropperView.swift */; };
016E5EFE1DFFE77400662D31 /* AKImageCropperView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 011028A71DFFDD24002AA94E /* AKImageCropperView.framework */; };
016E5EFF1DFFE77400662D31 /* AKImageCropperView.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 011028A71DFFDD24002AA94E /* AKImageCropperView.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
01C48FBC1DEC250100FBBE34 /* CustomImageCropperOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01C48FBB1DEC250100FBBE34 /* CustomImageCropperOverlayView.swift */; };
01F61BE51E4DC7B800977E33 /* IC_CGFloatExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F61BE41E4DC7B800977E33 /* IC_CGFloatExtension.swift */; };
01FA13EF1DDF0C6E006B8C3A /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01FA13EE1DDF0C6E006B8C3A /* Constants.swift */; };
4356D6141B8F3DE90033FDBD /* ImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4356D6131B8F3DE90033FDBD /* ImageViewController.swift */; };
43CBB4031B870EBC00C8F9AE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CBB4021B870EBC00C8F9AE /* AppDelegate.swift */; };
43CBB4051B870EBC00C8F9AE /* HomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CBB4041B870EBC00C8F9AE /* HomeViewController.swift */; };
43CBB4081B870EBC00C8F9AE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 43CBB4061B870EBC00C8F9AE /* Main.storyboard */; };
43CBB40A1B870EBC00C8F9AE /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 43CBB4091B870EBC00C8F9AE /* Images.xcassets */; };
43CBB40D1B870EBC00C8F9AE /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 43CBB40B1B870EBC00C8F9AE /* LaunchScreen.xib */; };
43CBB4241B8716EA00C8F9AE /* CropperViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CBB4231B8716EA00C8F9AE /* CropperViewController.swift */; };
43CBB4401B8739B100C8F9AE /* ImagesTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CBB43F1B8739B100C8F9AE /* ImagesTableViewController.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
016E5F001DFFE77400662D31 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 43CBB3F51B870EBC00C8F9AE /* Project object */;
proxyType = 1;
remoteGlobalIDString = 011028A61DFFDD24002AA94E;
remoteInfo = AKImageCropperView;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
016E5F021DFFE77400662D31 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
016E5EFF1DFFE77400662D31 /* AKImageCropperView.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
011028A71DFFDD24002AA94E /* AKImageCropperView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AKImageCropperView.framework; sourceTree = BUILT_PRODUCTS_DIR; };
011028A91DFFDD24002AA94E /* AKImageCropperView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AKImageCropperView.h; sourceTree = "<group>"; };
011028AA1DFFDD24002AA94E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
0141AF1C1E052BF6007C5672 /* AKImageCropperScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AKImageCropperScrollView.swift; sourceTree = "<group>"; };
015530491E28F2C500961922 /* IC_UIImageExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IC_UIImageExtensions.swift; sourceTree = "<group>"; };
0155304B1E28FDB800961922 /* IC_CGSizeExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IC_CGSizeExtensions.swift; sourceTree = "<group>"; };
0155304D1E28FEC600961922 /* IC_CGPointExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IC_CGPointExtension.swift; sourceTree = "<group>"; };
0166B68E1E03E1560081B751 /* AKImageCropperOverlayViewConfigurationOverlay.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AKImageCropperOverlayViewConfigurationOverlay.swift; sourceTree = "<group>"; };
016E5EE71DFFDD8300662D31 /* AKImageCropperOverlayViewTouchState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AKImageCropperOverlayViewTouchState.swift; sourceTree = "<group>"; };
016E5EE81DFFDD8300662D31 /* AKImageCropperOverlayViewConfigurationCorner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AKImageCropperOverlayViewConfigurationCorner.swift; sourceTree = "<group>"; };
016E5EE91DFFDD8300662D31 /* AKImageCropperOverlayViewConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AKImageCropperOverlayViewConfiguration.swift; sourceTree = "<group>"; };
016E5EEB1DFFDD8300662D31 /* AKImageCropperOverlayViewConfigurationEdge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AKImageCropperOverlayViewConfigurationEdge.swift; sourceTree = "<group>"; };
016E5EEC1DFFDD8300662D31 /* AKImageCropperOverlayViewConfigurationGrid.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AKImageCropperOverlayViewConfigurationGrid.swift; sourceTree = "<group>"; };
016E5EED1DFFDD8300662D31 /* AKImageCropperOverlayView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AKImageCropperOverlayView.swift; sourceTree = "<group>"; };
016E5EF01DFFDD8300662D31 /* AKImageCropperView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AKImageCropperView.swift; sourceTree = "<group>"; };
01C48FBB1DEC250100FBBE34 /* CustomImageCropperOverlayView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomImageCropperOverlayView.swift; sourceTree = "<group>"; };
01F61BE41E4DC7B800977E33 /* IC_CGFloatExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IC_CGFloatExtension.swift; sourceTree = "<group>"; };
01FA13EE1DDF0C6E006B8C3A /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
4356D6131B8F3DE90033FDBD /* ImageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageViewController.swift; sourceTree = "<group>"; };
43CBB3FD1B870EBC00C8F9AE /* AKImageCropperViewExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AKImageCropperViewExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
43CBB4011B870EBC00C8F9AE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
43CBB4021B870EBC00C8F9AE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
43CBB4041B870EBC00C8F9AE /* HomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewController.swift; sourceTree = "<group>"; };
43CBB4071B870EBC00C8F9AE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
43CBB4091B870EBC00C8F9AE /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
43CBB40C1B870EBC00C8F9AE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
43CBB4231B8716EA00C8F9AE /* CropperViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CropperViewController.swift; sourceTree = "<group>"; };
43CBB43F1B8739B100C8F9AE /* ImagesTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImagesTableViewController.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
011028A31DFFDD24002AA94E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
43CBB3FA1B870EBC00C8F9AE /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
016E5EFE1DFFE77400662D31 /* AKImageCropperView.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
011028A81DFFDD24002AA94E /* AKImageCropperView */ = {
isa = PBXGroup;
children = (
011028A91DFFDD24002AA94E /* AKImageCropperView.h */,
011028AA1DFFDD24002AA94E /* Info.plist */,
016E5EF01DFFDD8300662D31 /* AKImageCropperView.swift */,
0141AF1C1E052BF6007C5672 /* AKImageCropperScrollView.swift */,
016E5EED1DFFDD8300662D31 /* AKImageCropperOverlayView.swift */,
016E5EE71DFFDD8300662D31 /* AKImageCropperOverlayViewTouchState.swift */,
01F61BE61E4DC89300977E33 /* configuration */,
015530461E28EF5A00961922 /* extensions */,
);
path = AKImageCropperView;
sourceTree = "<group>";
};
015530461E28EF5A00961922 /* extensions */ = {
isa = PBXGroup;
children = (
015530491E28F2C500961922 /* IC_UIImageExtensions.swift */,
0155304B1E28FDB800961922 /* IC_CGSizeExtensions.swift */,
0155304D1E28FEC600961922 /* IC_CGPointExtension.swift */,
01F61BE41E4DC7B800977E33 /* IC_CGFloatExtension.swift */,
);
name = extensions;
sourceTree = "<group>";
};
01F61BE61E4DC89300977E33 /* configuration */ = {
isa = PBXGroup;
children = (
016E5EE91DFFDD8300662D31 /* AKImageCropperOverlayViewConfiguration.swift */,
0166B68E1E03E1560081B751 /* AKImageCropperOverlayViewConfigurationOverlay.swift */,
016E5EEB1DFFDD8300662D31 /* AKImageCropperOverlayViewConfigurationEdge.swift */,
016E5EE81DFFDD8300662D31 /* AKImageCropperOverlayViewConfigurationCorner.swift */,
016E5EEC1DFFDD8300662D31 /* AKImageCropperOverlayViewConfigurationGrid.swift */,
);
name = configuration;
sourceTree = "<group>";
};
43CBB3F41B870EBC00C8F9AE = {
isa = PBXGroup;
children = (
011028A81DFFDD24002AA94E /* AKImageCropperView */,
43CBB3FF1B870EBC00C8F9AE /* AKImageCropperViewExample */,
43CBB3FE1B870EBC00C8F9AE /* Products */,
);
sourceTree = "<group>";
};
43CBB3FE1B870EBC00C8F9AE /* Products */ = {
isa = PBXGroup;
children = (
43CBB3FD1B870EBC00C8F9AE /* AKImageCropperViewExample.app */,
011028A71DFFDD24002AA94E /* AKImageCropperView.framework */,
);
name = Products;
sourceTree = "<group>";
};
43CBB3FF1B870EBC00C8F9AE /* AKImageCropperViewExample */ = {
isa = PBXGroup;
children = (
43CBB4011B870EBC00C8F9AE /* Info.plist */,
43CBB4021B870EBC00C8F9AE /* AppDelegate.swift */,
01FA13EE1DDF0C6E006B8C3A /* Constants.swift */,
43CBB4091B870EBC00C8F9AE /* Images.xcassets */,
43CBB40B1B870EBC00C8F9AE /* LaunchScreen.xib */,
43CBB4061B870EBC00C8F9AE /* Main.storyboard */,
43CBB4041B870EBC00C8F9AE /* HomeViewController.swift */,
43CBB43F1B8739B100C8F9AE /* ImagesTableViewController.swift */,
43CBB4231B8716EA00C8F9AE /* CropperViewController.swift */,
4356D6131B8F3DE90033FDBD /* ImageViewController.swift */,
01C48FBB1DEC250100FBBE34 /* CustomImageCropperOverlayView.swift */,
);
path = AKImageCropperViewExample;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
011028A41DFFDD24002AA94E /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
011028AB1DFFDD24002AA94E /* AKImageCropperView.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
011028A61DFFDD24002AA94E /* AKImageCropperView */ = {
isa = PBXNativeTarget;
buildConfigurationList = 011028B01DFFDD24002AA94E /* Build configuration list for PBXNativeTarget "AKImageCropperView" */;
buildPhases = (
011028A21DFFDD24002AA94E /* Sources */,
011028A31DFFDD24002AA94E /* Frameworks */,
011028A41DFFDD24002AA94E /* Headers */,
011028A51DFFDD24002AA94E /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = AKImageCropperView;
productName = AKImageCropperView;
productReference = 011028A71DFFDD24002AA94E /* AKImageCropperView.framework */;
productType = "com.apple.product-type.framework";
};
43CBB3FC1B870EBC00C8F9AE /* AKImageCropperViewExample */ = {
isa = PBXNativeTarget;
buildConfigurationList = 43CBB41C1B870EBC00C8F9AE /* Build configuration list for PBXNativeTarget "AKImageCropperViewExample" */;
buildPhases = (
43CBB3F91B870EBC00C8F9AE /* Sources */,
43CBB3FA1B870EBC00C8F9AE /* Frameworks */,
43CBB3FB1B870EBC00C8F9AE /* Resources */,
016E5F021DFFE77400662D31 /* Embed Frameworks */,
);
buildRules = (
);
dependencies = (
016E5F011DFFE77400662D31 /* PBXTargetDependency */,
);
name = AKImageCropperViewExample;
productName = AKImageCropperDemo;
productReference = 43CBB3FD1B870EBC00C8F9AE /* AKImageCropperViewExample.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
43CBB3F51B870EBC00C8F9AE /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftMigration = 0700;
LastSwiftUpdateCheck = 0700;
LastUpgradeCheck = 0820;
ORGANIZATIONNAME = "Artem Krachulov";
TargetAttributes = {
011028A61DFFDD24002AA94E = {
CreatedOnToolsVersion = 8.1;
LastSwiftMigration = 0810;
ProvisioningStyle = Manual;
};
43CBB3FC1B870EBC00C8F9AE = {
CreatedOnToolsVersion = 6.4;
DevelopmentTeam = 5JV4U8LEZ8;
LastSwiftMigration = 0810;
};
};
};
buildConfigurationList = 43CBB3F81B870EBC00C8F9AE /* Build configuration list for PBXProject "AKImageCropperView" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 43CBB3F41B870EBC00C8F9AE;
productRefGroup = 43CBB3FE1B870EBC00C8F9AE /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
011028A61DFFDD24002AA94E /* AKImageCropperView */,
43CBB3FC1B870EBC00C8F9AE /* AKImageCropperViewExample */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
011028A51DFFDD24002AA94E /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
43CBB3FB1B870EBC00C8F9AE /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
43CBB4081B870EBC00C8F9AE /* Main.storyboard in Resources */,
43CBB40D1B870EBC00C8F9AE /* LaunchScreen.xib in Resources */,
43CBB40A1B870EBC00C8F9AE /* Images.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
011028A21DFFDD24002AA94E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
016E5EF41DFFDD8300662D31 /* AKImageCropperOverlayViewConfiguration.swift in Sources */,
0166B68F1E03E1560081B751 /* AKImageCropperOverlayViewConfigurationOverlay.swift in Sources */,
0155304C1E28FDB800961922 /* IC_CGSizeExtensions.swift in Sources */,
016E5EF81DFFDD8300662D31 /* AKImageCropperOverlayView.swift in Sources */,
0155304A1E28F2C500961922 /* IC_UIImageExtensions.swift in Sources */,
016E5EFB1DFFDD8300662D31 /* AKImageCropperView.swift in Sources */,
016E5EF31DFFDD8300662D31 /* AKImageCropperOverlayViewConfigurationCorner.swift in Sources */,
01F61BE51E4DC7B800977E33 /* IC_CGFloatExtension.swift in Sources */,
0155304E1E28FEC600961922 /* IC_CGPointExtension.swift in Sources */,
016E5EF21DFFDD8300662D31 /* AKImageCropperOverlayViewTouchState.swift in Sources */,
0141AF1D1E052BF6007C5672 /* AKImageCropperScrollView.swift in Sources */,
016E5EF71DFFDD8300662D31 /* AKImageCropperOverlayViewConfigurationGrid.swift in Sources */,
016E5EF61DFFDD8300662D31 /* AKImageCropperOverlayViewConfigurationEdge.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
43CBB3F91B870EBC00C8F9AE /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
43CBB4051B870EBC00C8F9AE /* HomeViewController.swift in Sources */,
01C48FBC1DEC250100FBBE34 /* CustomImageCropperOverlayView.swift in Sources */,
43CBB4241B8716EA00C8F9AE /* CropperViewController.swift in Sources */,
01FA13EF1DDF0C6E006B8C3A /* Constants.swift in Sources */,
43CBB4031B870EBC00C8F9AE /* AppDelegate.swift in Sources */,
4356D6141B8F3DE90033FDBD /* ImageViewController.swift in Sources */,
43CBB4401B8739B100C8F9AE /* ImagesTableViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
016E5F011DFFE77400662D31 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 011028A61DFFDD24002AA94E /* AKImageCropperView */;
targetProxy = 016E5F001DFFE77400662D31 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
43CBB4061B870EBC00C8F9AE /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
43CBB4071B870EBC00C8F9AE /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
43CBB40B1B870EBC00C8F9AE /* LaunchScreen.xib */ = {
isa = PBXVariantGroup;
children = (
43CBB40C1B870EBC00C8F9AE /* Base */,
);
name = LaunchScreen.xib;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
011028B11DFFDD24002AA94E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ENABLE_MODULES = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = AKImageCropperView/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.artemkrachulov.AKImageCropperView;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
011028B21DFFDD24002AA94E /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ENABLE_MODULES = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = AKImageCropperView/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.artemkrachulov.AKImageCropperView;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 3.0;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
43CBB41A1B870EBC00C8F9AE /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
43CBB41B1B870EBC00C8F9AE /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
43CBB41D1B870EBC00C8F9AE /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = 5JV4U8LEZ8;
INFOPLIST_FILE = AKImageCropperViewExample/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_SWIFT_FLAGS = "-D AKImageCropperViewDEBUG";
PRODUCT_BUNDLE_IDENTIFIER = artem.krachulov.AKImageCropper;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
};
name = Debug;
};
43CBB41E1B870EBC00C8F9AE /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = 5JV4U8LEZ8;
INFOPLIST_FILE = AKImageCropperViewExample/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MTL_ENABLE_DEBUG_INFO = No;
PRODUCT_BUNDLE_IDENTIFIER = artem.krachulov.AKImageCropper;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 3.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
011028B01DFFDD24002AA94E /* Build configuration list for PBXNativeTarget "AKImageCropperView" */ = {
isa = XCConfigurationList;
buildConfigurations = (
011028B11DFFDD24002AA94E /* Debug */,
011028B21DFFDD24002AA94E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
43CBB3F81B870EBC00C8F9AE /* Build configuration list for PBXProject "AKImageCropperView" */ = {
isa = XCConfigurationList;
buildConfigurations = (
43CBB41A1B870EBC00C8F9AE /* Debug */,
43CBB41B1B870EBC00C8F9AE /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
43CBB41C1B870EBC00C8F9AE /* Build configuration list for PBXNativeTarget "AKImageCropperViewExample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
43CBB41D1B870EBC00C8F9AE /* Debug */,
43CBB41E1B870EBC00C8F9AE /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 43CBB3F51B870EBC00C8F9AE /* Project object */;
}
================================================
FILE: AKImageCropperView.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:/Users/artemkrachulov/Repos/AKImageCropper/AKImageCropperView.xcodeproj">
</FileRef>
</Workspace>
================================================
FILE: AKImageCropperView.xcodeproj/project.xcworkspace/xcshareddata/AKImageCropperDemo.xcscmblueprint
================================================
{
"DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "74FC6B16832EDE035AC047E84A01E2F0C9097FE9",
"DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
},
"DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
"74FC6B16832EDE035AC047E84A01E2F0C9097FE9" : 0,
"F0D98E582FA112BC083159DA5B51E17128CD38B4" : 0,
"B234E04AE2BC64FD278418F0B83A3F07C2F904DB" : 0,
"78558585540D6A03C4B47C1A04640D7B2BD34C66" : 0,
"EAD4BE7C3980137405ECAE8A71597A264DEE9FD1" : 0
},
"DVTSourceControlWorkspaceBlueprintIdentifierKey" : "CACF0744-A1D3-4BA8-8481-EB16E6A7A6C0",
"DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
"74FC6B16832EDE035AC047E84A01E2F0C9097FE9" : "AKImageCropper\/",
"F0D98E582FA112BC083159DA5B51E17128CD38B4" : "AKImageCropper\/Demo\/Vendor\/Extensions\/Double-Float\/",
"B234E04AE2BC64FD278418F0B83A3F07C2F904DB" : "AKImageCropper\/Demo\/Vendor\/Extensions\/CGRect-CGSize\/",
"78558585540D6A03C4B47C1A04640D7B2BD34C66" : "AKImageCropper\/Demo\/Vendor\/Extensions\/Range\/",
"EAD4BE7C3980137405ECAE8A71597A264DEE9FD1" : "AKImageCropper\/Demo\/Vendor\/Extensions\/UIImage\/"
},
"DVTSourceControlWorkspaceBlueprintNameKey" : "AKImageCropperDemo",
"DVTSourceControlWorkspaceBlueprintVersion" : 204,
"DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "Demo\/AKImageCropperDemo.xcodeproj",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:artemkrachulov\/AKImageCropper.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "74FC6B16832EDE035AC047E84A01E2F0C9097FE9"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/artemkrachulov\/Range-Extension.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "78558585540D6A03C4B47C1A04640D7B2BD34C66"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/artemkrachulov\/CGRect-CGSize-Extensions.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "B234E04AE2BC64FD278418F0B83A3F07C2F904DB"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/artemkrachulov\/UIImage-Extension.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "EAD4BE7C3980137405ECAE8A71597A264DEE9FD1"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/artemkrachulov\/Double-Float-Extensions.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "F0D98E582FA112BC083159DA5B51E17128CD38B4"
}
]
}
================================================
FILE: AKImageCropperView.xcodeproj/project.xcworkspace/xcshareddata/Demo.xcscmblueprint
================================================
{
"DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "74FC6B16832EDE035AC047E84A01E2F0C9097FE9",
"DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
},
"DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
"74FC6B16832EDE035AC047E84A01E2F0C9097FE9" : 0,
"F0D98E582FA112BC083159DA5B51E17128CD38B4" : 0,
"B234E04AE2BC64FD278418F0B83A3F07C2F904DB" : 0,
"78558585540D6A03C4B47C1A04640D7B2BD34C66" : 0,
"EAD4BE7C3980137405ECAE8A71597A264DEE9FD1" : 0
},
"DVTSourceControlWorkspaceBlueprintIdentifierKey" : "349E57B7-4D5D-430B-958A-5768311D2060",
"DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
"74FC6B16832EDE035AC047E84A01E2F0C9097FE9" : "AKImageCropper\/",
"F0D98E582FA112BC083159DA5B51E17128CD38B4" : "AKImageCropper\/Demo\/Vendor\/Extensions\/Double-Float\/",
"B234E04AE2BC64FD278418F0B83A3F07C2F904DB" : "AKImageCropper\/Demo\/Vendor\/Extensions\/CGRect-CGSize\/",
"78558585540D6A03C4B47C1A04640D7B2BD34C66" : "AKImageCropper\/Demo\/Vendor\/Extensions\/Range\/",
"EAD4BE7C3980137405ECAE8A71597A264DEE9FD1" : "AKImageCropper\/Demo\/Vendor\/Extensions\/UIImage\/"
},
"DVTSourceControlWorkspaceBlueprintNameKey" : "Demo",
"DVTSourceControlWorkspaceBlueprintVersion" : 204,
"DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "Demo\/Demo.xcodeproj",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:artemkrachulov\/AKImageCropper.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "74FC6B16832EDE035AC047E84A01E2F0C9097FE9"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/artemkrachulov\/Range-Extension.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "78558585540D6A03C4B47C1A04640D7B2BD34C66"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/artemkrachulov\/CGRect-CGSize-Extensions.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "B234E04AE2BC64FD278418F0B83A3F07C2F904DB"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/artemkrachulov\/UIImage-Extension.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "EAD4BE7C3980137405ECAE8A71597A264DEE9FD1"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/artemkrachulov\/Double-Float-Extensions.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "F0D98E582FA112BC083159DA5B51E17128CD38B4"
}
]
}
================================================
FILE: AKImageCropperView.xcodeproj/xcshareddata/xcschemes/AKImageCropperView.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0820"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "011028A61DFFDD24002AA94E"
BuildableName = "AKImageCropperView.framework"
BlueprintName = "AKImageCropperView"
ReferencedContainer = "container:AKImageCropperView.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "011028A61DFFDD24002AA94E"
BuildableName = "AKImageCropperView.framework"
BlueprintName = "AKImageCropperView"
ReferencedContainer = "container:AKImageCropperView.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "011028A61DFFDD24002AA94E"
BuildableName = "AKImageCropperView.framework"
BlueprintName = "AKImageCropperView"
ReferencedContainer = "container:AKImageCropperView.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
================================================
FILE: AKImageCropperViewExample/AppDelegate.swift
================================================
//
// AppDelegate.swift
// AKImageCropperDemo
// GitHub: https://github.com/artemkrachulov/AKImageCropper
//
// Created by Krachulov Artem
// Copyright (c) 2015 Krachulov Artem. All rights reserved.
// Website: http://www.artemkrachulov.com/
//
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
================================================
FILE: AKImageCropperViewExample/Base.lproj/LaunchScreen.xib
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11542" systemVersion="16B2555" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11524"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="OND-g9-1i5">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics"/>
<point key="canvasLocation" x="589" y="579"/>
</view>
</objects>
</document>
================================================
FILE: AKImageCropperViewExample/Base.lproj/Main.storyboard
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="16D32" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="wQk-zT-bng">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Demo folder-->
<scene sceneID="R06-je-7kK">
<objects>
<tableViewController id="dCy-DU-1gs" customClass="ImagesTableViewController" customModule="AKImageCropperViewExample" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="80" sectionHeaderHeight="10" sectionFooterHeight="10" id="8Qw-gW-rSq">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.93725490196078431" green="0.93725490196078431" blue="0.95686274509803926" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<prototypes>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="image" textLabel="PAT-oO-2hL" detailTextLabel="mnx-Mm-eqW" style="IBUITableViewCellStyleSubtitle" id="uS4-ET-v3L">
<rect key="frame" x="0.0" y="56" width="375" height="80"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="uS4-ET-v3L" id="pRa-pE-0iP">
<rect key="frame" x="0.0" y="0.0" width="342" height="79"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="PAT-oO-2hL">
<rect key="frame" x="15" y="20" width="34" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Detail" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="mnx-Mm-eqW">
<rect key="frame" x="15" y="41" width="40" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
<connections>
<segue destination="Rdn-7Y-KLl" kind="show" id="awS-AO-qOC"/>
</connections>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="dCy-DU-1gs" id="AGP-R5-LfS"/>
<outlet property="delegate" destination="dCy-DU-1gs" id="Nx1-yp-dHE"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="Demo folder" id="7Is-JM-HRz"/>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="7Ef-Cy-a8G" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1220" y="618"/>
</scene>
<!--Crop View-->
<scene sceneID="knG-at-evs">
<objects>
<viewController storyboardIdentifier="cropperViewController" automaticallyAdjustsScrollViewInsets="NO" id="Rdn-7Y-KLl" customClass="CropperViewController" customModule="AKImageCropperViewExample" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Qt6-f6-NMs"/>
<viewControllerLayoutGuide type="bottom" id="aDG-WU-S8J"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="yhB-g2-lKN">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NNR-8r-Iig" customClass="AKImageCropperView" customModule="AKImageCropperView">
<rect key="frame" x="0.0" y="20" width="375" height="607"/>
<color key="backgroundColor" white="1" alpha="0.10000000000000001" colorSpace="calibratedWhite"/>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="yeD-f2-GC0">
<rect key="frame" x="0.0" y="577" width="375" height="50"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="OcE-Cr-oaV">
<rect key="frame" x="8" y="14" width="22" height="22"/>
<constraints>
<constraint firstAttribute="height" constant="22" id="0kW-xk-M2d"/>
<constraint firstAttribute="width" constant="22" id="nt9-AI-Qkr"/>
</constraints>
<color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<state key="normal" image="rotate"/>
<connections>
<action selector="rotateAction:" destination="Rdn-7Y-KLl" eventType="touchUpInside" id="uFB-UV-6jK"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aDm-Fl-m9C">
<rect key="frame" x="164.5" y="10" width="46" height="30"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<color key="tintColor" red="1" green="0.40000000600000002" blue="0.40000000600000002" alpha="1" colorSpace="calibratedRGB"/>
<state key="normal" title="RESET"/>
<connections>
<action selector="resetAction:" destination="Rdn-7Y-KLl" eventType="touchUpInside" id="iyC-RF-aoB"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="1uM-rd-DUd"/>
<constraint firstItem="OcE-Cr-oaV" firstAttribute="centerY" secondItem="yeD-f2-GC0" secondAttribute="centerY" id="227-80-ud3"/>
<constraint firstItem="OcE-Cr-oaV" firstAttribute="leading" secondItem="yeD-f2-GC0" secondAttribute="leading" constant="8" id="Yxu-MS-NnN"/>
<constraint firstItem="aDm-Fl-m9C" firstAttribute="centerX" secondItem="yeD-f2-GC0" secondAttribute="centerX" id="lrf-sz-2JO"/>
<constraint firstItem="aDm-Fl-m9C" firstAttribute="centerY" secondItem="yeD-f2-GC0" secondAttribute="centerY" id="saC-mg-X6V"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="wMD-3V-hUT">
<rect key="frame" x="0.0" y="627" width="375" height="40"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Xik-3z-UDc">
<rect key="frame" x="8" y="4" width="38" height="33"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="tintColor" red="0.0" green="0.50196081400000003" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<state key="normal" title="Back">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="backAction:" destination="Rdn-7Y-KLl" eventType="touchUpInside" id="Wkj-3h-8yo"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="AAk-pb-8ob">
<rect key="frame" x="329" y="4" width="38" height="33"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<state key="normal" title="Crop">
<color key="titleColor" red="1" green="1" blue="0.40000000600000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="cropImageAction:" destination="Rdn-7Y-KLl" eventType="touchUpInside" id="h04-VG-mda"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="vLm-vA-Gov">
<rect key="frame" x="176.5" y="9" width="22" height="22"/>
<color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<state key="normal" image="overlay"/>
<connections>
<action selector="showHideOverlayAction:" destination="Rdn-7Y-KLl" eventType="touchUpInside" id="tff-m1-bAb"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="qcw-Wp-RWG">
<rect key="frame" x="254" y="3" width="67" height="34"/>
<color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<state key="normal" title="Random"/>
<connections>
<action selector="randomImageAction:" destination="Rdn-7Y-KLl" eventType="touchUpInside" id="XVg-Cw-aoJ"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.10000000000000001" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="Xik-3z-UDc" firstAttribute="leading" secondItem="wMD-3V-hUT" secondAttribute="leading" constant="8" id="6rD-QV-lr8"/>
<constraint firstAttribute="trailing" secondItem="AAk-pb-8ob" secondAttribute="trailing" constant="8" id="9ig-1u-DSl"/>
<constraint firstItem="vLm-vA-Gov" firstAttribute="centerX" secondItem="wMD-3V-hUT" secondAttribute="centerX" id="MEl-vD-dqU"/>
<constraint firstItem="vLm-vA-Gov" firstAttribute="centerY" secondItem="wMD-3V-hUT" secondAttribute="centerY" id="cSm-Zo-lKa"/>
<constraint firstItem="qcw-Wp-RWG" firstAttribute="centerY" secondItem="AAk-pb-8ob" secondAttribute="centerY" id="cpp-Ph-nL5"/>
<constraint firstItem="AAk-pb-8ob" firstAttribute="leading" secondItem="qcw-Wp-RWG" secondAttribute="trailing" constant="8" id="k4D-Pz-J3X"/>
<constraint firstItem="AAk-pb-8ob" firstAttribute="centerY" secondItem="wMD-3V-hUT" secondAttribute="centerY" id="lp9-6a-Wrt"/>
<constraint firstItem="Xik-3z-UDc" firstAttribute="centerY" secondItem="wMD-3V-hUT" secondAttribute="centerY" id="qtX-gn-NoI"/>
<constraint firstAttribute="height" constant="40" id="rSL-W9-EEC"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="NNR-8r-Iig" secondAttribute="trailing" id="4m8-EW-rOa"/>
<constraint firstAttribute="trailing" secondItem="wMD-3V-hUT" secondAttribute="trailing" id="5mI-PE-xbj"/>
<constraint firstItem="yeD-f2-GC0" firstAttribute="leading" secondItem="yhB-g2-lKN" secondAttribute="leading" id="IqO-nt-y3G"/>
<constraint firstItem="wMD-3V-hUT" firstAttribute="leading" secondItem="yhB-g2-lKN" secondAttribute="leading" id="PZz-n2-ppW"/>
<constraint firstItem="wMD-3V-hUT" firstAttribute="top" secondItem="NNR-8r-Iig" secondAttribute="bottom" id="SAb-zF-ExQ"/>
<constraint firstItem="NNR-8r-Iig" firstAttribute="top" secondItem="Qt6-f6-NMs" secondAttribute="bottom" id="XIq-wp-GCF"/>
<constraint firstAttribute="trailing" secondItem="yeD-f2-GC0" secondAttribute="trailing" id="bQg-Xe-T56"/>
<constraint firstItem="aDG-WU-S8J" firstAttribute="top" secondItem="wMD-3V-hUT" secondAttribute="bottom" id="hZW-cD-Y7S"/>
<constraint firstAttribute="leading" secondItem="NNR-8r-Iig" secondAttribute="leading" id="jbS-cc-pHL"/>
<constraint firstItem="wMD-3V-hUT" firstAttribute="top" secondItem="yeD-f2-GC0" secondAttribute="bottom" id="zDw-ek-9hi"/>
</constraints>
</view>
<navigationItem key="navigationItem" title="Crop View" id="Dch-T8-hoF"/>
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics" statusBarStyle="lightContent"/>
<nil key="simulatedTopBarMetrics"/>
<connections>
<outlet property="cropViewStoryboard" destination="NNR-8r-Iig" id="3tD-2Q-b5A"/>
<outlet property="navigationView" destination="wMD-3V-hUT" id="Tmh-Sx-tyS"/>
<outlet property="overlayActionView" destination="yeD-f2-GC0" id="ONk-aH-Jyg"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="vfa-Bq-aJk" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2038" y="619"/>
</scene>
<!--Cropped Image-->
<scene sceneID="uND-rV-bwv">
<objects>
<viewController storyboardIdentifier="ImageViewController" id="RKe-5g-3sC" customClass="ImageViewController" customModule="AKImageCropperViewExample" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="3wc-Wp-Jyx"/>
<viewControllerLayoutGuide type="bottom" id="f21-mp-caw"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="s9k-zV-KY6">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="qUK-T2-uUb">
<rect key="frame" x="0.0" y="20" width="375" height="607"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</imageView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="VWe-jo-ncG">
<rect key="frame" x="0.0" y="627" width="375" height="40"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="nW8-ls-VF7">
<rect key="frame" x="8" y="4" width="38" height="33"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="tintColor" red="0.0" green="0.50196081400000003" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<state key="normal" title="Back">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="backAction:" destination="RKe-5g-3sC" eventType="touchUpInside" id="jKH-Ia-TmZ"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="GZk-pK-z6H">
<rect key="frame" x="277" y="4" width="90" height="33"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<state key="normal" title="IMAGE LIST">
<color key="titleColor" red="1" green="0.40000000600000002" blue="0.40000000600000002" alpha="1" colorSpace="calibratedRGB"/>
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="showListAction:" destination="RKe-5g-3sC" eventType="touchUpInside" id="UiD-hZ-odJ"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.10000000000000001" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="GZk-pK-z6H" firstAttribute="centerY" secondItem="VWe-jo-ncG" secondAttribute="centerY" id="5IT-pI-LBa"/>
<constraint firstAttribute="trailing" secondItem="GZk-pK-z6H" secondAttribute="trailing" constant="8" id="Gtt-0A-EXc"/>
<constraint firstItem="nW8-ls-VF7" firstAttribute="centerY" secondItem="VWe-jo-ncG" secondAttribute="centerY" id="aWr-jl-pAG"/>
<constraint firstItem="nW8-ls-VF7" firstAttribute="leading" secondItem="VWe-jo-ncG" secondAttribute="leading" constant="8" id="azT-mA-Kh1"/>
<constraint firstAttribute="height" constant="40" id="xdG-80-VrU"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="VWe-jo-ncG" secondAttribute="trailing" id="66d-L6-YFi"/>
<constraint firstItem="VWe-jo-ncG" firstAttribute="leading" secondItem="s9k-zV-KY6" secondAttribute="leading" id="If3-9n-nK3"/>
<constraint firstAttribute="trailing" secondItem="qUK-T2-uUb" secondAttribute="trailing" id="Umf-7n-wRw"/>
<constraint firstItem="f21-mp-caw" firstAttribute="top" secondItem="VWe-jo-ncG" secondAttribute="bottom" id="mLj-uD-q29"/>
<constraint firstItem="qUK-T2-uUb" firstAttribute="leading" secondItem="s9k-zV-KY6" secondAttribute="leading" id="nY3-UT-vp5"/>
<constraint firstItem="qUK-T2-uUb" firstAttribute="top" secondItem="3wc-Wp-Jyx" secondAttribute="bottom" id="x7h-gf-TAm"/>
<constraint firstItem="VWe-jo-ncG" firstAttribute="top" secondItem="qUK-T2-uUb" secondAttribute="bottom" id="xCs-wi-5cg"/>
</constraints>
</view>
<navigationItem key="navigationItem" title="Cropped Image" id="vfd-Eq-99c"/>
<connections>
<outlet property="imageView" destination="qUK-T2-uUb" id="XRf-oF-d9n"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="ZVo-Oy-Yc0" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2887" y="618"/>
</scene>
<!--AKLocationManager-->
<scene sceneID="mR8-El-N3M">
<objects>
<viewController id="o8A-Qi-PEH" userLabel="AKLocationManager" customClass="HomeViewController" customModule="AKImageCropperViewExample" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="ICs-CR-drR"/>
<viewControllerLayoutGuide type="bottom" id="QFS-sv-RYa"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="D8c-Xa-Pec">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Zeo-Cv-xbz">
<rect key="frame" x="171.5" y="249" width="32" height="1"/>
<color key="backgroundColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="1" id="70Z-Eo-lef"/>
<constraint firstAttribute="width" constant="32" id="yKr-PU-ZjI"/>
</constraints>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="uBf-yH-pWL">
<rect key="frame" x="99" y="282" width="177" height="30"/>
<state key="normal" title="Select Image from Gallery">
<color key="titleColor" red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="galleryAction" destination="o8A-Qi-PEH" eventType="touchUpInside" id="W7k-Kv-Dkl"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Responsive image cropper" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Cm5-TZ-NK7">
<rect key="frame" x="16" y="96" width="343" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.33333333333333331" green="0.33333333333333331" blue="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Demos" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="RnQ-Uu-Fqe">
<rect key="frame" x="16" y="214" width="343" height="27"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle2"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="WB8-2V-Rsz">
<rect key="frame" x="79" y="344" width="217" height="30"/>
<state key="normal" title="Select Image from Demo Folder"/>
<connections>
<segue destination="dCy-DU-1gs" kind="show" id="8va-bh-mBB"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="trailingMargin" secondItem="Cm5-TZ-NK7" secondAttribute="trailing" id="16w-U8-opa"/>
<constraint firstItem="Cm5-TZ-NK7" firstAttribute="top" secondItem="ICs-CR-drR" secondAttribute="bottom" constant="32" id="8a3-A9-iMG"/>
<constraint firstItem="Zeo-Cv-xbz" firstAttribute="centerX" secondItem="D8c-Xa-Pec" secondAttribute="centerX" id="B0r-iN-c8g"/>
<constraint firstItem="RnQ-Uu-Fqe" firstAttribute="top" secondItem="ICs-CR-drR" secondAttribute="bottom" constant="150" id="DLc-Rl-yrZ"/>
<constraint firstItem="Cm5-TZ-NK7" firstAttribute="leading" secondItem="D8c-Xa-Pec" secondAttribute="leadingMargin" id="Ewe-Hk-hDu"/>
<constraint firstItem="WB8-2V-Rsz" firstAttribute="centerX" secondItem="D8c-Xa-Pec" secondAttribute="centerX" id="GXe-fv-L7v"/>
<constraint firstItem="WB8-2V-Rsz" firstAttribute="top" secondItem="uBf-yH-pWL" secondAttribute="bottom" constant="32" id="Ihd-Ot-wYi"/>
<constraint firstItem="uBf-yH-pWL" firstAttribute="centerX" secondItem="D8c-Xa-Pec" secondAttribute="centerX" id="Pgv-SQ-HUN"/>
<constraint firstItem="uBf-yH-pWL" firstAttribute="top" secondItem="Zeo-Cv-xbz" secondAttribute="bottom" constant="32" id="XeJ-EB-beG"/>
<constraint firstItem="Zeo-Cv-xbz" firstAttribute="top" secondItem="RnQ-Uu-Fqe" secondAttribute="bottom" constant="8" id="hmX-w8-wSI"/>
<constraint firstItem="RnQ-Uu-Fqe" firstAttribute="leading" secondItem="D8c-Xa-Pec" secondAttribute="leadingMargin" id="rTj-hc-eRg"/>
<constraint firstAttribute="trailingMargin" secondItem="RnQ-Uu-Fqe" secondAttribute="trailing" id="yWP-7s-GtS"/>
</constraints>
</view>
<navigationItem key="navigationItem" title="AKImageCropperView" id="Qt3-o7-Xus"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Qjw-UL-ejc" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="414" y="619"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="khx-1F-DYa">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="wQk-zT-bng" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="MxY-Fx-TEB">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="o8A-Qi-PEH" kind="relationship" relationship="rootViewController" id="52B-Rg-ppu"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="JuF-T7-bWT" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-377" y="618"/>
</scene>
</scenes>
<resources>
<image name="overlay" width="22" height="22"/>
<image name="rotate" width="22" height="22"/>
</resources>
</document>
================================================
FILE: AKImageCropperViewExample/Constants.swift
================================================
//
// Constants.swift
// Demo
//
// Created by Artem Krachulov on 11/18/16.
// Copyright © 2016 Artem Krachulov. All rights reserved.
//
import Foundation
struct Constants {
static let images = [
["Attractive-girl", "Autumn-background", "Colorful-pillows"],
["Cupcakes", "Funnel-cake-stand", "Image-of-earth"]
]
}
================================================
FILE: AKImageCropperViewExample/CropperViewController.swift
================================================
//
// CropperViewController.swift
//
// Created by Artem Krachulov.
// Copyright (c) 2016 Artem Krachulov. All rights reserved.
// Website: http://www.artemkrachulov.com/
//
import UIKit
import AKImageCropperView
final class CropperViewController: UIViewController {
// MARK: - Properties
var image: UIImage!
// MARK: - Connections:
// MARK: -- Outlets
private var cropView: AKImageCropperView {
return cropViewProgrammatically ?? cropViewStoryboard
}
@IBOutlet weak var cropViewStoryboard: AKImageCropperView!
private var cropViewProgrammatically: AKImageCropperView!
@IBOutlet weak var overlayActionView: UIView!
@IBOutlet weak var navigationView: UIView!
// MARK: -- Actions
@IBAction func backAction(_ sender: AnyObject) {
guard !cropView.isEdited else {
let alertController = UIAlertController(title: "Warning!", message:
"All changes will be lost.", preferredStyle: UIAlertControllerStyle.alert)
alertController.addAction(UIAlertAction(title: "Yes", style: UIAlertActionStyle.cancel, handler: { _ in
_ = self.navigationController?.popViewController(animated: true)
}))
alertController.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default, handler: nil))
present(alertController, animated: true, completion: nil)
return
}
_ = navigationController?.popViewController(animated: true)
}
@IBAction func cropRandomAction(_ sender: AnyObject) {
// cropView.setCropRectAnin(CGRect(x: 50, y: 200, width: 100, height: 100))
/*
let randomWidth = max(UInt32(cropView.configuration.cropRect.minimumSize.width), arc4random_uniform(UInt32(cropView.scrollView.frame.size.width)))
let randomHeight = max(UInt32(cropView.configuration.cropRect.minimumSize.height), arc4random_uniform(UInt32(cropView.scrollView.frame.size.height)))
let offsetX = CGFloat(arc4random_uniform(UInt32(cropView.scrollView.frame.size.width) - randomWidth))
let offsetY = CGFloat(arc4random_uniform(UInt32(cropView.scrollView.frame.size.height) - randomHeight))
cropView.cropRect(CGRectMake(offsetX, offsetY, CGFloat(randomWidth), CGFloat(randomHeight)))*/
}
@IBAction func randomImageAction(_ sender: AnyObject) {
let images = Constants.images.flatMap { $0 }
cropView.image = UIImage(named: images[Int(arc4random_uniform(UInt32(images.count)))])
angle = 0.0
}
@IBAction func cropImageAction(_ sender: AnyObject) {
guard let image = cropView.croppedImage else {
return
}
let imageViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ImageViewController") as! ImageViewController
imageViewController.image = image
navigationController?.pushViewController(imageViewController, animated: true)
}
@IBAction func showHideOverlayAction(_ sender: AnyObject) {
if cropView.isoverlayViewActive {
cropView.hideOverlayView(animationDuration: 0.3)
UIView.animate(withDuration: 0.3, delay: 0, options: UIViewAnimationOptions.curveLinear, animations: {
self.overlayActionView.alpha = 0
}, completion: nil)
} else {
cropView.showOverlayView(animationDuration: 0.3)
UIView.animate(withDuration: 0.3, delay: 0.3, options: UIViewAnimationOptions.curveLinear, animations: {
self.overlayActionView.alpha = 1
}, completion: nil)
}
}
var angle: Double = 0.0
@IBAction func rotateAction(_ sender: AnyObject) {
angle += M_PI_2
cropView.rotate(angle, withDuration: 0.3, completion: { _ in
if self.angle == 2 * M_PI {
self.angle = 0.0
}
})
}
@IBAction func resetAction(_ sender: AnyObject) {
cropView.reset(animationDuration: 0.3)
angle = 0.0
}
// MARK: - Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.isNavigationBarHidden = true
// Programmatically initialization
/*
cropViewProgrammatically = AKImageCropperView()
*/
// iPhone 4.7"
/*
cropViewProgrammatically = AKImageCropperView(frame: CGRect(x: 0, y: 20.0, width: 375.0, height: 607.0))
view.addSubview(cropViewProgrammatically)
*/
// with constraints
/*
cropViewProgrammatically = AKImageCropperView()
cropViewProgrammatically.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(cropViewProgrammatically)
if #available(iOS 9.0, *) {
cropViewProgrammatically.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
cropViewProgrammatically.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
topLayoutGuide.bottomAnchor.constraint(equalTo: cropViewProgrammatically.topAnchor).isActive = true
cropViewProgrammatically.bottomAnchor.constraint(equalTo: navigationView.topAnchor).isActive = true
} else {
for attribute: NSLayoutAttribute in [.top, .left, .bottom, .right] {
var toItem: Any?
var toAttribute: NSLayoutAttribute!
if attribute == .top {
toItem = topLayoutGuide
toAttribute = .bottom
} else if attribute == .bottom {
toItem = navigationView
toAttribute = .top
} else {
toItem = view
toAttribute = attribute
}
view.addConstraint(
NSLayoutConstraint(
item: cropViewProgrammatically,
attribute: attribute,
relatedBy: NSLayoutRelation.equal,
toItem: toItem,
attribute: toAttribute,
multiplier: 1.0, constant: 0))
}
}
*/
// Inset for overlay action view
/*
cropView.overlayView?.configuraiton.cropRectInsets.bottom = 50
*/
// Custom overlay view configuration
/*
var customConfiguraiton = AKImageCropperCropViewConfiguration()
customConfiguraiton.cropRectInsets.bottom = 50
cropView.overlayView = CustomImageCropperOverlayView(configuraiton: customConfiguraiton)
*/
cropView.delegate = self
cropView.image = image
}
}
// MARK: - AKImageCropperViewDelegate
extension CropperViewController: AKImageCropperViewDelegate {
func imageCropperViewDidChangeCropRect(view: AKImageCropperView, cropRect rect: CGRect) {
// print("New crop rectangle: \(rect)")
}
}
================================================
FILE: AKImageCropperViewExample/CustomImageCropperOverlayView.swift
================================================
//
// CustomImageCropperOverlayView.swift
// Demo
//
// Created by Artem Krachulov on 11/28/16.
// Copyright © 2016 Artem Krachulov. All rights reserved.
//
import Foundation
import AKImageCropperView
final class CustomImageCropperOverlayView: AKImageCropperOverlayView {
private func drawCycleInCornerView(_ view: UIView, inTouchView touchView: UIView, forState state: AKImageCropperCropViewTouchState) {
var color: UIColor
var width: CGFloat
if state == .normal {
color = configuraiton.corner.normalLineColor
width = configuraiton.corner.normalLineWidth
} else {
color = configuraiton.corner.highlightedLineColor
width = configuraiton.corner.highlightedLineWidth
}
let layer: CAShapeLayer = view.layer.sublayers!.first as! CAShapeLayer
let circlePath = UIBezierPath(
arcCenter: CGPoint(x: touchView.bounds.midX, y: touchView.bounds.midY),
radius: width,
startAngle: 0.0,
endAngle: CGFloat(M_PI * 2),
clockwise: true)
layer.path = circlePath.cgPath
layer.fillColor = color.cgColor
layer.strokeColor = color.cgColor
}
override func layoutTopLeftCornerView(_ view: UIView, inTouchView touchView: UIView, forState state: AKImageCropperCropViewTouchState) {
drawCycleInCornerView(view, inTouchView: touchView, forState: state)
}
override func layoutTopRightCornerView(_ view: UIView, inTouchView touchView: UIView, forState state: AKImageCropperCropViewTouchState) {
drawCycleInCornerView(view, inTouchView: touchView, forState: state)
}
override func layoutBottomLeftCornerView(_ view: UIView, inTouchView touchView: UIView, forState state: AKImageCropperCropViewTouchState) {
drawCycleInCornerView(view, inTouchView: touchView, forState: state)
}
override func layoutBottomRightCornerView(_ view: UIView, inTouchView touchView: UIView, forState state: AKImageCropperCropViewTouchState) {
drawCycleInCornerView(view, inTouchView: touchView, forState: state)
}
}
================================================
FILE: AKImageCropperViewExample/HomeViewController.swift
================================================
//
// HomeViewController.swift
//
// Created by Artem Krachulov.
// Copyright (c) 2016 Artem Krachulov. All rights reserved.
// Website: http://www.artemkrachulov.com/
//
import UIKit
final class HomeViewController: UIViewController {
// MARK: - Connections:
// MARK: -- Actions
@IBAction func galleryAction() {
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.savedPhotosAlbum) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = UIImagePickerControllerSourceType.savedPhotosAlbum
imagePicker.allowsEditing = false
present(imagePicker, animated: true, completion: nil)
}
}
// MARK: - Life Cycle
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.isNavigationBarHidden = false
}
}
// MARK: - UIImagePickerControllerDelegate
extension HomeViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
let cropperViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "cropperViewController") as! CropperViewController
cropperViewController.image = pickedImage
picker.pushViewController(cropperViewController, animated: true)
}
}
}
================================================
FILE: AKImageCropperViewExample/ImageViewController.swift
================================================
//
// ImageViewController.swift
//
// Created by Artem Krachulov.
// Copyright (c) 2016 Artem Krachulov. All rights reserved.
// Website: http://www.artemkrachulov.com/
//
import UIKit
final class ImageViewController: UIViewController {
// MARK: - Properties
var image: UIImage!
// MARK: - Connections:
// MARK: -- Outlets
@IBOutlet weak var imageView: UIImageView!
// MARK: -- Actions
@IBAction func backAction(_ sender: UIButton) {
_ = navigationController?.popViewController(animated: true)
}
@IBAction func showListAction(_ sender: UIButton) {
if presentingViewController != nil {
_ = navigationController?.popToRootViewController(animated: true)
} else {
_ = navigationController?.popToViewController(navigationController!.viewControllers[1], animated: true)
}
}
// MARK: - Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
imageView.image = image
}
}
================================================
FILE: AKImageCropperViewExample/Images.xcassets/AppIcon.appiconset/Contents.json
================================================
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: AKImageCropperViewExample/Images.xcassets/Attractive-girl.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "Attractive-girl-on-a-yacht-at-summer-day.jpg"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: AKImageCropperViewExample/Images.xcassets/Autumn-background.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "Autumn-background.jpg"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: AKImageCropperViewExample/Images.xcassets/Colorful-pillows.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "Colorful-pillows-on-a-sofa-with-white-brick-wall-in-background.jpg"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: AKImageCropperViewExample/Images.xcassets/Contents.json
================================================
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: AKImageCropperViewExample/Images.xcassets/Cupcakes.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "Cupcakes.jpg"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: AKImageCropperViewExample/Images.xcassets/Funnel-cake-stand.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "Funnel-cake-stand-at-a-fair.jpg"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: AKImageCropperViewExample/Images.xcassets/Icons/Contents.json
================================================
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: AKImageCropperViewExample/Images.xcassets/Icons/overlay.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "overlay.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}
================================================
FILE: AKImageCropperViewExample/Images.xcassets/Icons/random.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "random.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}
================================================
FILE: AKImageCropperViewExample/Images.xcassets/Icons/rotate.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "rotate.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}
================================================
FILE: AKImageCropperViewExample/Images.xcassets/Image-of-earth.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "Image-of-earth-planet.-Elements-of-this-image-are-furnished-by-NASA.jpg"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: AKImageCropperViewExample/ImagesTableViewController.swift
================================================
//
// ImagesTableViewController.swift
//
// Created by Artem Krachulov.
// Copyright (c) 2016 Artem Krachulov. All rights reserved.
// Website: http://www.artemkrachulov.com/
//
import UIKit
final class ImagesTableViewController: UITableViewController {
// MARK: - Life Cycle
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.isNavigationBarHidden = false
}
// MARK: - Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let selectedPath = tableView.indexPathForSelectedRow!
(segue.destination as! CropperViewController).image = UIImage(named: Constants.images[selectedPath.section][selectedPath.row])
}
}
// MARK: - Table view data source
extension ImagesTableViewController {
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return section == 0 ? "Large" : "Small"
}
override func numberOfSections(in tableView: UITableView) -> Int {
return Constants.images.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Constants.images[section].count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "image", for: indexPath)
let name = Constants.images[indexPath.section][indexPath.row]
let image = UIImage(named: name)
// Configure the cell...
cell.textLabel!.text = name.components(separatedBy: "-").joined(separator: " ")
cell.detailTextLabel?.text = String(format: "Size %0.1f x %0.1f", image?.size.width as CGFloat!, image?.size.height as CGFloat!)
return cell
}
}
================================================
FILE: AKImageCropperViewExample/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSCameraUsageDescription</key>
<string>$(PRODUCT_NAME) camera use</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>$(PRODUCT_NAME) photo use</string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
================================================
FILE: LICENSE
================================================
Copyright (c) 2015-2016 Artem Krachulov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# AKImageCropper
> Responsive image cropper
[![Carthage compatible][carthage-bage]][carthage-bage]
[![CocoaPods Compatible][pods-bage]][pods-bage]
[![Platform][platform-bage]][platform-bage]
[![Swift Version][swift-bage]][swift-url]
[![Build Status][travis-bage]][travis-url]
[![License][license-bage]][license-url]
[pods-bage]: https://img.shields.io/badge/COCOAPODS-compatible-fb0006.svg
[pods-url]: https://cocoapods.org/
[carthage-bage]: https://img.shields.io/badge/Carthage-compatible-brightgreen.svg
[carthage-url]: https://github.com/Carthage/Carthage
[platform-bage]: https://img.shields.io/cocoapods/p/LFAlertController.svg
[platform-url]: http://cocoapods.org/pods/LFAlertController
[swift-bage]:https://img.shields.io/badge/swift-3.0-orange.svg
[swift-url]: https://swift.org/
[license-bage]: https://img.shields.io/badge/License-MIT-blue.svg
[license-url]: LICENSE
[travis-bage]: https://img.shields.io/travis/dbader/node-datadog-metrics/master.svg
[travis-url]: https://travis-ci.org/dbader/node-datadog-metrics
Image cropping plugin which supported different devices orientation. Easy to set up and configure. Has many settings for flexible integration into your project. Behavior is similar to native iOS photo cropper.

## Features
- [x] Overlay view & Crop rectangle full customization
- [x] Flexible settings
- [x] Image rotation
- [x] Infinite "Zoom To Fit"
- [x] Full image resolution
- [x] Ability to draw custom crop rectangle
## Requirements
- iOS 8.0+
- Xcode 7.3
## Installation
### CocoaPods
[CocoaPods][] is a dependency manager for Cocoa projects. To install **AKImageCropperView** with CocoaPods:
1. Make sure CocoaPods is [installed][CocoaPods Installation].
2. Update your Podfile to include the following:
``` ruby
use_frameworks!
pod 'AKImageCropperView'
```
3. Run `pod install`.
[CocoaPods]: https://cocoapods.org
[CocoaPods Installation]: https://guides.cocoapods.org/using/getting-started.html#getting-started
4. In your code import **AKImageCropperView** like so: `import AKImageCropperView`
### Carthage
[Carthage][] is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
To install **AKImageCropperView** with Carthage:
1. Install Carthage via [Homebrew][]
```bash
$ brew update
$ brew install carthage
```
2. Add `github "artemkrachulov/AKImageCropperView"` to your Cartfile.
3. Run `carthage update`.
4. Drag `AKMaskField.framework ` from the `Carthage/Build/iOS/` directory to the `Linked Frameworks and Libraries` section of your Xcode project’s `General` settings.
5. Add `$(SRCROOT)/Carthage/Build/iOS/AKImageCropperView.framework ` to `Input Files` of Run Script Phase for Carthage.
[Carthage]: https://github.com/Carthage/Carthage
[Homebrew]: http://brew.sh
### Manual
If you prefer not to use either of the aforementioned dependency managers, you can integrate **AKImageCropperView** into your project manually.
1. Download and drop **AKImageCropperView** folder in your project.
2. Done!
## Usage example
### Storyboard
```swift
@IBOutlet weak var cropView: AKImageCropperView!
override func viewDidLoad() {
super.viewDidLoad()
cropView.image = UIImage(named: "yourImage")
}
```
### Programmatically
```swift
var cropView: AKImageCropperView!
override func viewDidLoad() {
super.viewDidLoad()
cropView = AKImageCropperView(frame: CGRect(x: 0, y: 20.0, width: 375.0, height: 607.0))
cropView.image = UIImage(named: "yourImage")
view.addSubview(cropViewProgrammatically)
}
```
> Full examples with constraint, delegates and Overlay view configuration check in demo project.
>
> **NOTE**: If after cropper view initialization your image has top inset. Go to storyboard with your scene and in the attributes inspector, uncheck 'Adjust Scrollview Insets'.
## Initializing an Image Cropper View
```swift
func init(image: UIImage?)
```
Returns an image cropper view initialized with the specified image.
**Parameters**
- `image` : The initial image to display in the image cropper view.
## Accessing the Displayed Images
```swift
var image: UIImage? { get set }
```
The image displayed in the image cropper view.
Default value of this property is `nil`.
```swift
var croppedImage: UIImage? { get }
```
Cropperd image in the specified crop rectangle.
## Instance Properties
```swift
var isEdited: UIImage? { get }
```
Returns the image edited state flag.
## Managing the Delegate
```swift
weak var delegate: AKImageCropperViewDelegate? { get set }
```
The delegate of the cropper view object.
### Delegate methods
```swift
optional func imageCropperViewDidChangeCropRect(view: AKImageCropperView, cropRect rect: CGRect)
```
Tells the delegate that crop frame was changed.
Parameters:
- **`view`**: The image cropper view.
- **`rect`**: New crop rectangle origin and size.
## Customizing an Overlay View
```swift
var overlayView: AKImageCropperOverlayView? { get set }
```
Overlay view represented as AKImageCropperOverlayView open class.
Base configuration and behavior can
gitextract_x4b46xu8/ ├── .gitignore ├── AKImageCropperView/ │ ├── AKImageCropperOverlayView.swift │ ├── AKImageCropperOverlayViewConfiguration.swift │ ├── AKImageCropperOverlayViewConfigurationCorner.swift │ ├── AKImageCropperOverlayViewConfigurationEdge.swift │ ├── AKImageCropperOverlayViewConfigurationGrid.swift │ ├── AKImageCropperOverlayViewConfigurationOverlay.swift │ ├── AKImageCropperOverlayViewTouchState.swift │ ├── AKImageCropperScrollView.swift │ ├── AKImageCropperView.h │ ├── AKImageCropperView.swift │ ├── IC_CGFloatExtension.swift │ ├── IC_CGPointExtension.swift │ ├── IC_CGSizeExtensions.swift │ ├── IC_UIImageExtensions.swift │ ├── Info.plist │ └── PrimaryFilledButton.swift ├── AKImageCropperView.podspec ├── AKImageCropperView.xcodeproj/ │ ├── project.pbxproj │ ├── project.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ ├── AKImageCropperDemo.xcscmblueprint │ │ └── Demo.xcscmblueprint │ └── xcshareddata/ │ └── xcschemes/ │ └── AKImageCropperView.xcscheme ├── AKImageCropperViewExample/ │ ├── AppDelegate.swift │ ├── Base.lproj/ │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── Constants.swift │ ├── CropperViewController.swift │ ├── CustomImageCropperOverlayView.swift │ ├── HomeViewController.swift │ ├── ImageViewController.swift │ ├── Images.xcassets/ │ │ ├── AppIcon.appiconset/ │ │ │ └── Contents.json │ │ ├── Attractive-girl.imageset/ │ │ │ └── Contents.json │ │ ├── Autumn-background.imageset/ │ │ │ └── Contents.json │ │ ├── Colorful-pillows.imageset/ │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── Cupcakes.imageset/ │ │ │ └── Contents.json │ │ ├── Funnel-cake-stand.imageset/ │ │ │ └── Contents.json │ │ ├── Icons/ │ │ │ ├── Contents.json │ │ │ ├── overlay.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── random.imageset/ │ │ │ │ └── Contents.json │ │ │ └── rotate.imageset/ │ │ │ └── Contents.json │ │ └── Image-of-earth.imageset/ │ │ └── Contents.json │ ├── ImagesTableViewController.swift │ └── Info.plist ├── LICENSE └── README.md
Condensed preview — 47 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (216K chars).
[
{
"path": ".gitignore",
"chars": 249,
"preview": "# Xcode\n.DS_Store\n*/build/*\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspecti"
},
{
"path": "AKImageCropperView/AKImageCropperOverlayView.swift",
"chars": 37103,
"preview": "//\n// AKImageCropperOverlayView.swift\n//\n// Created by Artem Krachulov.\n// Copyright (c) 2016 Artem Krachulov. All ri"
},
{
"path": "AKImageCropperView/AKImageCropperOverlayViewConfiguration.swift",
"chars": 3182,
"preview": "//\n// AKImageCropperCropViewConfiguration.swift\n//\n// Created by Artem Krachulov.\n// Copyright (c) 2016 Artem Krachul"
},
{
"path": "AKImageCropperView/AKImageCropperOverlayViewConfigurationCorner.swift",
"chars": 2080,
"preview": "//\n// AKImageCropperCropViewConfigurationCorner.swift\n//\n// Created by Artem Krachulov.\n// Copyright (c) 2016 Artem K"
},
{
"path": "AKImageCropperView/AKImageCropperOverlayViewConfigurationEdge.swift",
"chars": 1838,
"preview": "//\n// AKImageCropperCropViewConfigurationEdge.swift\n//\n// Created by Artem Krachulov.\n// Copyright (c) 2016 Artem Kra"
},
{
"path": "AKImageCropperView/AKImageCropperOverlayViewConfigurationGrid.swift",
"chars": 2041,
"preview": "//\n// AKImageCropperCropViewConfigurationGrid.swift\n//\n// Created by Artem Krachulov.\n// Copyright (c) 2016 Artem Kra"
},
{
"path": "AKImageCropperView/AKImageCropperOverlayViewConfigurationOverlay.swift",
"chars": 1944,
"preview": "//\n// AKImageCropperCropViewConfigurationOverlay.swift\n//\n// Created by Artem Krachulov.\n// Copyright (c) 2016 Artem "
},
{
"path": "AKImageCropperView/AKImageCropperOverlayViewTouchState.swift",
"chars": 1405,
"preview": "//\n// AKImageCropperCropViewTouchState.swift\n//\n// Created by Artem Krachulov.\n// Copyright (c) 2016 Artem Krachulov."
},
{
"path": "AKImageCropperView/AKImageCropperScrollView.swift",
"chars": 2222,
"preview": "//\n// AKImageCropperScrollView.swift\n// AKImageCropperView\n//\n// Created by Artem Krachulov on 12/17/16.\n// Copyrigh"
},
{
"path": "AKImageCropperView/AKImageCropperView.h",
"chars": 567,
"preview": "//\n// AKImageCropperView.h\n// AKImageCropperView\n//\n// Created by Artem Krachulov on 12/13/16.\n// Copyright © 2016 A"
},
{
"path": "AKImageCropperView/AKImageCropperView.swift",
"chars": 28443,
"preview": "//\n// AKImageCropperView.swift\n//\n// Created by Artem Krachulov.\n// Copyright (c) 2016 Artem Krachulov. All rights re"
},
{
"path": "AKImageCropperView/IC_CGFloatExtension.swift",
"chars": 596,
"preview": "//\n// ic_CGFloatExtension.swift\n// AKImageCropperView\n//\n// Created by Artem Krachulov.\n// Copyright (c) 2016 Artem "
},
{
"path": "AKImageCropperView/IC_CGPointExtension.swift",
"chars": 420,
"preview": "//\n// IC_CGPointExtension.swift\n// AKImageCropperView\n//\n// Created by Artem Krachulov.\n// Copyright (c) 2016 Artem "
},
{
"path": "AKImageCropperView/IC_CGSizeExtensions.swift",
"chars": 1333,
"preview": "//\n// IC_CGSizeExtensions.swift\n// AKImageCropperView\n//\n// Created by Artem Krachulov.\n// Copyright (c) 2016 Artem "
},
{
"path": "AKImageCropperView/IC_UIImageExtensions.swift",
"chars": 2320,
"preview": "//\n// IC_UIImageExtensions.swift\n// AKImageCropperView\n//\n// Created by Artem Krachulov.\n// Copyright (c) 2016 Artem"
},
{
"path": "AKImageCropperView/Info.plist",
"chars": 755,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "AKImageCropperView/PrimaryFilledButton.swift",
"chars": 163,
"preview": "//\n// PrimaryFilledButton.swift\n// Visitor\n//\n// Created by Artem Krachulov on 1/16/17.\n// Copyright © 2017 VZPass. "
},
{
"path": "AKImageCropperView.podspec",
"chars": 916,
"preview": "Pod::Spec.new do |s|\n\n s.name = \"AKImageCropperView\"\n s.version = \"2.0.0\"\n s.homepage = \"https://git"
},
{
"path": "AKImageCropperView.xcodeproj/project.pbxproj",
"chars": 29224,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "AKImageCropperView.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
"chars": 206,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"self:/Users/artemkra"
},
{
"path": "AKImageCropperView.xcodeproj/project.xcworkspace/xcshareddata/AKImageCropperDemo.xcscmblueprint",
"chars": 3317,
"preview": "{\n \"DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey\" : \"74FC6B16832EDE035AC047E84A01E2F0C9097FE9\",\n \"DVTS"
},
{
"path": "AKImageCropperView.xcodeproj/project.xcworkspace/xcshareddata/Demo.xcscmblueprint",
"chars": 3289,
"preview": "{\n \"DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey\" : \"74FC6B16832EDE035AC047E84A01E2F0C9097FE9\",\n \"DVTS"
},
{
"path": "AKImageCropperView.xcodeproj/xcshareddata/xcschemes/AKImageCropperView.xcscheme",
"chars": 2949,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"0820\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "AKImageCropperViewExample/AppDelegate.swift",
"chars": 2279,
"preview": "//\n// AppDelegate.swift\n// AKImageCropperDemo\n// GitHub: https://github.com/artemkrachulov/AKImageCropper\n//\n// Crea"
},
{
"path": "AKImageCropperViewExample/Base.lproj/LaunchScreen.xib",
"chars": 1269,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" version=\"3.0\" toolsVe"
},
{
"path": "AKImageCropperViewExample/Base.lproj/Main.storyboard",
"chars": 35143,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
},
{
"path": "AKImageCropperViewExample/Constants.swift",
"chars": 349,
"preview": "//\n// Constants.swift\n// Demo\n//\n// Created by Artem Krachulov on 11/18/16.\n// Copyright © 2016 Artem Krachulov. All"
},
{
"path": "AKImageCropperViewExample/CropperViewController.swift",
"chars": 7622,
"preview": "//\n// CropperViewController.swift\n//\n// Created by Artem Krachulov.\n// Copyright (c) 2016 Artem Krachulov. All rights"
},
{
"path": "AKImageCropperViewExample/CustomImageCropperOverlayView.swift",
"chars": 2184,
"preview": "//\n// CustomImageCropperOverlayView.swift\n// Demo\n//\n// Created by Artem Krachulov on 11/28/16.\n// Copyright © 2016 "
},
{
"path": "AKImageCropperViewExample/HomeViewController.swift",
"chars": 1717,
"preview": "//\n// HomeViewController.swift\n//\n// Created by Artem Krachulov.\n// Copyright (c) 2016 Artem Krachulov. All rights re"
},
{
"path": "AKImageCropperViewExample/ImageViewController.swift",
"chars": 1098,
"preview": "//\n// ImageViewController.swift\n//\n// Created by Artem Krachulov.\n// Copyright (c) 2016 Artem Krachulov. All rights r"
},
{
"path": "AKImageCropperViewExample/Images.xcassets/AppIcon.appiconset/Contents.json",
"chars": 1495,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"iphone\",\n \"size\" : \"20x20\",\n \"scale\" : \"2x\"\n },\n {\n \"idiom\""
},
{
"path": "AKImageCropperViewExample/Images.xcassets/Attractive-girl.imageset/Contents.json",
"chars": 337,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\",\n \"filename\" : \"Attractive-girl-on-a-yacht"
},
{
"path": "AKImageCropperViewExample/Images.xcassets/Autumn-background.imageset/Contents.json",
"chars": 314,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\",\n \"filename\" : \"Autumn-background.jpg\"\n "
},
{
"path": "AKImageCropperViewExample/Images.xcassets/Colorful-pillows.imageset/Contents.json",
"chars": 359,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\",\n \"filename\" : \"Colorful-pillows-on-a-sofa"
},
{
"path": "AKImageCropperViewExample/Images.xcassets/Contents.json",
"chars": 62,
"preview": "{\n \"info\" : {\n \"version\" : 1,\n \"author\" : \"xcode\"\n }\n}"
},
{
"path": "AKImageCropperViewExample/Images.xcassets/Cupcakes.imageset/Contents.json",
"chars": 305,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\",\n \"filename\" : \"Cupcakes.jpg\"\n },\n {"
},
{
"path": "AKImageCropperViewExample/Images.xcassets/Funnel-cake-stand.imageset/Contents.json",
"chars": 324,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\",\n \"filename\" : \"Funnel-cake-stand-at-a-fai"
},
{
"path": "AKImageCropperViewExample/Images.xcassets/Icons/Contents.json",
"chars": 62,
"preview": "{\n \"info\" : {\n \"version\" : 1,\n \"author\" : \"xcode\"\n }\n}"
},
{
"path": "AKImageCropperViewExample/Images.xcassets/Icons/overlay.imageset/Contents.json",
"chars": 225,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"overlay.pdf\"\n }\n ],\n \"info\" : {\n \"versio"
},
{
"path": "AKImageCropperViewExample/Images.xcassets/Icons/random.imageset/Contents.json",
"chars": 224,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"random.pdf\"\n }\n ],\n \"info\" : {\n \"version"
},
{
"path": "AKImageCropperViewExample/Images.xcassets/Icons/rotate.imageset/Contents.json",
"chars": 224,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"rotate.pdf\"\n }\n ],\n \"info\" : {\n \"version"
},
{
"path": "AKImageCropperViewExample/Images.xcassets/Image-of-earth.imageset/Contents.json",
"chars": 364,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\",\n \"filename\" : \"Image-of-earth-planet.-Ele"
},
{
"path": "AKImageCropperViewExample/ImagesTableViewController.swift",
"chars": 1983,
"preview": "//\n// ImagesTableViewController.swift\n//\n// Created by Artem Krachulov.\n// Copyright (c) 2016 Artem Krachulov. All ri"
},
{
"path": "AKImageCropperViewExample/Info.plist",
"chars": 1666,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "LICENSE",
"chars": 1063,
"preview": "Copyright (c) 2015-2016 Artem Krachulov\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof"
},
{
"path": "README.md",
"chars": 14714,
"preview": "# AKImageCropper\n\n> Responsive image cropper\n\n[![Carthage compatible][carthage-bage]][carthage-bage] \n[![CocoaPods Compa"
}
]
About this extraction
This page contains the full source code of the artemkrachulov/AKImageCropperView GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 47 files (197.2 KB), approximately 51.0k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.