├── .gitignore ├── LICENSE ├── README.md ├── ptDrag ├── pointsDrag.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── pointsDrag │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── single_lhs_rotate.imageset │ │ ├── Contents.json │ │ ├── icon_zuozhuan@2x.png │ │ └── icon_zuozhuan@3x.png │ └── single_rhs_rotate.imageset │ │ ├── Contents.json │ │ ├── icon_youzhuan@2x.png │ │ └── icon_youzhuan@3x.png │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ ├── MagnifierView.swift │ ├── SceneDelegate.swift │ ├── SketchController.swift │ ├── SketchModel.swift │ ├── SketchView.swift │ ├── Util │ ├── FontAdd.swift │ ├── ImgAdd.swift │ ├── PointAdd.swift │ └── sizeAdd.swift │ ├── View │ └── DirectionRotateB.swift │ └── ViewController.swift ├── ptDragEasy ├── pointsDrag.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── pointsDrag │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── single_lhs_rotate.imageset │ │ ├── Contents.json │ │ ├── icon_zuozhuan@2x.png │ │ └── icon_zuozhuan@3x.png │ └── single_rhs_rotate.imageset │ │ ├── Contents.json │ │ ├── icon_youzhuan@2x.png │ │ └── icon_youzhuan@3x.png │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ ├── MagnifierView.swift │ ├── SceneDelegate.swift │ ├── SketchController.swift │ ├── SketchModel.swift │ ├── SketchView.swift │ ├── Util │ ├── FontAdd.swift │ ├── ImgAdd.swift │ ├── PointAdd.swift │ └── sizeAdd.swift │ ├── View │ └── DirectionRotateB.swift │ └── ViewController.swift ├── secondCrop ├── README.md ├── raw │ ├── Example │ │ ├── Example.xcodeproj │ │ │ ├── project.pbxproj │ │ │ └── project.xcworkspace │ │ │ │ ├── contents.xcworkspacedata │ │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── Example │ │ │ ├── AppDelegate.swift │ │ │ ├── Assets.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ │ ├── Classes │ │ │ ├── Array+shift.swift │ │ │ ├── CGPoint+geometry.swift │ │ │ ├── SEAreaView.swift │ │ │ ├── SECornerView.swift │ │ │ ├── SECropError.swift │ │ │ ├── SECropView.swift │ │ │ ├── SEQuadrangleHelper.swift │ │ │ └── UIView+globalCoordinates.swift │ │ │ ├── ImageViewController.swift │ │ │ ├── Info.plist │ │ │ ├── Resources │ │ │ └── paper.jpg │ │ │ └── ViewController.swift │ ├── Pod │ │ └── Classes │ │ │ ├── Array+shift.swift │ │ │ ├── CGPoint+geometry.swift │ │ │ ├── SEAreaView.swift │ │ │ ├── SECornerView.swift │ │ │ ├── SECropError.swift │ │ │ ├── SECropView.swift │ │ │ ├── SEQuadrangleHelper.swift │ │ │ └── UIView+globalCoordinates.swift │ └── README.md ├── secondCrop.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── secondCrop │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── ImageViewController.swift │ ├── Info.plist │ ├── Resources │ └── paper.jpg │ ├── SceneDelegate.swift │ ├── ViewController.swift │ └── crop │ ├── Array+shift.swift │ ├── CGPoint+geometry.swift │ ├── SEAreaView.swift │ ├── SECornerView.swift │ ├── SECropError.swift │ ├── SECropView.swift │ ├── SEQuadrangleHelper.swift │ └── UIView+globalCoordinates.swift ├── src ├── 0.PNG ├── 1.PNG ├── 2.PNG ├── 3.PNG └── second.png └── thirdCapture ├── README.md ├── captureAndFilter.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── captureAndFilter ├── AppDelegate.swift ├── Assets.xcassets │ ├── 4 │ │ ├── 4_tag_mask_plus.imageset │ │ │ ├── Contents.json │ │ │ ├── 编组@2x.png │ │ │ └── 编组@3x.png │ │ ├── 4_zhi_zhang_jump.imageset │ │ │ ├── Contents.json │ │ │ ├── 编组 8@2x.png │ │ │ └── 编组 8@3x.png │ │ ├── Contents.json │ │ ├── album_4_kiWa.imageset │ │ │ ├── Contents.json │ │ │ ├── 编组 2@2x.png │ │ │ └── 编组 2@3x.png │ │ ├── audio_4_3.imageset │ │ │ ├── Contents.json │ │ │ ├── 内容区/icon/120*120/创建音频@2x.png │ │ │ └── 内容区/icon/120*120/创建音频@3x.png │ │ ├── camera_4.imageset │ │ │ ├── Contents.json │ │ │ ├── 内容区/icon/历史记录@2x.png │ │ │ └── 内容区/icon/历史记录@3x.png │ │ ├── camera_4_highWox.imageset │ │ │ ├── Contents.json │ │ │ ├── 拍摄键@2x.png │ │ │ └── 拍摄键@3x.png │ │ ├── camera_4_tip.imageset │ │ │ ├── Contents.json │ │ │ ├── 弹窗提示@2x.png │ │ │ └── 弹窗提示@3x.png │ │ ├── fluorescence_4_1.imageset │ │ │ ├── Contents.json │ │ │ ├── 内容区/icon/120*120/荧光笔取词@2x.png │ │ │ └── 内容区/icon/120*120/荧光笔取词@3x.png │ │ ├── fork_4_kWa.imageset │ │ │ ├── Contents.json │ │ │ ├── 编组@2x.png │ │ │ └── 编组@3x.png │ │ ├── mine_4.imageset │ │ │ ├── Contents.json │ │ │ ├── 矩形备份 2@2x.png │ │ │ └── 矩形备份 2@3x.png │ │ ├── mine_4_arrow_item.imageset │ │ │ ├── Contents.json │ │ │ ├── 路径 8@2x.png │ │ │ └── 路径 8@3x.png │ │ ├── mine_4_selected.imageset │ │ │ ├── Contents.json │ │ │ ├── tab栏/icon/我的/选中@2x.png │ │ │ └── tab栏/icon/我的/选中@3x.png │ │ ├── mine_woX_header_4_arrow.imageset │ │ │ ├── Contents.json │ │ │ ├── 内容区/icon/历史记录@2x.png │ │ │ └── 内容区/icon/历史记录@3x.png │ │ ├── mine_woX_header_4_bg.imageset │ │ │ ├── Contents.json │ │ │ ├── 矩形@2x.png │ │ │ └── 矩形@3x.png │ │ ├── plus_4.imageset │ │ │ ├── Contents.json │ │ │ ├── tab栏/icon/加号@2x.png │ │ │ └── tab栏/icon/加号@3x.png │ │ ├── pop_fork_4.imageset │ │ │ ├── Contents.json │ │ │ ├── 内容区/icon/历史记录@2x.png │ │ │ └── 内容区/icon/历史记录@3x.png │ │ ├── rotate_4_X.imageset │ │ │ ├── Contents.json │ │ │ ├── 内容区/icon/历史记录@2x.png │ │ │ └── 内容区/icon/历史记录@3x.png │ │ ├── takenImg_4_tick.imageset │ │ │ ├── Contents.json │ │ │ ├── 拍摄键@2x.png │ │ │ └── 拍摄键@3x.png │ │ ├── textBook_4.imageset │ │ │ ├── Contents.json │ │ │ ├── 矩形备份 2@2x.png │ │ │ └── 矩形备份 2@3x.png │ │ ├── textBook_4_selected.imageset │ │ │ ├── Contents.json │ │ │ ├── tab栏/icon/课本/选中@2x.png │ │ │ └── tab栏/icon/课本/选中@3x.png │ │ └── text_4_2.imageset │ │ │ ├── Contents.json │ │ │ ├── 内容区/icon/120*120/创建文本@2x.png │ │ │ └── 内容区/icon/120*120/创建文本@3x.png │ ├── 5 │ │ ├── Contents.json │ │ └── not_a_net.imageset │ │ │ ├── Contents.json │ │ │ ├── 编组 2@2x.png │ │ │ └── 编组 2@3x.png │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── CusCamera.swift ├── Info.plist ├── SceneDelegate.swift ├── SnapKit.framework │ ├── Headers │ │ └── SnapKit-Swift.h │ ├── Info.plist │ ├── Modules │ │ ├── SnapKit.swiftmodule │ │ │ ├── Project │ │ │ │ ├── arm64-apple-ios.swiftsourceinfo │ │ │ │ └── arm64.swiftsourceinfo │ │ │ ├── arm64-apple-ios.swiftdoc │ │ │ ├── arm64-apple-ios.swiftmodule │ │ │ ├── arm64.swiftdoc │ │ │ └── arm64.swiftmodule │ │ └── module.modulemap │ └── SnapKit ├── ViewController.swift ├── crop │ ├── Array+shift.swift │ ├── CGPoint+geometry.swift │ ├── SEAreaView.swift │ ├── SECornerView.swift │ ├── SECropError.swift │ ├── SECropView.swift │ ├── SEQuadrangleHelper.swift │ └── UIView+globalCoordinates.swift ├── two │ ├── ButtomViewFinal.swift │ ├── ButtomViewP.swift │ └── LinedTipView.swift └── util │ ├── DebugLayer.swift │ ├── FontAdd.swift │ ├── RectAdd.swift │ ├── UIImageAdd.swift │ ├── ZLCustomCamera.swift │ ├── ZLGeneralDefine.swift │ └── ZLPhotoConfiguration.swift ├── captureAndFilterTests ├── Info.plist └── captureAndFilterTests.swift └── captureAndFilterUITests ├── Info.plist └── captureAndFilterUITests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | *.DS_store 30 | # CocoaPods 31 | # 32 | # We recommend against adding the Pods directory to your .gitignore. However 33 | # you should judge for yourself, the pros and cons are mentioned at: 34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 35 | # 36 | # Pods/ 37 | 38 | # Carthage 39 | # 40 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 41 | # Carthage/Checkouts 42 | 43 | Carthage/Build 44 | 45 | # fastlane 46 | # 47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 48 | # screenshots whenever they are needed. 49 | # For more information about the recommended setup visit: 50 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 51 | 52 | fastlane/report.xml 53 | fastlane/Preview.html 54 | fastlane/screenshots 55 | fastlane/test_output 56 | 57 | # Code Injection 58 | # 59 | # After new code Injection tools there's a generated folder /iOSInjectionProject 60 | # https://github.com/johnno1962/injectionforxcode 61 | 62 | iOSInjectionProject/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 邓江洲 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SmartCropTry 2 | Smart Crop Try, 努力仿照 “全能扫描王” 3 | 4 | 5 | ## [juejin blog: 仿扫描全能王的选择区域功能:拍照,旋转](https://juejin.im/post/6882950524786704398) 6 | 7 | 8 | ## [juejin blog: 低仿扫描全能王的选择区域功能](https://juejin.im/post/6875134095232335880) 9 | 10 | 11 | ### 添加选择区域,是否 OK 的交互 12 | 13 | > 有参考 rzmn/CropView 14 | 15 | 16 | ![2_1](https://github.com/BoxDengJZ/SmartCropTry/blob/master/src/second.png) 17 | 18 | 19 | ### 端点拖动, 和放大镜 20 | 21 | ![0](https://github.com/BoxDengJZ/SmartCropTry/blob/master/src/0.PNG) 22 | 23 | 24 | ### 凸四边形判定 25 | 26 | 27 | ![1](https://github.com/BoxDengJZ/SmartCropTry/blob/master/src/1.PNG) 28 | 29 | 30 | ### 交叉边,重新连线 31 | 32 | 33 | ![2](https://github.com/BoxDengJZ/SmartCropTry/blob/master/src/2.PNG) 34 | 35 | 36 | ![3](https://github.com/BoxDengJZ/SmartCropTry/blob/master/src/3.PNG) 37 | -------------------------------------------------------------------------------- /ptDrag/pointsDrag.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ptDrag/pointsDrag.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ptDrag/pointsDrag/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // pointsDrag 4 | // 5 | // Created by Jz D on 2020/9/16. 6 | // Copyright © 2020 Jz D. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | // Override point for customization after application launch. 18 | return true 19 | } 20 | 21 | // MARK: UISceneSession Lifecycle 22 | 23 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 24 | // Called when a new scene session is being created. 25 | // Use this method to select a configuration to create the new scene with. 26 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 27 | } 28 | 29 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 30 | // Called when the user discards a scene session. 31 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 32 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 33 | } 34 | 35 | 36 | } 37 | 38 | -------------------------------------------------------------------------------- /ptDrag/pointsDrag/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /ptDrag/pointsDrag/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ptDrag/pointsDrag/Assets.xcassets/single_lhs_rotate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "icon_zuozhuan@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "icon_zuozhuan@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ptDrag/pointsDrag/Assets.xcassets/single_lhs_rotate.imageset/icon_zuozhuan@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/ptDrag/pointsDrag/Assets.xcassets/single_lhs_rotate.imageset/icon_zuozhuan@2x.png -------------------------------------------------------------------------------- /ptDrag/pointsDrag/Assets.xcassets/single_lhs_rotate.imageset/icon_zuozhuan@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/ptDrag/pointsDrag/Assets.xcassets/single_lhs_rotate.imageset/icon_zuozhuan@3x.png -------------------------------------------------------------------------------- /ptDrag/pointsDrag/Assets.xcassets/single_rhs_rotate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "icon_youzhuan@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "icon_youzhuan@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ptDrag/pointsDrag/Assets.xcassets/single_rhs_rotate.imageset/icon_youzhuan@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/ptDrag/pointsDrag/Assets.xcassets/single_rhs_rotate.imageset/icon_youzhuan@2x.png -------------------------------------------------------------------------------- /ptDrag/pointsDrag/Assets.xcassets/single_rhs_rotate.imageset/icon_youzhuan@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/ptDrag/pointsDrag/Assets.xcassets/single_rhs_rotate.imageset/icon_youzhuan@3x.png -------------------------------------------------------------------------------- /ptDrag/pointsDrag/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 | -------------------------------------------------------------------------------- /ptDrag/pointsDrag/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /ptDrag/pointsDrag/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | NSCameraUsageDescription 14 | 需要 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UIApplicationSceneManifest 26 | 27 | UIApplicationSupportsMultipleScenes 28 | 29 | UISceneConfigurations 30 | 31 | UIWindowSceneSessionRoleApplication 32 | 33 | 34 | UISceneConfigurationName 35 | Default Configuration 36 | UISceneDelegateClassName 37 | $(PRODUCT_MODULE_NAME).SceneDelegate 38 | UISceneStoryboardFile 39 | Main 40 | 41 | 42 | 43 | 44 | UILaunchStoryboardName 45 | LaunchScreen 46 | UIMainStoryboardFile 47 | Main 48 | UIRequiredDeviceCapabilities 49 | 50 | armv7 51 | 52 | UISupportedInterfaceOrientations 53 | 54 | UIInterfaceOrientationPortrait 55 | UIInterfaceOrientationLandscapeLeft 56 | UIInterfaceOrientationLandscapeRight 57 | 58 | UISupportedInterfaceOrientations~ipad 59 | 60 | UIInterfaceOrientationPortrait 61 | UIInterfaceOrientationPortraitUpsideDown 62 | UIInterfaceOrientationLandscapeLeft 63 | UIInterfaceOrientationLandscapeRight 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /ptDrag/pointsDrag/MagnifierView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MagnifierView.swift 3 | // MagnifierView_demo 4 | // 5 | // Created by lidongxi on 2018/3/23. 6 | // Copyright © 2018年 lidongxi. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class MagnifierView: UIView { 12 | 13 | 14 | public var renderView: UIView? 15 | 16 | public var renderPoint : CGPoint = CGPoint.zero { 17 | didSet{ 18 | self.layer.setNeedsDisplay() 19 | } 20 | } 21 | 22 | override var isHidden: Bool { 23 | didSet{ 24 | layer.borderColor = isHidden ? UIColor.clear.cgColor : UIColor.lightGray.cgColor 25 | } 26 | } 27 | 28 | 29 | 30 | override init(frame: CGRect) { 31 | super.init(frame: frame) 32 | 33 | layer.borderWidth = 1 34 | layer.borderColor = UIColor.lightGray.cgColor 35 | isHidden = true 36 | layer.delegate = self 37 | // 保证和屏幕读取像素的比例一致 38 | layer.contentsScale = UIScreen.main.scale 39 | } 40 | 41 | required init?(coder aDecoder: NSCoder) { 42 | fatalError("init(coder:) has not been implemented") 43 | } 44 | 45 | override func draw(_ layer: CALayer, in ctx: CGContext) { 46 | super.draw(layer, in: ctx) 47 | guard let renderer = renderView else { 48 | return 49 | } 50 | // 提前位移半个长宽的坑 51 | ctx.translateBy(x: frame.size.width * 0.5, y: frame.size.height * 0.5) 52 | /// 缩放比例 53 | let scale : CGFloat = 3 54 | ctx.scaleBy(x: scale, y: scale) 55 | // 再次位移后就可以把触摸点移至self.center的位置 56 | ctx.translateBy(x: -renderPoint.x, y: -renderPoint.y) 57 | 58 | renderer.layer.render(in: ctx) 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /ptDrag/pointsDrag/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // pointsDrag 4 | // 5 | // Created by Jz D on 2020/9/16. 6 | // Copyright © 2020 Jz D. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 12 | 13 | var window: UIWindow? 14 | 15 | 16 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 17 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 18 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 19 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 20 | guard let _ = (scene as? UIWindowScene) else { return } 21 | } 22 | 23 | func sceneDidDisconnect(_ scene: UIScene) { 24 | // Called as the scene is being released by the system. 25 | // This occurs shortly after the scene enters the background, or when its session is discarded. 26 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 27 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 28 | } 29 | 30 | func sceneDidBecomeActive(_ scene: UIScene) { 31 | // Called when the scene has moved from an inactive state to an active state. 32 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 33 | } 34 | 35 | func sceneWillResignActive(_ scene: UIScene) { 36 | // Called when the scene will move from an active state to an inactive state. 37 | // This may occur due to temporary interruptions (ex. an incoming phone call). 38 | } 39 | 40 | func sceneWillEnterForeground(_ scene: UIScene) { 41 | // Called as the scene transitions from the background to the foreground. 42 | // Use this method to undo the changes made on entering the background. 43 | } 44 | 45 | func sceneDidEnterBackground(_ scene: UIScene) { 46 | // Called as the scene transitions from the foreground to the background. 47 | // Use this method to save data, release shared resources, and store enough scene-specific state information 48 | // to restore the scene back to its current state. 49 | } 50 | 51 | 52 | } 53 | 54 | -------------------------------------------------------------------------------- /ptDrag/pointsDrag/Util/FontAdd.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FontAdd.swift 3 | // 4 | // 5 | // Created by Jz D on 2019/8/23. 6 | // 7 | 8 | import UIKit 9 | 10 | 11 | 12 | enum FontWeight: String { 13 | case light = "Light" 14 | case regular = "Regular" 15 | case medium = "Medium" 16 | 17 | case semibold = "Semibold" 18 | case bold = "Bold" 19 | case heavy = "Heavy" 20 | } 21 | 22 | enum FontType: String { 23 | case PingFangSC = "PingFangSC" 24 | case SFProText = "SFProText" 25 | } 26 | 27 | extension UIFont { 28 | 29 | static func heavy(ofSize fontSize: CGFloat, type: FontType = .PingFangSC) -> UIFont { 30 | return customFont(type, weight: .heavy, fontSize: fontSize) 31 | } 32 | 33 | static func regular(ofSize fontSize: CGFloat, type: FontType = .PingFangSC) -> UIFont { 34 | return customFont(type, weight: .regular, fontSize: fontSize) 35 | } 36 | 37 | static func bold(ofSize fontSize: CGFloat, type: FontType = .PingFangSC) -> UIFont { 38 | return customFont(type, weight: .bold, fontSize: fontSize) 39 | } 40 | 41 | static func light(ofSize fontSize: CGFloat, type: FontType = .PingFangSC) -> UIFont { 42 | return customFont(type, weight: .light, fontSize: fontSize) 43 | } 44 | 45 | static func medium(ofSize fontSize: CGFloat, type: FontType = .PingFangSC) -> UIFont { 46 | return customFont(type, weight: .medium, fontSize: fontSize) 47 | } 48 | 49 | static func semibold(ofSize fontSize: CGFloat, type: FontType = .PingFangSC) -> UIFont { 50 | return customFont(type, weight: .semibold, fontSize: fontSize) 51 | } 52 | 53 | /// 自定义字体 54 | static func customFont(_ type: FontType, weight: FontWeight, fontSize: CGFloat) -> UIFont { 55 | let realFontSize = fontSize 56 | 57 | if let customFont = UIFont(name: "\(type.rawValue)-\(weight.rawValue)", size: realFontSize) { 58 | return customFont 59 | } 60 | 61 | var systemWeight = UIFont.Weight.regular 62 | switch weight { 63 | case .light: 64 | systemWeight = UIFont.Weight.light 65 | case .regular: 66 | systemWeight = UIFont.Weight.regular 67 | case .medium: 68 | systemWeight = UIFont.Weight.medium 69 | case .semibold: 70 | systemWeight = UIFont.Weight.semibold 71 | case .bold: 72 | systemWeight = UIFont.Weight.bold 73 | case .heavy: 74 | systemWeight = UIFont.Weight.heavy 75 | } 76 | return UIFont.systemFont(ofSize: realFontSize, weight: systemWeight) 77 | } 78 | } 79 | 80 | 81 | 82 | 83 | 84 | extension UIFont { 85 | 86 | static let scoreName = UIFont.medium(ofSize: 16) 87 | static let headerFirstThree = UIFont.medium(ofSize: 22) 88 | static let accountListTitle = UIFont.regular(ofSize: 16) 89 | 90 | 91 | 92 | static let myStudyTitle = UIFont.medium(ofSize: 18) 93 | static let myStudyListScoreName = UIFont.medium(ofSize: 14) 94 | static let loginHeaderName = UIFont.medium(ofSize: 16) 95 | 96 | 97 | } 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /ptDrag/pointsDrag/Util/ImgAdd.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImgAdd.swift 3 | // pointsDrag 4 | // 5 | // Created by Jz D on 2020/10/10. 6 | // Copyright © 2020 Jz D. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | 13 | 14 | extension UIImage{ 15 | 16 | 17 | func image(rotated time: Int) -> UIImage{ 18 | guard time != 0 else { 19 | return self 20 | } 21 | 22 | let radian = CGFloat(time) * CGFloat.pi / 2 23 | let rotatedSize = CGRect(origin: .zero, size: size) 24 | .applying(CGAffineTransform(rotationAngle: radian)) 25 | .integral.size 26 | UIGraphicsBeginImageContext(rotatedSize) 27 | if let context = UIGraphicsGetCurrentContext() { 28 | let origin = CGPoint(x: rotatedSize.width / 2.0, 29 | y: rotatedSize.height / 2.0) 30 | context.translateBy(x: origin.x, y: origin.y) 31 | context.rotate(by: radian) 32 | draw(in: CGRect(x: -origin.y, y: -origin.x, 33 | width: size.width, height: size.height)) 34 | let rotatedImage = UIGraphicsGetImageFromCurrentImageContext() 35 | UIGraphicsEndImageContext() 36 | 37 | return rotatedImage ?? self 38 | } 39 | 40 | return self 41 | 42 | } 43 | 44 | 45 | 46 | } 47 | 48 | 49 | 50 | extension UIImage { 51 | var leftTurn: UIImage{ 52 | image(rotated: 1) 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /ptDrag/pointsDrag/Util/PointAdd.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PointAdd.swift 3 | // pointsDrag 4 | // 5 | // Created by Jz D on 2020/10/12. 6 | // Copyright © 2020 Jz D. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | 13 | 14 | 15 | func -(lhs: CGPoint, rhs: CGPoint) -> CGPoint { 16 | return CGPoint(x: lhs.x - rhs.x, y: lhs.y - rhs.y) 17 | } 18 | 19 | 20 | 21 | // 用于坐标系变换 22 | 23 | extension CGPoint{ 24 | mutating 25 | func scale(by rate: CGFloat, forS size: CGSize){ 26 | let newX = size.width * 0.5 + (x - size.width * 0.5) * rate 27 | let newY = size.height * 0.5 + (y - size.height * 0.5) * rate 28 | self = CGPoint(x: newX, y: newY) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ptDrag/pointsDrag/Util/sizeAdd.swift: -------------------------------------------------------------------------------- 1 | // 2 | // sizeAdd.swift 3 | // pointsDrag 4 | // 5 | // Created by Jz D on 2020/10/12. 6 | // Copyright © 2020 Jz D. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | 13 | extension CGSize{ 14 | 15 | 16 | 17 | 18 | func size(in std: CGSize) -> CGSize{ 19 | 20 | let hRatio = height / std.height 21 | let wRatio = width / std.width 22 | 23 | let reSolution = height / width 24 | 25 | 26 | print(width, height) 27 | 28 | let s: CGSize 29 | if hRatio > wRatio{ 30 | s = CGSize(width: std.height / reSolution, height: std.height) 31 | } 32 | else{ 33 | s = CGSize(width: std.width, height: std.width * reSolution) 34 | } 35 | return s 36 | 37 | } 38 | 39 | 40 | func size(by horizontal: CGFloat) -> CGSize{ 41 | 42 | 43 | 44 | let reSolution = height / width 45 | 46 | 47 | print(width, height) 48 | 49 | return CGSize(width: horizontal, height: horizontal / reSolution) 50 | 51 | 52 | } 53 | 54 | 55 | func ratio(by rate: CGFloat) -> CGSize{ 56 | return CGSize(width: width * rate, height: height * rate) 57 | } 58 | 59 | 60 | 61 | var reverted: CGSize{ 62 | CGSize(width: height, height: width) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /ptDrag/pointsDrag/View/DirectionRotateB.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DirectionRotateB.swift 3 | // pointsDrag 4 | // 5 | // Created by Jz D on 2020/10/12. 6 | // Copyright © 2020 Jz D. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | 13 | 14 | enum RotateOpt{ 15 | case lhs, rhs 16 | } 17 | 18 | 19 | 20 | 21 | class DirectionRotateB: UIButton { 22 | 23 | 24 | let imgSize = CGSize(width: 32, height: 32) 25 | let titleW: CGFloat = 36 26 | 27 | let spacing: CGFloat = 6 28 | 29 | 30 | init(opt kind: RotateOpt){ 31 | 32 | super.init(frame: .zero) 33 | let img: UIImage? 34 | let title: String 35 | switch kind { 36 | case .lhs: 37 | title = "左转" 38 | 39 | img = UIImage(named: "single_lhs_rotate") 40 | case .rhs: 41 | title = "右转" 42 | 43 | img = UIImage(named: "single_rhs_rotate") 44 | } 45 | setImage(img, for: .normal) 46 | setTitle(title, for: .normal) 47 | setTitleColor(UIColor(rgb: 0x666666), for: .normal) 48 | backgroundColor = UIColor.white 49 | 50 | titleLabel?.font = UIFont.regular(ofSize: 18) 51 | } 52 | 53 | required init?(coder: NSCoder) { 54 | fatalError("init(coder:) has not been implemented") 55 | } 56 | 57 | 58 | 59 | 60 | 61 | 62 | override func imageRect(forContentRect contentRect: CGRect) -> CGRect { 63 | 64 | 65 | let x = contentRect.origin.x 66 | let y = (contentRect.size.height - imgSize.height)/2 67 | return CGRect(x: x, y: y, width: imgSize.width, height: imgSize.height) 68 | } 69 | 70 | 71 | 72 | override func titleRect(forContentRect contentRect: CGRect) -> CGRect { 73 | let titleH: CGFloat = 24 74 | let x = contentRect.origin.x + imgSize.width + spacing 75 | let y = (contentRect.size.height - titleH)/2 76 | return CGRect(x: x, y: y, width: titleW, height: titleH) 77 | 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /ptDrag/pointsDrag/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // pointsDrag 4 | // 5 | // Created by Jz D on 2020/10/10. 6 | // Copyright © 2020 Jz D. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | // Do any additional setup after loading the view. 17 | } 18 | 19 | 20 | 21 | 22 | @IBAction func photoIt(_ sender: Any) { 23 | 24 | let picker = UIImagePickerController() 25 | picker.delegate = self 26 | picker.sourceType = UIImagePickerController.SourceType.camera 27 | present(picker, animated: true) 28 | 29 | } 30 | 31 | 32 | } 33 | 34 | 35 | 36 | 37 | extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate{ 38 | 39 | 40 | func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { 41 | 42 | picker.dismiss(animated: true) 43 | 44 | 45 | if let image = info[.originalImage] as? UIImage{ 46 | var midImg = image 47 | let s = image.size 48 | if s.width > s.height{ 49 | midImg = image.leftTurn 50 | } 51 | 52 | 53 | let sketchC = SketchController() 54 | sketchC.image = midImg 55 | sketchC.modalPresentationStyle = .fullScreen 56 | present(sketchC, animated: true) {} 57 | 58 | 59 | 60 | } 61 | 62 | 63 | } 64 | 65 | 66 | 67 | } 68 | 69 | -------------------------------------------------------------------------------- /ptDragEasy/pointsDrag.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ptDragEasy/pointsDrag.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ptDragEasy/pointsDrag/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // pointsDrag 4 | // 5 | // Created by Jz D on 2020/9/16. 6 | // Copyright © 2020 Jz D. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | // Override point for customization after application launch. 18 | return true 19 | } 20 | 21 | // MARK: UISceneSession Lifecycle 22 | 23 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 24 | // Called when a new scene session is being created. 25 | // Use this method to select a configuration to create the new scene with. 26 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 27 | } 28 | 29 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 30 | // Called when the user discards a scene session. 31 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 32 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 33 | } 34 | 35 | 36 | } 37 | 38 | -------------------------------------------------------------------------------- /ptDragEasy/pointsDrag/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /ptDragEasy/pointsDrag/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ptDragEasy/pointsDrag/Assets.xcassets/single_lhs_rotate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "icon_zuozhuan@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "icon_zuozhuan@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ptDragEasy/pointsDrag/Assets.xcassets/single_lhs_rotate.imageset/icon_zuozhuan@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/ptDragEasy/pointsDrag/Assets.xcassets/single_lhs_rotate.imageset/icon_zuozhuan@2x.png -------------------------------------------------------------------------------- /ptDragEasy/pointsDrag/Assets.xcassets/single_lhs_rotate.imageset/icon_zuozhuan@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/ptDragEasy/pointsDrag/Assets.xcassets/single_lhs_rotate.imageset/icon_zuozhuan@3x.png -------------------------------------------------------------------------------- /ptDragEasy/pointsDrag/Assets.xcassets/single_rhs_rotate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "icon_youzhuan@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "icon_youzhuan@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ptDragEasy/pointsDrag/Assets.xcassets/single_rhs_rotate.imageset/icon_youzhuan@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/ptDragEasy/pointsDrag/Assets.xcassets/single_rhs_rotate.imageset/icon_youzhuan@2x.png -------------------------------------------------------------------------------- /ptDragEasy/pointsDrag/Assets.xcassets/single_rhs_rotate.imageset/icon_youzhuan@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/ptDragEasy/pointsDrag/Assets.xcassets/single_rhs_rotate.imageset/icon_youzhuan@3x.png -------------------------------------------------------------------------------- /ptDragEasy/pointsDrag/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 | -------------------------------------------------------------------------------- /ptDragEasy/pointsDrag/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /ptDragEasy/pointsDrag/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | NSCameraUsageDescription 24 | 需要 25 | UIApplicationSceneManifest 26 | 27 | UIApplicationSupportsMultipleScenes 28 | 29 | UISceneConfigurations 30 | 31 | UIWindowSceneSessionRoleApplication 32 | 33 | 34 | UISceneConfigurationName 35 | Default Configuration 36 | UISceneDelegateClassName 37 | $(PRODUCT_MODULE_NAME).SceneDelegate 38 | UISceneStoryboardFile 39 | Main 40 | 41 | 42 | 43 | 44 | UILaunchStoryboardName 45 | LaunchScreen 46 | UIMainStoryboardFile 47 | Main 48 | UIRequiredDeviceCapabilities 49 | 50 | armv7 51 | 52 | UISupportedInterfaceOrientations 53 | 54 | UIInterfaceOrientationPortrait 55 | 56 | UISupportedInterfaceOrientations~ipad 57 | 58 | UIInterfaceOrientationPortrait 59 | UIInterfaceOrientationPortraitUpsideDown 60 | UIInterfaceOrientationLandscapeLeft 61 | UIInterfaceOrientationLandscapeRight 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /ptDragEasy/pointsDrag/MagnifierView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MagnifierView.swift 3 | // MagnifierView_demo 4 | // 5 | // Created by lidongxi on 2018/3/23. 6 | // Copyright © 2018年 lidongxi. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class MagnifierView: UIView { 12 | 13 | 14 | public var renderView: UIView? 15 | 16 | public var renderPoint : CGPoint = CGPoint.zero { 17 | didSet{ 18 | self.layer.setNeedsDisplay() 19 | } 20 | } 21 | 22 | override var isHidden: Bool { 23 | didSet{ 24 | layer.borderColor = isHidden ? UIColor.clear.cgColor : UIColor.lightGray.cgColor 25 | } 26 | } 27 | 28 | 29 | 30 | override init(frame: CGRect) { 31 | super.init(frame: frame) 32 | 33 | layer.borderWidth = 1 34 | layer.borderColor = UIColor.lightGray.cgColor 35 | isHidden = true 36 | layer.delegate = self 37 | // 保证和屏幕读取像素的比例一致 38 | layer.contentsScale = UIScreen.main.scale 39 | } 40 | 41 | required init?(coder aDecoder: NSCoder) { 42 | fatalError("init(coder:) has not been implemented") 43 | } 44 | 45 | override func draw(_ layer: CALayer, in ctx: CGContext) { 46 | super.draw(layer, in: ctx) 47 | guard let renderer = renderView else { 48 | return 49 | } 50 | // 提前位移半个长宽的坑 51 | ctx.translateBy(x: frame.size.width * 0.5, y: frame.size.height * 0.5) 52 | /// 缩放比例 53 | let scale : CGFloat = 3 54 | ctx.scaleBy(x: scale, y: scale) 55 | // 再次位移后就可以把触摸点移至self.center的位置 56 | ctx.translateBy(x: -renderPoint.x, y: -renderPoint.y) 57 | 58 | renderer.layer.render(in: ctx) 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /ptDragEasy/pointsDrag/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // pointsDrag 4 | // 5 | // Created by Jz D on 2020/9/16. 6 | // Copyright © 2020 Jz D. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 12 | 13 | var window: UIWindow? 14 | 15 | 16 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 17 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 18 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 19 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 20 | guard let _ = (scene as? UIWindowScene) else { return } 21 | } 22 | 23 | func sceneDidDisconnect(_ scene: UIScene) { 24 | // Called as the scene is being released by the system. 25 | // This occurs shortly after the scene enters the background, or when its session is discarded. 26 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 27 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 28 | } 29 | 30 | func sceneDidBecomeActive(_ scene: UIScene) { 31 | // Called when the scene has moved from an inactive state to an active state. 32 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 33 | } 34 | 35 | func sceneWillResignActive(_ scene: UIScene) { 36 | // Called when the scene will move from an active state to an inactive state. 37 | // This may occur due to temporary interruptions (ex. an incoming phone call). 38 | } 39 | 40 | func sceneWillEnterForeground(_ scene: UIScene) { 41 | // Called as the scene transitions from the background to the foreground. 42 | // Use this method to undo the changes made on entering the background. 43 | } 44 | 45 | func sceneDidEnterBackground(_ scene: UIScene) { 46 | // Called as the scene transitions from the foreground to the background. 47 | // Use this method to save data, release shared resources, and store enough scene-specific state information 48 | // to restore the scene back to its current state. 49 | } 50 | 51 | 52 | } 53 | 54 | -------------------------------------------------------------------------------- /ptDragEasy/pointsDrag/SketchController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // pointsDrag 4 | // 5 | // Created by Jz D on 2020/9/16. 6 | // Copyright © 2020 Jz D. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | 13 | struct ImgLayout { 14 | 15 | static let std = ImgLayout() 16 | 17 | let w: CGFloat 18 | 19 | let h: CGFloat 20 | 21 | let center: CGPoint 22 | 23 | let s: CGSize 24 | 25 | 26 | let screenH: CGFloat 27 | 28 | let screenW: CGFloat 29 | 30 | 31 | 32 | init() { 33 | 34 | screenH = UIScreen.main.bounds.height 35 | screenW = UIScreen.main.bounds.width 36 | 37 | h = screenH - 160 38 | 39 | 40 | w = min(screenW - 80, h - 40) 41 | let edge = min(h, w) 42 | center = CGPoint(x: screenW * 0.5, y: screenH * 0.5 + 40) 43 | 44 | s = CGSize(width: edge, height: edge) 45 | 46 | 47 | } 48 | 49 | 50 | 51 | 52 | } 53 | 54 | 55 | 56 | 57 | class SketchController: UIViewController { 58 | 59 | var image: UIImage? 60 | 61 | lazy var imgView = UIImageView(image: image) 62 | 63 | lazy var sketch: SketchView = { 64 | let sk = SketchView() 65 | sk.delegate = self 66 | // sk.layer.borderColor = UIColor.green.cgColor 67 | // sk.layer.borderWidth = 2 68 | return sk 69 | }() 70 | 71 | let magnifieViewWH : CGFloat = 150 72 | 73 | lazy var magnifierV = MagnifierView(frame: CGRect(x: 20, y: 20, width: magnifieViewWH, height: magnifieViewWH)) 74 | 75 | 76 | lazy var measure = ImgLayout() 77 | 78 | 79 | lazy var lhsRotateB = DirectionRotateB(opt: .lhs) 80 | 81 | lazy var rhsRotateB = DirectionRotateB(opt: .rhs) 82 | 83 | 84 | var angle: CGFloat = 0 85 | 86 | override func viewDidLoad() { 87 | super.viewDidLoad() 88 | // Do any additional setup after loading the view. 89 | view.backgroundColor = UIColor.white 90 | 91 | 92 | if let img = image{ 93 | let s = img.size.size(in: measure.s) 94 | 95 | imgView.frame.size = s 96 | imgView.center = measure.center 97 | sketch.frame = imgView.frame 98 | view.addSubview(imgView) 99 | view.addSubview(sketch) 100 | sketch.reloadData() 101 | 102 | 103 | magnifierV.renderView = imgView 104 | view.addSubview(magnifierV) 105 | } 106 | 107 | 108 | view.addSubview(lhsRotateB) 109 | view.addSubview(rhsRotateB) 110 | let sizeB = CGSize(width: 74, height: 32) 111 | lhsRotateB.frame.size = sizeB 112 | rhsRotateB.frame.size = sizeB 113 | 114 | lhsRotateB.center = CGPoint(x: 50, y: measure.screenH - sizeB.height - 20) 115 | rhsRotateB.center = CGPoint(x: measure.screenW - sizeB.width - 50, y: measure.screenH - sizeB.height - 20) 116 | 117 | lhsRotateB.addTarget(self, action: #selector(leftTurn), for: .touchUpInside) 118 | rhsRotateB.addTarget(self, action: #selector(rightTurn), for: .touchUpInside) 119 | } 120 | 121 | 122 | @objc func leftTurn(){ 123 | rotate(with: .lhs) 124 | } 125 | 126 | 127 | 128 | @objc func rightTurn(){ 129 | rotate(with: .rhs) 130 | } 131 | 132 | 133 | 134 | func rotate(with direction: RotateOpt) { 135 | 136 | let sizeOld = sketch.frame.size 137 | let originOld = sketch.frame.origin 138 | let center = sketch.center 139 | 140 | switch direction { 141 | case .lhs: 142 | 143 | // 逆时针 144 | 145 | angle -= 1 146 | 147 | 148 | sketch.defaultPoints.update(clockwize: false, by: sizeOld) 149 | case .rhs: 150 | 151 | // 顺时针 152 | 153 | angle += 1 154 | 155 | sketch.defaultPoints.update(clockwize: true, by: sizeOld) 156 | // 下一步,对 UI 的修改,影响上一步 157 | 158 | } 159 | 160 | 161 | 162 | imgView.transform = CGAffineTransform(rotationAngle: ImgSingleAngle.time * angle) 163 | 164 | sketch.frame.size = CGSize(width: sizeOld.height, height: sizeOld.width) 165 | 166 | 167 | sketch.center = center 168 | let originNew = sketch.frame.origin 169 | 170 | sketch.defaultPoints.patch(vector: originNew - originOld) 171 | 172 | 173 | 174 | sketch.reloadData() 175 | 176 | } 177 | 178 | } 179 | 180 | 181 | 182 | 183 | extension SketchController: SketchViewProxy{ 184 | 185 | 186 | func sketch(status isStart: Bool) { 187 | magnifierV.isHidden = (isStart == false) 188 | } 189 | 190 | func sketch(moving pt: CGPoint) { 191 | // 设置渲染的中心点 192 | magnifierV.renderPoint = pt 193 | } 194 | 195 | } 196 | 197 | 198 | 199 | struct ImgSingleAngle { 200 | static let time = CGFloat.pi * 0.5 201 | } 202 | -------------------------------------------------------------------------------- /ptDragEasy/pointsDrag/Util/FontAdd.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FontAdd.swift 3 | // 4 | // 5 | // Created by Jz D on 2019/8/23. 6 | // 7 | 8 | import UIKit 9 | 10 | 11 | 12 | enum FontWeight: String { 13 | case light = "Light" 14 | case regular = "Regular" 15 | case medium = "Medium" 16 | 17 | case semibold = "Semibold" 18 | case bold = "Bold" 19 | case heavy = "Heavy" 20 | } 21 | 22 | enum FontType: String { 23 | case PingFangSC = "PingFangSC" 24 | case SFProText = "SFProText" 25 | } 26 | 27 | extension UIFont { 28 | 29 | static func heavy(ofSize fontSize: CGFloat, type: FontType = .PingFangSC) -> UIFont { 30 | return customFont(type, weight: .heavy, fontSize: fontSize) 31 | } 32 | 33 | static func regular(ofSize fontSize: CGFloat, type: FontType = .PingFangSC) -> UIFont { 34 | return customFont(type, weight: .regular, fontSize: fontSize) 35 | } 36 | 37 | static func bold(ofSize fontSize: CGFloat, type: FontType = .PingFangSC) -> UIFont { 38 | return customFont(type, weight: .bold, fontSize: fontSize) 39 | } 40 | 41 | static func light(ofSize fontSize: CGFloat, type: FontType = .PingFangSC) -> UIFont { 42 | return customFont(type, weight: .light, fontSize: fontSize) 43 | } 44 | 45 | static func medium(ofSize fontSize: CGFloat, type: FontType = .PingFangSC) -> UIFont { 46 | return customFont(type, weight: .medium, fontSize: fontSize) 47 | } 48 | 49 | static func semibold(ofSize fontSize: CGFloat, type: FontType = .PingFangSC) -> UIFont { 50 | return customFont(type, weight: .semibold, fontSize: fontSize) 51 | } 52 | 53 | /// 自定义字体 54 | static func customFont(_ type: FontType, weight: FontWeight, fontSize: CGFloat) -> UIFont { 55 | let realFontSize = fontSize 56 | 57 | if let customFont = UIFont(name: "\(type.rawValue)-\(weight.rawValue)", size: realFontSize) { 58 | return customFont 59 | } 60 | 61 | var systemWeight = UIFont.Weight.regular 62 | switch weight { 63 | case .light: 64 | systemWeight = UIFont.Weight.light 65 | case .regular: 66 | systemWeight = UIFont.Weight.regular 67 | case .medium: 68 | systemWeight = UIFont.Weight.medium 69 | case .semibold: 70 | systemWeight = UIFont.Weight.semibold 71 | case .bold: 72 | systemWeight = UIFont.Weight.bold 73 | case .heavy: 74 | systemWeight = UIFont.Weight.heavy 75 | } 76 | return UIFont.systemFont(ofSize: realFontSize, weight: systemWeight) 77 | } 78 | } 79 | 80 | 81 | 82 | 83 | 84 | extension UIFont { 85 | 86 | static let scoreName = UIFont.medium(ofSize: 16) 87 | static let headerFirstThree = UIFont.medium(ofSize: 22) 88 | static let accountListTitle = UIFont.regular(ofSize: 16) 89 | 90 | 91 | 92 | static let myStudyTitle = UIFont.medium(ofSize: 18) 93 | static let myStudyListScoreName = UIFont.medium(ofSize: 14) 94 | static let loginHeaderName = UIFont.medium(ofSize: 16) 95 | 96 | 97 | } 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /ptDragEasy/pointsDrag/Util/ImgAdd.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImgAdd.swift 3 | // pointsDrag 4 | // 5 | // Created by Jz D on 2020/10/10. 6 | // Copyright © 2020 Jz D. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | 13 | 14 | extension UIImage{ 15 | 16 | 17 | func image(rotated time: Int) -> UIImage{ 18 | guard time != 0 else { 19 | return self 20 | } 21 | 22 | let radian = CGFloat(time) * CGFloat.pi / 2 23 | let rotatedSize = CGRect(origin: .zero, size: size) 24 | .applying(CGAffineTransform(rotationAngle: radian)) 25 | .integral.size 26 | UIGraphicsBeginImageContext(rotatedSize) 27 | if let context = UIGraphicsGetCurrentContext() { 28 | let origin = CGPoint(x: rotatedSize.width / 2.0, 29 | y: rotatedSize.height / 2.0) 30 | context.translateBy(x: origin.x, y: origin.y) 31 | context.rotate(by: radian) 32 | draw(in: CGRect(x: -origin.y, y: -origin.x, 33 | width: size.width, height: size.height)) 34 | let rotatedImage = UIGraphicsGetImageFromCurrentImageContext() 35 | UIGraphicsEndImageContext() 36 | 37 | return rotatedImage ?? self 38 | } 39 | 40 | return self 41 | 42 | } 43 | 44 | 45 | 46 | } 47 | 48 | 49 | 50 | extension UIImage { 51 | var leftTurn: UIImage{ 52 | image(rotated: 1) 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /ptDragEasy/pointsDrag/Util/PointAdd.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PointAdd.swift 3 | // pointsDrag 4 | // 5 | // Created by Jz D on 2020/10/12. 6 | // Copyright © 2020 Jz D. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | 13 | 14 | 15 | func -(lhs: CGPoint, rhs: CGPoint) -> CGPoint { 16 | return CGPoint(x: lhs.x - rhs.x, y: lhs.y - rhs.y) 17 | } 18 | 19 | 20 | 21 | extension CGPoint{ 22 | mutating 23 | func scale(by rate: CGFloat, forS size: CGSize){ 24 | let newX = size.width * 0.5 * rate + (x - size.width * 0.5) * rate * 0.5 25 | let newY = size.height * 0.5 * rate + (y - size.height * 0.5) * rate * 0.5 26 | self = CGPoint(x: newX, y: newY) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ptDragEasy/pointsDrag/Util/sizeAdd.swift: -------------------------------------------------------------------------------- 1 | // 2 | // sizeAdd.swift 3 | // pointsDrag 4 | // 5 | // Created by Jz D on 2020/10/12. 6 | // Copyright © 2020 Jz D. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | 13 | extension CGSize{ 14 | 15 | 16 | 17 | 18 | func size(in std: CGSize) -> CGSize{ 19 | 20 | let hRatio = height / std.height 21 | let wRatio = width / std.width 22 | 23 | let reSolution = height / width 24 | 25 | 26 | print(width, height) 27 | 28 | let s: CGSize 29 | if hRatio > wRatio{ 30 | s = CGSize(width: std.height / reSolution, height: std.height) 31 | } 32 | else{ 33 | s = CGSize(width: std.width, height: std.width * reSolution) 34 | } 35 | return s 36 | 37 | } 38 | 39 | 40 | func size(by horizontal: CGFloat) -> CGSize{ 41 | 42 | 43 | 44 | let reSolution = height / width 45 | 46 | 47 | print(width, height) 48 | 49 | return CGSize(width: horizontal, height: horizontal / reSolution) 50 | 51 | 52 | } 53 | 54 | 55 | func ratio(by rate: CGFloat) -> CGSize{ 56 | return CGSize(width: width * rate, height: height * rate) 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /ptDragEasy/pointsDrag/View/DirectionRotateB.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DirectionRotateB.swift 3 | // pointsDrag 4 | // 5 | // Created by Jz D on 2020/10/12. 6 | // Copyright © 2020 Jz D. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | 13 | 14 | enum RotateOpt{ 15 | case lhs, rhs 16 | } 17 | 18 | 19 | 20 | 21 | class DirectionRotateB: UIButton { 22 | 23 | 24 | let imgSize = CGSize(width: 32, height: 32) 25 | let titleW: CGFloat = 36 26 | 27 | let spacing: CGFloat = 6 28 | 29 | 30 | init(opt kind: RotateOpt){ 31 | 32 | super.init(frame: .zero) 33 | let img: UIImage? 34 | let title: String 35 | switch kind { 36 | case .lhs: 37 | title = "左转" 38 | 39 | img = UIImage(named: "single_lhs_rotate") 40 | case .rhs: 41 | title = "右转" 42 | 43 | img = UIImage(named: "single_rhs_rotate") 44 | } 45 | setImage(img, for: .normal) 46 | setTitle(title, for: .normal) 47 | setTitleColor(UIColor(rgb: 0x666666), for: .normal) 48 | backgroundColor = UIColor.white 49 | 50 | titleLabel?.font = UIFont.regular(ofSize: 18) 51 | } 52 | 53 | required init?(coder: NSCoder) { 54 | fatalError("init(coder:) has not been implemented") 55 | } 56 | 57 | 58 | 59 | 60 | 61 | 62 | override func imageRect(forContentRect contentRect: CGRect) -> CGRect { 63 | 64 | 65 | let x = contentRect.origin.x 66 | let y = (contentRect.size.height - imgSize.height)/2 67 | return CGRect(x: x, y: y, width: imgSize.width, height: imgSize.height) 68 | } 69 | 70 | 71 | 72 | override func titleRect(forContentRect contentRect: CGRect) -> CGRect { 73 | let titleH: CGFloat = 24 74 | let x = contentRect.origin.x + imgSize.width + spacing 75 | let y = (contentRect.size.height - titleH)/2 76 | return CGRect(x: x, y: y, width: titleW, height: titleH) 77 | 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /ptDragEasy/pointsDrag/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // pointsDrag 4 | // 5 | // Created by Jz D on 2020/10/10. 6 | // Copyright © 2020 Jz D. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | // Do any additional setup after loading the view. 17 | } 18 | 19 | 20 | 21 | 22 | @IBAction func photoIt(_ sender: Any) { 23 | 24 | let picker = UIImagePickerController() 25 | picker.delegate = self 26 | picker.sourceType = UIImagePickerController.SourceType.camera 27 | present(picker, animated: true) 28 | 29 | } 30 | 31 | 32 | } 33 | 34 | 35 | 36 | 37 | extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate{ 38 | 39 | 40 | func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { 41 | 42 | picker.dismiss(animated: true) 43 | 44 | 45 | if let image = info[.originalImage] as? UIImage{ 46 | var midImg = image 47 | let s = image.size 48 | if s.width > s.height{ 49 | midImg = image.leftTurn 50 | } 51 | 52 | 53 | let sketchC = SketchController() 54 | sketchC.image = midImg 55 | sketchC.modalPresentationStyle = .fullScreen 56 | present(sketchC, animated: true) {} 57 | 58 | 59 | 60 | } 61 | 62 | 63 | } 64 | 65 | 66 | 67 | } 68 | 69 | -------------------------------------------------------------------------------- /secondCrop/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## [juejin blog: 照片选择区域功能的另一实现: 加动效](https://juejin.cn/post/6929917715813662727) 3 | 4 | ## [juejin blog: 仿扫描全能王的选择区域功能:拍照,旋转](https://juejin.im/post/6882950524786704398) 5 | 6 | 7 | ## [juejin blog: 低仿扫描全能王的选择区域功能](https://juejin.im/post/6875134095232335880) 8 | 9 | 10 | ### 添加选择区域,是否 OK 的交互 11 | 12 | > 有参考 rzmn/CropView 13 | 14 | 15 | ![2_1](https://github.com/BoxDengJZ/SmartCropTry/blob/master/src/second.png) 16 | 17 | 18 | ### 端点拖动, 和放大镜 19 | 20 | ![0](https://github.com/BoxDengJZ/SmartCropTry/blob/master/src/0.PNG) 21 | 22 | 23 | ### 凸四边形判定 24 | 25 | 26 | ![1](https://github.com/BoxDengJZ/SmartCropTry/blob/master/src/1.PNG) 27 | 28 | 29 | ### 交叉边,重新连线 30 | 31 | 32 | ![2](https://github.com/BoxDengJZ/SmartCropTry/blob/master/src/2.PNG) 33 | 34 | 35 | ![3](https://github.com/BoxDengJZ/SmartCropTry/blob/master/src/3.PNG) 36 | -------------------------------------------------------------------------------- /secondCrop/raw/Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /secondCrop/raw/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /secondCrop/raw/Example/Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Example 4 | // 5 | // Created by Никита Разумный on 11/6/17. 6 | // Copyright © 2017 example. 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: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { 18 | return true 19 | } 20 | 21 | func applicationWillResignActive(_ application: UIApplication) { 22 | // 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. 23 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 24 | } 25 | 26 | func applicationDidEnterBackground(_ application: UIApplication) { 27 | // 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. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | func applicationWillEnterForeground(_ application: UIApplication) { 32 | // 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. 33 | } 34 | 35 | func applicationDidBecomeActive(_ application: UIApplication) { 36 | // 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. 37 | } 38 | 39 | func applicationWillTerminate(_ application: UIApplication) { 40 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 41 | } 42 | 43 | 44 | } 45 | 46 | -------------------------------------------------------------------------------- /secondCrop/raw/Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /secondCrop/raw/Example/Example/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 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /secondCrop/raw/Example/Example/Classes/Array+shift.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Array+shift.swift 3 | // CropView 4 | // 5 | // Created by Никита Разумный on 2/3/18. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Array { 11 | func shifted(by shiftAmount: Int) -> Array { 12 | guard self.count > 0, (shiftAmount % self.count) != 0 else { return self } 13 | let moduloShiftAmount = shiftAmount % self.count 14 | 15 | let negativeShift = shiftAmount < 0 16 | let effectiveShiftAmount = negativeShift ? moduloShiftAmount + self.count : moduloShiftAmount 17 | let shift: (Int) -> Int = { return $0 + effectiveShiftAmount >= self.count ? $0 + effectiveShiftAmount - self.count : $0 + effectiveShiftAmount } 18 | 19 | return self.enumerated().sorted(by: { shift($0.offset) < shift($1.offset) }).map { $0.element } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /secondCrop/raw/Example/Example/Classes/CGPoint+geometry.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGPoint+geometry.swift 3 | // CropView 4 | // 5 | // Created by Никита Разумный on 2/3/18. 6 | // 7 | 8 | import UIKit 9 | 10 | extension CGPoint { 11 | func cartesian(for size: CGSize) -> CGPoint { 12 | return CGPoint(x: x, y: size.height - y) 13 | } 14 | static func cross(a: CGPoint, b: CGPoint) -> CGFloat { 15 | return a.x * b.y - a.y * b.x 16 | } 17 | func normalized(size: CGSize) -> CGPoint { 18 | return CGPoint(x: max(min(x, size.width), 0), y: max(min(y, size.height), 0)) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /secondCrop/raw/Example/Example/Classes/SEAreaView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AreaView.swift 3 | // CropViewController 4 | // 5 | // Created by Никита Разумный on 11/5/17. 6 | // Copyright © 2017 resquare. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SEAreaView: UIView { 12 | 13 | var cropView : SECropView? 14 | var isPathValid = true 15 | 16 | override init(frame: CGRect) { 17 | super.init(frame: frame) 18 | contentMode = .redraw 19 | 20 | CATransaction.begin() 21 | CATransaction.setDisableActions(true) 22 | CATransaction.commit() 23 | } 24 | 25 | required init?(coder aDecoder: NSCoder) { 26 | super.init(coder: aDecoder) 27 | contentMode = .redraw 28 | 29 | CATransaction.begin() 30 | CATransaction.setDisableActions(true) 31 | CATransaction.commit() 32 | } 33 | 34 | override func draw(_ rect: CGRect) { 35 | super.draw(rect) 36 | 37 | guard let path = cropView?.path else { return } 38 | 39 | let context = UIGraphicsGetCurrentContext() 40 | context?.setAllowsAntialiasing(true) 41 | context?.clip(to: rect) 42 | 43 | context?.addPath(path) 44 | context?.setLineWidth(1) 45 | context?.setLineCap(.round) 46 | context?.setLineJoin(.round) 47 | 48 | 49 | context?.setStrokeColor((isPathValid ? SECropView.goodAreaColor : SECropView.badAreaColor).cgColor) 50 | context?.strokePath() 51 | 52 | context?.saveGState() 53 | context?.addRect(bounds) 54 | context?.addPath(path) 55 | 56 | context?.setFillColor(UIColor(white: 0.3, alpha: 0.2).cgColor) 57 | context?.drawPath(using: .eoFill) 58 | 59 | context?.restoreGState() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /secondCrop/raw/Example/Example/Classes/SECornerView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CornerView.swift 3 | // CropViewController 4 | // 5 | // Created by Никита Разумный on 11/5/17. 6 | // Copyright © 2017 resquare. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SECornerView: UIView { 12 | 13 | override init(frame: CGRect) { 14 | super.init(frame: frame) 15 | layer.cornerRadius = frame.size.width / 2.0 16 | layer.borderWidth = 1.0 17 | layer.masksToBounds = true 18 | backgroundColor = UIColor.clear 19 | } 20 | 21 | required init?(coder aDecoder: NSCoder) { 22 | super.init(coder: aDecoder) 23 | } 24 | 25 | override func draw(_ rect: CGRect) { 26 | super.draw(rect) 27 | let position = superview!.convert(self.frame, to: nil) 28 | let touchPoint = position.origin 29 | 30 | let context = UIGraphicsGetCurrentContext()! 31 | 32 | context.translateBy(x: -(position.size.width / 2 - SECropView.cornerSize / 2), 33 | y: -(position.size.width / 2 - SECropView.cornerSize / 2)) 34 | 35 | context.translateBy(x: -touchPoint.x, 36 | y: -touchPoint.y) 37 | 38 | /* TODO: faster rendering 39 | isHidden = true 40 | (superview as! SECropView).areaQuadrangle.isHidden = true 41 | self.superview?.superview?.superview?.layer.render(in: context) 42 | (superview as! SECropView).areaQuadrangle.isHidden = false 43 | isHidden = false 44 | */ 45 | } 46 | 47 | func scaleUp() { 48 | UIView.animate(withDuration: 0.15, animations: { 49 | self.layer.borderWidth = 0.5 50 | self.transform = CGAffineTransform.identity.scaledBy(x: 2, y: 2) 51 | }) { (_) in 52 | self.setNeedsDisplay() 53 | } 54 | } 55 | 56 | func scaleDown() { 57 | UIView.animate(withDuration: 0.15, animations: { 58 | self.layer.borderWidth = 1 59 | self.transform = CGAffineTransform.identity 60 | }) { (_) in 61 | self.setNeedsDisplay() 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /secondCrop/raw/Example/Example/Classes/SECropError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SECropError.swift 3 | // CropView 4 | // 5 | // Created by Никита Разумный on 2/3/18. 6 | // 7 | 8 | import UIKit 9 | 10 | public enum SECropError: Error { 11 | case missingSuperview 12 | case missingImageOnImageView 13 | case invalidNumberOfCorners 14 | case nonConvexRect 15 | case missingImageWhileCropping 16 | case unknown 17 | } 18 | -------------------------------------------------------------------------------- /secondCrop/raw/Example/Example/Classes/SEQuadrangleHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SEQuadrangleHelper.swift 3 | // CropView 4 | // 5 | // Created by Никита Разумный on 2/3/18. 6 | // 7 | 8 | import UIKit 9 | import Foundation 10 | import AVFoundation 11 | 12 | public class SEQuadrangleHelper { 13 | 14 | static internal func orderPointsInQuadrangle(quad: [CGPoint]) throws -> [CGPoint] { 15 | func orderArrayClockwise(quad: [CGPoint]) -> [CGPoint] { 16 | // oriented area of quadrangle: cloclwise if it > 0 17 | var square : CGFloat = 0.0 18 | for i in 0 ..< quad.count - 1 { 19 | square += CGPoint.cross(a: CGPoint(x: quad[i].x - quad[0].x, y: quad[i].y - quad[0].y), 20 | b: CGPoint(x: quad[i + 1].x - quad[0].x, y: quad[i + 1].y - quad[0].y)) 21 | } 22 | return square > 0 ? quad : quad.reversed() 23 | } 24 | 25 | func findTopLeftPointIndex(quad: [CGPoint]) throws -> Int { 26 | var topLeftPointIdx : Int = -1 27 | var topLeftShiftValue : CGFloat = 1000.0 * 1000.0 * 1000.0 28 | 29 | for i in 0 ..< quad.count { 30 | let shiftValue = quad[i].y + quad[i].x 31 | if shiftValue < topLeftShiftValue { 32 | topLeftPointIdx = i 33 | topLeftShiftValue = shiftValue 34 | } 35 | } 36 | guard topLeftPointIdx != -1 else { throw SECropError.unknown } 37 | return topLeftPointIdx 38 | } 39 | 40 | guard quad.count == 4 else { throw SECropError.invalidNumberOfCorners } 41 | guard checkConvex(corners: quad) else { throw SECropError.nonConvexRect } 42 | 43 | let orderedQuad = orderArrayClockwise(quad: quad) 44 | let topLeftIdx = try findTopLeftPointIndex(quad: orderedQuad) 45 | return orderedQuad.shifted(by: orderedQuad.count - topLeftIdx) 46 | } 47 | 48 | static public func cropImage(with image: UIImage, quad: [CGPoint]) throws -> UIImage { 49 | 50 | let ciImage = CIImage(image: image) 51 | 52 | let perspectiveCorrection = CIFilter(name: "CIPerspectiveCorrection") 53 | let imgSize = CGSize(width: image.size.width * image.scale, 54 | height: image.size.height * image.scale) 55 | 56 | let orderedQuad = try orderPointsInQuadrangle(quad: quad) 57 | let context = CIContext(options: nil) 58 | print("ordered quad: ", orderedQuad) 59 | 60 | guard let transform = perspectiveCorrection else { throw SECropError.unknown } 61 | transform.setValue(CIVector(cgPoint: orderedQuad[0].cartesian(for: imgSize)), 62 | forKey: "inputTopLeft") 63 | transform.setValue(CIVector(cgPoint: orderedQuad[1].cartesian(for: imgSize)), 64 | forKey: "inputTopRight") 65 | transform.setValue(CIVector(cgPoint: orderedQuad[2].cartesian(for: imgSize)), 66 | forKey: "inputBottomRight") 67 | transform.setValue(CIVector(cgPoint: orderedQuad[3].cartesian(for: imgSize)), 68 | forKey: "inputBottomLeft") 69 | transform.setValue(ciImage, forKey: kCIInputImageKey) 70 | 71 | guard let perspectiveCorrectedImg = transform.outputImage, let cgImage = context.createCGImage(perspectiveCorrectedImg, from: perspectiveCorrectedImg.extent) else { throw SECropError.unknown } 72 | 73 | return UIImage(cgImage: cgImage) 74 | } 75 | 76 | static internal func checkConvex(corners: [CGPoint]) -> Bool { 77 | guard corners.count > 2 else { 78 | return false 79 | } 80 | var positiveCount = 0 81 | var negativeCount = 0 82 | for i in 0 ..< corners.count { 83 | let p0 = corners[i] 84 | let p1 = corners[(i + 1) % corners.count] 85 | let p2 = corners[(i + 2) % corners.count] 86 | 87 | let cross = (p1.x - p0.x) * (p2.y - p1.y) - (p1.y - p0.y) * (p2.x - p1.x); 88 | if cross > 0 { 89 | positiveCount += 1 90 | } else if cross < 0 { 91 | negativeCount += 1 92 | } 93 | } 94 | return positiveCount == corners.count || negativeCount == corners.count 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /secondCrop/raw/Example/Example/Classes/UIView+globalCoordinates.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+globalCoordinates.swift 3 | // CropView 4 | // 5 | // Created by Никита Разумный on 2/3/18. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIView { 11 | var globalPoint :CGPoint? { 12 | return self.superview?.convert(self.frame.origin, to: nil) 13 | } 14 | var globalFrame :CGRect? { 15 | return self.superview?.convert(self.frame, to: nil) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /secondCrop/raw/Example/Example/ImageViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageViewController.swift 3 | // Example 4 | // 5 | // Created by Никита Разумный on 2/3/18. 6 | // Copyright © 2018 example. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ImageViewController: UIViewController { 12 | @IBOutlet weak var imageView: UIImageView! 13 | var image : UIImage? 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | imageView.image = image 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /secondCrop/raw/Example/Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeRight 35 | UIInterfaceOrientationLandscapeLeft 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /secondCrop/raw/Example/Example/Resources/paper.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/secondCrop/raw/Example/Example/Resources/paper.jpg -------------------------------------------------------------------------------- /secondCrop/raw/Example/Example/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Example 4 | // 5 | // Created by Никита Разумный on 11/6/17. 6 | // Copyright © 2017 example. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | import AVFoundation 12 | 13 | class ViewController: UIViewController { 14 | 15 | let cropView = SECropView() 16 | @IBOutlet weak var imageView: UIImageView! 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | // points are defined in the coordinate system of the image 21 | cropView.configureWithCorners(corners: [CGPoint(x: 120, y: 100), 22 | CGPoint(x: 270, y: 170), 23 | CGPoint(x: 280, y: 450), 24 | CGPoint(x: 120, y: 400)], on: imageView) 25 | } 26 | 27 | override func viewDidAppear(_ animated: Bool) { 28 | super.viewDidAppear(animated) 29 | /* 30 | if you want to re-set cropView coordinates 31 | cropView.setCorners(newCorners: [CGPoint(x: 240, y: 200), 32 | CGPoint(x: 540, y: 340), 33 | CGPoint(x: 560, y: 900), 34 | CGPoint(x: 240, y: 800)]) 35 | */ 36 | } 37 | 38 | @IBAction func saveImg(_ sender: Any) { 39 | do { 40 | guard let corners = cropView.cornerLocations else { return } 41 | guard let image = imageView.image else { return } 42 | 43 | let croppedImage = try SEQuadrangleHelper.cropImage(with: image, quad: corners) 44 | 45 | performSegue(withIdentifier: "doCrop", sender: croppedImage) 46 | } catch let error as SECropError { 47 | print(error) 48 | } catch { 49 | print("Something went wrong, are you feeling OK?") 50 | } 51 | } 52 | 53 | override func prepare(for segue: UIStoryboardSegue, sender: Any?){ 54 | guard let vc = segue.destination as? ImageViewController else { return } 55 | guard let img = sender as? UIImage else { return } 56 | vc.image = img 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /secondCrop/raw/Pod/Classes/Array+shift.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Array+shift.swift 3 | // CropView 4 | // 5 | // Created by Никита Разумный on 2/3/18. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Array { 11 | func shifted(by shiftAmount: Int) -> Array { 12 | guard self.count > 0, (shiftAmount % self.count) != 0 else { return self } 13 | let moduloShiftAmount = shiftAmount % self.count 14 | 15 | let negativeShift = shiftAmount < 0 16 | let effectiveShiftAmount = negativeShift ? moduloShiftAmount + self.count : moduloShiftAmount 17 | let shift: (Int) -> Int = { return $0 + effectiveShiftAmount >= self.count ? $0 + effectiveShiftAmount - self.count : $0 + effectiveShiftAmount } 18 | 19 | return self.enumerated().sorted(by: { shift($0.offset) < shift($1.offset) }).map { $0.element } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /secondCrop/raw/Pod/Classes/CGPoint+geometry.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGPoint+geometry.swift 3 | // CropView 4 | // 5 | // Created by Никита Разумный on 2/3/18. 6 | // 7 | 8 | import UIKit 9 | 10 | extension CGPoint { 11 | func cartesian(for size: CGSize) -> CGPoint { 12 | return CGPoint(x: x, y: size.height - y) 13 | } 14 | static func cross(a: CGPoint, b: CGPoint) -> CGFloat { 15 | return a.x * b.y - a.y * b.x 16 | } 17 | func normalized(size: CGSize) -> CGPoint { 18 | return CGPoint(x: max(min(x, size.width), 0), y: max(min(y, size.height), 0)) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /secondCrop/raw/Pod/Classes/SEAreaView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AreaView.swift 3 | // CropViewController 4 | // 5 | // Created by Никита Разумный on 11/5/17. 6 | // Copyright © 2017 resquare. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SEAreaView: UIView { 12 | 13 | var cropView : SECropView? 14 | var isPathValid = true 15 | 16 | override init(frame: CGRect) { 17 | super.init(frame: frame) 18 | contentMode = .redraw 19 | 20 | CATransaction.begin() 21 | CATransaction.setDisableActions(true) 22 | CATransaction.commit() 23 | } 24 | 25 | required init?(coder aDecoder: NSCoder) { 26 | super.init(coder: aDecoder) 27 | contentMode = .redraw 28 | 29 | CATransaction.begin() 30 | CATransaction.setDisableActions(true) 31 | CATransaction.commit() 32 | } 33 | 34 | override func draw(_ rect: CGRect) { 35 | super.draw(rect) 36 | 37 | guard let path = cropView?.path else { return } 38 | 39 | let context = UIGraphicsGetCurrentContext() 40 | context?.setAllowsAntialiasing(true) 41 | context?.clip(to: rect) 42 | 43 | context?.addPath(path) 44 | context?.setLineWidth(1) 45 | context?.setLineCap(.round) 46 | context?.setLineJoin(.round) 47 | 48 | 49 | context?.setStrokeColor((isPathValid ? SECropView.goodAreaColor : SECropView.badAreaColor).cgColor) 50 | context?.strokePath() 51 | 52 | context?.saveGState() 53 | context?.addRect(bounds) 54 | context?.addPath(path) 55 | 56 | context?.setFillColor(UIColor(white: 0.3, alpha: 0.2).cgColor) 57 | context?.drawPath(using: .eoFill) 58 | 59 | context?.restoreGState() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /secondCrop/raw/Pod/Classes/SECornerView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CornerView.swift 3 | // CropViewController 4 | // 5 | // Created by Никита Разумный on 11/5/17. 6 | // Copyright © 2017 resquare. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SECornerView: UIView { 12 | 13 | override init(frame: CGRect) { 14 | super.init(frame: frame) 15 | layer.cornerRadius = frame.size.width / 2.0 16 | layer.borderWidth = 1.0 17 | layer.masksToBounds = true 18 | backgroundColor = UIColor.clear 19 | } 20 | 21 | required init?(coder aDecoder: NSCoder) { 22 | super.init(coder: aDecoder) 23 | } 24 | 25 | override func draw(_ rect: CGRect) { 26 | super.draw(rect) 27 | let position = superview!.convert(self.frame, to: nil) 28 | let touchPoint = position.origin 29 | 30 | let context = UIGraphicsGetCurrentContext()! 31 | 32 | context.translateBy(x: -(position.size.width / 2 - SECropView.cornerSize / 2), 33 | y: -(position.size.width / 2 - SECropView.cornerSize / 2)) 34 | 35 | context.translateBy(x: -touchPoint.x, 36 | y: -touchPoint.y) 37 | 38 | /* TODO: faster rendering 39 | isHidden = true 40 | (superview as! SECropView).areaQuadrangle.isHidden = true 41 | self.superview?.superview?.superview?.layer.render(in: context) 42 | (superview as! SECropView).areaQuadrangle.isHidden = false 43 | isHidden = false 44 | */ 45 | } 46 | 47 | func scaleUp() { 48 | UIView.animate(withDuration: 0.15, animations: { 49 | self.layer.borderWidth = 0.5 50 | self.transform = CGAffineTransform.identity.scaledBy(x: 2, y: 2) 51 | }) { (_) in 52 | self.setNeedsDisplay() 53 | } 54 | } 55 | 56 | func scaleDown() { 57 | UIView.animate(withDuration: 0.15, animations: { 58 | self.layer.borderWidth = 1 59 | self.transform = CGAffineTransform.identity 60 | }) { (_) in 61 | self.setNeedsDisplay() 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /secondCrop/raw/Pod/Classes/SECropError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SECropError.swift 3 | // CropView 4 | // 5 | // Created by Никита Разумный on 2/3/18. 6 | // 7 | 8 | import UIKit 9 | 10 | public enum SECropError: Error { 11 | case missingSuperview 12 | case missingImageOnImageView 13 | case invalidNumberOfCorners 14 | case nonConvexRect 15 | case missingImageWhileCropping 16 | case unknown 17 | } 18 | -------------------------------------------------------------------------------- /secondCrop/raw/Pod/Classes/SEQuadrangleHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SEQuadrangleHelper.swift 3 | // CropView 4 | // 5 | // Created by Никита Разумный on 2/3/18. 6 | // 7 | 8 | import UIKit 9 | import Foundation 10 | import AVFoundation 11 | 12 | public class SEQuadrangleHelper { 13 | 14 | static internal func orderPointsInQuadrangle(quad: [CGPoint]) throws -> [CGPoint] { 15 | func orderArrayClockwise(quad: [CGPoint]) -> [CGPoint] { 16 | // oriented area of quadrangle: cloclwise if it > 0 17 | var square : CGFloat = 0.0 18 | for i in 0 ..< quad.count - 1 { 19 | square += CGPoint.cross(a: CGPoint(x: quad[i].x - quad[0].x, y: quad[i].y - quad[0].y), 20 | b: CGPoint(x: quad[i + 1].x - quad[0].x, y: quad[i + 1].y - quad[0].y)) 21 | } 22 | return square > 0 ? quad : quad.reversed() 23 | } 24 | 25 | func findTopLeftPointIndex(quad: [CGPoint]) throws -> Int { 26 | var topLeftPointIdx : Int = -1 27 | var topLeftShiftValue : CGFloat = 1000.0 * 1000.0 * 1000.0 28 | 29 | for i in 0 ..< quad.count { 30 | let shiftValue = quad[i].y + quad[i].x 31 | if shiftValue < topLeftShiftValue { 32 | topLeftPointIdx = i 33 | topLeftShiftValue = shiftValue 34 | } 35 | } 36 | guard topLeftPointIdx != -1 else { throw SECropError.unknown } 37 | return topLeftPointIdx 38 | } 39 | 40 | guard quad.count == 4 else { throw SECropError.invalidNumberOfCorners } 41 | guard checkConvex(corners: quad) else { throw SECropError.nonConvexRect } 42 | 43 | let orderedQuad = orderArrayClockwise(quad: quad) 44 | let topLeftIdx = try findTopLeftPointIndex(quad: orderedQuad) 45 | return orderedQuad.shifted(by: orderedQuad.count - topLeftIdx) 46 | } 47 | 48 | static public func cropImage(with image: UIImage, quad: [CGPoint]) throws -> UIImage { 49 | 50 | let ciImage = CIImage(image: image) 51 | 52 | let perspectiveCorrection = CIFilter(name: "CIPerspectiveCorrection") 53 | let imgSize = CGSize(width: image.size.width * image.scale, 54 | height: image.size.height * image.scale) 55 | 56 | let orderedQuad = try orderPointsInQuadrangle(quad: quad) 57 | let context = CIContext(options: nil) 58 | print("ordered quad: ", orderedQuad) 59 | 60 | guard let transform = perspectiveCorrection else { throw SECropError.unknown } 61 | transform.setValue(CIVector(cgPoint: orderedQuad[0].cartesian(for: imgSize)), 62 | forKey: "inputTopLeft") 63 | transform.setValue(CIVector(cgPoint: orderedQuad[1].cartesian(for: imgSize)), 64 | forKey: "inputTopRight") 65 | transform.setValue(CIVector(cgPoint: orderedQuad[2].cartesian(for: imgSize)), 66 | forKey: "inputBottomRight") 67 | transform.setValue(CIVector(cgPoint: orderedQuad[3].cartesian(for: imgSize)), 68 | forKey: "inputBottomLeft") 69 | transform.setValue(ciImage, forKey: kCIInputImageKey) 70 | 71 | guard let perspectiveCorrectedImg = transform.outputImage, let cgImage = context.createCGImage(perspectiveCorrectedImg, from: perspectiveCorrectedImg.extent) else { throw SECropError.unknown } 72 | 73 | return UIImage(cgImage: cgImage) 74 | } 75 | 76 | static internal func checkConvex(corners: [CGPoint]) -> Bool { 77 | guard corners.count > 2 else { 78 | return false 79 | } 80 | var positiveCount = 0 81 | var negativeCount = 0 82 | for i in 0 ..< corners.count { 83 | let p0 = corners[i] 84 | let p1 = corners[(i + 1) % corners.count] 85 | let p2 = corners[(i + 2) % corners.count] 86 | 87 | let cross = (p1.x - p0.x) * (p2.y - p1.y) - (p1.y - p0.y) * (p2.x - p1.x); 88 | if cross > 0 { 89 | positiveCount += 1 90 | } else if cross < 0 { 91 | negativeCount += 1 92 | } 93 | } 94 | return positiveCount == corners.count || negativeCount == corners.count 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /secondCrop/raw/Pod/Classes/UIView+globalCoordinates.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+globalCoordinates.swift 3 | // CropView 4 | // 5 | // Created by Никита Разумный on 2/3/18. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIView { 11 | var globalPoint :CGPoint? { 12 | return self.superview?.convert(self.frame.origin, to: nil) 13 | } 14 | var globalFrame :CGRect? { 15 | return self.superview?.convert(self.frame, to: nil) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /secondCrop/raw/README.md: -------------------------------------------------------------------------------- 1 | # CropView 2 | Quadrangle view and image cropper like a Notes App 3 | 4 | 5 | 6 | # Demo 7 | ![Screenshot](https://github.com/rzmn/CropView/blob/master/sample.gif) 8 | 9 | # Usage 10 | ### Initialization 11 | Set initial coordinates in points 12 | ``` swift 13 | cropView.configureWithCorners(corners: [CGPoint(x: 120, y: 100), 14 | CGPoint(x: 270, y: 170), 15 | CGPoint(x: 280, y: 450), 16 | CGPoint(x: 120, y: 400)], on: imageView) 17 | ``` 18 | ### Customisation 19 | Customize color of your corners/contour and corners size 20 | ``` swift 21 | // SECornerView.swift 22 | static var cornerSize : CGFloat = 50.0 23 | ``` 24 | ``` swift 25 | // SECropView.swift 26 | static var goodAreaColor = #colorLiteral(red: 0.4666666687, green: 0.7647058964, blue: 0.2666666806, alpha: 1) 27 | static var badAreaColor = #colorLiteral(red: 0.9254902005, green: 0.2352941185, blue: 0.1019607857, alpha: 1) 28 | ``` 29 | ### Geometry 30 | ``` swift 31 | // SEQuadrangleHelper.swift 32 | 33 | // Crop image by quadrangle points in pixels 34 | static public func cropImage(with image: UIImage, quad: [CGPoint]) throws -> UIImage 35 | // get image frame in UIImageView and convert quadrangle coordinates into image pixels 36 | static public func getCoordinatesOnImageWithoutScale(on imageView: UIImageView, with cropView: SECropView) throws -> Array 37 | // get quadrangle coordinates, related by UIImageView frame in pixels 38 | static public func getCoordinatesOnImageView(on imageView: UIImageView, with cropView: SECropView) throws -> Array 39 | ``` 40 | 41 | -------------------------------------------------------------------------------- /secondCrop/secondCrop.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /secondCrop/secondCrop.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /secondCrop/secondCrop/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // secondCrop 4 | // 5 | // Created by Jz D on 2021/2/2. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | return true 18 | } 19 | 20 | // MARK: UISceneSession Lifecycle 21 | 22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 23 | // Called when a new scene session is being created. 24 | // Use this method to select a configuration to create the new scene with. 25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 26 | } 27 | 28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 29 | // Called when the user discards a scene session. 30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 32 | } 33 | 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /secondCrop/secondCrop/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /secondCrop/secondCrop/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /secondCrop/secondCrop/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /secondCrop/secondCrop/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 | -------------------------------------------------------------------------------- /secondCrop/secondCrop/ImageViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageViewController.swift 3 | // Example 4 | // 5 | // Created by Никита Разумный on 2/3/18. 6 | // Copyright © 2018 example. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ImageViewController: UIViewController { 12 | @IBOutlet weak var imageView: UIImageView! 13 | var image : UIImage? 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | imageView.image = image 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /secondCrop/secondCrop/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UIApplicationSupportsIndirectInputEvents 43 | 44 | UILaunchStoryboardName 45 | LaunchScreen 46 | UIMainStoryboardFile 47 | Main 48 | UIRequiredDeviceCapabilities 49 | 50 | armv7 51 | 52 | UISupportedInterfaceOrientations 53 | 54 | UIInterfaceOrientationPortrait 55 | UIInterfaceOrientationLandscapeLeft 56 | UIInterfaceOrientationLandscapeRight 57 | 58 | UISupportedInterfaceOrientations~ipad 59 | 60 | UIInterfaceOrientationPortrait 61 | UIInterfaceOrientationPortraitUpsideDown 62 | UIInterfaceOrientationLandscapeLeft 63 | UIInterfaceOrientationLandscapeRight 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /secondCrop/secondCrop/Resources/paper.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/secondCrop/secondCrop/Resources/paper.jpg -------------------------------------------------------------------------------- /secondCrop/secondCrop/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // secondCrop 4 | // 5 | // Created by Jz D on 2021/2/2. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 19 | guard let _ = (scene as? UIWindowScene) else { return } 20 | } 21 | 22 | func sceneDidDisconnect(_ scene: UIScene) { 23 | // Called as the scene is being released by the system. 24 | // This occurs shortly after the scene enters the background, or when its session is discarded. 25 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 26 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). 27 | } 28 | 29 | func sceneDidBecomeActive(_ scene: UIScene) { 30 | // Called when the scene has moved from an inactive state to an active state. 31 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 32 | } 33 | 34 | func sceneWillResignActive(_ scene: UIScene) { 35 | // Called when the scene will move from an active state to an inactive state. 36 | // This may occur due to temporary interruptions (ex. an incoming phone call). 37 | } 38 | 39 | func sceneWillEnterForeground(_ scene: UIScene) { 40 | // Called as the scene transitions from the background to the foreground. 41 | // Use this method to undo the changes made on entering the background. 42 | } 43 | 44 | func sceneDidEnterBackground(_ scene: UIScene) { 45 | // Called as the scene transitions from the foreground to the background. 46 | // Use this method to save data, release shared resources, and store enough scene-specific state information 47 | // to restore the scene back to its current state. 48 | } 49 | 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /secondCrop/secondCrop/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // secondCrop 4 | // 5 | // Created by Jz D on 2021/2/2. 6 | // 7 | 8 | import UIKit 9 | 10 | class ViewController: UIViewController { 11 | 12 | let cropView = SECropView() 13 | @IBOutlet weak var imageView: UIImageView! 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | // Do any additional setup after loading the view. 18 | cropView.configure(corners: imageView) 19 | } 20 | 21 | 22 | 23 | @IBAction func saveImg(_ sender: Any) { 24 | do { 25 | guard let corners = cropView.cornerLocations else{ 26 | return 27 | } 28 | let croppedImage = try SEQuadrangleHelper.cropImage(in: imageView, quad: corners) 29 | performSegue(withIdentifier: "doCrop", sender: croppedImage) 30 | } catch let error as SECropError { 31 | print(error) 32 | } catch { 33 | print(error) 34 | } 35 | } 36 | 37 | override func prepare(for segue: UIStoryboardSegue, sender: Any?){ 38 | guard let vc = segue.destination as? ImageViewController else { return } 39 | guard let img = sender as? UIImage else { return } 40 | vc.image = img 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /secondCrop/secondCrop/crop/Array+shift.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Array+shift.swift 3 | // CropView 4 | // 5 | // Created by Никита Разумный on 2/3/18. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Array { 11 | func shifted(by shiftAmount: Int) -> [Element]{ 12 | guard count > 0, (shiftAmount % count) != 0 else { return self } 13 | let moduloShiftAmount = shiftAmount % count 14 | let effectiveShiftAmount: Int 15 | if shiftAmount < 0{ 16 | effectiveShiftAmount = moduloShiftAmount + count 17 | } 18 | else{ 19 | effectiveShiftAmount = moduloShiftAmount 20 | } 21 | let shift: (Int) -> Int = { 22 | if $0 + effectiveShiftAmount >= count{ 23 | return $0 + effectiveShiftAmount - count 24 | } 25 | else{ 26 | return $0 + effectiveShiftAmount 27 | } 28 | } 29 | return enumerated().sorted(by: { shift($0.offset) < shift($1.offset) }).map { $0.element } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /secondCrop/secondCrop/crop/CGPoint+geometry.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGPoint+geometry.swift 3 | // CropView 4 | // 5 | // Created by Никита Разумный on 2/3/18. 6 | // 7 | 8 | import UIKit 9 | 10 | extension CGPoint { 11 | func cartesian(for size: CGSize) -> CGPoint { 12 | return CGPoint(x: x, y: size.height - y) 13 | } 14 | static func cross(a: CGPoint, b: CGPoint) -> CGFloat { 15 | return a.x * b.y - a.y * b.x 16 | } 17 | func normalized(size: CGSize) -> CGPoint { 18 | return CGPoint(x: max(min(x, size.width), 0), y: max(min(y, size.height), 0)) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /secondCrop/secondCrop/crop/SEAreaView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AreaView.swift 3 | // CropViewController 4 | // 5 | // Created by Никита Разумный on 11/5/17. 6 | // Copyright © 2017 resquare. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SEAreaView: UIView { 12 | 13 | var path: CGMutablePath? 14 | var isPathValid = true 15 | 16 | 17 | override func draw(_ rect: CGRect) { 18 | super.draw(rect) 19 | 20 | guard let p = path else { return } 21 | 22 | let context = UIGraphicsGetCurrentContext() 23 | context?.setAllowsAntialiasing(true) 24 | context?.clip(to: rect) 25 | 26 | context?.addPath(p) 27 | context?.setLineWidth(1) 28 | context?.setLineCap(.round) 29 | context?.setLineJoin(.round) 30 | 31 | 32 | context?.setStrokeColor((isPathValid ? Setting.std.goodAreaColor : Setting.std.badAreaColor).cgColor) 33 | context?.strokePath() 34 | context?.addRect(bounds) 35 | context?.addPath(p) 36 | 37 | context?.setFillColor(UIColor(white: 0.3, alpha: 0.2).cgColor) 38 | context?.drawPath(using: .eoFill) 39 | } 40 | 41 | 42 | func fill(path p: CGMutablePath){ 43 | path = p 44 | setNeedsDisplay() 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /secondCrop/secondCrop/crop/SECornerView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CornerView.swift 3 | // CropViewController 4 | // 5 | // Created by Никита Разумный on 11/5/17. 6 | // Copyright © 2017 resquare. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SECornerView: UIView { 12 | 13 | override init(frame: CGRect) { 14 | super.init(frame: frame) 15 | layer.cornerRadius = frame.size.width / 2.0 16 | layer.borderWidth = 1.0 17 | layer.masksToBounds = true 18 | backgroundColor = UIColor.clear 19 | } 20 | 21 | required init?(coder aDecoder: NSCoder) { 22 | super.init(coder: aDecoder) 23 | } 24 | 25 | func scaleUp() { 26 | UIView.animate(withDuration: 0.15, animations: { 27 | self.layer.borderWidth = 0.5 28 | self.transform = CGAffineTransform.identity.scaledBy(x: 2, y: 2) 29 | }) 30 | } 31 | 32 | func scaleDown() { 33 | UIView.animate(withDuration: 0.15, animations: { 34 | self.layer.borderWidth = 1 35 | self.transform = CGAffineTransform.identity 36 | }) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /secondCrop/secondCrop/crop/SECropError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SECropError.swift 3 | // CropView 4 | // 5 | // Created by Никита Разумный on 2/3/18. 6 | // 7 | 8 | import UIKit 9 | 10 | public enum SECropError: Error { 11 | case missingSuperview 12 | case missingImageOnImageView 13 | case invalidNumberOfCorners 14 | case nonConvexRect 15 | case missingImageWhileCropping 16 | case unknown 17 | case noImage 18 | } 19 | -------------------------------------------------------------------------------- /secondCrop/secondCrop/crop/SEQuadrangleHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SEQuadrangleHelper.swift 3 | // CropView 4 | // 5 | // Created by Никита Разумный on 2/3/18. 6 | // 7 | 8 | import UIKit 9 | import Foundation 10 | import AVFoundation 11 | 12 | public class SEQuadrangleHelper { 13 | 14 | static internal func orderPointsInQuadrangle(quad: [CGPoint]) throws -> [CGPoint] { 15 | func orderArrayClockwise(quad: [CGPoint]) -> [CGPoint] { 16 | // oriented area of quadrangle: cloclwise if it > 0 17 | var square: CGFloat = 0.0 18 | for i in 0 ..< quad.count - 1 { 19 | square += CGPoint.cross(a: CGPoint(x: quad[i].x - quad[0].x, y: quad[i].y - quad[0].y), 20 | b: CGPoint(x: quad[i + 1].x - quad[0].x, y: quad[i + 1].y - quad[0].y)) 21 | } 22 | return square > 0 ? quad : quad.reversed() 23 | } 24 | 25 | func findTopLeftPointIndex(quad: [CGPoint]) throws -> Int { 26 | var topLeftPointIdx : Int = -1 27 | var topLeftShiftValue : CGFloat = 1000.0 * 1000.0 * 1000.0 28 | 29 | for i in 0 ..< quad.count { 30 | let shiftValue = quad[i].y + quad[i].x 31 | if shiftValue < topLeftShiftValue { 32 | topLeftPointIdx = i 33 | topLeftShiftValue = shiftValue 34 | } 35 | } 36 | guard topLeftPointIdx != -1 else { throw SECropError.unknown } 37 | return topLeftPointIdx 38 | } 39 | 40 | guard quad.count == 4 else { throw SECropError.invalidNumberOfCorners } 41 | guard checkConvex(corners: quad) else { throw SECropError.nonConvexRect } 42 | 43 | let orderedQuad = orderArrayClockwise(quad: quad) 44 | let topLeftIdx = try findTopLeftPointIndex(quad: orderedQuad) 45 | return orderedQuad.shifted(by: orderedQuad.count - topLeftIdx) 46 | } 47 | 48 | static public func cropImage(in imageView: UIImageView, quad corners: [CGPoint]) throws -> UIImage { 49 | guard let image = imageView.image else { 50 | throw SECropError.noImage 51 | } 52 | let imgSize = image.size 53 | let f = AVMakeRect(aspectRatio: imgSize, insideRect: imageView.bounds) 54 | let quad = corners.map { (pt) -> CGPoint in 55 | return pt.inner(img: imgSize, relative: f.size) 56 | } 57 | let ciImage = CIImage(image: image) 58 | 59 | let perspectiveCorrection = CIFilter(name: "CIPerspectiveCorrection") 60 | 61 | let orderedQuad = try orderPointsInQuadrangle(quad: quad) 62 | let context = CIContext(options: nil) 63 | let pt = orderedQuad[2] 64 | print("ordered quad: ", orderedQuad, pt.y / pt.x, "imgSize: ", imgSize, imgSize.height/imgSize.width) 65 | 66 | guard let transform = perspectiveCorrection else { throw SECropError.unknown } 67 | transform.setValue(CIVector(cgPoint: orderedQuad[0].cartesian(for: imgSize)), 68 | forKey: "inputTopLeft") 69 | transform.setValue(CIVector(cgPoint: orderedQuad[1].cartesian(for: imgSize)), 70 | forKey: "inputTopRight") 71 | transform.setValue(CIVector(cgPoint: orderedQuad[2].cartesian(for: imgSize)), 72 | forKey: "inputBottomRight") 73 | transform.setValue(CIVector(cgPoint: orderedQuad[3].cartesian(for: imgSize)), 74 | forKey: "inputBottomLeft") 75 | transform.setValue(ciImage, forKey: kCIInputImageKey) 76 | 77 | guard let perspectiveCorrectedImg = transform.outputImage, let cgImage = context.createCGImage(perspectiveCorrectedImg, from: perspectiveCorrectedImg.extent) else { throw SECropError.unknown } 78 | 79 | return UIImage(cgImage: cgImage) 80 | } 81 | 82 | static internal func checkConvex(corners: [CGPoint]) -> Bool { 83 | guard corners.count > 2 else { 84 | return false 85 | } 86 | var positiveCount = 0 87 | var negativeCount = 0 88 | for i in 0 ..< corners.count { 89 | let p0 = corners[i] 90 | let p1 = corners[(i + 1) % corners.count] 91 | let p2 = corners[(i + 2) % corners.count] 92 | 93 | let cross = (p1.x - p0.x) * (p2.y - p1.y) - (p1.y - p0.y) * (p2.x - p1.x); 94 | if cross > 0 { 95 | positiveCount += 1 96 | } else if cross < 0 { 97 | negativeCount += 1 98 | } 99 | } 100 | return positiveCount == corners.count || negativeCount == corners.count 101 | } 102 | } 103 | 104 | 105 | 106 | 107 | extension CGPoint{ 108 | func inner(img s: CGSize, relative dot: CGSize) -> CGPoint{ 109 | let xx = x * s.width/dot.width 110 | let yy = y * s.height/dot.height 111 | return CGPoint(x: xx, y: yy) 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /secondCrop/secondCrop/crop/UIView+globalCoordinates.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+globalCoordinates.swift 3 | // CropView 4 | // 5 | // Created by Никита Разумный on 2/3/18. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIView { 11 | var globalPoint :CGPoint? { 12 | superview?.convert(frame.origin, to: nil) 13 | } 14 | var globalFrame :CGRect? { 15 | superview?.convert(frame, to: nil) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/0.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/src/0.PNG -------------------------------------------------------------------------------- /src/1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/src/1.PNG -------------------------------------------------------------------------------- /src/2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/src/2.PNG -------------------------------------------------------------------------------- /src/3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/src/3.PNG -------------------------------------------------------------------------------- /src/second.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/src/second.png -------------------------------------------------------------------------------- /thirdCapture/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## [juejin blog: 图片朝向,与滤镜(透视校正)](https://juejin.cn/post/6932496153556549646) 3 | 4 | 5 | 6 | 7 | ## [juejin blog: 照片选择区域功能的另一实现: 加动效](https://juejin.cn/post/6929917715813662727) 8 | 9 | ## [juejin blog: 仿扫描全能王的选择区域功能:拍照,旋转](https://juejin.im/post/6882950524786704398) 10 | 11 | 12 | ## [juejin blog: 低仿扫描全能王的选择区域功能](https://juejin.im/post/6875134095232335880) 13 | 14 | 15 | ### 添加选择区域,是否 OK 的交互 16 | 17 | > 有参考 rzmn/CropView 18 | 19 | 20 | ![2_1](https://github.com/BoxDengJZ/SmartCropTry/blob/master/src/second.png) 21 | 22 | 23 | ### 端点拖动, 和放大镜 24 | 25 | ![0](https://github.com/BoxDengJZ/SmartCropTry/blob/master/src/0.PNG) 26 | 27 | 28 | ### 凸四边形判定 29 | 30 | 31 | ![1](https://github.com/BoxDengJZ/SmartCropTry/blob/master/src/1.PNG) 32 | 33 | 34 | ### 交叉边,重新连线 35 | 36 | 37 | ![2](https://github.com/BoxDengJZ/SmartCropTry/blob/master/src/2.PNG) 38 | 39 | 40 | ![3](https://github.com/BoxDengJZ/SmartCropTry/blob/master/src/3.PNG) 41 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // captureAndFilter 4 | // 5 | // Created by Jz D on 2021/2/23. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | return true 18 | } 19 | 20 | // MARK: UISceneSession Lifecycle 21 | 22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 23 | // Called when a new scene session is being created. 24 | // Use this method to select a configuration to create the new scene with. 25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 26 | } 27 | 28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 29 | // Called when the user discards a scene session. 30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 32 | } 33 | 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/4_tag_mask_plus.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "编组@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "编组@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/4_tag_mask_plus.imageset/编组@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/4_tag_mask_plus.imageset/编组@2x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/4_tag_mask_plus.imageset/编组@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/4_tag_mask_plus.imageset/编组@3x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/4_zhi_zhang_jump.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "编组 8@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "编组 8@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/4_zhi_zhang_jump.imageset/编组 8@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/4_zhi_zhang_jump.imageset/编组 8@2x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/4_zhi_zhang_jump.imageset/编组 8@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/4_zhi_zhang_jump.imageset/编组 8@3x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/album_4_kiWa.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "编组 2@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "编组 2@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/album_4_kiWa.imageset/编组 2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/album_4_kiWa.imageset/编组 2@2x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/album_4_kiWa.imageset/编组 2@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/album_4_kiWa.imageset/编组 2@3x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/audio_4_3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "内容区/icon/120*120/创建音频@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "内容区/icon/120*120/创建音频@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/audio_4_3.imageset/内容区/icon/120*120/创建音频@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/audio_4_3.imageset/内容区/icon/120*120/创建音频@2x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/audio_4_3.imageset/内容区/icon/120*120/创建音频@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/audio_4_3.imageset/内容区/icon/120*120/创建音频@3x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/camera_4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "内容区/icon/历史记录@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "内容区/icon/历史记录@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/camera_4.imageset/内容区/icon/历史记录@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/camera_4.imageset/内容区/icon/历史记录@2x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/camera_4.imageset/内容区/icon/历史记录@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/camera_4.imageset/内容区/icon/历史记录@3x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/camera_4_highWox.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "拍摄键@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "拍摄键@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/camera_4_highWox.imageset/拍摄键@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/camera_4_highWox.imageset/拍摄键@2x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/camera_4_highWox.imageset/拍摄键@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/camera_4_highWox.imageset/拍摄键@3x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/camera_4_tip.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "弹窗提示@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "弹窗提示@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/camera_4_tip.imageset/弹窗提示@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/camera_4_tip.imageset/弹窗提示@2x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/camera_4_tip.imageset/弹窗提示@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/camera_4_tip.imageset/弹窗提示@3x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/fluorescence_4_1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "内容区/icon/120*120/荧光笔取词@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "内容区/icon/120*120/荧光笔取词@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/fluorescence_4_1.imageset/内容区/icon/120*120/荧光笔取词@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/fluorescence_4_1.imageset/内容区/icon/120*120/荧光笔取词@2x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/fluorescence_4_1.imageset/内容区/icon/120*120/荧光笔取词@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/fluorescence_4_1.imageset/内容区/icon/120*120/荧光笔取词@3x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/fork_4_kWa.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "编组@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "编组@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/fork_4_kWa.imageset/编组@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/fork_4_kWa.imageset/编组@2x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/fork_4_kWa.imageset/编组@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/fork_4_kWa.imageset/编组@3x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/mine_4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "矩形备份 2@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "矩形备份 2@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/mine_4.imageset/矩形备份 2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/mine_4.imageset/矩形备份 2@2x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/mine_4.imageset/矩形备份 2@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/mine_4.imageset/矩形备份 2@3x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/mine_4_arrow_item.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "路径 8@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "路径 8@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/mine_4_arrow_item.imageset/路径 8@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/mine_4_arrow_item.imageset/路径 8@2x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/mine_4_arrow_item.imageset/路径 8@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/mine_4_arrow_item.imageset/路径 8@3x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/mine_4_selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "tab栏/icon/我的/选中@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "tab栏/icon/我的/选中@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/mine_4_selected.imageset/tab栏/icon/我的/选中@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/mine_4_selected.imageset/tab栏/icon/我的/选中@2x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/mine_4_selected.imageset/tab栏/icon/我的/选中@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/mine_4_selected.imageset/tab栏/icon/我的/选中@3x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/mine_woX_header_4_arrow.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "内容区/icon/历史记录@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "内容区/icon/历史记录@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/mine_woX_header_4_arrow.imageset/内容区/icon/历史记录@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/mine_woX_header_4_arrow.imageset/内容区/icon/历史记录@2x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/mine_woX_header_4_arrow.imageset/内容区/icon/历史记录@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/mine_woX_header_4_arrow.imageset/内容区/icon/历史记录@3x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/mine_woX_header_4_bg.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "矩形@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "矩形@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/mine_woX_header_4_bg.imageset/矩形@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/mine_woX_header_4_bg.imageset/矩形@2x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/mine_woX_header_4_bg.imageset/矩形@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/mine_woX_header_4_bg.imageset/矩形@3x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/plus_4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "tab栏/icon/加号@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "tab栏/icon/加号@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/plus_4.imageset/tab栏/icon/加号@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/plus_4.imageset/tab栏/icon/加号@2x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/plus_4.imageset/tab栏/icon/加号@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/plus_4.imageset/tab栏/icon/加号@3x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/pop_fork_4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "内容区/icon/历史记录@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "内容区/icon/历史记录@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/pop_fork_4.imageset/内容区/icon/历史记录@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/pop_fork_4.imageset/内容区/icon/历史记录@2x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/pop_fork_4.imageset/内容区/icon/历史记录@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/pop_fork_4.imageset/内容区/icon/历史记录@3x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/rotate_4_X.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "内容区/icon/历史记录@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "内容区/icon/历史记录@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/rotate_4_X.imageset/内容区/icon/历史记录@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/rotate_4_X.imageset/内容区/icon/历史记录@2x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/rotate_4_X.imageset/内容区/icon/历史记录@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/rotate_4_X.imageset/内容区/icon/历史记录@3x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/takenImg_4_tick.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "拍摄键@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "拍摄键@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/takenImg_4_tick.imageset/拍摄键@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/takenImg_4_tick.imageset/拍摄键@2x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/takenImg_4_tick.imageset/拍摄键@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/takenImg_4_tick.imageset/拍摄键@3x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/textBook_4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "矩形备份 2@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "矩形备份 2@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/textBook_4.imageset/矩形备份 2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/textBook_4.imageset/矩形备份 2@2x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/textBook_4.imageset/矩形备份 2@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/textBook_4.imageset/矩形备份 2@3x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/textBook_4_selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "tab栏/icon/课本/选中@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "tab栏/icon/课本/选中@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/textBook_4_selected.imageset/tab栏/icon/课本/选中@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/textBook_4_selected.imageset/tab栏/icon/课本/选中@2x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/textBook_4_selected.imageset/tab栏/icon/课本/选中@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/textBook_4_selected.imageset/tab栏/icon/课本/选中@3x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/text_4_2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "内容区/icon/120*120/创建文本@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "内容区/icon/120*120/创建文本@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/text_4_2.imageset/内容区/icon/120*120/创建文本@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/text_4_2.imageset/内容区/icon/120*120/创建文本@2x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/4/text_4_2.imageset/内容区/icon/120*120/创建文本@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/4/text_4_2.imageset/内容区/icon/120*120/创建文本@3x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/5/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/5/not_a_net.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "编组 2@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "编组 2@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/5/not_a_net.imageset/编组 2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/5/not_a_net.imageset/编组 2@2x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/5/not_a_net.imageset/编组 2@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/Assets.xcassets/5/not_a_net.imageset/编组 2@3x.png -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/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 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | NSCameraUsageDescription 24 | 请点击"允许", 25 | NSMicrophoneUsageDescription 26 | 请点击"允许", 27 | UIApplicationSceneManifest 28 | 29 | UIApplicationSupportsMultipleScenes 30 | 31 | UISceneConfigurations 32 | 33 | UIWindowSceneSessionRoleApplication 34 | 35 | 36 | UISceneConfigurationName 37 | Default Configuration 38 | UISceneDelegateClassName 39 | $(PRODUCT_MODULE_NAME).SceneDelegate 40 | UISceneStoryboardFile 41 | Main 42 | 43 | 44 | 45 | 46 | UIApplicationSupportsIndirectInputEvents 47 | 48 | UILaunchStoryboardName 49 | LaunchScreen 50 | UIMainStoryboardFile 51 | Main 52 | UIRequiredDeviceCapabilities 53 | 54 | armv7 55 | 56 | UISupportedInterfaceOrientations 57 | 58 | UIInterfaceOrientationPortrait 59 | 60 | UISupportedInterfaceOrientations~ipad 61 | 62 | UIInterfaceOrientationPortrait 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // captureAndFilter 4 | // 5 | // Created by Jz D on 2021/2/23. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 19 | guard let _ = (scene as? UIWindowScene) else { return } 20 | } 21 | 22 | func sceneDidDisconnect(_ scene: UIScene) { 23 | // Called as the scene is being released by the system. 24 | // This occurs shortly after the scene enters the background, or when its session is discarded. 25 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 26 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). 27 | } 28 | 29 | func sceneDidBecomeActive(_ scene: UIScene) { 30 | // Called when the scene has moved from an inactive state to an active state. 31 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 32 | } 33 | 34 | func sceneWillResignActive(_ scene: UIScene) { 35 | // Called when the scene will move from an active state to an inactive state. 36 | // This may occur due to temporary interruptions (ex. an incoming phone call). 37 | } 38 | 39 | func sceneWillEnterForeground(_ scene: UIScene) { 40 | // Called as the scene transitions from the background to the foreground. 41 | // Use this method to undo the changes made on entering the background. 42 | } 43 | 44 | func sceneDidEnterBackground(_ scene: UIScene) { 45 | // Called as the scene transitions from the foreground to the background. 46 | // Use this method to save data, release shared resources, and store enough scene-specific state information 47 | // to restore the scene back to its current state. 48 | } 49 | 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/SnapKit.framework/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/SnapKit.framework/Info.plist -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/SnapKit.framework/Modules/SnapKit.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/SnapKit.framework/Modules/SnapKit.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/SnapKit.framework/Modules/SnapKit.swiftmodule/Project/arm64.swiftsourceinfo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/SnapKit.framework/Modules/SnapKit.swiftmodule/Project/arm64.swiftsourceinfo -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/SnapKit.framework/Modules/SnapKit.swiftmodule/arm64-apple-ios.swiftdoc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/SnapKit.framework/Modules/SnapKit.swiftmodule/arm64-apple-ios.swiftdoc -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/SnapKit.framework/Modules/SnapKit.swiftmodule/arm64-apple-ios.swiftmodule: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/SnapKit.framework/Modules/SnapKit.swiftmodule/arm64-apple-ios.swiftmodule -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/SnapKit.framework/Modules/SnapKit.swiftmodule/arm64.swiftdoc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/SnapKit.framework/Modules/SnapKit.swiftmodule/arm64.swiftdoc -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/SnapKit.framework/Modules/SnapKit.swiftmodule/arm64.swiftmodule: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/SnapKit.framework/Modules/SnapKit.swiftmodule/arm64.swiftmodule -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/SnapKit.framework/Modules/module.modulemap: -------------------------------------------------------------------------------- 1 | framework module SnapKit { 2 | header "SnapKit-Swift.h" 3 | requires objc 4 | } 5 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/SnapKit.framework/SnapKit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoxDengJZ/SmartCropTry/bf7ab1c6a30694d241b08cdc0ef6bea02f359533/thirdCapture/captureAndFilter/SnapKit.framework/SnapKit -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // captureAndFilter 4 | // 5 | // Created by Jz D on 2021/2/23. 6 | // 7 | 8 | import UIKit 9 | 10 | class ViewController: UIViewController { 11 | 12 | override func viewDidLoad() { 13 | super.viewDidLoad() 14 | // Do any additional setup after loading the view. 15 | 16 | 17 | } 18 | 19 | @IBAction func open(_ sender: Any) { 20 | 21 | let camera = CusCamera() 22 | camera.takeDoneBlockX = { (info) in 23 | print(info) 24 | } 25 | showDetailViewController(camera, sender: nil) 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/crop/Array+shift.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Array+shift.swift 3 | // CropView 4 | // 5 | // Created by Никита Разумный on 2/3/18. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Array { 11 | func shifted(by shiftAmount: Int) -> [Element]{ 12 | guard count > 0, (shiftAmount % count) != 0 else { return self } 13 | let moduloShiftAmount = shiftAmount % count 14 | let effectiveShiftAmount: Int 15 | if shiftAmount < 0{ 16 | effectiveShiftAmount = moduloShiftAmount + count 17 | } 18 | else{ 19 | effectiveShiftAmount = moduloShiftAmount 20 | } 21 | let shift: (Int) -> Int = { 22 | if $0 + effectiveShiftAmount >= count{ 23 | return $0 + effectiveShiftAmount - count 24 | } 25 | else{ 26 | return $0 + effectiveShiftAmount 27 | } 28 | } 29 | return enumerated().sorted(by: { shift($0.offset) < shift($1.offset) }).map { $0.element } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/crop/CGPoint+geometry.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGPoint+geometry.swift 3 | // CropView 4 | // 5 | // Created by Никита Разумный on 2/3/18. 6 | // 7 | 8 | import UIKit 9 | 10 | extension CGPoint { 11 | func cartesian(for size: CGSize) -> CGPoint { 12 | return CGPoint(x: x, y: size.height - y) 13 | } 14 | static func cross(a: CGPoint, b: CGPoint) -> CGFloat { 15 | return a.x * b.y - a.y * b.x 16 | } 17 | func normalized(size: CGSize) -> CGPoint { 18 | return CGPoint(x: max(min(x, size.width), 0), y: max(min(y, size.height), 0)) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/crop/SEAreaView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AreaView.swift 3 | // CropViewController 4 | // 5 | // Created by Никита Разумный on 11/5/17. 6 | // Copyright © 2017 resquare. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SEAreaView: UIView { 12 | 13 | var path: CGMutablePath? 14 | var isPathValid = true 15 | 16 | 17 | override func draw(_ rect: CGRect) { 18 | super.draw(rect) 19 | 20 | guard let p = path else { return } 21 | 22 | let context = UIGraphicsGetCurrentContext() 23 | context?.setAllowsAntialiasing(true) 24 | context?.clip(to: rect) 25 | 26 | context?.addPath(p) 27 | context?.setLineWidth(1) 28 | context?.setLineCap(.round) 29 | context?.setLineJoin(.round) 30 | 31 | 32 | context?.setStrokeColor((isPathValid ? Setting.std.goodAreaColor : Setting.std.badAreaColor).cgColor) 33 | context?.strokePath() 34 | context?.addRect(bounds) 35 | context?.addPath(p) 36 | 37 | context?.setFillColor(UIColor(white: 0.3, alpha: 0.2).cgColor) 38 | context?.drawPath(using: .eoFill) 39 | } 40 | 41 | 42 | func fill(path p: CGMutablePath){ 43 | path = p 44 | setNeedsDisplay() 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/crop/SECornerView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CornerView.swift 3 | // CropViewController 4 | // 5 | // Created by Никита Разумный on 11/5/17. 6 | // Copyright © 2017 resquare. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SECornerView: UIView { 12 | 13 | override init(frame: CGRect) { 14 | super.init(frame: frame) 15 | layer.cornerRadius = frame.size.width / 2.0 16 | layer.borderWidth = 1.0 17 | layer.masksToBounds = true 18 | backgroundColor = UIColor.clear 19 | } 20 | 21 | required init?(coder aDecoder: NSCoder) { 22 | super.init(coder: aDecoder) 23 | } 24 | 25 | func scaleUp() { 26 | UIView.animate(withDuration: 0.15, animations: { 27 | self.layer.borderWidth = 0.5 28 | self.transform = CGAffineTransform.identity.scaledBy(x: 2, y: 2) 29 | }) 30 | } 31 | 32 | func scaleDown() { 33 | UIView.animate(withDuration: 0.15, animations: { 34 | self.layer.borderWidth = 1 35 | self.transform = CGAffineTransform.identity 36 | }) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/crop/SECropError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SECropError.swift 3 | // CropView 4 | // 5 | // Created by Никита Разумный on 2/3/18. 6 | // 7 | 8 | import UIKit 9 | 10 | public enum SECropError: Error { 11 | case missingSuperview 12 | case missingImageOnImageView 13 | case invalidNumberOfCorners 14 | case nonConvexRect 15 | case missingImageWhileCropping 16 | case unknown 17 | case noImage 18 | } 19 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/crop/SEQuadrangleHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SEQuadrangleHelper.swift 3 | // CropView 4 | // 5 | // Created by Никита Разумный on 2/3/18. 6 | // 7 | 8 | import UIKit 9 | import Foundation 10 | import AVFoundation 11 | 12 | public class SEQuadrangleHelper { 13 | 14 | static internal func orderPointsInQuadrangle(quad: [CGPoint]) throws -> [CGPoint] { 15 | func orderArrayClockwise(quad: [CGPoint]) -> [CGPoint] { 16 | // oriented area of quadrangle: cloclwise if it > 0 17 | var square: CGFloat = 0.0 18 | for i in 0 ..< quad.count - 1 { 19 | square += CGPoint.cross(a: CGPoint(x: quad[i].x - quad[0].x, y: quad[i].y - quad[0].y), 20 | b: CGPoint(x: quad[i + 1].x - quad[0].x, y: quad[i + 1].y - quad[0].y)) 21 | } 22 | return square > 0 ? quad : quad.reversed() 23 | } 24 | 25 | func findTopLeftPointIndex(quad: [CGPoint]) throws -> Int { 26 | var topLeftPointIdx : Int = -1 27 | var topLeftShiftValue : CGFloat = 1000.0 * 1000.0 * 1000.0 28 | 29 | for i in 0 ..< quad.count { 30 | let shiftValue = quad[i].y + quad[i].x 31 | if shiftValue < topLeftShiftValue { 32 | topLeftPointIdx = i 33 | topLeftShiftValue = shiftValue 34 | } 35 | } 36 | guard topLeftPointIdx != -1 else { throw SECropError.unknown } 37 | return topLeftPointIdx 38 | } 39 | 40 | guard quad.count == 4 else { throw SECropError.invalidNumberOfCorners } 41 | guard checkConvex(corners: quad) else { throw SECropError.nonConvexRect } 42 | 43 | let orderedQuad = orderArrayClockwise(quad: quad) 44 | let topLeftIdx = try findTopLeftPointIndex(quad: orderedQuad) 45 | return orderedQuad.shifted(by: orderedQuad.count - topLeftIdx) 46 | } 47 | 48 | static public func cropImage(in rect: CGRect,with img: UIImage, quad corners: [CGPoint]) throws -> UIImage { 49 | let imgSize = img.size 50 | let f = AVMakeRect(aspectRatio: imgSize, insideRect: rect) 51 | let quad = corners.map { (pt) -> CGPoint in 52 | return pt.inner(img: imgSize, relative: f.size) 53 | } 54 | let ciImage = CIImage(image: img) 55 | // print("rect: ", rect, "corners: ",corners) 56 | let perspectiveCorrection = CIFilter(name: "CIPerspectiveCorrection") 57 | 58 | let orderedQuad = try orderPointsInQuadrangle(quad: quad) 59 | let context = CIContext(options: nil) 60 | // print("ordered quad: ", orderedQuad, "imgSize: ", imgSize) 61 | 62 | guard let transform = perspectiveCorrection else { 63 | throw SECropError.unknown 64 | } 65 | transform.setValue(CIVector(cgPoint: orderedQuad[0].cartesian(for: imgSize)), 66 | forKey: "inputTopLeft") 67 | transform.setValue(CIVector(cgPoint: orderedQuad[1].cartesian(for: imgSize)), 68 | forKey: "inputTopRight") 69 | transform.setValue(CIVector(cgPoint: orderedQuad[2].cartesian(for: imgSize)), 70 | forKey: "inputBottomRight") 71 | transform.setValue(CIVector(cgPoint: orderedQuad[3].cartesian(for: imgSize)), 72 | forKey: "inputBottomLeft") 73 | transform.setValue(ciImage, forKey: kCIInputImageKey) 74 | 75 | guard let perspectiveCorrectedImg = transform.outputImage, let cgImage = context.createCGImage(perspectiveCorrectedImg, from: perspectiveCorrectedImg.extent) else { 76 | throw SECropError.unknown 77 | } 78 | 79 | return UIImage(cgImage: cgImage, scale: img.scale, orientation: img.imageOrientation) 80 | } 81 | 82 | static internal func checkConvex(corners: [CGPoint]) -> Bool { 83 | guard corners.count > 2 else { 84 | return false 85 | } 86 | var positiveCount = 0 87 | var negativeCount = 0 88 | for i in 0 ..< corners.count { 89 | let p0 = corners[i] 90 | let p1 = corners[(i + 1) % corners.count] 91 | let p2 = corners[(i + 2) % corners.count] 92 | 93 | let cross = (p1.x - p0.x) * (p2.y - p1.y) - (p1.y - p0.y) * (p2.x - p1.x); 94 | if cross > 0 { 95 | positiveCount += 1 96 | } else if cross < 0 { 97 | negativeCount += 1 98 | } 99 | } 100 | return positiveCount == corners.count || negativeCount == corners.count 101 | } 102 | } 103 | 104 | 105 | 106 | 107 | extension CGPoint{ 108 | func inner(img s: CGSize, relative dot: CGSize) -> CGPoint{ 109 | let xx = x * s.width/dot.width 110 | let yy = y * s.height/dot.height 111 | return CGPoint(x: xx, y: yy) 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/crop/UIView+globalCoordinates.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+globalCoordinates.swift 3 | // CropView 4 | // 5 | // Created by Никита Разумный on 2/3/18. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIView { 11 | var globalPoint :CGPoint? { 12 | superview?.convert(frame.origin, to: nil) 13 | } 14 | var globalFrame :CGRect? { 15 | superview?.convert(frame, to: nil) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/two/ButtomViewFinal.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ButtomViewFinal.swift 3 | // petit 4 | // 5 | // Created by Jz D on 2021/2/3. 6 | // Copyright © 2021 swift. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ButtomViewFinal: UIView{ 12 | 13 | lazy var bottomBeach: UIView = { 14 | let v = UIView() 15 | v.backgroundColor = UIColor.black 16 | return v 17 | }() 18 | 19 | 20 | lazy var retakeBtn: UIButton = { 21 | let keBtn = UIButton() 22 | keBtn.setTitle("重拍", for: .normal) 23 | keBtn.setTitleColor(UIColor.white, for: .normal) 24 | keBtn.titleLabel?.font = UIFont.regular(ofSize: 16) 25 | keBtn.adjustsImageWhenHighlighted = false 26 | return keBtn 27 | }() 28 | 29 | lazy var rotateBtn: UIButton = { 30 | let neBtn = UIButton() 31 | neBtn.setImage(UIImage(named: "rotate_4_X"), for: .normal) 32 | return neBtn 33 | }() 34 | 35 | lazy var doneBtn: UIButton = { 36 | let neBtn = UIButton() 37 | neBtn.setImage(UIImage(named: "takenImg_4_tick"), for: .normal) 38 | return neBtn 39 | }() 40 | 41 | 42 | override init(frame: CGRect) { 43 | super.init(frame: frame) 44 | isHidden = true 45 | addSubs([bottomBeach, retakeBtn, doneBtn, 46 | rotateBtn]) 47 | bottomBeach.snp.makeConstraints { (m) in 48 | m.edges.equalToSuperview() 49 | } 50 | 51 | retakeBtn.snp.makeConstraints { (m) in 52 | m.leading.equalToSuperview().offset(40) 53 | m.centerY.equalTo(rotateBtn) 54 | } 55 | 56 | doneBtn.snp.makeConstraints { (m) in 57 | m.centerX.equalToSuperview() 58 | m.top.equalToSuperview().offset(48) 59 | m.size.equalTo(CGSize(width: 60, height: 60)) 60 | } 61 | 62 | rotateBtn.snp.makeConstraints { (m) in 63 | m.trailing.equalToSuperview().offset(-40) 64 | m.top.equalTo(bottomBeach).offset(63) 65 | m.size.equalTo(CGSize(width: 25, height: 25)) 66 | } 67 | } 68 | 69 | 70 | 71 | 72 | required init?(coder: NSCoder) { 73 | fatalError() 74 | } 75 | 76 | } 77 | 78 | 79 | 80 | extension UIView{ 81 | 82 | func addSubs(_ views: [UIView]){ 83 | views.forEach(addSubview(_:)) 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/two/ButtomViewP.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ButtomVIewP.swift 3 | // petit 4 | // 5 | // Created by Jz D on 2021/2/3. 6 | // Copyright © 2021 swift. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ButtomViewP: UIView{ 12 | 13 | 14 | lazy var dismissBtn: UIButton = { 15 | let edge: CGFloat = 45 16 | let b = UIButton(frame: CGRect(x: 40, y: 58, width: edge, height: edge)) 17 | b.setImage(UIImage(named: "fork_4_kWa"), for: .normal) 18 | b.adjustsImageWhenHighlighted = false 19 | return b 20 | }() 21 | 22 | 23 | lazy var albumBu: UIButton = { 24 | let edge: CGFloat = 45 25 | let x = UI.std.width - 40 - edge 26 | let b = UIButton(frame: CGRect(x: x, y: 58, width: edge, height: edge)) 27 | b.setImage(UIImage(named: "album_4_kiWa"), for: .normal) 28 | 29 | b.adjustsImageWhenHighlighted = false 30 | return b 31 | }() 32 | 33 | 34 | 35 | lazy var largeCircleB: UIImageView = { 36 | let img = UIImageView(image: UIImage(named: "camera_4_highWox")) 37 | img.isUserInteractionEnabled = true 38 | return img 39 | }() 40 | 41 | 42 | 43 | override init(frame: CGRect){ 44 | super.init(frame: frame) 45 | backgroundColor = UIColor.black 46 | addSubs([largeCircleB, 47 | dismissBtn, albumBu]) 48 | 49 | } 50 | 51 | 52 | override func layoutSubviews() { 53 | super.layoutSubviews() 54 | 55 | let largeCircleH: CGFloat = 60 56 | largeCircleB.frame = CGRect(x: (bounds.width-largeCircleH)/2, y: 48, width: largeCircleH, height: largeCircleH) 57 | } 58 | 59 | 60 | required init?(coder: NSCoder) { 61 | fatalError() 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/two/LinedTipView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LinedTipView.swift 3 | // petit 4 | // 5 | // Created by Jz D on 2021/2/3. 6 | // Copyright © 2021 swift. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class LinedTipView: UIView { 12 | 13 | 14 | lazy var tipIcon: UIImageView = { 15 | let frm = CGRect(x: 0, y: 0, width: 160, height: 160) 16 | let bel = UIImageView(frame: frm) 17 | bel.image = UIImage(named: "camera_4_tip") 18 | 19 | return bel 20 | }() 21 | 22 | 23 | override init(frame: CGRect) { 24 | super.init(frame: frame) 25 | backgroundColor = .clear 26 | addSubs([ tipIcon ]) 27 | // layer.debug() 28 | } 29 | 30 | 31 | required init?(coder: NSCoder) { 32 | fatalError() 33 | } 34 | 35 | 36 | override func layoutSubviews() { 37 | super.layoutSubviews() 38 | tipIcon.center = CGPoint(x: bounds.width / 2, y: bounds.height / 2) 39 | } 40 | 41 | 42 | // Only override draw() if you perform custom drawing. 43 | // An empty implementation adversely affects performance during animation. 44 | override func draw(_ rect: CGRect) { 45 | let b = UIBezierPath() 46 | b.lineWidth = 1 47 | let pieceX = rect.width * 0.33 48 | let pieceY = rect.height * 0.33 49 | // 两竖 50 | b.move(to: CGPoint(x: pieceX, y: 0)) 51 | b.addLine(to: CGPoint(x: pieceX, y: rect.height)) 52 | 53 | b.move(to: CGPoint(x: pieceX * 2, y: 0)) 54 | b.addLine(to: CGPoint(x: pieceX * 2, y: rect.height)) 55 | 56 | // 两横 57 | b.move(to: CGPoint(x: 0, y: pieceY)) 58 | b.addLine(to: CGPoint(x: rect.width, y: pieceY)) 59 | 60 | b.move(to: CGPoint(x: 0, y: pieceY * 2)) 61 | b.addLine(to: CGPoint(x: rect.width, y: pieceY * 2)) 62 | 63 | UIColor(rgb: 0xFFFFFF, alpha: 0.6).setStroke() 64 | b.stroke() 65 | } 66 | 67 | 68 | } 69 | 70 | 71 | 72 | 73 | 74 | 75 | extension UIColor { 76 | 77 | 78 | convenience init(red: Int, green: Int, blue: Int, alpha: CGFloat) { 79 | assert(red >= 0 && red <= 255, "Invalid red component") 80 | assert(green >= 0 && green <= 255, "Invalid green component") 81 | assert(blue >= 0 && blue <= 255, "Invalid blue component") 82 | 83 | self.init(red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue) / 255.0, alpha: alpha) 84 | } 85 | 86 | 87 | 88 | convenience init(rgb: Int, alpha: CGFloat = 1) { 89 | self.init( 90 | red: (rgb >> 16) & 0xFF, 91 | green: (rgb >> 8) & 0xFF, 92 | blue: rgb & 0xFF, 93 | alpha: alpha 94 | ) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/util/DebugLayer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // musicSheet 4 | // 5 | // Created by Jz D on 2019/8/22. 6 | // Copyright © 2019 上海莫小臣有限公司. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension CALayer{ 12 | 13 | func debug(){ 14 | borderColor = UIColor.blue.cgColor 15 | borderWidth = 1 16 | } 17 | } 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | protocol UI_debug { 27 | func debug( _ event: @escaping (UITapGestureRecognizer) -> Void) 28 | } 29 | 30 | 31 | 32 | extension UIView: UI_debug{ 33 | // debugV 34 | func debug( _ event: @escaping (UITapGestureRecognizer) -> Void){ 35 | // isUserInteractionEnabled = true 36 | let tap = UITapGestureRecognizer() 37 | tap.numberOfTapsRequired = 2 38 | // addGestureRecognizer(tap) 39 | // tap.rx.event.bind(onNext: event).disposed(by: rx.disposeBag) 40 | } 41 | 42 | } 43 | 44 | 45 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/util/FontAdd.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FontAdd.swift 3 | // 4 | // 5 | // Created by Jz D on 2019/8/23. 6 | // 7 | 8 | import UIKit 9 | 10 | 11 | 12 | enum FontWeight: String { 13 | case light = "Light" 14 | case regular = "Regular" 15 | case medium = "Medium" 16 | 17 | case semibold = "Semibold" 18 | case bold = "Bold" 19 | case heavy = "Heavy" 20 | } 21 | 22 | enum FontType: String { 23 | case PingFangSC = "PingFangSC" 24 | case SFProText = "SFProText" 25 | } 26 | 27 | extension UIFont { 28 | 29 | static func heavy(ofSize fontSize: CGFloat, type: FontType = .PingFangSC) -> UIFont { 30 | return customFont(type, weight: .heavy, fontSize: fontSize) 31 | } 32 | 33 | static func regular(ofSize fontSize: CGFloat, type: FontType = .PingFangSC) -> UIFont { 34 | return customFont(type, weight: .regular, fontSize: fontSize) 35 | } 36 | 37 | static func bold(ofSize fontSize: CGFloat, type: FontType = .PingFangSC) -> UIFont { 38 | return customFont(type, weight: .bold, fontSize: fontSize) 39 | } 40 | 41 | static func light(ofSize fontSize: CGFloat, type: FontType = .PingFangSC) -> UIFont { 42 | return customFont(type, weight: .light, fontSize: fontSize) 43 | } 44 | 45 | static func medium(ofSize fontSize: CGFloat, type: FontType = .PingFangSC) -> UIFont { 46 | return customFont(type, weight: .medium, fontSize: fontSize) 47 | } 48 | 49 | static func semibold(ofSize fontSize: CGFloat, type: FontType = .PingFangSC) -> UIFont { 50 | return customFont(type, weight: .semibold, fontSize: fontSize) 51 | } 52 | 53 | /// 自定义字体 54 | static func customFont(_ type: FontType, weight: FontWeight, fontSize: CGFloat) -> UIFont { 55 | let realFontSize = fontSize 56 | 57 | if let customFont = UIFont(name: "\(type.rawValue)-\(weight.rawValue)", size: realFontSize) { 58 | return customFont 59 | } 60 | 61 | var systemWeight = UIFont.Weight.regular 62 | switch weight { 63 | case .light: 64 | systemWeight = UIFont.Weight.light 65 | case .regular: 66 | systemWeight = UIFont.Weight.regular 67 | case .medium: 68 | systemWeight = UIFont.Weight.medium 69 | case .semibold: 70 | systemWeight = UIFont.Weight.semibold 71 | case .bold: 72 | systemWeight = UIFont.Weight.bold 73 | case .heavy: 74 | systemWeight = UIFont.Weight.heavy 75 | } 76 | return UIFont.systemFont(ofSize: realFontSize, weight: systemWeight) 77 | } 78 | } 79 | 80 | 81 | 82 | 83 | 84 | extension UIFont { 85 | 86 | static let scoreName = UIFont.medium(ofSize: 16) 87 | static let headerFirstThree = UIFont.medium(ofSize: 22) 88 | static let accountListTitle = UIFont.regular(ofSize: 16) 89 | 90 | 91 | 92 | static let myStudyTitle = UIFont.medium(ofSize: 18) 93 | static let myStudyListScoreName = UIFont.medium(ofSize: 14) 94 | static let loginHeaderName = UIFont.medium(ofSize: 16) 95 | 96 | 97 | } 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/util/RectAdd.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RectAdd.swift 3 | // musicSheet 4 | // 5 | // Created by Jz D on 2019/8/26. 6 | // Copyright © 2019 上海莫小臣有限公司. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | extension CGRect{ 13 | 14 | var end: CGPoint{ 15 | return CGPoint(x: maxX, y: maxY) 16 | } 17 | 18 | var leftBottom: CGPoint{ 19 | return CGPoint(x: minX, y: maxY) 20 | } 21 | 22 | var rightUp: CGPoint{ 23 | return CGPoint(x: maxX, y: minY) 24 | } 25 | } 26 | 27 | 28 | 29 | struct UI { 30 | 31 | static let std = UI() 32 | 33 | var height: CGFloat{ 34 | UIScreen.main.bounds.height 35 | } 36 | 37 | 38 | var layout: CGRect{ 39 | UIScreen.main.bounds 40 | } 41 | 42 | 43 | var width: CGFloat{ 44 | UIScreen.main.bounds.width 45 | } 46 | 47 | var bigS: Bool{ 48 | UIScreen.main.bounds.height >= 812 || special 49 | } 50 | 51 | 52 | 53 | var center: CGPoint{ 54 | CGPoint(x: width * 0.5, y: height * 0.5) 55 | } 56 | 57 | 58 | var origineY: CGFloat{ 59 | return 20 60 | } 61 | 62 | var safeY: CGFloat{ 63 | if bigS{ 64 | return 44 65 | } 66 | else{ 67 | return 0 68 | } 69 | } 70 | 71 | var bottomOffsetY: CGFloat{ 72 | if bigS{ 73 | return 34 74 | } 75 | else{ 76 | return 0 77 | } 78 | } 79 | 80 | 81 | var contentY: CGFloat{ 82 | origineY + 64 83 | } 84 | 85 | 86 | var special: Bool{ 87 | UIScreen.main.bounds.width <= 320 88 | } 89 | } 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | public func * (left: CGFloat, right: Int) -> CGFloat { 100 | return left * CGFloat(right) 101 | } 102 | 103 | 104 | public func * (left: Int, right: CGFloat) -> CGFloat { 105 | return CGFloat(left) * right 106 | } 107 | 108 | 109 | 110 | extension CGFloat{ 111 | var scalar: CGFloat{ 112 | CGFloat(Int(self)) 113 | } 114 | } 115 | 116 | 117 | 118 | extension CGRect{ 119 | func right(x offset: CGFloat) -> CGRect{ 120 | CGRect(origin: CGPoint(x: origin.x + offset, y: origin.y), size: size) 121 | } 122 | 123 | 124 | } 125 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/util/ZLCustomCamera.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ZLCustomCamera.swift 3 | // ZLPhotoBrowser 4 | // 5 | // Created by long on 2020/8/11. 6 | // 7 | // Copyright (c) 2020 Long Zhang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import UIKit 28 | import AVFoundation 29 | import CoreMotion 30 | 31 | 32 | 33 | enum CustomCameraOpt{ 34 | case dft 35 | case result 36 | } 37 | class ZLCustomCamera{} 38 | extension ZLCustomCamera { 39 | 40 | @objc public enum CaptureSessionPreset: Int { 41 | 42 | var avSessionPreset: AVCaptureSession.Preset { 43 | switch self { 44 | case .cif352x288: 45 | return .cif352x288 46 | case .vga640x480: 47 | return .vga640x480 48 | case .hd1280x720: 49 | return .hd1280x720 50 | case .hd1920x1080: 51 | return .hd1920x1080 52 | case .hd4K3840x2160: 53 | return .hd4K3840x2160 54 | } 55 | } 56 | 57 | case cif352x288 58 | case vga640x480 59 | case hd1280x720 60 | case hd1920x1080 61 | case hd4K3840x2160 62 | } 63 | 64 | @objc public enum CameraFlashMode: Int { 65 | 66 | // 转自定义相机 67 | var avFlashMode: AVCaptureDevice.FlashMode { 68 | switch self { 69 | case .auto: 70 | return .auto 71 | case .on: 72 | return .on 73 | case .off: 74 | return .off 75 | } 76 | } 77 | 78 | // 转系统相机 79 | var imagePickerFlashMode: UIImagePickerController.CameraFlashMode { 80 | switch self { 81 | case .auto: 82 | return .auto 83 | case .on: 84 | return .on 85 | case .off: 86 | return .off 87 | } 88 | } 89 | 90 | case auto 91 | case on 92 | case off 93 | } 94 | 95 | 96 | } 97 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilter/util/ZLGeneralDefine.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ZLGeneralDefine.swift 3 | // ZLPhotoBrowser 4 | // 5 | // Created by long on 2020/8/11. 6 | // 7 | // Copyright (c) 2020 Long Zhang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import UIKit 28 | 29 | let ZLMaxImageWidth: CGFloat = 600 30 | 31 | struct ZLLayout { 32 | 33 | static let navTitleFont = getFont(17) 34 | 35 | static let bottomToolViewH: CGFloat = 55 36 | 37 | static let bottomToolBtnH: CGFloat = 34 38 | 39 | static let bottomToolTitleFont = getFont(17) 40 | 41 | static let bottomToolBtnCornerRadius: CGFloat = 5 42 | 43 | static let thumbCollectionViewItemSpacing: CGFloat = 2 44 | 45 | static let thumbCollectionViewLineSpacing: CGFloat = 2 46 | 47 | } 48 | 49 | func zlRGB(_ red: CGFloat, _ green: CGFloat, _ blue: CGFloat) -> UIColor { 50 | return UIColor(red: red / 255, green: green / 255, blue: blue / 255, alpha: 1) 51 | } 52 | 53 | 54 | func getFont(_ size: CGFloat) -> UIFont { 55 | guard let name = ZLCustomFontDeploy.fontName else { 56 | return UIFont.systemFont(ofSize: size) 57 | } 58 | 59 | return UIFont(name: name, size: size) ?? UIFont.systemFont(ofSize: size) 60 | } 61 | 62 | func getAppName() -> String { 63 | if let name = Bundle.main.localizedInfoDictionary?["CFBundleDisplayName"] as? String { 64 | return name 65 | } 66 | if let name = Bundle.main.infoDictionary?["CFBundleDisplayName"] as? String { 67 | return name 68 | } 69 | if let name = Bundle.main.infoDictionary?["CFBundleName"] as? String { 70 | return name 71 | } 72 | return "App" 73 | } 74 | 75 | 76 | 77 | func getSpringAnimation() -> CAKeyframeAnimation { 78 | let animate = CAKeyframeAnimation(keyPath: "transform") 79 | animate.duration = 0.3 80 | animate.isRemovedOnCompletion = true 81 | animate.fillMode = .forwards 82 | 83 | animate.values = [CATransform3DMakeScale(0.7, 0.7, 1), 84 | CATransform3DMakeScale(1.2, 1.2, 1), 85 | CATransform3DMakeScale(0.8, 0.8, 1), 86 | CATransform3DMakeScale(1, 1, 1)] 87 | return animate 88 | } 89 | 90 | 91 | func zl_debugPrint(_ message: Any) { 92 | // debugPrint(message) 93 | } 94 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilterTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilterTests/captureAndFilterTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // captureAndFilterTests.swift 3 | // captureAndFilterTests 4 | // 5 | // Created by Jz D on 2021/2/23. 6 | // 7 | 8 | import XCTest 9 | @testable import captureAndFilter 10 | 11 | class captureAndFilterTests: XCTestCase { 12 | 13 | override func setUpWithError() throws { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDownWithError() throws { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | } 20 | 21 | func testExample() throws { 22 | // This is an example of a functional test case. 23 | // Use XCTAssert and related functions to verify your tests produce the correct results. 24 | } 25 | 26 | func testPerformanceExample() throws { 27 | // This is an example of a performance test case. 28 | self.measure { 29 | // Put the code you want to measure the time of here. 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilterUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /thirdCapture/captureAndFilterUITests/captureAndFilterUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // captureAndFilterUITests.swift 3 | // captureAndFilterUITests 4 | // 5 | // Created by Jz D on 2021/2/23. 6 | // 7 | 8 | import XCTest 9 | 10 | class captureAndFilterUITests: XCTestCase { 11 | 12 | override func setUpWithError() throws { 13 | // Put setup code here. This method is called before the invocation of each test method in the class. 14 | 15 | // In UI tests it is usually best to stop immediately when a failure occurs. 16 | continueAfterFailure = false 17 | 18 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 19 | } 20 | 21 | override func tearDownWithError() throws { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | func testExample() throws { 26 | // UI tests must launch the application that they test. 27 | let app = XCUIApplication() 28 | app.launch() 29 | 30 | // Use recording to get started writing UI tests. 31 | // Use XCTAssert and related functions to verify your tests produce the correct results. 32 | } 33 | 34 | func testLaunchPerformance() throws { 35 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { 36 | // This measures how long it takes to launch your application. 37 | measure(metrics: [XCTApplicationLaunchMetric()]) { 38 | XCUIApplication().launch() 39 | } 40 | } 41 | } 42 | } 43 | --------------------------------------------------------------------------------