├── .gitignore ├── LICENSE ├── Podfile ├── Podfile.lock ├── README.md ├── gif3d.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── gif3d.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── gif3d ├── AppDelegate.swift ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── Supporting Files │ ├── 1.gif │ ├── 2.gif │ └── 3.gif ├── extensions │ ├── Bundle+Extensions.swift │ ├── CALayer+Shadow.swift │ ├── Color.swift │ ├── FileManager+Extensions.swift │ ├── UIColorExtensions.swift │ ├── UIImage+Extensions.swift │ ├── UIView+Extensions.swift │ ├── UIViewController+Extensions.swift │ └── UIViewExtensions.swift ├── managers │ └── EditorManager.swift ├── model │ └── Editor.swift ├── resources │ └── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── arrowDown.imageset │ │ ├── Contents.json │ │ └── arrowDown.png │ │ ├── arrowUp.imageset │ │ ├── Contents.json │ │ └── arrowUp.png │ │ ├── back.imageset │ │ ├── Contents.json │ │ └── back.png │ │ ├── next.imageset │ │ ├── Contents.json │ │ └── next1.png │ │ └── share.imageset │ │ ├── Contents.json │ │ └── share.png ├── utility │ └── Contain.swift ├── viewcontrollers │ ├── EditorVC.swift │ ├── PreviewVC.swift │ ├── ViewController.swift │ └── cell │ │ ├── ColorItemCell.swift │ │ └── FrameItemCell.swift └── views │ ├── EditorFrameView.swift │ └── EditorView.swift └── screenshot ├── after.gif ├── before.gif └── screenshot1.png /.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 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | # Package.resolved 41 | .build/ 42 | 43 | # CocoaPods 44 | # 45 | # We recommend against adding the Pods directory to your .gitignore. However 46 | # you should judge for yourself, the pros and cons are mentioned at: 47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 48 | # 49 | # Pods/ 50 | 51 | # Carthage 52 | # 53 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 54 | # Carthage/Checkouts 55 | 56 | Carthage/Build 57 | 58 | # fastlane 59 | # 60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 61 | # screenshots whenever they are needed. 62 | # For more information about the recommended setup visit: 63 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 64 | 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots/**/*.png 68 | fastlane/test_output 69 | Pods 70 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | platform :ios, '11.0' 3 | 4 | target 'gif3d' do 5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | # Pods for gif3d 9 | pod 'SDWebImage/GIF' 10 | pod 'MBProgressHUD', '~> 1.1.0' 11 | end 12 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - FLAnimatedImage (1.0.12) 3 | - MBProgressHUD (1.1.0) 4 | - SDWebImage/Core (4.4.1) 5 | - SDWebImage/GIF (4.4.1): 6 | - FLAnimatedImage (~> 1.0) 7 | - SDWebImage/Core 8 | 9 | DEPENDENCIES: 10 | - MBProgressHUD (~> 1.1.0) 11 | - SDWebImage/GIF 12 | 13 | SPEC REPOS: 14 | https://github.com/cocoapods/specs.git: 15 | - FLAnimatedImage 16 | - MBProgressHUD 17 | - SDWebImage 18 | 19 | SPEC CHECKSUMS: 20 | FLAnimatedImage: 4a0b56255d9b05f18b6dd7ee06871be5d3b89e31 21 | MBProgressHUD: e7baa36a220447d8aeb12769bf0585582f3866d9 22 | SDWebImage: 47e9b5b925cbce75946c23f0c42dd19464189af4 23 | 24 | PODFILE CHECKSUM: 080d1cfab02b95a95c42233096b9b659867605f5 25 | 26 | COCOAPODS: 1.5.3 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Trick convert gif to gif3d using Swift 3 | 4 | ### Features 5 | 6 | * Simple way to convert gif to gif3d 7 | * Change background 8 | * Change size eraser tool 9 | 10 | ### How to work 11 | 12 | 1. Add a layer with two white line on top every frame from orignal gif 13 | 2. Try to erase white line to fix with your gif 14 | 3. Combine layers and convert to gif 15 | 16 | ![](https://raw.githubusercontent.com/chuongtrh/gif3d/master/screenshot/screenshot1.png) 17 | 18 | ### Screenshots 19 | 20 | Before 21 | 22 | ![Before](https://raw.githubusercontent.com/chuongtrh/gif3d/master/screenshot/before.gif) 23 | 24 | After 25 | 26 | ![After](https://raw.githubusercontent.com/chuongtrh/gif3d/master/screenshot/after.gif) 27 | 28 | ## Licensing 29 | 30 | This project is licensed under Unlicense license. This license does not require 31 | you to take the license with you to your project. 32 | -------------------------------------------------------------------------------- /gif3d.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2C18D97020D76C2A9A295DDF /* Pods_gif3d.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED3BE3C4E2EEBCB9955884AE /* Pods_gif3d.framework */; }; 11 | 4C2EFF6D20F9CEF200B244C4 /* 3.gif in Resources */ = {isa = PBXBuildFile; fileRef = 4C2EFF6C20F9CEF200B244C4 /* 3.gif */; }; 12 | 4C3859C420F8FB7E009C831B /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3859C320F8FB7E009C831B /* Color.swift */; }; 13 | 4C3859C620F8FDDE009C831B /* ColorItemCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3859C520F8FDDE009C831B /* ColorItemCell.swift */; }; 14 | 4C3859C920F905ED009C831B /* Contain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3859C820F905ED009C831B /* Contain.swift */; }; 15 | 4C3859CB20F90D93009C831B /* UIViewController+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3859CA20F90D93009C831B /* UIViewController+Extensions.swift */; }; 16 | 4C5BBE0620F3370A0098BAC3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5BBE0520F3370A0098BAC3 /* AppDelegate.swift */; }; 17 | 4C5BBE0820F3370A0098BAC3 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5BBE0720F3370A0098BAC3 /* ViewController.swift */; }; 18 | 4C5BBE0B20F3370A0098BAC3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4C5BBE0920F3370A0098BAC3 /* Main.storyboard */; }; 19 | 4C5BBE0D20F3370B0098BAC3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4C5BBE0C20F3370B0098BAC3 /* Assets.xcassets */; }; 20 | 4C5BBE1020F3370B0098BAC3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4C5BBE0E20F3370B0098BAC3 /* LaunchScreen.storyboard */; }; 21 | 4C5BBE1920F337BF0098BAC3 /* EditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5BBE1820F337BF0098BAC3 /* EditorView.swift */; }; 22 | 4C5BBE1B20F3685A0098BAC3 /* UIView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5BBE1A20F3685A0098BAC3 /* UIView+Extensions.swift */; }; 23 | 4C5BBE2020F3B67B0098BAC3 /* 2.gif in Resources */ = {isa = PBXBuildFile; fileRef = 4C5BBE1E20F3B67B0098BAC3 /* 2.gif */; }; 24 | 4C5BBE2120F3B67B0098BAC3 /* 1.gif in Resources */ = {isa = PBXBuildFile; fileRef = 4C5BBE1F20F3B67B0098BAC3 /* 1.gif */; }; 25 | 4C5BBE2320F3C4C60098BAC3 /* FileManager+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5BBE2220F3C4C60098BAC3 /* FileManager+Extensions.swift */; }; 26 | 4C9EDCEE20F6519000AD74AC /* UIViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9EDCEA20F6519000AD74AC /* UIViewExtensions.swift */; }; 27 | 4C9EDCEF20F6519000AD74AC /* Bundle+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9EDCEB20F6519000AD74AC /* Bundle+Extensions.swift */; }; 28 | 4C9EDCF020F6519000AD74AC /* CALayer+Shadow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9EDCEC20F6519000AD74AC /* CALayer+Shadow.swift */; }; 29 | 4C9EDCF120F6519000AD74AC /* UIColorExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9EDCED20F6519000AD74AC /* UIColorExtensions.swift */; }; 30 | 4C9EDCF320F656C000AD74AC /* EditorManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9EDCF220F656C000AD74AC /* EditorManager.swift */; }; 31 | 4C9EDCF620F657B500AD74AC /* Editor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9EDCF520F657B500AD74AC /* Editor.swift */; }; 32 | 4C9EDCF820F665FA00AD74AC /* PreviewVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9EDCF720F665FA00AD74AC /* PreviewVC.swift */; }; 33 | 4C9EDCFA20F669A300AD74AC /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9EDCF920F669A300AD74AC /* UIImage+Extensions.swift */; }; 34 | 4CFD0A5820F4AE480077C054 /* EditorVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFD0A5720F4AE480077C054 /* EditorVC.swift */; }; 35 | 4CFD0A5B20F4AEA30077C054 /* FrameItemCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFD0A5A20F4AEA30077C054 /* FrameItemCell.swift */; }; 36 | 4CFD0A5D20F4B73F0077C054 /* EditorFrameView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFD0A5C20F4B73F0077C054 /* EditorFrameView.swift */; }; 37 | /* End PBXBuildFile section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | 28A799EC8A7B45F167AF4D89 /* Pods-gif3d.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-gif3d.debug.xcconfig"; path = "Pods/Target Support Files/Pods-gif3d/Pods-gif3d.debug.xcconfig"; sourceTree = ""; }; 41 | 3FB49B9D72131C49E4556A2B /* Pods-gif3d.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-gif3d.release.xcconfig"; path = "Pods/Target Support Files/Pods-gif3d/Pods-gif3d.release.xcconfig"; sourceTree = ""; }; 42 | 4C2EFF6C20F9CEF200B244C4 /* 3.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = 3.gif; sourceTree = ""; }; 43 | 4C3859C320F8FB7E009C831B /* Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = ""; }; 44 | 4C3859C520F8FDDE009C831B /* ColorItemCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorItemCell.swift; sourceTree = ""; }; 45 | 4C3859C820F905ED009C831B /* Contain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contain.swift; sourceTree = ""; }; 46 | 4C3859CA20F90D93009C831B /* UIViewController+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Extensions.swift"; sourceTree = ""; }; 47 | 4C5BBE0220F3370A0098BAC3 /* gif3d.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = gif3d.app; sourceTree = BUILT_PRODUCTS_DIR; }; 48 | 4C5BBE0520F3370A0098BAC3 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 49 | 4C5BBE0720F3370A0098BAC3 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 50 | 4C5BBE0A20F3370A0098BAC3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 51 | 4C5BBE0C20F3370B0098BAC3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 52 | 4C5BBE0F20F3370B0098BAC3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 53 | 4C5BBE1120F3370C0098BAC3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 54 | 4C5BBE1820F337BF0098BAC3 /* EditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditorView.swift; sourceTree = ""; }; 55 | 4C5BBE1A20F3685A0098BAC3 /* UIView+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Extensions.swift"; sourceTree = ""; }; 56 | 4C5BBE1E20F3B67B0098BAC3 /* 2.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = 2.gif; sourceTree = ""; }; 57 | 4C5BBE1F20F3B67B0098BAC3 /* 1.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = 1.gif; sourceTree = ""; }; 58 | 4C5BBE2220F3C4C60098BAC3 /* FileManager+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManager+Extensions.swift"; sourceTree = ""; }; 59 | 4C9EDCEA20F6519000AD74AC /* UIViewExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewExtensions.swift; sourceTree = ""; }; 60 | 4C9EDCEB20F6519000AD74AC /* Bundle+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Bundle+Extensions.swift"; sourceTree = ""; }; 61 | 4C9EDCEC20F6519000AD74AC /* CALayer+Shadow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CALayer+Shadow.swift"; sourceTree = ""; }; 62 | 4C9EDCED20F6519000AD74AC /* UIColorExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIColorExtensions.swift; sourceTree = ""; }; 63 | 4C9EDCF220F656C000AD74AC /* EditorManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditorManager.swift; sourceTree = ""; }; 64 | 4C9EDCF520F657B500AD74AC /* Editor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Editor.swift; sourceTree = ""; }; 65 | 4C9EDCF720F665FA00AD74AC /* PreviewVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewVC.swift; sourceTree = ""; }; 66 | 4C9EDCF920F669A300AD74AC /* UIImage+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Extensions.swift"; sourceTree = ""; }; 67 | 4CFD0A5720F4AE480077C054 /* EditorVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditorVC.swift; sourceTree = ""; }; 68 | 4CFD0A5A20F4AEA30077C054 /* FrameItemCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FrameItemCell.swift; sourceTree = ""; }; 69 | 4CFD0A5C20F4B73F0077C054 /* EditorFrameView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditorFrameView.swift; sourceTree = ""; }; 70 | ED3BE3C4E2EEBCB9955884AE /* Pods_gif3d.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_gif3d.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 71 | /* End PBXFileReference section */ 72 | 73 | /* Begin PBXFrameworksBuildPhase section */ 74 | 4C5BBDFF20F3370A0098BAC3 /* Frameworks */ = { 75 | isa = PBXFrameworksBuildPhase; 76 | buildActionMask = 2147483647; 77 | files = ( 78 | 2C18D97020D76C2A9A295DDF /* Pods_gif3d.framework in Frameworks */, 79 | ); 80 | runOnlyForDeploymentPostprocessing = 0; 81 | }; 82 | /* End PBXFrameworksBuildPhase section */ 83 | 84 | /* Begin PBXGroup section */ 85 | 4C3859C720F905D8009C831B /* utility */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | 4C3859C820F905ED009C831B /* Contain.swift */, 89 | ); 90 | path = utility; 91 | sourceTree = ""; 92 | }; 93 | 4C5BBDF920F3370A0098BAC3 = { 94 | isa = PBXGroup; 95 | children = ( 96 | 4C5BBE0420F3370A0098BAC3 /* gif3d */, 97 | 4C5BBE0320F3370A0098BAC3 /* Products */, 98 | F6256857E0E45C8976540BB6 /* Pods */, 99 | C65EE308D7E15CED45E02637 /* Frameworks */, 100 | ); 101 | sourceTree = ""; 102 | }; 103 | 4C5BBE0320F3370A0098BAC3 /* Products */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 4C5BBE0220F3370A0098BAC3 /* gif3d.app */, 107 | ); 108 | name = Products; 109 | sourceTree = ""; 110 | }; 111 | 4C5BBE0420F3370A0098BAC3 /* gif3d */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 4C5BBE0520F3370A0098BAC3 /* AppDelegate.swift */, 115 | 4C9EDCF420F6578F00AD74AC /* model */, 116 | 4CFD0A5420F4A85E0077C054 /* managers */, 117 | 4C5BBE1720F337AC0098BAC3 /* extensions */, 118 | 4CFD0A5520F4A8840077C054 /* views */, 119 | 4CFD0A5620F4AD550077C054 /* viewcontrollers */, 120 | 4C3859C720F905D8009C831B /* utility */, 121 | 4C5BBE1C20F3B65D0098BAC3 /* resources */, 122 | 4CFD0A5320F3CC720077C054 /* Supporting Files */, 123 | 4C5BBE0E20F3370B0098BAC3 /* LaunchScreen.storyboard */, 124 | 4C5BBE0920F3370A0098BAC3 /* Main.storyboard */, 125 | 4C5BBE1120F3370C0098BAC3 /* Info.plist */, 126 | ); 127 | path = gif3d; 128 | sourceTree = ""; 129 | }; 130 | 4C5BBE1720F337AC0098BAC3 /* extensions */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | 4C3859CA20F90D93009C831B /* UIViewController+Extensions.swift */, 134 | 4C9EDCEB20F6519000AD74AC /* Bundle+Extensions.swift */, 135 | 4C9EDCEC20F6519000AD74AC /* CALayer+Shadow.swift */, 136 | 4C9EDCED20F6519000AD74AC /* UIColorExtensions.swift */, 137 | 4C9EDCEA20F6519000AD74AC /* UIViewExtensions.swift */, 138 | 4C5BBE1A20F3685A0098BAC3 /* UIView+Extensions.swift */, 139 | 4C5BBE2220F3C4C60098BAC3 /* FileManager+Extensions.swift */, 140 | 4C9EDCF920F669A300AD74AC /* UIImage+Extensions.swift */, 141 | 4C3859C320F8FB7E009C831B /* Color.swift */, 142 | ); 143 | path = extensions; 144 | sourceTree = ""; 145 | }; 146 | 4C5BBE1C20F3B65D0098BAC3 /* resources */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | 4C5BBE0C20F3370B0098BAC3 /* Assets.xcassets */, 150 | ); 151 | path = resources; 152 | sourceTree = ""; 153 | }; 154 | 4C9EDCF420F6578F00AD74AC /* model */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | 4C9EDCF520F657B500AD74AC /* Editor.swift */, 158 | ); 159 | path = model; 160 | sourceTree = ""; 161 | }; 162 | 4CFD0A5320F3CC720077C054 /* Supporting Files */ = { 163 | isa = PBXGroup; 164 | children = ( 165 | 4C2EFF6C20F9CEF200B244C4 /* 3.gif */, 166 | 4C5BBE1E20F3B67B0098BAC3 /* 2.gif */, 167 | 4C5BBE1F20F3B67B0098BAC3 /* 1.gif */, 168 | ); 169 | path = "Supporting Files"; 170 | sourceTree = ""; 171 | }; 172 | 4CFD0A5420F4A85E0077C054 /* managers */ = { 173 | isa = PBXGroup; 174 | children = ( 175 | 4C9EDCF220F656C000AD74AC /* EditorManager.swift */, 176 | ); 177 | path = managers; 178 | sourceTree = ""; 179 | }; 180 | 4CFD0A5520F4A8840077C054 /* views */ = { 181 | isa = PBXGroup; 182 | children = ( 183 | 4C5BBE1820F337BF0098BAC3 /* EditorView.swift */, 184 | 4CFD0A5C20F4B73F0077C054 /* EditorFrameView.swift */, 185 | ); 186 | path = views; 187 | sourceTree = ""; 188 | }; 189 | 4CFD0A5620F4AD550077C054 /* viewcontrollers */ = { 190 | isa = PBXGroup; 191 | children = ( 192 | 4CFD0A5920F4AE4F0077C054 /* cell */, 193 | 4C5BBE0720F3370A0098BAC3 /* ViewController.swift */, 194 | 4CFD0A5720F4AE480077C054 /* EditorVC.swift */, 195 | 4C9EDCF720F665FA00AD74AC /* PreviewVC.swift */, 196 | ); 197 | path = viewcontrollers; 198 | sourceTree = ""; 199 | }; 200 | 4CFD0A5920F4AE4F0077C054 /* cell */ = { 201 | isa = PBXGroup; 202 | children = ( 203 | 4CFD0A5A20F4AEA30077C054 /* FrameItemCell.swift */, 204 | 4C3859C520F8FDDE009C831B /* ColorItemCell.swift */, 205 | ); 206 | path = cell; 207 | sourceTree = ""; 208 | }; 209 | C65EE308D7E15CED45E02637 /* Frameworks */ = { 210 | isa = PBXGroup; 211 | children = ( 212 | ED3BE3C4E2EEBCB9955884AE /* Pods_gif3d.framework */, 213 | ); 214 | name = Frameworks; 215 | sourceTree = ""; 216 | }; 217 | F6256857E0E45C8976540BB6 /* Pods */ = { 218 | isa = PBXGroup; 219 | children = ( 220 | 28A799EC8A7B45F167AF4D89 /* Pods-gif3d.debug.xcconfig */, 221 | 3FB49B9D72131C49E4556A2B /* Pods-gif3d.release.xcconfig */, 222 | ); 223 | name = Pods; 224 | sourceTree = ""; 225 | }; 226 | /* End PBXGroup section */ 227 | 228 | /* Begin PBXNativeTarget section */ 229 | 4C5BBE0120F3370A0098BAC3 /* gif3d */ = { 230 | isa = PBXNativeTarget; 231 | buildConfigurationList = 4C5BBE1420F3370C0098BAC3 /* Build configuration list for PBXNativeTarget "gif3d" */; 232 | buildPhases = ( 233 | DFEBCDD61D9A755EF8DABBB7 /* [CP] Check Pods Manifest.lock */, 234 | 4C5BBDFE20F3370A0098BAC3 /* Sources */, 235 | 4C5BBDFF20F3370A0098BAC3 /* Frameworks */, 236 | 4C5BBE0020F3370A0098BAC3 /* Resources */, 237 | B7EC605760498C12E7CB8328 /* [CP] Embed Pods Frameworks */, 238 | ); 239 | buildRules = ( 240 | ); 241 | dependencies = ( 242 | ); 243 | name = gif3d; 244 | productName = gif3d; 245 | productReference = 4C5BBE0220F3370A0098BAC3 /* gif3d.app */; 246 | productType = "com.apple.product-type.application"; 247 | }; 248 | /* End PBXNativeTarget section */ 249 | 250 | /* Begin PBXProject section */ 251 | 4C5BBDFA20F3370A0098BAC3 /* Project object */ = { 252 | isa = PBXProject; 253 | attributes = { 254 | LastSwiftUpdateCheck = 0940; 255 | LastUpgradeCheck = 0940; 256 | ORGANIZATIONNAME = chuongtran; 257 | TargetAttributes = { 258 | 4C5BBE0120F3370A0098BAC3 = { 259 | CreatedOnToolsVersion = 9.4.1; 260 | }; 261 | }; 262 | }; 263 | buildConfigurationList = 4C5BBDFD20F3370A0098BAC3 /* Build configuration list for PBXProject "gif3d" */; 264 | compatibilityVersion = "Xcode 9.3"; 265 | developmentRegion = en; 266 | hasScannedForEncodings = 0; 267 | knownRegions = ( 268 | en, 269 | Base, 270 | ); 271 | mainGroup = 4C5BBDF920F3370A0098BAC3; 272 | productRefGroup = 4C5BBE0320F3370A0098BAC3 /* Products */; 273 | projectDirPath = ""; 274 | projectRoot = ""; 275 | targets = ( 276 | 4C5BBE0120F3370A0098BAC3 /* gif3d */, 277 | ); 278 | }; 279 | /* End PBXProject section */ 280 | 281 | /* Begin PBXResourcesBuildPhase section */ 282 | 4C5BBE0020F3370A0098BAC3 /* Resources */ = { 283 | isa = PBXResourcesBuildPhase; 284 | buildActionMask = 2147483647; 285 | files = ( 286 | 4C2EFF6D20F9CEF200B244C4 /* 3.gif in Resources */, 287 | 4C5BBE1020F3370B0098BAC3 /* LaunchScreen.storyboard in Resources */, 288 | 4C5BBE0D20F3370B0098BAC3 /* Assets.xcassets in Resources */, 289 | 4C5BBE0B20F3370A0098BAC3 /* Main.storyboard in Resources */, 290 | 4C5BBE2120F3B67B0098BAC3 /* 1.gif in Resources */, 291 | 4C5BBE2020F3B67B0098BAC3 /* 2.gif in Resources */, 292 | ); 293 | runOnlyForDeploymentPostprocessing = 0; 294 | }; 295 | /* End PBXResourcesBuildPhase section */ 296 | 297 | /* Begin PBXShellScriptBuildPhase section */ 298 | B7EC605760498C12E7CB8328 /* [CP] Embed Pods Frameworks */ = { 299 | isa = PBXShellScriptBuildPhase; 300 | buildActionMask = 2147483647; 301 | files = ( 302 | ); 303 | inputPaths = ( 304 | "${SRCROOT}/Pods/Target Support Files/Pods-gif3d/Pods-gif3d-frameworks.sh", 305 | "${BUILT_PRODUCTS_DIR}/FLAnimatedImage/FLAnimatedImage.framework", 306 | "${BUILT_PRODUCTS_DIR}/MBProgressHUD/MBProgressHUD.framework", 307 | "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework", 308 | ); 309 | name = "[CP] Embed Pods Frameworks"; 310 | outputPaths = ( 311 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FLAnimatedImage.framework", 312 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MBProgressHUD.framework", 313 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework", 314 | ); 315 | runOnlyForDeploymentPostprocessing = 0; 316 | shellPath = /bin/sh; 317 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-gif3d/Pods-gif3d-frameworks.sh\"\n"; 318 | showEnvVarsInLog = 0; 319 | }; 320 | DFEBCDD61D9A755EF8DABBB7 /* [CP] Check Pods Manifest.lock */ = { 321 | isa = PBXShellScriptBuildPhase; 322 | buildActionMask = 2147483647; 323 | files = ( 324 | ); 325 | inputPaths = ( 326 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 327 | "${PODS_ROOT}/Manifest.lock", 328 | ); 329 | name = "[CP] Check Pods Manifest.lock"; 330 | outputPaths = ( 331 | "$(DERIVED_FILE_DIR)/Pods-gif3d-checkManifestLockResult.txt", 332 | ); 333 | runOnlyForDeploymentPostprocessing = 0; 334 | shellPath = /bin/sh; 335 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 336 | showEnvVarsInLog = 0; 337 | }; 338 | /* End PBXShellScriptBuildPhase section */ 339 | 340 | /* Begin PBXSourcesBuildPhase section */ 341 | 4C5BBDFE20F3370A0098BAC3 /* Sources */ = { 342 | isa = PBXSourcesBuildPhase; 343 | buildActionMask = 2147483647; 344 | files = ( 345 | 4C5BBE2320F3C4C60098BAC3 /* FileManager+Extensions.swift in Sources */, 346 | 4C9EDCEF20F6519000AD74AC /* Bundle+Extensions.swift in Sources */, 347 | 4C5BBE0820F3370A0098BAC3 /* ViewController.swift in Sources */, 348 | 4C9EDCF320F656C000AD74AC /* EditorManager.swift in Sources */, 349 | 4C3859C420F8FB7E009C831B /* Color.swift in Sources */, 350 | 4C9EDCF620F657B500AD74AC /* Editor.swift in Sources */, 351 | 4C9EDCF120F6519000AD74AC /* UIColorExtensions.swift in Sources */, 352 | 4C3859C920F905ED009C831B /* Contain.swift in Sources */, 353 | 4C5BBE1B20F3685A0098BAC3 /* UIView+Extensions.swift in Sources */, 354 | 4C9EDCF820F665FA00AD74AC /* PreviewVC.swift in Sources */, 355 | 4C9EDCF020F6519000AD74AC /* CALayer+Shadow.swift in Sources */, 356 | 4C5BBE1920F337BF0098BAC3 /* EditorView.swift in Sources */, 357 | 4C3859CB20F90D93009C831B /* UIViewController+Extensions.swift in Sources */, 358 | 4C5BBE0620F3370A0098BAC3 /* AppDelegate.swift in Sources */, 359 | 4C3859C620F8FDDE009C831B /* ColorItemCell.swift in Sources */, 360 | 4CFD0A5D20F4B73F0077C054 /* EditorFrameView.swift in Sources */, 361 | 4CFD0A5820F4AE480077C054 /* EditorVC.swift in Sources */, 362 | 4C9EDCEE20F6519000AD74AC /* UIViewExtensions.swift in Sources */, 363 | 4CFD0A5B20F4AEA30077C054 /* FrameItemCell.swift in Sources */, 364 | 4C9EDCFA20F669A300AD74AC /* UIImage+Extensions.swift in Sources */, 365 | ); 366 | runOnlyForDeploymentPostprocessing = 0; 367 | }; 368 | /* End PBXSourcesBuildPhase section */ 369 | 370 | /* Begin PBXVariantGroup section */ 371 | 4C5BBE0920F3370A0098BAC3 /* Main.storyboard */ = { 372 | isa = PBXVariantGroup; 373 | children = ( 374 | 4C5BBE0A20F3370A0098BAC3 /* Base */, 375 | ); 376 | name = Main.storyboard; 377 | sourceTree = ""; 378 | }; 379 | 4C5BBE0E20F3370B0098BAC3 /* LaunchScreen.storyboard */ = { 380 | isa = PBXVariantGroup; 381 | children = ( 382 | 4C5BBE0F20F3370B0098BAC3 /* Base */, 383 | ); 384 | name = LaunchScreen.storyboard; 385 | sourceTree = ""; 386 | }; 387 | /* End PBXVariantGroup section */ 388 | 389 | /* Begin XCBuildConfiguration section */ 390 | 4C5BBE1220F3370C0098BAC3 /* Debug */ = { 391 | isa = XCBuildConfiguration; 392 | buildSettings = { 393 | ALWAYS_SEARCH_USER_PATHS = NO; 394 | CLANG_ANALYZER_NONNULL = YES; 395 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 396 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 397 | CLANG_CXX_LIBRARY = "libc++"; 398 | CLANG_ENABLE_MODULES = YES; 399 | CLANG_ENABLE_OBJC_ARC = YES; 400 | CLANG_ENABLE_OBJC_WEAK = YES; 401 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 402 | CLANG_WARN_BOOL_CONVERSION = YES; 403 | CLANG_WARN_COMMA = YES; 404 | CLANG_WARN_CONSTANT_CONVERSION = YES; 405 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 406 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 407 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 408 | CLANG_WARN_EMPTY_BODY = YES; 409 | CLANG_WARN_ENUM_CONVERSION = YES; 410 | CLANG_WARN_INFINITE_RECURSION = YES; 411 | CLANG_WARN_INT_CONVERSION = YES; 412 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 413 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 414 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 415 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 416 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 417 | CLANG_WARN_STRICT_PROTOTYPES = YES; 418 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 419 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 420 | CLANG_WARN_UNREACHABLE_CODE = YES; 421 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 422 | CODE_SIGN_IDENTITY = "iPhone Developer"; 423 | COPY_PHASE_STRIP = NO; 424 | DEBUG_INFORMATION_FORMAT = dwarf; 425 | ENABLE_STRICT_OBJC_MSGSEND = YES; 426 | ENABLE_TESTABILITY = YES; 427 | GCC_C_LANGUAGE_STANDARD = gnu11; 428 | GCC_DYNAMIC_NO_PIC = NO; 429 | GCC_NO_COMMON_BLOCKS = YES; 430 | GCC_OPTIMIZATION_LEVEL = 0; 431 | GCC_PREPROCESSOR_DEFINITIONS = ( 432 | "DEBUG=1", 433 | "$(inherited)", 434 | ); 435 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 436 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 437 | GCC_WARN_UNDECLARED_SELECTOR = YES; 438 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 439 | GCC_WARN_UNUSED_FUNCTION = YES; 440 | GCC_WARN_UNUSED_VARIABLE = YES; 441 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 442 | MTL_ENABLE_DEBUG_INFO = YES; 443 | ONLY_ACTIVE_ARCH = YES; 444 | SDKROOT = iphoneos; 445 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 446 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 447 | }; 448 | name = Debug; 449 | }; 450 | 4C5BBE1320F3370C0098BAC3 /* Release */ = { 451 | isa = XCBuildConfiguration; 452 | buildSettings = { 453 | ALWAYS_SEARCH_USER_PATHS = NO; 454 | CLANG_ANALYZER_NONNULL = YES; 455 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 456 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 457 | CLANG_CXX_LIBRARY = "libc++"; 458 | CLANG_ENABLE_MODULES = YES; 459 | CLANG_ENABLE_OBJC_ARC = YES; 460 | CLANG_ENABLE_OBJC_WEAK = YES; 461 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 462 | CLANG_WARN_BOOL_CONVERSION = YES; 463 | CLANG_WARN_COMMA = YES; 464 | CLANG_WARN_CONSTANT_CONVERSION = YES; 465 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 466 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 467 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 468 | CLANG_WARN_EMPTY_BODY = YES; 469 | CLANG_WARN_ENUM_CONVERSION = YES; 470 | CLANG_WARN_INFINITE_RECURSION = YES; 471 | CLANG_WARN_INT_CONVERSION = YES; 472 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 473 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 474 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 475 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 476 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 477 | CLANG_WARN_STRICT_PROTOTYPES = YES; 478 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 479 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 480 | CLANG_WARN_UNREACHABLE_CODE = YES; 481 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 482 | CODE_SIGN_IDENTITY = "iPhone Developer"; 483 | COPY_PHASE_STRIP = NO; 484 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 485 | ENABLE_NS_ASSERTIONS = NO; 486 | ENABLE_STRICT_OBJC_MSGSEND = YES; 487 | GCC_C_LANGUAGE_STANDARD = gnu11; 488 | GCC_NO_COMMON_BLOCKS = YES; 489 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 490 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 491 | GCC_WARN_UNDECLARED_SELECTOR = YES; 492 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 493 | GCC_WARN_UNUSED_FUNCTION = YES; 494 | GCC_WARN_UNUSED_VARIABLE = YES; 495 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 496 | MTL_ENABLE_DEBUG_INFO = NO; 497 | SDKROOT = iphoneos; 498 | SWIFT_COMPILATION_MODE = wholemodule; 499 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 500 | VALIDATE_PRODUCT = YES; 501 | }; 502 | name = Release; 503 | }; 504 | 4C5BBE1520F3370C0098BAC3 /* Debug */ = { 505 | isa = XCBuildConfiguration; 506 | baseConfigurationReference = 28A799EC8A7B45F167AF4D89 /* Pods-gif3d.debug.xcconfig */; 507 | buildSettings = { 508 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 509 | CODE_SIGN_STYLE = Automatic; 510 | DEVELOPMENT_TEAM = W336V3LYMS; 511 | INFOPLIST_FILE = gif3d/Info.plist; 512 | LD_RUNPATH_SEARCH_PATHS = ( 513 | "$(inherited)", 514 | "@executable_path/Frameworks", 515 | ); 516 | PRODUCT_BUNDLE_IDENTIFIER = com.chuongtran.gif3d; 517 | PRODUCT_NAME = "$(TARGET_NAME)"; 518 | SWIFT_VERSION = 4.0; 519 | TARGETED_DEVICE_FAMILY = "1,2"; 520 | }; 521 | name = Debug; 522 | }; 523 | 4C5BBE1620F3370C0098BAC3 /* Release */ = { 524 | isa = XCBuildConfiguration; 525 | baseConfigurationReference = 3FB49B9D72131C49E4556A2B /* Pods-gif3d.release.xcconfig */; 526 | buildSettings = { 527 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 528 | CODE_SIGN_STYLE = Automatic; 529 | DEVELOPMENT_TEAM = W336V3LYMS; 530 | INFOPLIST_FILE = gif3d/Info.plist; 531 | LD_RUNPATH_SEARCH_PATHS = ( 532 | "$(inherited)", 533 | "@executable_path/Frameworks", 534 | ); 535 | PRODUCT_BUNDLE_IDENTIFIER = com.chuongtran.gif3d; 536 | PRODUCT_NAME = "$(TARGET_NAME)"; 537 | SWIFT_VERSION = 4.0; 538 | TARGETED_DEVICE_FAMILY = "1,2"; 539 | }; 540 | name = Release; 541 | }; 542 | /* End XCBuildConfiguration section */ 543 | 544 | /* Begin XCConfigurationList section */ 545 | 4C5BBDFD20F3370A0098BAC3 /* Build configuration list for PBXProject "gif3d" */ = { 546 | isa = XCConfigurationList; 547 | buildConfigurations = ( 548 | 4C5BBE1220F3370C0098BAC3 /* Debug */, 549 | 4C5BBE1320F3370C0098BAC3 /* Release */, 550 | ); 551 | defaultConfigurationIsVisible = 0; 552 | defaultConfigurationName = Release; 553 | }; 554 | 4C5BBE1420F3370C0098BAC3 /* Build configuration list for PBXNativeTarget "gif3d" */ = { 555 | isa = XCConfigurationList; 556 | buildConfigurations = ( 557 | 4C5BBE1520F3370C0098BAC3 /* Debug */, 558 | 4C5BBE1620F3370C0098BAC3 /* Release */, 559 | ); 560 | defaultConfigurationIsVisible = 0; 561 | defaultConfigurationName = Release; 562 | }; 563 | /* End XCConfigurationList section */ 564 | }; 565 | rootObject = 4C5BBDFA20F3370A0098BAC3 /* Project object */; 566 | } 567 | -------------------------------------------------------------------------------- /gif3d.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /gif3d.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /gif3d.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /gif3d.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /gif3d/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // gif3d 4 | // 5 | // Created by Sam on 7/9/18. 6 | // Copyright © 2018 chuongtran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // 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. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // 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. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // 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. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // 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. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /gif3d/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 | -------------------------------------------------------------------------------- /gif3d/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 172 | 183 | 184 | 185 | 186 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 336 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | -------------------------------------------------------------------------------- /gif3d/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 | NSPhotoLibraryUsageDescription 32 | This app requires access to the photo library. 33 | NSPhotoLibraryAddUsageDescription 34 | This app requires access to the photo library. 35 | 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UISupportedInterfaceOrientations~ipad 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationPortraitUpsideDown 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /gif3d/Supporting Files/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chuongtrh/gif3d/8829c0e837c87565f974e3f655f441e47cf963e1/gif3d/Supporting Files/1.gif -------------------------------------------------------------------------------- /gif3d/Supporting Files/2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chuongtrh/gif3d/8829c0e837c87565f974e3f655f441e47cf963e1/gif3d/Supporting Files/2.gif -------------------------------------------------------------------------------- /gif3d/Supporting Files/3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chuongtrh/gif3d/8829c0e837c87565f974e3f655f441e47cf963e1/gif3d/Supporting Files/3.gif -------------------------------------------------------------------------------- /gif3d/extensions/Bundle+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Bun.swift 3 | // YWOMOLLY_dev 4 | // 5 | // Created by Sam on 5/30/18. 6 | // Copyright © 2018 94. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | extension Bundle { 13 | var releaseVersionNumber: String? { 14 | return infoDictionary?["CFBundleShortVersionString"] as? String 15 | } 16 | var buildVersionNumber: String? { 17 | return infoDictionary?["CFBundleVersion"] as? String 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /gif3d/extensions/CALayer+Shadow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CALayer+Shadow.swift 3 | // YWOMOLLY_dev 4 | // 5 | // Created by Sam on 5/10/18. 6 | // Copyright © 2018 94. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | extension CALayer { 13 | 14 | func makeShadow(color: UIColor, 15 | x: CGFloat = 0, 16 | y: CGFloat = 0, 17 | blur: CGFloat = 0, 18 | spread: CGFloat = 0) { 19 | shadowColor = color.cgColor 20 | shadowOpacity = 1 21 | shadowOffset = CGSize(width: x, height: y) 22 | shadowRadius = blur / 2 23 | if spread == 0 { 24 | shadowPath = nil 25 | } 26 | else { 27 | let rect = bounds.insetBy(dx: -spread, dy: -spread) 28 | shadowPath = UIBezierPath(rect: rect).cgPath 29 | } 30 | } 31 | 32 | func removeShadow(){ 33 | shadowColor = UIColor.clear.cgColor 34 | shadowOpacity = 0 35 | shadowRadius = 0 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /gif3d/extensions/Color.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 - 2018, Daniel Dahan and CosmicMind, Inc. . 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of CosmicMind nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | import UIKit 32 | 33 | @objc(ColorPalette) 34 | public protocol ColorPalette { 35 | /// Material color code: 50 36 | static var lighten5: UIColor { get } 37 | /// Material color code: 100 38 | static var lighten4: UIColor { get } 39 | /// Material color code: 200 40 | static var lighten3: UIColor { get } 41 | /// Material color code: 300 42 | static var lighten2: UIColor { get } 43 | /// Material color code: 400 44 | static var lighten1: UIColor { get } 45 | /// Material color code: 500 46 | static var base: UIColor { get } 47 | /// Material color code: 600 48 | static var darken1: UIColor { get } 49 | /// Material color code: 700 50 | static var darken2: UIColor { get } 51 | /// Material color code: 800 52 | static var darken3: UIColor { get } 53 | /// Material color code: 900 54 | static var darken4: UIColor { get } 55 | 56 | /// Material color code: A100 57 | @objc 58 | optional static var accent1: UIColor { get } 59 | 60 | /// Material color code: A200 61 | @objc 62 | optional static var accent2: UIColor { get } 63 | 64 | /// Material color code: A400 65 | @objc 66 | optional static var accent3: UIColor { get } 67 | 68 | /// Material color code: A700 69 | @objc 70 | optional static var accent4: UIColor { get } 71 | } 72 | 73 | open class Color: UIColor { 74 | // dark text 75 | open class darkText { 76 | public static let primary = Color.black.withAlphaComponent(0.87) 77 | public static let secondary = Color.black.withAlphaComponent(0.54) 78 | public static let others = Color.black.withAlphaComponent(0.38) 79 | public static let dividers = Color.black.withAlphaComponent(0.12) 80 | } 81 | 82 | // light text 83 | open class lightText { 84 | public static let primary = Color.white 85 | public static let secondary = Color.white.withAlphaComponent(0.7) 86 | public static let others = Color.white.withAlphaComponent(0.5) 87 | public static let dividers = Color.white.withAlphaComponent(0.12) 88 | } 89 | 90 | // red 91 | open class red: ColorPalette { 92 | public static let lighten5 = UIColor(red: 255/255, green: 235/255, blue: 238/255, alpha: 1) 93 | public static let lighten4 = UIColor(red: 255/255, green: 205/255, blue: 210/255, alpha: 1) 94 | public static let lighten3 = UIColor(red: 239/255, green: 154/255, blue: 154/255, alpha: 1) 95 | public static let lighten2 = UIColor(red: 229/255, green: 115/255, blue: 115/255, alpha: 1) 96 | public static let lighten1 = UIColor(red: 229/255, green: 83/255, blue: 80/255, alpha: 1) 97 | public static let base = UIColor(red: 244/255, green: 67/255, blue: 54/255, alpha: 1) 98 | public static let darken1 = UIColor(red: 229/255, green: 57/255, blue: 53/255, alpha: 1) 99 | public static let darken2 = UIColor(red: 211/255, green: 47/255, blue: 47/255, alpha: 1) 100 | public static let darken3 = UIColor(red: 198/255, green: 40/255, blue: 40/255, alpha: 1) 101 | public static let darken4 = UIColor(red: 183/255, green: 28/255, blue: 28/255, alpha: 1) 102 | public static let accent1 = UIColor(red: 255/255, green: 138/255, blue: 128/255, alpha: 1) 103 | public static let accent2 = UIColor(red: 255/255, green: 82/255, blue: 82/255, alpha: 1) 104 | public static let accent3 = UIColor(red: 255/255, green: 23/255, blue: 68/255, alpha: 1) 105 | public static let accent4 = UIColor(red: 213/255, green: 0/255, blue: 0/255, alpha: 1) 106 | } 107 | 108 | // pink 109 | open class pink: ColorPalette { 110 | public static let lighten5 = UIColor(red: 252/255, green: 228/255, blue: 236/255, alpha: 1) 111 | public static let lighten4 = UIColor(red: 248/255, green: 187/255, blue: 208/255, alpha: 1) 112 | public static let lighten3 = UIColor(red: 244/255, green: 143/255, blue: 177/255, alpha: 1) 113 | public static let lighten2 = UIColor(red: 240/255, green: 98/255, blue: 146/255, alpha: 1) 114 | public static let lighten1 = UIColor(red: 236/255, green: 64/255, blue: 122/255, alpha: 1) 115 | public static let base = UIColor(red: 233/255, green: 30/255, blue: 99/255, alpha: 1) 116 | public static let darken1 = UIColor(red: 216/255, green: 27/255, blue: 96/255, alpha: 1) 117 | public static let darken2 = UIColor(red: 194/255, green: 24/255, blue: 91/255, alpha: 1) 118 | public static let darken3 = UIColor(red: 173/255, green: 20/255, blue: 87/255, alpha: 1) 119 | public static let darken4 = UIColor(red: 136/255, green: 14/255, blue: 79/255, alpha: 1) 120 | public static let accent1 = UIColor(red: 255/255, green: 128/255, blue: 171/255, alpha: 1) 121 | public static let accent2 = UIColor(red: 255/255, green: 64/255, blue: 129/255, alpha: 1) 122 | public static let accent3 = UIColor(red: 245/255, green: 0/255, blue: 87/255, alpha: 1) 123 | public static let accent4 = UIColor(red: 197/255, green: 17/255, blue: 98/255, alpha: 1) 124 | } 125 | 126 | // purple 127 | open class purple: ColorPalette { 128 | public static let lighten5 = UIColor(red: 243/255, green: 229/255, blue: 245/255, alpha: 1) 129 | public static let lighten4 = UIColor(red: 225/255, green: 190/255, blue: 231/255, alpha: 1) 130 | public static let lighten3 = UIColor(red: 206/255, green: 147/255, blue: 216/255, alpha: 1) 131 | public static let lighten2 = UIColor(red: 186/255, green: 104/255, blue: 200/255, alpha: 1) 132 | public static let lighten1 = UIColor(red: 171/255, green: 71/255, blue: 188/255, alpha: 1) 133 | public static let base = UIColor(red: 156/255, green: 39/255, blue: 176/255, alpha: 1) 134 | public static let darken1 = UIColor(red: 142/255, green: 36/255, blue: 170/255, alpha: 1) 135 | public static let darken2 = UIColor(red: 123/255, green: 31/255, blue: 162/255, alpha: 1) 136 | public static let darken3 = UIColor(red: 106/255, green: 27/255, blue: 154/255, alpha: 1) 137 | public static let darken4 = UIColor(red: 74/255, green: 20/255, blue: 140/255, alpha: 1) 138 | public static let accent1 = UIColor(red: 234/255, green: 128/255, blue: 252/255, alpha: 1) 139 | public static let accent2 = UIColor(red: 224/255, green: 64/255, blue: 251/255, alpha: 1) 140 | public static let accent3 = UIColor(red: 213/255, green: 0/255, blue: 249/255, alpha: 1) 141 | public static let accent4 = UIColor(red: 170/255, green: 0/255, blue: 255/255, alpha: 1) 142 | } 143 | 144 | // deepPurple 145 | open class deepPurple: ColorPalette { 146 | public static let lighten5 = UIColor(red: 237/255, green: 231/255, blue: 246/255, alpha: 1) 147 | public static let lighten4 = UIColor(red: 209/255, green: 196/255, blue: 233/255, alpha: 1) 148 | public static let lighten3 = UIColor(red: 179/255, green: 157/255, blue: 219/255, alpha: 1) 149 | public static let lighten2 = UIColor(red: 149/255, green: 117/255, blue: 205/255, alpha: 1) 150 | public static let lighten1 = UIColor(red: 126/255, green: 87/255, blue: 194/255, alpha: 1) 151 | public static let base = UIColor(red: 103/255, green: 58/255, blue: 183/255, alpha: 1) 152 | public static let darken1 = UIColor(red: 94/255, green: 53/255, blue: 177/255, alpha: 1) 153 | public static let darken2 = UIColor(red: 81/255, green: 45/255, blue: 168/255, alpha: 1) 154 | public static let darken3 = UIColor(red: 69/255, green: 39/255, blue: 160/255, alpha: 1) 155 | public static let darken4 = UIColor(red: 49/255, green: 27/255, blue: 146/255, alpha: 1) 156 | public static let accent1 = UIColor(red: 179/255, green: 136/255, blue: 255/255, alpha: 1) 157 | public static let accent2 = UIColor(red: 124/255, green: 77/255, blue: 255/255, alpha: 1) 158 | public static let accent3 = UIColor(red: 101/255, green: 31/255, blue: 255/255, alpha: 1) 159 | public static let accent4 = UIColor(red: 98/255, green: 0/255, blue: 234/255, alpha: 1) 160 | } 161 | 162 | // indigo 163 | open class indigo: ColorPalette { 164 | public static let lighten5 = UIColor(red: 232/255, green: 234/255, blue: 246/255, alpha: 1) 165 | public static let lighten4 = UIColor(red: 197/255, green: 202/255, blue: 233/255, alpha: 1) 166 | public static let lighten3 = UIColor(red: 159/255, green: 168/255, blue: 218/255, alpha: 1) 167 | public static let lighten2 = UIColor(red: 121/255, green: 134/255, blue: 203/255, alpha: 1) 168 | public static let lighten1 = UIColor(red: 92/255, green: 107/255, blue: 192/255, alpha: 1) 169 | public static let base = UIColor(red: 63/255, green: 81/255, blue: 181/255, alpha: 1) 170 | public static let darken1 = UIColor(red: 57/255, green: 73/255, blue: 171/255, alpha: 1) 171 | public static let darken2 = UIColor(red: 48/255, green: 63/255, blue: 159/255, alpha: 1) 172 | public static let darken3 = UIColor(red: 40/255, green: 53/255, blue: 147/255, alpha: 1) 173 | public static let darken4 = UIColor(red: 26/255, green: 35/255, blue: 126/255, alpha: 1) 174 | public static let accent1 = UIColor(red: 140/255, green: 158/255, blue: 255/255, alpha: 1) 175 | public static let accent2 = UIColor(red: 83/255, green: 109/255, blue: 254/255, alpha: 1) 176 | public static let accent3 = UIColor(red: 61/255, green: 90/255, blue: 254/255, alpha: 1) 177 | public static let accent4 = UIColor(red: 48/255, green: 79/255, blue: 254/255, alpha: 1) 178 | } 179 | 180 | // blue 181 | open class blue: ColorPalette { 182 | public static let lighten5 = UIColor(red: 227/255, green: 242/255, blue: 253/255, alpha: 1) 183 | public static let lighten4 = UIColor(red: 187/255, green: 222/255, blue: 251/255, alpha: 1) 184 | public static let lighten3 = UIColor(red: 144/255, green: 202/255, blue: 249/255, alpha: 1) 185 | public static let lighten2 = UIColor(red: 100/255, green: 181/255, blue: 246/255, alpha: 1) 186 | public static let lighten1 = UIColor(red: 66/255, green: 165/255, blue: 245/255, alpha: 1) 187 | public static let base = UIColor(red: 33/255, green: 150/255, blue: 243/255, alpha: 1) 188 | public static let darken1 = UIColor(red: 30/255, green: 136/255, blue: 229/255, alpha: 1) 189 | public static let darken2 = UIColor(red: 25/255, green: 118/255, blue: 210/255, alpha: 1) 190 | public static let darken3 = UIColor(red: 21/255, green: 101/255, blue: 192/255, alpha: 1) 191 | public static let darken4 = UIColor(red: 13/255, green: 71/255, blue: 161/255, alpha: 1) 192 | public static let accent1 = UIColor(red: 130/255, green: 177/255, blue: 255/255, alpha: 1) 193 | public static let accent2 = UIColor(red: 68/255, green: 138/255, blue: 255/255, alpha: 1) 194 | public static let accent3 = UIColor(red: 41/255, green: 121/255, blue: 255/255, alpha: 1) 195 | public static let accent4 = UIColor(red: 41/255, green: 98/255, blue: 255/255, alpha: 1) 196 | } 197 | 198 | // light blue 199 | open class lightBlue: ColorPalette { 200 | public static let lighten5 = UIColor(red: 225/255, green: 245/255, blue: 254/255, alpha: 1) 201 | public static let lighten4 = UIColor(red: 179/255, green: 229/255, blue: 252/255, alpha: 1) 202 | public static let lighten3 = UIColor(red: 129/255, green: 212/255, blue: 250/255, alpha: 1) 203 | public static let lighten2 = UIColor(red: 79/255, green: 195/255, blue: 247/255, alpha: 1) 204 | public static let lighten1 = UIColor(red: 41/255, green: 182/255, blue: 246/255, alpha: 1) 205 | public static let base = UIColor(red: 3/255, green: 169/255, blue: 244/255, alpha: 1) 206 | public static let darken1 = UIColor(red: 3/255, green: 155/255, blue: 229/255, alpha: 1) 207 | public static let darken2 = UIColor(red: 2/255, green: 136/255, blue: 209/255, alpha: 1) 208 | public static let darken3 = UIColor(red: 2/255, green: 119/255, blue: 189/255, alpha: 1) 209 | public static let darken4 = UIColor(red: 1/255, green: 87/255, blue: 155/255, alpha: 1) 210 | public static let accent1 = UIColor(red: 128/255, green: 216/255, blue: 255/255, alpha: 1) 211 | public static let accent2 = UIColor(red: 64/255, green: 196/255, blue: 255/255, alpha: 1) 212 | public static let accent3 = UIColor(red: 0/255, green: 176/255, blue: 255/255, alpha: 1) 213 | public static let accent4 = UIColor(red: 0/255, green: 145/255, blue: 234/255, alpha: 1) 214 | } 215 | 216 | // cyan 217 | open class cyan: ColorPalette { 218 | public static let lighten5 = UIColor(red: 224/255, green: 247/255, blue: 250/255, alpha: 1) 219 | public static let lighten4 = UIColor(red: 178/255, green: 235/255, blue: 242/255, alpha: 1) 220 | public static let lighten3 = UIColor(red: 128/255, green: 222/255, blue: 234/255, alpha: 1) 221 | public static let lighten2 = UIColor(red: 77/255, green: 208/255, blue: 225/255, alpha: 1) 222 | public static let lighten1 = UIColor(red: 38/255, green: 198/255, blue: 218/255, alpha: 1) 223 | public static let base = UIColor(red: 0/255, green: 188/255, blue: 212/255, alpha: 1) 224 | public static let darken1 = UIColor(red: 0/255, green: 172/255, blue: 193/255, alpha: 1) 225 | public static let darken2 = UIColor(red: 0/255, green: 151/255, blue: 167/255, alpha: 1) 226 | public static let darken3 = UIColor(red: 0/255, green: 131/255, blue: 143/255, alpha: 1) 227 | public static let darken4 = UIColor(red: 0/255, green: 96/255, blue: 100/255, alpha: 1) 228 | public static let accent1 = UIColor(red: 132/255, green: 255/255, blue: 255/255, alpha: 1) 229 | public static let accent2 = UIColor(red: 24/255, green: 255/255, blue: 255/255, alpha: 1) 230 | public static let accent3 = UIColor(red: 0/255, green: 229/255, blue: 255/255, alpha: 1) 231 | public static let accent4 = UIColor(red: 0/255, green: 184/255, blue: 212/255, alpha: 1) 232 | } 233 | 234 | // teal 235 | open class teal: ColorPalette { 236 | public static let lighten5 = UIColor(red: 224/255, green: 242/255, blue: 241/255, alpha: 1) 237 | public static let lighten4 = UIColor(red: 178/255, green: 223/255, blue: 219/255, alpha: 1) 238 | public static let lighten3 = UIColor(red: 128/255, green: 203/255, blue: 196/255, alpha: 1) 239 | public static let lighten2 = UIColor(red: 77/255, green: 182/255, blue: 172/255, alpha: 1) 240 | public static let lighten1 = UIColor(red: 38/255, green: 166/255, blue: 154/255, alpha: 1) 241 | public static let base = UIColor(red: 0/255, green: 150/255, blue: 136/255, alpha: 1) 242 | public static let darken1 = UIColor(red: 0/255, green: 137/255, blue: 123/255, alpha: 1) 243 | public static let darken2 = UIColor(red: 0/255, green: 121/255, blue: 107/255, alpha: 1) 244 | public static let darken3 = UIColor(red: 0/255, green: 105/255, blue: 92/255, alpha: 1) 245 | public static let darken4 = UIColor(red: 0/255, green: 77/255, blue: 64/255, alpha: 1) 246 | public static let accent1 = UIColor(red: 167/255, green: 255/255, blue: 235/255, alpha: 1) 247 | public static let accent2 = UIColor(red: 100/255, green: 255/255, blue: 218/255, alpha: 1) 248 | public static let accent3 = UIColor(red: 29/255, green: 233/255, blue: 182/255, alpha: 1) 249 | public static let accent4 = UIColor(red: 0/255, green: 191/255, blue: 165/255, alpha: 1) 250 | } 251 | 252 | // green 253 | open class green: ColorPalette { 254 | public static let lighten5 = UIColor(red: 232/255, green: 245/255, blue: 233/255, alpha: 1) 255 | public static let lighten4 = UIColor(red: 200/255, green: 230/255, blue: 201/255, alpha: 1) 256 | public static let lighten3 = UIColor(red: 165/255, green: 214/255, blue: 167/255, alpha: 1) 257 | public static let lighten2 = UIColor(red: 129/255, green: 199/255, blue: 132/255, alpha: 1) 258 | public static let lighten1 = UIColor(red: 102/255, green: 187/255, blue: 106/255, alpha: 1) 259 | public static let base = UIColor(red: 76/255, green: 175/255, blue: 80/255, alpha: 1) 260 | public static let darken1 = UIColor(red: 67/255, green: 160/255, blue: 71/255, alpha: 1) 261 | public static let darken2 = UIColor(red: 56/255, green: 142/255, blue: 60/255, alpha: 1) 262 | public static let darken3 = UIColor(red: 46/255, green: 125/255, blue: 50/255, alpha: 1) 263 | public static let darken4 = UIColor(red: 27/255, green: 94/255, blue: 32/255, alpha: 1) 264 | public static let accent1 = UIColor(red: 185/255, green: 246/255, blue: 202/255, alpha: 1) 265 | public static let accent2 = UIColor(red: 105/255, green: 240/255, blue: 174/255, alpha: 1) 266 | public static let accent3 = UIColor(red: 0/255, green: 230/255, blue: 118/255, alpha: 1) 267 | public static let accent4 = UIColor(red: 0/255, green: 200/255, blue: 83/255, alpha: 1) 268 | } 269 | 270 | // light green 271 | open class lightGreen: ColorPalette { 272 | public static let lighten5 = UIColor(red: 241/255, green: 248/255, blue: 233/255, alpha: 1) 273 | public static let lighten4 = UIColor(red: 220/255, green: 237/255, blue: 200/255, alpha: 1) 274 | public static let lighten3 = UIColor(red: 197/255, green: 225/255, blue: 165/255, alpha: 1) 275 | public static let lighten2 = UIColor(red: 174/255, green: 213/255, blue: 129/255, alpha: 1) 276 | public static let lighten1 = UIColor(red: 156/255, green: 204/255, blue: 101/255, alpha: 1) 277 | public static let base = UIColor(red: 139/255, green: 195/255, blue: 74/255, alpha: 1) 278 | public static let darken1 = UIColor(red: 124/255, green: 179/255, blue: 66/255, alpha: 1) 279 | public static let darken2 = UIColor(red: 104/255, green: 159/255, blue: 56/255, alpha: 1) 280 | public static let darken3 = UIColor(red: 85/255, green: 139/255, blue: 47/255, alpha: 1) 281 | public static let darken4 = UIColor(red: 51/255, green: 105/255, blue: 30/255, alpha: 1) 282 | public static let accent1 = UIColor(red: 204/255, green: 255/255, blue: 144/255, alpha: 1) 283 | public static let accent2 = UIColor(red: 178/255, green: 255/255, blue: 89/255, alpha: 1) 284 | public static let accent3 = UIColor(red: 118/255, green: 255/255, blue: 3/255, alpha: 1) 285 | public static let accent4 = UIColor(red: 100/255, green: 221/255, blue: 23/255, alpha: 1) 286 | } 287 | 288 | // lime 289 | open class lime: ColorPalette { 290 | public static let lighten5 = UIColor(red: 249/255, green: 251/255, blue: 231/255, alpha: 1) 291 | public static let lighten4 = UIColor(red: 240/255, green: 244/255, blue: 195/255, alpha: 1) 292 | public static let lighten3 = UIColor(red: 230/255, green: 238/255, blue: 156/255, alpha: 1) 293 | public static let lighten2 = UIColor(red: 220/255, green: 231/255, blue: 117/255, alpha: 1) 294 | public static let lighten1 = UIColor(red: 212/255, green: 225/255, blue: 87/255, alpha: 1) 295 | public static let base = UIColor(red: 205/255, green: 220/255, blue: 57/255, alpha: 1) 296 | public static let darken1 = UIColor(red: 192/255, green: 202/255, blue: 51/255, alpha: 1) 297 | public static let darken2 = UIColor(red: 175/255, green: 180/255, blue: 43/255, alpha: 1) 298 | public static let darken3 = UIColor(red: 158/255, green: 157/255, blue: 36/255, alpha: 1) 299 | public static let darken4 = UIColor(red: 130/255, green: 119/255, blue: 23/255, alpha: 1) 300 | public static let accent1 = UIColor(red: 244/255, green: 255/255, blue: 129/255, alpha: 1) 301 | public static let accent2 = UIColor(red: 238/255, green: 255/255, blue: 65/255, alpha: 1) 302 | public static let accent3 = UIColor(red: 198/255, green: 255/255, blue: 0/255, alpha: 1) 303 | public static let accent4 = UIColor(red: 174/255, green: 234/255, blue: 0/255, alpha: 1) 304 | } 305 | 306 | // yellow 307 | open class yellow: ColorPalette { 308 | public static let lighten5 = UIColor(red: 255/255, green: 253/255, blue: 231/255, alpha: 1) 309 | public static let lighten4 = UIColor(red: 255/255, green: 249/255, blue: 196/255, alpha: 1) 310 | public static let lighten3 = UIColor(red: 255/255, green: 245/255, blue: 157/255, alpha: 1) 311 | public static let lighten2 = UIColor(red: 255/255, green: 241/255, blue: 118/255, alpha: 1) 312 | public static let lighten1 = UIColor(red: 255/255, green: 238/255, blue: 88/255, alpha: 1) 313 | public static let base = UIColor(red: 255/255, green: 235/255, blue: 59/255, alpha: 1) 314 | public static let darken1 = UIColor(red: 253/255, green: 216/255, blue: 53/255, alpha: 1) 315 | public static let darken2 = UIColor(red: 251/255, green: 192/255, blue: 45/255, alpha: 1) 316 | public static let darken3 = UIColor(red: 249/255, green: 168/255, blue: 37/255, alpha: 1) 317 | public static let darken4 = UIColor(red: 245/255, green: 127/255, blue: 23/255, alpha: 1) 318 | public static let accent1 = UIColor(red: 255/255, green: 255/255, blue: 141/255, alpha: 1) 319 | public static let accent2 = UIColor(red: 255/255, green: 255/255, blue: 0/255, alpha: 1) 320 | public static let accent3 = UIColor(red: 255/255, green: 234/255, blue: 0/255, alpha: 1) 321 | public static let accent4 = UIColor(red: 255/255, green: 214/255, blue: 0/255, alpha: 1) 322 | } 323 | 324 | // amber 325 | open class amber: ColorPalette { 326 | public static let lighten5 = UIColor(red: 255/255, green: 248/255, blue: 225/255, alpha: 1) 327 | public static let lighten4 = UIColor(red: 255/255, green: 236/255, blue: 179/255, alpha: 1) 328 | public static let lighten3 = UIColor(red: 255/255, green: 224/255, blue: 130/255, alpha: 1) 329 | public static let lighten2 = UIColor(red: 255/255, green: 213/255, blue: 79/255, alpha: 1) 330 | public static let lighten1 = UIColor(red: 255/255, green: 202/255, blue: 40/255, alpha: 1) 331 | public static let base = UIColor(red: 255/255, green: 193/255, blue: 7/255, alpha: 1) 332 | public static let darken1 = UIColor(red: 255/255, green: 179/255, blue: 0/255, alpha: 1) 333 | public static let darken2 = UIColor(red: 255/255, green: 160/255, blue: 0/255, alpha: 1) 334 | public static let darken3 = UIColor(red: 255/255, green: 143/255, blue: 0/255, alpha: 1) 335 | public static let darken4 = UIColor(red: 255/255, green: 111/255, blue: 0/255, alpha: 1) 336 | public static let accent1 = UIColor(red: 255/255, green: 229/255, blue: 127/255, alpha: 1) 337 | public static let accent2 = UIColor(red: 255/255, green: 215/255, blue: 64/255, alpha: 1) 338 | public static let accent3 = UIColor(red: 255/255, green: 196/255, blue: 0/255, alpha: 1) 339 | public static let accent4 = UIColor(red: 255/255, green: 171/255, blue: 0/255, alpha: 1) 340 | } 341 | 342 | // orange 343 | open class orange: ColorPalette { 344 | public static let lighten5 = UIColor(red: 255/255, green: 243/255, blue: 224/255, alpha: 1) 345 | public static let lighten4 = UIColor(red: 255/255, green: 224/255, blue: 178/255, alpha: 1) 346 | public static let lighten3 = UIColor(red: 255/255, green: 204/255, blue: 128/255, alpha: 1) 347 | public static let lighten2 = UIColor(red: 255/255, green: 183/255, blue: 77/255, alpha: 1) 348 | public static let lighten1 = UIColor(red: 255/255, green: 167/255, blue: 38/255, alpha: 1) 349 | public static let base = UIColor(red: 255/255, green: 152/255, blue: 0/255, alpha: 1) 350 | public static let darken1 = UIColor(red: 251/255, green: 140/255, blue: 0/255, alpha: 1) 351 | public static let darken2 = UIColor(red: 245/255, green: 124/255, blue: 0/255, alpha: 1) 352 | public static let darken3 = UIColor(red: 239/255, green: 108/255, blue: 0/255, alpha: 1) 353 | public static let darken4 = UIColor(red: 230/255, green: 81/255, blue: 0/255, alpha: 1) 354 | public static let accent1 = UIColor(red: 255/255, green: 209/255, blue: 128/255, alpha: 1) 355 | public static let accent2 = UIColor(red: 255/255, green: 171/255, blue: 64/255, alpha: 1) 356 | public static let accent3 = UIColor(red: 255/255, green: 145/255, blue: 0/255, alpha: 1) 357 | public static let accent4 = UIColor(red: 255/255, green: 109/255, blue: 0/255, alpha: 1) 358 | } 359 | 360 | 361 | // deep orange 362 | open class deepOrange: ColorPalette { 363 | public static let lighten5 = UIColor(red: 251/255, green: 233/255, blue: 231/255, alpha: 1) 364 | public static let lighten4 = UIColor(red: 255/255, green: 204/255, blue: 188/255, alpha: 1) 365 | public static let lighten3 = UIColor(red: 255/255, green: 171/255, blue: 145/255, alpha: 1) 366 | public static let lighten2 = UIColor(red: 255/255, green: 138/255, blue: 101/255, alpha: 1) 367 | public static let lighten1 = UIColor(red: 255/255, green: 112/255, blue: 67/255, alpha: 1) 368 | public static let base = UIColor(red: 255/255, green: 87/255, blue: 34/255, alpha: 1) 369 | public static let darken1 = UIColor(red: 244/255, green: 81/255, blue: 30/255, alpha: 1) 370 | public static let darken2 = UIColor(red: 230/255, green: 74/255, blue: 25/255, alpha: 1) 371 | public static let darken3 = UIColor(red: 216/255, green: 67/255, blue: 21/255, alpha: 1) 372 | public static let darken4 = UIColor(red: 191/255, green: 54/255, blue: 12/255, alpha: 1) 373 | public static let accent1 = UIColor(red: 255/255, green: 158/255, blue: 128/255, alpha: 1) 374 | public static let accent2 = UIColor(red: 255/255, green: 110/255, blue: 64/255, alpha: 1) 375 | public static let accent3 = UIColor(red: 255/255, green: 61/255, blue: 0/255, alpha: 1) 376 | public static let accent4 = UIColor(red: 221/255, green: 44/255, blue: 0/255, alpha: 1) 377 | } 378 | 379 | 380 | // brown 381 | open class brown: ColorPalette { 382 | public static let lighten5 = UIColor(red: 239/255, green: 235/255, blue: 233/255, alpha: 1) 383 | public static let lighten4 = UIColor(red: 215/255, green: 204/255, blue: 200/255, alpha: 1) 384 | public static let lighten3 = UIColor(red: 188/255, green: 170/255, blue: 164/255, alpha: 1) 385 | public static let lighten2 = UIColor(red: 161/255, green: 136/255, blue: 127/255, alpha: 1) 386 | public static let lighten1 = UIColor(red: 141/255, green: 110/255, blue: 99/255, alpha: 1) 387 | public static let base = UIColor(red: 121/255, green: 85/255, blue: 72/255, alpha: 1) 388 | public static let darken1 = UIColor(red: 109/255, green: 76/255, blue: 65/255, alpha: 1) 389 | public static let darken2 = UIColor(red: 93/255, green: 64/255, blue: 55/255, alpha: 1) 390 | public static let darken3 = UIColor(red: 78/255, green: 52/255, blue: 46/255, alpha: 1) 391 | public static let darken4 = UIColor(red: 62/255, green: 39/255, blue: 35/255, alpha: 1) 392 | } 393 | 394 | // grey 395 | open class grey: ColorPalette { 396 | public static let lighten5 = UIColor(red: 250/255, green: 250/255, blue: 250/255, alpha: 1) 397 | public static let lighten4 = UIColor(red: 245/255, green: 245/255, blue: 245/255, alpha: 1) 398 | public static let lighten3 = UIColor(red: 238/255, green: 238/255, blue: 238/255, alpha: 1) 399 | public static let lighten2 = UIColor(red: 224/255, green: 224/255, blue: 224/255, alpha: 1) 400 | public static let lighten1 = UIColor(red: 189/255, green: 189/255, blue: 189/255, alpha: 1) 401 | public static let base = UIColor(red: 158/255, green: 158/255, blue: 158/255, alpha: 1) 402 | public static let darken1 = UIColor(red: 117/255, green: 117/255, blue: 117/255, alpha: 1) 403 | public static let darken2 = UIColor(red: 97/255, green: 97/255, blue: 97/255, alpha: 1) 404 | public static let darken3 = UIColor(red: 66/255, green: 66/255, blue: 66/255, alpha: 1) 405 | public static let darken4 = UIColor(red: 33/255, green: 33/255, blue: 33/255, alpha: 1) 406 | } 407 | 408 | // blue grey 409 | open class blueGrey: ColorPalette { 410 | public static let lighten5 = UIColor(red: 236/255, green: 239/255, blue: 241/255, alpha: 1) 411 | public static let lighten4 = UIColor(red: 207/255, green: 216/255, blue: 220/255, alpha: 1) 412 | public static let lighten3 = UIColor(red: 176/255, green: 190/255, blue: 197/255, alpha: 1) 413 | public static let lighten2 = UIColor(red: 144/255, green: 164/255, blue: 174/255, alpha: 1) 414 | public static let lighten1 = UIColor(red: 120/255, green: 144/255, blue: 156/255, alpha: 1) 415 | public static let base = UIColor(red: 96/255, green: 125/255, blue: 139/255, alpha: 1) 416 | public static let darken1 = UIColor(red: 84/255, green: 110/255, blue: 122/255, alpha: 1) 417 | public static let darken2 = UIColor(red: 69/255, green: 90/255, blue: 100/255, alpha: 1) 418 | public static let darken3 = UIColor(red: 55/255, green: 71/255, blue: 79/255, alpha: 1) 419 | public static let darken4 = UIColor(red: 38/255, green: 50/255, blue: 56/255, alpha: 1) 420 | } 421 | } 422 | -------------------------------------------------------------------------------- /gif3d/extensions/FileManager+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // gif3d 4 | // 5 | // Created by Sam on 7/9/18. 6 | // Copyright © 2018 chuongtran. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension FileManager { 12 | func homeDirectory() -> URL { 13 | return URL(fileURLWithPath: NSHomeDirectory()) 14 | } 15 | 16 | func homeDirectoryPath() -> String { 17 | return NSHomeDirectory() 18 | } 19 | 20 | func tmpDirectory() -> URL { 21 | return homeDirectory().appendingPathComponent("tmp") 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /gif3d/extensions/UIImage+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+Extensions.swift 3 | // gif3d 4 | // 5 | // Created by Sam on 7/11/18. 6 | // Copyright © 2018 chuongtran. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import ImageIO 12 | import MobileCoreServices 13 | 14 | extension UIImage { 15 | 16 | func overlayWith(image: UIImage, posX: CGFloat, posY: CGFloat) -> UIImage { 17 | let newWidth = size.width < posX + image.size.width ? posX + image.size.width : size.width 18 | let newHeight = size.height < posY + image.size.height ? posY + image.size.height : size.height 19 | let newSize = CGSize(width: newWidth, height: newHeight) 20 | 21 | UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0) 22 | draw(in: CGRect(origin: CGPoint.zero, size: size)) 23 | image.draw(in: CGRect(origin: CGPoint(x: posX, y: posY), size: image.size)) 24 | let newImage = UIGraphicsGetImageFromCurrentImageContext()! 25 | UIGraphicsEndImageContext() 26 | 27 | return newImage 28 | } 29 | static func animatedGif(from images: [UIImage]) -> URL? { 30 | let fileProperties: CFDictionary = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFLoopCount as String: 0]] as CFDictionary 31 | let frameProperties: CFDictionary = [kCGImagePropertyGIFDictionary as String: [(kCGImagePropertyGIFDelayTime as String): 1.0/8.0]] as CFDictionary 32 | 33 | let documentsDirectoryURL: URL? = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) 34 | let fileURL: URL? = documentsDirectoryURL?.appendingPathComponent("animated.gif") 35 | 36 | if let url = fileURL as CFURL? { 37 | if let destination = CGImageDestinationCreateWithURL(url, kUTTypeGIF, images.count, nil) { 38 | CGImageDestinationSetProperties(destination, fileProperties) 39 | for image in images { 40 | if let cgImage = image.cgImage { 41 | CGImageDestinationAddImage(destination, cgImage, frameProperties) 42 | } 43 | } 44 | if !CGImageDestinationFinalize(destination) { 45 | print("Failed to finalize the image destination") 46 | } 47 | } 48 | } 49 | return fileURL 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /gif3d/extensions/UIView+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // gif3d 4 | // 5 | // Created by Sam on 7/9/18. 6 | // Copyright © 2018 chuongtran. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | import UIKit 12 | 13 | extension UIView { 14 | 15 | func createImage() -> UIImage? { 16 | 17 | let rect: CGRect = self.frame 18 | 19 | UIGraphicsBeginImageContext(rect.size) 20 | let context: CGContext = UIGraphicsGetCurrentContext()! 21 | self.layer.render(in: context) 22 | let img:UIImage = UIGraphicsGetImageFromCurrentImageContext()! 23 | UIGraphicsEndImageContext() 24 | return img 25 | 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /gif3d/extensions/UIViewController+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+Extensions.swift 3 | // 4 | // Created by Sam on 10/13/17. 5 | // Copyright © 2017 Wooft. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | import MBProgressHUD 11 | 12 | 13 | extension UIViewController { 14 | 15 | func showAlert(title: String, message: String) { 16 | let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) 17 | let Cancel = UIAlertAction(title: "OK", style: .cancel) { _ in 18 | 19 | } 20 | alert.addAction(Cancel) 21 | self.present(alert, animated: true) { } 22 | } 23 | 24 | func showAlert(title: String, message: String, arrButtons: [String], titleDestructive: String?, completion: @escaping(_ index: Int) -> ()) { 25 | let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) 26 | 27 | if titleDestructive != nil { 28 | let destructive = UIAlertAction(title: titleDestructive, style: .destructive) { _ in 29 | completion(0) 30 | } 31 | alert.addAction(destructive) 32 | } 33 | 34 | 35 | for str in arrButtons { 36 | let action = UIAlertAction(title: str, style: .default, handler: { (action) in 37 | completion(arrButtons.index(of: action.title!)! + 1) 38 | }) 39 | alert.addAction(action) 40 | } 41 | 42 | self.present(alert, animated: true) { } 43 | } 44 | 45 | // func showToast(message: String) { 46 | // Utility.showToast(message: message) 47 | // } 48 | 49 | func showHUD() { 50 | let loadingNotification = MBProgressHUD.showAdded(to: self.view, animated: true) 51 | loadingNotification.mode = MBProgressHUDMode.indeterminate 52 | } 53 | func showHUDWith(text:String) { 54 | let loadingNotification = MBProgressHUD.showAdded(to: self.view, animated: true) 55 | loadingNotification.mode = MBProgressHUDMode.indeterminate 56 | loadingNotification.label.text = text 57 | } 58 | func dismissHUD() { 59 | MBProgressHUD.hide(for: self.view, animated: true) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /gif3d/extensions/UIViewExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewExtensions.swift 3 | // SwifterSwift 4 | // 5 | // Created by Omar Albeik on 8/5/16. 6 | // Copyright © 2016 Omar Albeik. All rights reserved. 7 | // 8 | 9 | #if os(iOS) || os(tvOS) 10 | import UIKit 11 | 12 | 13 | // MARK: - enums 14 | 15 | /// SwifterSwift: Shake directions of a view. 16 | /// 17 | /// - horizontal: Shake left and right. 18 | /// - vertical: Shake up and down. 19 | public enum ShakeDirection { 20 | case horizontal 21 | case vertical 22 | } 23 | 24 | /// SwifterSwift: Angle units. 25 | /// 26 | /// - degrees: degrees. 27 | /// - radians: radians. 28 | public enum AngleUnit { 29 | case degrees 30 | case radians 31 | } 32 | 33 | /// SwifterSwift: Shake animations types. 34 | /// 35 | /// - linear: linear animation. 36 | /// - easeIn: easeIn animation 37 | /// - easeOut: easeOut animation. 38 | /// - easeInOut: easeInOut animation. 39 | public enum ShakeAnimationType { 40 | case linear 41 | case easeIn 42 | case easeOut 43 | case easeInOut 44 | } 45 | 46 | 47 | // MARK: - Properties 48 | public extension UIView { 49 | /* 50 | @IBInspectable 51 | /// SwifterSwift: Border color of view; also inspectable from Storyboard. 52 | public var borderColor: UIColor? { 53 | get { 54 | guard let color = layer.borderColor else { 55 | return nil 56 | } 57 | return UIColor(cgColor: color) 58 | } 59 | set { 60 | guard let color = newValue else { 61 | layer.borderColor = nil 62 | return 63 | } 64 | layer.borderColor = color.cgColor 65 | } 66 | } 67 | 68 | @IBInspectable 69 | /// SwifterSwift: Border width of view; also inspectable from Storyboard. 70 | public var borderWidth: CGFloat { 71 | get { 72 | return layer.borderWidth 73 | } 74 | set { 75 | layer.borderWidth = newValue 76 | } 77 | } 78 | 79 | @IBInspectable 80 | /// SwifterSwift: Corner radius of view; also inspectable from Storyboard. 81 | public var cornerRadius: CGFloat { 82 | get { 83 | return layer.cornerRadius 84 | } 85 | set { 86 | layer.masksToBounds = true 87 | layer.cornerRadius = abs(CGFloat(Int(newValue * 100)) / 100) 88 | } 89 | } 90 | */ 91 | 92 | // SwifterSwift: Height of view. 93 | public var height: CGFloat { 94 | get { 95 | return frame.size.height 96 | } 97 | set { 98 | frame.size.height = newValue 99 | } 100 | } 101 | 102 | /// SwifterSwift: Check if view is in RTL format. 103 | public var isRightToLeft: Bool { 104 | if #available(iOS 10.0, *, tvOS 10.0, *) { 105 | return effectiveUserInterfaceLayoutDirection == .rightToLeft 106 | } else { 107 | return false 108 | } 109 | } 110 | 111 | /// SwifterSwift: Take screenshot of view (if applicable). 112 | public var screenshot: UIImage? { 113 | UIGraphicsBeginImageContextWithOptions(layer.frame.size, false, 0.0); 114 | defer { 115 | UIGraphicsEndImageContext() 116 | } 117 | guard let context = UIGraphicsGetCurrentContext() else { 118 | return nil 119 | } 120 | layer.render(in: context) 121 | return UIGraphicsGetImageFromCurrentImageContext() 122 | } 123 | 124 | /* 125 | @IBInspectable 126 | /// SwifterSwift: Shadow color of view; also inspectable from Storyboard. 127 | public var shadowColor: UIColor? { 128 | get { 129 | guard let color = layer.shadowColor else { 130 | return nil 131 | } 132 | return UIColor(cgColor: color) 133 | } 134 | set { 135 | layer.shadowColor = newValue?.cgColor 136 | } 137 | } 138 | 139 | @IBInspectable 140 | /// SwifterSwift: Shadow offset of view; also inspectable from Storyboard. 141 | public var shadowOffset: CGSize { 142 | get { 143 | return layer.shadowOffset 144 | } 145 | set { 146 | layer.shadowOffset = newValue 147 | } 148 | } 149 | 150 | @IBInspectable 151 | /// SwifterSwift: Shadow opacity of view; also inspectable from Storyboard. 152 | public var shadowOpacity: Float { 153 | get { 154 | return layer.shadowOpacity 155 | } 156 | set { 157 | layer.shadowOpacity = newValue 158 | } 159 | } 160 | 161 | @IBInspectable 162 | /// SwifterSwift: Shadow radius of view; also inspectable from Storyboard. 163 | public var shadowRadius: CGFloat { 164 | get { 165 | return layer.shadowRadius 166 | } 167 | set { 168 | layer.shadowRadius = newValue 169 | } 170 | } 171 | */ 172 | /// SwifterSwift: Size of view. 173 | public var size: CGSize { 174 | get { 175 | return frame.size 176 | } 177 | set { 178 | width = newValue.width 179 | height = newValue.height 180 | } 181 | } 182 | 183 | /// SwifterSwift: Width of view. 184 | public var width: CGFloat { 185 | get { 186 | return frame.size.width 187 | } 188 | set { 189 | frame.size.width = newValue 190 | } 191 | } 192 | 193 | /// SwifterSwift: x origin of view. 194 | public var x: CGFloat { 195 | get { 196 | return frame.origin.x 197 | } 198 | set { 199 | frame.origin.x = newValue 200 | } 201 | } 202 | 203 | /// SwifterSwift: y origin of view. 204 | public var y: CGFloat { 205 | get { 206 | return frame.origin.y 207 | } 208 | set { 209 | frame.origin.y = newValue 210 | } 211 | } 212 | 213 | /// SwifterSwift: Get view's parent view controller 214 | public var parentViewController: UIViewController? { 215 | weak var parentResponder: UIResponder? = self 216 | while parentResponder != nil { 217 | parentResponder = parentResponder!.next 218 | if let viewController = parentResponder as? UIViewController { 219 | return viewController 220 | } 221 | } 222 | return nil 223 | } 224 | 225 | /// SwifterSwift: First responder. 226 | public var firstResponder: UIView? { 227 | guard !isFirstResponder else { 228 | return self 229 | } 230 | for subView in subviews { 231 | if subView.isFirstResponder { 232 | return subView 233 | } 234 | } 235 | return nil 236 | } 237 | } 238 | 239 | 240 | // MARK: - Methods 241 | public extension UIView { 242 | 243 | /// SwifterSwift: Set some or all corners radiuses of view. 244 | /// 245 | /// - Parameters: 246 | /// - corners: array of corners to change (example: [.bottomLeft, .topRight]). 247 | /// - radius: radius for selected corners. 248 | public func roundCorners(_ corners: UIRectCorner, radius: CGFloat) { 249 | let maskPath = UIBezierPath(roundedRect: bounds, 250 | byRoundingCorners: corners, 251 | cornerRadii: CGSize(width: radius, height: radius)) 252 | let shape = CAShapeLayer() 253 | shape.path = maskPath.cgPath 254 | layer.mask = shape 255 | } 256 | 257 | /// SwifterSwift: Add shadow to view. 258 | /// 259 | /// - Parameters: 260 | /// - color: shadow color (default is #137992). 261 | /// - radius: shadow radius (default is 3). 262 | /// - offset: shadow offset (default is .zero). 263 | /// - opacity: shadow opacity (default is 0.5). 264 | public func addShadow(ofColor color: UIColor = UIColor(hex: 0x137992)!, 265 | radius: CGFloat = 3, 266 | offset: CGSize = .zero, 267 | opacity: Float = 0.5) { 268 | layer.shadowColor = color.cgColor 269 | layer.shadowOffset = offset 270 | layer.shadowRadius = radius 271 | layer.shadowOpacity = opacity 272 | layer.masksToBounds = true 273 | } 274 | 275 | /// SwifterSwift: Add array of subviews to view. 276 | /// 277 | /// - Parameter subviews: array of subviews to add to self. 278 | public func addSubviews(_ subviews: [UIView]) { 279 | subviews.forEach({self.addSubview($0)}) 280 | } 281 | 282 | /// SwifterSwift: Fade in view. 283 | /// 284 | /// - Parameters: 285 | /// - duration: animation duration in seconds (default is 1 second). 286 | /// - completion: optional completion handler to run with animation finishes (default is nil) 287 | public func fadeIn(duration: TimeInterval = 1, completion: ((Bool) -> Void)? = nil) { 288 | if isHidden { 289 | isHidden = false 290 | } 291 | UIView.animate(withDuration: duration, animations: { 292 | self.alpha = 1 293 | }, completion: completion) 294 | } 295 | 296 | /// SwifterSwift: Fade out view. 297 | /// 298 | /// - Parameters: 299 | /// - duration: animation duration in seconds (default is 1 second). 300 | /// - completion: optional completion handler to run with animation finishes (default is nil) 301 | public func fadeOut(duration: TimeInterval = 1, completion: ((Bool) -> Void)? = nil) { 302 | if isHidden { 303 | isHidden = false 304 | } 305 | UIView.animate(withDuration: duration, animations: { 306 | self.alpha = 0 307 | }, completion: completion) 308 | } 309 | 310 | /// SwifterSwift: Load view from nib. 311 | /// 312 | /// - Parameters: 313 | /// - name: nib name. 314 | /// - bundle: bundle of nib (default is nil). 315 | /// - Returns: optional UIView (if applicable). 316 | public class func loadFromNib(named name: String, bundle : Bundle? = nil) -> UIView? { 317 | return UINib(nibName: name, bundle: bundle).instantiate(withOwner: nil, options: nil)[0] as? UIView 318 | } 319 | 320 | /// SwifterSwift: Remove all subviews in view. 321 | public func removeSubviews() { 322 | subviews.forEach({$0.removeFromSuperview()}) 323 | } 324 | 325 | /// SwifterSwift: Remove all gesture recognizers from view. 326 | public func removeGestureRecognizers() { 327 | gestureRecognizers?.forEach(removeGestureRecognizer) 328 | } 329 | 330 | /// SwifterSwift: Rotate view by angle on relative axis. 331 | /// 332 | /// - Parameters: 333 | /// - angle: angle to rotate view by. 334 | /// - type: type of the rotation angle. 335 | /// - animated: set true to animate rotation (default is true). 336 | /// - duration: animation duration in seconds (default is 1 second). 337 | /// - completion: optional completion handler to run with animation finishes (default is nil). 338 | public func rotate(byAngle angle : CGFloat, ofType type: AngleUnit, animated: Bool = false, duration: TimeInterval = 1, completion:((Bool) -> Void)? = nil) { 339 | let angleWithType = (type == .degrees) ? CGFloat.pi * angle / 180.0 : angle 340 | let aDuration = animated ? duration : 0 341 | UIView.animate(withDuration: aDuration, delay: 0, options: .curveLinear, animations: { () -> Void in 342 | self.transform = self.transform.rotated(by: angleWithType) 343 | }, completion: completion) 344 | } 345 | 346 | /// SwifterSwift: Rotate view to angle on fixed axis. 347 | /// 348 | /// - Parameters: 349 | /// - angle: angle to rotate view to. 350 | /// - type: type of the rotation angle. 351 | /// - animated: set true to animate rotation (default is false). 352 | /// - duration: animation duration in seconds (default is 1 second). 353 | /// - completion: optional completion handler to run with animation finishes (default is nil). 354 | public func rotate(toAngle angle: CGFloat, ofType type: AngleUnit, animated: Bool = false, duration: TimeInterval = 1, completion:((Bool) -> Void)? = nil) { 355 | let angleWithType = (type == .degrees) ? CGFloat.pi * angle / 180.0 : angle 356 | let aDuration = animated ? duration : 0 357 | UIView.animate(withDuration: aDuration, animations: { 358 | self.transform = self.transform.concatenating(CGAffineTransform(rotationAngle: angleWithType)) 359 | }, completion: completion) 360 | } 361 | 362 | /// SwifterSwift: Scale view by offset. 363 | /// 364 | /// - Parameters: 365 | /// - offset: scale offset 366 | /// - animated: set true to animate scaling (default is false). 367 | /// - duration: animation duration in seconds (default is 1 second). 368 | /// - completion: optional completion handler to run with animation finishes (default is nil). 369 | public func scale(by offset: CGPoint, animated: Bool = false, duration: TimeInterval = 1, completion:((Bool) -> Void)? = nil) { 370 | if animated { 371 | UIView.animate(withDuration: duration, delay: 0, options: .curveLinear, animations: { () -> Void in 372 | self.transform = self.transform.scaledBy(x: offset.x, y: offset.y) 373 | }, completion: completion) 374 | } else { 375 | transform = transform.scaledBy(x: offset.x, y: offset.y) 376 | completion?(true) 377 | } 378 | } 379 | 380 | /// SwifterSwift: Shake view. 381 | /// 382 | /// - Parameters: 383 | /// - direction: shake direction (horizontal or vertical), (default is .horizontal) 384 | /// - duration: animation duration in seconds (default is 1 second). 385 | /// - animationType: shake animation type (default is .easeOut). 386 | /// - completion: optional completion handler to run with animation finishes (default is nil). 387 | public func shake(direction: ShakeDirection = .horizontal, duration: TimeInterval = 1, animationType: ShakeAnimationType = .easeOut, completion:(() -> Void)? = nil) { 388 | 389 | CATransaction.begin() 390 | let animation: CAKeyframeAnimation 391 | switch direction { 392 | case .horizontal: 393 | animation = CAKeyframeAnimation(keyPath: "transform.translation.x") 394 | case .vertical: 395 | animation = CAKeyframeAnimation(keyPath: "transform.translation.y") 396 | } 397 | switch animationType { 398 | case .linear: 399 | animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear) 400 | case .easeIn: 401 | animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn) 402 | case .easeOut: 403 | animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut) 404 | case .easeInOut: 405 | animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) 406 | } 407 | CATransaction.setCompletionBlock(completion) 408 | animation.duration = duration 409 | animation.values = [-20.0, 20.0, -20.0, 20.0, -10.0, 10.0, -5.0, 5.0, 0.0 ] 410 | layer.add(animation, forKey: "shake") 411 | CATransaction.commit() 412 | } 413 | 414 | /// SwifterSwift: Add Visual Format constraints. 415 | /// 416 | /// - Parameters: 417 | /// - withFormat: visual Format language 418 | /// - views: array of views which will be accessed starting with index 0 (example: [v0], [v1], [v2]..) 419 | @available(iOS 9, *) public func addConstraints(withFormat: String, views: UIView...) { 420 | // https://videos.letsbuildthatapp.com/ 421 | var viewsDictionary: [String: UIView] = [:] 422 | for (index, view) in views.enumerated() { 423 | let key = "v\(index)" 424 | view.translatesAutoresizingMaskIntoConstraints = false 425 | viewsDictionary[key] = view 426 | } 427 | addConstraints(NSLayoutConstraint.constraints(withVisualFormat: withFormat, options: NSLayoutFormatOptions(), metrics: nil, views: viewsDictionary)) 428 | } 429 | 430 | /// SwifterSwift: Anchor all sides of the view into it's superview. 431 | @available(iOS 9, *) public func fillToSuperview() { 432 | // https://videos.letsbuildthatapp.com/ 433 | translatesAutoresizingMaskIntoConstraints = false 434 | if let superview = superview { 435 | leftAnchor.constraint(equalTo: superview.leftAnchor).isActive = true 436 | rightAnchor.constraint(equalTo: superview.rightAnchor).isActive = true 437 | topAnchor.constraint(equalTo: superview.topAnchor).isActive = true 438 | bottomAnchor.constraint(equalTo: superview.bottomAnchor).isActive = true 439 | } 440 | } 441 | 442 | /// SwifterSwift: Add anchors from any side of the current view into the specified anchors and returns the newly added constraints. 443 | /// 444 | /// - Parameters: 445 | /// - top: current view's top anchor will be anchored into the specified anchor 446 | /// - left: current view's left anchor will be anchored into the specified anchor 447 | /// - bottom: current view's bottom anchor will be anchored into the specified anchor 448 | /// - right: current view's right anchor will be anchored into the specified anchor 449 | /// - topConstant: current view's top anchor margin 450 | /// - leftConstant: current view's left anchor margin 451 | /// - bottomConstant: current view's bottom anchor margin 452 | /// - rightConstant: current view's right anchor margin 453 | /// - widthConstant: current view's width 454 | /// - heightConstant: current view's height 455 | /// - Returns: array of newly added constraints (if applicable). 456 | @available(iOS 9, *) @discardableResult public func anchor( 457 | top: NSLayoutYAxisAnchor? = nil, 458 | left: NSLayoutXAxisAnchor? = nil, 459 | bottom: NSLayoutYAxisAnchor? = nil, 460 | right: NSLayoutXAxisAnchor? = nil, 461 | topConstant: CGFloat = 0, 462 | leftConstant: CGFloat = 0, 463 | bottomConstant: CGFloat = 0, 464 | rightConstant: CGFloat = 0, 465 | widthConstant: CGFloat = 0, 466 | heightConstant: CGFloat = 0) -> [NSLayoutConstraint] { 467 | // https://videos.letsbuildthatapp.com/ 468 | translatesAutoresizingMaskIntoConstraints = false 469 | 470 | var anchors = [NSLayoutConstraint]() 471 | 472 | if let top = top { 473 | anchors.append(topAnchor.constraint(equalTo: top, constant: topConstant)) 474 | } 475 | 476 | if let left = left { 477 | anchors.append(leftAnchor.constraint(equalTo: left, constant: leftConstant)) 478 | } 479 | 480 | if let bottom = bottom { 481 | anchors.append(bottomAnchor.constraint(equalTo: bottom, constant: -bottomConstant)) 482 | } 483 | 484 | if let right = right { 485 | anchors.append(rightAnchor.constraint(equalTo: right, constant: -rightConstant)) 486 | } 487 | 488 | if widthConstant > 0 { 489 | anchors.append(widthAnchor.constraint(equalToConstant: widthConstant)) 490 | } 491 | 492 | if heightConstant > 0 { 493 | anchors.append(heightAnchor.constraint(equalToConstant: heightConstant)) 494 | } 495 | 496 | anchors.forEach({$0.isActive = true}) 497 | 498 | return anchors 499 | } 500 | 501 | /// SwifterSwift: Anchor center X into current view's superview with a constant margin value. 502 | /// 503 | /// - Parameter constant: constant of the anchor constraint (default is 0). 504 | @available(iOS 9, *) public func anchorCenterXToSuperview(constant: CGFloat = 0) { 505 | // https://videos.letsbuildthatapp.com/ 506 | translatesAutoresizingMaskIntoConstraints = false 507 | if let anchor = superview?.centerXAnchor { 508 | centerXAnchor.constraint(equalTo: anchor, constant: constant).isActive = true 509 | } 510 | } 511 | 512 | /// SwifterSwift: Anchor center Y into current view's superview with a constant margin value. 513 | /// 514 | /// - Parameter withConstant: constant of the anchor constraint (default is 0). 515 | @available(iOS 9, *) public func anchorCenterYToSuperview(constant: CGFloat = 0) { 516 | // https://videos.letsbuildthatapp.com/ 517 | translatesAutoresizingMaskIntoConstraints = false 518 | if let anchor = superview?.centerYAnchor { 519 | centerYAnchor.constraint(equalTo: anchor, constant: constant).isActive = true 520 | } 521 | } 522 | 523 | /// SwifterSwift: Anchor center X and Y into current view's superview 524 | @available(iOS 9, *) public func anchorCenterSuperview() { 525 | // https://videos.letsbuildthatapp.com/ 526 | anchorCenterXToSuperview() 527 | anchorCenterYToSuperview() 528 | } 529 | 530 | } 531 | #endif 532 | -------------------------------------------------------------------------------- /gif3d/managers/EditorManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EditorManager.swift 3 | // gif3d 4 | // 5 | // Created by Sam on 7/11/18. 6 | // Copyright © 2018 chuongtran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class EditorManager { 12 | // Can't init is singleton 13 | private init() { 14 | 15 | } 16 | 17 | //MARK: Shared Instance 18 | 19 | static let shared:EditorManager = EditorManager() 20 | 21 | //MARK: Local Variable 22 | var editors:[Editor] = [] 23 | 24 | func setupEditor(images:[UIImage]){ 25 | editors.removeAll() 26 | images.forEach { (image) in 27 | editors.append(Editor(image: image)) 28 | } 29 | } 30 | 31 | func cleanData(){ 32 | editors.forEach { (editor) in 33 | editor.image = nil 34 | editor.tools?.removeAll() 35 | } 36 | editors.removeAll() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /gif3d/model/Editor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Editors.swift 3 | // gif3d 4 | // 5 | // Created by Sam on 7/11/18. 6 | // Copyright © 2018 chuongtran. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | struct Path { 13 | let fromPoint: CGPoint 14 | let toPoint: CGPoint 15 | } 16 | 17 | struct Tool { 18 | var path:Path 19 | var radius:CGFloat 20 | } 21 | 22 | class Editor { 23 | var image:UIImage? 24 | var tools:[Tool]? 25 | 26 | init() { 27 | 28 | } 29 | 30 | init(image:UIImage, tools:[Tool] = []) { 31 | self.image = image 32 | self.tools = tools 33 | } 34 | 35 | func isEdited() -> Bool { 36 | return (self.tools?.count)! > 0 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /gif3d/resources/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 | } -------------------------------------------------------------------------------- /gif3d/resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /gif3d/resources/Assets.xcassets/arrowDown.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "arrowDown.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /gif3d/resources/Assets.xcassets/arrowDown.imageset/arrowDown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chuongtrh/gif3d/8829c0e837c87565f974e3f655f441e47cf963e1/gif3d/resources/Assets.xcassets/arrowDown.imageset/arrowDown.png -------------------------------------------------------------------------------- /gif3d/resources/Assets.xcassets/arrowUp.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "arrowUp.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /gif3d/resources/Assets.xcassets/arrowUp.imageset/arrowUp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chuongtrh/gif3d/8829c0e837c87565f974e3f655f441e47cf963e1/gif3d/resources/Assets.xcassets/arrowUp.imageset/arrowUp.png -------------------------------------------------------------------------------- /gif3d/resources/Assets.xcassets/back.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "back.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /gif3d/resources/Assets.xcassets/back.imageset/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chuongtrh/gif3d/8829c0e837c87565f974e3f655f441e47cf963e1/gif3d/resources/Assets.xcassets/back.imageset/back.png -------------------------------------------------------------------------------- /gif3d/resources/Assets.xcassets/next.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "next1.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /gif3d/resources/Assets.xcassets/next.imageset/next1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chuongtrh/gif3d/8829c0e837c87565f974e3f655f441e47cf963e1/gif3d/resources/Assets.xcassets/next.imageset/next1.png -------------------------------------------------------------------------------- /gif3d/resources/Assets.xcassets/share.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "share.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /gif3d/resources/Assets.xcassets/share.imageset/share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chuongtrh/gif3d/8829c0e837c87565f974e3f655f441e47cf963e1/gif3d/resources/Assets.xcassets/share.imageset/share.png -------------------------------------------------------------------------------- /gif3d/utility/Contain.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Contain.swift 3 | // gif3d 4 | // 5 | // Created by Sam on 7/13/18. 6 | // Copyright © 2018 chuongtran. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | let APP_NAME:String = "gif3d" 13 | 14 | let SCREEN_WIDTH : CGFloat = UIScreen.main.bounds.size.width 15 | let SCREEN_HEIGHT : CGFloat = UIScreen.main.bounds.size.height 16 | 17 | //MARK: - Font 18 | 19 | func font_regular(size:CGFloat)->UIFont{ 20 | return UIFont(name: "Helvetica", size: size)! 21 | } 22 | func font_medium(size:CGFloat)->UIFont{ 23 | return UIFont(name: "Helvetica-Medium", size: size)! 24 | } 25 | func font_light(size:CGFloat)->UIFont{ 26 | return UIFont(name: "Helvetica-Light", size: size)! 27 | } 28 | func font_semibold(size:CGFloat)->UIFont{ 29 | return UIFont(name: "Helvetica-Semibold", size: size)! 30 | } 31 | 32 | func font_bold(size:CGFloat)->UIFont{ 33 | return UIFont(name: "Helvetica-Bold", size: size)! 34 | } 35 | -------------------------------------------------------------------------------- /gif3d/viewcontrollers/EditorVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EditorVC.swift 3 | // gif3d 4 | // 5 | // Created by Sam on 7/10/18. 6 | // Copyright © 2018 chuongtran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class EditorVC: UIViewController { 12 | 13 | @IBOutlet weak var editorFrameView:EditorFrameView! 14 | @IBOutlet weak var collectionView:UICollectionView! 15 | @IBOutlet weak var colorCollectionView:UICollectionView! 16 | @IBOutlet weak var ivBackground:UIImageView! 17 | @IBOutlet weak var btnTool: UIButton! 18 | @IBOutlet weak var radiusSlider: UISlider! 19 | @IBOutlet weak var toolView: UIView! 20 | @IBOutlet weak var radiusView: UIView! 21 | 22 | 23 | @IBOutlet weak var toolViewHeightConstraint: NSLayoutConstraint! 24 | 25 | var images:[UIImage]! 26 | 27 | private var currentIndexpath:IndexPath! 28 | private var selectBackgroundFrameColor = UIColor.black 29 | 30 | private var activeEditor:Editor! { 31 | didSet { 32 | self.editorFrameView.setupEditor(editor: self.activeEditor) 33 | } 34 | } 35 | 36 | private var colors = [Color.red.base, 37 | Color.pink.base, 38 | Color.purple.base, 39 | Color.deepPurple.base, 40 | Color.indigo.base, 41 | Color.blue.base, 42 | Color.lightBlue.base, 43 | Color.cyan.base, 44 | Color.teal.base, 45 | Color.green.base, 46 | Color.lightGreen.base, 47 | Color.lime.base, 48 | Color.yellow.base, 49 | Color.amber.base, 50 | Color.orange.base, 51 | Color.deepOrange.base, 52 | Color.brown.base, 53 | Color.grey.base, 54 | Color.blueGrey.base, 55 | Color.black, 56 | Color.white] 57 | 58 | var isShowTool = false 59 | 60 | override func viewDidLoad() { 61 | super.viewDidLoad() 62 | 63 | setup() 64 | } 65 | override func viewDidAppear(_ animated: Bool) { 66 | super.viewDidAppear(animated) 67 | } 68 | override func didReceiveMemoryWarning() { 69 | super.didReceiveMemoryWarning() 70 | // Dispose of any resources that can be recreated. 71 | } 72 | 73 | deinit { 74 | images.removeAll() 75 | EditorManager.shared.cleanData() 76 | } 77 | 78 | //MARK: - Helper 79 | func setup() { 80 | editorFrameView.delegate = self 81 | 82 | ivBackground.image = images.first 83 | editorFrameView.ivThumbnail.backgroundColor = selectBackgroundFrameColor 84 | 85 | EditorManager.shared.setupEditor(images: images) 86 | activeEditor = EditorManager.shared.editors.first 87 | 88 | collectionView.backgroundColor = UIColor.clear 89 | colorCollectionView.backgroundColor = UIColor.clear 90 | 91 | toolView.backgroundColor = UIColor.clear 92 | toolView.isUserInteractionEnabled = false 93 | toolView.alpha = 0 94 | //Change radius 95 | editorFrameView.editorView.radius = 16 96 | updateRadiusView(radius: 16) 97 | 98 | 99 | if currentIndexpath == nil { 100 | currentIndexpath = IndexPath(row: 0, section: 0) 101 | } 102 | } 103 | func buildFinalFrameSuccess(images:[UIImage]){ 104 | 105 | 106 | if currentIndexpath != nil { 107 | activeEditor = EditorManager.shared.editors[currentIndexpath.row] 108 | } 109 | 110 | self.performSegue(withIdentifier: "ShowPreviewVCIdentifier", sender: images) 111 | } 112 | func buildFinalFrame() { 113 | var images:[UIImage] = [] 114 | 115 | print("buildFinalFrame") 116 | showHUDWith(text: "processing...") 117 | let nCount = EditorManager.shared.editors.count 118 | let queue = DispatchQueue(label: "com.chuongtran.gif3d.queue.1") 119 | queue.sync { 120 | EditorManager.shared.editors.forEach { (editor) in 121 | 122 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { // 2 123 | self.activeEditor = editor 124 | images.append(self.editorFrameView.buildFrame()) 125 | 126 | if(images.count == nCount) { 127 | self.dismissHUD() 128 | self.buildFinalFrameSuccess(images: images) 129 | } 130 | } 131 | } 132 | } 133 | 134 | } 135 | 136 | func updateRadiusView(radius:CGFloat) { 137 | radiusView.frame = CGRect(x: radiusView.center.x - radius/2, y: radiusView.center.y - radius/2, width: radius, height: radius) 138 | radiusView.layer.cornerRadius = radiusView.width/2 139 | } 140 | //MARK: - OnAction 141 | @IBAction func onBack(){ 142 | ivBackground.image = nil 143 | self.navigationController?.popViewController(animated: true) 144 | } 145 | @IBAction func onNext(){ 146 | buildFinalFrame() 147 | } 148 | 149 | @IBAction func onShowTool(){ 150 | isShowTool = !isShowTool 151 | 152 | UIView.animate(withDuration: 0.3, animations: { 153 | self.toolViewHeightConstraint.constant = self.isShowTool == true ? -150: 10 154 | self.collectionView.alpha = self.isShowTool == true ? 0 : 1 155 | self.toolView.alpha = self.isShowTool == true ? 1 : 0 156 | self.view.layoutIfNeeded() 157 | }) { (complete) in 158 | self.collectionView.isUserInteractionEnabled = self.isShowTool == true ? false : true 159 | self.toolView.isUserInteractionEnabled = self.isShowTool == true ? true : false 160 | self.btnTool.setImage(UIImage(named: self.isShowTool == true ? "arrowDown" : "arrowUp"), for: .normal) 161 | } 162 | } 163 | 164 | @IBAction func radiusOnValueChanged(_ sender: Any) { 165 | let radius = CGFloat(Int(radiusSlider.value)) 166 | editorFrameView.editorView.radius = radius 167 | updateRadiusView(radius: radius) 168 | 169 | } 170 | //MARK: Navigation 171 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 172 | if segue.identifier == "ShowPreviewVCIdentifier" { 173 | if let vc:PreviewVC = segue.destination as? PreviewVC { 174 | vc.images = sender as? [UIImage] 175 | vc.bgImage = self.ivBackground.image 176 | } 177 | } 178 | } 179 | 180 | } 181 | 182 | extension EditorVC: UICollectionViewDelegate, UICollectionViewDataSource { 183 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 184 | if collectionView == self.collectionView { 185 | return images.count 186 | } 187 | return colors.count 188 | } 189 | 190 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 191 | if collectionView == self.collectionView { 192 | let cell:FrameItemCell = collectionView.dequeueReusableCell(withReuseIdentifier: FrameItemCell.identifier(), for: indexPath) as! FrameItemCell 193 | let editor:Editor = EditorManager.shared.editors[indexPath.row] 194 | 195 | cell.updateUI(image: images[indexPath.row], isEdited: editor.isEdited(), color: selectBackgroundFrameColor, index: indexPath.row, isActive:indexPath.row == currentIndexpath.row) 196 | 197 | return cell 198 | }else { 199 | let cell:ColorItemCell = collectionView.dequeueReusableCell(withReuseIdentifier: ColorItemCell.identifier(), for: indexPath) as! ColorItemCell 200 | cell.backgroundColor = colors[indexPath.row] 201 | return cell 202 | } 203 | } 204 | 205 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 206 | if collectionView == self.collectionView { 207 | if currentIndexpath != indexPath { 208 | currentIndexpath = indexPath 209 | activeEditor = EditorManager.shared.editors[indexPath.row] 210 | self.collectionView.reloadData() 211 | 212 | } 213 | }else{ 214 | selectBackgroundFrameColor = colors[indexPath.row] 215 | editorFrameView.ivThumbnail.backgroundColor = selectBackgroundFrameColor 216 | self.collectionView.reloadData() 217 | } 218 | } 219 | 220 | } 221 | 222 | extension EditorVC: EditorFrameViewDelegate { 223 | func editorFrameChange(editorFrameView: EditorFrameView) { 224 | if activeEditor.tools?.count == 1 { 225 | self.collectionView.reloadData() 226 | } 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /gif3d/viewcontrollers/PreviewVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PreviewVC.swift 3 | // gif3d 4 | // 5 | // Created by Sam on 7/11/18. 6 | // Copyright © 2018 chuongtran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class PreviewVC: UIViewController { 12 | 13 | @IBOutlet weak var imageView: UIImageView! 14 | @IBOutlet weak var ivBackground:UIImageView! 15 | 16 | var images:[UIImage]! 17 | var bgImage:UIImage! 18 | 19 | private var fileURL:URL! 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | 24 | setup() 25 | } 26 | override func didReceiveMemoryWarning() { 27 | super.didReceiveMemoryWarning() 28 | // Dispose of any resources that can be recreated. 29 | } 30 | 31 | deinit { 32 | images.removeAll() 33 | imageView.image = nil 34 | } 35 | 36 | //MARK: - Helper 37 | func setup() { 38 | ivBackground.image = bgImage 39 | 40 | imageView.backgroundColor = UIColor.clear 41 | fileURL = UIImage.animatedGif(from: images) 42 | 43 | print("fileURL:\(fileURL)") 44 | 45 | if FileManager.default.fileExists(atPath: (fileURL?.path)!){ 46 | do { 47 | let data = try Data(contentsOf: fileURL!) 48 | let image = UIImage.sd_animatedGIF(with: data) 49 | 50 | imageView.image = image 51 | imageView.startAnimating() 52 | } catch { 53 | 54 | } 55 | } 56 | } 57 | //MARK: - OnAction 58 | @IBAction func onBack(){ 59 | ivBackground.image = nil 60 | self.navigationController?.popViewController(animated: true) 61 | } 62 | @IBAction func onShare(){ 63 | if let fileURL = self.fileURL { 64 | let activityVC = UIActivityViewController( 65 | activityItems: ["#GIF3D", fileURL], 66 | applicationActivities: nil) 67 | activityVC.completionWithItemsHandler = {(activityType: UIActivityType?, completed: Bool, returnedItems: [Any]?, error: Error?) in 68 | print("Activity: \(activityType) Success: \(completed) Items: \(returnedItems) Error: \(error)") 69 | 70 | if !completed { 71 | // User canceled 72 | return 73 | } 74 | // User completed activity 75 | } 76 | present(activityVC, animated: true, completion: nil) 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /gif3d/viewcontrollers/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // gif3d 4 | // 5 | // Created by Sam on 7/9/18. 6 | // Copyright © 2018 chuongtran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SDWebImage 11 | //import FLAnimatedImage 12 | 13 | class ViewController: UIViewController { 14 | @IBOutlet weak var imageView: UIImageView! 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | 19 | 20 | let fileURL = Bundle.main.url(forResource:"1", withExtension: "gif") 21 | 22 | print("fileURL:\(fileURL)") 23 | 24 | if FileManager.default.fileExists(atPath: (fileURL?.path)!){ 25 | do { 26 | let data = try Data(contentsOf: fileURL!) 27 | let image = UIImage.sd_animatedGIF(with: data) 28 | print("images:\(image?.images)") 29 | 30 | imageView.image = image 31 | 32 | } catch { 33 | 34 | } 35 | } 36 | 37 | } 38 | 39 | 40 | override func didReceiveMemoryWarning() { 41 | super.didReceiveMemoryWarning() 42 | // Dispose of any resources that can be recreated. 43 | } 44 | 45 | 46 | //MARK: OnAction 47 | @IBAction func onNext(){ 48 | self.performSegue(withIdentifier: "ShowEditorVCIdentifier", sender: nil) 49 | } 50 | 51 | //MARK: Navigation 52 | 53 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 54 | if segue.identifier == "ShowEditorVCIdentifier" { 55 | if let vc:EditorVC = segue.destination as? EditorVC { 56 | vc.images = imageView.image?.images 57 | } 58 | } 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /gif3d/viewcontrollers/cell/ColorItemCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ColorItemCell.swift 3 | // gif3d 4 | // 5 | // Created by Sam on 7/13/18. 6 | // Copyright © 2018 chuongtran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ColorItemCell: UICollectionViewCell { 12 | open class func identifier() -> String { 13 | return "ColorItemCellIdentifier" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /gif3d/viewcontrollers/cell/FrameItemCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FrameItemCell.swift 3 | // gif3d 4 | // 5 | // Created by Sam on 7/10/18. 6 | // Copyright © 2018 chuongtran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class FrameItemCell: UICollectionViewCell { 12 | 13 | @IBOutlet private weak var ivThumbnail:UIImageView! 14 | @IBOutlet private weak var lbText:UILabel! 15 | 16 | override func awakeFromNib() { 17 | super.awakeFromNib() 18 | self.backgroundColor = UIColor.clear 19 | lbText.layer.cornerRadius = 10 20 | lbText.layer.masksToBounds = true 21 | 22 | ivThumbnail.layer.makeShadow(color: UIColor.darkGray, x: 0, y: 2, blur: 4, spread: 0) 23 | 24 | } 25 | open class func identifier() -> String { 26 | return "FrameItemCellIdentifier" 27 | } 28 | override func prepareForReuse() { 29 | super.prepareForReuse() 30 | ivThumbnail.image = nil 31 | } 32 | 33 | //MARK: - Public 34 | func updateUI(image:UIImage, isEdited:Bool, color:UIColor, index:Int, isActive:Bool){ 35 | ivThumbnail.image = image 36 | ivThumbnail.backgroundColor = color 37 | lbText.textColor = isActive == true ? UIColor.red : UIColor.black 38 | 39 | if isEdited == true { 40 | lbText.text = "\(index + 1) ✓" 41 | 42 | }else{ 43 | lbText.text = "\(index + 1)" 44 | 45 | } 46 | 47 | //lbText.backgroundColor = isActive == true ? UIColor.green: UIColor.clear 48 | lbText.font = isActive == true ? font_bold(size:18) : font_light(size: 16) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /gif3d/views/EditorFrameView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EditorFrameView.swift 3 | // gif3d 4 | // 5 | // Created by Sam on 7/10/18. 6 | // Copyright © 2018 chuongtran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol EditorFrameViewDelegate: class { 12 | func editorFrameChange(editorFrameView:EditorFrameView) 13 | } 14 | 15 | class EditorFrameView: UIView { 16 | 17 | @IBOutlet weak var ivThumbnail:UIImageView! 18 | @IBOutlet weak var editorView:EditorView! 19 | 20 | public weak var delegate:EditorFrameViewDelegate? 21 | 22 | var editor:Editor! 23 | 24 | // MARK: - Initialization 25 | 26 | required init?(coder aDecoder: NSCoder) { 27 | super.init(coder: aDecoder) 28 | 29 | } 30 | override func awakeFromNib() { 31 | super.awakeFromNib() 32 | 33 | editorView.delegate = self 34 | } 35 | override init(frame: CGRect) { 36 | super.init(frame: frame) 37 | } 38 | 39 | func setupEditor(editor:Editor) { 40 | self.editor = editor 41 | ivThumbnail.image = editor.image 42 | 43 | editorView.resetDrawing() 44 | editorView.setupEditor(editor: editor) 45 | editorView.commit() 46 | } 47 | 48 | func buildFrame() -> UIImage { 49 | if let image = ivThumbnail.createImage(), let overlay = editorView.getEditedFrame(){ 50 | return image.overlayWith(image: overlay, posX: 0, posY: 0) 51 | } 52 | return UIImage() 53 | } 54 | } 55 | 56 | extension EditorFrameView: EditorViewDelegate{ 57 | func drawPath(editorView: EditorView, from: CGPoint, to: CGPoint, radius: CGFloat) { 58 | let path = Path(fromPoint: from, toPoint: to) 59 | editor.tools?.append(Tool(path: path, radius: radius)) 60 | 61 | if let delegate = self.delegate{ 62 | delegate.editorFrameChange(editorFrameView: self) 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /gif3d/views/EditorView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // gif3d 4 | // 5 | // Created by Sam on 7/9/18. 6 | // Copyright © 2018 chuongtran. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | 13 | 14 | class DrawingView: UIView { 15 | 16 | private var lastPoint: CGPoint? 17 | fileprivate var drawHandler: ((_ fromPoint: CGPoint, _ toPoint: CGPoint) -> Void)? 18 | 19 | // MARK: - override 20 | 21 | open override func touchesBegan(_ touches: Set, with event: UIEvent?) { 22 | guard let point = touches.first?.location(in: self) else { return } 23 | if lastPoint == nil { 24 | lastPoint = point 25 | } 26 | } 27 | 28 | open override func touchesMoved(_ touches: Set, with event: UIEvent?) { 29 | super.touchesMoved(touches, with: event) 30 | 31 | 32 | guard let point = touches.first?.location(in: self) else { return } 33 | defer { lastPoint = point } 34 | guard let lastPoint = lastPoint else { return } 35 | 36 | drawHandler?(lastPoint, point) 37 | } 38 | 39 | open override func touchesEnded(_ touches: Set, with event: UIEvent?) { 40 | super.touchesEnded(touches, with: event) 41 | 42 | guard let _ = lastPoint else { return } 43 | lastPoint = nil 44 | } 45 | 46 | } 47 | 48 | protocol EditorViewDelegate: class { 49 | func drawPath(editorView:EditorView, from:CGPoint, to:CGPoint, radius:CGFloat) 50 | } 51 | 52 | class EditorView: DrawingView { 53 | 54 | open var lineCap: CGLineCap = .round 55 | open var radius: CGFloat = 20 56 | public weak var delegate:EditorViewDelegate? 57 | 58 | private var tempImageView: UIImageView! 59 | private var chunkedPath:[Tool] = [] 60 | private var imageCache:UIImage! 61 | // MARK: - Drawing 62 | 63 | override func draw(_ rect: CGRect) { 64 | super.draw(rect) 65 | } 66 | 67 | override func layoutSubviews () { 68 | super.layoutSubviews() 69 | } 70 | 71 | 72 | // MARK: - Initialization 73 | 74 | required init?(coder aDecoder: NSCoder) { 75 | super.init(coder: aDecoder) 76 | 77 | setupLine() 78 | 79 | setupDrawing() 80 | 81 | } 82 | 83 | override init(frame: CGRect) { 84 | super.init(frame: frame) 85 | 86 | setupLine() 87 | 88 | setupDrawing() 89 | 90 | } 91 | 92 | 93 | // MARK: - Private methods 94 | private func setupDrawing(){ 95 | 96 | self.imageCache = self.createImage() 97 | tempImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: SCREEN_WIDTH)) 98 | tempImageView.image = self.imageCache 99 | self.addSubview(tempImageView) 100 | self.backgroundColor = UIColor.clear 101 | 102 | subviews.forEach { (temp) in 103 | temp.backgroundColor = UIColor.clear 104 | } 105 | 106 | self.drawHandler = { [weak self] fromPoint, toPoint in 107 | self?.drawPath(from: fromPoint, to: toPoint) 108 | if let delegate = self?.delegate{ 109 | delegate.drawPath(editorView: self!, from: fromPoint, to: toPoint, radius: (self?.radius)!) 110 | } 111 | } 112 | } 113 | 114 | private func setupLine(){ 115 | 116 | let screenWidth = SCREEN_WIDTH 117 | let line1 = UIView() 118 | line1.frame = CGRect(x: screenWidth/3 - 10, y: 0, width: 20, height: screenWidth) 119 | line1.backgroundColor = UIColor.white 120 | self.addSubview(line1) 121 | 122 | let line2 = UIView() 123 | line2.frame = CGRect(x: screenWidth*2/3 - 10, y: 0, width: 20, height: screenWidth) 124 | line2.backgroundColor = UIColor.white 125 | self.addSubview(line2) 126 | 127 | } 128 | 129 | private func drawPath(from fromPoint: CGPoint, to toPoint: CGPoint) { 130 | let imageSize = CGSize.init( 131 | width: floor(tempImageView.frame.size.width) , 132 | height: floor(tempImageView.frame.size.height)) 133 | UIGraphicsBeginImageContextWithOptions(imageSize, false, 0.0) 134 | defer { UIGraphicsEndImageContext() } 135 | guard let context = UIGraphicsGetCurrentContext() else { return } 136 | tempImageView.image?.draw(in: .init(origin: .zero, size: imageSize)) 137 | let path = Path(fromPoint: fromPoint, toPoint: toPoint) 138 | chunkedPath.append(Tool(path: path, radius: radius)) 139 | addStrokePath(context, from: fromPoint, to: toPoint, radius: radius) 140 | tempImageView.image = UIGraphicsGetImageFromCurrentImageContext() 141 | UIGraphicsEndImageContext() 142 | } 143 | 144 | private func addStrokePath(_ context: CGContext, from fromPoint: CGPoint, to toPoint: CGPoint, radius:CGFloat, lineWidthRatio: CGFloat = 1.0) { 145 | context.move(to: fromPoint) 146 | context.addLine(to: toPoint) 147 | context.setLineCap(lineCap) 148 | context.setLineWidth(radius * lineWidthRatio) 149 | context.setStrokeColor(UIColor.clear.cgColor) 150 | context.setBlendMode(.clear) 151 | context.strokePath() 152 | } 153 | 154 | //MAKR: - Public methods 155 | 156 | func resetDrawing(){ 157 | chunkedPath.removeAll() 158 | tempImageView.image = self.imageCache 159 | 160 | } 161 | 162 | func setupEditor(editor:Editor){ 163 | chunkedPath.removeAll() 164 | chunkedPath.append(contentsOf: editor.tools!) 165 | } 166 | func commit(){ 167 | 168 | let imageSize = CGSize.init( 169 | width: floor(tempImageView.frame.size.width) , 170 | height: floor(tempImageView.frame.size.height)) 171 | UIGraphicsBeginImageContextWithOptions(imageSize, false, 0.0) 172 | defer { UIGraphicsEndImageContext() } 173 | guard let context = UIGraphicsGetCurrentContext() else { return } 174 | tempImageView.image?.draw(in: .init(origin: .zero, size: imageSize)) 175 | 176 | chunkedPath.forEach { (tool) in 177 | addStrokePath(context, from: tool.path.fromPoint, to: tool.path.toPoint, radius: tool.radius) 178 | } 179 | 180 | tempImageView.image = UIGraphicsGetImageFromCurrentImageContext() 181 | } 182 | 183 | func getEditedFrame()->UIImage?{ 184 | return tempImageView.image 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /screenshot/after.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chuongtrh/gif3d/8829c0e837c87565f974e3f655f441e47cf963e1/screenshot/after.gif -------------------------------------------------------------------------------- /screenshot/before.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chuongtrh/gif3d/8829c0e837c87565f974e3f655f441e47cf963e1/screenshot/before.gif -------------------------------------------------------------------------------- /screenshot/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chuongtrh/gif3d/8829c0e837c87565f974e3f655f441e47cf963e1/screenshot/screenshot1.png --------------------------------------------------------------------------------