├── .gitignore ├── ALSpotlightView.podspec ├── ALSpotlightView.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── ALSpotlightView ├── AppDelegate.swift ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json ├── Info.plist ├── Source │ └── ALSpotlightView.swift └── ViewController.swift ├── ALSpotlightViewTests ├── ALSpotlightViewTests.swift └── Info.plist ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | -------------------------------------------------------------------------------- /ALSpotlightView.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "ALSpotlightView" 4 | s.version = "0.0.2" 5 | s.summary = "Custom control that simulates a modal spotlight effect." 6 | 7 | s.homepage = "http://github.com/al7/ALSpotlightView" 8 | s.license = "MIT" 9 | s.author = { "Alex Leite" => "admin@al7dev.com" } 10 | s.platform = :ios, "8.0" 11 | s.source = { :git => "https://github.com/al7/ALSpotlightView.git", :tag => "0.0.3" } 12 | s.source_files = "ALSpotlightView/Source", "ALSpotlightView/Source/**/*.{h,m,swift}" 13 | s.requires_arc = true 14 | 15 | end 16 | -------------------------------------------------------------------------------- /ALSpotlightView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 137ACA5A1A9EEFC8006689AC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 137ACA591A9EEFC8006689AC /* AppDelegate.swift */; }; 11 | 137ACA5C1A9EEFC8006689AC /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 137ACA5B1A9EEFC8006689AC /* ViewController.swift */; }; 12 | 137ACA611A9EEFC8006689AC /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 137ACA601A9EEFC8006689AC /* Images.xcassets */; }; 13 | 137ACA701A9EEFC8006689AC /* ALSpotlightViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 137ACA6F1A9EEFC8006689AC /* ALSpotlightViewTests.swift */; }; 14 | 137ACA7B1A9EF27A006689AC /* ALSpotlightView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 137ACA7A1A9EF27A006689AC /* ALSpotlightView.swift */; }; 15 | /* End PBXBuildFile section */ 16 | 17 | /* Begin PBXContainerItemProxy section */ 18 | 137ACA6A1A9EEFC8006689AC /* PBXContainerItemProxy */ = { 19 | isa = PBXContainerItemProxy; 20 | containerPortal = 137ACA4C1A9EEFC8006689AC /* Project object */; 21 | proxyType = 1; 22 | remoteGlobalIDString = 137ACA531A9EEFC8006689AC; 23 | remoteInfo = ALSpotlightView; 24 | }; 25 | /* End PBXContainerItemProxy section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | 137ACA541A9EEFC8006689AC /* ALSpotlightView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ALSpotlightView.app; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | 137ACA581A9EEFC8006689AC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 30 | 137ACA591A9EEFC8006689AC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 31 | 137ACA5B1A9EEFC8006689AC /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 32 | 137ACA601A9EEFC8006689AC /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 33 | 137ACA691A9EEFC8006689AC /* ALSpotlightViewTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ALSpotlightViewTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | 137ACA6E1A9EEFC8006689AC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 35 | 137ACA6F1A9EEFC8006689AC /* ALSpotlightViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ALSpotlightViewTests.swift; sourceTree = ""; }; 36 | 137ACA7A1A9EF27A006689AC /* ALSpotlightView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ALSpotlightView.swift; sourceTree = ""; }; 37 | /* End PBXFileReference section */ 38 | 39 | /* Begin PBXFrameworksBuildPhase section */ 40 | 137ACA511A9EEFC8006689AC /* Frameworks */ = { 41 | isa = PBXFrameworksBuildPhase; 42 | buildActionMask = 2147483647; 43 | files = ( 44 | ); 45 | runOnlyForDeploymentPostprocessing = 0; 46 | }; 47 | 137ACA661A9EEFC8006689AC /* Frameworks */ = { 48 | isa = PBXFrameworksBuildPhase; 49 | buildActionMask = 2147483647; 50 | files = ( 51 | ); 52 | runOnlyForDeploymentPostprocessing = 0; 53 | }; 54 | /* End PBXFrameworksBuildPhase section */ 55 | 56 | /* Begin PBXGroup section */ 57 | 137ACA4B1A9EEFC8006689AC = { 58 | isa = PBXGroup; 59 | children = ( 60 | 137ACA561A9EEFC8006689AC /* ALSpotlightView */, 61 | 137ACA6C1A9EEFC8006689AC /* ALSpotlightViewTests */, 62 | 137ACA551A9EEFC8006689AC /* Products */, 63 | ); 64 | sourceTree = ""; 65 | }; 66 | 137ACA551A9EEFC8006689AC /* Products */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | 137ACA541A9EEFC8006689AC /* ALSpotlightView.app */, 70 | 137ACA691A9EEFC8006689AC /* ALSpotlightViewTests.xctest */, 71 | ); 72 | name = Products; 73 | sourceTree = ""; 74 | }; 75 | 137ACA561A9EEFC8006689AC /* ALSpotlightView */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | 137ACA791A9EF24F006689AC /* Source */, 79 | 137ACA591A9EEFC8006689AC /* AppDelegate.swift */, 80 | 137ACA5B1A9EEFC8006689AC /* ViewController.swift */, 81 | 137ACA601A9EEFC8006689AC /* Images.xcassets */, 82 | 137ACA571A9EEFC8006689AC /* Supporting Files */, 83 | ); 84 | path = ALSpotlightView; 85 | sourceTree = ""; 86 | }; 87 | 137ACA571A9EEFC8006689AC /* Supporting Files */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | 137ACA581A9EEFC8006689AC /* Info.plist */, 91 | ); 92 | name = "Supporting Files"; 93 | sourceTree = ""; 94 | }; 95 | 137ACA6C1A9EEFC8006689AC /* ALSpotlightViewTests */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 137ACA6F1A9EEFC8006689AC /* ALSpotlightViewTests.swift */, 99 | 137ACA6D1A9EEFC8006689AC /* Supporting Files */, 100 | ); 101 | path = ALSpotlightViewTests; 102 | sourceTree = ""; 103 | }; 104 | 137ACA6D1A9EEFC8006689AC /* Supporting Files */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 137ACA6E1A9EEFC8006689AC /* Info.plist */, 108 | ); 109 | name = "Supporting Files"; 110 | sourceTree = ""; 111 | }; 112 | 137ACA791A9EF24F006689AC /* Source */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | 137ACA7A1A9EF27A006689AC /* ALSpotlightView.swift */, 116 | ); 117 | path = Source; 118 | sourceTree = ""; 119 | }; 120 | /* End PBXGroup section */ 121 | 122 | /* Begin PBXNativeTarget section */ 123 | 137ACA531A9EEFC8006689AC /* ALSpotlightView */ = { 124 | isa = PBXNativeTarget; 125 | buildConfigurationList = 137ACA731A9EEFC8006689AC /* Build configuration list for PBXNativeTarget "ALSpotlightView" */; 126 | buildPhases = ( 127 | 137ACA501A9EEFC8006689AC /* Sources */, 128 | 137ACA511A9EEFC8006689AC /* Frameworks */, 129 | 137ACA521A9EEFC8006689AC /* Resources */, 130 | ); 131 | buildRules = ( 132 | ); 133 | dependencies = ( 134 | ); 135 | name = ALSpotlightView; 136 | productName = ALSpotlightView; 137 | productReference = 137ACA541A9EEFC8006689AC /* ALSpotlightView.app */; 138 | productType = "com.apple.product-type.application"; 139 | }; 140 | 137ACA681A9EEFC8006689AC /* ALSpotlightViewTests */ = { 141 | isa = PBXNativeTarget; 142 | buildConfigurationList = 137ACA761A9EEFC8006689AC /* Build configuration list for PBXNativeTarget "ALSpotlightViewTests" */; 143 | buildPhases = ( 144 | 137ACA651A9EEFC8006689AC /* Sources */, 145 | 137ACA661A9EEFC8006689AC /* Frameworks */, 146 | 137ACA671A9EEFC8006689AC /* Resources */, 147 | ); 148 | buildRules = ( 149 | ); 150 | dependencies = ( 151 | 137ACA6B1A9EEFC8006689AC /* PBXTargetDependency */, 152 | ); 153 | name = ALSpotlightViewTests; 154 | productName = ALSpotlightViewTests; 155 | productReference = 137ACA691A9EEFC8006689AC /* ALSpotlightViewTests.xctest */; 156 | productType = "com.apple.product-type.bundle.unit-test"; 157 | }; 158 | /* End PBXNativeTarget section */ 159 | 160 | /* Begin PBXProject section */ 161 | 137ACA4C1A9EEFC8006689AC /* Project object */ = { 162 | isa = PBXProject; 163 | attributes = { 164 | LastUpgradeCheck = 0610; 165 | ORGANIZATIONNAME = "Alexandre Leite"; 166 | TargetAttributes = { 167 | 137ACA531A9EEFC8006689AC = { 168 | CreatedOnToolsVersion = 6.1.1; 169 | }; 170 | 137ACA681A9EEFC8006689AC = { 171 | CreatedOnToolsVersion = 6.1.1; 172 | TestTargetID = 137ACA531A9EEFC8006689AC; 173 | }; 174 | }; 175 | }; 176 | buildConfigurationList = 137ACA4F1A9EEFC8006689AC /* Build configuration list for PBXProject "ALSpotlightView" */; 177 | compatibilityVersion = "Xcode 3.2"; 178 | developmentRegion = English; 179 | hasScannedForEncodings = 0; 180 | knownRegions = ( 181 | en, 182 | Base, 183 | ); 184 | mainGroup = 137ACA4B1A9EEFC8006689AC; 185 | productRefGroup = 137ACA551A9EEFC8006689AC /* Products */; 186 | projectDirPath = ""; 187 | projectRoot = ""; 188 | targets = ( 189 | 137ACA531A9EEFC8006689AC /* ALSpotlightView */, 190 | 137ACA681A9EEFC8006689AC /* ALSpotlightViewTests */, 191 | ); 192 | }; 193 | /* End PBXProject section */ 194 | 195 | /* Begin PBXResourcesBuildPhase section */ 196 | 137ACA521A9EEFC8006689AC /* Resources */ = { 197 | isa = PBXResourcesBuildPhase; 198 | buildActionMask = 2147483647; 199 | files = ( 200 | 137ACA611A9EEFC8006689AC /* Images.xcassets in Resources */, 201 | ); 202 | runOnlyForDeploymentPostprocessing = 0; 203 | }; 204 | 137ACA671A9EEFC8006689AC /* Resources */ = { 205 | isa = PBXResourcesBuildPhase; 206 | buildActionMask = 2147483647; 207 | files = ( 208 | ); 209 | runOnlyForDeploymentPostprocessing = 0; 210 | }; 211 | /* End PBXResourcesBuildPhase section */ 212 | 213 | /* Begin PBXSourcesBuildPhase section */ 214 | 137ACA501A9EEFC8006689AC /* Sources */ = { 215 | isa = PBXSourcesBuildPhase; 216 | buildActionMask = 2147483647; 217 | files = ( 218 | 137ACA7B1A9EF27A006689AC /* ALSpotlightView.swift in Sources */, 219 | 137ACA5C1A9EEFC8006689AC /* ViewController.swift in Sources */, 220 | 137ACA5A1A9EEFC8006689AC /* AppDelegate.swift in Sources */, 221 | ); 222 | runOnlyForDeploymentPostprocessing = 0; 223 | }; 224 | 137ACA651A9EEFC8006689AC /* Sources */ = { 225 | isa = PBXSourcesBuildPhase; 226 | buildActionMask = 2147483647; 227 | files = ( 228 | 137ACA701A9EEFC8006689AC /* ALSpotlightViewTests.swift in Sources */, 229 | ); 230 | runOnlyForDeploymentPostprocessing = 0; 231 | }; 232 | /* End PBXSourcesBuildPhase section */ 233 | 234 | /* Begin PBXTargetDependency section */ 235 | 137ACA6B1A9EEFC8006689AC /* PBXTargetDependency */ = { 236 | isa = PBXTargetDependency; 237 | target = 137ACA531A9EEFC8006689AC /* ALSpotlightView */; 238 | targetProxy = 137ACA6A1A9EEFC8006689AC /* PBXContainerItemProxy */; 239 | }; 240 | /* End PBXTargetDependency section */ 241 | 242 | /* Begin XCBuildConfiguration section */ 243 | 137ACA711A9EEFC8006689AC /* Debug */ = { 244 | isa = XCBuildConfiguration; 245 | buildSettings = { 246 | ALWAYS_SEARCH_USER_PATHS = NO; 247 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 248 | CLANG_CXX_LIBRARY = "libc++"; 249 | CLANG_ENABLE_MODULES = YES; 250 | CLANG_ENABLE_OBJC_ARC = YES; 251 | CLANG_WARN_BOOL_CONVERSION = YES; 252 | CLANG_WARN_CONSTANT_CONVERSION = YES; 253 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 254 | CLANG_WARN_EMPTY_BODY = YES; 255 | CLANG_WARN_ENUM_CONVERSION = YES; 256 | CLANG_WARN_INT_CONVERSION = YES; 257 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 258 | CLANG_WARN_UNREACHABLE_CODE = YES; 259 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 260 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 261 | COPY_PHASE_STRIP = NO; 262 | ENABLE_STRICT_OBJC_MSGSEND = YES; 263 | GCC_C_LANGUAGE_STANDARD = gnu99; 264 | GCC_DYNAMIC_NO_PIC = NO; 265 | GCC_OPTIMIZATION_LEVEL = 0; 266 | GCC_PREPROCESSOR_DEFINITIONS = ( 267 | "DEBUG=1", 268 | "$(inherited)", 269 | ); 270 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 271 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 272 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 273 | GCC_WARN_UNDECLARED_SELECTOR = YES; 274 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 275 | GCC_WARN_UNUSED_FUNCTION = YES; 276 | GCC_WARN_UNUSED_VARIABLE = YES; 277 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 278 | MTL_ENABLE_DEBUG_INFO = YES; 279 | ONLY_ACTIVE_ARCH = YES; 280 | SDKROOT = iphoneos; 281 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 282 | TARGETED_DEVICE_FAMILY = "1,2"; 283 | }; 284 | name = Debug; 285 | }; 286 | 137ACA721A9EEFC8006689AC /* Release */ = { 287 | isa = XCBuildConfiguration; 288 | buildSettings = { 289 | ALWAYS_SEARCH_USER_PATHS = NO; 290 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 291 | CLANG_CXX_LIBRARY = "libc++"; 292 | CLANG_ENABLE_MODULES = YES; 293 | CLANG_ENABLE_OBJC_ARC = YES; 294 | CLANG_WARN_BOOL_CONVERSION = YES; 295 | CLANG_WARN_CONSTANT_CONVERSION = YES; 296 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 297 | CLANG_WARN_EMPTY_BODY = YES; 298 | CLANG_WARN_ENUM_CONVERSION = YES; 299 | CLANG_WARN_INT_CONVERSION = YES; 300 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 301 | CLANG_WARN_UNREACHABLE_CODE = YES; 302 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 303 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 304 | COPY_PHASE_STRIP = YES; 305 | ENABLE_NS_ASSERTIONS = NO; 306 | ENABLE_STRICT_OBJC_MSGSEND = YES; 307 | GCC_C_LANGUAGE_STANDARD = gnu99; 308 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 309 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 310 | GCC_WARN_UNDECLARED_SELECTOR = YES; 311 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 312 | GCC_WARN_UNUSED_FUNCTION = YES; 313 | GCC_WARN_UNUSED_VARIABLE = YES; 314 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 315 | MTL_ENABLE_DEBUG_INFO = NO; 316 | SDKROOT = iphoneos; 317 | TARGETED_DEVICE_FAMILY = "1,2"; 318 | VALIDATE_PRODUCT = YES; 319 | }; 320 | name = Release; 321 | }; 322 | 137ACA741A9EEFC8006689AC /* Debug */ = { 323 | isa = XCBuildConfiguration; 324 | buildSettings = { 325 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 326 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 327 | INFOPLIST_FILE = ALSpotlightView/Info.plist; 328 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 329 | PRODUCT_NAME = "$(TARGET_NAME)"; 330 | }; 331 | name = Debug; 332 | }; 333 | 137ACA751A9EEFC8006689AC /* Release */ = { 334 | isa = XCBuildConfiguration; 335 | buildSettings = { 336 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 337 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 338 | INFOPLIST_FILE = ALSpotlightView/Info.plist; 339 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 340 | PRODUCT_NAME = "$(TARGET_NAME)"; 341 | }; 342 | name = Release; 343 | }; 344 | 137ACA771A9EEFC8006689AC /* Debug */ = { 345 | isa = XCBuildConfiguration; 346 | buildSettings = { 347 | BUNDLE_LOADER = "$(TEST_HOST)"; 348 | FRAMEWORK_SEARCH_PATHS = ( 349 | "$(SDKROOT)/Developer/Library/Frameworks", 350 | "$(inherited)", 351 | ); 352 | GCC_PREPROCESSOR_DEFINITIONS = ( 353 | "DEBUG=1", 354 | "$(inherited)", 355 | ); 356 | INFOPLIST_FILE = ALSpotlightViewTests/Info.plist; 357 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 358 | PRODUCT_NAME = "$(TARGET_NAME)"; 359 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ALSpotlightView.app/ALSpotlightView"; 360 | }; 361 | name = Debug; 362 | }; 363 | 137ACA781A9EEFC8006689AC /* Release */ = { 364 | isa = XCBuildConfiguration; 365 | buildSettings = { 366 | BUNDLE_LOADER = "$(TEST_HOST)"; 367 | FRAMEWORK_SEARCH_PATHS = ( 368 | "$(SDKROOT)/Developer/Library/Frameworks", 369 | "$(inherited)", 370 | ); 371 | INFOPLIST_FILE = ALSpotlightViewTests/Info.plist; 372 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 373 | PRODUCT_NAME = "$(TARGET_NAME)"; 374 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ALSpotlightView.app/ALSpotlightView"; 375 | }; 376 | name = Release; 377 | }; 378 | /* End XCBuildConfiguration section */ 379 | 380 | /* Begin XCConfigurationList section */ 381 | 137ACA4F1A9EEFC8006689AC /* Build configuration list for PBXProject "ALSpotlightView" */ = { 382 | isa = XCConfigurationList; 383 | buildConfigurations = ( 384 | 137ACA711A9EEFC8006689AC /* Debug */, 385 | 137ACA721A9EEFC8006689AC /* Release */, 386 | ); 387 | defaultConfigurationIsVisible = 0; 388 | defaultConfigurationName = Release; 389 | }; 390 | 137ACA731A9EEFC8006689AC /* Build configuration list for PBXNativeTarget "ALSpotlightView" */ = { 391 | isa = XCConfigurationList; 392 | buildConfigurations = ( 393 | 137ACA741A9EEFC8006689AC /* Debug */, 394 | 137ACA751A9EEFC8006689AC /* Release */, 395 | ); 396 | defaultConfigurationIsVisible = 0; 397 | }; 398 | 137ACA761A9EEFC8006689AC /* Build configuration list for PBXNativeTarget "ALSpotlightViewTests" */ = { 399 | isa = XCConfigurationList; 400 | buildConfigurations = ( 401 | 137ACA771A9EEFC8006689AC /* Debug */, 402 | 137ACA781A9EEFC8006689AC /* Release */, 403 | ); 404 | defaultConfigurationIsVisible = 0; 405 | }; 406 | /* End XCConfigurationList section */ 407 | }; 408 | rootObject = 137ACA4C1A9EEFC8006689AC /* Project object */; 409 | } 410 | -------------------------------------------------------------------------------- /ALSpotlightView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ALSpotlightView/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015 al7dev - Alex Leite. All rights reserved. 4 | 5 | */ 6 | 7 | import UIKit 8 | 9 | @UIApplicationMain 10 | class AppDelegate: UIResponder, UIApplicationDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 16 | self.window = UIWindow(frame: UIScreen.mainScreen().bounds) 17 | self.window?.backgroundColor = UIColor.whiteColor() 18 | self.window?.rootViewController = ViewController() 19 | self.window?.makeKeyAndVisible() 20 | 21 | return true 22 | } 23 | 24 | func applicationWillResignActive(application: UIApplication) { 25 | 26 | } 27 | 28 | func applicationDidEnterBackground(application: UIApplication) { 29 | 30 | } 31 | 32 | func applicationWillEnterForeground(application: UIApplication) { 33 | 34 | } 35 | 36 | func applicationDidBecomeActive(application: UIApplication) { 37 | 38 | } 39 | 40 | func applicationWillTerminate(application: UIApplication) { 41 | 42 | } 43 | 44 | } 45 | 46 | -------------------------------------------------------------------------------- /ALSpotlightView/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /ALSpotlightView/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "8.0", 8 | "subtype" : "736h", 9 | "scale" : "3x" 10 | }, 11 | { 12 | "orientation" : "landscape", 13 | "idiom" : "iphone", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "8.0", 16 | "subtype" : "736h", 17 | "scale" : "3x" 18 | }, 19 | { 20 | "orientation" : "portrait", 21 | "idiom" : "iphone", 22 | "extent" : "full-screen", 23 | "minimum-system-version" : "8.0", 24 | "subtype" : "667h", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "orientation" : "portrait", 29 | "idiom" : "iphone", 30 | "extent" : "full-screen", 31 | "minimum-system-version" : "7.0", 32 | "scale" : "2x" 33 | }, 34 | { 35 | "orientation" : "portrait", 36 | "idiom" : "iphone", 37 | "extent" : "full-screen", 38 | "minimum-system-version" : "7.0", 39 | "subtype" : "retina4", 40 | "scale" : "2x" 41 | }, 42 | { 43 | "orientation" : "portrait", 44 | "idiom" : "ipad", 45 | "extent" : "full-screen", 46 | "minimum-system-version" : "7.0", 47 | "scale" : "1x" 48 | }, 49 | { 50 | "orientation" : "landscape", 51 | "idiom" : "ipad", 52 | "extent" : "full-screen", 53 | "minimum-system-version" : "7.0", 54 | "scale" : "1x" 55 | }, 56 | { 57 | "orientation" : "portrait", 58 | "idiom" : "ipad", 59 | "extent" : "full-screen", 60 | "minimum-system-version" : "7.0", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "orientation" : "landscape", 65 | "idiom" : "ipad", 66 | "extent" : "full-screen", 67 | "minimum-system-version" : "7.0", 68 | "scale" : "2x" 69 | }, 70 | { 71 | "orientation" : "portrait", 72 | "idiom" : "iphone", 73 | "extent" : "full-screen", 74 | "scale" : "1x" 75 | }, 76 | { 77 | "orientation" : "portrait", 78 | "idiom" : "iphone", 79 | "extent" : "full-screen", 80 | "scale" : "2x" 81 | }, 82 | { 83 | "orientation" : "portrait", 84 | "idiom" : "iphone", 85 | "extent" : "full-screen", 86 | "subtype" : "retina4", 87 | "scale" : "2x" 88 | }, 89 | { 90 | "orientation" : "portrait", 91 | "idiom" : "ipad", 92 | "extent" : "to-status-bar", 93 | "scale" : "1x" 94 | }, 95 | { 96 | "orientation" : "portrait", 97 | "idiom" : "ipad", 98 | "extent" : "full-screen", 99 | "scale" : "1x" 100 | }, 101 | { 102 | "orientation" : "landscape", 103 | "idiom" : "ipad", 104 | "extent" : "to-status-bar", 105 | "scale" : "1x" 106 | }, 107 | { 108 | "orientation" : "landscape", 109 | "idiom" : "ipad", 110 | "extent" : "full-screen", 111 | "scale" : "1x" 112 | }, 113 | { 114 | "orientation" : "portrait", 115 | "idiom" : "ipad", 116 | "extent" : "to-status-bar", 117 | "scale" : "2x" 118 | }, 119 | { 120 | "orientation" : "portrait", 121 | "idiom" : "ipad", 122 | "extent" : "full-screen", 123 | "scale" : "2x" 124 | }, 125 | { 126 | "orientation" : "landscape", 127 | "idiom" : "ipad", 128 | "extent" : "to-status-bar", 129 | "scale" : "2x" 130 | }, 131 | { 132 | "orientation" : "landscape", 133 | "idiom" : "ipad", 134 | "extent" : "full-screen", 135 | "scale" : "2x" 136 | } 137 | ], 138 | "info" : { 139 | "version" : 1, 140 | "author" : "xcode" 141 | } 142 | } -------------------------------------------------------------------------------- /ALSpotlightView/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.al7dev.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 0.0.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /ALSpotlightView/Source/ALSpotlightView.swift: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015 - Alex Leite (al7dev) 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 13 | all 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 21 | THE SOFTWARE. 22 | 23 | */ 24 | 25 | import UIKit 26 | 27 | public typealias SpotlightTapHandler = () -> Void 28 | 29 | public class ALSpotlightView: UIView { 30 | 31 | public var spotlightCenter: CGPoint = CGPoint(x: 0.5, y: 0.5) 32 | public var spotlightRadius: CGFloat = 200.0 33 | public var modalOpacity: CGFloat = 0.6 34 | private weak var _targetView: UIView? 35 | public var targetView: UIView? { get { return _targetView } } 36 | public var onTapHandler: SpotlightTapHandler? 37 | 38 | public init(spotlightCenter: CGPoint, spotlightRadius: CGFloat = 200.0, modalOpacity: CGFloat = 0.6, onTapHandler: SpotlightTapHandler? = nil) { 39 | super.init(frame: CGRectZero) 40 | self.backgroundColor = UIColor.clearColor() 41 | self.autoresizingMask = [.FlexibleWidth, .FlexibleHeight] 42 | self.spotlightRadius = spotlightRadius 43 | self.spotlightCenter = spotlightCenter 44 | self.modalOpacity = modalOpacity 45 | self.onTapHandler = onTapHandler 46 | } 47 | 48 | override public init(frame: CGRect) { 49 | fatalError("init(frame:) not available. Please use designated initializer") 50 | } 51 | 52 | required public init(coder aDecoder: NSCoder) { 53 | fatalError("init(coder:) has not been implemented") 54 | } 55 | 56 | //MARK- Base Overrides 57 | 58 | public override func drawRect(rect: CGRect) { 59 | let context = UIGraphicsGetCurrentContext() 60 | let colorSpace = CGColorSpaceCreateDeviceRGB() 61 | let locations: [CGFloat] = [0.0, 1.0] 62 | let colors: [CGFloat] = [ 63 | 0.0, 0.0, 0.0, 0.0, 64 | 0.0, 0.0, 0.0, self.modalOpacity 65 | ] 66 | let gradient = CGGradientCreateWithColorComponents(colorSpace, colors, locations, 2) 67 | 68 | let gradientCenter = CGPoint(x: self.spotlightCenter.x * rect.size.width, y: self.spotlightCenter.y * rect.size.height) 69 | let startPoint = gradientCenter 70 | let startRadius: CGFloat = 0.0 71 | let endPoint = gradientCenter 72 | let endRadius = self.spotlightRadius 73 | 74 | CGContextDrawRadialGradient(context, gradient, startPoint, startRadius, endPoint, endRadius, .DrawsAfterEndLocation) 75 | } 76 | 77 | public override func layoutSubviews() { 78 | self.setNeedsDisplay() 79 | } 80 | 81 | //MARK- Public Methods 82 | 83 | public func showInView(view: UIView?, animated: Bool = true, duration: NSTimeInterval = 0.3) { 84 | self._targetView = (view != nil) ? view : UIApplication.sharedApplication().keyWindow 85 | if let tv = self.targetView { 86 | self.alpha = 0.0 87 | self.frame = tv.bounds 88 | self.setNeedsDisplay() 89 | tv.addSubview(self) 90 | 91 | let animation: () -> Void = { 92 | self.alpha = 1.0 93 | } 94 | 95 | let completion: (Bool) -> Void = { 96 | finished in 97 | 98 | let tapRecognizer = UITapGestureRecognizer(target: self, action: "onSpotlightViewTap:") 99 | tapRecognizer.numberOfTapsRequired = 1 100 | self.addGestureRecognizer(tapRecognizer) 101 | } 102 | 103 | if animated { 104 | UIView.animateWithDuration(duration, animations: animation, completion: completion) 105 | } 106 | else { 107 | animation() 108 | } 109 | } 110 | } 111 | 112 | public func show(animated: Bool = true) { 113 | self.showInView(nil, animated: animated) 114 | } 115 | 116 | public func hide(animated: Bool = true, duration: NSTimeInterval = 0.3) { 117 | self.clearGestureRecognizers() 118 | self.onTapHandler = nil 119 | 120 | let animation: () -> Void = { 121 | self.alpha = 0.0 122 | } 123 | 124 | let completion: (Bool) -> Void = { 125 | finished in 126 | 127 | self._targetView = nil 128 | self.removeFromSuperview() 129 | } 130 | 131 | if animated { 132 | UIView.animateWithDuration(duration, animations: animation, completion: completion) 133 | } 134 | else { 135 | animation() 136 | completion(true) 137 | } 138 | } 139 | 140 | //MARK- Helper Methods 141 | 142 | private func clearGestureRecognizers() { 143 | if let recognizers = self.gestureRecognizers { 144 | for recognizer in recognizers { 145 | self.removeGestureRecognizer(recognizer) 146 | } 147 | } 148 | } 149 | 150 | //MARK- Action Targets 151 | 152 | func onSpotlightViewTap(sender: UITapGestureRecognizer) { 153 | if let handler = self.onTapHandler { 154 | handler() 155 | } 156 | } 157 | 158 | } -------------------------------------------------------------------------------- /ALSpotlightView/ViewController.swift: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015 al7dev - Alex Leite. All rights reserved. 4 | 5 | */ 6 | 7 | import UIKit 8 | 9 | class ViewController: UIViewController { 10 | 11 | //MARK- View lifecycle 12 | 13 | override func loadView() { 14 | self.view = UIView(frame: UIScreen.mainScreen().bounds) 15 | self.view.backgroundColor = UIColor.whiteColor() 16 | 17 | let label = UILabel(frame: self.view.bounds) 18 | label.textAlignment = .Center 19 | label.autoresizingMask = .FlexibleWidth | .FlexibleHeight 20 | label.numberOfLines = 0 21 | label.lineBreakMode = .ByWordWrapping 22 | label.text = "Tap Anywhere in screen\nto see Spotlight View" 23 | self.view.addSubview(label) 24 | } 25 | 26 | override func viewDidLoad() { 27 | super.viewDidLoad() 28 | 29 | let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: "onTap:") 30 | tapGestureRecognizer.numberOfTapsRequired = 1 31 | self.view.addGestureRecognizer(tapGestureRecognizer) 32 | } 33 | 34 | //MARK- Tap Gesture Recognizer 35 | 36 | func onTap(sender: UITapGestureRecognizer) { 37 | let location = sender.locationInView(self.view) 38 | var spotlightCenter = CGPointZero 39 | spotlightCenter.x = location.x / self.view.bounds.size.width 40 | spotlightCenter.y = location.y / self.view.bounds.size.height 41 | 42 | let spotlightView = ALSpotlightView(spotlightCenter: spotlightCenter) 43 | spotlightView.onTapHandler = { 44 | spotlightView.hide() 45 | } 46 | spotlightView.show() 47 | } 48 | 49 | } 50 | 51 | -------------------------------------------------------------------------------- /ALSpotlightViewTests/ALSpotlightViewTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ALSpotlightViewTests.swift 3 | // ALSpotlightViewTests 4 | // 5 | // Created by Alexandre Leite on 2/25/15. 6 | // Copyright (c) 2015 Alexandre Leite. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import XCTest 11 | 12 | class ALSpotlightViewTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | XCTAssert(true, "Pass") 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measureBlock() { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /ALSpotlightViewTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.al7dev.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 - Alex Leite (al7dev) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ALSpotlightView 2 | ===================== 3 | 4 | ### About 5 | 6 | This custom control that simulates a modal spotlight effect. It can be used to, among other things, highlight a certain portion of your screen. It is completely written in Swift, and pretty simple to use. See the code below: 7 | 8 | ### Sample Code 9 | 10 | ```swift 11 | //-- Initializing a spotlight view (shortest initialization): 12 | let spotlightView = ALSpotlightView(spotlightCenter: CGPoint(x: 0.5, y: 0,5)) 13 | 14 | //-- Setting a tap handler (to hide when tapped) 15 | spotlightView.onTapHandler = { 16 | spotlightView.hide } 17 | 18 | //-- Presenting the spotlight view: 19 | spotlightView.show() 20 | ``` 21 | 22 | ### Customizing appearance and behavior 23 | 24 | There are several ways you can customize the way this control looks and behaves. Here are a few things you can do: 25 | 26 | #### Initialization: 27 | In the sample code above, I showed how to initialize the control in the simplest way. Here's the full control initialization signature: 28 | 29 | ```swift 30 | //-- Full Initialization: 31 | 32 | let spotlightView = ALSpotlightView(spotlightCenter: center, 33 | spotlightRadius: radius, 34 | modalOpacity: opacity, 35 | onTapHandler: nil) 36 | ``` 37 | The control has the following properties: 38 | 39 | - ``spotlightCenter``: a CGPoint that indicates the spotlight's center *x* and *y* coordinates relative to its superview. Values for each coordinate range between *0.0* and *1.0*. For example, to display the spotlight at the center of the window, set ``spotlightCenter`` to *CGPoint(x: 0.5, y: 0.5)* 40 | - ``spotlightRadius``: a CGFloat indicating the radius of the spotlight effect in points. 41 | - ``modalOpacity``: a CGFloat between *0.0* and *1.0*, adjusts the alpha value of the area around the spotlight effect. 42 | - `onTapHandler`: closure with the following signature: ``() -> Void``, handles tap behavior after the spotlight view is visible. 43 | 44 | #### Showing and hiding: 45 | 46 | After initializing the control, you can show it and hide it just by calling simply `show()` or `hide()`. To have more control, these methods will give you a few other options, like if you want the show/hide effect to be animated, and the duration of the transition. 47 | 48 | On the ``showInView:`` function, you can also determine in which view you want the control to show. If you don't specify, it automatically appears on the app's key window. Here is the full signature to the functions: 49 | 50 | ```swift 51 | //-- Showing spotlight view on a custom view: 52 | spotlightView.showInView(view, animated: true) 53 | 54 | //-- Showing spotlight view in one second: 55 | spotlightView.show(animated: true, duration: 1.0) 56 | 57 | //-- Hiding spotlight view without animation: 58 | spotlightView.hide(animated: false) 59 | ``` 60 | 61 | These examples should be pretty straight-forward. In any case, I intend to elaborate more on these instructions in the future. Feel free to explore with the control, and it's sample code. 62 | 63 | ### How to use 64 | 65 | To use this control, you can either copy the *APSpotlightView.swift* class to your project, or add the following pod to your project, using CocoaPods 0.36 and up: 66 | 67 | ``` 68 | pod 'ALSpotlightView' 69 | ``` 70 | 71 | ### License 72 | 73 | This component is available under MIT license. --------------------------------------------------------------------------------