├── .gitignore ├── .swift-version ├── LICENSE ├── MTSlideToOpen.podspec ├── MTSlideToOpen.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── martinlee.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── martinlee.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── MTSlideToOpen.xcscheme │ └── xcschememanagement.plist ├── MTSlideToOpen ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ └── ic_arrow.imageset │ │ ├── Contents.json │ │ └── ic_arrow.png ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist └── ViewController.swift ├── README.md ├── Right-To-Left.png ├── Screenshot.png └── Source └── MTSlideToOpenView.swift /.gitignore: -------------------------------------------------------------------------------- 1 | ## Build generated 2 | build/ 3 | DerivedData 4 | build.xcarchive 5 | 6 | ## Various settings 7 | *.pbxuser 8 | !default.pbxuser 9 | *.mode1v3 10 | !default.mode1v3 11 | *.mode2v3 12 | !default.mode2v3 13 | *.perspectivev3 14 | !default.perspectivev3 15 | xcuserdata 16 | 17 | ## Other 18 | *.xccheckout 19 | *.moved-aside 20 | *.xcuserstate 21 | *.xcscmblueprint 22 | 23 | ## Obj-C/Swift specific 24 | *.hmap 25 | *.ipa 26 | 27 | # CocoaPods 28 | Pods/ 29 | 30 | # Carthage 31 | Carthage/Checkouts 32 | Carthage/Build -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 5.0 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Tien Le 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MTSlideToOpen.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 4 | # 5 | 6 | s.name = "MTSlideToOpen" 7 | s.version = "1.5.1" 8 | s.summary = "A simple SlideToUnlock iOS UI component." 9 | 10 | s.description = "A simple iOS UI component acts Slide To Unlock." 11 | 12 | s.homepage = "https://github.com/lemanhtien/MTSlideToOpen" 13 | s.screenshots = "https://raw.githubusercontent.com/lemanhtien/MTSlideToOpen/master/Screenshot.png" 14 | 15 | 16 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 17 | # 18 | 19 | s.license = { :type => "MIT", :file => "LICENSE" } 20 | 21 | 22 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 23 | # 24 | 25 | s.author = { "Tien Le" => "lemanhtien.bka@gmail.com" } 26 | s.social_media_url = "https://twitter.com/Martin_ManhTien" 27 | 28 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 29 | # 30 | # If this Pod runs only on iOS or OS X, then specify the platform and 31 | # the deployment target. You can optionally include the target after the platform. 32 | # 33 | 34 | s.platform = :ios, "9.0" 35 | 36 | 37 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 38 | # 39 | 40 | s.source = { :git => "https://github.com/lemanhtien/MTSlideToOpen.git", :tag => s.version } 41 | 42 | 43 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 44 | # 45 | 46 | s.source_files = "Source" 47 | s.framework = "UIKit" 48 | s.pod_target_xcconfig = { "SWIFT_VERSION" => "5.0" } 49 | s.swift_version = '5.0' 50 | s.requires_arc = true 51 | 52 | 53 | end 54 | -------------------------------------------------------------------------------- /MTSlideToOpen.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | BD0B6A0F1F8F4E1200D3AC94 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD0B6A0E1F8F4E1200D3AC94 /* AppDelegate.swift */; }; 11 | BD0B6A111F8F4E1200D3AC94 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD0B6A101F8F4E1200D3AC94 /* ViewController.swift */; }; 12 | BD0B6A141F8F4E1200D3AC94 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BD0B6A121F8F4E1200D3AC94 /* Main.storyboard */; }; 13 | BD0B6A161F8F4E1200D3AC94 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BD0B6A151F8F4E1200D3AC94 /* Assets.xcassets */; }; 14 | BD0B6A191F8F4E1200D3AC94 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BD0B6A171F8F4E1200D3AC94 /* LaunchScreen.storyboard */; }; 15 | BD392E561F8F5835005F0194 /* MTSlideToOpenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD392E551F8F5835005F0194 /* MTSlideToOpenView.swift */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXFileReference section */ 19 | BD0B6A0B1F8F4E1200D3AC94 /* MTSlideToOpen.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MTSlideToOpen.app; sourceTree = BUILT_PRODUCTS_DIR; }; 20 | BD0B6A0E1F8F4E1200D3AC94 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 21 | BD0B6A101F8F4E1200D3AC94 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 22 | BD0B6A131F8F4E1200D3AC94 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 23 | BD0B6A151F8F4E1200D3AC94 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 24 | BD0B6A181F8F4E1200D3AC94 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 25 | BD0B6A1A1F8F4E1200D3AC94 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 26 | BD392E551F8F5835005F0194 /* MTSlideToOpenView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MTSlideToOpenView.swift; sourceTree = ""; }; 27 | /* End PBXFileReference section */ 28 | 29 | /* Begin PBXFrameworksBuildPhase section */ 30 | BD0B6A081F8F4E1200D3AC94 /* Frameworks */ = { 31 | isa = PBXFrameworksBuildPhase; 32 | buildActionMask = 2147483647; 33 | files = ( 34 | ); 35 | runOnlyForDeploymentPostprocessing = 0; 36 | }; 37 | /* End PBXFrameworksBuildPhase section */ 38 | 39 | /* Begin PBXGroup section */ 40 | BD0B6A021F8F4E1200D3AC94 = { 41 | isa = PBXGroup; 42 | children = ( 43 | BD0B6A0D1F8F4E1200D3AC94 /* MTSlideToOpen */, 44 | BD0B6A0C1F8F4E1200D3AC94 /* Products */, 45 | ); 46 | sourceTree = ""; 47 | }; 48 | BD0B6A0C1F8F4E1200D3AC94 /* Products */ = { 49 | isa = PBXGroup; 50 | children = ( 51 | BD0B6A0B1F8F4E1200D3AC94 /* MTSlideToOpen.app */, 52 | ); 53 | name = Products; 54 | sourceTree = ""; 55 | }; 56 | BD0B6A0D1F8F4E1200D3AC94 /* MTSlideToOpen */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | BD392E541F8F5790005F0194 /* Source */, 60 | BD0B6A0E1F8F4E1200D3AC94 /* AppDelegate.swift */, 61 | BD0B6A101F8F4E1200D3AC94 /* ViewController.swift */, 62 | BD0B6A121F8F4E1200D3AC94 /* Main.storyboard */, 63 | BD0B6A151F8F4E1200D3AC94 /* Assets.xcassets */, 64 | BD0B6A171F8F4E1200D3AC94 /* LaunchScreen.storyboard */, 65 | BD0B6A1A1F8F4E1200D3AC94 /* Info.plist */, 66 | ); 67 | path = MTSlideToOpen; 68 | sourceTree = ""; 69 | }; 70 | BD392E541F8F5790005F0194 /* Source */ = { 71 | isa = PBXGroup; 72 | children = ( 73 | BD392E551F8F5835005F0194 /* MTSlideToOpenView.swift */, 74 | ); 75 | path = Source; 76 | sourceTree = SOURCE_ROOT; 77 | }; 78 | /* End PBXGroup section */ 79 | 80 | /* Begin PBXNativeTarget section */ 81 | BD0B6A0A1F8F4E1200D3AC94 /* MTSlideToOpen */ = { 82 | isa = PBXNativeTarget; 83 | buildConfigurationList = BD0B6A1D1F8F4E1200D3AC94 /* Build configuration list for PBXNativeTarget "MTSlideToOpen" */; 84 | buildPhases = ( 85 | BD0B6A071F8F4E1200D3AC94 /* Sources */, 86 | BD0B6A081F8F4E1200D3AC94 /* Frameworks */, 87 | BD0B6A091F8F4E1200D3AC94 /* Resources */, 88 | ); 89 | buildRules = ( 90 | ); 91 | dependencies = ( 92 | ); 93 | name = MTSlideToOpen; 94 | productName = MTSlideToOpen; 95 | productReference = BD0B6A0B1F8F4E1200D3AC94 /* MTSlideToOpen.app */; 96 | productType = "com.apple.product-type.application"; 97 | }; 98 | /* End PBXNativeTarget section */ 99 | 100 | /* Begin PBXProject section */ 101 | BD0B6A031F8F4E1200D3AC94 /* Project object */ = { 102 | isa = PBXProject; 103 | attributes = { 104 | LastSwiftUpdateCheck = 0900; 105 | LastUpgradeCheck = 1000; 106 | ORGANIZATIONNAME = "Martin Le"; 107 | TargetAttributes = { 108 | BD0B6A0A1F8F4E1200D3AC94 = { 109 | CreatedOnToolsVersion = 9.0; 110 | LastSwiftMigration = 1000; 111 | ProvisioningStyle = Automatic; 112 | }; 113 | }; 114 | }; 115 | buildConfigurationList = BD0B6A061F8F4E1200D3AC94 /* Build configuration list for PBXProject "MTSlideToOpen" */; 116 | compatibilityVersion = "Xcode 8.0"; 117 | developmentRegion = en; 118 | hasScannedForEncodings = 0; 119 | knownRegions = ( 120 | en, 121 | Base, 122 | ); 123 | mainGroup = BD0B6A021F8F4E1200D3AC94; 124 | productRefGroup = BD0B6A0C1F8F4E1200D3AC94 /* Products */; 125 | projectDirPath = ""; 126 | projectRoot = ""; 127 | targets = ( 128 | BD0B6A0A1F8F4E1200D3AC94 /* MTSlideToOpen */, 129 | ); 130 | }; 131 | /* End PBXProject section */ 132 | 133 | /* Begin PBXResourcesBuildPhase section */ 134 | BD0B6A091F8F4E1200D3AC94 /* Resources */ = { 135 | isa = PBXResourcesBuildPhase; 136 | buildActionMask = 2147483647; 137 | files = ( 138 | BD0B6A191F8F4E1200D3AC94 /* LaunchScreen.storyboard in Resources */, 139 | BD0B6A161F8F4E1200D3AC94 /* Assets.xcassets in Resources */, 140 | BD0B6A141F8F4E1200D3AC94 /* Main.storyboard in Resources */, 141 | ); 142 | runOnlyForDeploymentPostprocessing = 0; 143 | }; 144 | /* End PBXResourcesBuildPhase section */ 145 | 146 | /* Begin PBXSourcesBuildPhase section */ 147 | BD0B6A071F8F4E1200D3AC94 /* Sources */ = { 148 | isa = PBXSourcesBuildPhase; 149 | buildActionMask = 2147483647; 150 | files = ( 151 | BD0B6A111F8F4E1200D3AC94 /* ViewController.swift in Sources */, 152 | BD392E561F8F5835005F0194 /* MTSlideToOpenView.swift in Sources */, 153 | BD0B6A0F1F8F4E1200D3AC94 /* AppDelegate.swift in Sources */, 154 | ); 155 | runOnlyForDeploymentPostprocessing = 0; 156 | }; 157 | /* End PBXSourcesBuildPhase section */ 158 | 159 | /* Begin PBXVariantGroup section */ 160 | BD0B6A121F8F4E1200D3AC94 /* Main.storyboard */ = { 161 | isa = PBXVariantGroup; 162 | children = ( 163 | BD0B6A131F8F4E1200D3AC94 /* Base */, 164 | ); 165 | name = Main.storyboard; 166 | sourceTree = ""; 167 | }; 168 | BD0B6A171F8F4E1200D3AC94 /* LaunchScreen.storyboard */ = { 169 | isa = PBXVariantGroup; 170 | children = ( 171 | BD0B6A181F8F4E1200D3AC94 /* Base */, 172 | ); 173 | name = LaunchScreen.storyboard; 174 | sourceTree = ""; 175 | }; 176 | /* End PBXVariantGroup section */ 177 | 178 | /* Begin XCBuildConfiguration section */ 179 | BD0B6A1B1F8F4E1200D3AC94 /* Debug */ = { 180 | isa = XCBuildConfiguration; 181 | buildSettings = { 182 | ALWAYS_SEARCH_USER_PATHS = NO; 183 | CLANG_ANALYZER_NONNULL = YES; 184 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 185 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 186 | CLANG_CXX_LIBRARY = "libc++"; 187 | CLANG_ENABLE_MODULES = YES; 188 | CLANG_ENABLE_OBJC_ARC = YES; 189 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 190 | CLANG_WARN_BOOL_CONVERSION = YES; 191 | CLANG_WARN_COMMA = YES; 192 | CLANG_WARN_CONSTANT_CONVERSION = YES; 193 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 194 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 195 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 196 | CLANG_WARN_EMPTY_BODY = YES; 197 | CLANG_WARN_ENUM_CONVERSION = YES; 198 | CLANG_WARN_INFINITE_RECURSION = YES; 199 | CLANG_WARN_INT_CONVERSION = YES; 200 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 201 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 202 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 203 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 204 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 205 | CLANG_WARN_STRICT_PROTOTYPES = YES; 206 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 207 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 208 | CLANG_WARN_UNREACHABLE_CODE = YES; 209 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 210 | CODE_SIGN_IDENTITY = "iPhone Developer"; 211 | COPY_PHASE_STRIP = NO; 212 | DEBUG_INFORMATION_FORMAT = dwarf; 213 | ENABLE_STRICT_OBJC_MSGSEND = YES; 214 | ENABLE_TESTABILITY = YES; 215 | GCC_C_LANGUAGE_STANDARD = gnu11; 216 | GCC_DYNAMIC_NO_PIC = NO; 217 | GCC_NO_COMMON_BLOCKS = YES; 218 | GCC_OPTIMIZATION_LEVEL = 0; 219 | GCC_PREPROCESSOR_DEFINITIONS = ( 220 | "DEBUG=1", 221 | "$(inherited)", 222 | ); 223 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 224 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 225 | GCC_WARN_UNDECLARED_SELECTOR = YES; 226 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 227 | GCC_WARN_UNUSED_FUNCTION = YES; 228 | GCC_WARN_UNUSED_VARIABLE = YES; 229 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 230 | MTL_ENABLE_DEBUG_INFO = YES; 231 | ONLY_ACTIVE_ARCH = YES; 232 | SDKROOT = iphoneos; 233 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 234 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 235 | SWIFT_VERSION = 4.0; 236 | }; 237 | name = Debug; 238 | }; 239 | BD0B6A1C1F8F4E1200D3AC94 /* Release */ = { 240 | isa = XCBuildConfiguration; 241 | buildSettings = { 242 | ALWAYS_SEARCH_USER_PATHS = NO; 243 | CLANG_ANALYZER_NONNULL = YES; 244 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 245 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 246 | CLANG_CXX_LIBRARY = "libc++"; 247 | CLANG_ENABLE_MODULES = YES; 248 | CLANG_ENABLE_OBJC_ARC = YES; 249 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 250 | CLANG_WARN_BOOL_CONVERSION = YES; 251 | CLANG_WARN_COMMA = YES; 252 | CLANG_WARN_CONSTANT_CONVERSION = YES; 253 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 254 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 255 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 256 | CLANG_WARN_EMPTY_BODY = YES; 257 | CLANG_WARN_ENUM_CONVERSION = YES; 258 | CLANG_WARN_INFINITE_RECURSION = YES; 259 | CLANG_WARN_INT_CONVERSION = YES; 260 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 261 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 262 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 263 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 264 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 265 | CLANG_WARN_STRICT_PROTOTYPES = YES; 266 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 267 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 268 | CLANG_WARN_UNREACHABLE_CODE = YES; 269 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 270 | CODE_SIGN_IDENTITY = "iPhone Developer"; 271 | COPY_PHASE_STRIP = NO; 272 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 273 | ENABLE_NS_ASSERTIONS = NO; 274 | ENABLE_STRICT_OBJC_MSGSEND = YES; 275 | GCC_C_LANGUAGE_STANDARD = gnu11; 276 | GCC_NO_COMMON_BLOCKS = YES; 277 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 278 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 279 | GCC_WARN_UNDECLARED_SELECTOR = YES; 280 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 281 | GCC_WARN_UNUSED_FUNCTION = YES; 282 | GCC_WARN_UNUSED_VARIABLE = YES; 283 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 284 | MTL_ENABLE_DEBUG_INFO = NO; 285 | SDKROOT = iphoneos; 286 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 287 | SWIFT_VERSION = 4.0; 288 | VALIDATE_PRODUCT = YES; 289 | }; 290 | name = Release; 291 | }; 292 | BD0B6A1E1F8F4E1200D3AC94 /* Debug */ = { 293 | isa = XCBuildConfiguration; 294 | buildSettings = { 295 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 296 | CODE_SIGN_STYLE = Automatic; 297 | DEVELOPMENT_TEAM = 4Y74WWZQUR; 298 | INFOPLIST_FILE = MTSlideToOpen/Info.plist; 299 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 300 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 301 | PRODUCT_BUNDLE_IDENTIFIER = "Martin-Le.MTSlideToOpen"; 302 | PRODUCT_NAME = "$(TARGET_NAME)"; 303 | SWIFT_VERSION = 5.0; 304 | TARGETED_DEVICE_FAMILY = "1,2"; 305 | }; 306 | name = Debug; 307 | }; 308 | BD0B6A1F1F8F4E1200D3AC94 /* Release */ = { 309 | isa = XCBuildConfiguration; 310 | buildSettings = { 311 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 312 | CODE_SIGN_STYLE = Automatic; 313 | DEVELOPMENT_TEAM = 4Y74WWZQUR; 314 | INFOPLIST_FILE = MTSlideToOpen/Info.plist; 315 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 316 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 317 | PRODUCT_BUNDLE_IDENTIFIER = "Martin-Le.MTSlideToOpen"; 318 | PRODUCT_NAME = "$(TARGET_NAME)"; 319 | SWIFT_VERSION = 5.0; 320 | TARGETED_DEVICE_FAMILY = "1,2"; 321 | }; 322 | name = Release; 323 | }; 324 | /* End XCBuildConfiguration section */ 325 | 326 | /* Begin XCConfigurationList section */ 327 | BD0B6A061F8F4E1200D3AC94 /* Build configuration list for PBXProject "MTSlideToOpen" */ = { 328 | isa = XCConfigurationList; 329 | buildConfigurations = ( 330 | BD0B6A1B1F8F4E1200D3AC94 /* Debug */, 331 | BD0B6A1C1F8F4E1200D3AC94 /* Release */, 332 | ); 333 | defaultConfigurationIsVisible = 0; 334 | defaultConfigurationName = Release; 335 | }; 336 | BD0B6A1D1F8F4E1200D3AC94 /* Build configuration list for PBXNativeTarget "MTSlideToOpen" */ = { 337 | isa = XCConfigurationList; 338 | buildConfigurations = ( 339 | BD0B6A1E1F8F4E1200D3AC94 /* Debug */, 340 | BD0B6A1F1F8F4E1200D3AC94 /* Release */, 341 | ); 342 | defaultConfigurationIsVisible = 0; 343 | defaultConfigurationName = Release; 344 | }; 345 | /* End XCConfigurationList section */ 346 | }; 347 | rootObject = BD0B6A031F8F4E1200D3AC94 /* Project object */; 348 | } 349 | -------------------------------------------------------------------------------- /MTSlideToOpen.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MTSlideToOpen.xcodeproj/project.xcworkspace/xcuserdata/martinlee.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lemanhtien/MTSlideToOpen/c127000a143ed4bec506a84e555e921a14294fda/MTSlideToOpen.xcodeproj/project.xcworkspace/xcuserdata/martinlee.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /MTSlideToOpen.xcodeproj/xcuserdata/martinlee.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /MTSlideToOpen.xcodeproj/xcuserdata/martinlee.xcuserdatad/xcschemes/MTSlideToOpen.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /MTSlideToOpen.xcodeproj/xcuserdata/martinlee.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | MTSlideToOpen.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | BD0B6A0A1F8F4E1200D3AC94 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /MTSlideToOpen/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // MTSlideToOpen 4 | // 5 | // Created by Martin Lee on 10/12/17. 6 | // Copyright © 2017 Martin Le. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> 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 | -------------------------------------------------------------------------------- /MTSlideToOpen/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 | } -------------------------------------------------------------------------------- /MTSlideToOpen/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /MTSlideToOpen/Assets.xcassets/ic_arrow.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "ic_arrow.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MTSlideToOpen/Assets.xcassets/ic_arrow.imageset/ic_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lemanhtien/MTSlideToOpen/c127000a143ed4bec506a84e555e921a14294fda/MTSlideToOpen/Assets.xcassets/ic_arrow.imageset/ic_arrow.png -------------------------------------------------------------------------------- /MTSlideToOpen/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 | -------------------------------------------------------------------------------- /MTSlideToOpen/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 | -------------------------------------------------------------------------------- /MTSlideToOpen/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /MTSlideToOpen/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // MTSlideToOpen 4 | // 5 | // Created by Martin Lee on 10/12/17. 6 | // Copyright © 2017 Martin Le. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController, MTSlideToOpenDelegate { 12 | lazy var slideToOpen: MTSlideToOpenView = { 13 | let slide = MTSlideToOpenView(frame: CGRect(x: 26, y: 100, width: 317, height: 56)) 14 | slide.sliderViewTopDistance = 0 15 | slide.sliderCornerRadius = 28 16 | slide.showSliderText = true 17 | slide.thumbnailColor = UIColor(red:141.0/255, green:19.0/255, blue:65.0/255, alpha:1.0) 18 | slide.slidingColor = UIColor.red 19 | slide.textColor = UIColor.orange 20 | slide.sliderBackgroundColor = UIColor(red:0.88, green:1, blue:0.98, alpha:1.0) 21 | slide.delegate = self 22 | slide.thumnailImageView.image = #imageLiteral(resourceName: "ic_arrow").imageFlippedForRightToLeftLayoutDirection() 23 | 24 | return slide 25 | }() 26 | lazy var slideToLock: MTSlideToOpenView = { 27 | let slide = MTSlideToOpenView(frame: CGRect(x: 26, y: 200, width: 317, height: 56)) 28 | slide.sliderViewTopDistance = 0 29 | slide.sliderCornerRadius = 28 30 | slide.thumnailImageView.backgroundColor = UIColor(red:200.0/255, green:200.0/255, blue:200.0/255, alpha:1.0) 31 | slide.draggedView.backgroundColor = UIColor(red:200.0/255, green:200.0/255, blue:200.0/255, alpha:1.0) 32 | slide.delegate = self 33 | slide.thumbnailViewStartingDistance = 20 34 | slide.labelText = "Slide To Lock" 35 | slide.thumnailImageView.image = #imageLiteral(resourceName: "ic_arrow").imageFlippedForRightToLeftLayoutDirection() 36 | return slide 37 | }() 38 | lazy var customizeSlideToOpen: MTSlideToOpenView = { 39 | let slide = MTSlideToOpenView(frame: CGRect(x: 26, y: 300, width: 317, height: 56)) 40 | slide.sliderViewTopDistance = 0 41 | slide.thumbnailViewTopDistance = 4; 42 | slide.thumbnailViewStartingDistance = 4; 43 | slide.sliderCornerRadius = 28 44 | slide.thumnailImageView.backgroundColor = .white 45 | slide.draggedView.backgroundColor = .clear 46 | slide.delegate = self 47 | slide.thumnailImageView.image = #imageLiteral(resourceName: "ic_arrow").imageFlippedForRightToLeftLayoutDirection() 48 | slide.sliderBackgroundColor = .black 49 | return slide 50 | }() 51 | lazy var slideToUnlock: MTSlideToOpenView = { 52 | let frame = CGRect(x: 26, y: 400, width: 317, height: 56) 53 | let slide = MTSlideToOpenView(frame: frame) 54 | slide.sliderViewTopDistance = 6 55 | slide.sliderCornerRadius = 22 56 | slide.delegate = self 57 | slide.labelText = "Slide To Unlock" 58 | slide.thumnailImageView.image = #imageLiteral(resourceName: "ic_arrow").imageFlippedForRightToLeftLayoutDirection() 59 | slide.animationChangedEnabledBlock = { isEnabled in 60 | if isEnabled { 61 | slide.thumnailImageView.backgroundColor = slide.thumbnailColor 62 | slide.draggedView.isHidden = false 63 | slide.thumnailImageView.backgroundColor = slide.sliderBackgroundColor 64 | slide.textLabel.text = slide.labelText 65 | slide.thumnailImageView.layer.shadowOpacity = 0 66 | } else { 67 | slide.thumnailImageView.backgroundColor = UIColor(red:210.0/255, green:219.0/255, blue:228.0/255, alpha:0.8) 68 | slide.thumnailImageView.layer.shadowColor = UIColor(red:0/255, green:0/255, blue:0/255, alpha:0.2).cgColor 69 | slide.thumnailImageView.layer.shadowOffset = CGSize(width: 0, height: 25) 70 | slide.thumnailImageView.layer.shadowRadius = 28 71 | slide.thumnailImageView.layer.shadowOpacity = 1 72 | slide.draggedView.isHidden = true 73 | slide.sliderHolderView.backgroundColor = UIColor(red:245.0/255, green:247.0/255, blue:250.0/255, alpha:1) 74 | slide.textLabel.text = "" 75 | } 76 | } 77 | slide.isEnabled = false 78 | return slide 79 | }() 80 | 81 | override func viewDidLoad() { 82 | super.viewDidLoad() 83 | self.view.addSubview(slideToOpen) 84 | self.view.addSubview(slideToUnlock) 85 | self.view.addSubview(customizeSlideToOpen) 86 | self.view.addSubview(slideToLock) 87 | } 88 | 89 | // MARK: MTSlideToOpenDelegate 90 | func mtSlideToOpenDelegateDidFinish(_ sender: MTSlideToOpenView) { 91 | let alertController = UIAlertController(title: "", message: "Done!", preferredStyle: .alert) 92 | let doneAction = UIAlertAction(title: "Okay", style: .default) { (action) in 93 | sender.resetStateWithAnimation(false) 94 | } 95 | alertController.addAction(doneAction) 96 | self.present(alertController, animated: true, completion: nil) 97 | 98 | } 99 | } 100 | 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MTSlideToOpen 2 | A simple SlideToUnlock iOS UI component 3 | 4 | A simple SlideToUnlock iOS UI component. Support both **Left-To-Right** and **Right-To-Left** languages. 5 | 6 |

7 | 8 | 9 |

10 | 11 | ## Requirements 12 | * Swift 5.0 13 | * iOS 9.0 or later 14 | 15 | ## Installation 16 | 17 | #### Using CocoaPod 18 | Just add to your Pod file 19 | > pod 'MTSlideToOpen' 20 | 21 | #### Manual install 22 | 23 | Drag and drop folder `Source` to your project. Drag and drop to folder. 24 | 25 | 26 | 27 | ## Usage 28 | ``` 29 | let slide = MTSlideToOpenView(frame: CGRect(x: 26, y: 400, width: 317, height: 56)) 30 | slide.sliderViewTopDistance = 6 31 | slide.sliderCornerRadious = 22 32 | slide.delegate = self 33 | slide.defaultLabelText = "Slide To Unlock" 34 | slide.thumnailImageView.image = ic_arrow 35 | ``` 36 | 37 | ## SwiftUI version 38 | 39 | I built another version using SwiftUI. You can check it here [MTSlideToOpen-SwiftUI](https://github.com/lemanhtien/MTSlideToOpen-SwiftUI). 40 | 41 | ## License 42 | 43 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details 44 | -------------------------------------------------------------------------------- /Right-To-Left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lemanhtien/MTSlideToOpen/c127000a143ed4bec506a84e555e921a14294fda/Right-To-Left.png -------------------------------------------------------------------------------- /Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lemanhtien/MTSlideToOpen/c127000a143ed4bec506a84e555e921a14294fda/Screenshot.png -------------------------------------------------------------------------------- /Source/MTSlideToOpenView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MTSlideToOpenControl.swift 3 | // MTSlideToOpen 4 | // 5 | // Created by Martin Lee on 10/12/17. 6 | // Copyright © 2017 Martin Le. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @objc public protocol MTSlideToOpenDelegate { 12 | func mtSlideToOpenDelegateDidFinish(_ sender: MTSlideToOpenView) 13 | } 14 | 15 | @objcMembers public class MTSlideToOpenView: UIView { 16 | // MARK: All Views 17 | public let textLabel: UILabel = { 18 | let label = UILabel.init() 19 | return label 20 | }() 21 | public let sliderTextLabel: UILabel = { 22 | let label = UILabel() 23 | return label 24 | }() 25 | public let thumnailImageView: UIImageView = { 26 | let view = MTRoundImageView() 27 | view.isUserInteractionEnabled = true 28 | view.contentMode = .center 29 | return view 30 | }() 31 | public let sliderHolderView: UIView = { 32 | let view = UIView() 33 | return view 34 | }() 35 | public let draggedView: UIView = { 36 | let view = UIView() 37 | return view 38 | }() 39 | public let view: UIView = { 40 | let view = UIView() 41 | return view 42 | }() 43 | // MARK: Public properties 44 | public weak var delegate: MTSlideToOpenDelegate? 45 | public var animationVelocity: Double = 0.2 46 | public var sliderViewTopDistance: CGFloat = 8.0 { 47 | didSet { 48 | topSliderConstraint?.constant = sliderViewTopDistance 49 | layoutIfNeeded() 50 | } 51 | } 52 | public var thumbnailViewTopDistance: CGFloat = 0.0 { 53 | didSet { 54 | topThumbnailViewConstraint?.constant = thumbnailViewTopDistance 55 | layoutIfNeeded() 56 | } 57 | } 58 | public var thumbnailViewStartingDistance: CGFloat = 0.0 { 59 | didSet { 60 | leadingThumbnailViewConstraint?.constant = thumbnailViewStartingDistance 61 | trailingDraggedViewConstraint?.constant = thumbnailViewStartingDistance 62 | setNeedsLayout() 63 | } 64 | } 65 | public var textLabelLeadingDistance: CGFloat = 0 { 66 | didSet { 67 | leadingTextLabelConstraint?.constant = textLabelLeadingDistance 68 | setNeedsLayout() 69 | } 70 | } 71 | public var isEnabled:Bool = true { 72 | didSet { 73 | animationChangedEnabledBlock?(isEnabled) 74 | } 75 | } 76 | public var showSliderText:Bool = false { 77 | didSet { 78 | sliderTextLabel.isHidden = !showSliderText 79 | } 80 | } 81 | public var animationChangedEnabledBlock:((Bool) -> Void)? 82 | // MARK: Default styles 83 | public var sliderCornerRadius: CGFloat = 30.0 { 84 | didSet { 85 | sliderHolderView.layer.cornerRadius = sliderCornerRadius 86 | draggedView.layer.cornerRadius = sliderCornerRadius 87 | } 88 | } 89 | public var sliderBackgroundColor: UIColor = UIColor(red:0.1, green:0.61, blue:0.84, alpha:0.1) { 90 | didSet { 91 | sliderHolderView.backgroundColor = sliderBackgroundColor 92 | sliderTextLabel.textColor = sliderBackgroundColor 93 | } 94 | } 95 | 96 | public var textColor:UIColor = UIColor(red:25.0/255, green:155.0/255, blue:215.0/255, alpha:0.7) { 97 | didSet { 98 | textLabel.textColor = textColor 99 | } 100 | } 101 | 102 | public var slidingColor:UIColor = UIColor(red:25.0/255, green:155.0/255, blue:215.0/255, alpha:0.7) { 103 | didSet { 104 | draggedView.backgroundColor = slidingColor 105 | } 106 | } 107 | public var thumbnailColor:UIColor = UIColor(red:25.0/255, green:155.0/255, blue:215.0/255, alpha:1) { 108 | didSet { 109 | thumnailImageView.backgroundColor = thumbnailColor 110 | } 111 | } 112 | public var labelText: String = "Swipe to open" { 113 | didSet { 114 | textLabel.text = labelText 115 | sliderTextLabel.text = labelText 116 | } 117 | } 118 | public var textFont: UIFont = UIFont.systemFont(ofSize: 15.0) { 119 | didSet { 120 | textLabel.font = textFont 121 | sliderTextLabel.font = textFont 122 | } 123 | } 124 | // MARK: Private Properties 125 | private var leadingThumbnailViewConstraint: NSLayoutConstraint? 126 | private var leadingTextLabelConstraint: NSLayoutConstraint? 127 | private var topSliderConstraint: NSLayoutConstraint? 128 | private var topThumbnailViewConstraint: NSLayoutConstraint? 129 | private var trailingDraggedViewConstraint: NSLayoutConstraint? 130 | private var xPositionInThumbnailView: CGFloat = 0 131 | private var xEndingPoint: CGFloat { 132 | get { 133 | return (self.view.frame.maxX - thumnailImageView.bounds.width - thumbnailViewStartingDistance) 134 | } 135 | } 136 | private var isFinished: Bool = false 137 | 138 | override public init(frame: CGRect) { 139 | super.init(frame: frame) 140 | setupView() 141 | } 142 | private var panGestureRecognizer: UIPanGestureRecognizer! 143 | 144 | required public init?(coder aDecoder: NSCoder) { 145 | super.init(coder: aDecoder)! 146 | setupView() 147 | } 148 | 149 | private func setupView() { 150 | self.addSubview(view) 151 | view.addSubview(thumnailImageView) 152 | view.addSubview(sliderHolderView) 153 | view.addSubview(draggedView) 154 | draggedView.addSubview(sliderTextLabel) 155 | sliderHolderView.addSubview(textLabel) 156 | view.bringSubviewToFront(self.thumnailImageView) 157 | setupConstraint() 158 | setStyle() 159 | // Add pan gesture 160 | panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.handlePanGesture(_:))) 161 | panGestureRecognizer.minimumNumberOfTouches = 1 162 | thumnailImageView.addGestureRecognizer(panGestureRecognizer) 163 | } 164 | 165 | private func setupConstraint() { 166 | view.translatesAutoresizingMaskIntoConstraints = false 167 | thumnailImageView.translatesAutoresizingMaskIntoConstraints = false 168 | sliderHolderView.translatesAutoresizingMaskIntoConstraints = false 169 | textLabel.translatesAutoresizingMaskIntoConstraints = false 170 | sliderTextLabel.translatesAutoresizingMaskIntoConstraints = false 171 | draggedView.translatesAutoresizingMaskIntoConstraints = false 172 | // Setup for view 173 | view.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true 174 | view.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true 175 | view.topAnchor.constraint(equalTo: self.topAnchor).isActive = true 176 | view.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true 177 | // Setup for circle View 178 | leadingThumbnailViewConstraint = thumnailImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor) 179 | leadingThumbnailViewConstraint?.isActive = true 180 | topThumbnailViewConstraint = thumnailImageView.topAnchor.constraint(equalTo: view.topAnchor, constant: thumbnailViewTopDistance) 181 | topThumbnailViewConstraint?.isActive = true 182 | thumnailImageView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true 183 | thumnailImageView.heightAnchor.constraint(equalTo: thumnailImageView.widthAnchor).isActive = true 184 | // Setup for slider holder view 185 | topSliderConstraint = sliderHolderView.topAnchor.constraint(equalTo: view.topAnchor, constant: sliderViewTopDistance) 186 | topSliderConstraint?.isActive = true 187 | sliderHolderView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true 188 | sliderHolderView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true 189 | sliderHolderView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true 190 | // Setup for textLabel 191 | textLabel.topAnchor.constraint(equalTo: sliderHolderView.topAnchor).isActive = true 192 | textLabel.centerYAnchor.constraint(equalTo: sliderHolderView.centerYAnchor).isActive = true 193 | leadingTextLabelConstraint = textLabel.leadingAnchor.constraint(equalTo: sliderHolderView.leadingAnchor, constant: textLabelLeadingDistance) 194 | leadingTextLabelConstraint?.isActive = true 195 | textLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: CGFloat(-8)).isActive = true 196 | // Setup for sliderTextLabel 197 | sliderTextLabel.topAnchor.constraint(equalTo: textLabel.topAnchor).isActive = true 198 | sliderTextLabel.centerYAnchor.constraint(equalTo: textLabel.centerYAnchor).isActive = true 199 | sliderTextLabel.leadingAnchor.constraint(equalTo: textLabel.leadingAnchor).isActive = true 200 | sliderTextLabel.trailingAnchor.constraint(equalTo: textLabel.trailingAnchor).isActive = true 201 | // Setup for Dragged View 202 | draggedView.leadingAnchor.constraint(equalTo: sliderHolderView.leadingAnchor).isActive = true 203 | draggedView.topAnchor.constraint(equalTo: sliderHolderView.topAnchor).isActive = true 204 | draggedView.centerYAnchor.constraint(equalTo: sliderHolderView.centerYAnchor).isActive = true 205 | trailingDraggedViewConstraint = draggedView.trailingAnchor.constraint(equalTo: thumnailImageView.trailingAnchor, constant: thumbnailViewStartingDistance) 206 | trailingDraggedViewConstraint?.isActive = true 207 | } 208 | 209 | private func setStyle() { 210 | thumnailImageView.backgroundColor = thumbnailColor 211 | textLabel.text = labelText 212 | textLabel.font = textFont 213 | textLabel.textColor = textColor 214 | textLabel.textAlignment = .center 215 | 216 | sliderTextLabel.text = labelText 217 | sliderTextLabel.font = textFont 218 | sliderTextLabel.textColor = sliderBackgroundColor 219 | sliderTextLabel.textAlignment = .center 220 | sliderTextLabel.isHidden = !showSliderText 221 | 222 | if isOnRightToLeftLanguage() { 223 | textLabel.mt_flipView() 224 | sliderTextLabel.mt_flipView() 225 | } 226 | 227 | sliderHolderView.backgroundColor = sliderBackgroundColor 228 | sliderHolderView.layer.cornerRadius = sliderCornerRadius 229 | draggedView.backgroundColor = slidingColor 230 | draggedView.layer.cornerRadius = sliderCornerRadius 231 | draggedView.clipsToBounds = true 232 | draggedView.layer.masksToBounds = true 233 | } 234 | 235 | private func isTapOnThumbnailViewWithPoint(_ point: CGPoint) -> Bool{ 236 | return self.thumnailImageView.frame.contains(point) 237 | } 238 | 239 | private func updateThumbnailXPosition(_ x: CGFloat) { 240 | leadingThumbnailViewConstraint?.constant = x 241 | setNeedsLayout() 242 | } 243 | 244 | // MARK: UIPanGestureRecognizer 245 | @objc private func handlePanGesture(_ sender: UIPanGestureRecognizer) { 246 | if isFinished || !isEnabled { 247 | return 248 | } 249 | let translatedPoint = sender.translation(in: view).x * (self.isOnRightToLeftLanguage() ? -1 : 1) 250 | switch sender.state { 251 | case .began: 252 | break 253 | case .changed: 254 | if translatedPoint >= xEndingPoint { 255 | updateThumbnailXPosition(xEndingPoint) 256 | return 257 | } 258 | if translatedPoint <= thumbnailViewStartingDistance { 259 | textLabel.alpha = 1 260 | updateThumbnailXPosition(thumbnailViewStartingDistance) 261 | return 262 | } 263 | updateThumbnailXPosition(translatedPoint) 264 | textLabel.alpha = (xEndingPoint - translatedPoint) / xEndingPoint 265 | break 266 | case .ended: 267 | if translatedPoint >= xEndingPoint { 268 | textLabel.alpha = 0 269 | updateThumbnailXPosition(xEndingPoint) 270 | // Finish action 271 | isFinished = true 272 | delegate?.mtSlideToOpenDelegateDidFinish(self) 273 | return 274 | } 275 | if translatedPoint <= thumbnailViewStartingDistance { 276 | textLabel.alpha = 1 277 | updateThumbnailXPosition(thumbnailViewStartingDistance) 278 | return 279 | } 280 | UIView.animate(withDuration: animationVelocity) { 281 | self.leadingThumbnailViewConstraint?.constant = self.thumbnailViewStartingDistance 282 | self.textLabel.alpha = 1 283 | self.layoutIfNeeded() 284 | } 285 | break 286 | default: 287 | break 288 | } 289 | } 290 | // Others 291 | public func resetStateWithAnimation(_ animated: Bool) { 292 | let action = { 293 | self.leadingThumbnailViewConstraint?.constant = self.thumbnailViewStartingDistance 294 | self.textLabel.alpha = 1 295 | self.layoutIfNeeded() 296 | // 297 | self.isFinished = false 298 | } 299 | if animated { 300 | UIView.animate(withDuration: animationVelocity) { 301 | action() 302 | } 303 | } else { 304 | action() 305 | } 306 | } 307 | 308 | // MARK: Helpers 309 | 310 | func isOnRightToLeftLanguage() -> Bool { 311 | return UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft 312 | } 313 | } 314 | 315 | extension UIView { 316 | func mt_flipView() { 317 | self.transform = CGAffineTransform(scaleX: -1.0, y: 1.0) 318 | } 319 | } 320 | 321 | class MTRoundImageView: UIImageView { 322 | override func layoutSubviews() { 323 | super.layoutSubviews() 324 | let radius: CGFloat = self.bounds.size.width / 2.0 325 | self.layer.cornerRadius = radius 326 | } 327 | } 328 | --------------------------------------------------------------------------------