├── .DS_Store ├── .gitignore ├── .swift-version ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTIONS.md ├── Claps ├── Claps.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata └── Claps │ ├── AppDelegate.swift │ ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── ClapsView.swift │ ├── Images │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ └── 5.jpg │ ├── Info.plist │ └── ViewController.swift ├── ClapsView.podspec ├── LICENSE ├── README.md └── Screenshots ├── ClapsViewUsage.gif ├── Screenshot-1.png └── Screenshot-2.png /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hemangshah/ClapsView/94984769cc7f4e45d27056b4f41ca03867b63d72/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/swift,xcode,objective-c 2 | 3 | ### Objective-C ### 4 | # Xcode 5 | # 6 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 7 | 8 | ## Build generated 9 | build/ 10 | DerivedData/ 11 | 12 | ## Various settings 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata/ 22 | 23 | ## Other 24 | *.moved-aside 25 | *.xccheckout 26 | *.xcscmblueprint 27 | 28 | ## Obj-C/Swift specific 29 | *.hmap 30 | *.ipa 31 | *.dSYM.zip 32 | *.dSYM 33 | 34 | # CocoaPods 35 | # 36 | # We recommend against adding the Pods directory to your .gitignore. However 37 | # you should judge for yourself, the pros and cons are mentioned at: 38 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 39 | # 40 | # Pods/ 41 | 42 | # Carthage 43 | # 44 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 45 | # Carthage/Checkouts 46 | 47 | Carthage/Build 48 | 49 | # fastlane 50 | # 51 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 52 | # screenshots whenever they are needed. 53 | # For more information about the recommended setup visit: 54 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 55 | 56 | fastlane/report.xml 57 | fastlane/Preview.html 58 | fastlane/screenshots 59 | fastlane/test_output 60 | 61 | # Code Injection 62 | # 63 | # After new code Injection tools there's a generated folder /iOSInjectionProject 64 | # https://github.com/johnno1962/injectionforxcode 65 | 66 | iOSInjectionProject/ 67 | 68 | ### Objective-C Patch ### 69 | 70 | ### Swift ### 71 | # Xcode 72 | # 73 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 74 | 75 | ## Build generated 76 | 77 | ## Various settings 78 | 79 | ## Other 80 | 81 | ## Obj-C/Swift specific 82 | 83 | ## Playgrounds 84 | timeline.xctimeline 85 | playground.xcworkspace 86 | 87 | # Swift Package Manager 88 | # 89 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 90 | # Packages/ 91 | # Package.pins 92 | .build/ 93 | 94 | # CocoaPods 95 | # 96 | # We recommend against adding the Pods directory to your .gitignore. However 97 | # you should judge for yourself, the pros and cons are mentioned at: 98 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 99 | # 100 | # Pods/ 101 | 102 | # Carthage 103 | # 104 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 105 | # Carthage/Checkouts 106 | 107 | 108 | # fastlane 109 | # 110 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 111 | # screenshots whenever they are needed. 112 | # For more information about the recommended setup visit: 113 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 114 | 115 | 116 | ### Xcode ### 117 | # Xcode 118 | # 119 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 120 | 121 | ## Build generated 122 | 123 | ## Various settings 124 | 125 | ## Other 126 | 127 | ### Xcode Patch ### 128 | *.xcodeproj/* 129 | !*.xcodeproj/project.pbxproj 130 | !*.xcodeproj/xcshareddata/ 131 | !*.xcworkspace/contents.xcworkspacedata 132 | /*.gcno 133 | 134 | # End of https://www.gitignore.io/api/swift,xcode,objective-c 135 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 4.0 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | osx_image: xcode9.0 2 | language: objective-c 3 | 4 | xcode_project: Claps.xcodeproj 5 | 6 | # whitelist 7 | branches: 8 | only: 9 | - master 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /CONTRIBUTIONS.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Claps/Claps.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | F1701B5C1F4953D90064BFF5 /* 1.jpg in Resources */ = {isa = PBXBuildFile; fileRef = F1701B571F4953D90064BFF5 /* 1.jpg */; }; 11 | F1701B5D1F4953D90064BFF5 /* 2.jpg in Resources */ = {isa = PBXBuildFile; fileRef = F1701B581F4953D90064BFF5 /* 2.jpg */; }; 12 | F1701B601F4953D90064BFF5 /* 5.jpg in Resources */ = {isa = PBXBuildFile; fileRef = F1701B5B1F4953D90064BFF5 /* 5.jpg */; }; 13 | F1701B631F4955C70064BFF5 /* 3.jpg in Resources */ = {isa = PBXBuildFile; fileRef = F1701B611F4955C70064BFF5 /* 3.jpg */; }; 14 | F1701B641F4955C70064BFF5 /* 4.jpg in Resources */ = {isa = PBXBuildFile; fileRef = F1701B621F4955C70064BFF5 /* 4.jpg */; }; 15 | F1C3EA251F45B45E0092CFCC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1C3EA241F45B45E0092CFCC /* AppDelegate.swift */; }; 16 | F1C3EA271F45B45E0092CFCC /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1C3EA261F45B45E0092CFCC /* ViewController.swift */; }; 17 | F1C3EA2A1F45B45E0092CFCC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F1C3EA281F45B45E0092CFCC /* Main.storyboard */; }; 18 | F1C3EA2C1F45B45E0092CFCC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F1C3EA2B1F45B45E0092CFCC /* Assets.xcassets */; }; 19 | F1C3EA2F1F45B45E0092CFCC /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F1C3EA2D1F45B45E0092CFCC /* LaunchScreen.storyboard */; }; 20 | F1C3EA371F45B4710092CFCC /* ClapsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1C3EA361F45B4710092CFCC /* ClapsView.swift */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | F1701B571F4953D90064BFF5 /* 1.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 1.jpg; sourceTree = ""; }; 25 | F1701B581F4953D90064BFF5 /* 2.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 2.jpg; sourceTree = ""; }; 26 | F1701B5B1F4953D90064BFF5 /* 5.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 5.jpg; sourceTree = ""; }; 27 | F1701B611F4955C70064BFF5 /* 3.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 3.jpg; sourceTree = ""; }; 28 | F1701B621F4955C70064BFF5 /* 4.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 4.jpg; sourceTree = ""; }; 29 | F1C3EA211F45B45E0092CFCC /* Claps.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Claps.app; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | F1C3EA241F45B45E0092CFCC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 31 | F1C3EA261F45B45E0092CFCC /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 32 | F1C3EA291F45B45E0092CFCC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 33 | F1C3EA2B1F45B45E0092CFCC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 34 | F1C3EA2E1F45B45E0092CFCC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 35 | F1C3EA301F45B45E0092CFCC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 36 | F1C3EA361F45B4710092CFCC /* ClapsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClapsView.swift; sourceTree = ""; }; 37 | /* End PBXFileReference section */ 38 | 39 | /* Begin PBXFrameworksBuildPhase section */ 40 | F1C3EA1E1F45B45E0092CFCC /* Frameworks */ = { 41 | isa = PBXFrameworksBuildPhase; 42 | buildActionMask = 2147483647; 43 | files = ( 44 | ); 45 | runOnlyForDeploymentPostprocessing = 0; 46 | }; 47 | /* End PBXFrameworksBuildPhase section */ 48 | 49 | /* Begin PBXGroup section */ 50 | F1701B561F4953D90064BFF5 /* Images */ = { 51 | isa = PBXGroup; 52 | children = ( 53 | F1701B571F4953D90064BFF5 /* 1.jpg */, 54 | F1701B581F4953D90064BFF5 /* 2.jpg */, 55 | F1701B611F4955C70064BFF5 /* 3.jpg */, 56 | F1701B621F4955C70064BFF5 /* 4.jpg */, 57 | F1701B5B1F4953D90064BFF5 /* 5.jpg */, 58 | ); 59 | path = Images; 60 | sourceTree = ""; 61 | }; 62 | F1C3EA181F45B45E0092CFCC = { 63 | isa = PBXGroup; 64 | children = ( 65 | F1C3EA231F45B45E0092CFCC /* Claps */, 66 | F1C3EA221F45B45E0092CFCC /* Products */, 67 | ); 68 | sourceTree = ""; 69 | }; 70 | F1C3EA221F45B45E0092CFCC /* Products */ = { 71 | isa = PBXGroup; 72 | children = ( 73 | F1C3EA211F45B45E0092CFCC /* Claps.app */, 74 | ); 75 | name = Products; 76 | sourceTree = ""; 77 | }; 78 | F1C3EA231F45B45E0092CFCC /* Claps */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | F1701B561F4953D90064BFF5 /* Images */, 82 | F1C3EA241F45B45E0092CFCC /* AppDelegate.swift */, 83 | F1C3EA261F45B45E0092CFCC /* ViewController.swift */, 84 | F1C3EA361F45B4710092CFCC /* ClapsView.swift */, 85 | F1C3EA281F45B45E0092CFCC /* Main.storyboard */, 86 | F1C3EA2B1F45B45E0092CFCC /* Assets.xcassets */, 87 | F1C3EA2D1F45B45E0092CFCC /* LaunchScreen.storyboard */, 88 | F1C3EA301F45B45E0092CFCC /* Info.plist */, 89 | ); 90 | path = Claps; 91 | sourceTree = ""; 92 | }; 93 | /* End PBXGroup section */ 94 | 95 | /* Begin PBXNativeTarget section */ 96 | F1C3EA201F45B45E0092CFCC /* Claps */ = { 97 | isa = PBXNativeTarget; 98 | buildConfigurationList = F1C3EA331F45B45E0092CFCC /* Build configuration list for PBXNativeTarget "Claps" */; 99 | buildPhases = ( 100 | F1C3EA1D1F45B45E0092CFCC /* Sources */, 101 | F1C3EA1E1F45B45E0092CFCC /* Frameworks */, 102 | F1C3EA1F1F45B45E0092CFCC /* Resources */, 103 | ); 104 | buildRules = ( 105 | ); 106 | dependencies = ( 107 | ); 108 | name = Claps; 109 | productName = Claps; 110 | productReference = F1C3EA211F45B45E0092CFCC /* Claps.app */; 111 | productType = "com.apple.product-type.application"; 112 | }; 113 | /* End PBXNativeTarget section */ 114 | 115 | /* Begin PBXProject section */ 116 | F1C3EA191F45B45E0092CFCC /* Project object */ = { 117 | isa = PBXProject; 118 | attributes = { 119 | LastSwiftUpdateCheck = 0900; 120 | LastUpgradeCheck = 0900; 121 | ORGANIZATIONNAME = "Hemang Shah"; 122 | TargetAttributes = { 123 | F1C3EA201F45B45E0092CFCC = { 124 | CreatedOnToolsVersion = 9.0; 125 | ProvisioningStyle = Automatic; 126 | }; 127 | }; 128 | }; 129 | buildConfigurationList = F1C3EA1C1F45B45E0092CFCC /* Build configuration list for PBXProject "Claps" */; 130 | compatibilityVersion = "Xcode 8.0"; 131 | developmentRegion = en; 132 | hasScannedForEncodings = 0; 133 | knownRegions = ( 134 | en, 135 | Base, 136 | ); 137 | mainGroup = F1C3EA181F45B45E0092CFCC; 138 | productRefGroup = F1C3EA221F45B45E0092CFCC /* Products */; 139 | projectDirPath = ""; 140 | projectRoot = ""; 141 | targets = ( 142 | F1C3EA201F45B45E0092CFCC /* Claps */, 143 | ); 144 | }; 145 | /* End PBXProject section */ 146 | 147 | /* Begin PBXResourcesBuildPhase section */ 148 | F1C3EA1F1F45B45E0092CFCC /* Resources */ = { 149 | isa = PBXResourcesBuildPhase; 150 | buildActionMask = 2147483647; 151 | files = ( 152 | F1701B641F4955C70064BFF5 /* 4.jpg in Resources */, 153 | F1C3EA2F1F45B45E0092CFCC /* LaunchScreen.storyboard in Resources */, 154 | F1701B601F4953D90064BFF5 /* 5.jpg in Resources */, 155 | F1701B5C1F4953D90064BFF5 /* 1.jpg in Resources */, 156 | F1701B5D1F4953D90064BFF5 /* 2.jpg in Resources */, 157 | F1C3EA2C1F45B45E0092CFCC /* Assets.xcassets in Resources */, 158 | F1701B631F4955C70064BFF5 /* 3.jpg in Resources */, 159 | F1C3EA2A1F45B45E0092CFCC /* Main.storyboard in Resources */, 160 | ); 161 | runOnlyForDeploymentPostprocessing = 0; 162 | }; 163 | /* End PBXResourcesBuildPhase section */ 164 | 165 | /* Begin PBXSourcesBuildPhase section */ 166 | F1C3EA1D1F45B45E0092CFCC /* Sources */ = { 167 | isa = PBXSourcesBuildPhase; 168 | buildActionMask = 2147483647; 169 | files = ( 170 | F1C3EA271F45B45E0092CFCC /* ViewController.swift in Sources */, 171 | F1C3EA251F45B45E0092CFCC /* AppDelegate.swift in Sources */, 172 | F1C3EA371F45B4710092CFCC /* ClapsView.swift in Sources */, 173 | ); 174 | runOnlyForDeploymentPostprocessing = 0; 175 | }; 176 | /* End PBXSourcesBuildPhase section */ 177 | 178 | /* Begin PBXVariantGroup section */ 179 | F1C3EA281F45B45E0092CFCC /* Main.storyboard */ = { 180 | isa = PBXVariantGroup; 181 | children = ( 182 | F1C3EA291F45B45E0092CFCC /* Base */, 183 | ); 184 | name = Main.storyboard; 185 | sourceTree = ""; 186 | }; 187 | F1C3EA2D1F45B45E0092CFCC /* LaunchScreen.storyboard */ = { 188 | isa = PBXVariantGroup; 189 | children = ( 190 | F1C3EA2E1F45B45E0092CFCC /* Base */, 191 | ); 192 | name = LaunchScreen.storyboard; 193 | sourceTree = ""; 194 | }; 195 | /* End PBXVariantGroup section */ 196 | 197 | /* Begin XCBuildConfiguration section */ 198 | F1C3EA311F45B45E0092CFCC /* Debug */ = { 199 | isa = XCBuildConfiguration; 200 | buildSettings = { 201 | ALWAYS_SEARCH_USER_PATHS = NO; 202 | CLANG_ANALYZER_NONNULL = YES; 203 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 204 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 205 | CLANG_CXX_LIBRARY = "libc++"; 206 | CLANG_ENABLE_MODULES = YES; 207 | CLANG_ENABLE_OBJC_ARC = YES; 208 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 209 | CLANG_WARN_BOOL_CONVERSION = YES; 210 | CLANG_WARN_COMMA = YES; 211 | CLANG_WARN_CONSTANT_CONVERSION = YES; 212 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 213 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 214 | CLANG_WARN_EMPTY_BODY = YES; 215 | CLANG_WARN_ENUM_CONVERSION = YES; 216 | CLANG_WARN_INFINITE_RECURSION = YES; 217 | CLANG_WARN_INT_CONVERSION = YES; 218 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 219 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 220 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 221 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 222 | CLANG_WARN_STRICT_PROTOTYPES = YES; 223 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 224 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 225 | CLANG_WARN_UNREACHABLE_CODE = YES; 226 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 227 | CODE_SIGN_IDENTITY = "iPhone Developer"; 228 | COPY_PHASE_STRIP = NO; 229 | DEBUG_INFORMATION_FORMAT = dwarf; 230 | ENABLE_STRICT_OBJC_MSGSEND = YES; 231 | ENABLE_TESTABILITY = YES; 232 | GCC_C_LANGUAGE_STANDARD = gnu11; 233 | GCC_DYNAMIC_NO_PIC = NO; 234 | GCC_NO_COMMON_BLOCKS = YES; 235 | GCC_OPTIMIZATION_LEVEL = 0; 236 | GCC_PREPROCESSOR_DEFINITIONS = ( 237 | "DEBUG=1", 238 | "$(inherited)", 239 | ); 240 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 241 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 242 | GCC_WARN_UNDECLARED_SELECTOR = YES; 243 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 244 | GCC_WARN_UNUSED_FUNCTION = YES; 245 | GCC_WARN_UNUSED_VARIABLE = YES; 246 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 247 | MTL_ENABLE_DEBUG_INFO = YES; 248 | ONLY_ACTIVE_ARCH = YES; 249 | SDKROOT = iphoneos; 250 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 251 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 252 | }; 253 | name = Debug; 254 | }; 255 | F1C3EA321F45B45E0092CFCC /* Release */ = { 256 | isa = XCBuildConfiguration; 257 | buildSettings = { 258 | ALWAYS_SEARCH_USER_PATHS = NO; 259 | CLANG_ANALYZER_NONNULL = YES; 260 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 261 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 262 | CLANG_CXX_LIBRARY = "libc++"; 263 | CLANG_ENABLE_MODULES = YES; 264 | CLANG_ENABLE_OBJC_ARC = YES; 265 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 266 | CLANG_WARN_BOOL_CONVERSION = YES; 267 | CLANG_WARN_COMMA = YES; 268 | CLANG_WARN_CONSTANT_CONVERSION = YES; 269 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 270 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 271 | CLANG_WARN_EMPTY_BODY = YES; 272 | CLANG_WARN_ENUM_CONVERSION = YES; 273 | CLANG_WARN_INFINITE_RECURSION = YES; 274 | CLANG_WARN_INT_CONVERSION = YES; 275 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 276 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 277 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 278 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 279 | CLANG_WARN_STRICT_PROTOTYPES = YES; 280 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 281 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 282 | CLANG_WARN_UNREACHABLE_CODE = YES; 283 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 284 | CODE_SIGN_IDENTITY = "iPhone Developer"; 285 | COPY_PHASE_STRIP = NO; 286 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 287 | ENABLE_NS_ASSERTIONS = NO; 288 | ENABLE_STRICT_OBJC_MSGSEND = YES; 289 | GCC_C_LANGUAGE_STANDARD = gnu11; 290 | GCC_NO_COMMON_BLOCKS = YES; 291 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 292 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 293 | GCC_WARN_UNDECLARED_SELECTOR = YES; 294 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 295 | GCC_WARN_UNUSED_FUNCTION = YES; 296 | GCC_WARN_UNUSED_VARIABLE = YES; 297 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 298 | MTL_ENABLE_DEBUG_INFO = NO; 299 | SDKROOT = iphoneos; 300 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 301 | VALIDATE_PRODUCT = YES; 302 | }; 303 | name = Release; 304 | }; 305 | F1C3EA341F45B45E0092CFCC /* Debug */ = { 306 | isa = XCBuildConfiguration; 307 | buildSettings = { 308 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 309 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 310 | CODE_SIGN_STYLE = Automatic; 311 | DEVELOPMENT_TEAM = ""; 312 | INFOPLIST_FILE = Claps/Info.plist; 313 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 314 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 315 | PRODUCT_BUNDLE_IDENTIFIER = com.hemangshah.Claps; 316 | PRODUCT_NAME = "$(TARGET_NAME)"; 317 | PROVISIONING_PROFILE_SPECIFIER = ""; 318 | SWIFT_VERSION = 4.0; 319 | TARGETED_DEVICE_FAMILY = "1,2"; 320 | }; 321 | name = Debug; 322 | }; 323 | F1C3EA351F45B45E0092CFCC /* Release */ = { 324 | isa = XCBuildConfiguration; 325 | buildSettings = { 326 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 327 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 328 | CODE_SIGN_STYLE = Automatic; 329 | DEVELOPMENT_TEAM = ""; 330 | INFOPLIST_FILE = Claps/Info.plist; 331 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 332 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 333 | PRODUCT_BUNDLE_IDENTIFIER = com.hemangshah.Claps; 334 | PRODUCT_NAME = "$(TARGET_NAME)"; 335 | PROVISIONING_PROFILE_SPECIFIER = ""; 336 | SWIFT_VERSION = 4.0; 337 | TARGETED_DEVICE_FAMILY = "1,2"; 338 | }; 339 | name = Release; 340 | }; 341 | /* End XCBuildConfiguration section */ 342 | 343 | /* Begin XCConfigurationList section */ 344 | F1C3EA1C1F45B45E0092CFCC /* Build configuration list for PBXProject "Claps" */ = { 345 | isa = XCConfigurationList; 346 | buildConfigurations = ( 347 | F1C3EA311F45B45E0092CFCC /* Debug */, 348 | F1C3EA321F45B45E0092CFCC /* Release */, 349 | ); 350 | defaultConfigurationIsVisible = 0; 351 | defaultConfigurationName = Release; 352 | }; 353 | F1C3EA331F45B45E0092CFCC /* Build configuration list for PBXNativeTarget "Claps" */ = { 354 | isa = XCConfigurationList; 355 | buildConfigurations = ( 356 | F1C3EA341F45B45E0092CFCC /* Debug */, 357 | F1C3EA351F45B45E0092CFCC /* Release */, 358 | ); 359 | defaultConfigurationIsVisible = 0; 360 | defaultConfigurationName = Release; 361 | }; 362 | /* End XCConfigurationList section */ 363 | }; 364 | rootObject = F1C3EA191F45B45E0092CFCC /* Project object */; 365 | } 366 | -------------------------------------------------------------------------------- /Claps/Claps.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Claps/Claps/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Claps 4 | // 5 | // Created by Hemang Shah on 8/17/17. 6 | // Copyright © 2017 Hemang Shah. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 17 | // Override point for customization after application launch. 18 | return true 19 | } 20 | 21 | func applicationWillResignActive(_ application: UIApplication) { 22 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 23 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 24 | } 25 | 26 | func applicationDidEnterBackground(_ application: UIApplication) { 27 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | func applicationWillEnterForeground(_ application: UIApplication) { 32 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 33 | } 34 | 35 | func applicationDidBecomeActive(_ application: UIApplication) { 36 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 37 | } 38 | 39 | func applicationWillTerminate(_ application: UIApplication) { 40 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Claps/Claps/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 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /Claps/Claps/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 | -------------------------------------------------------------------------------- /Claps/Claps/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 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 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 | -------------------------------------------------------------------------------- /Claps/Claps/ClapsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ClapsView.swift 3 | // Claps 4 | // 5 | // Created by Hemang Shah on 8/17/17. 6 | // Copyright © 2017 Hemang Shah. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | fileprivate extension UIView { 12 | func asCircle() { 13 | self.layer.cornerRadius = self.frame.size.width/2.0 14 | self.layer.masksToBounds = true 15 | } 16 | } 17 | 18 | fileprivate extension UIColor { 19 | class func RGB(r: CGFloat, g: CGFloat, b: CGFloat) -> UIColor { 20 | return UIColor.init(red: r/255, green: g/255, blue: b/255, alpha: 1.0) 21 | } 22 | } 23 | 24 | fileprivate extension Int { 25 | var abbreviated: String { 26 | let abbrev = "KMBTPE" 27 | return abbrev.enumerated().reversed().reduce(nil as String?) { accum, tuple in 28 | let factor = Double(self) / pow(10, Double(tuple.0 + 1) * 3) 29 | let format = (factor.truncatingRemainder(dividingBy: 1) == 0 ? "%.0f%@" : "%.1f%@") 30 | return accum ?? (factor > 1 ? String(format: format, factor, String(tuple.1)) : nil) 31 | } ?? String(self) 32 | } 33 | } 34 | 35 | fileprivate enum ClapsStatus { 36 | case loaded, unloaded 37 | } 38 | 39 | @objc public enum ClapsViewStates: Int { 40 | case begin 41 | case end 42 | case clapping 43 | case finalized 44 | } 45 | 46 | @objc public protocol ClapsViewDelegate { 47 | func clapsViewStateChanged(clapsView: ClapsView, state: ClapsViewStates, totalClaps: Int, currentClaps: Int) 48 | } 49 | 50 | @IBDesignable 51 | public class ClapsView: UIView { 52 | 53 | @IBOutlet public var delegate: ClapsViewDelegate? 54 | 55 | ///Inactive State Border Color. Default: .lightGray 56 | @IBInspectable public var inActiveStateBorderColor: UIColor = .lightGray { 57 | didSet { 58 | self.inActiveState() 59 | } 60 | } 61 | 62 | ///Active State Border Color. 63 | @IBInspectable public var activeStateBorderColor: UIColor = .RGB(r: 255.0, g: 207.0, b: 74.0) { 64 | didSet { 65 | self.activeState() 66 | } 67 | } 68 | 69 | ///Inside Background Color. Default: .clear 70 | @IBInspectable public var insideBakcgroundColor: UIColor = .clear { 71 | didSet { 72 | self.emojiLabel?.backgroundColor = insideBakcgroundColor 73 | } 74 | } 75 | 76 | ///ClapsView Background Color. 77 | @IBInspectable public var clapsLabelBackgroundColor: UIColor = .RGB(r: 255.0, g: 207.0, b: 74.0) { 78 | didSet { 79 | self.clapsLabel?.backgroundColor = clapsLabelBackgroundColor 80 | } 81 | } 82 | 83 | ///ClapsView Text Color. Default: .white 84 | @IBInspectable public var clapsLabelTextColor: UIColor = .white { 85 | didSet { 86 | self.clapsLabel?.textColor = clapsLabelTextColor 87 | } 88 | } 89 | 90 | ///ClapsView Font. Default: SystemFont = 12.0 91 | @IBInspectable public var clapsLabelFont: UIFont = UIFont.systemFont(ofSize: 12.0) { 92 | didSet { 93 | self.clapsLabel?.font = clapsLabelFont 94 | } 95 | } 96 | 97 | ///ClapsView Emoji. Default: 👏 98 | @IBInspectable public var emoji: String = "👏" { 99 | didSet { 100 | if emoji.isEmpty { 101 | fatalError("\(String.init(describing: ClapsView.self)) requires an Emoji.") 102 | } else { 103 | self.emojiLabel?.text = emoji 104 | } 105 | } 106 | } 107 | 108 | ///Increase/Decrease the size of Emoji. Default: 40.0 | Minimum: 20.0 109 | @IBInspectable public var emojiLabelFontSize: CGFloat = 40.0 { 110 | didSet { 111 | if self.emojiLabelFontSize >= 20.0 { 112 | self.emojiLabel?.font = UIFont.init(name: "AppleColorEmoji", size: emojiLabelFontSize) 113 | } else { 114 | fatalError("\(String.init(describing: ClapsView.self)) requires 20.0 size for Emoji. This is the minimum size.") 115 | } 116 | } 117 | } 118 | 119 | ///Show 1000 claps as 1k. Default: false 120 | @IBInspectable public var showClapsAbbreviated: Bool = false 121 | 122 | ///Total Claps Count by All the Users. 123 | @IBInspectable public var totalClaps: Int = 0 124 | ///Current Claps Count By a User. 125 | @IBInspectable public var currentClaps: Int = 0 126 | ///The maximum number of claps allowed. 127 | @IBInspectable public var maxClaps: Int = 50 128 | ///i.e. currentClaps + iteratorClaps = new claps. 129 | @IBInspectable public var iteratorClaps: Int = 1 130 | 131 | //To show the exact claps each time. 132 | fileprivate var lastClapsCount: Int = 0 133 | 134 | //To Decide whether to Add (or Already Added) Inner UI Components 135 | fileprivate var clapsStatus: ClapsStatus = .unloaded 136 | 137 | //Private Values 138 | fileprivate var clapsLabel: UILabel? = nil 139 | fileprivate var emojiLabel: UILabel? = nil 140 | fileprivate var removeClapsButton: UIButton? = nil 141 | 142 | fileprivate var animationTimer: Timer? = nil 143 | 144 | fileprivate let animationDuration: TimeInterval = 0.25 145 | fileprivate let timerDuration: TimeInterval = 0.30 146 | 147 | //MARK: Init 148 | public override func awakeFromNib() { 149 | super.awakeFromNib() 150 | } 151 | 152 | public override init(frame: CGRect) { 153 | super.init(frame: frame) 154 | } 155 | 156 | public required init?(coder aDecoder: NSCoder) { 157 | super.init(coder: aDecoder) 158 | } 159 | 160 | override public func layoutSubviews() { 161 | super.layoutSubviews() 162 | if self.clapsStatus == .unloaded { 163 | self.setupClapsView() 164 | } 165 | } 166 | 167 | //MARK: Setup 168 | fileprivate func setupClapsView() { 169 | if self.frameValidator() { 170 | self.backgroundColor = UIColor.white 171 | self.asCircle() 172 | self.addEmojiLabel() 173 | self.inActiveState() 174 | self.addLongPressGesture() 175 | self.addClapsLabel() 176 | self.addRemoveClapsButton() 177 | self.addSingleAndDoubleTapGestures() 178 | self.clapsStatus = .loaded 179 | } 180 | } 181 | 182 | //MARK: Gestures 183 | fileprivate func addLongPressGesture() { 184 | //This gesture will be used to iterate the claps on long press. 185 | let longPressGesture = UILongPressGestureRecognizer.init(target: self, action: #selector(self.actionUserTappedOrLongPress)) 186 | self.addGestureRecognizer(longPressGesture) 187 | } 188 | 189 | fileprivate func addSingleAndDoubleTapGestures() { 190 | //This gesture will be used to see current claps by a user. 191 | let singleTapGesture = UITapGestureRecognizer.init(target: self, action: #selector(self.actionUserSingleTaps)) 192 | singleTapGesture.numberOfTapsRequired = 1 193 | self.addGestureRecognizer(singleTapGesture) 194 | 195 | //This gesture will be used to show a 'X' button. This will be used to remove all of the claps by a user. 196 | let doubleTapGesture = UITapGestureRecognizer.init(target: self, action: #selector(self.actionUserDoubleTaps)) 197 | doubleTapGesture.numberOfTapsRequired = 2 198 | self.addGestureRecognizer(doubleTapGesture) 199 | 200 | singleTapGesture.require(toFail: doubleTapGesture) 201 | } 202 | 203 | //MARK: Gesture Action 204 | @objc fileprivate func actionUserTappedOrLongPress(longPressGesture: UILongPressGestureRecognizer) { 205 | 206 | if longPressGesture.state == .began { 207 | 208 | self.updateForBeginState() 209 | self.countManagement() 210 | self.activeState() 211 | self.runTimer() 212 | 213 | } else if longPressGesture.state == .ended { 214 | 215 | self.updateForEndState() 216 | self.stopTimer() 217 | self.updateClaps() 218 | self.inActiveState() 219 | } 220 | } 221 | 222 | @objc fileprivate func actionUserDoubleTaps(gesture: UIPanGestureRecognizer) { 223 | if self.currentClaps > 0 { 224 | if (self.removeClapsButton?.isHidden)! { 225 | self.removeClapsButton?.isHidden = false 226 | self.autoHideRemoveClapsButton() 227 | } 228 | } 229 | } 230 | 231 | @objc fileprivate func actionUserSingleTaps(gesture: UIPanGestureRecognizer) { 232 | self.finalizeClaps() 233 | } 234 | 235 | @objc fileprivate func actionRemoveClaps(sender: UIButton) { 236 | let totalClaps = (self.totalClaps == self.currentClaps) ? 0 : self.totalClaps 237 | self.remove(withTotalClaps: totalClaps, withFinalizeAnimation: false) 238 | self.removeClapsButton?.isHidden = true 239 | } 240 | 241 | //MARK: Update for Begin/End States 242 | fileprivate func updateForBeginState() { 243 | if (self.delegate != nil) { 244 | self.delegate?.clapsViewStateChanged(clapsView: self, state: .begin, totalClaps: self.totalClaps, currentClaps: self.currentClaps) 245 | } 246 | } 247 | 248 | fileprivate func updateForEndState() { 249 | if (self.delegate != nil) { 250 | self.delegate?.clapsViewStateChanged(clapsView: self, state: .end, totalClaps: self.totalClaps, currentClaps: self.currentClaps) 251 | } 252 | } 253 | 254 | //MARK: Timer Management (Start/Stop) 255 | fileprivate func runTimer() { 256 | 257 | self.stopTimer() 258 | 259 | self.animationTimer = Timer.scheduledTimer(withTimeInterval: self.timerDuration, repeats: true) { (timer) in 260 | self.animateClapsLabel() 261 | } 262 | } 263 | 264 | fileprivate func stopTimer() { 265 | if let timer = animationTimer { 266 | timer.invalidate() 267 | } 268 | } 269 | 270 | fileprivate func autoHideRemoveClapsButton() { 271 | if !(self.removeClapsButton?.isHidden)! { 272 | Timer.scheduledTimer(withTimeInterval: 3.0, repeats: false) { (timer) in 273 | self.removeClapsButton?.isHidden = true 274 | } 275 | } 276 | } 277 | 278 | //MARK: Finalize Claps 279 | fileprivate func updateClaps() { 280 | let differenceInClapsCount = (self.currentClaps - self.lastClapsCount) 281 | self.totalClaps = self.totalClaps + differenceInClapsCount 282 | self.finalizeClaps() 283 | } 284 | 285 | internal func finalizeClaps() { 286 | 287 | if self.totalClaps <= 0 { 288 | return 289 | } 290 | 291 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + timerDuration, execute: { 292 | 293 | if self.showClapsAbbreviated { 294 | self.clapsLabel?.text = self.totalClaps.abbreviated 295 | } else { 296 | self.clapsLabel?.text = "+" + "\(self.totalClaps)" 297 | } 298 | 299 | if self.delegate != nil { 300 | self.delegate?.clapsViewStateChanged(clapsView: self, state: .finalized, totalClaps: self.totalClaps, currentClaps: self.currentClaps) 301 | } 302 | 303 | UIView.animate(withDuration: self.animationDuration, delay: 0.0, options: .curveEaseOut, animations: { 304 | self.clapsLabel?.alpha = 1.0 305 | self.clapsLabel?.transform = CGAffineTransform(scaleX: 1.5,y: 1.5) 306 | }) { (isCompleted) in 307 | self.clapsLabel?.alpha = 0.0 308 | self.clapsLabel?.transform = CGAffineTransform(scaleX: 1.0,y: 1.0) 309 | } 310 | }) 311 | } 312 | 313 | ///This is to remove a user claps. So we will requires to update the ClapsView with new claps. 314 | internal func remove(withTotalClaps totalClaps: Int, withFinalizeAnimation finalizeAnimation: Bool) { 315 | self.totalClaps = totalClaps 316 | self.currentClaps = 0 317 | self.lastClapsCount = 0 318 | self.countManagement() 319 | 320 | if finalizeAnimation { 321 | self.updateClaps() 322 | } 323 | } 324 | 325 | //MARK: Iterator 326 | @objc fileprivate func animateClapsLabel() { 327 | 328 | self.iterateClaps() 329 | 330 | UIView.animate(withDuration: self.animationDuration, delay: 0.0, options: .curveEaseOut, animations: { 331 | self.clapsLabel?.alpha = 1.0 332 | self.clapsLabel?.transform = CGAffineTransform(scaleX: 1.5,y: 1.5) 333 | self.emojiLabel?.transform = CGAffineTransform(scaleX: 1.5,y: 1.5) 334 | }) { (isCompleted) in 335 | self.clapsLabel?.alpha = 0.0 336 | self.clapsLabel?.transform = CGAffineTransform(scaleX: 1.0,y: 1.0) 337 | self.emojiLabel?.transform = CGAffineTransform(scaleX: 1.0,y: 1.0) 338 | } 339 | } 340 | 341 | fileprivate func iterateClaps() { 342 | if !self.hasMaxClaps() { 343 | self.currentClaps = self.currentClaps + self.iteratorClaps 344 | if (self.delegate != nil) { 345 | self.delegate?.clapsViewStateChanged(clapsView: self, state: .clapping, totalClaps: self.totalClaps, currentClaps: self.currentClaps) 346 | } 347 | } 348 | 349 | if self.showClapsAbbreviated { 350 | self.clapsLabel?.text = self.currentClaps.abbreviated 351 | } else { 352 | self.clapsLabel?.text = "+" + String(self.currentClaps) 353 | } 354 | } 355 | 356 | internal func hasMaxClaps() -> Bool { 357 | return (self.currentClaps == self.maxClaps) 358 | } 359 | 360 | fileprivate func countManagement() { 361 | if self.lastClapsCount >= 0 { 362 | self.lastClapsCount = self.currentClaps 363 | } 364 | } 365 | 366 | //MARK: UI Helpers 367 | fileprivate func initialFrame() -> CGRect { 368 | return CGRect.init(origin: .zero, size: CGSize.init(width: self.frame.size.width, height: self.frame.size.height)) 369 | } 370 | 371 | fileprivate func addEmojiLabel() { 372 | let label = UILabel.init(frame: initialFrame()) 373 | label.asCircle() 374 | label.backgroundColor = self.insideBakcgroundColor 375 | label.isUserInteractionEnabled = false 376 | label.text = emoji 377 | label.font = UIFont.init(name: "AppleColorEmoji", size: emojiLabelFontSize) 378 | label.textAlignment = .center 379 | label.clipsToBounds = false 380 | label.isUserInteractionEnabled = false 381 | self.addSubview(label) 382 | label.center = CGPoint.init(x: self.frame.size.width/2.0, y: self.frame.size.height/2.0) 383 | self.emojiLabel = label 384 | } 385 | 386 | fileprivate func addClapsLabel() { 387 | if let superView = self.superview { 388 | let clapsViewSize = self.frame.size.width/2.0 389 | let label = UILabel.init(frame: CGRect.init(origin: CGPoint.zero, size: CGSize.init(width: clapsViewSize, height: clapsViewSize))) 390 | label.asCircle() 391 | label.backgroundColor = self.clapsLabelBackgroundColor 392 | label.isUserInteractionEnabled = false 393 | label.text = String(totalClaps) 394 | label.font = self.clapsLabelFont 395 | label.adjustsFontSizeToFitWidth = true 396 | label.textColor = self.clapsLabelTextColor 397 | label.textAlignment = .center 398 | label.alpha = 0.0 399 | label.isUserInteractionEnabled = false 400 | superView.addSubview(label) 401 | label.center = CGPoint.init(x: self.center.x, y: self.center.y - (self.frame.size.height + 25.0)) 402 | self.clapsLabel = label 403 | } else { 404 | fatalError("\(String.init(describing: ClapsView.self)) couldn't find it's super view.") 405 | } 406 | } 407 | 408 | fileprivate func addRemoveClapsButton() { 409 | if let superView = self.superview { 410 | let button = UIButton.init(type: .custom) 411 | button.frame = CGRect.init(origin: .zero, size: self.frame.size) 412 | button.asCircle() 413 | button.setTitle("x", for: .normal) 414 | button.backgroundColor = self.clapsLabelBackgroundColor 415 | button.addTarget(self, action: #selector(self.actionRemoveClaps), for: .touchUpInside) 416 | button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 30.0) 417 | superView.addSubview(button) 418 | button.center = self.center 419 | button.isHidden = true 420 | self.removeClapsButton = button 421 | } else { 422 | fatalError("\(String.init(describing: ClapsView.self)) couldn't find it's super view.") 423 | } 424 | } 425 | 426 | fileprivate func activeState() { 427 | self.layer.borderColor = self.activeStateBorderColor.cgColor 428 | self.layer.borderWidth = 2.0 429 | } 430 | 431 | fileprivate func inActiveState() { 432 | self.layer.borderColor = self.inActiveStateBorderColor.cgColor 433 | self.layer.borderWidth = 2.0 434 | } 435 | 436 | //MARK: Validator 437 | fileprivate func frameValidator() -> Bool { 438 | let size = self.frame.size 439 | if size.width != size.height { 440 | fatalError("\(String.init(describing: ClapsView.self)) width and height should be equal.") 441 | } 442 | return true 443 | } 444 | } 445 | -------------------------------------------------------------------------------- /Claps/Claps/Images/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hemangshah/ClapsView/94984769cc7f4e45d27056b4f41ca03867b63d72/Claps/Claps/Images/1.jpg -------------------------------------------------------------------------------- /Claps/Claps/Images/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hemangshah/ClapsView/94984769cc7f4e45d27056b4f41ca03867b63d72/Claps/Claps/Images/2.jpg -------------------------------------------------------------------------------- /Claps/Claps/Images/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hemangshah/ClapsView/94984769cc7f4e45d27056b4f41ca03867b63d72/Claps/Claps/Images/3.jpg -------------------------------------------------------------------------------- /Claps/Claps/Images/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hemangshah/ClapsView/94984769cc7f4e45d27056b4f41ca03867b63d72/Claps/Claps/Images/4.jpg -------------------------------------------------------------------------------- /Claps/Claps/Images/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hemangshah/ClapsView/94984769cc7f4e45d27056b4f41ca03867b63d72/Claps/Claps/Images/5.jpg -------------------------------------------------------------------------------- /Claps/Claps/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.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 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Claps/Claps/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Claps 4 | // 5 | // Created by Hemang Shah on 8/17/17. 6 | // Copyright © 2017 Hemang Shah. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | @IBOutlet var clapsView: ClapsView! 14 | @IBOutlet var webView: UIWebView! 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | // Do any additional setup after loading the view, typically from a nib. 19 | 20 | self.webView.loadRequest(URLRequest.init(url: URL.init(string: "https://blog.medium.com/show-authors-more-%EF%B8%8F-with-s-c1652279ba01")!)) 21 | } 22 | } 23 | 24 | extension ViewController: ClapsViewDelegate { 25 | func clapsViewStateChanged(clapsView: ClapsView, state: ClapsViewStates, totalClaps: Int, currentClaps: Int) { 26 | if state == .finalized { 27 | print("Finalized | Total Claps:\(totalClaps) | Current Claps:\(currentClaps)") 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ClapsView.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'ClapsView' 3 | s.module_name = 'ClapsView' 4 | s.version = '2.0.0' 5 | s.summary = 'Implemented the functionality of Medium.com Claps. 👏' 6 | s.description = 'ClapsView is a new way to give your ratings or feedback from your users.' 7 | s.homepage = 'https://github.com/hemangshah/ClapsView' 8 | s.license = 'MIT' 9 | s.author = { 'hemangshah' => 'hemangshah.in@gmail.com' } 10 | s.source = { :git => 'https://github.com/hemangshah/ClapsView.git', :tag => s.version.to_s } 11 | s.platform = :ios, '10.0' 12 | s.requires_arc = true 13 | s.source_files = '**/ClapsView.swift' 14 | end 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Hemang Shah 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ClapsView 👏 2 | Implemented the functionality of [Medium.com Claps](https://blog.medium.com/show-authors-more-%EF%B8%8F-with-s-c1652279ba01). 3 | 4 | ![License](https://img.shields.io/badge/License-MIT-lightgrey.svg) 5 | ![Platform](https://img.shields.io/badge/Platforms-iOS-red.svg) 6 | ![Swift 4.x](https://img.shields.io/badge/Swift-4.x-blue.svg) 7 | ![MadeWithLove](https://img.shields.io/badge/Made%20with%20%E2%9D%A4-India-green.svg) 8 | [![Awesome-Swift](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/matteocrippa/awesome-swift/) 9 | 10 | 1. [Screenshots](#screenshots) 11 | 2. [Features](#features) 12 | 3. [Installation](#installation) 13 | 4. [Usage Guide](#usage-guide) 14 | 5. [Setup](#setup) 15 | 6. [Usage](#usage) 16 | 6. [ToDos](#todos) 17 | 7. [Credits](#credits) 18 | 8. [Thanks](#thank-you) 19 | 9. [License](#license) 20 | 21 | ## Screenshots 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
UsageUsage
Usage
32 | 33 | ## Features 34 | 35 | 1. Create Programmatically or in Storyboard. 36 | 2. Dynamic Property Configurations. 37 | 3. Replica of Medium.com's Claps. 38 | 7. Lightweight with zero dependancies. 39 | 40 | ## Installation 41 | 42 | 1. **Storyboard** – Add a `UIView` and change the class in Identity Inspector to `ClapsView`.
43 | 2. **Manually** – Add `ClapsView.swift` file to your Project.
44 | 3. **CocoaPods** – `pod 'ClapsView'` 45 | 46 | > You can read the [CHANGELOG](https://github.com/hemangshah/ClapsView/blob/master/CHANGELOG.md) file for a particular release. 47 | 48 | ## Usage Guide 49 | 50 | - Long Press – To start clapping. 👏 51 | - Double Tap - To remove current claps. 52 | - Single Tap - To see current Claps. 53 | 54 | ## Setup 55 | 56 | ````swift 57 | import ClapsView 58 | ```` 59 | 60 | ## Usage 61 | ````swift 62 | let claps = ClapsView.init(frame: CGRect.init(origin: CGPoint.zero, size: CGSize.init(width: 100.0, height: 100.0))) 63 | claps.delegate = self 64 | //Customize Emoji. 65 | claps.emoji = "👏" 66 | //The number of claps by all of the user. 67 | claps.totalClaps = 30 68 | //The maximum number of claps a user can do. 69 | claps.maxClaps = 50 70 | //If set true, 1000 claps will be display as 1k 71 | claps.showClapsAbbreviated = true 72 | self.view.addSubview(claps) 73 | claps.center = self.view.center 74 | 75 | //Implementing Delegate Call 76 | extension ViewController: ClapsViewDelegate { 77 | func clapsViewStateChanged(clapsView: ClapsView, state: ClapsViewStates, totalClaps: Int, currentClaps: Int) { 78 | if state == .finalized { 79 | print("ClapsViewTag: \(clapsView.tag) | Total Claps:\(totalClaps) | Current Claps:\(currentClaps)") 80 | } 81 | } 82 | } 83 | ```` 84 | 85 | ## ToDo[s] 86 | 87 | - [x] CocoaPods Support 88 | 89 | You can [watch](https://github.com/hemangshah/ClapsView/subscription) to ClapsView to see continuous updates. Stay tuned. 90 | 91 | Have an idea for improvements of this class? 92 | Please open an [issue](https://github.com/hemangshah/ClapsView/issues/new). 93 |     94 | ## Credits 95 | 96 | [Hemang Shah](https://about.me/hemang.shah) 97 | 98 | **You can shoot me an [email](http://www.google.com/recaptcha/mailhide/d?k=01IzGihUsyfigse2G9z80rBw==&c=vU7vyAaau8BctOAIJFwHVbKfgtIqQ4QLJaL73yhnB3k=) to contact.** 99 |   100 | ## Thank You!! 101 | 102 | See the [contributions](https://github.com/hemangshah/ClapsView/blob/master/CONTRIBUTIONS.md) for details. 103 | 104 | ## License 105 | 106 | The MIT License (MIT) 107 | 108 | > Read the [LICENSE](https://github.com/hemangshah/ClapsView/blob/master/LICENSE) file for details. 109 | -------------------------------------------------------------------------------- /Screenshots/ClapsViewUsage.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hemangshah/ClapsView/94984769cc7f4e45d27056b4f41ca03867b63d72/Screenshots/ClapsViewUsage.gif -------------------------------------------------------------------------------- /Screenshots/Screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hemangshah/ClapsView/94984769cc7f4e45d27056b4f41ca03867b63d72/Screenshots/Screenshot-1.png -------------------------------------------------------------------------------- /Screenshots/Screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hemangshah/ClapsView/94984769cc7f4e45d27056b4f41ca03867b63d72/Screenshots/Screenshot-2.png --------------------------------------------------------------------------------