├── .gitignore ├── LICENSE ├── README.md ├── SiriWave ├── SiriWave.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── SiriWave │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ ├── Utility │ ├── DateExtension.swift │ ├── Lerp.swift │ └── Timeout.swift │ ├── ViewController.swift │ └── Views │ ├── SiriWaveLine.swift │ └── SiriWaveView.swift └── siri-wave-ios9+.gif /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | # Package.resolved 41 | .build/ 42 | 43 | # CocoaPods 44 | # 45 | # We recommend against adding the Pods directory to your .gitignore. However 46 | # you should judge for yourself, the pros and cons are mentioned at: 47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 48 | # 49 | # Pods/ 50 | 51 | # Carthage 52 | # 53 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 54 | # Carthage/Checkouts 55 | 56 | Carthage/Build 57 | 58 | # fastlane 59 | # 60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 61 | # screenshots whenever they are needed. 62 | # For more information about the recommended setup visit: 63 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 64 | 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots/**/*.png 68 | fastlane/test_output 69 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 mapo80 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 | # SiriWave 2 | 3 | The "Apple Siri" wave replicated in pure Swift. 4 | 5 | It was inspired by this library: https://github.com/kopiro/siriwave 6 | 7 | If you want to know what are the mathematical functions for its operation, you can read this post written by Flavio De Stefano: 8 | https://medium.freecodecamp.org/how-i-built-siriwavejs-library-maths-and-code-behind-6971497ae5c1 9 | 10 | ### iOS 9+ style 11 | 12 | 13 | ## Usage 14 | 15 | To use it add SiriWaveView.swift in you project. 16 | -------------------------------------------------------------------------------- /SiriWave/SiriWave.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 890CBC0E2362263F002BEF06 /* SiriWaveLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 890CBC0D2362263F002BEF06 /* SiriWaveLine.swift */; }; 11 | C88D36D02232FB2F00594238 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88D36CF2232FB2F00594238 /* AppDelegate.swift */; }; 12 | C88D36D22232FB2F00594238 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88D36D12232FB2F00594238 /* ViewController.swift */; }; 13 | C88D36D52232FB2F00594238 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C88D36D32232FB2F00594238 /* Main.storyboard */; }; 14 | C88D36D72232FB3000594238 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C88D36D62232FB3000594238 /* Assets.xcassets */; }; 15 | C88D36DA2232FB3000594238 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C88D36D82232FB3000594238 /* LaunchScreen.storyboard */; }; 16 | C88D36E32232FB9200594238 /* SiriWaveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88D36E22232FB9200594238 /* SiriWaveView.swift */; }; 17 | C88D36F32233D61800594238 /* DateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88D36F22233D61800594238 /* DateExtension.swift */; }; 18 | C88D36F52233D63800594238 /* Lerp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88D36F42233D63800594238 /* Lerp.swift */; }; 19 | C88D36F72233D66000594238 /* Timeout.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88D36F62233D66000594238 /* Timeout.swift */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXFileReference section */ 23 | 890CBC0D2362263F002BEF06 /* SiriWaveLine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiriWaveLine.swift; sourceTree = ""; }; 24 | C88D36CC2232FB2F00594238 /* SiriWave.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SiriWave.app; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | C88D36CF2232FB2F00594238 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 26 | C88D36D12232FB2F00594238 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 27 | C88D36D42232FB2F00594238 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 28 | C88D36D62232FB3000594238 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 29 | C88D36D92232FB3000594238 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 30 | C88D36DB2232FB3000594238 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 31 | C88D36E22232FB9200594238 /* SiriWaveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiriWaveView.swift; sourceTree = ""; }; 32 | C88D36F22233D61800594238 /* DateExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateExtension.swift; sourceTree = ""; }; 33 | C88D36F42233D63800594238 /* Lerp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Lerp.swift; sourceTree = ""; }; 34 | C88D36F62233D66000594238 /* Timeout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Timeout.swift; sourceTree = ""; }; 35 | /* End PBXFileReference section */ 36 | 37 | /* Begin PBXFrameworksBuildPhase section */ 38 | C88D36C92232FB2F00594238 /* Frameworks */ = { 39 | isa = PBXFrameworksBuildPhase; 40 | buildActionMask = 2147483647; 41 | files = ( 42 | ); 43 | runOnlyForDeploymentPostprocessing = 0; 44 | }; 45 | /* End PBXFrameworksBuildPhase section */ 46 | 47 | /* Begin PBXGroup section */ 48 | C88D36C32232FB2F00594238 = { 49 | isa = PBXGroup; 50 | children = ( 51 | C88D36CE2232FB2F00594238 /* SiriWave */, 52 | C88D36CD2232FB2F00594238 /* Products */, 53 | ); 54 | sourceTree = ""; 55 | }; 56 | C88D36CD2232FB2F00594238 /* Products */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | C88D36CC2232FB2F00594238 /* SiriWave.app */, 60 | ); 61 | name = Products; 62 | sourceTree = ""; 63 | }; 64 | C88D36CE2232FB2F00594238 /* SiriWave */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | C88D36F12233D60600594238 /* Utility */, 68 | C88D36E12232FB6E00594238 /* Views */, 69 | C88D36CF2232FB2F00594238 /* AppDelegate.swift */, 70 | C88D36D12232FB2F00594238 /* ViewController.swift */, 71 | C88D36D32232FB2F00594238 /* Main.storyboard */, 72 | C88D36D62232FB3000594238 /* Assets.xcassets */, 73 | C88D36D82232FB3000594238 /* LaunchScreen.storyboard */, 74 | C88D36DB2232FB3000594238 /* Info.plist */, 75 | ); 76 | path = SiriWave; 77 | sourceTree = ""; 78 | }; 79 | C88D36E12232FB6E00594238 /* Views */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | C88D36E22232FB9200594238 /* SiriWaveView.swift */, 83 | 890CBC0D2362263F002BEF06 /* SiriWaveLine.swift */, 84 | ); 85 | path = Views; 86 | sourceTree = ""; 87 | }; 88 | C88D36F12233D60600594238 /* Utility */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | C88D36F22233D61800594238 /* DateExtension.swift */, 92 | C88D36F42233D63800594238 /* Lerp.swift */, 93 | C88D36F62233D66000594238 /* Timeout.swift */, 94 | ); 95 | path = Utility; 96 | sourceTree = ""; 97 | }; 98 | /* End PBXGroup section */ 99 | 100 | /* Begin PBXNativeTarget section */ 101 | C88D36CB2232FB2F00594238 /* SiriWave */ = { 102 | isa = PBXNativeTarget; 103 | buildConfigurationList = C88D36DE2232FB3000594238 /* Build configuration list for PBXNativeTarget "SiriWave" */; 104 | buildPhases = ( 105 | C88D36C82232FB2F00594238 /* Sources */, 106 | C88D36C92232FB2F00594238 /* Frameworks */, 107 | C88D36CA2232FB2F00594238 /* Resources */, 108 | ); 109 | buildRules = ( 110 | ); 111 | dependencies = ( 112 | ); 113 | name = SiriWave; 114 | productName = SiriWave; 115 | productReference = C88D36CC2232FB2F00594238 /* SiriWave.app */; 116 | productType = "com.apple.product-type.application"; 117 | }; 118 | /* End PBXNativeTarget section */ 119 | 120 | /* Begin PBXProject section */ 121 | C88D36C42232FB2F00594238 /* Project object */ = { 122 | isa = PBXProject; 123 | attributes = { 124 | LastSwiftUpdateCheck = 1010; 125 | LastUpgradeCheck = 1010; 126 | TargetAttributes = { 127 | C88D36CB2232FB2F00594238 = { 128 | CreatedOnToolsVersion = 10.1; 129 | }; 130 | }; 131 | }; 132 | buildConfigurationList = C88D36C72232FB2F00594238 /* Build configuration list for PBXProject "SiriWave" */; 133 | compatibilityVersion = "Xcode 9.3"; 134 | developmentRegion = en; 135 | hasScannedForEncodings = 0; 136 | knownRegions = ( 137 | en, 138 | Base, 139 | ); 140 | mainGroup = C88D36C32232FB2F00594238; 141 | productRefGroup = C88D36CD2232FB2F00594238 /* Products */; 142 | projectDirPath = ""; 143 | projectRoot = ""; 144 | targets = ( 145 | C88D36CB2232FB2F00594238 /* SiriWave */, 146 | ); 147 | }; 148 | /* End PBXProject section */ 149 | 150 | /* Begin PBXResourcesBuildPhase section */ 151 | C88D36CA2232FB2F00594238 /* Resources */ = { 152 | isa = PBXResourcesBuildPhase; 153 | buildActionMask = 2147483647; 154 | files = ( 155 | C88D36DA2232FB3000594238 /* LaunchScreen.storyboard in Resources */, 156 | C88D36D72232FB3000594238 /* Assets.xcassets in Resources */, 157 | C88D36D52232FB2F00594238 /* Main.storyboard in Resources */, 158 | ); 159 | runOnlyForDeploymentPostprocessing = 0; 160 | }; 161 | /* End PBXResourcesBuildPhase section */ 162 | 163 | /* Begin PBXSourcesBuildPhase section */ 164 | C88D36C82232FB2F00594238 /* Sources */ = { 165 | isa = PBXSourcesBuildPhase; 166 | buildActionMask = 2147483647; 167 | files = ( 168 | C88D36D22232FB2F00594238 /* ViewController.swift in Sources */, 169 | C88D36F32233D61800594238 /* DateExtension.swift in Sources */, 170 | C88D36D02232FB2F00594238 /* AppDelegate.swift in Sources */, 171 | C88D36E32232FB9200594238 /* SiriWaveView.swift in Sources */, 172 | C88D36F52233D63800594238 /* Lerp.swift in Sources */, 173 | 890CBC0E2362263F002BEF06 /* SiriWaveLine.swift in Sources */, 174 | C88D36F72233D66000594238 /* Timeout.swift in Sources */, 175 | ); 176 | runOnlyForDeploymentPostprocessing = 0; 177 | }; 178 | /* End PBXSourcesBuildPhase section */ 179 | 180 | /* Begin PBXVariantGroup section */ 181 | C88D36D32232FB2F00594238 /* Main.storyboard */ = { 182 | isa = PBXVariantGroup; 183 | children = ( 184 | C88D36D42232FB2F00594238 /* Base */, 185 | ); 186 | name = Main.storyboard; 187 | sourceTree = ""; 188 | }; 189 | C88D36D82232FB3000594238 /* LaunchScreen.storyboard */ = { 190 | isa = PBXVariantGroup; 191 | children = ( 192 | C88D36D92232FB3000594238 /* Base */, 193 | ); 194 | name = LaunchScreen.storyboard; 195 | sourceTree = ""; 196 | }; 197 | /* End PBXVariantGroup section */ 198 | 199 | /* Begin XCBuildConfiguration section */ 200 | C88D36DC2232FB3000594238 /* Debug */ = { 201 | isa = XCBuildConfiguration; 202 | buildSettings = { 203 | ALWAYS_SEARCH_USER_PATHS = NO; 204 | CLANG_ANALYZER_NONNULL = YES; 205 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 206 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 207 | CLANG_CXX_LIBRARY = "libc++"; 208 | CLANG_ENABLE_MODULES = YES; 209 | CLANG_ENABLE_OBJC_ARC = YES; 210 | CLANG_ENABLE_OBJC_WEAK = YES; 211 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 212 | CLANG_WARN_BOOL_CONVERSION = YES; 213 | CLANG_WARN_COMMA = YES; 214 | CLANG_WARN_CONSTANT_CONVERSION = YES; 215 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 216 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 217 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 218 | CLANG_WARN_EMPTY_BODY = YES; 219 | CLANG_WARN_ENUM_CONVERSION = YES; 220 | CLANG_WARN_INFINITE_RECURSION = YES; 221 | CLANG_WARN_INT_CONVERSION = YES; 222 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 223 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 224 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 225 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 226 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 227 | CLANG_WARN_STRICT_PROTOTYPES = YES; 228 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 229 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 230 | CLANG_WARN_UNREACHABLE_CODE = YES; 231 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 232 | CODE_SIGN_IDENTITY = "iPhone Developer"; 233 | COPY_PHASE_STRIP = NO; 234 | DEBUG_INFORMATION_FORMAT = dwarf; 235 | ENABLE_STRICT_OBJC_MSGSEND = YES; 236 | ENABLE_TESTABILITY = YES; 237 | GCC_C_LANGUAGE_STANDARD = gnu11; 238 | GCC_DYNAMIC_NO_PIC = NO; 239 | GCC_NO_COMMON_BLOCKS = YES; 240 | GCC_OPTIMIZATION_LEVEL = 0; 241 | GCC_PREPROCESSOR_DEFINITIONS = ( 242 | "DEBUG=1", 243 | "$(inherited)", 244 | ); 245 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 246 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 247 | GCC_WARN_UNDECLARED_SELECTOR = YES; 248 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 249 | GCC_WARN_UNUSED_FUNCTION = YES; 250 | GCC_WARN_UNUSED_VARIABLE = YES; 251 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 252 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 253 | MTL_FAST_MATH = YES; 254 | ONLY_ACTIVE_ARCH = YES; 255 | SDKROOT = iphoneos; 256 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 257 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 258 | SWIFT_VERSION = 5.0; 259 | }; 260 | name = Debug; 261 | }; 262 | C88D36DD2232FB3000594238 /* Release */ = { 263 | isa = XCBuildConfiguration; 264 | buildSettings = { 265 | ALWAYS_SEARCH_USER_PATHS = NO; 266 | CLANG_ANALYZER_NONNULL = YES; 267 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 268 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 269 | CLANG_CXX_LIBRARY = "libc++"; 270 | CLANG_ENABLE_MODULES = YES; 271 | CLANG_ENABLE_OBJC_ARC = YES; 272 | CLANG_ENABLE_OBJC_WEAK = YES; 273 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 274 | CLANG_WARN_BOOL_CONVERSION = YES; 275 | CLANG_WARN_COMMA = YES; 276 | CLANG_WARN_CONSTANT_CONVERSION = YES; 277 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 278 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 279 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 280 | CLANG_WARN_EMPTY_BODY = YES; 281 | CLANG_WARN_ENUM_CONVERSION = YES; 282 | CLANG_WARN_INFINITE_RECURSION = YES; 283 | CLANG_WARN_INT_CONVERSION = YES; 284 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 285 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 286 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 287 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 288 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 289 | CLANG_WARN_STRICT_PROTOTYPES = YES; 290 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 291 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 292 | CLANG_WARN_UNREACHABLE_CODE = YES; 293 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 294 | CODE_SIGN_IDENTITY = "iPhone Developer"; 295 | COPY_PHASE_STRIP = NO; 296 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 297 | ENABLE_NS_ASSERTIONS = NO; 298 | ENABLE_STRICT_OBJC_MSGSEND = YES; 299 | GCC_C_LANGUAGE_STANDARD = gnu11; 300 | GCC_NO_COMMON_BLOCKS = YES; 301 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 302 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 303 | GCC_WARN_UNDECLARED_SELECTOR = YES; 304 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 305 | GCC_WARN_UNUSED_FUNCTION = YES; 306 | GCC_WARN_UNUSED_VARIABLE = YES; 307 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 308 | MTL_ENABLE_DEBUG_INFO = NO; 309 | MTL_FAST_MATH = YES; 310 | SDKROOT = iphoneos; 311 | SWIFT_COMPILATION_MODE = wholemodule; 312 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 313 | SWIFT_VERSION = 5.0; 314 | VALIDATE_PRODUCT = YES; 315 | }; 316 | name = Release; 317 | }; 318 | C88D36DF2232FB3000594238 /* Debug */ = { 319 | isa = XCBuildConfiguration; 320 | buildSettings = { 321 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 322 | CODE_SIGN_STYLE = Automatic; 323 | DEVELOPMENT_TEAM = 94Q9JHN685; 324 | INFOPLIST_FILE = SiriWave/Info.plist; 325 | LD_RUNPATH_SEARCH_PATHS = ( 326 | "$(inherited)", 327 | "@executable_path/Frameworks", 328 | ); 329 | PRODUCT_BUNDLE_IDENTIFIER = com.stoltz.SiriWave; 330 | PRODUCT_NAME = "$(TARGET_NAME)"; 331 | SWIFT_VERSION = 5.0; 332 | TARGETED_DEVICE_FAMILY = "1,2"; 333 | }; 334 | name = Debug; 335 | }; 336 | C88D36E02232FB3000594238 /* Release */ = { 337 | isa = XCBuildConfiguration; 338 | buildSettings = { 339 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 340 | CODE_SIGN_STYLE = Automatic; 341 | DEVELOPMENT_TEAM = 94Q9JHN685; 342 | INFOPLIST_FILE = SiriWave/Info.plist; 343 | LD_RUNPATH_SEARCH_PATHS = ( 344 | "$(inherited)", 345 | "@executable_path/Frameworks", 346 | ); 347 | PRODUCT_BUNDLE_IDENTIFIER = com.stoltz.SiriWave; 348 | PRODUCT_NAME = "$(TARGET_NAME)"; 349 | SWIFT_VERSION = 5.0; 350 | TARGETED_DEVICE_FAMILY = "1,2"; 351 | }; 352 | name = Release; 353 | }; 354 | /* End XCBuildConfiguration section */ 355 | 356 | /* Begin XCConfigurationList section */ 357 | C88D36C72232FB2F00594238 /* Build configuration list for PBXProject "SiriWave" */ = { 358 | isa = XCConfigurationList; 359 | buildConfigurations = ( 360 | C88D36DC2232FB3000594238 /* Debug */, 361 | C88D36DD2232FB3000594238 /* Release */, 362 | ); 363 | defaultConfigurationIsVisible = 0; 364 | defaultConfigurationName = Release; 365 | }; 366 | C88D36DE2232FB3000594238 /* Build configuration list for PBXNativeTarget "SiriWave" */ = { 367 | isa = XCConfigurationList; 368 | buildConfigurations = ( 369 | C88D36DF2232FB3000594238 /* Debug */, 370 | C88D36E02232FB3000594238 /* Release */, 371 | ); 372 | defaultConfigurationIsVisible = 0; 373 | defaultConfigurationName = Release; 374 | }; 375 | /* End XCConfigurationList section */ 376 | }; 377 | rootObject = C88D36C42232FB2F00594238 /* Project object */; 378 | } 379 | -------------------------------------------------------------------------------- /SiriWave/SiriWave.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SiriWave/SiriWave.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SiriWave/SiriWave/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SiriWave 4 | // 5 | // Created by politom on 08/03/2019. 6 | // 7 | 8 | import UIKit 9 | 10 | @UIApplicationMain 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | var window: UIWindow? 14 | 15 | 16 | func application(_ application: UIApplication, 17 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | } 23 | 24 | -------------------------------------------------------------------------------- /SiriWave/SiriWave/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 | } -------------------------------------------------------------------------------- /SiriWave/SiriWave/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SiriWave/SiriWave/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 | -------------------------------------------------------------------------------- /SiriWave/SiriWave/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 | -------------------------------------------------------------------------------- /SiriWave/SiriWave/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 | NSMicrophoneUsageDescription 45 | Need microphone access to test Siri Wave 46 | 47 | 48 | -------------------------------------------------------------------------------- /SiriWave/SiriWave/Utility/DateExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DateExtension.swift 3 | // SiriWave 4 | // 5 | // Created by politom on 09/03/2019. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Date { 11 | var millisecondsSince1970: Int { 12 | return Int((self.timeIntervalSince1970 * 1000.0).rounded()) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /SiriWave/SiriWave/Utility/Lerp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Lerp.swift 3 | // SiriWave 4 | // 5 | // Created by politom on 09/03/2019. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | public class Lerp { 12 | public static func lerp(_ v0: CGFloat, _ v1: CGFloat, _ t: CGFloat) -> CGFloat { 13 | return v0 * (1 - t) + v1 * t 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /SiriWave/SiriWave/Utility/Timeout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Timeout.swift 3 | // SiriWave 4 | // 5 | // Created by politom on 09/03/2019. 6 | // 7 | 8 | import Foundation 9 | 10 | public class Timeout { 11 | 12 | public typealias ComplectionBlock = () -> Void 13 | 14 | public static func setInterval(_ interval: TimeInterval, 15 | block: @escaping ComplectionBlock) -> Timer { 16 | return Timer.scheduledTimer(timeInterval: interval, 17 | target: BlockOperation(block: block), 18 | selector: #selector(Operation.main), 19 | userInfo: nil, repeats: true) 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /SiriWave/SiriWave/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // SiriWave 4 | // 5 | // Created by politom on 08/03/2019. 6 | // 7 | 8 | import UIKit 9 | import AVKit 10 | 11 | final class ViewController: UIViewController { 12 | 13 | @IBOutlet weak var siriWave: SiriWaveView! 14 | 15 | private var recorder:AVAudioRecorder! 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | 20 | //setupRecorder() 21 | testWithoutMic() 22 | } 23 | 24 | private func testWithoutMic() { 25 | var ampl: CGFloat = 1 26 | let speed: CGFloat = 0.1 27 | 28 | func modulate() { 29 | ampl = Lerp.lerp(ampl, 1.5, speed) 30 | self.siriWave.update(ampl * 5) 31 | } 32 | 33 | _ = Timeout.setInterval(TimeInterval(speed)) { 34 | DispatchQueue.main.async { 35 | modulate() 36 | } 37 | } 38 | } 39 | 40 | /// Recorder Setup Begin 41 | @objc func setupRecorder() { 42 | if(checkMicPermission()) { 43 | startRecording() 44 | } else { 45 | print("permission denied") 46 | } 47 | } 48 | 49 | @objc func updateMeters() { 50 | var normalizedValue: Float 51 | recorder.updateMeters() 52 | normalizedValue = normalizedPowerLevelFromDecibels(decibels: recorder.averagePower(forChannel: 0)) 53 | self.siriWave.update(CGFloat(normalizedValue) * 10) 54 | } 55 | 56 | private func startRecording() { 57 | let recordingSession = AVAudioSession.sharedInstance() 58 | let recorderSettings = [AVSampleRateKey: NSNumber(value: 44100.0), 59 | AVFormatIDKey: NSNumber(value: kAudioFormatAppleLossless), 60 | AVNumberOfChannelsKey: NSNumber(value: 2), 61 | AVEncoderAudioQualityKey: NSNumber(value: Int8(AVAudioQuality.min.rawValue))] 62 | 63 | let url: URL = URL(fileURLWithPath:"/dev/null") 64 | do { 65 | 66 | let displayLink: CADisplayLink = CADisplayLink(target: self, 67 | selector: #selector(ViewController.updateMeters)) 68 | displayLink.add(to: RunLoop.current, 69 | forMode: RunLoop.Mode.common) 70 | 71 | try recordingSession.setCategory(.playAndRecord, 72 | mode: .default) 73 | try recordingSession.setActive(true) 74 | self.recorder = try AVAudioRecorder.init(url: url, 75 | settings: recorderSettings as [String : Any]) 76 | self.recorder.prepareToRecord() 77 | self.recorder.isMeteringEnabled = true; 78 | self.recorder.record() 79 | print("recorder enabled") 80 | } catch { 81 | self.showErrorPopUp(errorMessage: error.localizedDescription) 82 | print("recorder init failed") 83 | } 84 | } 85 | 86 | private func checkMicPermission() -> Bool { 87 | var permissionCheck: Bool = false 88 | 89 | switch AVAudioSession.sharedInstance().recordPermission { 90 | case AVAudioSession.RecordPermission.granted: 91 | permissionCheck = true 92 | case AVAudioSession.RecordPermission.denied: 93 | permissionCheck = false 94 | case AVAudioSession.RecordPermission.undetermined: 95 | AVAudioSession.sharedInstance().requestRecordPermission({ (granted) in 96 | if granted { 97 | permissionCheck = true 98 | } else { 99 | permissionCheck = false 100 | } 101 | }) 102 | default: 103 | break 104 | } 105 | 106 | return permissionCheck 107 | } 108 | 109 | private func normalizedPowerLevelFromDecibels(decibels: Float) -> Float { 110 | let minDecibels: Float = -60.0 111 | if (decibels < minDecibels || decibels.isZero) { 112 | return .zero 113 | } 114 | 115 | let powDecibels = pow(10.0, 0.05 * decibels) 116 | let powMinDecibels = pow(10.0, 0.05 * minDecibels) 117 | return pow((powDecibels - powMinDecibels) * (1.0 / (1.0 - powMinDecibels)), 1.0 / 2.0) 118 | 119 | } 120 | 121 | private func showErrorPopUp(errorMessage: String) { 122 | let alertController = UIAlertController(title: "Error", 123 | message: errorMessage, 124 | preferredStyle: .alert) 125 | let okAction = UIAlertAction(title: "Ok", style: .cancel, handler: nil) 126 | alertController.addAction(okAction) 127 | present(alertController, animated: true, completion: nil) 128 | } 129 | } 130 | 131 | -------------------------------------------------------------------------------- /SiriWave/SiriWave/Views/SiriWaveLine.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SiriWaveLine.swift 3 | // SiriWave 4 | // 5 | // Created by Alexander Shoshiashvili on 24.10.2019. 6 | // 7 | 8 | import UIKit 9 | 10 | public class SiriWaveLine { 11 | 12 | let GRAPH_X: CGFloat = 25 13 | let AMPLITUDE_FACTOR: CGFloat = 0.8 14 | let SPEED_FACTOR: CGFloat = 1 15 | let DEAD_PX: CGFloat = 2 16 | let ATT_FACTOR: CGFloat = 4 17 | let DESPAWN_FACTOR: CGFloat = 0.02 18 | let NOOFCURVES_RANGES: [CGFloat] = [5, 5] 19 | let AMPLITUDE_RANGES: [CGFloat] = [0.3, 1] 20 | let OFFSET_RANGES: [CGFloat] = [-3, 3] 21 | let WIDTH_RANGES: [CGFloat] = [1, 3] 22 | let SPEED_RANGES: [CGFloat] = [0.5, 1] 23 | let DESPAWN_TIMEOUT_RANGES: [CGFloat] = [500, 2000] 24 | 25 | var spawnAt: Int = Date().millisecondsSince1970 26 | var noOfCurves: Int! 27 | var phases: [CGFloat] = [] 28 | var offsets: [CGFloat] = [] 29 | var speeds: [CGFloat] = [] 30 | var finalAmplitudes: [CGFloat] = [] 31 | var widths: [CGFloat] = [] 32 | var amplitudes: [CGFloat] = [] 33 | var despawnTimeouts: [CGFloat] = [] 34 | var verses: [CGFloat] = [] 35 | var prevMaxY: CGFloat = 0 36 | 37 | public private (set) var amplitude: CGFloat 38 | public private (set) var speed: CGFloat 39 | public private (set) var pixelDepth: CGFloat 40 | public private (set) var heightMax: CGFloat 41 | public private (set) var width: CGFloat 42 | public private (set) var color: UIColor 43 | 44 | public init(amplitude: CGFloat, 45 | speed: CGFloat, 46 | pixelDepth: CGFloat, 47 | width: CGFloat, 48 | heightMax: CGFloat, 49 | color: UIColor) { 50 | 51 | self.amplitude = amplitude 52 | self.speed = speed 53 | self.pixelDepth = pixelDepth 54 | self.width = width 55 | self.heightMax = heightMax 56 | self.color = color 57 | 58 | commonInit() 59 | } 60 | 61 | private func commonInit() { 62 | self.spawnAt = Date().millisecondsSince1970 63 | self.noOfCurves = Int(floor(getRandomRange(NOOFCURVES_RANGES))) 64 | 65 | self.phases = Array(repeating: 0.0, count: noOfCurves) 66 | self.offsets = Array(repeating: 0.0, count: noOfCurves) 67 | self.speeds = Array(repeating: 0.0, count: noOfCurves) 68 | self.finalAmplitudes = Array(repeating: 0.0, count: noOfCurves) 69 | self.widths = Array(repeating: 0.0, count: noOfCurves) 70 | self.amplitudes = Array(repeating: 0.0, count: noOfCurves) 71 | self.despawnTimeouts = Array(repeating: 0.0, count: noOfCurves) 72 | self.verses = Array(repeating: 0.0, count: noOfCurves) 73 | 74 | for ci in 0.. maxY) { 132 | respawn() 133 | } 134 | 135 | prevMaxY = maxY 136 | } 137 | 138 | private func respawn() { 139 | commonInit() 140 | } 141 | private func respawnSingle(_ ci: Int) { 142 | self.phases[ci] = 0 143 | self.amplitudes[ci] = 0 144 | 145 | self.despawnTimeouts[ci] = getRandomRange(DESPAWN_TIMEOUT_RANGES) 146 | self.offsets[ci] = getRandomRange(OFFSET_RANGES) 147 | self.speeds[ci] = getRandomRange(SPEED_RANGES) 148 | self.finalAmplitudes[ci] = getRandomRange(AMPLITUDE_RANGES) 149 | self.widths[ci] = getRandomRange(WIDTH_RANGES) 150 | self.verses[ci] = getRandomRange([-1, 1]) 151 | } 152 | private func yRelativePos(_ i: CGFloat) -> CGFloat { 153 | var y: CGFloat = 0 154 | 155 | for ci in 0.. CGFloat { 175 | return AMPLITUDE_FACTOR * 176 | heightMax * 177 | amplitude * 178 | yRelativePos(i) * 179 | globalAttFn(i / GRAPH_X * 2) 180 | } 181 | private func xpos(_ i: CGFloat) -> CGFloat { 182 | return width * ((i + GRAPH_X) / (GRAPH_X * 2)) 183 | } 184 | 185 | private func getRandomRange(_ e: [CGFloat]) -> CGFloat { 186 | return e[0] + (CGFloat.random(in: 0 ..< 1) * (e[1] - e[0])) 187 | } 188 | private func globalAttFn(_ x: CGFloat) -> CGFloat { 189 | return pow((ATT_FACTOR) / (ATT_FACTOR + pow(x, 2)), 190 | ATT_FACTOR) 191 | } 192 | private func sinus(_ x: CGFloat, _ phase: CGFloat) -> CGFloat { 193 | return sin(x - phase) 194 | } 195 | 196 | } 197 | -------------------------------------------------------------------------------- /SiriWave/SiriWave/Views/SiriWaveView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SiriWaveView.swift 3 | // SiriWave 4 | // 5 | // Created by politom on 08/03/2019. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIColor { 11 | convenience init(red: Int, green: Int, blue: Int) { 12 | assert(red >= 0 && red <= 255, "Invalid red component") 13 | assert(green >= 0 && green <= 255, "Invalid green component") 14 | assert(blue >= 0 && blue <= 255, "Invalid blue component") 15 | 16 | self.init(red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue) / 255.0, alpha: 1.0) 17 | } 18 | 19 | convenience init(rgb: Int) { 20 | self.init( 21 | red: (rgb >> 16) & 0xFF, 22 | green: (rgb >> 8) & 0xFF, 23 | blue: rgb & 0xFF 24 | ) 25 | } 26 | } 27 | 28 | public class SiriWaveView: UIView { 29 | 30 | public private (set) var pixelDepth: CGFloat = 0.4 31 | public private (set) var amplitude: CGFloat = 1 32 | 33 | @IBInspectable 34 | public var idleAmplitude: CGFloat = 0.01 35 | @IBInspectable 36 | public var speed: CGFloat = 0.4 37 | 38 | public var colors: [UIColor] = [UIColor(rgb: 0xF66767), 39 | UIColor(rgb: 0xF9D5BB), 40 | UIColor(rgb: 0xD35656)] 41 | 42 | private var lines: [SiriWaveLine] = [] 43 | 44 | private var heightMax: CGFloat { 45 | return self.frame.height 46 | } 47 | private var width: CGFloat { 48 | return self.frame.width 49 | } 50 | 51 | public override init(frame: CGRect) { 52 | super.init(frame: frame) 53 | commonInit() 54 | } 55 | 56 | public required init?(coder aDecoder: NSCoder) { 57 | super.init(coder: aDecoder) 58 | commonInit() 59 | } 60 | 61 | public func update(_ level: CGFloat) { 62 | 63 | self.amplitude = fmax(level, 64 | idleAmplitude) 65 | 66 | self.setNeedsDisplay() 67 | 68 | } 69 | 70 | override public func draw(_ rect: CGRect) { 71 | 72 | if let ctx: CGContext = UIGraphicsGetCurrentContext() { 73 | ctx.setAlpha(0.7) 74 | ctx.setBlendMode(.lighten) 75 | 76 | drawSupportLine(ctx) 77 | 78 | for line in lines { 79 | line.drawLine(ctx, 80 | amplitude, 81 | speed) 82 | } 83 | } 84 | } 85 | 86 | private func commonInit() { 87 | 88 | for color in colors { 89 | 90 | lines.append(SiriWaveLine(amplitude: amplitude, 91 | speed: speed, 92 | pixelDepth: pixelDepth, 93 | width: width, 94 | heightMax: heightMax, 95 | color: color)) 96 | } 97 | 98 | } 99 | 100 | private func drawSupportLine(_ ctx: CGContext) { 101 | 102 | let colors = [UIColor.clear.cgColor, 103 | UIColor(red: 1, green: 1, blue: 1, alpha: 0.5).cgColor, 104 | UIColor(red: 1, green: 1, blue: 1, alpha: 0.5).cgColor, 105 | UIColor.clear.cgColor] 106 | 107 | let colorSpace = CGColorSpaceCreateDeviceRGB() 108 | let colorLocations: [CGFloat] = [0.0, 0.1, 1.0, 0.8, 1] 109 | let gradient = CGGradient(colorsSpace: colorSpace, 110 | colors: colors as CFArray, 111 | locations: colorLocations)! 112 | 113 | let startPoint = CGPoint(x: 0, y: (heightMax/2)-0.5) 114 | let endPoint = CGPoint(x: 0, y: (heightMax/2)+0.5) 115 | 116 | ctx.drawLinearGradient(gradient, 117 | start: startPoint, 118 | end: endPoint, 119 | options: []) 120 | } 121 | } 122 | 123 | 124 | -------------------------------------------------------------------------------- /siri-wave-ios9+.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapo80/SiriWave/de5d0327ae95fdf29fcd1f51a57468b1d8d484d8/siri-wave-ios9+.gif --------------------------------------------------------------------------------