Skip to content

Commit 91292e8

Browse files
author
Chris Hu
committed
DemoImagePreview:添加CropImageView
1 parent af09d1f commit 91292e8

File tree

5 files changed

+266
-3
lines changed

5 files changed

+266
-3
lines changed

DemoImagePreview/DemoImagePreview.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
A418FB581E2321D3005BE849 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A418FB571E2321D3005BE849 /* Assets.xcassets */; };
1414
A418FB5B1E2321D3005BE849 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A418FB591E2321D3005BE849 /* LaunchScreen.storyboard */; };
1515
A418FB631E23229E005BE849 /* MyZoomScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A418FB621E23229E005BE849 /* MyZoomScrollView.swift */; };
16+
A4B5F7511E2DB5E7005BA002 /* CropImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4B5F7501E2DB5E7005BA002 /* CropImageView.swift */; };
17+
A4B5F7531E2DBBB2005BA002 /* CropImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4B5F7521E2DBBB2005BA002 /* CropImageViewController.swift */; };
1618
A4F9E0361E28B12D0073423E /* CropViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4F9E0351E28B12D0073423E /* CropViewController.swift */; };
1719
A4F9E0381E28C3130073423E /* Model2.png in Resources */ = {isa = PBXBuildFile; fileRef = A4F9E0371E28C3130073423E /* Model2.png */; };
1820
A4F9E03A1E28C3460073423E /* Model.png in Resources */ = {isa = PBXBuildFile; fileRef = A4F9E0391E28C3460073423E /* Model.png */; };
@@ -27,6 +29,8 @@
2729
A418FB5A1E2321D3005BE849 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
2830
A418FB5C1E2321D3005BE849 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
2931
A418FB621E23229E005BE849 /* MyZoomScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MyZoomScrollView.swift; sourceTree = "<group>"; };
32+
A4B5F7501E2DB5E7005BA002 /* CropImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CropImageView.swift; sourceTree = "<group>"; };
33+
A4B5F7521E2DBBB2005BA002 /* CropImageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CropImageViewController.swift; sourceTree = "<group>"; };
3034
A4F9E0351E28B12D0073423E /* CropViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CropViewController.swift; sourceTree = "<group>"; };
3135
A4F9E0371E28C3130073423E /* Model2.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Model2.png; sourceTree = "<group>"; };
3236
A4F9E0391E28C3460073423E /* Model.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Model.png; sourceTree = "<group>"; };
@@ -68,6 +72,8 @@
6872
A418FB521E2321D3005BE849 /* ViewController.swift */,
6973
A418FB621E23229E005BE849 /* MyZoomScrollView.swift */,
7074
A4F9E0351E28B12D0073423E /* CropViewController.swift */,
75+
A4B5F7521E2DBBB2005BA002 /* CropImageViewController.swift */,
76+
A4B5F7501E2DB5E7005BA002 /* CropImageView.swift */,
7177
A418FB541E2321D3005BE849 /* Main.storyboard */,
7278
A418FB571E2321D3005BE849 /* Assets.xcassets */,
7379
A418FB591E2321D3005BE849 /* LaunchScreen.storyboard */,
@@ -151,9 +157,11 @@
151157
isa = PBXSourcesBuildPhase;
152158
buildActionMask = 2147483647;
153159
files = (
160+
A4B5F7511E2DB5E7005BA002 /* CropImageView.swift in Sources */,
154161
A418FB531E2321D3005BE849 /* ViewController.swift in Sources */,
155162
A418FB511E2321D3005BE849 /* AppDelegate.swift in Sources */,
156163
A4F9E0361E28B12D0073423E /* CropViewController.swift in Sources */,
164+
A4B5F7531E2DBBB2005BA002 /* CropImageViewController.swift in Sources */,
157165
A418FB631E23229E005BE849 /* MyZoomScrollView.swift in Sources */,
158166
);
159167
runOnlyForDeploymentPostprocessing = 0;

DemoImagePreview/DemoImagePreview/Base.lproj/Main.storyboard

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
99
</dependencies>
1010
<scenes>
11-
<!--Crop View Controller-->
11+
<!--Crop Image View Controller-->
1212
<scene sceneID="tne-QT-ifu">
1313
<objects>
14-
<viewController id="BYZ-38-t0r" customClass="CropViewController" customModule="DemoImagePreview" customModuleProvider="target" sceneMemberID="viewController">
14+
<viewController id="BYZ-38-t0r" customClass="CropImageViewController" customModule="DemoImagePreview" customModuleProvider="target" sceneMemberID="viewController">
1515
<layoutGuides>
1616
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
1717
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
//
2+
// CropImageView.swift
3+
// DemoImagePreview
4+
//
5+
// Created by Chris Hu on 17/1/17.
6+
// Copyright © 2017年 com.icetime17. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
class CropImageView: UIView {
12+
13+
var originImage: UIImage! {
14+
didSet {
15+
imageView.image = originImage
16+
}
17+
}
18+
var croppedImage: UIImage!
19+
20+
21+
fileprivate var imageView: UIImageView!
22+
fileprivate var panGesture: UIPanGestureRecognizer!
23+
fileprivate var pinchGesture: UIPinchGestureRecognizer!
24+
fileprivate var rotationGesture: UIRotationGestureRecognizer!
25+
26+
var minFrame: CGRect! // 图片缩放的极限值
27+
var maxFrame: CGRect!
28+
29+
30+
override init(frame: CGRect) {
31+
super.init(frame: frame)
32+
33+
clipsToBounds = true
34+
backgroundColor = UIColor.red
35+
36+
initImageView()
37+
}
38+
39+
required init?(coder aDecoder: NSCoder) {
40+
fatalError("init(coder:) has not been implemented")
41+
}
42+
43+
fileprivate func initImageView() {
44+
minFrame = bounds
45+
maxFrame = CGRect(x: -minFrame.width, y: -minFrame.height, width: minFrame.width * 3, height: minFrame.height * 3)
46+
47+
imageView = UIImageView(frame: bounds)
48+
imageView.isUserInteractionEnabled = true
49+
imageView.contentMode = .scaleAspectFit
50+
addSubview(imageView)
51+
52+
panGesture = UIPanGestureRecognizer(target: self, action: .actionPanGesture)
53+
imageView.addGestureRecognizer(panGesture)
54+
55+
pinchGesture = UIPinchGestureRecognizer(target: self, action: .actionPinchGesture)
56+
imageView.addGestureRecognizer(pinchGesture)
57+
58+
rotationGesture = UIRotationGestureRecognizer(target: self, action: .actionRotationGesture)
59+
// imageView.addGestureRecognizer(rotationGesture)
60+
61+
// panGesture.require(toFail: pinchGesture)
62+
// panGesture.require(toFail: rotationGesture)
63+
}
64+
65+
}
66+
67+
// MARK: - 手势操作
68+
private extension Selector {
69+
static let actionPanGesture = #selector(CropImageView.actionPanGesture(_:))
70+
static let actionPinchGesture = #selector(CropImageView.actionPinchGesture(_:))
71+
static let actionRotationGesture = #selector(CropImageView.actionRotationGesture(_:))
72+
}
73+
74+
private var lastPanPoint = CGPoint.zero
75+
private var lastScale = CGFloat(1.0)
76+
private var lastRotation = CGFloat(0.0)
77+
78+
extension CropImageView {
79+
80+
private func finalAdjustImageView() {
81+
// 平移的极限值
82+
let offsetY = imageView.frame.height - frame.height
83+
if imageView.frame.minY < -offsetY {
84+
imageView.frame.origin.y = -offsetY
85+
}
86+
if imageView.frame.maxY > frame.height + offsetY {
87+
imageView.frame.origin.y = 0
88+
}
89+
}
90+
91+
@objc fileprivate func actionPanGesture(_ sender: UIPanGestureRecognizer) {
92+
if sender.state == .began {
93+
lastPanPoint = CGPoint.zero
94+
}
95+
96+
let panPoint = sender.translation(in: self)
97+
98+
let transform = CGAffineTransform(translationX: panPoint.x - lastPanPoint.x, y: panPoint.y - lastPanPoint.y)
99+
imageView.transform = imageView.transform.concatenating(transform)
100+
101+
lastPanPoint = panPoint
102+
103+
finalAdjustImageView()
104+
}
105+
106+
@objc fileprivate func actionPinchGesture(_ sender: UIPinchGestureRecognizer) {
107+
if sender.state == .began {
108+
lastScale = 1.0
109+
return
110+
}
111+
112+
let scale = sender.scale / lastScale
113+
let currentTransform = imageView.transform
114+
let newTransform = currentTransform.scaledBy(x: scale, y: scale)
115+
imageView.transform = newTransform
116+
lastScale = sender.scale
117+
118+
// 缩放的极限值
119+
if sender.state == .ended {
120+
if imageView.frame.height < minFrame.height {
121+
UIView.animate(withDuration: 0.3, animations: {
122+
self.imageView.frame = self.minFrame
123+
})
124+
}
125+
if imageView.frame.height > maxFrame.height {
126+
UIView.animate(withDuration: 0.3, animations: {
127+
self.imageView.frame = self.maxFrame
128+
})
129+
}
130+
131+
finalAdjustImageView()
132+
}
133+
}
134+
135+
@objc fileprivate func actionRotationGesture(_ sender: UIRotationGestureRecognizer) {
136+
if sender.state == .ended {
137+
lastRotation = 0.0
138+
return
139+
}
140+
141+
var rotation = sender.rotation - lastRotation
142+
let currentTransform = imageView.transform
143+
let newTransform = currentTransform.rotated(by: rotation)
144+
imageView.transform = newTransform
145+
146+
lastRotation = sender.rotation
147+
}
148+
}
149+
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
//
2+
// CropImageViewController.swift
3+
// DemoImagePreview
4+
//
5+
// Created by Chris Hu on 17/1/17.
6+
// Copyright © 2017年 com.icetime17. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
private let offset: CGFloat = 10.0
12+
13+
class CropImageViewController: UIViewController {
14+
15+
var topBar: UIView!
16+
var btnRight: UIButton!
17+
18+
var cropImageView: CropImageView!
19+
var ratioView: UIView! // 裁剪框
20+
21+
var imageOriginal: UIImage! // 原图
22+
var imageCropped: UIImage! // 裁剪结果图
23+
24+
25+
override func viewDidLoad() {
26+
super.viewDidLoad()
27+
28+
view.backgroundColor = UIColor.black
29+
30+
imageOriginal = UIImage(named: "Model.png")
31+
32+
initTopBar()
33+
34+
initCropImageView()
35+
36+
initRatioView()
37+
}
38+
39+
// MARK: - topBar
40+
func initTopBar() {
41+
topBar = UIView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 40))
42+
topBar.backgroundColor = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1)
43+
view.addSubview(topBar)
44+
45+
btnRight = UIButton(frame: CGRect(x: topBar.frame.width - 70, y: 0, width: 70, height: topBar.frame.height))
46+
btnRight.setTitle("裁剪", for: .normal)
47+
btnRight.addTarget(self, action: #selector(CropViewController.actionBtnRight), for: .touchUpInside)
48+
topBar.addSubview(btnRight)
49+
}
50+
51+
func actionBtnRight() {
52+
// TODO 裁剪操作
53+
let image = cropImage()
54+
print(image?.size)
55+
}
56+
57+
func initCropImageView() {
58+
let height = view.frame.height - topBar.frame.height - 250 - offset * 3
59+
60+
cropImageView = CropImageView(frame: CGRect(x: 0, y: topBar.frame.height + offset, width: view.frame.width, height: height))
61+
view.addSubview(cropImageView)
62+
cropImageView.originImage = imageOriginal
63+
}
64+
65+
func initRatioView() {
66+
let height = cropImageView.frame.height + 4
67+
let width = CGFloat(height / 16 * 9)
68+
ratioView = UIView(frame: CGRect(x: (view.frame.width - width) / 2, y: topBar.frame.height + offset - 2, width: width, height: height))
69+
ratioView.layer.borderColor = UIColor.yellow.cgColor
70+
ratioView.layer.borderWidth = 1
71+
ratioView.center = cropImageView.center
72+
view.addSubview(ratioView)
73+
ratioView.isUserInteractionEnabled = false
74+
}
75+
76+
}
77+
78+
extension CropImageViewController {
79+
80+
func cropImage() -> UIImage? {
81+
let scaleImage = imageOriginal.size.height / cropImageView.frame.height
82+
// 注意convert的写法
83+
// 注意2的offset
84+
let ratioRect = CGRect(x: ratioView.frame.origin.x, y: ratioView.frame.origin.y + 2, width: ratioView.frame.width, height: ratioView.frame.height - 4)
85+
var rect = ratioView.convert(ratioRect, to: cropImageView)
86+
rect = CGRect(x: rect.origin.x, y: rect.origin.y, width: rect.width * scaleImage, height: rect.height * scaleImage)
87+
88+
imageCropped = imageOriginal.imageCropped(bounds: rect)
89+
return imageCropped
90+
}
91+
92+
}
93+
94+
private extension UIImage {
95+
/**
96+
Crop UIImage
97+
98+
- returns: UIImage cropped
99+
*/
100+
func imageCropped(bounds: CGRect) -> UIImage {
101+
let imageRef = cgImage!.cropping(to: bounds)
102+
let imageCropped = UIImage(cgImage: imageRef!)
103+
return imageCropped
104+
}
105+
}
106+

DemoImagePreview/DemoImagePreview/CropViewController.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ extension CropViewController {
124124

125125
}
126126

127-
extension UIImage {
127+
private extension UIImage {
128128
/**
129129
Crop UIImage
130130

0 commit comments

Comments
 (0)