├── .DS_Store
├── README.md
└── imageEditor
├── Default-568h@2x.png
├── Main
├── AppDelegate.swift
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── ImageEditor2Nib.xib
└── Info.plist
├── ViewController.swift
├── class
├── .DS_Store
└── kernel
│ └── .DS_Store
├── icons
├── .DS_Store
├── cancel.png
├── center.png
├── center.selected.png
├── crop.png
├── crop.selected.png
├── crop_169.png
├── crop_43.png
├── crop_canvas.png
├── crop_circle.png
├── crop_free.png
├── crop_free.selected.png
├── crop_image.png
├── crop_oval.png
├── crop_path_c.png
├── crop_path_s.png
├── crop_polygon.png
├── crop_square.png
├── crop_triangle.png
├── done.png
├── fullCanvas.png
├── fullCanvas_scaleFill.png
├── fullCanvas_scaleFit.png
├── index.png
├── mark.png
├── mark2.png
├── redo.png
├── rotate.png
├── rotate.selected.png
├── rotate_90.png
├── rotate_left.png
├── rotate_right.png
└── undo.png
├── imageEditor.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── pow.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
└── xcuserdata
│ └── pow.xcuserdatad
│ └── xcschemes
│ ├── imageEditor.xcscheme
│ └── xcschememanagement.plist
└── kernel
├── WPAngleRuler.swift
├── WPBaseEditorView.swift
├── WPCropEditorView.swift
├── WPExtension.swift
├── WPFreePathView.swift
└── WPSideMenu.swift
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/.DS_Store
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WPImageEditor
2 |
3 | 全功能系列裁剪图片。
4 | # 裁剪
5 |
6 | 1. 自由矩形,拖动四角自由边矩形
7 |
8 | 2. 根据图片大小的SIZE
9 |
10 | 3. 根据您需要的画布大小,设置WPBaseEditor.canvas的size大小
11 |
12 | 4. 多边形,自由添加多边形。最少5边形。最大30边形。太大了也显示不了。可以在WPCorpEditor里面设置max&min 值.辅助菜单显示加减的按钮可以加角减角
13 |
14 | 5. 定点自由抠图,点击一个点,再点击下一个点,两点之间会连线,形成矩形之后,就可以切图了。
15 |
16 | 6. 曲线抠图,按下去一个点,拖拽到想到达的点,出现第二点,修改其点弧度可以点击当前点,弹出两根控制弧度的线来拖拽,这个用笔的话比较灵敏。手指不太好监测,有好的实现欢迎补丁。
17 |
18 | 7. 正方形切图,
19 |
20 | 8. 等边三角形,
21 |
22 | 9. 圆形
23 |
24 | 10. 椭圆形
25 |
26 | 11. 4:3的比例切图,有辅助按钮就可以变换4:3或者3:4
27 |
28 | 12. 16:9的比例切图,有辅助按钮就可以变换16:9或者9:16
29 |
30 | # redo&undo
31 |
32 | 再出现了redo&undo按钮的情况下,是支持的。
33 | # 旋转
34 |
35 | 点击旋转,会出现半圆刻度尺,尺度上的度数是算过的精准的,可以向左90度,或者向右,也可以滑动刻度尺来调整角度。
36 | # 等比
37 |
38 | 图片会根据屏幕的大小,横屏与竖屏的请款下,fill屏幕。而保持原始的图片尺寸,不会更改。
39 |
40 | -----------------
41 | # WPImageEditor
42 |
43 | Full feature series cut pictures.
44 | # cutting
45 | 1. Free rectangle, drag the rectangle with free edges.
46 | 2. SIZE according to the picture SIZE.
47 | 3. Set the size of the WPBaseEditor. Canvas according to the size of the canvas you need.
48 | 4. Polygons, free to add polygons. At least 5 edges. Maximum 30 edges. Too big to show. The max&min value can be set in WPCorpEditor. The auxiliary menu shows the addition and subtraction of the button to add Angle reduction.
49 | 5. Click on a point and click on the next point. After the rectangle is formed, the map can be cut.
50 | 6. Curve cutout, press down a point, drag the point to think of, appear in the second place, change its radian can click on the current points, pop-up control arc two lines to drag and drop, this pen is sensitive. Finger is not very good monitor, have good implementation welcome patch.
51 | 7. Square cut,
52 | Equilateral triangle,
53 | 9. The circular
54 | 10. The oval
55 | 11.4: the proportion of 3 is cut, and the auxiliary button can be changed at 4:3 or 3:4.
56 | 12.16: the ratio of 9 is cut, and the auxiliary button can be changed to 16:9 or 9:16.
57 | # redo&undo
58 | There are redo&undo buttons that are supported.
59 | # rotation
60 | If you click on the rotation, you will see the semicircle scale, the degree of the scale is accurate, can be 90 degrees to the left, or to the right, you can also slide the scale to adjust the Angle.
61 | # geometric
62 | The picture will be based on the size of the screen, the horizontal and vertical screens, fill the screen. Keep the original image size unchanged.
63 |
--------------------------------------------------------------------------------
/imageEditor/Default-568h@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/Default-568h@2x.png
--------------------------------------------------------------------------------
/imageEditor/Main/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // WPImageEditor2
4 | //
5 | // Created by pow on 2017/11/9.
6 | // Copyright © 2017年 pow. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
18 | // Override point for customization after application launch.
19 |
20 |
21 |
22 |
23 |
24 | return true
25 | }
26 |
27 |
28 | func applicationWillResignActive(_ application: UIApplication) {
29 | // 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.
30 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
31 | }
32 |
33 | func applicationDidEnterBackground(_ application: UIApplication) {
34 | // 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.
35 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
36 | }
37 |
38 | func applicationWillEnterForeground(_ application: UIApplication) {
39 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
40 | }
41 |
42 | func applicationDidBecomeActive(_ application: UIApplication) {
43 | // 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.
44 | }
45 |
46 | func applicationWillTerminate(_ application: UIApplication) {
47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
48 | }
49 |
50 |
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/imageEditor/Main/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "ipad",
35 | "size" : "29x29",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "ipad",
40 | "size" : "29x29",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "40x40",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "40x40",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "76x76",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "76x76",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/imageEditor/Main/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/imageEditor/Main/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/imageEditor/Main/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | NSPhotoLibraryUsageDescription
24 | 使用相机
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 | UIInterfaceOrientationPortraitUpsideDown
39 |
40 | UISupportedInterfaceOrientations~ipad
41 |
42 | UIInterfaceOrientationPortrait
43 | UIInterfaceOrientationPortraitUpsideDown
44 | UIInterfaceOrientationLandscapeLeft
45 | UIInterfaceOrientationLandscapeRight
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/imageEditor/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // WPImageEditor2
4 | //
5 | // Created by pow on 2017/11/9.
6 | // Copyright © 2017年 pow. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ViewController: UIViewController {
12 |
13 | override func viewDidLoad() {
14 | super.viewDidLoad()
15 |
16 | // addEditorImageView(originalImage: UIImage.init(named: "ape_fwk_all")!)
17 |
18 |
19 |
20 | // Do any additional setup after loading the view, typically from a nib.
21 | }
22 |
23 | override func didReceiveMemoryWarning() {
24 | super.didReceiveMemoryWarning()
25 | // Dispose of any resources that can be recreated.
26 | }
27 |
28 |
29 | func addEditorImageView(originalImage:UIImage) {
30 |
31 | let baseView:WPBaseEditorView = WPBaseEditorView.loadNibFileToSetup(editorImage:originalImage , InSuperView: self.view)
32 | baseView.moveableArea = self.view.frame
33 | baseView.setup()
34 | baseView.pasteBlock = {(image, bounds) in
35 |
36 | self.backImage(image: image, point: bounds)
37 |
38 | }
39 | }
40 |
41 | func backImage(image:UIImage,point:CGRect) {
42 |
43 | let imgView = UIImageView.init(image: image)
44 |
45 |
46 |
47 | imgView.frame = point
48 |
49 | imgView.backgroundColor = UIColor.black
50 | self.view.addSubview(imgView)
51 |
52 | }
53 |
54 |
55 |
56 | }
57 |
58 | //MARK:Import images from Photos Library
59 | extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
60 |
61 |
62 | @IBAction func performImport2(_ sender: UIButton) {
63 | let imagePicker = UIImagePickerController()
64 | imagePicker.sourceType = .savedPhotosAlbum
65 | imagePicker.delegate = self
66 | self.present(imagePicker, animated: true, completion: nil)
67 | }
68 |
69 | func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
70 | if (info[UIImagePickerControllerOriginalImage] as? UIImage) != nil {
71 |
72 | picker.dismiss(animated: true, completion: {
73 |
74 |
75 | self.addEditorImageView(originalImage:info[UIImagePickerControllerOriginalImage] as! UIImage)
76 |
77 | })
78 | }
79 | }
80 |
81 |
82 | override var shouldAutorotate: Bool {
83 | return false
84 | }
85 |
86 |
87 |
88 |
89 |
90 |
91 | }
92 |
93 |
--------------------------------------------------------------------------------
/imageEditor/class/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/class/.DS_Store
--------------------------------------------------------------------------------
/imageEditor/class/kernel/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/class/kernel/.DS_Store
--------------------------------------------------------------------------------
/imageEditor/icons/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/.DS_Store
--------------------------------------------------------------------------------
/imageEditor/icons/cancel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/cancel.png
--------------------------------------------------------------------------------
/imageEditor/icons/center.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/center.png
--------------------------------------------------------------------------------
/imageEditor/icons/center.selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/center.selected.png
--------------------------------------------------------------------------------
/imageEditor/icons/crop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/crop.png
--------------------------------------------------------------------------------
/imageEditor/icons/crop.selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/crop.selected.png
--------------------------------------------------------------------------------
/imageEditor/icons/crop_169.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/crop_169.png
--------------------------------------------------------------------------------
/imageEditor/icons/crop_43.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/crop_43.png
--------------------------------------------------------------------------------
/imageEditor/icons/crop_canvas.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/crop_canvas.png
--------------------------------------------------------------------------------
/imageEditor/icons/crop_circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/crop_circle.png
--------------------------------------------------------------------------------
/imageEditor/icons/crop_free.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/crop_free.png
--------------------------------------------------------------------------------
/imageEditor/icons/crop_free.selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/crop_free.selected.png
--------------------------------------------------------------------------------
/imageEditor/icons/crop_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/crop_image.png
--------------------------------------------------------------------------------
/imageEditor/icons/crop_oval.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/crop_oval.png
--------------------------------------------------------------------------------
/imageEditor/icons/crop_path_c.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/crop_path_c.png
--------------------------------------------------------------------------------
/imageEditor/icons/crop_path_s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/crop_path_s.png
--------------------------------------------------------------------------------
/imageEditor/icons/crop_polygon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/crop_polygon.png
--------------------------------------------------------------------------------
/imageEditor/icons/crop_square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/crop_square.png
--------------------------------------------------------------------------------
/imageEditor/icons/crop_triangle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/crop_triangle.png
--------------------------------------------------------------------------------
/imageEditor/icons/done.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/done.png
--------------------------------------------------------------------------------
/imageEditor/icons/fullCanvas.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/fullCanvas.png
--------------------------------------------------------------------------------
/imageEditor/icons/fullCanvas_scaleFill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/fullCanvas_scaleFill.png
--------------------------------------------------------------------------------
/imageEditor/icons/fullCanvas_scaleFit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/fullCanvas_scaleFit.png
--------------------------------------------------------------------------------
/imageEditor/icons/index.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/index.png
--------------------------------------------------------------------------------
/imageEditor/icons/mark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/mark.png
--------------------------------------------------------------------------------
/imageEditor/icons/mark2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/mark2.png
--------------------------------------------------------------------------------
/imageEditor/icons/redo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/redo.png
--------------------------------------------------------------------------------
/imageEditor/icons/rotate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/rotate.png
--------------------------------------------------------------------------------
/imageEditor/icons/rotate.selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/rotate.selected.png
--------------------------------------------------------------------------------
/imageEditor/icons/rotate_90.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/rotate_90.png
--------------------------------------------------------------------------------
/imageEditor/icons/rotate_left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/rotate_left.png
--------------------------------------------------------------------------------
/imageEditor/icons/rotate_right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/rotate_right.png
--------------------------------------------------------------------------------
/imageEditor/icons/undo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/icons/undo.png
--------------------------------------------------------------------------------
/imageEditor/imageEditor.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 9636DC741FE122CF005B93BD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9636DC191FE122CF005B93BD /* AppDelegate.swift */; };
11 | 9636DC751FE122CF005B93BD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC1A1FE122CF005B93BD /* Assets.xcassets */; };
12 | 9636DC781FE122CF005B93BD /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC1F1FE122CF005B93BD /* LaunchScreen.storyboard */; };
13 | 9636DC791FE122CF005B93BD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC211FE122CF005B93BD /* Main.storyboard */; };
14 | 9636DC911FE122CF005B93BD /* cancel.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC3C1FE122CF005B93BD /* cancel.png */; };
15 | 9636DC921FE122CF005B93BD /* center.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC3D1FE122CF005B93BD /* center.png */; };
16 | 9636DC931FE122CF005B93BD /* center.selected.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC3E1FE122CF005B93BD /* center.selected.png */; };
17 | 9636DC941FE122CF005B93BD /* crop.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC3F1FE122CF005B93BD /* crop.png */; };
18 | 9636DC951FE122CF005B93BD /* crop.selected.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC401FE122CF005B93BD /* crop.selected.png */; };
19 | 9636DC961FE122CF005B93BD /* crop_169.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC411FE122CF005B93BD /* crop_169.png */; };
20 | 9636DC971FE122CF005B93BD /* crop_43.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC421FE122CF005B93BD /* crop_43.png */; };
21 | 9636DC981FE122CF005B93BD /* crop_canvas.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC431FE122CF005B93BD /* crop_canvas.png */; };
22 | 9636DC991FE122CF005B93BD /* crop_circle.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC441FE122CF005B93BD /* crop_circle.png */; };
23 | 9636DC9A1FE122CF005B93BD /* crop_free.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC451FE122CF005B93BD /* crop_free.png */; };
24 | 9636DC9B1FE122CF005B93BD /* crop_free.selected.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC461FE122CF005B93BD /* crop_free.selected.png */; };
25 | 9636DC9C1FE122CF005B93BD /* crop_image.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC471FE122CF005B93BD /* crop_image.png */; };
26 | 9636DC9D1FE122CF005B93BD /* crop_oval.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC481FE122CF005B93BD /* crop_oval.png */; };
27 | 9636DC9E1FE122CF005B93BD /* crop_path_c.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC491FE122CF005B93BD /* crop_path_c.png */; };
28 | 9636DC9F1FE122CF005B93BD /* crop_path_s.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC4A1FE122CF005B93BD /* crop_path_s.png */; };
29 | 9636DCA01FE122CF005B93BD /* crop_polygon.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC4B1FE122CF005B93BD /* crop_polygon.png */; };
30 | 9636DCA11FE122CF005B93BD /* crop_square.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC4C1FE122CF005B93BD /* crop_square.png */; };
31 | 9636DCA21FE122CF005B93BD /* crop_triangle.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC4D1FE122CF005B93BD /* crop_triangle.png */; };
32 | 9636DCA31FE122CF005B93BD /* done.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC4E1FE122CF005B93BD /* done.png */; };
33 | 9636DCA41FE122CF005B93BD /* fullCanvas.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC4F1FE122CF005B93BD /* fullCanvas.png */; };
34 | 9636DCA51FE122CF005B93BD /* fullCanvas_scaleFill.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC501FE122CF005B93BD /* fullCanvas_scaleFill.png */; };
35 | 9636DCA61FE122CF005B93BD /* fullCanvas_scaleFit.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC511FE122CF005B93BD /* fullCanvas_scaleFit.png */; };
36 | 9636DCA71FE122CF005B93BD /* index.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC521FE122CF005B93BD /* index.png */; };
37 | 9636DCA81FE122CF005B93BD /* mark.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC531FE122CF005B93BD /* mark.png */; };
38 | 9636DCA91FE122CF005B93BD /* mark2.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC541FE122CF005B93BD /* mark2.png */; };
39 | 9636DCAA1FE122CF005B93BD /* redo.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC551FE122CF005B93BD /* redo.png */; };
40 | 9636DCAB1FE122CF005B93BD /* rotate.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC561FE122CF005B93BD /* rotate.png */; };
41 | 9636DCAC1FE122CF005B93BD /* rotate.selected.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC571FE122CF005B93BD /* rotate.selected.png */; };
42 | 9636DCAD1FE122CF005B93BD /* rotate_90.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC581FE122CF005B93BD /* rotate_90.png */; };
43 | 9636DCAE1FE122CF005B93BD /* rotate_left.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC591FE122CF005B93BD /* rotate_left.png */; };
44 | 9636DCAF1FE122CF005B93BD /* rotate_right.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC5A1FE122CF005B93BD /* rotate_right.png */; };
45 | 9636DCB01FE122CF005B93BD /* undo.png in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC5B1FE122CF005B93BD /* undo.png */; };
46 | 9636DCB11FE122CF005B93BD /* ImageEditor2Nib.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC5C1FE122CF005B93BD /* ImageEditor2Nib.xib */; };
47 | 9636DCB31FE122CF005B93BD /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9636DC5E1FE122CF005B93BD /* Info.plist */; };
48 | 9636DCC01FE122CF005B93BD /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9636DC6C1FE122CF005B93BD /* ViewController.swift */; };
49 | 9636DCC11FE122CF005B93BD /* WPAngleRuler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9636DC6D1FE122CF005B93BD /* WPAngleRuler.swift */; };
50 | 9636DCC21FE122CF005B93BD /* WPBaseEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9636DC6E1FE122CF005B93BD /* WPBaseEditorView.swift */; };
51 | 9636DCC31FE122CF005B93BD /* WPCropEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9636DC6F1FE122CF005B93BD /* WPCropEditorView.swift */; };
52 | 9636DCC41FE122CF005B93BD /* WPExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9636DC701FE122CF005B93BD /* WPExtension.swift */; };
53 | 9636DCC51FE122CF005B93BD /* WPFreePathView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9636DC711FE122CF005B93BD /* WPFreePathView.swift */; };
54 | 9636DCC61FE122CF005B93BD /* WPSideMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9636DC721FE122CF005B93BD /* WPSideMenu.swift */; };
55 | 96E3437A201AFEE800AB031E /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 96E34379201AFEE800AB031E /* Default-568h@2x.png */; };
56 | /* End PBXBuildFile section */
57 |
58 | /* Begin PBXFileReference section */
59 | 9636DC191FE122CF005B93BD /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
60 | 9636DC1A1FE122CF005B93BD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
61 | 9636DC201FE122CF005B93BD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
62 | 9636DC221FE122CF005B93BD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
63 | 9636DC3C1FE122CF005B93BD /* cancel.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = cancel.png; sourceTree = ""; };
64 | 9636DC3D1FE122CF005B93BD /* center.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = center.png; sourceTree = ""; };
65 | 9636DC3E1FE122CF005B93BD /* center.selected.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = center.selected.png; sourceTree = ""; };
66 | 9636DC3F1FE122CF005B93BD /* crop.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = crop.png; sourceTree = ""; };
67 | 9636DC401FE122CF005B93BD /* crop.selected.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = crop.selected.png; sourceTree = ""; };
68 | 9636DC411FE122CF005B93BD /* crop_169.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = crop_169.png; sourceTree = ""; };
69 | 9636DC421FE122CF005B93BD /* crop_43.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = crop_43.png; sourceTree = ""; };
70 | 9636DC431FE122CF005B93BD /* crop_canvas.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = crop_canvas.png; sourceTree = ""; };
71 | 9636DC441FE122CF005B93BD /* crop_circle.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = crop_circle.png; sourceTree = ""; };
72 | 9636DC451FE122CF005B93BD /* crop_free.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = crop_free.png; sourceTree = ""; };
73 | 9636DC461FE122CF005B93BD /* crop_free.selected.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = crop_free.selected.png; sourceTree = ""; };
74 | 9636DC471FE122CF005B93BD /* crop_image.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = crop_image.png; sourceTree = ""; };
75 | 9636DC481FE122CF005B93BD /* crop_oval.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = crop_oval.png; sourceTree = ""; };
76 | 9636DC491FE122CF005B93BD /* crop_path_c.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = crop_path_c.png; sourceTree = ""; };
77 | 9636DC4A1FE122CF005B93BD /* crop_path_s.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = crop_path_s.png; sourceTree = ""; };
78 | 9636DC4B1FE122CF005B93BD /* crop_polygon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = crop_polygon.png; sourceTree = ""; };
79 | 9636DC4C1FE122CF005B93BD /* crop_square.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = crop_square.png; sourceTree = ""; };
80 | 9636DC4D1FE122CF005B93BD /* crop_triangle.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = crop_triangle.png; sourceTree = ""; };
81 | 9636DC4E1FE122CF005B93BD /* done.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = done.png; sourceTree = ""; };
82 | 9636DC4F1FE122CF005B93BD /* fullCanvas.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = fullCanvas.png; sourceTree = ""; };
83 | 9636DC501FE122CF005B93BD /* fullCanvas_scaleFill.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = fullCanvas_scaleFill.png; sourceTree = ""; };
84 | 9636DC511FE122CF005B93BD /* fullCanvas_scaleFit.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = fullCanvas_scaleFit.png; sourceTree = ""; };
85 | 9636DC521FE122CF005B93BD /* index.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = index.png; sourceTree = ""; };
86 | 9636DC531FE122CF005B93BD /* mark.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = mark.png; sourceTree = ""; };
87 | 9636DC541FE122CF005B93BD /* mark2.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = mark2.png; sourceTree = ""; };
88 | 9636DC551FE122CF005B93BD /* redo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = redo.png; sourceTree = ""; };
89 | 9636DC561FE122CF005B93BD /* rotate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = rotate.png; sourceTree = ""; };
90 | 9636DC571FE122CF005B93BD /* rotate.selected.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = rotate.selected.png; sourceTree = ""; };
91 | 9636DC581FE122CF005B93BD /* rotate_90.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = rotate_90.png; sourceTree = ""; };
92 | 9636DC591FE122CF005B93BD /* rotate_left.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = rotate_left.png; sourceTree = ""; };
93 | 9636DC5A1FE122CF005B93BD /* rotate_right.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = rotate_right.png; sourceTree = ""; };
94 | 9636DC5B1FE122CF005B93BD /* undo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = undo.png; sourceTree = ""; };
95 | 9636DC5C1FE122CF005B93BD /* ImageEditor2Nib.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ImageEditor2Nib.xib; sourceTree = ""; };
96 | 9636DC5E1FE122CF005B93BD /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
97 | 9636DC6C1FE122CF005B93BD /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
98 | 9636DC6D1FE122CF005B93BD /* WPAngleRuler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WPAngleRuler.swift; sourceTree = ""; };
99 | 9636DC6E1FE122CF005B93BD /* WPBaseEditorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WPBaseEditorView.swift; sourceTree = ""; };
100 | 9636DC6F1FE122CF005B93BD /* WPCropEditorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WPCropEditorView.swift; sourceTree = ""; };
101 | 9636DC701FE122CF005B93BD /* WPExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WPExtension.swift; sourceTree = ""; };
102 | 9636DC711FE122CF005B93BD /* WPFreePathView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WPFreePathView.swift; sourceTree = ""; };
103 | 9636DC721FE122CF005B93BD /* WPSideMenu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WPSideMenu.swift; sourceTree = ""; };
104 | 966025481FAC45A200BEA651 /* imageEditor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = imageEditor.app; sourceTree = BUILT_PRODUCTS_DIR; };
105 | 96E34379201AFEE800AB031E /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; };
106 | /* End PBXFileReference section */
107 |
108 | /* Begin PBXFrameworksBuildPhase section */
109 | 966025451FAC45A200BEA651 /* Frameworks */ = {
110 | isa = PBXFrameworksBuildPhase;
111 | buildActionMask = 2147483647;
112 | files = (
113 | );
114 | runOnlyForDeploymentPostprocessing = 0;
115 | };
116 | /* End PBXFrameworksBuildPhase section */
117 |
118 | /* Begin PBXGroup section */
119 | 9636DC3B1FE122CF005B93BD /* icons */ = {
120 | isa = PBXGroup;
121 | children = (
122 | 9636DC3C1FE122CF005B93BD /* cancel.png */,
123 | 9636DC3D1FE122CF005B93BD /* center.png */,
124 | 9636DC3E1FE122CF005B93BD /* center.selected.png */,
125 | 9636DC3F1FE122CF005B93BD /* crop.png */,
126 | 9636DC401FE122CF005B93BD /* crop.selected.png */,
127 | 9636DC411FE122CF005B93BD /* crop_169.png */,
128 | 9636DC421FE122CF005B93BD /* crop_43.png */,
129 | 9636DC431FE122CF005B93BD /* crop_canvas.png */,
130 | 9636DC441FE122CF005B93BD /* crop_circle.png */,
131 | 9636DC451FE122CF005B93BD /* crop_free.png */,
132 | 9636DC461FE122CF005B93BD /* crop_free.selected.png */,
133 | 9636DC471FE122CF005B93BD /* crop_image.png */,
134 | 9636DC481FE122CF005B93BD /* crop_oval.png */,
135 | 9636DC491FE122CF005B93BD /* crop_path_c.png */,
136 | 9636DC4A1FE122CF005B93BD /* crop_path_s.png */,
137 | 9636DC4B1FE122CF005B93BD /* crop_polygon.png */,
138 | 9636DC4C1FE122CF005B93BD /* crop_square.png */,
139 | 9636DC4D1FE122CF005B93BD /* crop_triangle.png */,
140 | 9636DC4E1FE122CF005B93BD /* done.png */,
141 | 9636DC4F1FE122CF005B93BD /* fullCanvas.png */,
142 | 9636DC501FE122CF005B93BD /* fullCanvas_scaleFill.png */,
143 | 9636DC511FE122CF005B93BD /* fullCanvas_scaleFit.png */,
144 | 9636DC521FE122CF005B93BD /* index.png */,
145 | 9636DC531FE122CF005B93BD /* mark.png */,
146 | 9636DC541FE122CF005B93BD /* mark2.png */,
147 | 9636DC551FE122CF005B93BD /* redo.png */,
148 | 9636DC561FE122CF005B93BD /* rotate.png */,
149 | 9636DC571FE122CF005B93BD /* rotate.selected.png */,
150 | 9636DC581FE122CF005B93BD /* rotate_90.png */,
151 | 9636DC591FE122CF005B93BD /* rotate_left.png */,
152 | 9636DC5A1FE122CF005B93BD /* rotate_right.png */,
153 | 9636DC5B1FE122CF005B93BD /* undo.png */,
154 | );
155 | path = icons;
156 | sourceTree = "";
157 | };
158 | 9660253F1FAC45A200BEA651 = {
159 | isa = PBXGroup;
160 | children = (
161 | 96E34379201AFEE800AB031E /* Default-568h@2x.png */,
162 | 9636DC6C1FE122CF005B93BD /* ViewController.swift */,
163 | 9636DC3B1FE122CF005B93BD /* icons */,
164 | 96E34378201AFEB600AB031E /* Main */,
165 | 96E34377201AFEA600AB031E /* kernel */,
166 | 966025491FAC45A200BEA651 /* Products */,
167 | );
168 | sourceTree = "";
169 | };
170 | 966025491FAC45A200BEA651 /* Products */ = {
171 | isa = PBXGroup;
172 | children = (
173 | 966025481FAC45A200BEA651 /* imageEditor.app */,
174 | );
175 | name = Products;
176 | sourceTree = "";
177 | };
178 | 96E34377201AFEA600AB031E /* kernel */ = {
179 | isa = PBXGroup;
180 | children = (
181 | 9636DC6D1FE122CF005B93BD /* WPAngleRuler.swift */,
182 | 9636DC6E1FE122CF005B93BD /* WPBaseEditorView.swift */,
183 | 9636DC6F1FE122CF005B93BD /* WPCropEditorView.swift */,
184 | 9636DC701FE122CF005B93BD /* WPExtension.swift */,
185 | 9636DC711FE122CF005B93BD /* WPFreePathView.swift */,
186 | 9636DC721FE122CF005B93BD /* WPSideMenu.swift */,
187 | );
188 | path = kernel;
189 | sourceTree = "";
190 | };
191 | 96E34378201AFEB600AB031E /* Main */ = {
192 | isa = PBXGroup;
193 | children = (
194 | 9636DC5E1FE122CF005B93BD /* Info.plist */,
195 | 9636DC191FE122CF005B93BD /* AppDelegate.swift */,
196 | 9636DC1A1FE122CF005B93BD /* Assets.xcassets */,
197 | 9636DC1F1FE122CF005B93BD /* LaunchScreen.storyboard */,
198 | 9636DC211FE122CF005B93BD /* Main.storyboard */,
199 | 9636DC5C1FE122CF005B93BD /* ImageEditor2Nib.xib */,
200 | );
201 | path = Main;
202 | sourceTree = "";
203 | };
204 | /* End PBXGroup section */
205 |
206 | /* Begin PBXNativeTarget section */
207 | 966025471FAC45A200BEA651 /* imageEditor */ = {
208 | isa = PBXNativeTarget;
209 | buildConfigurationList = 9660255A1FAC45A200BEA651 /* Build configuration list for PBXNativeTarget "imageEditor" */;
210 | buildPhases = (
211 | 966025441FAC45A200BEA651 /* Sources */,
212 | 966025451FAC45A200BEA651 /* Frameworks */,
213 | 966025461FAC45A200BEA651 /* Resources */,
214 | );
215 | buildRules = (
216 | );
217 | dependencies = (
218 | );
219 | name = imageEditor;
220 | productName = imageEditor;
221 | productReference = 966025481FAC45A200BEA651 /* imageEditor.app */;
222 | productType = "com.apple.product-type.application";
223 | };
224 | /* End PBXNativeTarget section */
225 |
226 | /* Begin PBXProject section */
227 | 966025401FAC45A200BEA651 /* Project object */ = {
228 | isa = PBXProject;
229 | attributes = {
230 | LastSwiftUpdateCheck = 0830;
231 | LastUpgradeCheck = 0920;
232 | ORGANIZATIONNAME = pow;
233 | TargetAttributes = {
234 | 966025471FAC45A200BEA651 = {
235 | CreatedOnToolsVersion = 8.3.3;
236 | DevelopmentTeam = 387DLK86QQ;
237 | LastSwiftMigration = 0910;
238 | ProvisioningStyle = Automatic;
239 | };
240 | };
241 | };
242 | buildConfigurationList = 966025431FAC45A200BEA651 /* Build configuration list for PBXProject "imageEditor" */;
243 | compatibilityVersion = "Xcode 3.2";
244 | developmentRegion = English;
245 | hasScannedForEncodings = 0;
246 | knownRegions = (
247 | en,
248 | Base,
249 | );
250 | mainGroup = 9660253F1FAC45A200BEA651;
251 | productRefGroup = 966025491FAC45A200BEA651 /* Products */;
252 | projectDirPath = "";
253 | projectRoot = "";
254 | targets = (
255 | 966025471FAC45A200BEA651 /* imageEditor */,
256 | );
257 | };
258 | /* End PBXProject section */
259 |
260 | /* Begin PBXResourcesBuildPhase section */
261 | 966025461FAC45A200BEA651 /* Resources */ = {
262 | isa = PBXResourcesBuildPhase;
263 | buildActionMask = 2147483647;
264 | files = (
265 | 9636DC791FE122CF005B93BD /* Main.storyboard in Resources */,
266 | 9636DCB11FE122CF005B93BD /* ImageEditor2Nib.xib in Resources */,
267 | 9636DCA01FE122CF005B93BD /* crop_polygon.png in Resources */,
268 | 9636DCAB1FE122CF005B93BD /* rotate.png in Resources */,
269 | 9636DCA51FE122CF005B93BD /* fullCanvas_scaleFill.png in Resources */,
270 | 9636DCAA1FE122CF005B93BD /* redo.png in Resources */,
271 | 9636DC971FE122CF005B93BD /* crop_43.png in Resources */,
272 | 9636DCAF1FE122CF005B93BD /* rotate_right.png in Resources */,
273 | 9636DC921FE122CF005B93BD /* center.png in Resources */,
274 | 9636DC951FE122CF005B93BD /* crop.selected.png in Resources */,
275 | 9636DC941FE122CF005B93BD /* crop.png in Resources */,
276 | 9636DC781FE122CF005B93BD /* LaunchScreen.storyboard in Resources */,
277 | 9636DCAE1FE122CF005B93BD /* rotate_left.png in Resources */,
278 | 9636DCAD1FE122CF005B93BD /* rotate_90.png in Resources */,
279 | 9636DCA21FE122CF005B93BD /* crop_triangle.png in Resources */,
280 | 9636DCA81FE122CF005B93BD /* mark.png in Resources */,
281 | 9636DCB31FE122CF005B93BD /* Info.plist in Resources */,
282 | 9636DC751FE122CF005B93BD /* Assets.xcassets in Resources */,
283 | 9636DCA41FE122CF005B93BD /* fullCanvas.png in Resources */,
284 | 9636DCB01FE122CF005B93BD /* undo.png in Resources */,
285 | 9636DCA71FE122CF005B93BD /* index.png in Resources */,
286 | 9636DC981FE122CF005B93BD /* crop_canvas.png in Resources */,
287 | 9636DC931FE122CF005B93BD /* center.selected.png in Resources */,
288 | 9636DCA31FE122CF005B93BD /* done.png in Resources */,
289 | 9636DC9E1FE122CF005B93BD /* crop_path_c.png in Resources */,
290 | 9636DC9D1FE122CF005B93BD /* crop_oval.png in Resources */,
291 | 9636DC911FE122CF005B93BD /* cancel.png in Resources */,
292 | 9636DC961FE122CF005B93BD /* crop_169.png in Resources */,
293 | 9636DC9F1FE122CF005B93BD /* crop_path_s.png in Resources */,
294 | 9636DC9A1FE122CF005B93BD /* crop_free.png in Resources */,
295 | 9636DC9B1FE122CF005B93BD /* crop_free.selected.png in Resources */,
296 | 96E3437A201AFEE800AB031E /* Default-568h@2x.png in Resources */,
297 | 9636DCA91FE122CF005B93BD /* mark2.png in Resources */,
298 | 9636DCA61FE122CF005B93BD /* fullCanvas_scaleFit.png in Resources */,
299 | 9636DC9C1FE122CF005B93BD /* crop_image.png in Resources */,
300 | 9636DCAC1FE122CF005B93BD /* rotate.selected.png in Resources */,
301 | 9636DCA11FE122CF005B93BD /* crop_square.png in Resources */,
302 | 9636DC991FE122CF005B93BD /* crop_circle.png in Resources */,
303 | );
304 | runOnlyForDeploymentPostprocessing = 0;
305 | };
306 | /* End PBXResourcesBuildPhase section */
307 |
308 | /* Begin PBXSourcesBuildPhase section */
309 | 966025441FAC45A200BEA651 /* Sources */ = {
310 | isa = PBXSourcesBuildPhase;
311 | buildActionMask = 2147483647;
312 | files = (
313 | 9636DCC51FE122CF005B93BD /* WPFreePathView.swift in Sources */,
314 | 9636DC741FE122CF005B93BD /* AppDelegate.swift in Sources */,
315 | 9636DCC21FE122CF005B93BD /* WPBaseEditorView.swift in Sources */,
316 | 9636DCC11FE122CF005B93BD /* WPAngleRuler.swift in Sources */,
317 | 9636DCC61FE122CF005B93BD /* WPSideMenu.swift in Sources */,
318 | 9636DCC41FE122CF005B93BD /* WPExtension.swift in Sources */,
319 | 9636DCC01FE122CF005B93BD /* ViewController.swift in Sources */,
320 | 9636DCC31FE122CF005B93BD /* WPCropEditorView.swift in Sources */,
321 | );
322 | runOnlyForDeploymentPostprocessing = 0;
323 | };
324 | /* End PBXSourcesBuildPhase section */
325 |
326 | /* Begin PBXVariantGroup section */
327 | 9636DC1F1FE122CF005B93BD /* LaunchScreen.storyboard */ = {
328 | isa = PBXVariantGroup;
329 | children = (
330 | 9636DC201FE122CF005B93BD /* Base */,
331 | );
332 | name = LaunchScreen.storyboard;
333 | sourceTree = "";
334 | };
335 | 9636DC211FE122CF005B93BD /* Main.storyboard */ = {
336 | isa = PBXVariantGroup;
337 | children = (
338 | 9636DC221FE122CF005B93BD /* Base */,
339 | );
340 | name = Main.storyboard;
341 | sourceTree = "";
342 | };
343 | /* End PBXVariantGroup section */
344 |
345 | /* Begin XCBuildConfiguration section */
346 | 966025581FAC45A200BEA651 /* Debug */ = {
347 | isa = XCBuildConfiguration;
348 | buildSettings = {
349 | ALWAYS_SEARCH_USER_PATHS = NO;
350 | CLANG_ANALYZER_NONNULL = YES;
351 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
352 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
353 | CLANG_CXX_LIBRARY = "libc++";
354 | CLANG_ENABLE_MODULES = YES;
355 | CLANG_ENABLE_OBJC_ARC = YES;
356 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
357 | CLANG_WARN_BOOL_CONVERSION = YES;
358 | CLANG_WARN_COMMA = YES;
359 | CLANG_WARN_CONSTANT_CONVERSION = YES;
360 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
361 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
362 | CLANG_WARN_EMPTY_BODY = YES;
363 | CLANG_WARN_ENUM_CONVERSION = YES;
364 | CLANG_WARN_INFINITE_RECURSION = YES;
365 | CLANG_WARN_INT_CONVERSION = YES;
366 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
367 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
368 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
369 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
370 | CLANG_WARN_STRICT_PROTOTYPES = YES;
371 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
372 | CLANG_WARN_UNREACHABLE_CODE = YES;
373 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
374 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
375 | COPY_PHASE_STRIP = NO;
376 | DEBUG_INFORMATION_FORMAT = dwarf;
377 | ENABLE_STRICT_OBJC_MSGSEND = YES;
378 | ENABLE_TESTABILITY = YES;
379 | GCC_C_LANGUAGE_STANDARD = gnu99;
380 | GCC_DYNAMIC_NO_PIC = NO;
381 | GCC_NO_COMMON_BLOCKS = YES;
382 | GCC_OPTIMIZATION_LEVEL = 0;
383 | GCC_PREPROCESSOR_DEFINITIONS = (
384 | "DEBUG=1",
385 | "$(inherited)",
386 | );
387 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
388 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
389 | GCC_WARN_UNDECLARED_SELECTOR = YES;
390 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
391 | GCC_WARN_UNUSED_FUNCTION = YES;
392 | GCC_WARN_UNUSED_VARIABLE = YES;
393 | IPHONEOS_DEPLOYMENT_TARGET = 10.3;
394 | MTL_ENABLE_DEBUG_INFO = YES;
395 | ONLY_ACTIVE_ARCH = YES;
396 | SDKROOT = iphoneos;
397 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
398 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
399 | TARGETED_DEVICE_FAMILY = "1,2";
400 | };
401 | name = Debug;
402 | };
403 | 966025591FAC45A200BEA651 /* Release */ = {
404 | isa = XCBuildConfiguration;
405 | buildSettings = {
406 | ALWAYS_SEARCH_USER_PATHS = NO;
407 | CLANG_ANALYZER_NONNULL = YES;
408 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
409 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
410 | CLANG_CXX_LIBRARY = "libc++";
411 | CLANG_ENABLE_MODULES = YES;
412 | CLANG_ENABLE_OBJC_ARC = YES;
413 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
414 | CLANG_WARN_BOOL_CONVERSION = YES;
415 | CLANG_WARN_COMMA = YES;
416 | CLANG_WARN_CONSTANT_CONVERSION = YES;
417 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
418 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
419 | CLANG_WARN_EMPTY_BODY = YES;
420 | CLANG_WARN_ENUM_CONVERSION = YES;
421 | CLANG_WARN_INFINITE_RECURSION = YES;
422 | CLANG_WARN_INT_CONVERSION = YES;
423 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
424 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
425 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
426 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
427 | CLANG_WARN_STRICT_PROTOTYPES = YES;
428 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
429 | CLANG_WARN_UNREACHABLE_CODE = YES;
430 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
431 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
432 | COPY_PHASE_STRIP = NO;
433 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
434 | ENABLE_NS_ASSERTIONS = NO;
435 | ENABLE_STRICT_OBJC_MSGSEND = YES;
436 | GCC_C_LANGUAGE_STANDARD = gnu99;
437 | GCC_NO_COMMON_BLOCKS = YES;
438 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
439 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
440 | GCC_WARN_UNDECLARED_SELECTOR = YES;
441 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
442 | GCC_WARN_UNUSED_FUNCTION = YES;
443 | GCC_WARN_UNUSED_VARIABLE = YES;
444 | IPHONEOS_DEPLOYMENT_TARGET = 10.3;
445 | MTL_ENABLE_DEBUG_INFO = NO;
446 | SDKROOT = iphoneos;
447 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
448 | TARGETED_DEVICE_FAMILY = "1,2";
449 | VALIDATE_PRODUCT = YES;
450 | };
451 | name = Release;
452 | };
453 | 9660255B1FAC45A200BEA651 /* Debug */ = {
454 | isa = XCBuildConfiguration;
455 | buildSettings = {
456 | CLANG_ENABLE_MODULES = YES;
457 | DEVELOPMENT_TEAM = 387DLK86QQ;
458 | INFOPLIST_FILE = "$(SRCROOT)/Main/Info.plist";
459 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
460 | PRODUCT_BUNDLE_IDENTIFIER = com.powWong.imageEditor;
461 | PRODUCT_NAME = "$(TARGET_NAME)";
462 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
463 | SWIFT_VERSION = 3.0;
464 | };
465 | name = Debug;
466 | };
467 | 9660255C1FAC45A200BEA651 /* Release */ = {
468 | isa = XCBuildConfiguration;
469 | buildSettings = {
470 | CLANG_ENABLE_MODULES = YES;
471 | DEVELOPMENT_TEAM = 387DLK86QQ;
472 | INFOPLIST_FILE = "$(SRCROOT)/Main/Info.plist";
473 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
474 | PRODUCT_BUNDLE_IDENTIFIER = com.powWong.imageEditor;
475 | PRODUCT_NAME = "$(TARGET_NAME)";
476 | SWIFT_VERSION = 3.0;
477 | };
478 | name = Release;
479 | };
480 | /* End XCBuildConfiguration section */
481 |
482 | /* Begin XCConfigurationList section */
483 | 966025431FAC45A200BEA651 /* Build configuration list for PBXProject "imageEditor" */ = {
484 | isa = XCConfigurationList;
485 | buildConfigurations = (
486 | 966025581FAC45A200BEA651 /* Debug */,
487 | 966025591FAC45A200BEA651 /* Release */,
488 | );
489 | defaultConfigurationIsVisible = 0;
490 | defaultConfigurationName = Release;
491 | };
492 | 9660255A1FAC45A200BEA651 /* Build configuration list for PBXNativeTarget "imageEditor" */ = {
493 | isa = XCConfigurationList;
494 | buildConfigurations = (
495 | 9660255B1FAC45A200BEA651 /* Debug */,
496 | 9660255C1FAC45A200BEA651 /* Release */,
497 | );
498 | defaultConfigurationIsVisible = 0;
499 | defaultConfigurationName = Release;
500 | };
501 | /* End XCConfigurationList section */
502 | };
503 | rootObject = 966025401FAC45A200BEA651 /* Project object */;
504 | }
505 |
--------------------------------------------------------------------------------
/imageEditor/imageEditor.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/imageEditor/imageEditor.xcodeproj/project.xcworkspace/xcuserdata/pow.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaineWang/WPImageEditor/1c2cca3a61f7b533f79ecbf346b7cb97910dbdb0/imageEditor/imageEditor.xcodeproj/project.xcworkspace/xcuserdata/pow.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/imageEditor/imageEditor.xcodeproj/xcuserdata/pow.xcuserdatad/xcschemes/imageEditor.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
40 |
41 |
42 |
43 |
44 |
45 |
56 |
58 |
64 |
65 |
66 |
67 |
68 |
69 |
75 |
77 |
83 |
84 |
85 |
86 |
88 |
89 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/imageEditor/imageEditor.xcodeproj/xcuserdata/pow.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | imageEditor.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 966025471FAC45A200BEA651
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/imageEditor/kernel/WPAngleRuler.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WPAngleRuler.swift
3 | // WPImageEditor2
4 | //
5 | // Created by pow on 2017/11/20.
6 | // Copyright © 2017年 pow. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | let markCellIdentifier = "markCellIdentifier"
12 |
13 | protocol WPAngleRulerProtocol {
14 |
15 | func didScrollAngle(angle:CGFloat)
16 | func scrollRulerState(panGresture:UIPanGestureRecognizer)
17 |
18 | }
19 |
20 | class WPAngleRuler: UIView ,UICollectionViewDelegate,UICollectionViewDataSource{
21 |
22 |
23 | var rulerSources:[Int] = Array()
24 |
25 | var attachView:UIView?
26 | var lastclockwise_Angle:CGFloat = 0
27 |
28 | var delegate:WPAngleRulerProtocol?
29 |
30 | // point convet angle
31 | internal func AngleBetweenPoints(loacationPoint locapoint:CGPoint, startPoint:CGPoint, centerPonit:CGPoint)->CGFloat {
32 | return atan2(locapoint.y - centerPonit.y, locapoint.x - centerPonit.x) - atan2(startPoint.y - centerPonit.y, startPoint.x - centerPonit.x);
33 | }
34 |
35 | lazy var angleCollectionView:CricleCollectionView = {
36 | let layout:RadianLayout = RadianLayout.init()
37 | let coll = CricleCollectionView.init(frame:CGRect.init(origin: CGPoint.zero, size: self.frame.size), collectionViewLayout: layout)
38 | coll.backgroundColor = UIColor.clear
39 | coll.delegate = self
40 | coll.dataSource = self
41 | self.addSubview(coll)
42 |
43 | return coll
44 | }()
45 |
46 |
47 | init(rulerDataSource:[Int],rotatedView:UIView) {
48 | super.init(frame:CGRect.zero)
49 | self.bounds = CGRect.init(x: 0, y: 0, width: compute_Size().width, height: compute_Size().height)
50 | self.center = CGPoint.init(x:UIScreen.main.bounds.width/2, y:UIScreen.main.bounds.height+(self.bounds.height/10))
51 | // self.center = rotatedView.center
52 | self.rulerSources = rulerDataSource
53 | self.backgroundColor = UIColor.clear
54 | self.angleCollectionView.register(MarkCell.self, forCellWithReuseIdentifier:markCellIdentifier)
55 | let pan:UIPanGestureRecognizer = UIPanGestureRecognizer.init(target: self, action: #selector(panGestureRecognizer(pan:)))
56 | self.addGestureRecognizer(pan)
57 | let arr:UIImageView = UIImageView.init(image: UIImage.init(named: "arrow", in: Bundle.init(for: WPAngleRuler.self), compatibleWith: nil))
58 | arr.frame = CGRect.zero
59 | arr.center = CGPoint.init(x: self.bounds.width/2, y:50)
60 | arr.bounds = CGRect.init(x: 0, y: 0, width:15, height:15)
61 | self.addSubview(arr)
62 | self.attachView = rotatedView
63 | NotificationCenter.default.addObserver(self, selector: #selector(willChnagngeStatusBarFrame), name: NSNotification.Name.UIApplicationDidChangeStatusBarOrientation, object: nil)
64 |
65 |
66 | }
67 |
68 | @objc func willChnagngeStatusBarFrame() {
69 | self.center = CGPoint.init(x:UIScreen.main.bounds.width/2, y:UIScreen.main.bounds.height+(self.bounds.height/10))
70 |
71 | }
72 |
73 | func compute_Size() -> CGSize {
74 | var width:CGFloat,height:CGFloat
75 | let min:CGFloat = UIScreen.main.bounds.width > UIScreen.main.bounds.height ? UIScreen.main.bounds.height : UIScreen.main.bounds.width
76 | width = min/2
77 | height = width
78 | return CGSize.init(width: width, height: height)
79 |
80 | }
81 |
82 | required init?(coder aDecoder: NSCoder) {
83 | fatalError("init(coder:) has not been implemented")
84 | }
85 |
86 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
87 | return self.rulerSources.count
88 | }
89 |
90 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
91 |
92 | let cell:MarkCell = collectionView.dequeueReusableCell(withReuseIdentifier: markCellIdentifier, for: indexPath) as! MarkCell
93 | if self.rulerSources[indexPath.row]%18 == 0 {
94 | cell.addMarkNumber(angle:self.rulerSources[indexPath.row])
95 | if self.rulerSources[indexPath.row] == 90 || self.rulerSources[indexPath.row] == 180||self.rulerSources[indexPath.row] == 270||self.rulerSources[indexPath.row] == 0 {
96 | cell.markNumber.textColor = UIColor.orange
97 |
98 | }
99 | }
100 | cell.backgroundColor = UIColor.clear
101 | return cell
102 | }
103 |
104 | deinit {
105 | NotificationCenter.default.removeObserver(self)
106 | }
107 |
108 | @objc func panGestureRecognizer(pan:UIPanGestureRecognizer){
109 |
110 | if pan.location(in: self).x >= self.frame.width || pan.location(in: self).x <= 0 {
111 | return
112 | }
113 | let degree:CGFloat = self.frame.width/90
114 | let point:CGPoint = pan.translation(in: self)
115 | let angle = DegreesToRadians(Double(point.x/degree))
116 |
117 | if pan.state == .changed {
118 | self.angleCollectionView.transform = CGAffineTransform.init(rotationAngle: CGFloat(angle)+lastclockwise_Angle)
119 | delegate?.didScrollAngle(angle:CGFloat(angle)+lastclockwise_Angle)
120 | }else if pan.state == .ended {
121 | lastclockwise_Angle = CGFloat(angle)+lastclockwise_Angle
122 | }
123 | delegate?.scrollRulerState(panGresture: pan)
124 | }
125 |
126 |
127 |
128 | }
129 |
130 | class MarkCell: UICollectionViewCell {
131 |
132 | var markNumber:UILabel = UILabel.init(frame: CGRect.zero)
133 | func addMarkNumber(angle:Int) {
134 | self.markNumber.text = "\(angle)"
135 | self.markNumber.frame = self.bounds
136 | self.markNumber.backgroundColor = UIColor.clear
137 | self.markNumber.textColor = UIColor.WP_Color_Conversion("08006D")
138 | self.markNumber.textAlignment = .center
139 | self.markNumber.font = UIFont.systemFont(ofSize: 12)
140 | self.addSubview(markNumber)
141 |
142 |
143 | }
144 |
145 | }
146 |
147 | class RadianLayout: UICollectionViewLayout {
148 |
149 | private var layoutAttributes: [UICollectionViewLayoutAttributes] = []
150 | var center:CGPoint = CGPoint.zero
151 | var radius: CGFloat = 0.0
152 | var totalNum:Int = 0
153 |
154 | override func prepare() {
155 | super.prepare()
156 | totalNum = (collectionView?.numberOfItems(inSection: 0))!
157 | layoutAttributes = []
158 | center = CGPoint(x: Double(collectionView!.bounds.width * 0.5), y: Double(collectionView!.bounds.height * 0.5))
159 | radius = min(collectionView!.bounds.width, collectionView!.bounds.height)/3
160 | var indexPath: IndexPath
161 | for index in 0.. UICollectionViewLayoutAttributes? {
169 |
170 | let attributes :UICollectionViewLayoutAttributes = UICollectionViewLayoutAttributes.init(forCellWith: indexPath)
171 | attributes.size = CGSize.init(width: 50, height: 50)
172 | let angle = 2 * CGFloat(Double.pi) * CGFloat(indexPath.row) / CGFloat(totalNum)+CGFloat(Double.pi/2)
173 | attributes.center = CGPoint(x: center.x + radius*cos(-angle), y: center.y + radius*sin(-angle))
174 | attributes.transform = CGAffineTransform.init(rotationAngle:CGFloat(-angle+CGFloat(DegreesToRadians(90))))
175 | return attributes
176 | }
177 |
178 | override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
179 | return layoutAttributes
180 | }
181 |
182 | override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
183 | return true
184 | }
185 |
186 |
187 |
188 |
189 | }
190 |
191 | class CricleCollectionView: UICollectionView {
192 |
193 | override func draw(_ rect: CGRect) {
194 |
195 | let circlePath:UIBezierPath = UIBezierPath.init(arcCenter:CGPoint.init(x: self.bounds.width/2, y: self.bounds.height/2), radius: min(self.bounds.width, self.bounds.height) / 3.0-12, startAngle: 0, endAngle: CGFloat(Double.pi*2), clockwise: true)
196 | UIColor.WP_Color_Conversion("08006D").setStroke()
197 | circlePath.setLineDash([2], count:1, phase: 0)
198 | circlePath.lineJoinStyle = .round
199 | circlePath.lineWidth = 10
200 | circlePath.stroke()
201 |
202 | }
203 |
204 |
205 |
206 |
207 | }
208 |
209 |
210 |
--------------------------------------------------------------------------------
/imageEditor/kernel/WPBaseEditorView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WPBaseEditorView.swift
3 | // WPImageEditor2
4 | //
5 | // Created by pow on 2017/11/9.
6 | // Copyright © 2017年 pow. All rights reserved.
7 | //
8 |
9 |
10 | import UIKit
11 |
12 | enum undoKey:String {
13 | case image
14 | case center
15 | case orientation
16 | case transform
17 | }
18 |
19 | public typealias baseBlock = (UIImage,CGRect)->()
20 | public class WPBaseEditorView: UIView,WPSideMenuDelegate,WPAngleRulerProtocol {
21 |
22 | /**
23 | Free path view
24 | */
25 | @IBOutlet public weak var freePathView: WPFreePathView!
26 | /**
27 | photo library of original image
28 | */
29 | public var originalImage:UIImage?
30 | /**
31 | oringinalFrame
32 | */
33 | public var oringinalFrame:CGRect?
34 | /**
35 | rotated angle
36 | */
37 | public var baseViewangle:CGFloat?
38 |
39 | var sideMenu:WPSideMenu?
40 |
41 | var angleRulerView:WPAngleRuler?
42 | /**
43 | current crop view
44 | */
45 | public var currentCropView:WPCropEditorView?// current clip view
46 |
47 | @IBOutlet weak var shpaeImageView: WPShapeImageView!
48 |
49 | var undoPackage:[(image:UIImage,orientation:UIInterfaceOrientation,transform:CGAffineTransform,center:CGPoint)] = Array()
50 | var stepBystep:Int = 1
51 | /**
52 | call back block .return after clip image and current frame
53 | */
54 | public var pasteBlock:baseBlock?
55 |
56 | var moveGestureRecognizer:UIPanGestureRecognizer?
57 |
58 | public var moveableArea:CGRect?
59 |
60 | var undoIndex:Int = 0
61 |
62 | var isRotatedClip:Bool = false
63 |
64 | var willChangeRatio:CGRect = CGRect.zero
65 |
66 | var willChangeFrame:CGRect = CGRect.zero
67 | // isPortrait
68 | var initIsPortrait:Bool = UIApplication.shared.statusBarOrientation.isPortrait
69 |
70 | var imageMaxRatio:CGFloat = 1
71 |
72 | var currentIsPortrait:Bool = UIApplication.shared.statusBarOrientation.isPortrait
73 |
74 | override public func layoutSubviews() {
75 | if let frame = self.oringinalFrame {
76 | if !self.frame.equalTo(frame){
77 | self.sideMenu?.resetButton.isEnabled = true
78 | }
79 | }
80 | }
81 | }
82 | // MARK: - INIT -
83 | extension WPBaseEditorView {
84 |
85 | public class func loadNibFileToSetup(editorImage:UIImage,InSuperView:UIView)->WPBaseEditorView {
86 |
87 | var baseView:WPBaseEditorView?
88 | _ = UINib.init(nibName: nibName, instantiate: WPBaseEditorView.self) {
89 | (view) in
90 | baseView = view as? WPBaseEditorView
91 | }
92 | InSuperView.addSubview(baseView!)
93 | baseView?.loadsideMenuView()
94 | baseView?.originalImage = editorImage
95 | return baseView!
96 | }
97 |
98 | public func setup(){
99 | if let editorImage = self.originalImage {
100 | self.shpaeImageView.image = editorImage
101 | let bigBounds = CGRect.init(x: 0, y: 0, width:getSizeToFitScreen().width, height:getSizeToFitScreen().height)
102 | self.bounds = bigBounds
103 | self.center = (superview?.center)!
104 | self.oringinalFrame = self.frame
105 | addMoveGesture()
106 | saveToUndoPackage(image: editorImage)
107 | }
108 | }
109 | func updateLayout() {
110 | let undoTuples = self.undoPackage[undoIndex]
111 | var isMatch:Bool = false
112 | self.bounds = CGRect.init(origin: CGPoint.zero, size: self.willChangeFrame.size)
113 | if self.baseViewangle != nil && !isCut{
114 | updateScaleFit()
115 | }else {
116 | self.bounds = CGRect.init(origin: CGPoint.zero, size: getSizeToFitScreen(undoTuples.image))
117 | }
118 | if isRotatedClip {
119 | self.bounds = CGRect.init(origin: CGPoint.zero, size: self.shpaeImageView.frame.size)
120 | self.shpaeImageView.frame = CGRect.init(origin: CGPoint.zero, size: self.bounds.size)
121 | self.shpaeImageView.transform = CGAffineTransform.identity
122 | }
123 | if undoTuples.orientation.isPortrait == UIApplication.shared.statusBarOrientation.isPortrait {
124 | isMatch = true
125 | }else if undoTuples.orientation.isLandscape == UIApplication.shared.statusBarOrientation.isLandscape {
126 | isMatch = true
127 | }
128 | if isMatch {
129 | self.center = undoTuples.center
130 | }else{
131 | self.center = (superview?.center)!
132 | }
133 | self.oringinalFrame = self.frame
134 | if let crop = self.currentCropView {
135 | crop.frame = computeRatio(didChangeFrame: self.willChangeFrame, withFrame:crop.willChangeFrame)
136 | }
137 | }
138 | func updateScaleFit() {
139 | let canvasSize:CGSize = (self.moveableArea?.size)!
140 | let minCanvasValue:CGFloat = min((canvasSize.width), (canvasSize.height))
141 | let frameFit:CGSize = self.shpaeImageView.frame.size
142 | var ratio :CGFloat = 1
143 | if !currentIsPortrait { // landscape
144 | ratio = UIApplication.shared.statusBarOrientation.isPortrait ? min(canvasSize.width, canvasSize.height) / frameFit.width : min(canvasSize.width, canvasSize.height) / frameFit.height
145 | }else{
146 | ratio = UIApplication.shared.statusBarOrientation.isLandscape ? min(canvasSize.width, canvasSize.height)/frameFit.height : minCanvasValue / frameFit.width
147 | }
148 | let maxRatio:CGFloat = getMaxRatio()
149 | if ratio > maxRatio {
150 | ratio = maxRatio
151 | }
152 | self.shpaeImageView.transform = self.shpaeImageView.transform.scaledBy(x:ratio, y:ratio)
153 | }
154 | func updateScalefill() {
155 | let canvasSize:CGSize = (self.moveableArea?.size)!
156 | let minCanvasValue:CGFloat = min((canvasSize.width), (canvasSize.height))
157 | let minImageViewValue:CGFloat = UIApplication.shared.statusBarOrientation.isPortrait ? (self.shpaeImageView.frame.size.width) : (self.shpaeImageView.frame.size.height)
158 | var ratio:CGFloat = minCanvasValue/minImageViewValue
159 | let maxRatio:CGFloat = getMaxRatio()
160 | if ratio > maxRatio {
161 | ratio = maxRatio
162 | }
163 | self.currentIsPortrait = UIApplication.shared.statusBarOrientation.isPortrait
164 | self.shpaeImageView.transform = self.shpaeImageView.transform.scaledBy(x:ratio, y:ratio)
165 | }
166 |
167 | func getSizeToFitScreen() -> CGSize {
168 | if let image = originalImage {
169 | return getSizeToFitScreen(image)
170 | }
171 | return CGSize.zero
172 | }
173 |
174 | func getSizeToFitScreen(_ image: UIImage) -> CGSize {
175 | let scale = getScaleToFitScreen(image)
176 | return image.size.applying(CGAffineTransform(scaleX: scale, y: scale))
177 | }
178 |
179 | func getScaleToFitScreen(_ image: UIImage, isHorizontal: Bool = false) -> CGFloat {
180 | var maxWidth = UIScreen.main.bounds.width // Maximum width of image container
181 | var maxHeight = UIScreen.main.bounds.height // Maximum height of image container
182 | let imageOriginalSize = CGSize(width: image.scale * image.size.width, height: image.scale * image.size.height) // Original size of image.
183 | if let canvasSize = self.moveableArea {
184 | maxWidth = min(maxWidth, canvasSize.width)
185 | maxHeight = min(maxHeight, canvasSize.height)
186 | }
187 | let scaleX: CGFloat
188 | if isHorizontal {
189 | scaleX = maxWidth / imageOriginalSize.height
190 | } else {
191 | scaleX = maxWidth / imageOriginalSize.width
192 | }
193 | let scaleY: CGFloat
194 | if isHorizontal {
195 | scaleY = maxHeight / imageOriginalSize.width
196 | } else {
197 | scaleY = maxHeight / imageOriginalSize.height
198 | }
199 | return min(1, min(scaleX, scaleY))
200 | }
201 | func getImageIsHorizontal() -> Bool {
202 | let size:CGSize = (self.shpaeImageView.image?.size)!
203 | if size.width > size.height {
204 | return true
205 | }else{
206 | return false
207 | }
208 | }
209 | func getMaxRatio() -> CGFloat{
210 | let canvasSize:CGSize = (self.moveableArea?.size)!
211 | let imageSize:CGSize = (self.shpaeImageView.image?.size)!
212 | let baseBounds:CGSize = self.shpaeImageView.frame.size
213 | var maxRatio:CGFloat = 1
214 | if self.shpaeImageView.frame.size.width > imageSize.width || self.shpaeImageView.frame.size.height > imageSize.height {
215 |
216 | return maxRatio
217 | }
218 | var width:CGFloat ,height:CGFloat
219 | if UIApplication.shared.statusBarOrientation.isPortrait {
220 | width = canvasSize.height/baseBounds.width
221 | height = canvasSize.height/baseBounds.height
222 | }else{
223 | width = canvasSize.width/baseBounds.width
224 | height = canvasSize.width/baseBounds.height
225 | }
226 | maxRatio = min(width, height)
227 | return maxRatio
228 | }
229 | func computeRatio(didChangeFrame:CGRect,withFrame:CGRect) -> CGRect {
230 | self.willChangeRatio.size.width = withFrame.size.width/(didChangeFrame.size.width / self.frame.size.width)
231 | self.willChangeRatio.size.height = withFrame.size.height/(didChangeFrame.size.height / self.frame.size.height)
232 | self.willChangeRatio.origin.x = withFrame.origin.x/(didChangeFrame.size.width / self.frame.size.width)
233 | self.willChangeRatio.origin.y = withFrame.origin.y/(didChangeFrame.size.height / self.frame.size.height)
234 | return self.willChangeRatio
235 | }
236 |
237 | func getCurrentScreenSize(moveSize:CGSize) -> CGSize {
238 | var toScaleSize:CGSize?
239 | toScaleSize = UIScreen.main.bounds.width > UIScreen.main.bounds.height ? CGSize.init(width: moveSize.width, height: moveSize.height) : moveSize
240 | return toScaleSize!
241 | }
242 | }
243 | // MARK: - MOVE -
244 | extension WPBaseEditorView {
245 | func addMoveGesture() {
246 | if self.moveGestureRecognizer == nil {
247 | let moveGesture = UIPanGestureRecognizer(target: self, action: #selector(performMoveAction(_:)))
248 | moveGesture.minimumNumberOfTouches = 1
249 | self.moveGestureRecognizer = moveGesture
250 | self.addGestureRecognizer(moveGesture)
251 | }
252 | }
253 |
254 | func setMoveable(_ canMove: Bool) {
255 | self.moveGestureRecognizer?.isEnabled = canMove
256 | }
257 |
258 | @objc func performMoveAction(_ recognizer: UIPanGestureRecognizer) {
259 | let translation = recognizer.translation(in: self)
260 |
261 | switch recognizer.state {
262 | case .ended:
263 | saveToUndoPackage(image: self.shpaeImageView.image!)
264 | self.sideMenu?.undoButton.isEnabled = true
265 | undoIndex = undoPackage.count - 1
266 | break
267 |
268 | default:
269 | break
270 | }
271 | if let visibleRect = moveableArea {
272 | let location = self.frame.origin.applying(CGAffineTransform(translationX: translation.x, y: translation.y))
273 | var x = location.x
274 | var y = location.y
275 | let width = self.frame.width
276 | let height = self.frame.height
277 |
278 | if x < visibleRect.minX {
279 | x = visibleRect.minX
280 | }
281 | if y < visibleRect.minY {
282 | y = visibleRect.minY
283 | }
284 | if (x + width) > visibleRect.maxX {
285 | x = visibleRect.maxX - width
286 | }
287 | if (y + height) > visibleRect.maxY {
288 | y = visibleRect.maxY - height
289 | }
290 | self.center = CGPoint.init(x: x+(width/2), y: y+(height/2))
291 | }
292 |
293 | recognizer.setTranslation(CGPoint.zero, in: self)
294 | }
295 |
296 | }
297 | // MARK: PROTOCOL
298 | extension WPBaseEditorView {
299 |
300 | func didScrollAngle(angle: CGFloat) {
301 | baseViewangle = angle
302 | if isCut {
303 | self.shpaeImageView.transform = CGAffineTransform.init(rotationAngle: angle)
304 | }else{
305 | self.shpaeImageView.transform = CGAffineTransform.init(rotationAngle: angle)
306 | updateScalefill()
307 | }
308 | }
309 | func scrollRulerState(panGresture: UIPanGestureRecognizer) {
310 | if panGresture.state == .ended {
311 | saveToUndoPackage(image: self.shpaeImageView.image!)
312 | self.sideMenu?.undoButton.isEnabled = true
313 | self.sideMenu?.resetButton.isEnabled = true
314 | undoIndex = self.undoPackage.count - 1
315 | }
316 | }
317 | func onClick_SideMenu_SubAction_Events(type: String, sender: UIButton) {
318 |
319 | switch type {
320 | case OperationType.rotation.rawValue:
321 | selectedActionType = type
322 | addRotationView()
323 | break
324 | case OperationType.rotateRatio.rawValue:
325 | selectedActionType = type
326 | if let crop = self.currentCropView {
327 | crop.rotateRatio()
328 | }
329 | break
330 | case SubActionType.rotate_right.rawValue:
331 | selectedActionType = type
332 | if let angle = baseViewangle {
333 | var d_Value:Int = Int(RadiansToDegrees(Double(abs(angle))))
334 | baseViewangle = CGFloat(DegreesToRadians(Double(d_Value.maxAngleValue())))
335 | }else{
336 | baseViewangle = CGFloat(DegreesToRadians(90))
337 | }
338 | self.angleRulerView?.angleCollectionView.transform = CGAffineTransform.init(rotationAngle:baseViewangle!)
339 | self.angleRulerView?.lastclockwise_Angle = baseViewangle!
340 | if isCut {
341 | self.shpaeImageView.transform = (self.angleRulerView?.angleCollectionView.transform)!
342 | }else{
343 | self.shpaeImageView.transform = (self.angleRulerView?.angleCollectionView.transform)!
344 | updateScalefill()
345 | }
346 | saveToUndoPackage(image: self.shpaeImageView.image!)
347 | undoIndex = undoPackage.count - 1
348 | if RadiansToDegrees(Double(baseViewangle!)) == 360 {
349 | baseViewangle = nil
350 | }
351 | self.sideMenu?.undoButton.isEnabled = true
352 | self.sideMenu?.resetButton.isEnabled = true
353 | break
354 | case SubActionType.rotate_left.rawValue:
355 | selectedActionType = type
356 | if let angle = baseViewangle {
357 | var d_Value:Int = Int(RadiansToDegrees(Double(abs(angle))))
358 | baseViewangle = -CGFloat(DegreesToRadians(Double(d_Value.maxAngleValue())))
359 | }else{
360 | baseViewangle = -CGFloat(DegreesToRadians(90))
361 | }
362 | self.angleRulerView?.angleCollectionView.transform = CGAffineTransform.init(rotationAngle:baseViewangle!)
363 | self.angleRulerView?.lastclockwise_Angle = baseViewangle!
364 | if isCut {
365 | self.shpaeImageView.transform = (self.angleRulerView?.angleCollectionView.transform)!
366 | }else{
367 | self.shpaeImageView.transform = (self.angleRulerView?.angleCollectionView.transform)!
368 | updateScalefill()
369 | }
370 | saveToUndoPackage(image: self.shpaeImageView.image!)
371 | undoIndex = undoPackage.count - 1
372 | if RadiansToDegrees(Double(baseViewangle!)) == -360 {
373 | baseViewangle = nil
374 | }
375 | self.sideMenu?.undoButton.isEnabled = true
376 | self.sideMenu?.resetButton.isEnabled = true
377 | break
378 | case SubActionType.fullCanvas_scaleFit.rawValue:
379 | selectedActionType = type
380 | let undoTuples = self.undoPackage[undoIndex]
381 | self.shpaeImageView.image = UIImage.imageRotatedByDegrees(self.shpaeImageView.image!, degrees:acos(undoTuples.transform.a))
382 | self.shpaeImageView.image = UIImage.imageWithImage(undoTuples.image , scaledToFitToSize:getCurrentScreenSize(moveSize: (self.moveableArea?.size)!))
383 | self.bounds = CGRect.init(x: 0, y: 0, width: (self.shpaeImageView.image?.size.width)!, height: (self.shpaeImageView.image?.size.height)!)
384 | self.center = (superview?.center)!
385 | break
386 | case SubActionType.fullCanvas_scaleFill.rawValue:
387 | selectedActionType = type
388 | let undoTuples = self.undoPackage[undoIndex]
389 | self.shpaeImageView.image = UIImage.imageRotatedByDegrees(self.shpaeImageView.image!, degrees:acos(undoTuples.transform.a))
390 | let fillRect:CGRect = CGRect.init(origin: CGPoint.zero, size: getCurrentScreenSize(moveSize: (self.moveableArea?.size)!))
391 | self.shpaeImageView.image = UIImage.imageWithImage(undoTuples.image, scaledToSize:fillRect.size, inRect:fillRect)
392 | self.bounds = CGRect.init(x: 0, y: 0, width: (self.shpaeImageView.image?.size.width)!, height: (self.shpaeImageView.image?.size.height)!)
393 | self.center = (superview?.center)!
394 | break
395 | case OperationType.crop.rawValue:
396 | selectedActionType = type
397 | break
398 | case OperationType.center.rawValue:
399 | selectedActionType = type
400 | self.center = (superview?.center)!
401 | break
402 | case SubActionType.subCancel.rawValue:
403 | selectedActionType = type
404 | self.currentCropView?.drawBackgroudView?.removeFromSuperview()
405 | self.currentCropView?.removeFromSuperview()
406 | self.currentCropView = nil
407 | self.freePathView.isHidden = true
408 | NotificationCenter.default.removeObserver(self.angleRulerView as Any)
409 | self.angleRulerView?.removeFromSuperview()
410 | self.angleRulerView = nil
411 | setMoveable(true)
412 | self.shpaeImageView.image = self.originalImage
413 | let bigBounds = CGRect.init(x: 0, y: 0, width:getSizeToFitScreen().width, height:getSizeToFitScreen().height)
414 | self.bounds = bigBounds
415 | self.shpaeImageView.transform = CGAffineTransform.identity
416 | isRotatedClip = false
417 | self.baseViewangle = nil
418 | break
419 | case SubActionType.rotateCancel.rawValue:
420 | selectedActionType = type
421 | NotificationCenter.default.removeObserver(self.angleRulerView as Any)
422 | self.angleRulerView?.removeFromSuperview()
423 | self.angleRulerView = nil
424 | setMoveable(true)
425 | break
426 | case OperationType.cancel.rawValue:
427 | selectedActionType = type
428 | self.pasteBlock!(UIImage(),CGRect.zero)
429 | self.removeAllEvents()
430 | break
431 | case OperationType.reset.rawValue:
432 | self.reset()
433 | break
434 | case SubActionType.clip.rawValue:
435 | selectedActionType = type
436 | if !self.freePathView.isHidden {
437 | self.cropFrreShape()
438 | }else{
439 | self.cropShpae()
440 | }
441 | saveToUndoPackage(image: self.shpaeImageView.image!)
442 | undoIndex = undoPackage.count - 1
443 | self.sideMenu?.undoButton.isEnabled = true
444 | self.sideMenu?.redoButton.isEnabled = false
445 | self.sideMenu?.rotateRatioButton.isHidden = true
446 | setMoveable(true)
447 | isRotatedClip = false
448 | break
449 | case OperationType.undo.rawValue:
450 | if !self.freePathView.isHidden {
451 | self.freePathView.undoPath(sender: (self.sideMenu?.undoButton)!)
452 | }else{
453 | undoAction(sender)
454 | }
455 | break
456 | case OperationType.redo.rawValue:
457 | redoAction(sender)
458 | break
459 | case OperationType.paste.rawValue:
460 | pasteImage()
461 | break
462 | default:
463 | selectedActionType = type
464 | self.addCropView(type:type)
465 | }
466 | }
467 |
468 | func pasteImage() {
469 | var pasteImage:UIImage?
470 | self.shpaeImageView.image = UIImage.imageRotatedByDegrees(self.shpaeImageView.image!, degrees: (baseViewangle != nil) ? baseViewangle! : 0)
471 | pasteImage = UIImage.imageWithImage(self.shpaeImageView.image!, scaledToSize: self.shpaeImageView.frame.size, inRect:CGRect.init(origin: CGPoint.zero, size: self.shpaeImageView.frame.size))
472 | let origin:CGPoint = self.convert(self.shpaeImageView.center, to: self.superview)
473 | let cropSize:CGSize = (pasteImage?.size)!
474 | var cropFrame:CGRect = CGRect.init(origin:CGPoint.init(x: origin.x - (cropSize.width/2), y: origin.y - (cropSize.height/2)), size:cropSize)
475 | if cropFrame.origin.x < 0 || cropFrame.origin.y < 0 {
476 | cropFrame.origin.x = 0 ; cropFrame.origin.y = 0
477 | }
478 | let currentRect:CGSize = getCurrentScreenSize(moveSize: (self.moveableArea?.size)!)
479 | if cropFrame.size.width > currentRect.width {
480 | cropFrame.size.width = currentRect.width
481 | }
482 | if cropFrame.size.height > currentRect.height {
483 | cropFrame.size.height = currentRect.height
484 | }
485 | if let block = self.pasteBlock {
486 | block(pasteImage!,cropFrame)
487 | }
488 | self.removeAllEvents()
489 | }
490 |
491 | }
492 | // MARK: - UNDO & REDO -
493 | extension WPBaseEditorView {
494 |
495 | func saveToUndoPackage(image:UIImage) {
496 | if (self.sideMenu?.redoButton?.isEnabled)! {
497 | self.undoPackage.removeSubrange(Range.init(NSRange.init(location:undoPackage.count - self.stepBystep, length: self.stepBystep))!)
498 | self.stepBystep = 0
499 | }
500 | self.undoPackage.append((image,UIApplication.shared.statusBarOrientation,self.shpaeImageView.transform,self.center))
501 | self.sideMenu?.redoButton.isEnabled = false
502 | }
503 |
504 | func undoAction(_ sender:UIButton) {
505 | self.sideMenu?.redoButton?.isEnabled = true
506 | self.stepBystep += 1
507 | if self.stepBystep == undoPackage.count {
508 | sender.isEnabled = false
509 | }
510 | updateUI(undoObject:undoPackage[undoPackage.count - self.stepBystep])
511 | undoIndex = undoPackage.count - self.stepBystep
512 | }
513 |
514 | func redoAction(_ sender:UIButton) {
515 |
516 | self.sideMenu?.undoButton?.isEnabled = true
517 | self.stepBystep -= 1
518 | if self.stepBystep == 1 {
519 | sender.isEnabled = false
520 | }
521 |
522 | updateUI(undoObject: undoPackage[undoPackage.count - self.stepBystep])
523 | undoIndex = undoPackage.count - self.stepBystep
524 | }
525 |
526 | func updateUI(undoObject:(UIImage,UIInterfaceOrientation,CGAffineTransform,CGPoint)) {
527 | var isMatch:Bool = false
528 | self.shpaeImageView.image = undoObject.0
529 | self.shpaeImageView.transform = undoObject.2
530 | if let angleRuler = self.angleRulerView {
531 | angleRuler.angleCollectionView.transform = CGAffineTransform.init(rotationAngle: acos(undoObject.2.a))
532 | }
533 | self.baseViewangle = acos(self.shpaeImageView.transform.a)
534 | if undoObject.1.isPortrait == UIApplication.shared.statusBarOrientation.isPortrait {
535 | isMatch = true
536 | }
537 | if undoObject.1.isLandscape == UIApplication.shared.statusBarOrientation.isLandscape {
538 | isMatch = true
539 | }
540 | if isMatch {
541 | let undoCenter:CGPoint = undoObject.3
542 | self.center = undoCenter
543 | }else{
544 | self.center = (superview?.center)!
545 | }
546 | self.bounds = CGRect.init(origin: CGPoint.zero, size:getSizeToFitScreen(undoObject.0))
547 | self.sideMenu?.resetButton.isEnabled = (self.sideMenu?.undoButton.isEnabled)!
548 |
549 | }
550 |
551 | }
552 | //MARK: - ADD VIEW -
553 | extension WPBaseEditorView {
554 | // add top menu
555 | func loadsideMenuView() {
556 |
557 | self.sideMenu = WPSideMenu.loadsideMenu(CGRect.init(x:0, y:UIScreen.main.bounds.size.height - 64, width: UIScreen.main.bounds.size.width, height:64))
558 | superview?.insertSubview(self.sideMenu!, aboveSubview: self)
559 | self.sideMenu?.delegate = self
560 | NotificationCenter.default.addObserver(self, selector: #selector(didChangeStatusBarOrientation), name: NSNotification.Name.UIApplicationDidChangeStatusBarOrientation, object: nil)
561 |
562 | NotificationCenter.default.addObserver(self, selector: #selector(willChangeStatusBarOrientation), name: NSNotification.Name.UIApplicationWillChangeStatusBarOrientation, object: nil)
563 |
564 | }
565 | @objc func didChangeStatusBarOrientation() {
566 | self.moveableArea = self.moveableArea?.applyingInversed()
567 | self.updateLayout()
568 | if let side = self.sideMenu {
569 | side.frame = CGRect.init(x:0, y:UIScreen.main.bounds.height-64, width:UIScreen.main.bounds.width, height:64)
570 | }
571 | }
572 | @objc func willChangeStatusBarOrientation() {
573 | self.willChangeFrame = self.frame
574 | if let crop = self.currentCropView {
575 | crop.willChangeFrame = crop.frame
576 | }
577 | }
578 |
579 |
580 | func addCropView(type:String) {
581 |
582 | setMoveable(false)
583 | isCut = false
584 | self.sideMenu?.rotateRatioButton.isHidden = true
585 |
586 | if type.isEqual(SubActionType.Free_S.rawValue)||type.isEqual(SubActionType.Free_C.rawValue) {
587 | if let cropView = self.currentCropView {
588 | cropView.drawBackgroudView?.removeFromSuperview()
589 | cropView.removeFromSuperview()
590 | self.currentCropView = nil
591 | }
592 | self.freePathView.isHidden = false
593 | self.freePathView.baseImage = self.originalImage
594 | self.freePathView.sideMenu_undoButton = self.sideMenu?.undoButton
595 | self.freePathView.sideMenu_clipButton = self.sideMenu?.doneButton
596 | self.freePathView.freePathName = NSString.init(string: type)
597 | self.sideMenu?.doneButton.isEnabled = false
598 | self.sideMenu?.closePolygonCountView()
599 | }else{
600 |
601 | if let cropView = self.currentCropView {
602 | cropView.initShapeLayout(type: type)
603 | self.sideMenu?.rotateRatioButton.isHidden = true
604 | if !self.freePathView.isHidden {
605 | self.freePathView.isHidden = true
606 | }
607 | if !type.isEqual(SubActionType.crop_polygon.rawValue) {
608 | cropView.polygonLayer.removeFromSuperlayer()
609 | self.sideMenu?.closePolygonCountView()
610 | }
611 | if type.isEqual(SubActionType.crop_43.rawValue) || type.isEqual(SubActionType.crop_169.rawValue) || type.isEqual(SubActionType.crop_image.rawValue) || type.isEqual(SubActionType.crop_canvas.rawValue) {
612 | self.sideMenu?.rotateRatioButton.isHidden = false
613 | }
614 | return
615 | }else{
616 | self.sideMenu?.resetButton.isEnabled = true
617 | self.sideMenu?.undoButton.isEnabled = false
618 | self.sideMenu?.redoButton.isEnabled = false
619 | self.freePathView.isHidden = true
620 | self.sideMenu?.doneButton.isEnabled = true
621 | if type.isEqual(SubActionType.crop_43.rawValue) || type.isEqual(SubActionType.crop_169.rawValue) || type.isEqual(SubActionType.crop_image.rawValue) || type.isEqual(SubActionType.crop_canvas.rawValue) {
622 | self.sideMenu?.rotateRatioButton.isHidden = false
623 | }
624 | isRotatedClip = false
625 | if let angle = self.baseViewangle {
626 | self.bounds = CGRect.init(origin: CGPoint.zero, size: self.shpaeImageView.frame.size)
627 | self.shpaeImageView.frame = CGRect.init(origin: CGPoint.zero, size: self.bounds.size)
628 | self.shpaeImageView.image = UIImage.imageRotatedByDegrees(self.shpaeImageView.image!, degrees: angle)
629 | self.shpaeImageView.transform = CGAffineTransform.identity
630 | isRotatedClip = true
631 | }
632 | self.currentCropView = WPCropEditorView.loadNibFileToCropView(onTheSuperView: self)
633 | self.currentCropView?.initShapeLayout(type: type)
634 | }
635 | }
636 | }
637 |
638 | @objc func add_Count_Polygon_Action(_ sender:UIButton) {
639 | self.currentCropView?.addAndDecrease_Polygon_Action(sender)
640 | }
641 | // add rotationview
642 | func addRotationView() {
643 | var sourc:[Int] = Array()
644 | for index in 0..<20 {
645 | sourc.append(index*18)
646 | }
647 | self.angleRulerView = WPAngleRuler.init(rulerDataSource: sourc, rotatedView: self)
648 | angleRulerView?.delegate = self
649 | self.superview?.insertSubview(self.angleRulerView!, belowSubview: self.sideMenu!)
650 | }
651 |
652 |
653 | // add free path clip View to be main view
654 | func cropFrreShape() {
655 | self.shpaeImageView.lastPath = self.freePathView.getCurrentPath()
656 | self.freePathView.isHidden = true
657 | self.shpaeImageView.image = self.shpaeImageView.pasteImage()
658 | let cutImage:UIImage = (self.shpaeImageView.cuting())
659 | let convertFrame = self.convert((self.shpaeImageView.lastPath?.bounds)!, to: self.superview)
660 | self.frame = convertFrame
661 | self.shpaeImageView.image = cutImage
662 | self.shpaeImageView.backgroundColor = UIColor.clear
663 | self.shpaeImageView.transform = CGAffineTransform.identity
664 | isCut = true
665 | baseViewangle = nil
666 | }
667 |
668 | // add clip view to superView later operation
669 | func cropShpae(){
670 |
671 | self.currentCropView?.removeFromSuperview()
672 | let sourceImageRef: CGImage = self.shpaeImageView.pasteImage().cgImage!
673 | isCut = true
674 | let newCGImage :CGImage = sourceImageRef.cropping(to:(self.currentCropView?.frame)!)!
675 | let newImage:UIImage = UIImage.init(cgImage: newCGImage)
676 | self.shpaeImageView.image = newImage
677 | self.shpaeImageView.lastPath = self.currentCropView?.crop_shapePath
678 | let cutImage:UIImage = (self.shpaeImageView.cuting())
679 | self.shpaeImageView.image = cutImage
680 | let convertPoint:CGPoint = self.convert((self.currentCropView?.center)!, to: self.superview)
681 | var convertSize:CGSize = cutImage.size
682 | if (self.currentCropView?.crop_shapeName.isEqual(SubActionType.crop_polygon.rawValue))! {
683 | convertSize = cutImage.size
684 | }
685 | self.bounds = CGRect.init(origin: CGPoint.zero, size: convertSize)
686 | self.center = convertPoint
687 | self.currentCropView?.drawBackgroudView?.removeFromSuperview()
688 | self.currentCropView?.removeFromSuperview()
689 | self.currentCropView = nil
690 | self.shpaeImageView.transform = CGAffineTransform.identity
691 | baseViewangle = nil
692 |
693 |
694 | }
695 | // over method
696 | func removeAllEvents() {
697 | self.sideMenu?.rotateRatioButton.isHidden = true
698 | self.sideMenu?.removeFromSuperview()
699 | self.angleRulerView?.removeFromSuperview()
700 | DispatchQueue._onceTracker = [String]()
701 | NotificationCenter.default.removeObserver(self)
702 | self.removeFromSuperview()
703 | selectedActionType = OperationType.unknown.rawValue
704 | self.angleRulerView = nil
705 | self.baseViewangle = nil
706 | isCut = false
707 | isRotatedClip = false
708 | }
709 |
710 | func reset() {
711 | self.undoPackage.removeAll()
712 | undoIndex = 0
713 | self.sideMenu?.redoButton.isEnabled = false
714 | self.sideMenu?.undoButton.isEnabled = false
715 | self.sideMenu?.rotateRatioButton.isHidden = true
716 | if let cropView = self.currentCropView {
717 | cropView.drawBackgroudView?.removeFromSuperview()
718 | cropView.removeFromSuperview()
719 | self.currentCropView = nil
720 | }
721 | if let editorImage = self.originalImage {
722 | self.shpaeImageView.image = editorImage
723 | self.bounds = CGRect.init(origin: CGPoint.zero, size: getSizeToFitScreen())
724 | self.center = (superview?.center)!
725 | self.shpaeImageView.transform = CGAffineTransform.identity
726 | saveToUndoPackage(image: editorImage)
727 | }
728 | if !self.freePathView.isHidden {
729 | self.freePathView.isHidden = true
730 | }
731 | if self.angleRulerView != nil {
732 | self.angleRulerView?.angleCollectionView.transform = CGAffineTransform.identity
733 | self.angleRulerView?.lastclockwise_Angle = 0
734 | self.angleRulerView?.removeFromSuperview()
735 | self.angleRulerView = nil
736 | }
737 | self.baseViewangle = nil
738 | setMoveable(true)
739 | isCut = false
740 | }
741 |
742 | }
743 |
744 | class WPShapeImageView: UIImageView {
745 |
746 | public var lastPath:UIBezierPath?
747 | // free path clip image
748 | public func cuting() -> UIImage {
749 |
750 | var imgSize:CGSize = CGSize.zero
751 | imgSize = (self.frame.size)
752 | UIGraphicsBeginImageContextWithOptions(imgSize, false, 1)
753 | lastPath?.addClip()
754 | self.image?.draw(at: CGPoint.zero)
755 | let newImage2 = UIGraphicsGetImageFromCurrentImageContext()
756 | UIGraphicsBeginImageContextWithOptions(imgSize, false, 1)
757 | let sourceImageRef: CGImage = (newImage2!.cgImage!)// next In accordance with the path bouns .clip image szie
758 | let newCGImage :CGImage = sourceImageRef.cropping(to:(self.lastPath?.bounds)!)!
759 | let newImage:UIImage = UIImage.init(cgImage: newCGImage)
760 | return newImage
761 | }
762 |
763 | func pasteImage() -> UIImage {
764 | let imageSize = self.bounds.size
765 | var screen:UIView = self
766 | if !isCut {
767 | screen = self.superview!
768 | }
769 | UIGraphicsBeginImageContextWithOptions(imageSize, false,1)
770 | screen.layer.render(in: UIGraphicsGetCurrentContext()!)
771 | let layerImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
772 | UIGraphicsEndImageContext()
773 | return layerImage
774 | }
775 |
776 | }
777 |
778 |
779 |
780 |
--------------------------------------------------------------------------------
/imageEditor/kernel/WPCropEditorView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WPCropEditorView.swift
3 | // WPImageEditor2
4 | //
5 | // Created by pow on 2017/11/9.
6 | // Copyright © 2017年 pow. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | // FIXME: do make clip drawing path
12 | public class WPCropEditorView: UIView {
13 |
14 | // drawing path type name
15 | var crop_shapeName:String = ""
16 | // drawing path of self
17 | var crop_shapePath:UIBezierPath = UIBezierPath()
18 | // spaceing
19 | let space:CGFloat = 1
20 | // setup min value count of the polygon
21 | var polygonCount = 5
22 | // degree measure of the polygon
23 | var degreeMeasure:Double?
24 | /*------------------------------------------------------------------*/
25 | /**XIB file in property **/
26 | @IBOutlet weak var controlSizeButton_0: UIButton!// left top
27 | @IBOutlet weak var controlSizeButton_2: UIButton!// left bottom
28 | @IBOutlet weak var controlSizeButton_5: UIButton!// right top
29 | @IBOutlet weak var controlSizeButton_7: UIButton!// right bottom
30 | /**XIB file in internal hidden property **/
31 | @IBOutlet weak var leftBottom: UILabel!
32 | @IBOutlet weak var left_leftBottom: UILabel!
33 | @IBOutlet weak var rightBottom: UILabel!
34 | @IBOutlet weak var right_rightBottom: UILabel!
35 | @IBOutlet weak var topRight: UILabel!
36 | @IBOutlet weak var right_topBottom: UILabel!
37 | @IBOutlet weak var leftTop: UILabel!
38 | @IBOutlet weak var left_TopBottom: UILabel!
39 | var isMove:Bool? // is out edge
40 | var edgePoint:CGPoint = CGPoint.zero // edge point
41 | var currentFrame:CGRect = CGRect.zero // current frame
42 | var toScale:Bool = true
43 | var lockX:CGFloat?
44 | var lockY:CGFloat?
45 | var lockWidth:CGFloat = 0
46 | var lockheight:CGFloat = 0
47 | var maxRatio:CGFloat?
48 | var polygonLayer:CAShapeLayer = CAShapeLayer()
49 | var drawBackgroudView:WPDrawingView?
50 | var willChangeFrame:CGRect = CGRect.zero
51 |
52 | public override var bounds: CGRect {
53 | didSet{
54 | self.setNeedsLayout()
55 | }
56 | }
57 |
58 | override public func draw(_ rect: CGRect) {
59 | crop_shapePath.removeAllPoints()
60 | // update out side obscure view
61 | self.draw_BaseView_Outside_BezierPath()
62 | // main view path update
63 | switch crop_shapeName {
64 | case SubActionType.Cercle.rawValue:
65 | crop_shapePath = UIBezierPath.init(arcCenter:CGPoint.init(x: self.frame.width/2, y: self.frame.height/2), radius:self.frame.width/2-space, startAngle: 0, endAngle: CGFloat(Double.pi)*2, clockwise: true)
66 | break
67 | case SubActionType.Rectangle.rawValue:
68 | crop_shapePath = UIBezierPath(rect: CGRect.init(x: 0, y: 0, width: rect.width, height: rect.height))
69 | break
70 | case SubActionType.Triangle.rawValue:
71 | crop_shapePath.move(to: CGPoint.init(x: rect.width-space, y: rect.height-space))
72 | crop_shapePath.addLine(to: CGPoint.init(x: rect.width/2, y:space))
73 | crop_shapePath.addLine(to: CGPoint.init(x: space, y:rect.height-space))
74 | crop_shapePath.addLine(to: CGPoint.init(x: rect.width-space, y:rect.height-space))
75 | break
76 | case SubActionType.crop_43.rawValue:
77 | crop_shapePath = UIBezierPath.init(rect: rect)
78 | break
79 | case SubActionType.crop_169.rawValue:
80 | crop_shapePath = UIBezierPath.init(rect: rect)
81 | break
82 | case SubActionType.crop_image.rawValue:
83 | crop_shapePath = UIBezierPath.init(rect: rect)
84 | break
85 | case SubActionType.crop_canvas.rawValue:
86 | crop_shapePath = UIBezierPath.init(rect: rect)
87 | break
88 | case SubActionType.Oval.rawValue:
89 | crop_shapePath = UIBezierPath.init(ovalIn: CGRect.init(x: space, y: space, width: rect.width-space*2, height: rect.height-space*2))
90 | break
91 | case SubActionType.crop_polygon.rawValue:
92 | crop_shapePath = getPolygonPath(rect: rect)
93 | polygonLayer.lineWidth = 2
94 | polygonLayer.strokeColor = UIColor.orange.cgColor
95 | polygonLayer.fillColor = UIColor.clear.cgColor
96 | polygonLayer.bounds = crop_shapePath.bounds
97 | polygonLayer.frame = CGRect.init(x: (rect.width - crop_shapePath.bounds.width)/2, y: (rect.height - crop_shapePath.bounds.height)/2, width: crop_shapePath.bounds.width, height: crop_shapePath.bounds.height)
98 | polygonLayer.path = crop_shapePath.cgPath
99 | break
100 | case SubActionType.crop_free.rawValue:
101 | crop_shapePath = UIBezierPath(rect: CGRect.init(x: 0, y: 0, width:rect.size.width, height:rect.height))
102 | break
103 | default:
104 | print("not type")
105 | }
106 | if !crop_shapeName.isEqual(SubActionType.crop_polygon.rawValue) {
107 | UIColor.orange.setStroke()
108 | crop_shapePath.lineWidth = 2
109 | crop_shapePath.stroke()
110 | }
111 | }
112 |
113 |
114 | override public func layoutSubviews() {
115 | self.drawBackgroudView?.frame = CGRect.init(origin: CGPoint.zero, size: (self.superview?.bounds.size)!)
116 | self.setNeedsDisplay()
117 | }
118 | // init loading nib file to be WPCropEditorView class object
119 | class func loadNibFileToCropView(onTheSuperView:WPBaseEditorView)->WPCropEditorView {
120 |
121 | var crop:WPCropEditorView?
122 | _ = UINib.init(nibName:nibName, instantiate: WPCropEditorView.self) {
123 | (view) in
124 | crop = view as? WPCropEditorView
125 | }
126 | isCut = false
127 | crop?.drawBackgroudView = WPDrawingView(frame:onTheSuperView.bounds)
128 | onTheSuperView.addSubview((crop?.drawBackgroudView!)!)
129 | onTheSuperView.addSubview(crop!)
130 | return crop!
131 | }
132 | // init shape layout
133 | func initShapeLayout(type:String) {
134 |
135 | crop_shapeName = type
136 | self.toScale = true
137 | switch type {
138 | case SubActionType.crop_free.rawValue:
139 | self.toScale = false
140 | self.bounds = CGRect.init(origin: CGPoint.zero, size: (self.superview?.frame.size)!)
141 | break
142 | case SubActionType.crop_image.rawValue:
143 | let baseView:WPBaseEditorView = (self.superview as! WPBaseEditorView)
144 | maxRatio = max((baseView.originalImage?.size.width)!, (baseView.originalImage?.size.height)!)/min ((baseView.originalImage?.size.width)!, (baseView.originalImage?.size.height)!)
145 | let proSize:CGSize = baseView.bounds.size.proportionSize(p: maxRatio!)
146 | self.bounds = CGRect.init(origin: CGPoint.zero, size:proSize)
147 | break
148 | case SubActionType.crop_canvas.rawValue:
149 | let baseView:WPBaseEditorView = (self.superview as! WPBaseEditorView)
150 | maxRatio = max((baseView.moveableArea?.size.width)!, (baseView.moveableArea?.size.height)!)/min ((baseView.moveableArea?.size.width)!, (baseView.moveableArea?.size.height)!)
151 | let proSize:CGSize = baseView.bounds.size.proportionSize(p: maxRatio!)
152 | self.bounds = CGRect.init(origin: CGPoint.zero, size:proSize)
153 | break
154 | case SubActionType.Oval.rawValue:
155 | self.toScale = false
156 | self.bounds = CGRect.init(origin: CGPoint.zero, size:(self.superview?.frame.size)!)
157 | break
158 | case SubActionType.crop_polygon.rawValue:
159 | let minSize = min((self.superview?.frame.size.width)!, (self.superview?.frame.size.height)!)
160 | self.layer.addSublayer(polygonLayer)
161 | self.bounds = CGRect.init(origin: CGPoint.zero, size:CGSize.init(width:minSize, height: minSize))
162 | break
163 | case SubActionType.crop_43.rawValue:
164 | self.bounds = CGRect.init(origin: CGPoint.zero, size: (self.superview?.frame.size)!)
165 | let proSize = self.frame.size.proportionSize(p: 4/3)
166 | maxRatio = 4/3
167 | self.bounds = CGRect.init(origin: CGPoint.zero, size:CGSize.init(width: proSize.width, height: proSize.height))
168 | break
169 | case SubActionType.crop_169.rawValue:
170 | self.bounds = CGRect.init(origin: CGPoint.zero, size: (self.superview?.frame.size)!)
171 | let proSize = self.frame.size.proportionSize(p: 16/9)
172 | maxRatio = 16/9
173 | self.bounds = CGRect.init(origin: CGPoint.zero, size:CGSize.init(width: proSize.width, height: proSize.height))
174 | break
175 | default:
176 | let minSize:CGFloat = min((self.superview?.frame.width)!, (self.superview?.frame.height)!)
177 | self.bounds = CGRect.init(origin: CGPoint.zero, size:CGSize.init(width: minSize, height: minSize))
178 | }
179 |
180 | self.center = CGPoint.init(x: (self.superview?.frame.width)!/2, y: (self.superview?.frame.height)!/2)
181 |
182 |
183 | }
184 | // MARK: - rotateRatio -
185 | func rotateRatio() {
186 | let scaleSize:CGSize = self.superview!.frame.size
187 | let baseView:WPBaseEditorView = (self.superview as! WPBaseEditorView)
188 | var maxValue:CGFloat?, minValue:CGFloat?
189 | if crop_shapeName.isEqual(SubActionType.crop_169.rawValue) {
190 | maxValue = 16
191 | minValue = 9
192 | }else if crop_shapeName.isEqual(SubActionType.crop_43.rawValue) {
193 | maxValue = 4
194 | minValue = 3
195 | }else if crop_shapeName.isEqual(SubActionType.crop_canvas.rawValue) {
196 | maxValue = max((baseView.moveableArea?.size.width)!, (baseView.moveableArea?.size.height)!)
197 | minValue = min((baseView.moveableArea?.size.width)!, (baseView.moveableArea?.size.height)!)
198 | }else if crop_shapeName.isEqual(SubActionType.crop_image.rawValue) {
199 | maxValue = max((baseView.originalImage?.size.width)!, (baseView.originalImage?.size.height)!)
200 | minValue = min((baseView.originalImage?.size.width)!, (baseView.originalImage?.size.height)!)
201 | }
202 |
203 | if let ratio = maxRatio {
204 | maxRatio = ratio == maxValue!/minValue! ? minValue!/maxValue! : maxValue!/minValue!
205 | let proSize:CGSize = (scaleSize.proportionSize(p:maxRatio!))
206 | self.bounds = CGRect.init(origin: CGPoint.zero, size:CGSize.init(width:proSize.width, height: proSize.height))
207 | self.center = CGPoint.init(x: (superview?.frame.width)!/2, y: (superview?.frame.height)!/2)
208 |
209 | }
210 |
211 | }
212 |
213 | override public func touchesBegan(_ touches: Set, with event: UIEvent?) {
214 | super.touchesBegan(touches, with: event)
215 | isMove = false
216 | let touch:UITouch = touches.first!
217 | let point = touch.location(in: superview)
218 | if self.frame.contains(point) {
219 | edgePoint = touch.location(in: self)
220 | isMove = true
221 | }
222 | }
223 |
224 | override public func touchesMoved(_ touches: Set, with event: UIEvent?) {
225 |
226 | if !isMove! {
227 | return
228 | }
229 | let touch = touches.first
230 | var point = touch?.location(in: superview)
231 | point?.x += self.frame.size.width/2 - edgePoint.x
232 | point?.y += self.frame.size.height/2 - edgePoint.y
233 | if (point?.x)! < self.frame.size.width/2 {
234 | point?.x = self.frame.size.width/2
235 | }
236 | if (point?.y)! < self.frame.size.height/2 {
237 | point?.y = self.frame.size.height/2
238 | }
239 | if (point?.x)! > (superview?.frame.size.width)! - self.frame.size.width / 2 {
240 | point?.x = (superview?.frame.size.width)! - self.frame.size.width / 2
241 | }
242 | if (point?.y)! > (superview?.frame.size.height)! - self.frame.size.height / 2 {
243 | point?.y = (superview?.frame.size.height)! - self.frame.size.height / 2
244 | }
245 | self.center = point!
246 | self.setNeedsDisplay()
247 | }
248 |
249 |
250 |
251 | }
252 | // FIXME: update base editor view draw
253 | extension WPCropEditorView{
254 |
255 |
256 | func draw_BaseView_Outside_BezierPath() {
257 |
258 | let outSidePath = UIBezierPath()
259 |
260 | let super_X:CGFloat = 0
261 | let super_Y:CGFloat = 0
262 | let super_W = (superview?.frame.size.width)!
263 | let super_H = (superview?.frame.size.height)!
264 | let self_X = self.frame.origin.x
265 | let self_Y = self.frame.origin.y
266 | let self_W = self.frame.size.width
267 | let self_H = self.frame.size.height
268 |
269 | outSidePath.move(to: CGPoint.init(x:super_X, y:super_Y))
270 | outSidePath.addLine(to: CGPoint.init(x:self_X, y:self_Y))
271 | outSidePath.addLine(to: CGPoint.init(x:self_X+self_W, y:self_Y))
272 | outSidePath.addLine(to: CGPoint.init(x: super_X+super_W, y: super_Y))
273 | outSidePath.addLine(to: CGPoint.init(x: super_X, y: super_Y))
274 |
275 | outSidePath.move(to: CGPoint.init(x: super_X, y: super_H))
276 | outSidePath.addLine(to: CGPoint.init(x:self_X, y: self_H+self_Y))
277 | outSidePath.addLine(to: CGPoint.init(x:self_X, y:self_Y))
278 | outSidePath.addLine(to: CGPoint.zero)
279 | outSidePath.addLine(to: CGPoint.init(x: super_X, y: super_H+super_Y))
280 |
281 | outSidePath.move(to: CGPoint.init(x: super_X, y: super_Y+super_H))
282 | outSidePath.addLine(to: CGPoint.init(x:self_X, y:self_Y+self_H))
283 | outSidePath.addLine(to: CGPoint.init(x:self_X+self_W, y:self_Y+self_H))
284 | outSidePath.addLine(to: CGPoint.init(x: super_X+super_W, y: super_Y+super_H))
285 | outSidePath.addLine(to: CGPoint.init(x: super_X, y: super_Y+super_H))
286 |
287 | outSidePath.move(to: CGPoint.init(x: super_X+super_W, y: super_Y+super_H))
288 | outSidePath.addLine(to: CGPoint.init(x:self_X+self_W, y: self_H+self_Y))
289 | outSidePath.addLine(to: CGPoint.init(x: self_W+self_X, y: self_Y))
290 | outSidePath.addLine(to: CGPoint.init(x: super_W+super_X, y: super_Y))
291 | outSidePath.addLine(to: CGPoint.init(x: super_X+super_W, y: super_Y+super_H))
292 |
293 | self.drawBackgroudView?.drawPath = outSidePath
294 |
295 | }
296 |
297 |
298 |
299 |
300 | }
301 | // FIXME: make add polygon
302 | extension WPCropEditorView{
303 |
304 | func getPolygonPath(rect:CGRect) -> UIBezierPath {
305 | if self.degreeMeasure == nil {
306 | degreeMeasure = degree(count:CGFloat(polygonCount))
307 | }
308 | crop_shapePath.removeAllPoints()
309 | let beginPoint:CGPoint = CGPoint.init(x: rect.size.width/2, y:0)
310 | let boundsCenter = CGPoint.init(x: rect.size.width/2, y:rect.size.height/2)
311 | var endPoint:CGPoint = pointRotatedAroundAnchorPoint(point:beginPoint,anchorPoint:boundsCenter,angle:degreeMeasure!)
312 | crop_shapePath.move(to:beginPoint)
313 | for _ in 0..Double {
320 | let clipLength:CGFloat = 100*4.13
321 | let cercleLength:CGFloat = 100*CGFloat(Double.pi)
322 | let length:CGFloat = clipLength / count
323 | let measureLength:CGFloat = 360/cercleLength
324 | let degree:Double = Double(length/measureLength)
325 | return Double.pi*degree/180
326 | }
327 | func addAndDecrease_Polygon_Action(_ sender: UIButton) {
328 | // update frame
329 | if sender.tag == 345 {// add
330 | polygonCount += 1
331 | }else{//Decrease
332 | polygonCount -= 1
333 | }
334 | if polygonCount>20 {
335 | polygonCount = 20
336 | }
337 | if polygonCount<5 {
338 | polygonCount = 5
339 | }
340 | degreeMeasure = degree(count:CGFloat(polygonCount))
341 | self.setNeedsDisplay()
342 | }
343 | func pointRotatedAroundAnchorPoint(point:CGPoint,anchorPoint:CGPoint,angle:Double) ->CGPoint{
344 | let x:CGFloat = (point.x-anchorPoint.x)*CGFloat(cos(angle))-(point.y-anchorPoint.y)*CGFloat(sin(angle)) + anchorPoint.x
345 | let y:CGFloat = (point.x-anchorPoint.x)*CGFloat(sin(angle)) + (point.y-anchorPoint.y)*CGFloat(cos(angle))+anchorPoint.y
346 | return CGPoint(x:x, y:y)
347 | }
348 |
349 |
350 |
351 | }
352 | // FIXME: --------- WPDrawingView -------------------
353 | class WPDrawingView: UIView {
354 |
355 | var drawPath:UIBezierPath = UIBezierPath() {
356 | didSet{
357 | self.setNeedsDisplay()
358 | }
359 | }
360 | override init(frame: CGRect) {
361 | super.init(frame:frame)
362 | self.backgroundColor = UIColor.clear
363 | }
364 | required init?(coder aDecoder: NSCoder) {
365 | fatalError("init(coder:) has not been implemented")
366 | }
367 | override func draw(_ rect: CGRect) {
368 | UIColor.init(red: 102/255, green: 102/255, blue: 102/255, alpha: 0.7).setFill()
369 | drawPath.fill()
370 | }
371 |
372 |
373 | }
374 | extension WPCropEditorView{
375 |
376 |
377 | //MARK: change view size gesture action
378 | override public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
379 | // handle -9
380 | currentFrame = self.frame
381 | if lockX != nil {lockX = nil}
382 | if lockY != nil {lockY = nil}
383 | lockX = self.frame.origin.x
384 | lockY = self.frame.origin.y
385 | lockheight = 0
386 | lockWidth = 0
387 | return true
388 | }
389 |
390 | //MARK: octagonal location frre size
391 | @IBAction func controlSizeButton_collectionManage_Gesture(_ sender: UIPanGestureRecognizer) {
392 |
393 |
394 | var sP:CGPoint = CGPoint.zero;// size point
395 | var aP:CGPoint = CGPoint.zero // appear point
396 |
397 | if (sender.view?.isEqual(self.controlSizeButton_0))! { // left top
398 |
399 | sP = sender.location(in: self.superview)
400 |
401 | sP.x = currentFrame.origin.x - sP.x
402 |
403 | sP.y = currentFrame.origin.y - sP.y
404 | // handle -16 to scale size
405 | if self.toScale {
406 | if sP.x > sP.y {sP.y = sP.x}
407 | if sP.y > sP.x {sP.x = sP.y}
408 | }
409 | aP.x = currentFrame.origin.x - sP.x
410 | aP.y = currentFrame.origin.y - sP.y
411 |
412 |
413 | }else if (sender.view?.isEqual(self.controlSizeButton_2))! {// left bottom
414 |
415 | sP = sender.location(in: self.superview)
416 |
417 | sP.x = currentFrame.origin.x - sP.x
418 |
419 | sP.y = sP.y - currentFrame.origin.y - currentFrame.height
420 | if self.toScale {
421 | if sP.x > sP.y {sP.y = sP.x}
422 | if sP.y > sP.x {sP.x = sP.y}
423 | }
424 | aP.y = currentFrame.origin.y
425 |
426 | aP.x = currentFrame.origin.x - sP.x
427 |
428 |
429 | }else if (sender.view?.isEqual(self.controlSizeButton_5))! {// right top
430 |
431 |
432 | sP = sender.location(in: self.superview)
433 |
434 | sP.x = sP.x - currentFrame.origin.x - currentFrame.width
435 |
436 | sP.y = currentFrame.origin.y - sP.y
437 | if self.toScale {
438 | if sP.x > sP.y {sP.y = sP.x}
439 | if sP.y > sP.x {sP.x = sP.y}
440 | }
441 | aP.y = currentFrame.origin.y - sP.y
442 |
443 | aP.x = currentFrame.origin.x
444 |
445 | }else if (sender.view?.isEqual(self.controlSizeButton_7))! {// right bottom
446 |
447 |
448 | sP = sender.location(in: self.superview)
449 |
450 | aP = currentFrame.origin
451 |
452 | sP.x = sP.x - currentFrame.size.width - currentFrame.origin.x
453 |
454 | sP.y = sP.y - currentFrame.size.height - currentFrame.origin.y
455 | if self.toScale {
456 | if sP.x > sP.y {sP.y = sP.x}
457 | if sP.y > sP.x {sP.x = sP.y}
458 | }
459 |
460 |
461 | }
462 | self.changeViewSizeEDGE(sP , ap:aP)
463 | }
464 | /**
465 | centralized processing view size change
466 | - parameter sp size point
467 | - parameter ap appear point
468 | - parameter orientation
469 | **/
470 | func changeViewSizeEDGE(_ sp:CGPoint,ap:CGPoint) {
471 | var moveFrame = CGRect.zero
472 | moveFrame.origin.x = ap.x
473 | moveFrame.origin.y = ap.y
474 | moveFrame.size.width = currentFrame.size.width + sp.x
475 | moveFrame.size.height = currentFrame.size.height + sp.y
476 | if crop_shapeName.isEqual(SubActionType.crop_43.rawValue)||crop_shapeName.isEqual(SubActionType.crop_169.rawValue)||crop_shapeName.isEqual(SubActionType.crop_canvas.rawValue)||crop_shapeName.isEqual(SubActionType.crop_image.rawValue) {
477 | moveFrame.size = moveFrame.size.proportionSize(p: maxRatio!)
478 | }
479 |
480 | if moveFrame.size.width <= 100 {
481 | moveFrame.size.width = 100
482 | return
483 | }else if moveFrame.size.height <= 100 {
484 | moveFrame.size.height = 100
485 | return
486 | }
487 |
488 |
489 | /**
490 | size out handle
491 | */
492 | if moveFrame.origin.x < lockX! && moveFrame.origin.x > 0 {
493 |
494 | lockWidth = (self.superview?.frame.width)! - moveFrame.origin.x - moveFrame.size.width
495 | }
496 | if moveFrame.origin.y < lockY! && moveFrame.origin.y > 0 {
497 |
498 | lockheight = (self.superview?.frame.height)! - moveFrame.origin.y - moveFrame.size.height
499 | }
500 |
501 | if moveFrame.origin.x < 0 {
502 |
503 | moveFrame.origin.x = 0
504 | moveFrame.size.width = (self.superview?.frame.width)! - lockWidth
505 | if crop_shapeName.isEqual(SubActionType.Cercle.rawValue) || crop_shapeName.isEqual(SubActionType.Triangle.rawValue)||crop_shapeName.isEqual(SubActionType.Rectangle.rawValue)||crop_shapeName.isEqual(SubActionType.crop_polygon.rawValue) {
506 | moveFrame.size.height = moveFrame.size.width
507 | }else if crop_shapeName.isEqual(SubActionType.crop_43.rawValue) || crop_shapeName.isEqual(SubActionType.crop_169.rawValue) || crop_shapeName.isEqual(SubActionType.crop_image.rawValue)||crop_shapeName.isEqual(SubActionType.crop_canvas.rawValue){
508 | moveFrame.size.height = moveFrame.size.proportionSize(p: maxRatio!).height
509 | }
510 |
511 | }
512 | if moveFrame.origin.y < 0 {
513 | moveFrame.origin.y = 0
514 | moveFrame.size.height = (self.superview?.frame.height)! - lockheight
515 | if crop_shapeName.isEqual(SubActionType.Cercle.rawValue) || crop_shapeName.isEqual(SubActionType.Triangle.rawValue)||crop_shapeName.isEqual(SubActionType.Rectangle.rawValue)||crop_shapeName.isEqual(SubActionType.crop_polygon.rawValue) {
516 | moveFrame.size.width = moveFrame.size.height
517 | }else if crop_shapeName.isEqual(SubActionType.crop_43.rawValue) || crop_shapeName.isEqual(SubActionType.crop_169.rawValue) || crop_shapeName.isEqual(SubActionType.crop_image.rawValue)||crop_shapeName.isEqual(SubActionType.crop_canvas.rawValue){
518 | moveFrame.size.width = moveFrame.size.proportionSize(p: maxRatio!).width
519 | }
520 | }
521 | if moveFrame.size.width + moveFrame.origin.x > (self.superview?.frame.size.width)! {
522 | moveFrame.size.width = (self.superview?.frame.size.width)! - moveFrame.origin.x
523 | if crop_shapeName.isEqual(SubActionType.Cercle.rawValue) || crop_shapeName.isEqual(SubActionType.Triangle.rawValue)||crop_shapeName.isEqual(SubActionType.Rectangle.rawValue)||crop_shapeName.isEqual(SubActionType.crop_polygon.rawValue) {
524 | moveFrame.size.height = moveFrame.size.width
525 | }else if crop_shapeName.isEqual(SubActionType.crop_43.rawValue) || crop_shapeName.isEqual(SubActionType.crop_169.rawValue) || crop_shapeName.isEqual(SubActionType.crop_image.rawValue)||crop_shapeName.isEqual(SubActionType.crop_canvas.rawValue){
526 | moveFrame.size.height = moveFrame.size.proportionSize(p: maxRatio!).height
527 | }
528 | }
529 | if moveFrame.size.height + moveFrame.origin.y > (self.superview?.frame.size.height)! {
530 |
531 | moveFrame.size.height = (self.superview?.frame.size.height)! - moveFrame.origin.y
532 | if crop_shapeName.isEqual(SubActionType.Cercle.rawValue) || crop_shapeName.isEqual(SubActionType.Triangle.rawValue)||crop_shapeName.isEqual(SubActionType.Rectangle.rawValue)||crop_shapeName.isEqual(SubActionType.crop_polygon.rawValue) {
533 | moveFrame.size.width = moveFrame.size.height
534 | }else if crop_shapeName.isEqual(SubActionType.crop_43.rawValue) || crop_shapeName.isEqual(SubActionType.crop_169.rawValue) || crop_shapeName.isEqual(SubActionType.crop_image.rawValue)||crop_shapeName.isEqual(SubActionType.crop_canvas.rawValue){
535 | moveFrame.size.width = moveFrame.size.proportionSize(p: maxRatio!).width
536 | }
537 |
538 | }
539 | self.frame = moveFrame
540 | }
541 |
542 | }
543 |
544 |
545 |
--------------------------------------------------------------------------------
/imageEditor/kernel/WPExtension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WPExtension.swift
3 | // WPImageEditor2
4 | //
5 | // Created by pow on 2017/11/9.
6 | // Copyright © 2017年 pow. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | // sub action type
12 | public enum SubActionType : String {
13 | case crop_free
14 | case crop_image
15 | case fullCanvas_scaleFill
16 | case fullCanvas_scaleFit
17 | case crop_canvas
18 | case crop_polygon
19 | case crop_43
20 | case crop_169
21 | case Cercle
22 | case Triangle
23 | case Rectangle
24 | case Oval
25 | case Free_C
26 | case Free_S
27 | case rotate_left
28 | case rotate_right
29 | case subCancel
30 | case centerSelected
31 | case rotateCancel
32 | case clip
33 | }
34 | // main action type
35 | public enum OperationType : String {
36 | case unknown
37 | case crop
38 | case rotation
39 | case full
40 | case center
41 | case undo
42 | case cancel
43 | case done
44 | case redo
45 | case reset
46 | case paste
47 | case rotateRatio
48 | }
49 | var isCut:Bool = false
50 |
51 | var selectedActionType:String = OperationType.unknown.rawValue
52 |
53 | //DegreesToRadians
54 | func DegreesToRadians(_ angle :Double )->Double{ return Double.pi*angle/180 }
55 | func RadiansToDegrees(_ angle :Double )->Double{ return angle*180/Double.pi }
56 | func pointRotatedAroundAnchorPoint(point:CGPoint,anchorPoint:CGPoint,angle:Double) ->CGPoint{
57 | let x:CGFloat = (point.x-anchorPoint.x)*CGFloat(cos(angle))-(point.y-anchorPoint.y)*CGFloat(sin(angle)) + anchorPoint.x
58 | let y:CGFloat = (point.x-anchorPoint.x)*CGFloat(sin(angle)) + (point.y-anchorPoint.y)*CGFloat(cos(angle))+anchorPoint.y
59 | return CGPoint(x:x, y:y)
60 | }
61 | extension Dictionary {
62 |
63 | func sortKeys() -> Array {
64 |
65 | return self.keys.sorted { (key1, key2) -> Bool in
66 |
67 | return key1.hashValue < key2.hashValue
68 | }
69 |
70 |
71 |
72 | }
73 |
74 | }
75 | extension CGSize {
76 |
77 | func proportionSize(p:CGFloat) -> CGSize {
78 |
79 | let Oriscale = (self.width)/(self.height)
80 | var width:CGFloat = 0
81 | var height:CGFloat = 0
82 |
83 | if Oriscale > p {
84 | height = (self.height)
85 | width = height * p
86 |
87 | }else{
88 | width = (self.width)
89 | height = width/p
90 |
91 |
92 | }
93 | return CGSize.init(width: width, height: height)
94 | }
95 |
96 |
97 |
98 | }
99 |
100 | extension CGFloat {
101 |
102 | func exchange(x:CGFloat,y:CGFloat) -> CGFloat {
103 | if self == x/y {
104 | return y/x
105 | }
106 | return x/y
107 | }
108 | }
109 |
110 |
111 |
112 |
113 | /**color rgb to convert code string **/
114 | extension UIColor{
115 |
116 |
117 | public class func WP_Color_Conversion ( _ Color_Value:NSString)->UIColor{
118 | var Str :NSString = Color_Value.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).uppercased() as NSString
119 | if Color_Value.hasPrefix("#"){
120 | Str=(Color_Value as NSString).substring(from: 1) as NSString
121 | }
122 | let wp_StrRed = (Str as NSString ).substring(to: 2)
123 | let wp_StrGreen = ((Str as NSString).substring(from: 2) as NSString).substring(to: 2)
124 | let wp_StrBlue = ((Str as NSString).substring(from: 4) as NSString).substring(to: 2)
125 | var r:CUnsignedInt = 0, g:CUnsignedInt = 0, b:CUnsignedInt = 0;
126 | Scanner(string:wp_StrRed).scanHexInt32(&r)
127 | Scanner(string: wp_StrGreen).scanHexInt32(&g)
128 | Scanner(string: wp_StrBlue).scanHexInt32(&b)
129 | return UIColor(red: CGFloat(r)/255.0, green: CGFloat(g)/255.0, blue: CGFloat(b)/255.0, alpha: 1)
130 |
131 | }
132 |
133 | }
134 | /**DispatchQueue once extension**/
135 | public extension DispatchQueue {
136 |
137 | public static var _onceTracker = [String]()
138 |
139 | /**
140 | Executes a block of code, associated with a unique token, only once. The code is thread safe and will
141 | only execute the code once even in the presence of multithreaded calls.
142 |
143 | - parameter token: A unique reverse DNS style name such as com.vectorform. or a GUID
144 | - parameter block: Block to execute once
145 | */
146 | public class func once(token: String, block:()->Void) {
147 | objc_sync_enter(self)
148 | defer { objc_sync_exit(self) }
149 |
150 | if _onceTracker.contains(token) {
151 | return
152 | }
153 |
154 | _onceTracker.append(token)
155 | block()
156 | }
157 | }
158 | /**
159 | if to setting imageView of image ,must before adjust image size .to Fit imageView .becase clip relative to self image size .not imageView frame
160 | **/
161 | extension UIImage{
162 |
163 |
164 | /**
165 | calculate rotaed
166 | - parameter Radian:
167 | */
168 | public func imageRotatedByRadians(radinFlaot:Double) -> UIImage {
169 |
170 |
171 | let t:CGAffineTransform = CGAffineTransform.init(rotationAngle: CGFloat(radinFlaot))
172 |
173 | let rotatedRect = CGRect.init(x: 0, y: 0, width: self.size.width, height: self.size.height).applying(t)
174 |
175 | let rotaedSize = rotatedRect.size
176 |
177 | UIGraphicsBeginImageContextWithOptions(rotaedSize, false, UIScreen.main.scale)
178 |
179 | let context = UIGraphicsGetCurrentContext()
180 |
181 | context?.translateBy(x: (rotaedSize.width)/2, y: (rotaedSize.height)/2)
182 |
183 | context?.rotate(by:CGFloat(radinFlaot))
184 |
185 | context?.scaleBy(x:1, y:-1)
186 |
187 | context?.draw((self.cgImage!), in:CGRect.init(x: -self.size.width/2, y: -self.size.height/2, width: self.size.width, height:self.size.height))
188 |
189 | let newImage = UIGraphicsGetImageFromCurrentImageContext()
190 |
191 | UIGraphicsEndImageContext()
192 |
193 | return newImage!
194 | }
195 | class func imageWithImage(_ image: UIImage, scaledToFillToSize newSize: CGSize) -> UIImage? {
196 | //Determine the scale factors
197 | let widthScale = newSize.width / image.size.width;
198 | let heightScale = newSize.height / image.size.height;
199 |
200 | var scaleFactor: CGFloat
201 |
202 | //The larger scale factor will scale less (0 < scaleFactor < 1) leaving the other dimension hanging outside the newSize rect
203 | widthScale > heightScale ? (scaleFactor = widthScale) : (scaleFactor = heightScale)
204 | let scaledSize = CGSize(width: image.size.width * scaleFactor, height: image.size.height * scaleFactor)
205 |
206 | //Create origin point so that the center of the image falls into the drawing context rect (the origin will have negative component).
207 | var imageDrawOrigin = CGPoint(x: 0, y: 0)
208 | widthScale > heightScale ? (imageDrawOrigin.y = (newSize.height - scaledSize.height) * 0.5):
209 | (imageDrawOrigin.x = (newSize.width - scaledSize.width) * 0.5);
210 |
211 |
212 | //Create rect where the image will draw
213 | let imageDrawRect = CGRect(x: imageDrawOrigin.x, y: imageDrawOrigin.y, width: scaledSize.width, height: scaledSize.height);
214 |
215 | //The imageDrawRect is larger than the newSize rect, where the imageDraw origin is located defines what part of
216 | //the image will fall into the newSize rect.
217 | return imageWithImage(image, scaledToSize: newSize, inRect: imageDrawRect)
218 | }
219 |
220 | class func imageWithImage(_ image: UIImage, scaledToFitToSize newSize: CGSize) -> UIImage? {
221 | //Only scale images down
222 | if (image.size.width < newSize.width && image.size.height < newSize.height) {
223 | return image;
224 | }
225 | //Determine the scale factors
226 | let widthScale = newSize.width / image.size.width;
227 | let heightScale = newSize.height / image.size.height;
228 | var scaleFactor: CGFloat;
229 | //The smaller scale factor will scale more (0 < scaleFactor < 1) leaving the other dimension inside the newSize rect
230 | widthScale < heightScale ? (scaleFactor = widthScale) : (scaleFactor = heightScale);
231 | let scaledSize = CGSize(width: image.size.width * scaleFactor, height: image.size.height * scaleFactor);
232 | //Scale the image
233 | return imageWithImage(image, scaledToSize: scaledSize, inRect: CGRect(x: 0.0, y: 0.0, width: scaledSize.width, height: scaledSize.height));
234 | }
235 | class func imageWithImage(_ image: UIImage, scaledToSize newSize: CGSize, inRect rect: CGRect) -> UIImage? {
236 | //Determine whether the screen is retina
237 | if UIScreen.main.scale == 2.0 {
238 | UIGraphicsBeginImageContextWithOptions(newSize, true, 2.0);
239 | } else {
240 | UIGraphicsBeginImageContext(newSize);
241 | }
242 |
243 | //Draw image in provided rect
244 | image.draw(in: rect)
245 | let newImage = UIGraphicsGetImageFromCurrentImageContext();
246 |
247 | //Pop this context
248 | UIGraphicsEndImageContext();
249 |
250 | return newImage;
251 | }
252 | class func imageRotatedByDegrees(_ image: UIImage, degrees: CGFloat) -> UIImage?
253 | {
254 | if degrees == 0 {
255 | return image
256 | }
257 | let radians = degrees
258 | // calculate the size of the rotated view's containing box for our drawing space
259 | let rect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
260 | let rotatedRect = rect.applying(CGAffineTransform(rotationAngle: CGFloat(radians)))
261 | let rotatedSize = rotatedRect.size;
262 |
263 | // Create the bitmap context
264 | UIGraphicsBeginImageContextWithOptions(rotatedSize, false, UIScreen.main.scale)
265 | if let bitmap = UIGraphicsGetCurrentContext() {
266 | // Move the origin to the middle of the image so we will rotate and scale around the center.
267 | bitmap.translateBy(x: rotatedSize.width / 2, y: rotatedSize.height / 2);
268 |
269 | // // Rotate the image context
270 | bitmap.rotate(by: CGFloat(radians));
271 |
272 | // Now, draw the rotated/scaled image into the context
273 | bitmap.scaleBy(x: 1.0, y: -1.0);
274 |
275 | if let cgImage = image.cgImage {
276 | bitmap.draw(cgImage, in: CGRect(x: -image.size.width / 2, y: -image.size.height / 2, width: image.size.width, height: image.size.height))
277 | }
278 | }
279 |
280 | let newImage = UIGraphicsGetImageFromCurrentImageContext();
281 | UIGraphicsEndImageContext();
282 | return newImage;
283 |
284 | }
285 | }
286 | func getAddress(obj:AnyObject) -> String {
287 | let address = Unmanaged.passUnretained(obj).toOpaque()
288 | return "\(address)"
289 | }
290 | let nibName:String = "ImageEditor2Nib"
291 | extension UINib{
292 |
293 | convenience init(nibName:String,instantiate:Any,viewBlock:@escaping (_ view:Any)->Void) {
294 | self.init(nibName: nibName, bundle: Bundle.init(for:instantiate as! AnyClass))
295 | let owner = self.instantiate(withOwner: instantiate, options: nil)
296 | for view:Any in owner {
297 | if ((view as AnyObject).isMember(of:instantiate as! AnyClass)) {
298 | viewBlock(view as AnyObject)
299 | }
300 | }
301 |
302 |
303 | }
304 | }
305 | extension CGRect {
306 | func applyingInversed() -> CGRect {
307 | return CGRect.init(x: self.minY, y: self.minX, width: self.height, height: self.width)
308 | }
309 | }
310 | extension Int {
311 |
312 | mutating func maxAngleValue() -> Int {
313 | if self >= 360 {
314 | self = 0
315 | }
316 | switch abs(self) {
317 | case 0...89:
318 | return 90
319 | case 91...179:
320 | return 180
321 | case 181...269:
322 | return 270
323 | case 271...359:
324 | return 360
325 | default:
326 | return self+90
327 | }
328 |
329 | }
330 | }
331 | extension UIImagePickerController {
332 |
333 | open override var supportedInterfaceOrientations: UIInterfaceOrientationMask
334 | {
335 | return UIInterfaceOrientationMask.all
336 | }
337 |
338 | open override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation
339 | {
340 | var orinentation: UIInterfaceOrientation = UIInterfaceOrientation.init(rawValue: UIApplication.shared.statusBarOrientation.rawValue)!
341 | if UIApplication.shared.statusBarOrientation.rawValue > 4 || UIApplication.shared.statusBarOrientation.rawValue < 1 {
342 | orinentation = .portrait
343 | }
344 | return orinentation
345 | }
346 |
347 |
348 |
349 | }
350 | // handle -3 undo
351 | class MoveGestureRecognizer: UIPanGestureRecognizer {
352 |
353 |
354 | var newState:Int = 0
355 |
356 | func getState() -> UIGestureRecognizerState {
357 | return UIGestureRecognizerState(rawValue: self.newState)!
358 | }
359 |
360 | override func setTranslation(_ translation: CGPoint, in view: UIView?) {
361 | self.newState = self.state.rawValue
362 | super.setTranslation(translation, in: view)
363 | self.newState = self.state.rawValue
364 |
365 | }
366 |
367 |
368 | }
369 |
370 |
371 |
--------------------------------------------------------------------------------
/imageEditor/kernel/WPFreePathView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WPFreePathView.swift
3 | // imageEditor
4 | //
5 | // Created by pow on 2017/9/30.
6 | // Copyright © 2017年 WANG. All rights reserved.
7 |
8 | /**
9 |
10 | -state-
11 |
12 | The current effect is curve and line, cut and finished. Specify shape cutting completion.
13 | 1. Curve cutting, can not be cancelled after completion
14 | 2.The curve is a point-to-point operation
15 | 3.The line is a drag and drop operation
16 | 4.Choose a curve or a straight line. You can't scale
17 |
18 | **/
19 |
20 | import UIKit
21 |
22 | public class WPFreePathView: UIView {
23 |
24 | enum PathState {
25 | case on_control
26 | case move
27 | case off_control
28 | case Refresh
29 |
30 | }
31 |
32 | /**private property**/
33 | private var points:NSMutableArray = NSMutableArray() // current all point
34 | private var paths:NSMutableArray = NSMutableArray() // current Frre_S of Bezier path
35 | private var recordArray:[CGPoint] = Array() // record start and end point
36 | var isDone:Bool = false // is done darwing line
37 | var isOpenControl:Bool = false
38 | var currentMark:UILabel? // current move change shape of mark label
39 | var currentStartPoint_1:CGPoint = CGPoint.zero // move point 1
40 | var currentStartPoint_2:CGPoint = CGPoint.zero // move point 2
41 | var movePath_1 = UIBezierPath()// current moveed two point . in the 1
42 | var movePath_2 = UIBezierPath()// current moveed two point . in the 2
43 | var lineCount:Int = 0 // number record
44 | var addPoint:CGPoint = CGPoint.zero // move path add point
45 | var controlMark:UILabel? // control mark
46 | var movePaths:[UIBezierPath] = Array()
47 | var currentEvent:NSString = "" // current evenet in the is began and moveed and enden
48 | var currentOPerationPaths:NSArray? // cureent operation path
49 |
50 | /**
51 | record four point
52 | indexPath
53 |
54 | 1.control point 1 actually is move point 1
55 | 2.control point 2 actually is move point 3
56 | 3.control path 1 first
57 | 4.control path 2 last
58 | **/
59 | var indexPath:[Int] = Array()
60 | var doneTimer:Timer?// done timer
61 |
62 | // public property
63 | public var sideMenu_undoButton:UIButton?
64 | public var sideMenu_clipButton:UIButton?
65 | public var baseImage:UIImage? // operation image
66 | public var path = UIBezierPath() // operation path
67 |
68 | // monitor Prperty
69 | override public var isHidden: Bool{didSet{removeAllEvents()}}//monitor hidden
70 | public var freePathName:NSString = "" {didSet{removeAllEvents()}}//monitor freePathName
71 |
72 | func changeState(newState:PathState,Whihresource:NSArray) {
73 |
74 | switch newState {
75 | case .on_control:
76 |
77 | indexPath.removeAll()
78 | removeControlMark()
79 | control_Free_C_LinePoint(index:Whihresource[1] as! Int)
80 |
81 | case .off_control:
82 | let operationPath = Whihresource[1] as! UIBezierPath
83 | self.paths.replaceObject(at:indexPath[0], with: [PathState.off_control,operationPath])
84 | indexPath.removeAll()
85 | removeControlMark()
86 |
87 | case .move:
88 | let operationPath = Whihresource[1] as! UIBezierPath
89 | // all event need remove
90 | self.paths.replaceObject(at:indexPath[0], with: [PathState.move,operationPath])
91 | removeControlMark()
92 | case .Refresh:
93 | // add new then change old
94 | for index in 0..1 {
203 |
204 | // start moveed time
205 | if !(self.points[self.paths.count] as! CGPoint).equalTo(self.points.lastObject as! CGPoint) {
206 |
207 | path.removeAllPoints()
208 | path.move(to: self.points[self.paths.count] as! CGPoint)
209 | path.addLine(to: self.points.lastObject as! CGPoint)
210 | path.stroke()
211 | let s_path:UIBezierPath = UIBezierPath.init(cgPath: path.cgPath)
212 | self.currentOPerationPaths = [PathState.off_control,s_path]
213 | self.paths.add(self.currentOPerationPaths!)
214 |
215 | }
216 | }
217 | }
218 |
219 | // that is straight line darw operation
220 | }else if (self.freePathName.isEqual(to: "Free_S")) {
221 |
222 | path.removeAllPoints()
223 | for s_path in self.paths {
224 |
225 | (s_path as! UIBezierPath).lineWidth = 2
226 | UIColor.orange.setStroke()
227 | (s_path as! UIBezierPath).stroke()
228 | }
229 |
230 | if isDone {
231 |
232 | if let mark = self.currentMark {
233 |
234 | self.movePath_1.removeAllPoints()
235 | self.movePath_1.move(to: self.currentStartPoint_1)
236 | self.movePath_1.addLine(to: (mark.center))
237 |
238 | self.movePath_2.removeAllPoints()
239 | self.movePath_2.move(to: self.currentStartPoint_2)
240 | self.movePath_2.addLine(to: (mark.center))
241 | }
242 |
243 | }else{
244 |
245 | if self.points.count>0 {
246 | path.move(to: self.points[0] as! CGPoint)
247 | path.addLine(to: self.points.lastObject as! CGPoint)
248 |
249 | }
250 |
251 | }
252 | path.stroke()
253 | if !self.recordArray.isEmpty {
254 | self.sideMenu_undoButton?.isSelected = true;
255 | self.sideMenu_undoButton?.isEnabled = true
256 | }
257 | }
258 |
259 | }
260 |
261 | override public func touchesBegan(_ touches: Set, with event: UIEvent?) {
262 |
263 | let startPoint = touches.first?.location(in: self)
264 | currentEvent = "Began"
265 |
266 | if isDone {// done time do it
267 |
268 | if freePathName.isEqual(to: "Free_C") {
269 |
270 | // do someting
271 |
272 | // MARK :No operation at the moment
273 |
274 | }else if freePathName.isEqual(to: "Free_S") {
275 |
276 | move_Free_S_LinePoint(startPoint: startPoint!)
277 |
278 | }
279 |
280 | }else{
281 |
282 | if freePathName.isEqual(to: "Free_S") {
283 |
284 |
285 | // not done do it
286 |
287 | if (self.subviews.isEmpty) {
288 | // add start mark to view
289 | self.addStartLineMarkview(point: startPoint!)
290 |
291 | }
292 | // add start point to record array
293 | self.recordArray.append(startPoint!)
294 | self.points.add(startPoint!)
295 |
296 |
297 |
298 | }else if freePathName.isEqual(to: "Free_C"){
299 |
300 |
301 | self.recordArray.append(startPoint!)
302 |
303 | if isContains(point: startPoint!).0 {
304 |
305 | if (self.subviews.first?.isEqual(isContains(point: startPoint!).1 as? UILabel))! {return}
306 | controlMark = isContains(point: startPoint!).1 as? UILabel
307 | if !(controlMark?.text?.hasPrefix("C"))! {
308 |
309 | isOpenControl = true
310 | }
311 | }else
312 | {
313 | controlMark = isContains(point: startPoint!).1 as? UILabel
314 | controlMark?.text = ""
315 | isOpenControl = false
316 |
317 | }
318 | }
319 | }
320 |
321 | }
322 |
323 | override public func touchesMoved(_ touches: Set, with event: UIEvent?) {
324 |
325 | let movePoint = touches.first?.location(in: self)
326 | currentEvent = "Moved"
327 |
328 | if isDone {
329 |
330 | if freePathName.isEqual(to: "Free_C") {
331 |
332 | // MARK :No operation at the moment
333 |
334 |
335 | }else if freePathName.isEqual(to: "Free_S"){
336 |
337 | self.currentMark?.center = movePoint!
338 | self.setNeedsDisplay()
339 |
340 | }
341 | }else
342 | {
343 | if freePathName.isEqual(to: "Free_C") {
344 |
345 | if isContains(point: movePoint!).0 { // pencil Too sensitive
346 |
347 | if (self.subviews.first?.isEqual(isContains(point: movePoint!).1 as? UILabel))! {return}
348 |
349 | if (controlMark?.text?.isEqual("C1"))! {
350 |
351 | self.currentStartPoint_1 = movePoint!
352 | controlMark?.center = movePoint!
353 | self.setNeedsDisplay()
354 |
355 | }else if (controlMark?.text?.isEqual("C2"))!{
356 |
357 | self.currentStartPoint_2 = movePoint!
358 | controlMark?.center = movePoint!
359 | self.setNeedsDisplay()
360 | }else
361 | {
362 |
363 | if let operationPath:NSArray = self.currentOPerationPaths {
364 |
365 | // only last point ,so can move
366 | if indexPath[0] == self.paths.count-1 {
367 |
368 | self.changeState(newState: .move, Whihresource: operationPath)
369 |
370 | addPoint = movePoint!
371 | controlMark?.center = movePoint!
372 | isOpenControl = true
373 | self.setNeedsDisplay()
374 | }
375 |
376 | }
377 |
378 |
379 | }
380 |
381 | }
382 |
383 |
384 | }else if freePathName.isEqual(to: "Free_S"){
385 |
386 | self.points.add(movePoint!)
387 | self.setNeedsDisplay()
388 | }
389 | }
390 | }
391 |
392 | override public func touchesEnded(_ touches: Set, with event: UIEvent?) {
393 |
394 |
395 | let endPoint = touches.first?.location(in: self)
396 |
397 |
398 | currentEvent = "Ended"
399 | //MARK: --out side view can't add point
400 | if !self.frame.contains(endPoint!) {return}
401 |
402 | if isDone {
403 |
404 | if freePathName.isEqual(to: "Free_C") {
405 |
406 | // MARK :No operation at the moment
407 |
408 |
409 | }else if freePathName.isEqual(to: "Free_S"){
410 |
411 |
412 | if !indexPath.isEmpty {
413 |
414 | let s_path1:UIBezierPath = UIBezierPath.init(cgPath: self.movePath_1.cgPath)
415 | let s_path2:UIBezierPath = UIBezierPath.init(cgPath: self.movePath_2.cgPath)
416 |
417 |
418 | self.paths.replaceObject(at: indexPath[2], with: s_path1)
419 | self.paths.replaceObject(at: indexPath[3], with: s_path2)
420 |
421 | indexPath.removeAll()
422 | self.currentMark = nil
423 | self.setNeedsDisplay()
424 |
425 | }
426 | }
427 | }else{
428 |
429 | if freePathName.isEqual(to: "Free_C") {
430 |
431 |
432 | if isContains(point: endPoint!).0 { // is point inside
433 |
434 | currentMark = (isContains(point: endPoint!).1 as? UILabel)!
435 | let mark:UILabel = currentMark!
436 |
437 |
438 | // this is done later operation
439 | if (mark.frame.contains(self.recordArray[0] )) {
440 |
441 | for index in 0..0 {
536 | // Bezier Path move point equal height + origin.y
537 | let firstPoint = CGPoint.init(x: (self.paths[0] as! UIBezierPath).bounds.origin.x, y: (self.paths[0] as! UIBezierPath).bounds.origin.y+(self.paths[0] as! UIBezierPath).bounds.size.height)
538 |
539 | // 1.state : undo later path
540 | if (self.subviews.last?.frame.contains(firstPoint))! {
541 |
542 | if ((self.subviews.first as! MarkLabel).text?.isEqual("1"))! {
543 |
544 | self.subviews.first?.removeFromSuperview()
545 | }
546 | doneAnimation()
547 | }
548 |
549 | }
550 | self.points.removeAllObjects()
551 | let s_path:UIBezierPath = UIBezierPath.init(cgPath: path.cgPath)
552 | self.paths.add(s_path)
553 | }
554 |
555 | // add start point to record array
556 | self.recordArray.append(endPoint!)
557 | }
558 | }
559 | //MARK: ** get current path **/
560 | public func getCurrentPath()->UIBezierPath {
561 |
562 |
563 | let currentPath = UIBezierPath()
564 |
565 | if freePathName.isEqual(to: "Free_C") {
566 |
567 | currentPath.move(to:self.subviews[0].center)
568 |
569 | for index in 1.. (Bool,UIView){
704 |
705 |
706 | for mark in self.subviews {
707 |
708 | if mark.frame.contains(point) {
709 |
710 | let index = self.subviews.index(of:mark)!
711 |
712 | if index == self.subviews.count-1 {
713 | if isDone {
714 |
715 | indexPath.append(0)//first
716 | indexPath.append(index)//
717 | }else
718 | {
719 | indexPath.append(index-1)//0
720 | indexPath.append(index)//1
721 | }
722 |
723 |
724 | }else if index == 0 {
725 | indexPath.append(self.subviews.count-1)//0
726 | indexPath.append(index)//1
727 |
728 | }else{
729 | indexPath.append(index-1)//0
730 | indexPath.append(index)//1
731 |
732 | }
733 | return (true,mark)
734 |
735 | }
736 |
737 | }
738 |
739 | return (false,UILabel())
740 |
741 | }
742 | /**
743 | remove all views or events
744 | */
745 | func removeAllEvents() {
746 |
747 | self.points.removeAllObjects()
748 | self.recordArray.removeAll()
749 | for mark in self.subviews {
750 | mark.removeFromSuperview()
751 | }
752 | self.path = UIBezierPath()
753 | self.setNeedsDisplay()
754 | self.paths.removeAllObjects()
755 | lineCount = 0
756 | isDone = false
757 | }
758 |
759 | // MARK: undo action
760 |
761 | /**
762 | undo operation
763 | - parameter sender: sidemenu Undo Button
764 | */
765 | public func undoPath(sender:UIButton){
766 |
767 | self.sideMenu_clipButton?.isSelected = false
768 | self.sideMenu_clipButton?.isEnabled = false
769 | currentEvent = "Undo"
770 | if (freePathName.isEqual(to: "Free_S")) {
771 |
772 | self.paths.removeLastObject()
773 |
774 | }else if(freePathName.isEqual(to: "Free_C")){
775 | self.points.removeLastObject()
776 | self.path.removeAllPoints()// remove all points then afresh draw path
777 | self.paths.removeLastObject()// remove last path line
778 | removeControlMark()
779 | }
780 |
781 | // remove record startPoint last
782 | self.recordArray.removeLast()
783 | self.recordArray.removeLast()
784 | // remove mark label last and first
785 | self.subviews.last?.removeFromSuperview()
786 | lineCount -= 1
787 | isDone = false
788 | if let time = self.doneTimer {
789 |
790 | time.invalidate()
791 | for mark in self.subviews {
792 |
793 | if mark.layer.borderColor == UIColor.orange.cgColor{
794 | mark.layer.borderColor = UIColor.white.cgColor
795 | }
796 | }
797 |
798 | }
799 | self.layoutSubviews()
800 | self.setNeedsDisplay()// afresh draw
801 | // change undo button state
802 | if self.paths.count == 0 {
803 | self.recordArray.removeAll()
804 | self.points.removeAllObjects()
805 | for marl in self.subviews {
806 |
807 | marl.removeFromSuperview()
808 |
809 | }
810 | sender.isEnabled = false;
811 | sender.isSelected = false;
812 | lineCount = 0
813 | }
814 |
815 | }
816 |
817 | /**
818 | done anmation
819 | */
820 | func doneAnimation() {
821 |
822 | isDone = true
823 | self.sideMenu_clipButton?.isSelected = true
824 | self.sideMenu_clipButton?.isEnabled = true
825 | self.doneTimer = Timer.scheduledTimer(withTimeInterval:0.5, repeats: true) { (mer) in
826 | for endMark in self.subviews{
827 |
828 | if endMark.layer.borderColor == UIColor.white.cgColor{
829 |
830 | endMark.layer.borderColor = UIColor.orange.cgColor
831 |
832 | }else
833 | {
834 | endMark.layer.borderColor = UIColor.white.cgColor
835 | }
836 |
837 | }
838 | }
839 |
840 | }
841 |
842 | /**
843 | add mark label
844 | - parameter point: mark center
845 | */
846 | func addStartLineMarkview(point:CGPoint) {
847 |
848 | indexPath.removeAll()// the first remove data
849 |
850 | lineCount += 1
851 | let markLabel:MarkLabel = MarkLabel.init(frame: CGRect.init(x: 0, y:0, width: 20, height: 20))
852 | markLabel.center = point
853 | markLabel.bounds = CGRect.init(x: 0, y: 0, width:20, height: 20)
854 | markLabel.layer.borderWidth = 3
855 | markLabel.layer.borderColor = UIColor.white.cgColor
856 | markLabel.layer.cornerRadius = 10
857 | markLabel.layer.masksToBounds = true
858 | markLabel.currentPathName = self.freePathName
859 | markLabel.backgroundColor = UIColor.black
860 | self.addSubview(markLabel)
861 | markLabel.text = "\(lineCount)"
862 | markLabel.textColor = UIColor.white
863 | markLabel.textAlignment = .center
864 | markLabel.font = UIFont.systemFont(ofSize: 10)
865 | markLabel.isUserInteractionEnabled = true
866 |
867 | if freePathName.isEqual(to: "Free_C") {
868 |
869 | // add every one point 。must to afresh index
870 | let index = self.subviews.index(of:markLabel)!
871 |
872 | if index == self.subviews.count-1 {
873 | if isDone {
874 |
875 | indexPath.append(0)//first
876 | indexPath.append(index)//
877 | }else
878 | {
879 | indexPath.append(index-1)//0
880 | indexPath.append(index)//1
881 | }
882 |
883 | }else if index == 0 {
884 | indexPath.append(self.subviews.count-1)//0
885 | indexPath.append(index)//1
886 |
887 | }else{
888 | indexPath.append(index-1)//0
889 | indexPath.append(index)//1
890 |
891 | }
892 | }
893 | }
894 |
895 | // add control mark label
896 | func addControllerMark(point:CGPoint,tag:Int) {
897 |
898 | let markLabel:UILabel = UILabel.init(frame: CGRect.init(x: 0, y:0, width: 20, height: 20))
899 | markLabel.center = point
900 | markLabel.bounds = CGRect.init(x: 0, y: 0, width:20, height: 20)
901 | markLabel.layer.borderWidth = 3
902 | markLabel.layer.borderColor = UIColor.white.cgColor
903 | markLabel.layer.cornerRadius = 10
904 | markLabel.layer.masksToBounds = true
905 | markLabel.backgroundColor = UIColor.black
906 | self.addSubview(markLabel)
907 | markLabel.text = "C\(tag)"
908 | markLabel.textColor = UIColor.white
909 | markLabel.textAlignment = .center
910 | markLabel.font = UIFont.systemFont(ofSize: 8)
911 |
912 | }
913 |
914 | }
915 |
916 | //MARK: ---mark label class
917 |
918 | class MarkLabel: UILabel {
919 |
920 |
921 |
922 | public var currentPathName:NSString = ""
923 |
924 | override func touchesBegan(_ touches: Set, with event: UIEvent?) {
925 | superview?.touchesBegan(touches, with: event)
926 | }
927 |
928 | override func touchesMoved(_ touches: Set, with event: UIEvent?) {
929 |
930 | if currentPathName.isEqual(to: "Free_S") {
931 | superview?.touchesMoved(touches, with: event)
932 | }
933 |
934 | }
935 |
936 | override func touchesEnded(_ touches: Set, with event: UIEvent?) {
937 | superview?.touchesEnded(touches, with: event)
938 | self.isUserInteractionEnabled = false
939 |
940 | }
941 |
942 |
943 | }
944 |
945 |
--------------------------------------------------------------------------------
/imageEditor/kernel/WPSideMenu.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WPSideMenu.swift
3 | // imageEditor
4 | //
5 | // Created by pow on 2017/11/7.
6 | // Copyright © 2017年 pow. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | protocol WPSideMenuDelegate {
12 |
13 |
14 | func onClick_SideMenu_SubAction_Events(type:String,sender:UIButton)
15 |
16 | }
17 |
18 | //MARK:internal class *WPSideMenu*
19 | public typealias imagePasteAndClipBlock = (String)->Void
20 |
21 | public class WPSideMenu: UIView {
22 |
23 | open var pasteOrClipImageBlock:imagePasteAndClipBlock?
24 | var delegate:WPSideMenuDelegate?
25 |
26 |
27 | @IBOutlet weak var operationStackView: UIStackView!
28 |
29 | @IBOutlet weak var countControlView: UIView!
30 |
31 | @IBOutlet weak var eventsStackView: UIStackView!
32 |
33 | @IBOutlet weak var centerButton: UIButton!
34 |
35 | @IBOutlet weak var doneButton: UIButton!
36 |
37 | @IBOutlet weak var undoButton: UIButton!
38 |
39 | @IBOutlet weak var resetButton: UIButton!
40 |
41 | @IBOutlet weak var redoButton: UIButton!
42 |
43 | @IBOutlet weak var rotateRatioButton: UIButton!
44 |
45 |
46 |
47 | // instantiation nib file
48 | public class func loadsideMenu(_ frame:CGRect) -> WPSideMenu {
49 | var menu:WPSideMenu?
50 | _ = UINib.init(nibName:nibName, instantiate: WPSideMenu.self) {
51 | (view) in
52 | menu = view as? WPSideMenu
53 |
54 | }
55 | menu?.frame = frame
56 | return menu!
57 |
58 | }
59 |
60 | // MARK: load Actions To StackView
61 | func loadActionsToStackView(type:OperationType) {
62 | // loading button
63 | close_ActionsStackView()
64 | for b:UIButton in loadImageData(type: type) {
65 | self.operationStackView.addArrangedSubview(b)
66 | }
67 |
68 | }
69 | //MARK: close_ActionsStackView
70 | func close_ActionsStackView() {
71 | for view in self.operationStackView.subviews {
72 | self.operationStackView.removeArrangedSubview(view)
73 | view.removeFromSuperview()
74 | }
75 |
76 | }
77 | //MARK: reset_ActionsStackView
78 | func reset_ActionsStackView() {
79 | loadActionsToStackView(type: .unknown)
80 | selectedActionType = OperationType.unknown.rawValue
81 | }
82 |
83 | //MARK: load button image
84 | func loadImageData(type:OperationType) -> Array {
85 |
86 | var datas:[UIButton] = Array()
87 | var images:[String] = Array()
88 | if type == .crop {
89 | // the frsit setup stack view and init install
90 | images = ["crop_free","crop_image","crop_canvas","crop_polygon","crop_path_c","crop_path_s","crop_square","crop_triangle","crop_circle","crop_oval","crop_43","crop_169"]
91 |
92 | }else if type == .full{
93 | images = ["fullCanvas_scaleFill","fullCanvas_scaleFit"]
94 | }else if type == .center{
95 |
96 | }else if type == .rotation{
97 | images = ["rotate_left","rotate_right"]
98 | }else{
99 | images = ["crop","rotate","center","fullCanvas"]
100 | }
101 | for name:String in images {
102 | let actionsBut:UIButton = UIButton()
103 | actionsBut.setImage(UIImage.init(named: name, in:Bundle.init(for: WPSideMenu.self), compatibleWith: nil), for: .normal)
104 | actionsBut.addTarget(self, action: NSSelectorFromString(name+"_Button_Action:"), for: .touchUpInside)
105 | datas.append(actionsBut)
106 | }
107 |
108 | return datas
109 |
110 | }
111 | }
112 | // FIXME: main operation extension
113 | extension WPSideMenu{
114 |
115 | // cancel button action
116 | @IBAction func cancelButton_Action(_ sender: UIButton) {
117 |
118 | if selectedActionType == OperationType.unknown.rawValue {
119 |
120 | delegate?.onClick_SideMenu_SubAction_Events(type: OperationType.cancel.rawValue, sender: sender)
121 |
122 | }else{
123 | delegate?.onClick_SideMenu_SubAction_Events(type: SubActionType.subCancel.rawValue, sender: sender)
124 | reset_ActionsStackView()
125 | closePolygonCountView()
126 | }
127 |
128 | }
129 | // clip button action
130 | @IBAction func crop_Button_Action(_ sender: UIButton) {
131 | loadActionsToStackView(type: .crop)
132 | doneButton.isEnabled = true
133 | undoButton.isEnabled = false
134 | redoButton.isEnabled = false
135 | rotateRatioButton.isHidden = false
136 | delegate?.onClick_SideMenu_SubAction_Events(type: OperationType.crop.rawValue, sender: sender)
137 | }
138 |
139 | @IBAction func rotate_Button_Action(_ sender: UIButton) {
140 | loadActionsToStackView(type: .rotation)
141 | selectedActionType = OperationType.rotation.rawValue
142 | delegate?.onClick_SideMenu_SubAction_Events(type: OperationType.rotation.rawValue, sender: sender)
143 | doneButton.isEnabled = true
144 | rotateRatioButton.isHidden = true
145 |
146 |
147 | }
148 | @IBAction func center_Button_Action(_ sender: UIButton) {
149 | self.centerButton = sender
150 | sender.setImage(UIImage.init(named: "center.selected", in: Bundle.init(for: WPSideMenu.self), compatibleWith: nil), for: .highlighted)
151 | selectedActionType = OperationType.center.rawValue
152 | delegate?.onClick_SideMenu_SubAction_Events(type: OperationType.center.rawValue, sender: sender)
153 | rotateRatioButton.isHidden = true
154 | }
155 | @IBAction func redoButton_Action(_ sender:UIButton){
156 |
157 | delegate?.onClick_SideMenu_SubAction_Events(type: OperationType.redo.rawValue, sender: sender)
158 | rotateRatioButton.isHidden = true
159 | }
160 | @IBAction func undoButton_Action(_ sender: UIButton) {
161 | delegate?.onClick_SideMenu_SubAction_Events(type: OperationType.undo.rawValue, sender: sender)
162 | rotateRatioButton.isHidden = true
163 | }
164 | @IBAction func fullCanvas_Button_Action(_ sender: UIButton) {
165 | loadActionsToStackView(type: .full)
166 | selectedActionType = OperationType.full.rawValue
167 | doneButton.isEnabled = true
168 | rotateRatioButton.isHidden = true
169 | }
170 |
171 | @IBAction func doneButton_Action(_ sender:UIButton){
172 |
173 | if selectedActionType.isEqual(SubActionType.rotate_left.rawValue)||selectedActionType.isEqual(OperationType.center.rawValue)||selectedActionType.isEqual(OperationType.rotation.rawValue)||selectedActionType.isEqual(OperationType.unknown.rawValue)||selectedActionType.isEqual(OperationType.full.rawValue)||selectedActionType.isEqual(SubActionType.rotate_right.rawValue)||selectedActionType.isEqual(SubActionType.fullCanvas_scaleFit.rawValue)||selectedActionType.isEqual(SubActionType.fullCanvas_scaleFill.rawValue)||selectedActionType.isEqual(OperationType.crop.rawValue)||selectedActionType.isEqual(OperationType.undo.rawValue)||selectedActionType.isEqual(OperationType.redo.rawValue)||selectedActionType.isEqual(OperationType.reset.rawValue){
174 | delegate?.onClick_SideMenu_SubAction_Events(type: OperationType.paste.rawValue, sender: sender)
175 | }else{
176 | delegate?.onClick_SideMenu_SubAction_Events(type: SubActionType.clip.rawValue, sender: sender)
177 | loadActionsToStackView(type: .unknown)
178 | selectedActionType = OperationType.unknown.rawValue
179 | closePolygonCountView()
180 |
181 | }
182 | }
183 |
184 | @IBAction func resetButton_Action(_ sender:UIButton){
185 | reset_ActionsStackView()
186 | closePolygonCountView()
187 | delegate?.onClick_SideMenu_SubAction_Events(type: OperationType.reset.rawValue, sender: sender)
188 | resetButton.isEnabled = false
189 | undoButton.isEnabled = false
190 | redoButton.isEnabled = false
191 | }
192 |
193 | @IBAction func rotationScaleButton_Action(_ sender:UIButton){
194 |
195 | delegate?.onClick_SideMenu_SubAction_Events(type: OperationType.rotateRatio.rawValue, sender: sender)
196 | }
197 | }
198 |
199 |
200 |
201 | // FIXME: sub actions extension
202 | extension WPSideMenu {
203 |
204 | @objc func crop_free_Button_Action(_ sender:UIButton) {
205 | delegate?.onClick_SideMenu_SubAction_Events(type: SubActionType.crop_free.rawValue, sender: sender)
206 | }
207 | @objc func crop_image_Button_Action(_ sender:UIButton) {
208 | delegate?.onClick_SideMenu_SubAction_Events(type: SubActionType.crop_image.rawValue, sender: sender)
209 |
210 | }
211 | @objc func crop_canvas_Button_Action(_ sender:UIButton) {
212 | delegate?.onClick_SideMenu_SubAction_Events(type: SubActionType.crop_canvas.rawValue, sender: sender)
213 |
214 | }
215 | @objc func crop_polygon_Button_Action(_ sender:UIButton) {
216 | delegate?.onClick_SideMenu_SubAction_Events(type: SubActionType.crop_polygon.rawValue, sender: sender)
217 | openPolygon_countOperationButtonView()
218 | }
219 | @objc func crop_path_c_Button_Action(_ sender:UIButton) {
220 | delegate?.onClick_SideMenu_SubAction_Events(type: SubActionType.Free_C.rawValue, sender: sender)
221 |
222 | }
223 | @objc func crop_path_s_Button_Action(_ sender:UIButton) {
224 |
225 | delegate?.onClick_SideMenu_SubAction_Events(type: SubActionType.Free_S.rawValue, sender: sender)
226 |
227 | }
228 | @objc func crop_square_Button_Action(_ sender:UIButton) {
229 |
230 | delegate?.onClick_SideMenu_SubAction_Events(type: SubActionType.Rectangle.rawValue, sender: sender)
231 |
232 | }
233 | @objc func crop_triangle_Button_Action(_ sender:UIButton) {
234 |
235 | delegate?.onClick_SideMenu_SubAction_Events(type: SubActionType.Triangle.rawValue, sender: sender)
236 |
237 | }
238 | @objc func crop_circle_Button_Action(_ sender:UIButton) {
239 |
240 | delegate?.onClick_SideMenu_SubAction_Events(type: SubActionType.Cercle.rawValue, sender: sender)
241 |
242 | }
243 | @objc func crop_oval_Button_Action(_ sender:UIButton) {
244 |
245 | delegate?.onClick_SideMenu_SubAction_Events(type: SubActionType.Oval.rawValue, sender: sender)
246 |
247 | }
248 | @objc func crop_43_Button_Action(_ sender:UIButton) {
249 |
250 | delegate?.onClick_SideMenu_SubAction_Events(type: SubActionType.crop_43.rawValue, sender: sender)
251 |
252 |
253 | }
254 | @objc func crop_169_Button_Action(_ sender:UIButton) {
255 | delegate?.onClick_SideMenu_SubAction_Events(type: SubActionType.crop_169.rawValue, sender: sender)
256 |
257 | }
258 | @objc func fullCanvas_scaleFill_Button_Action(_ sender:UIButton) {
259 | delegate?.onClick_SideMenu_SubAction_Events(type: SubActionType.fullCanvas_scaleFill.rawValue, sender: sender)
260 |
261 | }
262 | @objc func fullCanvas_scaleFit_Button_Action(_ sender:UIButton) {
263 |
264 | delegate?.onClick_SideMenu_SubAction_Events(type: SubActionType.fullCanvas_scaleFit.rawValue, sender: sender)
265 | }
266 | @objc func rotate_left_Button_Action(_ sender:UIButton) {
267 | delegate?.onClick_SideMenu_SubAction_Events(type: SubActionType.rotate_left.rawValue, sender: sender)
268 | }
269 | @objc func rotate_right_Button_Action(_ sender:UIButton) {
270 | delegate?.onClick_SideMenu_SubAction_Events(type: SubActionType.rotate_right.rawValue, sender: sender)
271 | }
272 | func openPolygon_countOperationButtonView() {
273 | self.countControlView.isHidden = false
274 | let countPolygonSelector:Selector = NSSelectorFromString("add_Count_Polygon_Action:")
275 | let addCount:UIButton = self.countControlView.subviews.first as! UIButton
276 | let decreaseCount:UIButton = self.countControlView.subviews.last as! UIButton
277 | addCount.tag = 345
278 | decreaseCount.tag = 346
279 | addCount.addTarget(delegate, action:countPolygonSelector, for: .touchUpInside)
280 | decreaseCount.addTarget(delegate, action:countPolygonSelector, for: .touchUpInside)
281 | }
282 |
283 | func closePolygonCountView() {
284 | self.countControlView.isHidden = true
285 | }
286 |
287 |
288 |
289 | }
290 |
291 |
--------------------------------------------------------------------------------