Repository: ninjaprox/NVActivityIndicatorView Branch: master Commit: 9777b047a904 Files: 100 Total size: 615.7 KB Directory structure: gitextract_aoakydhx/ ├── .github/ │ └── workflows/ │ └── ios.yml ├── .gitignore ├── .jazzy.yaml ├── .swiftlint.yml ├── CHANGELOG.md ├── Example/ │ ├── AppDelegate.swift │ ├── Assets.xcassets/ │ │ ├── AppIcon.appiconset/ │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj/ │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ ├── SceneDelegate.swift │ └── ViewController.swift ├── LICENSE ├── NVActivityIndicatorView.podspec ├── NVActivityIndicatorView.xcodeproj/ │ ├── NVActivityIndicatorViewExtended_Info.plist │ ├── NVActivityIndicatorView_Info.plist │ ├── project.pbxproj │ ├── project.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ ├── IDEWorkspaceChecks.plist │ │ ├── WorkspaceSettings.xcsettings │ │ └── swiftpm/ │ │ └── Package.resolved │ └── xcshareddata/ │ └── xcschemes/ │ ├── Example.xcscheme │ └── NVActivityIndicatorView-Package.xcscheme ├── Package.swift ├── PrivacyInfo.xcprivacy ├── README.md ├── Sources/ │ ├── Base/ │ │ ├── Animations/ │ │ │ ├── NVActivityIndicatorAnimationAudioEqualizer.swift │ │ │ ├── NVActivityIndicatorAnimationBallBeat.swift │ │ │ ├── NVActivityIndicatorAnimationBallClipRotate.swift │ │ │ ├── NVActivityIndicatorAnimationBallClipRotateMultiple.swift │ │ │ ├── NVActivityIndicatorAnimationBallClipRotatePulse.swift │ │ │ ├── NVActivityIndicatorAnimationBallDoubleBounce.swift │ │ │ ├── NVActivityIndicatorAnimationBallGridBeat.swift │ │ │ ├── NVActivityIndicatorAnimationBallGridPulse.swift │ │ │ ├── NVActivityIndicatorAnimationBallPulse.swift │ │ │ ├── NVActivityIndicatorAnimationBallPulseRise.swift │ │ │ ├── NVActivityIndicatorAnimationBallPulseSync.swift │ │ │ ├── NVActivityIndicatorAnimationBallRotate.swift │ │ │ ├── NVActivityIndicatorAnimationBallRotateChase.swift │ │ │ ├── NVActivityIndicatorAnimationBallScale.swift │ │ │ ├── NVActivityIndicatorAnimationBallScaleMultiple.swift │ │ │ ├── NVActivityIndicatorAnimationBallScaleRipple.swift │ │ │ ├── NVActivityIndicatorAnimationBallScaleRippleMultiple.swift │ │ │ ├── NVActivityIndicatorAnimationBallSpinFadeLoader.swift │ │ │ ├── NVActivityIndicatorAnimationBallTrianglePath.swift │ │ │ ├── NVActivityIndicatorAnimationBallZigZag.swift │ │ │ ├── NVActivityIndicatorAnimationBallZigZagDeflect.swift │ │ │ ├── NVActivityIndicatorAnimationBlank.swift │ │ │ ├── NVActivityIndicatorAnimationCircleStrokeSpin.swift │ │ │ ├── NVActivityIndicatorAnimationCubeTransition.swift │ │ │ ├── NVActivityIndicatorAnimationLineScale.swift │ │ │ ├── NVActivityIndicatorAnimationLineScaleParty.swift │ │ │ ├── NVActivityIndicatorAnimationLineScalePulseOut.swift │ │ │ ├── NVActivityIndicatorAnimationLineScalePulseOutRapid.swift │ │ │ ├── NVActivityIndicatorAnimationLineSpinFadeLoader.swift │ │ │ ├── NVActivityIndicatorAnimationOrbit.swift │ │ │ ├── NVActivityIndicatorAnimationPacman.swift │ │ │ ├── NVActivityIndicatorAnimationSemiCircleSpin.swift │ │ │ ├── NVActivityIndicatorAnimationSquareSpin.swift │ │ │ └── NVActivityIndicatorAnimationTriangleSkewSpin.swift │ │ ├── NVActivityIndicatorAnimationDelegate.swift │ │ ├── NVActivityIndicatorShape.swift │ │ └── NVActivityIndicatorView.swift │ └── Extended/ │ ├── NVActivityIndicatorPresenter.swift │ └── NVActivityIndicatorViewable.swift ├── Tests/ │ ├── ActivityDataTests.swift │ ├── Info.plist │ ├── NVActivityIndicatorPresenterTests.swift │ ├── NVActivityIndicatorTypeTests.swift │ ├── NVActivityIndicatorViewTests.swift │ └── Tests-Bridging-Header.h ├── docs/ │ ├── CNAME │ ├── Classes/ │ │ └── NVActivityIndicatorView.html │ ├── Classes.html │ ├── Enums/ │ │ └── NVActivityIndicatorType.html │ ├── Enums.html │ ├── Typealiases.html │ ├── css/ │ │ ├── highlight.css │ │ └── jazzy.css │ ├── docsets/ │ │ ├── NVActivityIndicatorView.docset/ │ │ │ └── Contents/ │ │ │ ├── Info.plist │ │ │ └── Resources/ │ │ │ ├── Documents/ │ │ │ │ ├── Classes/ │ │ │ │ │ └── NVActivityIndicatorView.html │ │ │ │ ├── Classes.html │ │ │ │ ├── Enums/ │ │ │ │ │ └── NVActivityIndicatorType.html │ │ │ │ ├── Enums.html │ │ │ │ ├── Typealiases.html │ │ │ │ ├── css/ │ │ │ │ │ ├── highlight.css │ │ │ │ │ └── jazzy.css │ │ │ │ ├── index.html │ │ │ │ └── js/ │ │ │ │ ├── jazzy.js │ │ │ │ ├── jazzy.search.js │ │ │ │ └── typeahead.jquery.js │ │ │ └── docSet.dsidx │ │ └── NVActivityIndicatorView.tgz │ ├── index.html │ ├── js/ │ │ ├── jazzy.js │ │ ├── jazzy.search.js │ │ └── typeahead.jquery.js │ └── undocumented.json └── scripts/ └── bump-version.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/ios.yml ================================================ name: iOS starter workflow on: push: branches: [ "master" ] pull_request: branches: [ "master" ] jobs: build: name: Build and Test default scheme using any available iPhone simulator runs-on: macos-latest steps: - name: Checkout uses: actions/checkout@v3 - name: Set Default Scheme run: | scheme_list=$(xcodebuild -list -json | tr -d "\n") default=$(echo $scheme_list | ruby -e "require 'json'; puts JSON.parse(STDIN.gets)['project']['schemes'][0]") echo $default | cat >default echo Using default scheme: $default - name: Build env: scheme: ${{ 'default' }} platform: ${{ 'iOS Simulator' }} run: | # xcrun xctrace returns via stderr, not the expected stdout (see https://developer.apple.com/forums/thread/663959) device=`xcrun xctrace list devices 2>&1 | grep -oE 'iPhone.*?[^\(]+' | head -1 | awk '{$1=$1;print}' | sed -e "s/ Simulator$//"` if [ $scheme = default ]; then scheme=$(cat default); fi if [ "`ls -A | grep -i \\.xcworkspace\$`" ]; then filetype_parameter="workspace" && file_to_build="`ls -A | grep -i \\.xcworkspace\$`"; else filetype_parameter="project" && file_to_build="`ls -A | grep -i \\.xcodeproj\$`"; fi file_to_build=`echo $file_to_build | awk '{$1=$1;print}'` xcodebuild build-for-testing -scheme "$scheme" -"$filetype_parameter" "$file_to_build" -destination "platform=$platform,name=$device" -skipPackagePluginValidation -skipMacroValidation - name: Test env: scheme: ${{ 'default' }} platform: ${{ 'iOS Simulator' }} run: | # xcrun xctrace returns via stderr, not the expected stdout (see https://developer.apple.com/forums/thread/663959) device=`xcrun xctrace list devices 2>&1 | grep -oE 'iPhone.*?[^\(]+' | head -1 | awk '{$1=$1;print}' | sed -e "s/ Simulator$//"` if [ $scheme = default ]; then scheme=$(cat default); fi if [ "`ls -A | grep -i \\.xcworkspace\$`" ]; then filetype_parameter="workspace" && file_to_build="`ls -A | grep -i \\.xcworkspace\$`"; else filetype_parameter="project" && file_to_build="`ls -A | grep -i \\.xcodeproj\$`"; fi file_to_build=`echo $file_to_build | awk '{$1=$1;print}'` xcodebuild test-without-building -scheme "$scheme" -"$filetype_parameter" "$file_to_build" -destination "platform=$platform,name=$device" ================================================ FILE: .gitignore ================================================ # General .DS_Store .AppleDouble .LSOverride # Icon must end with two \r Icon # Thumbnails ._* # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns .com.apple.timemachine.donotpresent # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk # Xcode # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore ## User settings xcuserdata/ ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) *.xcscmblueprint *.xccheckout ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) build/ DerivedData/ *.moved-aside *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 ## Obj-C/Swift specific *.hmap ## App packaging *.ipa *.dSYM.zip *.dSYM ## Playgrounds timeline.xctimeline playground.xcworkspace # Swift Package Manager # # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. # Packages/ # Package.pins # Package.resolved # *.xcodeproj # # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata # hence it is not needed unless you have added a package configuration file to your project .swiftpm .build/ # CocoaPods # # We recommend against adding the Pods directory to your .gitignore. However # you should judge for yourself, the pros and cons are mentioned at: # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control # # Pods/ # # Add this line if you want to avoid checking in source code from the Xcode workspace # *.xcworkspace # Carthage # # Add this line if you want to avoid checking in source code from Carthage dependencies. # Carthage/Checkouts Carthage/Build/ # Accio dependency management Dependencies/ .accio/ # fastlane # # It is recommended to not store the screenshots in the git repo. # Instead, use fastlane to re-generate the screenshots whenever they are needed. # For more information about the recommended setup visit: # https://docs.fastlane.tools/best-practices/source-control/#source-control fastlane/report.xml fastlane/Preview.html fastlane/screenshots/**/*.png fastlane/test_output # Code Injection # # After new code Injection tools there's a generated folder /iOSInjectionProject # https://github.com/johnno1962/injectionforxcode iOSInjectionProject/ ================================================ FILE: .jazzy.yaml ================================================ readme: README.md author: Vinh Nguyen author_url: https://github.com/ninjaprox github_url: https://github.com/ninjaprox/NVActivityIndicatorView theme: fullwidth disable_search: true hide_documentation_coverage: true clean: true module: NVActivityIndicatorView module_version: 5.2.0 build_tool_arguments: - -scheme - NVActivityIndicatorView-Package - -project - NVActivityIndicatorView.xcodeproj ================================================ FILE: .swiftlint.yml ================================================ disabled_rules: - line_length type_name: max_length: 60 identifier_name: excluded: - x - y - i - j function_body_length: warning: 50 ================================================ FILE: CHANGELOG.md ================================================ # Change log ## [5.2.0](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/5.2.0) - Add privacy manifest (#349) ## [5.1.1](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/5.1.1) - Fix #319 (amendment) ## [5.1.0](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/5.1.0) - Fix #319 ## [5.0.1](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/5.0.1) - Fix #314 ## [5.0.0](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/5.0.0) - Deprecate `NVActivityIndicatorViewable` and `NVActivityIndicatorPresenter` - By default, `NVActivityIndicatorView` pod doesn't include `NVActivityIndicatorViewable` and `NVActivityIndicatorPresenter`, use `NVActivityIndicatorView/Extended` instead - The same applies for `NVActivityIndicatorView` and `NVActivityIndicatorViewExtended` when using Swift Package Manager - Drop support for Swift <4.2 ## [4.8.0](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/4.8.0) - Add Swift Package Manager support (#290) ## [4.7.0](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/4.7.0) - Default nil fadeInAnimation and fadeOutAnimation (#264) - Swift 5 migration (#274) ## [4.6.1](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/4.6.1) - Fix #261 ## [4.6.0](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/4.6.0) - New NVActivityIndicatorAnimationBallDoubleBounce animation (#258) - Unify product name (#260) ## [4.5.1](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/4.5.1) - Fix #251 ## [4.5.0](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/4.5.0) - Add tvOS target (#248) ## [4.4.1](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/4.4.1) - Don’t start/stop animating if unnecessary (#239) - Specify Swift version in Podspec (#245) ## [4.4.0](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/4.4.0) - Fix #211 - Swift 4.2 migration ## [4.3.0](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/4.3.0) - Handle starting while waiting to stop (#218) - UI blocker fade in/out animation (#219) ## [4.2.1](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/4.2.1) - Fix #213 ## [4.2.0](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/4.2.0) - Refactor `NVActivityIndicatorPresenter` with state pattern - Expose `isAnimating` from `NVActivityIndicatorPresenter` and `NVActivityIndicatorViewable` (#204) - Fix tests ## [4.1.1](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/4.1.1) - Fix #186, #189 ## [4.1.0](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/4.1.0) - Introduce message spacing - New circle stroke spin animation ## [4.0.1](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/4.0.1) - Fix view in auto layout - Fix #173, #180 ## [4.0.0](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/4.0.0) - Update to Swift 4 ## [3.7.0](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/3.7.0) - Support app extension ## [3.6.1](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/3.6.1) - Fix #138 ## [3.6.0](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/3.6.0) - Fix #128 ## [3.5.2](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/3.5.2) - Fix #132 ## [3.5.1](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/3.5.1) - Fix #123 ## [v3.5](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/v3.5) - Fix #121 - Fix setting message ## [v3.4](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/v3.4) - Fix constraint conflict in UI blocker ## [v3.3](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/v3.3) - Able to change text color in UI blocker - Fix UI blocker layer on orientation change ## [v3.2](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/v3.2) - Able to change UI blocker background color - Able to change message in UI blocker during displaying ## [v3.1](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/v3.1) - Optimize compile time - Update setting to Swift 3.0.1 - Add default message - Custom message font - Auto message width ## [v3.0](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/v3.0) - Support Xcode 8 and Swift 3 ## [v2.12](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/v2.12) - Fix infinite loop caused by `stopAnimation()` (#67) ## [v2.11](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/v2.11) - Fix when used with MBProgressHUD - Abilitiy to display UI blocker anywher - Time threshold to display UI blocker - Minimum display time of UI blocker - Deprecate some APIs - Add tests ## [v2.10](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/v2.10) - Fix UI blocker not showing full screen - Support tvOS ## [v2.9](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/v2.9) - Ability to change size of animation in UI blocker ## [v2.8](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/v2.8) - New Orbit animation - New Audio equalizer animation ## [v2.7](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/v2.7) - Display as UI blocker for UIViewController - Be able to change defaults ## [v2.6](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/v2.6) - Fix BallSpinFadeLoader animation - Fix LineSpinFadeLoader animation - Fix BallRotateChase animation - Change default animation to BallSpinFadeLoader ## [v2.5](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/v2.5) - Fix padding issues ## [v2.4](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/v2.4) - Fix autolayout compability when using in Storyboard - Support for IBInpectable ## [v2.3](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/v2.3) - Fix bug when orientation changes once stopped animation - Update document ## [v2.2](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/v2.2) - Public properties of animation - Public hidesWhenStopped and animating - New animation: - Ball rotate chase ## [v2.1](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/v2.1) - Support Carthage ## [v2.0](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/v2.0) - Update to Swift 2.0 ## [v1.3](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/v1.3) - More convenient constructor - Hide when stopped option ## [v1.2](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/v1.2) - Fix issue when using as framework - Set default animation type to pacman - Add documentation ## [v1.1](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/v1.1) - Change project structure to work well with CocoaPods - Small fix in `podspec` ## [v1.0](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/v1.0) - Initial release - Full implementations of 28 loaders from loaders.css ================================================ FILE: Example/AppDelegate.swift ================================================ // // AppDelegate.swift // Example // // The MIT License (MIT) // Copyright (c) 2020 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } // MARK: UISceneSession Lifecycle func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { // Called when a new scene session is being created. // Use this method to select a configuration to create the new scene with. return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { // Called when the user discards a scene session. // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. // Use this method to release any resources that were specific to the discarded scenes, as they will not return. } } ================================================ FILE: Example/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "20x20", "scale" : "2x" }, { "idiom" : "iphone", "size" : "20x20", "scale" : "3x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "3x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "3x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "3x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "1x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "2x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "1x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "2x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "1x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "2x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "1x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "2x" }, { "idiom" : "ipad", "size" : "83.5x83.5", "scale" : "2x" }, { "idiom" : "ios-marketing", "size" : "1024x1024", "scale" : "1x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Example/Assets.xcassets/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Example/Base.lproj/LaunchScreen.storyboard ================================================ ================================================ FILE: Example/Base.lproj/Main.storyboard ================================================ ================================================ FILE: Example/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString 5.2.0 CFBundleVersion 48 LSRequiresIPhoneOS UIApplicationSceneManifest UIApplicationSupportsMultipleScenes UISceneConfigurations UIWindowSceneSessionRoleApplication UISceneConfigurationName Default Configuration UISceneDelegateClassName $(PRODUCT_MODULE_NAME).SceneDelegate UISceneStoryboardFile Main UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: Example/SceneDelegate.swift ================================================ // // SceneDelegate.swift // Example // // The MIT License (MIT) // Copyright (c) 2020 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // import UIKit class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). guard (scene as? UIWindowScene) != nil else { return } } func sceneDidDisconnect(_ scene: UIScene) { // Called as the scene is being released by the system. // This occurs shortly after the scene enters the background, or when its session is discarded. // Release any resources associated with this scene that can be re-created the next time the scene connects. // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). } func sceneDidBecomeActive(_ scene: UIScene) { // Called when the scene has moved from an inactive state to an active state. // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. } func sceneWillResignActive(_ scene: UIScene) { // Called when the scene will move from an active state to an inactive state. // This may occur due to temporary interruptions (ex. an incoming phone call). } func sceneWillEnterForeground(_ scene: UIScene) { // Called as the scene transitions from the background to the foreground. // Use this method to undo the changes made on entering the background. } func sceneDidEnterBackground(_ scene: UIScene) { // Called as the scene transitions from the foreground to the background. // Use this method to save data, release shared resources, and store enough scene-specific state information // to restore the scene back to its current state. } } ================================================ FILE: Example/ViewController.swift ================================================ // // ViewController.swift // Example // // The MIT License (MIT) // Copyright (c) 2020 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // import UIKit import NVActivityIndicatorView import NVActivityIndicatorViewExtended class ViewController: UIViewController, NVActivityIndicatorViewable { private let presentingIndicatorTypes = { return NVActivityIndicatorType.allCases.filter { $0 != .blank } }() override func viewDidLoad() { super.viewDidLoad() self.view.backgroundColor = UIColor(red: CGFloat(237 / 255.0), green: CGFloat(85 / 255.0), blue: CGFloat(101 / 255.0), alpha: 1) } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() let safeArea = view.safeAreaLayoutGuide.layoutFrame var cols = 4 var rows = Int(ceil(Double(presentingIndicatorTypes.count) / 4.0)) if safeArea.width > safeArea.height { //Landscape cols = Int(ceil(Double(presentingIndicatorTypes.count) / 4.0)) rows = 4 } let cellWidth = Int(safeArea.width / CGFloat(cols)) let cellHeight = Int(safeArea.height / CGFloat(rows)) self.view.subviews.forEach { $0.removeFromSuperview() } for (index, indicatorType) in presentingIndicatorTypes.enumerated() { let x = index % cols * cellWidth + Int(safeArea.origin.x) let y = index / cols * cellHeight + Int(safeArea.origin.y) let frame = CGRect(x: x, y: y, width: cellWidth, height: cellHeight) let activityIndicatorView = NVActivityIndicatorView(frame: frame, type: indicatorType) let animationTypeLabel = UILabel(frame: frame) animationTypeLabel.text = String(index) animationTypeLabel.sizeToFit() animationTypeLabel.allowsDefaultTighteningForTruncation = true animationTypeLabel.textColor = UIColor.white animationTypeLabel.frame.origin.x += 5 animationTypeLabel.frame.origin.y += CGFloat(cellHeight) - animationTypeLabel.frame.size.height activityIndicatorView.padding = 20 if indicatorType == NVActivityIndicatorType.orbit { activityIndicatorView.padding = 0 } self.view.addSubview(activityIndicatorView) self.view.addSubview(animationTypeLabel) activityIndicatorView.startAnimating() let button = UIButton(frame: frame) button.tag = index button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside) self.view.addSubview(button) } } @objc func buttonTapped(_ sender: UIButton) { let size = CGSize(width: 30, height: 30) let selectedIndicatorIndex = sender.tag let indicatorType = presentingIndicatorTypes[selectedIndicatorIndex] startAnimating(size, message: "Loading...", type: indicatorType, fadeInAnimation: nil) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1.5) { NVActivityIndicatorPresenter.sharedInstance.setMessage("Authenticating...") } DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) { self.stopAnimating(nil) } } } ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2016 Vinh Nguyen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: NVActivityIndicatorView.podspec ================================================ Pod::Spec.new do |s| s.name = 'NVActivityIndicatorView' s.version = '5.2.0' s.summary = 'A collection of awesome loading animations' s.homepage = 'https://github.com/ninjaprox/NVActivityIndicatorView' s.screenshot = 'https://raw.githubusercontent.com/ninjaprox/NVActivityIndicatorView/master/Demo.gif' s.license = { type: 'MIT', file: 'LICENSE' } s.author = { 'Vinh Nguyen' => 'ninjaprox@gmail.com' } s.social_media_url = 'http://twitter.com/ninjaprox' s.documentation_url = 'https://nvactivityindicatorview.vinhis.me' s.ios.deployment_target = '9.0' s.tvos.deployment_target = '9.0' s.swift_version = '5.0' s.source = { git: 'https://github.com/ninjaprox/NVActivityIndicatorView.git', tag: s.version } s.subspec 'Base' do |ss| ss.source_files = 'Sources/Base/**/*.swift' end s.subspec 'Extended' do |ss| ss.dependency 'NVActivityIndicatorView/Base' ss.source_files = 'Sources/Extended/**/*.swift' end s.default_subspec = 'Base' s.frameworks = 'UIKit', 'QuartzCore' end ================================================ FILE: NVActivityIndicatorView.xcodeproj/NVActivityIndicatorViewExtended_Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType FMWK CFBundleShortVersionString 5.2.0 CFBundleSignature ???? CFBundleVersion 48 NSPrincipalClass ================================================ FILE: NVActivityIndicatorView.xcodeproj/NVActivityIndicatorView_Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType FMWK CFBundleShortVersionString 5.2.0 CFBundleSignature ???? CFBundleVersion 48 NSPrincipalClass ================================================ FILE: NVActivityIndicatorView.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 54; objects = { /* Begin PBXBuildFile section */ 1F1836E0248CEFC200583430 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F1836DF248CEFC200583430 /* AppDelegate.swift */; }; 1F1836E2248CEFC200583430 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F1836E1248CEFC200583430 /* SceneDelegate.swift */; }; 1F1836E4248CEFC200583430 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F1836E3248CEFC200583430 /* ViewController.swift */; }; 1F1836E7248CEFC200583430 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1F1836E5248CEFC200583430 /* Main.storyboard */; }; 1F1836E9248CEFC300583430 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1F1836E8248CEFC300583430 /* Assets.xcassets */; }; 1F1836EC248CEFC300583430 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1F1836EA248CEFC300583430 /* LaunchScreen.storyboard */; }; 1F1836F2248CF08B00583430 /* NVActivityIndicatorViewExtended.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "NVActivityIndicatorView::NVActivityIndicatorViewExtended::Product" /* NVActivityIndicatorViewExtended.framework */; }; 1F1836F3248CF08B00583430 /* NVActivityIndicatorViewExtended.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = "NVActivityIndicatorView::NVActivityIndicatorViewExtended::Product" /* NVActivityIndicatorViewExtended.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 1F183707248CF29F00583430 /* ActivityDataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F183706248CF29F00583430 /* ActivityDataTests.swift */; }; 1F18370B248CF31500583430 /* NVActivityIndicatorPresenterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F183708248CF31500583430 /* NVActivityIndicatorPresenterTests.swift */; }; 1F18370C248CF31500583430 /* NVActivityIndicatorViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F183709248CF31500583430 /* NVActivityIndicatorViewTests.swift */; }; 1F18370D248CF31500583430 /* NVActivityIndicatorTypeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F18370A248CF31500583430 /* NVActivityIndicatorTypeTests.swift */; }; OBJ_102 /* NVActivityIndicatorPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_48 /* NVActivityIndicatorPresenter.swift */; }; OBJ_103 /* NVActivityIndicatorViewable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_49 /* NVActivityIndicatorViewable.swift */; }; OBJ_105 /* NVActivityIndicatorView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "NVActivityIndicatorView::NVActivityIndicatorView::Product" /* NVActivityIndicatorView.framework */; }; OBJ_112 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_6 /* Package.swift */; }; OBJ_59 /* NVActivityIndicatorAnimationAudioEqualizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* NVActivityIndicatorAnimationAudioEqualizer.swift */; }; OBJ_60 /* NVActivityIndicatorAnimationBallBeat.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_11 /* NVActivityIndicatorAnimationBallBeat.swift */; }; OBJ_61 /* NVActivityIndicatorAnimationBallClipRotate.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* NVActivityIndicatorAnimationBallClipRotate.swift */; }; OBJ_62 /* NVActivityIndicatorAnimationBallClipRotateMultiple.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* NVActivityIndicatorAnimationBallClipRotateMultiple.swift */; }; OBJ_63 /* NVActivityIndicatorAnimationBallClipRotatePulse.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_14 /* NVActivityIndicatorAnimationBallClipRotatePulse.swift */; }; OBJ_64 /* NVActivityIndicatorAnimationBallDoubleBounce.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_15 /* NVActivityIndicatorAnimationBallDoubleBounce.swift */; }; OBJ_65 /* NVActivityIndicatorAnimationBallGridBeat.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_16 /* NVActivityIndicatorAnimationBallGridBeat.swift */; }; OBJ_66 /* NVActivityIndicatorAnimationBallGridPulse.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_17 /* NVActivityIndicatorAnimationBallGridPulse.swift */; }; OBJ_67 /* NVActivityIndicatorAnimationBallPulse.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_18 /* NVActivityIndicatorAnimationBallPulse.swift */; }; OBJ_68 /* NVActivityIndicatorAnimationBallPulseRise.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_19 /* NVActivityIndicatorAnimationBallPulseRise.swift */; }; OBJ_69 /* NVActivityIndicatorAnimationBallPulseSync.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_20 /* NVActivityIndicatorAnimationBallPulseSync.swift */; }; OBJ_70 /* NVActivityIndicatorAnimationBallRotate.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_21 /* NVActivityIndicatorAnimationBallRotate.swift */; }; OBJ_71 /* NVActivityIndicatorAnimationBallRotateChase.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_22 /* NVActivityIndicatorAnimationBallRotateChase.swift */; }; OBJ_72 /* NVActivityIndicatorAnimationBallScale.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_23 /* NVActivityIndicatorAnimationBallScale.swift */; }; OBJ_73 /* NVActivityIndicatorAnimationBallScaleMultiple.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_24 /* NVActivityIndicatorAnimationBallScaleMultiple.swift */; }; OBJ_74 /* NVActivityIndicatorAnimationBallScaleRipple.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_25 /* NVActivityIndicatorAnimationBallScaleRipple.swift */; }; OBJ_75 /* NVActivityIndicatorAnimationBallScaleRippleMultiple.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_26 /* NVActivityIndicatorAnimationBallScaleRippleMultiple.swift */; }; OBJ_76 /* NVActivityIndicatorAnimationBallSpinFadeLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_27 /* NVActivityIndicatorAnimationBallSpinFadeLoader.swift */; }; OBJ_77 /* NVActivityIndicatorAnimationBallTrianglePath.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_28 /* NVActivityIndicatorAnimationBallTrianglePath.swift */; }; OBJ_78 /* NVActivityIndicatorAnimationBallZigZag.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_29 /* NVActivityIndicatorAnimationBallZigZag.swift */; }; OBJ_79 /* NVActivityIndicatorAnimationBallZigZagDeflect.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_30 /* NVActivityIndicatorAnimationBallZigZagDeflect.swift */; }; OBJ_80 /* NVActivityIndicatorAnimationBlank.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_31 /* NVActivityIndicatorAnimationBlank.swift */; }; OBJ_81 /* NVActivityIndicatorAnimationCircleStrokeSpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_32 /* NVActivityIndicatorAnimationCircleStrokeSpin.swift */; }; OBJ_82 /* NVActivityIndicatorAnimationCubeTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_33 /* NVActivityIndicatorAnimationCubeTransition.swift */; }; OBJ_83 /* NVActivityIndicatorAnimationLineScale.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_34 /* NVActivityIndicatorAnimationLineScale.swift */; }; OBJ_84 /* NVActivityIndicatorAnimationLineScaleParty.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_35 /* NVActivityIndicatorAnimationLineScaleParty.swift */; }; OBJ_85 /* NVActivityIndicatorAnimationLineScalePulseOut.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_36 /* NVActivityIndicatorAnimationLineScalePulseOut.swift */; }; OBJ_86 /* NVActivityIndicatorAnimationLineScalePulseOutRapid.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_37 /* NVActivityIndicatorAnimationLineScalePulseOutRapid.swift */; }; OBJ_87 /* NVActivityIndicatorAnimationLineSpinFadeLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_38 /* NVActivityIndicatorAnimationLineSpinFadeLoader.swift */; }; OBJ_88 /* NVActivityIndicatorAnimationOrbit.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_39 /* NVActivityIndicatorAnimationOrbit.swift */; }; OBJ_89 /* NVActivityIndicatorAnimationPacman.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_40 /* NVActivityIndicatorAnimationPacman.swift */; }; OBJ_90 /* NVActivityIndicatorAnimationSemiCircleSpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_41 /* NVActivityIndicatorAnimationSemiCircleSpin.swift */; }; OBJ_91 /* NVActivityIndicatorAnimationSquareSpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_42 /* NVActivityIndicatorAnimationSquareSpin.swift */; }; OBJ_92 /* NVActivityIndicatorAnimationTriangleSkewSpin.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_43 /* NVActivityIndicatorAnimationTriangleSkewSpin.swift */; }; OBJ_93 /* NVActivityIndicatorAnimationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_44 /* NVActivityIndicatorAnimationDelegate.swift */; }; OBJ_94 /* NVActivityIndicatorShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_45 /* NVActivityIndicatorShape.swift */; }; OBJ_95 /* NVActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_46 /* NVActivityIndicatorView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 1F1836A8248CEC7400583430 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = OBJ_1 /* Project object */; proxyType = 1; remoteGlobalIDString = "NVActivityIndicatorView::NVActivityIndicatorView"; remoteInfo = NVActivityIndicatorView; }; 1F1836F4248CF08B00583430 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = OBJ_1 /* Project object */; proxyType = 1; remoteGlobalIDString = "NVActivityIndicatorView::NVActivityIndicatorViewExtended"; remoteInfo = NVActivityIndicatorViewExtended; }; 1F183700248CF25600583430 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = OBJ_1 /* Project object */; proxyType = 1; remoteGlobalIDString = 1F1836DC248CEFC200583430; remoteInfo = Example; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ 1F1836F6248CF08B00583430 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( 1F1836F3248CF08B00583430 /* NVActivityIndicatorViewExtended.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 1F1836DD248CEFC200583430 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 1F1836DF248CEFC200583430 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 1F1836E1248CEFC200583430 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 1F1836E3248CEFC200583430 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 1F1836E6248CEFC200583430 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 1F1836E8248CEFC300583430 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 1F1836EB248CEFC300583430 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 1F1836ED248CEFC300583430 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 1F1836FB248CF25600583430 /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 1F1836FF248CF25600583430 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 1F183705248CF29E00583430 /* Tests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-Bridging-Header.h"; sourceTree = ""; }; 1F183706248CF29F00583430 /* ActivityDataTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityDataTests.swift; sourceTree = ""; }; 1F183708248CF31500583430 /* NVActivityIndicatorPresenterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorPresenterTests.swift; sourceTree = ""; }; 1F183709248CF31500583430 /* NVActivityIndicatorViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorViewTests.swift; sourceTree = ""; }; 1F18370A248CF31500583430 /* NVActivityIndicatorTypeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorTypeTests.swift; sourceTree = ""; }; 1F73073E2BCFFEC700FBF25A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; "NVActivityIndicatorView::NVActivityIndicatorView::Product" /* NVActivityIndicatorView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = NVActivityIndicatorView.framework; sourceTree = BUILT_PRODUCTS_DIR; }; "NVActivityIndicatorView::NVActivityIndicatorViewExtended::Product" /* NVActivityIndicatorViewExtended.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = NVActivityIndicatorViewExtended.framework; sourceTree = BUILT_PRODUCTS_DIR; }; OBJ_10 /* NVActivityIndicatorAnimationAudioEqualizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationAudioEqualizer.swift; sourceTree = ""; }; OBJ_11 /* NVActivityIndicatorAnimationBallBeat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationBallBeat.swift; sourceTree = ""; }; OBJ_12 /* NVActivityIndicatorAnimationBallClipRotate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationBallClipRotate.swift; sourceTree = ""; }; OBJ_13 /* NVActivityIndicatorAnimationBallClipRotateMultiple.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationBallClipRotateMultiple.swift; sourceTree = ""; }; OBJ_14 /* NVActivityIndicatorAnimationBallClipRotatePulse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationBallClipRotatePulse.swift; sourceTree = ""; }; OBJ_15 /* NVActivityIndicatorAnimationBallDoubleBounce.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationBallDoubleBounce.swift; sourceTree = ""; }; OBJ_16 /* NVActivityIndicatorAnimationBallGridBeat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationBallGridBeat.swift; sourceTree = ""; }; OBJ_17 /* NVActivityIndicatorAnimationBallGridPulse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationBallGridPulse.swift; sourceTree = ""; }; OBJ_18 /* NVActivityIndicatorAnimationBallPulse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationBallPulse.swift; sourceTree = ""; }; OBJ_19 /* NVActivityIndicatorAnimationBallPulseRise.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationBallPulseRise.swift; sourceTree = ""; }; OBJ_20 /* NVActivityIndicatorAnimationBallPulseSync.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationBallPulseSync.swift; sourceTree = ""; }; OBJ_21 /* NVActivityIndicatorAnimationBallRotate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationBallRotate.swift; sourceTree = ""; }; OBJ_22 /* NVActivityIndicatorAnimationBallRotateChase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationBallRotateChase.swift; sourceTree = ""; }; OBJ_23 /* NVActivityIndicatorAnimationBallScale.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationBallScale.swift; sourceTree = ""; }; OBJ_24 /* NVActivityIndicatorAnimationBallScaleMultiple.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationBallScaleMultiple.swift; sourceTree = ""; }; OBJ_25 /* NVActivityIndicatorAnimationBallScaleRipple.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationBallScaleRipple.swift; sourceTree = ""; }; OBJ_26 /* NVActivityIndicatorAnimationBallScaleRippleMultiple.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationBallScaleRippleMultiple.swift; sourceTree = ""; }; OBJ_27 /* NVActivityIndicatorAnimationBallSpinFadeLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationBallSpinFadeLoader.swift; sourceTree = ""; }; OBJ_28 /* NVActivityIndicatorAnimationBallTrianglePath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationBallTrianglePath.swift; sourceTree = ""; }; OBJ_29 /* NVActivityIndicatorAnimationBallZigZag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationBallZigZag.swift; sourceTree = ""; }; OBJ_30 /* NVActivityIndicatorAnimationBallZigZagDeflect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationBallZigZagDeflect.swift; sourceTree = ""; }; OBJ_31 /* NVActivityIndicatorAnimationBlank.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationBlank.swift; sourceTree = ""; }; OBJ_32 /* NVActivityIndicatorAnimationCircleStrokeSpin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationCircleStrokeSpin.swift; sourceTree = ""; }; OBJ_33 /* NVActivityIndicatorAnimationCubeTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationCubeTransition.swift; sourceTree = ""; }; OBJ_34 /* NVActivityIndicatorAnimationLineScale.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationLineScale.swift; sourceTree = ""; }; OBJ_35 /* NVActivityIndicatorAnimationLineScaleParty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationLineScaleParty.swift; sourceTree = ""; }; OBJ_36 /* NVActivityIndicatorAnimationLineScalePulseOut.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationLineScalePulseOut.swift; sourceTree = ""; }; OBJ_37 /* NVActivityIndicatorAnimationLineScalePulseOutRapid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationLineScalePulseOutRapid.swift; sourceTree = ""; }; OBJ_38 /* NVActivityIndicatorAnimationLineSpinFadeLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationLineSpinFadeLoader.swift; sourceTree = ""; }; OBJ_39 /* NVActivityIndicatorAnimationOrbit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationOrbit.swift; sourceTree = ""; }; OBJ_40 /* NVActivityIndicatorAnimationPacman.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationPacman.swift; sourceTree = ""; }; OBJ_41 /* NVActivityIndicatorAnimationSemiCircleSpin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationSemiCircleSpin.swift; sourceTree = ""; }; OBJ_42 /* NVActivityIndicatorAnimationSquareSpin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationSquareSpin.swift; sourceTree = ""; }; OBJ_43 /* NVActivityIndicatorAnimationTriangleSkewSpin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationTriangleSkewSpin.swift; sourceTree = ""; }; OBJ_44 /* NVActivityIndicatorAnimationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorAnimationDelegate.swift; sourceTree = ""; }; OBJ_45 /* NVActivityIndicatorShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorShape.swift; sourceTree = ""; }; OBJ_46 /* NVActivityIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorView.swift; sourceTree = ""; }; OBJ_48 /* NVActivityIndicatorPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorPresenter.swift; sourceTree = ""; }; OBJ_49 /* NVActivityIndicatorViewable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVActivityIndicatorViewable.swift; sourceTree = ""; }; OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 1F1836DA248CEFC200583430 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 1F1836F2248CF08B00583430 /* NVActivityIndicatorViewExtended.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 1F1836F8248CF25600583430 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; OBJ_104 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 0; files = ( OBJ_105 /* NVActivityIndicatorView.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; OBJ_96 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 0; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 1F1836DE248CEFC200583430 /* Example */ = { isa = PBXGroup; children = ( 1F1836DF248CEFC200583430 /* AppDelegate.swift */, 1F1836E1248CEFC200583430 /* SceneDelegate.swift */, 1F1836E3248CEFC200583430 /* ViewController.swift */, 1F1836E5248CEFC200583430 /* Main.storyboard */, 1F1836E8248CEFC300583430 /* Assets.xcassets */, 1F1836EA248CEFC300583430 /* LaunchScreen.storyboard */, 1F1836ED248CEFC300583430 /* Info.plist */, ); path = Example; sourceTree = ""; }; 1F1836F1248CF08B00583430 /* Frameworks */ = { isa = PBXGroup; children = ( ); name = Frameworks; sourceTree = ""; }; 1F1836FC248CF25600583430 /* Tests */ = { isa = PBXGroup; children = ( 1F183708248CF31500583430 /* NVActivityIndicatorPresenterTests.swift */, 1F18370A248CF31500583430 /* NVActivityIndicatorTypeTests.swift */, 1F183709248CF31500583430 /* NVActivityIndicatorViewTests.swift */, 1F183706248CF29F00583430 /* ActivityDataTests.swift */, 1F1836FF248CF25600583430 /* Info.plist */, 1F183705248CF29E00583430 /* Tests-Bridging-Header.h */, ); path = Tests; sourceTree = ""; }; OBJ_47 /* NVActivityIndicatorViewExtended */ = { isa = PBXGroup; children = ( OBJ_48 /* NVActivityIndicatorPresenter.swift */, OBJ_49 /* NVActivityIndicatorViewable.swift */, ); name = NVActivityIndicatorViewExtended; path = Sources/Extended; sourceTree = SOURCE_ROOT; }; OBJ_5 = { isa = PBXGroup; children = ( 1F73073E2BCFFEC700FBF25A /* PrivacyInfo.xcprivacy */, OBJ_6 /* Package.swift */, OBJ_7 /* Sources */, 1F1836DE248CEFC200583430 /* Example */, 1F1836FC248CF25600583430 /* Tests */, OBJ_51 /* Products */, 1F1836F1248CF08B00583430 /* Frameworks */, ); sourceTree = ""; }; OBJ_51 /* Products */ = { isa = PBXGroup; children = ( "NVActivityIndicatorView::NVActivityIndicatorView::Product" /* NVActivityIndicatorView.framework */, "NVActivityIndicatorView::NVActivityIndicatorViewExtended::Product" /* NVActivityIndicatorViewExtended.framework */, 1F1836DD248CEFC200583430 /* Example.app */, 1F1836FB248CF25600583430 /* Tests.xctest */, ); name = Products; sourceTree = BUILT_PRODUCTS_DIR; }; OBJ_7 /* Sources */ = { isa = PBXGroup; children = ( OBJ_8 /* NVActivityIndicatorView */, OBJ_47 /* NVActivityIndicatorViewExtended */, ); name = Sources; sourceTree = SOURCE_ROOT; }; OBJ_8 /* NVActivityIndicatorView */ = { isa = PBXGroup; children = ( OBJ_9 /* Animations */, OBJ_44 /* NVActivityIndicatorAnimationDelegate.swift */, OBJ_45 /* NVActivityIndicatorShape.swift */, OBJ_46 /* NVActivityIndicatorView.swift */, ); name = NVActivityIndicatorView; path = Sources/Base; sourceTree = SOURCE_ROOT; }; OBJ_9 /* Animations */ = { isa = PBXGroup; children = ( OBJ_10 /* NVActivityIndicatorAnimationAudioEqualizer.swift */, OBJ_11 /* NVActivityIndicatorAnimationBallBeat.swift */, OBJ_12 /* NVActivityIndicatorAnimationBallClipRotate.swift */, OBJ_13 /* NVActivityIndicatorAnimationBallClipRotateMultiple.swift */, OBJ_14 /* NVActivityIndicatorAnimationBallClipRotatePulse.swift */, OBJ_15 /* NVActivityIndicatorAnimationBallDoubleBounce.swift */, OBJ_16 /* NVActivityIndicatorAnimationBallGridBeat.swift */, OBJ_17 /* NVActivityIndicatorAnimationBallGridPulse.swift */, OBJ_18 /* NVActivityIndicatorAnimationBallPulse.swift */, OBJ_19 /* NVActivityIndicatorAnimationBallPulseRise.swift */, OBJ_20 /* NVActivityIndicatorAnimationBallPulseSync.swift */, OBJ_21 /* NVActivityIndicatorAnimationBallRotate.swift */, OBJ_22 /* NVActivityIndicatorAnimationBallRotateChase.swift */, OBJ_23 /* NVActivityIndicatorAnimationBallScale.swift */, OBJ_24 /* NVActivityIndicatorAnimationBallScaleMultiple.swift */, OBJ_25 /* NVActivityIndicatorAnimationBallScaleRipple.swift */, OBJ_26 /* NVActivityIndicatorAnimationBallScaleRippleMultiple.swift */, OBJ_27 /* NVActivityIndicatorAnimationBallSpinFadeLoader.swift */, OBJ_28 /* NVActivityIndicatorAnimationBallTrianglePath.swift */, OBJ_29 /* NVActivityIndicatorAnimationBallZigZag.swift */, OBJ_30 /* NVActivityIndicatorAnimationBallZigZagDeflect.swift */, OBJ_31 /* NVActivityIndicatorAnimationBlank.swift */, OBJ_32 /* NVActivityIndicatorAnimationCircleStrokeSpin.swift */, OBJ_33 /* NVActivityIndicatorAnimationCubeTransition.swift */, OBJ_34 /* NVActivityIndicatorAnimationLineScale.swift */, OBJ_35 /* NVActivityIndicatorAnimationLineScaleParty.swift */, OBJ_36 /* NVActivityIndicatorAnimationLineScalePulseOut.swift */, OBJ_37 /* NVActivityIndicatorAnimationLineScalePulseOutRapid.swift */, OBJ_38 /* NVActivityIndicatorAnimationLineSpinFadeLoader.swift */, OBJ_39 /* NVActivityIndicatorAnimationOrbit.swift */, OBJ_40 /* NVActivityIndicatorAnimationPacman.swift */, OBJ_41 /* NVActivityIndicatorAnimationSemiCircleSpin.swift */, OBJ_42 /* NVActivityIndicatorAnimationSquareSpin.swift */, OBJ_43 /* NVActivityIndicatorAnimationTriangleSkewSpin.swift */, ); path = Animations; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 1F1836DC248CEFC200583430 /* Example */ = { isa = PBXNativeTarget; buildConfigurationList = 1F1836F0248CEFC300583430 /* Build configuration list for PBXNativeTarget "Example" */; buildPhases = ( 1F1836D9248CEFC200583430 /* Sources */, 1F1836DA248CEFC200583430 /* Frameworks */, 1F1836DB248CEFC200583430 /* Resources */, 1F1836F6248CF08B00583430 /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( 1F1836F5248CF08B00583430 /* PBXTargetDependency */, ); name = Example; productName = Example; productReference = 1F1836DD248CEFC200583430 /* Example.app */; productType = "com.apple.product-type.application"; }; 1F1836FA248CF25600583430 /* Tests */ = { isa = PBXNativeTarget; buildConfigurationList = 1F183702248CF25600583430 /* Build configuration list for PBXNativeTarget "Tests" */; buildPhases = ( 1F1836F7248CF25600583430 /* Sources */, 1F1836F8248CF25600583430 /* Frameworks */, 1F1836F9248CF25600583430 /* Resources */, ); buildRules = ( ); dependencies = ( 1F183701248CF25600583430 /* PBXTargetDependency */, ); name = Tests; productName = Tests; productReference = 1F1836FB248CF25600583430 /* Tests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; "NVActivityIndicatorView::NVActivityIndicatorView" /* NVActivityIndicatorView */ = { isa = PBXNativeTarget; buildConfigurationList = OBJ_55 /* Build configuration list for PBXNativeTarget "NVActivityIndicatorView" */; buildPhases = ( OBJ_58 /* Sources */, OBJ_96 /* Frameworks */, ); buildRules = ( ); dependencies = ( 1F0742D12F6A40C600304500 /* PBXTargetDependency */, ); name = NVActivityIndicatorView; productName = NVActivityIndicatorView; productReference = "NVActivityIndicatorView::NVActivityIndicatorView::Product" /* NVActivityIndicatorView.framework */; productType = "com.apple.product-type.framework"; }; "NVActivityIndicatorView::NVActivityIndicatorViewExtended" /* NVActivityIndicatorViewExtended */ = { isa = PBXNativeTarget; buildConfigurationList = OBJ_98 /* Build configuration list for PBXNativeTarget "NVActivityIndicatorViewExtended" */; buildPhases = ( OBJ_101 /* Sources */, OBJ_104 /* Frameworks */, ); buildRules = ( ); dependencies = ( 1F0742D32F6A40CF00304500 /* PBXTargetDependency */, OBJ_106 /* PBXTargetDependency */, ); name = NVActivityIndicatorViewExtended; productName = NVActivityIndicatorViewExtended; productReference = "NVActivityIndicatorView::NVActivityIndicatorViewExtended::Product" /* NVActivityIndicatorViewExtended.framework */; productType = "com.apple.product-type.framework"; }; "NVActivityIndicatorView::SwiftPMPackageDescription" /* NVActivityIndicatorViewPackageDescription */ = { isa = PBXNativeTarget; buildConfigurationList = OBJ_108 /* Build configuration list for PBXNativeTarget "NVActivityIndicatorViewPackageDescription" */; buildPhases = ( OBJ_111 /* Sources */, ); buildRules = ( ); dependencies = ( ); name = NVActivityIndicatorViewPackageDescription; productName = NVActivityIndicatorViewPackageDescription; productType = "com.apple.product-type.framework"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ OBJ_1 /* Project object */ = { isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; LastSwiftMigration = 9999; LastSwiftUpdateCheck = 1130; LastUpgradeCheck = 2630; TargetAttributes = { 1F1836DC248CEFC200583430 = { CreatedOnToolsVersion = 11.3.1; ProvisioningStyle = Automatic; }; 1F1836FA248CF25600583430 = { CreatedOnToolsVersion = 11.3.1; LastSwiftMigration = 1130; ProvisioningStyle = Automatic; TestTargetID = 1F1836DC248CEFC200583430; }; }; }; buildConfigurationList = OBJ_2 /* Build configuration list for PBXProject "NVActivityIndicatorView" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = OBJ_5; packageReferences = ( 1F0742CF2F6A408300304500 /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */, ); productRefGroup = OBJ_51 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( "NVActivityIndicatorView::NVActivityIndicatorView" /* NVActivityIndicatorView */, "NVActivityIndicatorView::NVActivityIndicatorViewExtended" /* NVActivityIndicatorViewExtended */, "NVActivityIndicatorView::SwiftPMPackageDescription" /* NVActivityIndicatorViewPackageDescription */, 1F1836DC248CEFC200583430 /* Example */, 1F1836FA248CF25600583430 /* Tests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 1F1836DB248CEFC200583430 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 1F1836EC248CEFC300583430 /* LaunchScreen.storyboard in Resources */, 1F1836E9248CEFC300583430 /* Assets.xcassets in Resources */, 1F1836E7248CEFC200583430 /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 1F1836F9248CF25600583430 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 1F1836D9248CEFC200583430 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 1F1836E4248CEFC200583430 /* ViewController.swift in Sources */, 1F1836E0248CEFC200583430 /* AppDelegate.swift in Sources */, 1F1836E2248CEFC200583430 /* SceneDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 1F1836F7248CF25600583430 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 1F18370D248CF31500583430 /* NVActivityIndicatorTypeTests.swift in Sources */, 1F18370C248CF31500583430 /* NVActivityIndicatorViewTests.swift in Sources */, 1F183707248CF29F00583430 /* ActivityDataTests.swift in Sources */, 1F18370B248CF31500583430 /* NVActivityIndicatorPresenterTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; OBJ_101 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 0; files = ( OBJ_102 /* NVActivityIndicatorPresenter.swift in Sources */, OBJ_103 /* NVActivityIndicatorViewable.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; OBJ_111 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 0; files = ( OBJ_112 /* Package.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; OBJ_58 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 0; files = ( OBJ_59 /* NVActivityIndicatorAnimationAudioEqualizer.swift in Sources */, OBJ_60 /* NVActivityIndicatorAnimationBallBeat.swift in Sources */, OBJ_61 /* NVActivityIndicatorAnimationBallClipRotate.swift in Sources */, OBJ_62 /* NVActivityIndicatorAnimationBallClipRotateMultiple.swift in Sources */, OBJ_63 /* NVActivityIndicatorAnimationBallClipRotatePulse.swift in Sources */, OBJ_64 /* NVActivityIndicatorAnimationBallDoubleBounce.swift in Sources */, OBJ_65 /* NVActivityIndicatorAnimationBallGridBeat.swift in Sources */, OBJ_66 /* NVActivityIndicatorAnimationBallGridPulse.swift in Sources */, OBJ_67 /* NVActivityIndicatorAnimationBallPulse.swift in Sources */, OBJ_68 /* NVActivityIndicatorAnimationBallPulseRise.swift in Sources */, OBJ_69 /* NVActivityIndicatorAnimationBallPulseSync.swift in Sources */, OBJ_70 /* NVActivityIndicatorAnimationBallRotate.swift in Sources */, OBJ_71 /* NVActivityIndicatorAnimationBallRotateChase.swift in Sources */, OBJ_72 /* NVActivityIndicatorAnimationBallScale.swift in Sources */, OBJ_73 /* NVActivityIndicatorAnimationBallScaleMultiple.swift in Sources */, OBJ_74 /* NVActivityIndicatorAnimationBallScaleRipple.swift in Sources */, OBJ_75 /* NVActivityIndicatorAnimationBallScaleRippleMultiple.swift in Sources */, OBJ_76 /* NVActivityIndicatorAnimationBallSpinFadeLoader.swift in Sources */, OBJ_77 /* NVActivityIndicatorAnimationBallTrianglePath.swift in Sources */, OBJ_78 /* NVActivityIndicatorAnimationBallZigZag.swift in Sources */, OBJ_79 /* NVActivityIndicatorAnimationBallZigZagDeflect.swift in Sources */, OBJ_80 /* NVActivityIndicatorAnimationBlank.swift in Sources */, OBJ_81 /* NVActivityIndicatorAnimationCircleStrokeSpin.swift in Sources */, OBJ_82 /* NVActivityIndicatorAnimationCubeTransition.swift in Sources */, OBJ_83 /* NVActivityIndicatorAnimationLineScale.swift in Sources */, OBJ_84 /* NVActivityIndicatorAnimationLineScaleParty.swift in Sources */, OBJ_85 /* NVActivityIndicatorAnimationLineScalePulseOut.swift in Sources */, OBJ_86 /* NVActivityIndicatorAnimationLineScalePulseOutRapid.swift in Sources */, OBJ_87 /* NVActivityIndicatorAnimationLineSpinFadeLoader.swift in Sources */, OBJ_88 /* NVActivityIndicatorAnimationOrbit.swift in Sources */, OBJ_89 /* NVActivityIndicatorAnimationPacman.swift in Sources */, OBJ_90 /* NVActivityIndicatorAnimationSemiCircleSpin.swift in Sources */, OBJ_91 /* NVActivityIndicatorAnimationSquareSpin.swift in Sources */, OBJ_92 /* NVActivityIndicatorAnimationTriangleSkewSpin.swift in Sources */, OBJ_93 /* NVActivityIndicatorAnimationDelegate.swift in Sources */, OBJ_94 /* NVActivityIndicatorShape.swift in Sources */, OBJ_95 /* NVActivityIndicatorView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 1F0742D12F6A40C600304500 /* PBXTargetDependency */ = { isa = PBXTargetDependency; productRef = 1F0742D02F6A40C600304500 /* SwiftLintBuildToolPlugin */; }; 1F0742D32F6A40CF00304500 /* PBXTargetDependency */ = { isa = PBXTargetDependency; productRef = 1F0742D22F6A40CF00304500 /* SwiftLintBuildToolPlugin */; }; 1F1836F5248CF08B00583430 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = "NVActivityIndicatorView::NVActivityIndicatorViewExtended" /* NVActivityIndicatorViewExtended */; targetProxy = 1F1836F4248CF08B00583430 /* PBXContainerItemProxy */; }; 1F183701248CF25600583430 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 1F1836DC248CEFC200583430 /* Example */; targetProxy = 1F183700248CF25600583430 /* PBXContainerItemProxy */; }; OBJ_106 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = "NVActivityIndicatorView::NVActivityIndicatorView" /* NVActivityIndicatorView */; targetProxy = 1F1836A8248CEC7400583430 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ 1F1836E5248CEFC200583430 /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( 1F1836E6248CEFC200583430 /* Base */, ); name = Main.storyboard; sourceTree = ""; }; 1F1836EA248CEFC300583430 /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( 1F1836EB248CEFC300583430 /* Base */, ); name = LaunchScreen.storyboard; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 1F1836EE248CEFC300583430 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_STYLE = Automatic; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = Example/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.2; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = me.vinhis.Example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 1F1836EF248CEFC300583430 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = Example/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.2; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = me.vinhis.Example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; name = Release; }; 1F183703248CF25600583430 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_STYLE = Automatic; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = Tests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.2; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = me.vinhis.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OBJC_BRIDGING_HEADER = "Tests/Tests-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example.app/Example"; }; name = Debug; }; 1F183704248CF25600583430 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = Tests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.2; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = me.vinhis.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Tests/Tests-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example.app/Example"; VALIDATE_PRODUCT = YES; }; name = Release; }; OBJ_100 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = ""; CURRENT_PROJECT_VERSION = 48; ENABLE_TESTABILITY = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PLATFORM_DIR)/Developer/Library/Frameworks", ); HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = NVActivityIndicatorView.xcodeproj/NVActivityIndicatorViewExtended_Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx", ); MACOSX_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 5.1.0; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = "$(inherited)"; OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = NVActivityIndicatorViewExtended; PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TARGET_NAME = NVActivityIndicatorViewExtended; TVOS_DEPLOYMENT_TARGET = 12.0; WATCHOS_DEPLOYMENT_TARGET = 2.0; }; name = Release; }; OBJ_109 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = ""; LD = /usr/bin/true; MACOSX_DEPLOYMENT_TARGET = 11.0; OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4_2 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -package-description-version 5"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; OBJ_110 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = ""; LD = /usr/bin/true; MACOSX_DEPLOYMENT_TARGET = 11.0; OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4_2 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -package-description-version 5"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; OBJ_3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_NS_ASSERTIONS = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", "SWIFT_PACKAGE=1", "DEBUG=1", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.0; MACOSX_DEPLOYMENT_TARGET = 10.10; ONLY_ACTIVE_ARCH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -DXcode"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; STRING_CATALOG_GENERATE_SYMBOLS = YES; SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) SWIFT_PACKAGE DEBUG"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; USE_HEADERMAP = NO; }; name = Debug; }; OBJ_4 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = s; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", "SWIFT_PACKAGE=1", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.0; MACOSX_DEPLOYMENT_TARGET = 10.10; OTHER_SWIFT_FLAGS = "$(inherited) -DXcode"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; STRING_CATALOG_GENERATE_SYMBOLS = YES; SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) SWIFT_PACKAGE"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; USE_HEADERMAP = NO; }; name = Release; }; OBJ_56 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = ""; CURRENT_PROJECT_VERSION = 48; ENABLE_TESTABILITY = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PLATFORM_DIR)/Developer/Library/Frameworks", ); HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = NVActivityIndicatorView.xcodeproj/NVActivityIndicatorView_Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx", ); MACOSX_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 5.1.0; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = "$(inherited)"; OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = NVActivityIndicatorView; PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TARGET_NAME = NVActivityIndicatorView; TVOS_DEPLOYMENT_TARGET = 12.0; WATCHOS_DEPLOYMENT_TARGET = 2.0; }; name = Debug; }; OBJ_57 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = ""; CURRENT_PROJECT_VERSION = 48; ENABLE_TESTABILITY = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PLATFORM_DIR)/Developer/Library/Frameworks", ); HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = NVActivityIndicatorView.xcodeproj/NVActivityIndicatorView_Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx", ); MACOSX_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 5.1.0; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = "$(inherited)"; OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = NVActivityIndicatorView; PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TARGET_NAME = NVActivityIndicatorView; TVOS_DEPLOYMENT_TARGET = 12.0; WATCHOS_DEPLOYMENT_TARGET = 2.0; }; name = Release; }; OBJ_99 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = ""; CURRENT_PROJECT_VERSION = 48; ENABLE_TESTABILITY = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PLATFORM_DIR)/Developer/Library/Frameworks", ); HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = NVActivityIndicatorView.xcodeproj/NVActivityIndicatorViewExtended_Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx", ); MACOSX_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 5.1.0; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = "$(inherited)"; OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = NVActivityIndicatorViewExtended; PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TARGET_NAME = NVActivityIndicatorViewExtended; TVOS_DEPLOYMENT_TARGET = 12.0; WATCHOS_DEPLOYMENT_TARGET = 2.0; }; name = Debug; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 1F1836F0248CEFC300583430 /* Build configuration list for PBXNativeTarget "Example" */ = { isa = XCConfigurationList; buildConfigurations = ( 1F1836EE248CEFC300583430 /* Debug */, 1F1836EF248CEFC300583430 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 1F183702248CF25600583430 /* Build configuration list for PBXNativeTarget "Tests" */ = { isa = XCConfigurationList; buildConfigurations = ( 1F183703248CF25600583430 /* Debug */, 1F183704248CF25600583430 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; OBJ_108 /* Build configuration list for PBXNativeTarget "NVActivityIndicatorViewPackageDescription" */ = { isa = XCConfigurationList; buildConfigurations = ( OBJ_109 /* Debug */, OBJ_110 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; OBJ_2 /* Build configuration list for PBXProject "NVActivityIndicatorView" */ = { isa = XCConfigurationList; buildConfigurations = ( OBJ_3 /* Debug */, OBJ_4 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; OBJ_55 /* Build configuration list for PBXNativeTarget "NVActivityIndicatorView" */ = { isa = XCConfigurationList; buildConfigurations = ( OBJ_56 /* Debug */, OBJ_57 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; OBJ_98 /* Build configuration list for PBXNativeTarget "NVActivityIndicatorViewExtended" */ = { isa = XCConfigurationList; buildConfigurations = ( OBJ_99 /* Debug */, OBJ_100 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ 1F0742CF2F6A408300304500 /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/SimplyDanny/SwiftLintPlugins"; requirement = { kind = upToNextMajorVersion; minimumVersion = 0.63.2; }; }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ 1F0742D02F6A40C600304500 /* SwiftLintBuildToolPlugin */ = { isa = XCSwiftPackageProductDependency; package = 1F0742CF2F6A408300304500 /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */; productName = "plugin:SwiftLintBuildToolPlugin"; }; 1F0742D22F6A40CF00304500 /* SwiftLintBuildToolPlugin */ = { isa = XCSwiftPackageProductDependency; package = 1F0742CF2F6A408300304500 /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */; productName = "plugin:SwiftLintBuildToolPlugin"; }; /* End XCSwiftPackageProductDependency section */ }; rootObject = OBJ_1 /* Project object */; } ================================================ FILE: NVActivityIndicatorView.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: NVActivityIndicatorView.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: NVActivityIndicatorView.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings ================================================ IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded ================================================ FILE: NVActivityIndicatorView.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved ================================================ { "originHash" : "1fa961aa1dc717cea452f3389668f0f99a254f07e4eb11d190768a53798f744f", "pins" : [ { "identity" : "swiftlintplugins", "kind" : "remoteSourceControl", "location" : "https://github.com/SimplyDanny/SwiftLintPlugins", "state" : { "revision" : "8a4640d14777685ba8f14e832373160498fbab92", "version" : "0.63.2" } } ], "version" : 3 } ================================================ FILE: NVActivityIndicatorView.xcodeproj/xcshareddata/xcschemes/Example.xcscheme ================================================ ================================================ FILE: NVActivityIndicatorView.xcodeproj/xcshareddata/xcschemes/NVActivityIndicatorView-Package.xcscheme ================================================ ================================================ FILE: Package.swift ================================================ // swift-tools-version:5.0 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "NVActivityIndicatorView", platforms: [ .iOS(.v9), .tvOS(.v9) ], products: [ .library(name: "NVActivityIndicatorView", targets: ["NVActivityIndicatorView"]), .library(name: "NVActivityIndicatorViewExtended", targets: ["NVActivityIndicatorViewExtended"]) ], targets: [ .target(name: "NVActivityIndicatorView", path: "Sources/Base"), .target(name: "NVActivityIndicatorViewExtended", dependencies: ["NVActivityIndicatorView"], path: "Sources/Extended") ] ) ================================================ FILE: PrivacyInfo.xcprivacy ================================================ NSPrivacyTracking ================================================ FILE: README.md ================================================ # NVActivityIndicatorView [![Build Status](https://github.com/ninjaprox/NVActivityIndicatorView/actions/workflows/ios.yml/badge.svg?event=push)](https://github.com/ninjaprox/NVActivityIndicatorView/actions/workflows/ios.yml) [![Cocoapods Compatible](https://img.shields.io/cocoapods/v/NVActivityIndicatorView.svg)](https://img.shields.io/cocoapods/v/NVActivityIndicatorView.svg) [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) ⚠️ Check out [LoaderUI](https://github.com/ninjaprox/LoaderUI) (ready to use with Swift Package Mananger supported) for SwiftUI implementation of this. 🎉 ## Introduction `NVActivityIndicatorView` is a collection of awesome loading animations. ![Demo](https://raw.githubusercontent.com/ninjaprox/NVActivityIndicatorView/master/Demo.gif) ## Animation types | Type | Type | Type | Type | | ---------------------- | --------------------------- | ---------------------- | -------------------------- | | 1. ballPulse | 2. ballGridPulse | 3. ballClipRotate | 4. squareSpin | | 5. ballClipRotatePulse | 6. ballClipRotateMultiple | 7. ballPulseRise | 8. ballRotate | | 9. cubeTransition | 10. ballZigZag | 11. ballZigZagDeflect | 12. ballTrianglePath | | 13. ballScale | 14. lineScale | 15. lineScaleParty | 16. ballScaleMultiple | | 17. ballPulseSync | 18. ballBeat | 19. lineScalePulseOut | 20. lineScalePulseOutRapid | | 21. ballScaleRipple | 22. ballScaleRippleMultiple | 23. ballSpinFadeLoader | 24. lineSpinFadeLoader | | 25. triangleSkewSpin | 26. pacman | 27. ballGridBeat | 28. semiCircleSpin | | 29. ballRotateChase | 30. orbit | 31. audioEqualizer | 32. circleStrokeSpin | ## Installation ### Cocoapods [Cocoapods](https://cocoapods.org/#install) is a dependency manager for Swift and Objective-C Cocoa projects. To use NVActivityIndicatorView with CocoaPods, add it in your `Podfile`. ```ruby pod 'NVActivityIndicatorView' ``` ### Carthage [Carthage](https://github.com/Carthage/Carthage#installing-carthage) is intended to be the simplest way to add frameworks to your Cocoa application. To use NVActivityIndicatorView with Carthage, add it in your `Cartfile`. ```ruby github "ninjaprox/NVActivityIndicatorView" ``` ### Swift Package Manager The [Swift Package Manager](https://swift.org/package-manager/) is a tool for managing the distribution of Swift code. To use NVActivityIndicatorView with Swift Package Manger, add it to `dependencies` in your `Package.swift` ```swift dependencies: [ .package(url: "https://github.com/ninjaprox/NVActivityIndicatorView.git") ] ``` ## Migration Version [5.0.0](https://github.com/ninjaprox/NVActivityIndicatorView/releases/tag/5.0.0) comes with breaking changes. Please refer to the release note for details. ## Usage Firstly, import `NVActivityIndicatorView`. ```swift import NVActivityIndicatorView ``` ### Initialization Then, there are two ways you can create `NVActivityIndicatorView`: - By storyboard, changing class of any `UIView` to `NVActivityIndicatorView`. _**Note:** Set `Module` to `NVActivityIndicatorView`._ - By code, using initializer. All parameters other than `frame` are optional and [`NVActivityIndicatorView.DEFAULT_*`](https://nvactivityindicatorview.vinhis.me/Classes/NVActivityIndicatorView.html) are used as default values. ```swift NVActivityIndicatorView(frame: frame, type: type, color: color, padding: padding) ``` ### Control Start animating. ```swift activityIndicatorView.startAnimating() ``` Stop animating. ```swift activityIndicatorView.stopAnimating() ``` Determine if it is animating. ```swift animating = activityIndicatorView.isAnimating ``` ### Change properties In storyboard, you can change all properties in Attributes inspector tab of Utilities panel. _**Note:** Use one of values (case-insensitive) in [Animation types](#animation-types) for `Type Name`._ All properties are public so you can change them after initializing. _**Note:** All changes must be made before calling `startAnimating()`._ ## Documentation https://nvactivityindicatorview.vinhis.me/ ## Acknowledgment Thanks [Connor Atherton](https://github.com/ConnorAtherton) for inspired [Loaders.css](https://github.com/ConnorAtherton/loaders.css) and [Danil Gontovnik](https://github.com/gontovnik) for [DGActivityIndicatorView](https://github.com/gontovnik/DGActivityIndicatorView). ## License The MIT License (MIT) Copyright (c) 2016 Vinh Nguyen [@ninjaprox](http://twitter.com/ninjaprox) ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationAudioEqualizer.swift ================================================ // // NVActivityIndicatorAnimationAudioEqualizer.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationAudioEqualizer: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let lineSize = size.width / 9 let x = (layer.bounds.size.width - lineSize * 7) / 2 let y = (layer.bounds.size.height - size.height) / 2 let duration: [CFTimeInterval] = [4.3, 2.5, 1.7, 3.1] let values = [0, 0.7, 0.4, 0.05, 0.95, 0.3, 0.9, 0.4, 0.15, 0.18, 0.75, 0.01] // Draw lines for i in 0 ..< 4 { let animation = CAKeyframeAnimation() animation.keyPath = "path" animation.isAdditive = true animation.values = [] for j in 0 ..< values.count { let heightFactor = values[j] let height = size.height * CGFloat(heightFactor) let point = CGPoint(x: 0, y: size.height - height) let path = UIBezierPath(rect: CGRect(origin: point, size: CGSize(width: lineSize, height: height))) animation.values?.append(path.cgPath) } animation.duration = duration[i] animation.repeatCount = HUGE animation.isRemovedOnCompletion = false let line = NVActivityIndicatorShape.line.layerWith(size: CGSize(width: lineSize, height: size.height), color: color) let frame = CGRect(x: x + lineSize * 2 * CGFloat(i), y: y, width: lineSize, height: size.height) line.frame = frame line.add(animation, forKey: "animation") layer.addSublayer(line) } } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationBallBeat.swift ================================================ // // NVActivityIndicatorAnimationBallBeat.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationBallBeat: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let circleSpacing: CGFloat = 2 let circleSize = (size.width - circleSpacing * 2) / 3 let x = (layer.bounds.size.width - size.width) / 2 let y = (layer.bounds.size.height - circleSize) / 2 let duration: CFTimeInterval = 0.7 let beginTime = CACurrentMediaTime() let beginTimes = [0.35, 0, 0.35] // Scale animation let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale") scaleAnimation.keyTimes = [0, 0.5, 1] scaleAnimation.values = [1, 0.75, 1] scaleAnimation.duration = duration // Opacity animation let opacityAnimation = CAKeyframeAnimation(keyPath: "opacity") opacityAnimation.keyTimes = [0, 0.5, 1] opacityAnimation.values = [1, 0.2, 1] opacityAnimation.duration = duration // Aniamtion let animation = CAAnimationGroup() animation.animations = [scaleAnimation, opacityAnimation] animation.timingFunction = CAMediaTimingFunction(name: .linear) animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw circles for i in 0 ..< 3 { let circle = NVActivityIndicatorShape.circle.layerWith(size: CGSize(width: circleSize, height: circleSize), color: color) let frame = CGRect(x: x + circleSize * CGFloat(i) + circleSpacing * CGFloat(i), y: y, width: circleSize, height: circleSize) animation.beginTime = beginTime + beginTimes[i] circle.frame = frame circle.add(animation, forKey: "animation") layer.addSublayer(circle) } } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationBallClipRotate.swift ================================================ // // NVActivityIndicatorBallClipRotate.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationBallClipRotate: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let duration: CFTimeInterval = 0.75 // Scale animation let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale") scaleAnimation.keyTimes = [0, 0.5, 1] scaleAnimation.values = [1, 0.6, 1] // Rotate animation let rotateAnimation = CAKeyframeAnimation(keyPath: "transform.rotation.z") rotateAnimation.keyTimes = scaleAnimation.keyTimes rotateAnimation.values = [0, Double.pi, 2 * Double.pi] // Animation let animation = CAAnimationGroup() animation.animations = [scaleAnimation, rotateAnimation] animation.timingFunction = CAMediaTimingFunction(name: .linear) animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw circle let circle = NVActivityIndicatorShape.ringThirdFour.layerWith(size: CGSize(width: size.width, height: size.height), color: color) let frame = CGRect(x: (layer.bounds.size.width - size.width) / 2, y: (layer.bounds.size.height - size.height) / 2, width: size.width, height: size.height) circle.frame = frame circle.add(animation, forKey: "animation") layer.addSublayer(circle) } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationBallClipRotateMultiple.swift ================================================ // // NVActivityIndicatorAnimationBallClipRotateMultiple.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationBallClipRotateMultiple: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let bigCircleSize: CGFloat = size.width let smallCircleSize: CGFloat = size.width / 2 let longDuration: CFTimeInterval = 1 let timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) circleOf(shape: .ringTwoHalfHorizontal, duration: longDuration, timingFunction: timingFunction, layer: layer, size: bigCircleSize, color: color, reverse: false) circleOf(shape: .ringTwoHalfVertical, duration: longDuration, timingFunction: timingFunction, layer: layer, size: smallCircleSize, color: color, reverse: true) } func createAnimationIn(duration: CFTimeInterval, timingFunction: CAMediaTimingFunction, reverse: Bool) -> CAAnimation { // Scale animation let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale") scaleAnimation.keyTimes = [0, 0.5, 1] scaleAnimation.timingFunctions = [timingFunction, timingFunction] scaleAnimation.values = [1, 0.6, 1] scaleAnimation.duration = duration // Rotate animation let rotateAnimation = CAKeyframeAnimation(keyPath: "transform.rotation.z") rotateAnimation.keyTimes = scaleAnimation.keyTimes rotateAnimation.timingFunctions = [timingFunction, timingFunction] if !reverse { rotateAnimation.values = [0, Double.pi, 2 * Double.pi] } else { rotateAnimation.values = [0, -Double.pi, -2 * Double.pi] } rotateAnimation.duration = duration // Animation let animation = CAAnimationGroup() animation.animations = [scaleAnimation, rotateAnimation] animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false return animation } // swiftlint:disable:next function_parameter_count func circleOf(shape: NVActivityIndicatorShape, duration: CFTimeInterval, timingFunction: CAMediaTimingFunction, layer: CALayer, size: CGFloat, color: UIColor, reverse: Bool) { let circle = shape.layerWith(size: CGSize(width: size, height: size), color: color) let frame = CGRect(x: (layer.bounds.size.width - size) / 2, y: (layer.bounds.size.height - size) / 2, width: size, height: size) let animation = createAnimationIn(duration: duration, timingFunction: timingFunction, reverse: reverse) circle.frame = frame circle.add(animation, forKey: "animation") layer.addSublayer(circle) } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationBallClipRotatePulse.swift ================================================ // // NVActivityIndicatorAnimationBallClipRotatePulse.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationBallClipRotatePulse: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let duration: CFTimeInterval = 1 let timingFunction = CAMediaTimingFunction(controlPoints: 0.09, 0.57, 0.49, 0.9) smallCircleWith(duration: duration, timingFunction: timingFunction, layer: layer, size: size, color: color) bigCircleWith(duration: duration, timingFunction: timingFunction, layer: layer, size: size, color: color) } func smallCircleWith(duration: CFTimeInterval, timingFunction: CAMediaTimingFunction, layer: CALayer, size: CGSize, color: UIColor) { // Animation let animation = CAKeyframeAnimation(keyPath: "transform.scale") animation.keyTimes = [0, 0.3, 1] animation.timingFunctions = [timingFunction, timingFunction] animation.values = [1, 0.3, 1] animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw circle let circleSize = size.width / 2 let circle = NVActivityIndicatorShape.circle.layerWith(size: CGSize(width: circleSize, height: circleSize), color: color) let frame = CGRect(x: (layer.bounds.size.width - circleSize) / 2, y: (layer.bounds.size.height - circleSize) / 2, width: circleSize, height: circleSize) circle.frame = frame circle.add(animation, forKey: "animation") layer.addSublayer(circle) } func bigCircleWith(duration: CFTimeInterval, timingFunction: CAMediaTimingFunction, layer: CALayer, size: CGSize, color: UIColor) { // Scale animation let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale") scaleAnimation.keyTimes = [0, 0.5, 1] scaleAnimation.timingFunctions = [timingFunction, timingFunction] scaleAnimation.values = [1, 0.6, 1] scaleAnimation.duration = duration // Rotate animation let rotateAnimation = CAKeyframeAnimation(keyPath: "transform.rotation.z") rotateAnimation.keyTimes = scaleAnimation.keyTimes rotateAnimation.timingFunctions = [timingFunction, timingFunction] rotateAnimation.values = [0, Double.pi, 2 * Double.pi] rotateAnimation.duration = duration // Animation let animation = CAAnimationGroup() animation.animations = [scaleAnimation, rotateAnimation] animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw circle let circle = NVActivityIndicatorShape.ringTwoHalfVertical.layerWith(size: size, color: color) let frame = CGRect(x: (layer.bounds.size.width - size.width) / 2, y: (layer.bounds.size.height - size.height) / 2, width: size.width, height: size.height) circle.frame = frame circle.add(animation, forKey: "animation") layer.addSublayer(circle) } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationBallDoubleBounce.swift ================================================ // // NVActivityIndicatorAnimationBallsBounce.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationBallDoubleBounce: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { for index in (0...1) { bouncingBall(in: layer, size: size, color: color, startingAt: CACurrentMediaTime() + Double(index)) } } fileprivate func bouncingBall(in layer: CALayer, size: CGSize, color: UIColor, startingAt: CFTimeInterval) { // Scale animation let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale") scaleAnimation.duration = 2 scaleAnimation.keyTimes = [0, 0.5, 1] scaleAnimation.values = [-1, 0, -1] scaleAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) scaleAnimation.repeatCount = HUGE scaleAnimation.isRemovedOnCompletion = false let circle = NVActivityIndicatorShape.circle.layerWith(size: size, color: color) let frame = CGRect(x: (layer.bounds.size.width - size.width) / 2, y: (layer.bounds.size.height - size.height) / 2, width: size.width, height: size.height) scaleAnimation.beginTime = startingAt circle.frame = frame circle.opacity = 0.6 circle.add(scaleAnimation, forKey: "animation") layer.addSublayer(circle) } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationBallGridBeat.swift ================================================ // // NVActivityIndicatorAnimationBallGridBeat.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationBallGridBeat: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let circleSpacing: CGFloat = 2 let circleSize = (size.width - circleSpacing * 2) / 3 let x = (layer.bounds.size.width - size.width) / 2 let y = (layer.bounds.size.height - size.height) / 2 let durations = [0.96, 0.93, 1.19, 1.13, 1.34, 0.94, 1.2, 0.82, 1.19] let beginTime = CACurrentMediaTime() let beginTimes = [0.36, 0.4, 0.68, 0.41, 0.71, -0.15, -0.12, 0.01, 0.32] let timingFunction = CAMediaTimingFunction(name: .default) // Animation let animation = CAKeyframeAnimation(keyPath: "opacity") animation.keyTimes = [0, 0.5, 1] animation.timingFunctions = [timingFunction, timingFunction] animation.values = [1, 0.7, 1] animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw circles for i in 0 ..< 3 { for j in 0 ..< 3 { let circle = NVActivityIndicatorShape.circle.layerWith(size: CGSize(width: circleSize, height: circleSize), color: color) let frame = CGRect(x: x + circleSize * CGFloat(j) + circleSpacing * CGFloat(j), y: y + circleSize * CGFloat(i) + circleSpacing * CGFloat(i), width: circleSize, height: circleSize) animation.duration = durations[3 * i + j] animation.beginTime = beginTime + beginTimes[3 * i + j] circle.frame = frame circle.add(animation, forKey: "animation") layer.addSublayer(circle) } } } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationBallGridPulse.swift ================================================ // // NVActivityIndicatorAnimationBallGridPulse.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationBallGridPulse: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let circleSpacing: CGFloat = 2 let circleSize = (size.width - circleSpacing * 2) / 3 let x = (layer.bounds.size.width - size.width) / 2 let y = (layer.bounds.size.height - size.height) / 2 let durations: [CFTimeInterval] = [0.72, 1.02, 1.28, 1.42, 1.45, 1.18, 0.87, 1.45, 1.06] let beginTime = CACurrentMediaTime() let beginTimes: [CFTimeInterval] = [ -0.06, 0.25, -0.17, 0.48, 0.31, 0.03, 0.46, 0.78, 0.45] let timingFunction = CAMediaTimingFunction(name: .default) // Scale animation let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale") scaleAnimation.keyTimes = [0, 0.5, 1] scaleAnimation.timingFunctions = [timingFunction, timingFunction] scaleAnimation.values = [1, 0.5, 1] // Opacity animation let opacityAnimation = CAKeyframeAnimation(keyPath: "opacity") opacityAnimation.keyTimes = [0, 0.5, 1] opacityAnimation.timingFunctions = [timingFunction, timingFunction] opacityAnimation.values = [1, 0.7, 1] // Animation let animation = CAAnimationGroup() animation.animations = [scaleAnimation, opacityAnimation] animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw circles for i in 0 ..< 3 { for j in 0 ..< 3 { let circle = NVActivityIndicatorShape.circle.layerWith(size: CGSize(width: circleSize, height: circleSize), color: color) let frame = CGRect(x: x + circleSize * CGFloat(j) + circleSpacing * CGFloat(j), y: y + circleSize * CGFloat(i) + circleSpacing * CGFloat(i), width: circleSize, height: circleSize) animation.duration = durations[3 * i + j] animation.beginTime = beginTime + beginTimes[3 * i + j] circle.frame = frame circle.add(animation, forKey: "animation") layer.addSublayer(circle) } } } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationBallPulse.swift ================================================ // // NVActivityIndicatorAnimationBallPulse.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationBallPulse: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let circleSpacing: CGFloat = 2 let circleSize: CGFloat = (size.width - 2 * circleSpacing) / 3 let x: CGFloat = (layer.bounds.size.width - size.width) / 2 let y: CGFloat = (layer.bounds.size.height - circleSize) / 2 let duration: CFTimeInterval = 0.75 let beginTime = CACurrentMediaTime() let beginTimes: [CFTimeInterval] = [0.12, 0.24, 0.36] let timingFunction = CAMediaTimingFunction(controlPoints: 0.2, 0.68, 0.18, 1.08) let animation = CAKeyframeAnimation(keyPath: "transform.scale") // Animation animation.keyTimes = [0, 0.3, 1] animation.timingFunctions = [timingFunction, timingFunction] animation.values = [1, 0.3, 1] animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw circles for i in 0 ..< 3 { let circle = NVActivityIndicatorShape.circle.layerWith(size: CGSize(width: circleSize, height: circleSize), color: color) let frame = CGRect(x: x + circleSize * CGFloat(i) + circleSpacing * CGFloat(i), y: y, width: circleSize, height: circleSize) animation.beginTime = beginTime + beginTimes[i] circle.frame = frame circle.add(animation, forKey: "animation") layer.addSublayer(circle) } } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationBallPulseRise.swift ================================================ // // NVActivityIndicatorAnimationBallPulseRise.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationBallPulseRise: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let circleSpacing: CGFloat = 2 let circleSize = (size.width - 4 * circleSpacing) / 5 let x = (layer.bounds.size.width - size.width) / 2 let y = (layer.bounds.size.height - circleSize) / 2 let deltaY = size.height / 2 let duration: CFTimeInterval = 1 let timingFunction = CAMediaTimingFunction(controlPoints: 0.15, 0.46, 0.9, 0.6) let oddAnimation = self.oddAnimation(duration: duration, deltaY: deltaY, timingFunction: timingFunction) let evenAnimation = self.evenAnimation(duration: duration, deltaY: deltaY, timingFunction: timingFunction) // Draw circles for i in 0 ..< 5 { let circle = NVActivityIndicatorShape.circle.layerWith(size: CGSize(width: circleSize, height: circleSize), color: color) let frame = CGRect(x: x + circleSize * CGFloat(i) + circleSpacing * CGFloat(i), y: y, width: circleSize, height: circleSize) circle.frame = frame if i % 2 == 0 { circle.add(evenAnimation, forKey: "animation") } else { circle.add(oddAnimation, forKey: "animation") } layer.addSublayer(circle) } } func oddAnimation(duration: CFTimeInterval, deltaY: CGFloat, timingFunction: CAMediaTimingFunction) -> CAAnimation { // Scale animation let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale") scaleAnimation.keyTimes = [0, 0.5, 1] scaleAnimation.timingFunctions = [timingFunction, timingFunction] scaleAnimation.values = [0.4, 1.1, 0.75] scaleAnimation.duration = duration // Translate animation let translateAnimation = CAKeyframeAnimation(keyPath: "transform.translation.y") translateAnimation.keyTimes = [0, 0.25, 0.75, 1] translateAnimation.timingFunctions = [timingFunction, timingFunction, timingFunction] translateAnimation.values = [0, deltaY, -deltaY, 0] translateAnimation.duration = duration let animation = CAAnimationGroup() animation.animations = [scaleAnimation, translateAnimation] animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false return animation } func evenAnimation(duration: CFTimeInterval, deltaY: CGFloat, timingFunction: CAMediaTimingFunction) -> CAAnimation { // Scale animation let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale") scaleAnimation.keyTimes = [0, 0.5, 1] scaleAnimation.timingFunctions = [timingFunction, timingFunction] scaleAnimation.values = [1.1, 0.4, 1] scaleAnimation.duration = duration // Translate animation let translateAnimation = CAKeyframeAnimation(keyPath: "transform.translation.y") translateAnimation.keyTimes = [0, 0.25, 0.75, 1] translateAnimation.timingFunctions = [timingFunction, timingFunction, timingFunction] translateAnimation.values = [0, -deltaY, deltaY, 0] translateAnimation.duration = duration let animation = CAAnimationGroup() animation.animations = [scaleAnimation, translateAnimation] animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false return animation } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationBallPulseSync.swift ================================================ // // NVActivityIndicatorAnimationBallPulseSync.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationBallPulseSync: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let circleSpacing: CGFloat = 2 let circleSize = (size.width - circleSpacing * 2) / 3 let x = (layer.bounds.size.width - size.width) / 2 let y = (layer.bounds.size.height - circleSize) / 2 let deltaY = (size.height / 2 - circleSize / 2) / 2 let duration: CFTimeInterval = 0.6 let beginTime = CACurrentMediaTime() let beginTimes: [CFTimeInterval] = [0.07, 0.14, 0.21] let timingFunciton = CAMediaTimingFunction(name: .easeInEaseOut) // Animation let animation = CAKeyframeAnimation(keyPath: "transform.translation.y") animation.keyTimes = [0, 0.33, 0.66, 1] animation.timingFunctions = [timingFunciton, timingFunciton, timingFunciton] animation.values = [0, deltaY, -deltaY, 0] animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw circles for i in 0 ..< 3 { let circle = NVActivityIndicatorShape.circle.layerWith(size: CGSize(width: circleSize, height: circleSize), color: color) let frame = CGRect(x: x + circleSize * CGFloat(i) + circleSpacing * CGFloat(i), y: y, width: circleSize, height: circleSize) animation.beginTime = beginTime + beginTimes[i] circle.frame = frame circle.add(animation, forKey: "animation") layer.addSublayer(circle) } } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationBallRotate.swift ================================================ // // NVActivityIndicatorAnimationBallRotate.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationBallRotate: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let circleSize: CGFloat = size.width / 5 let duration: CFTimeInterval = 1 let timingFunction = CAMediaTimingFunction(controlPoints: 0.7, -0.13, 0.22, 0.86) // Scale animation let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale") scaleAnimation.keyTimes = [0, 0.5, 1] scaleAnimation.timingFunctions = [timingFunction, timingFunction] scaleAnimation.values = [1, 0.6, 1] scaleAnimation.duration = duration // Rotate animation let rotateAnimation = CAKeyframeAnimation(keyPath: "transform.rotation.z") rotateAnimation.keyTimes = [0, 0.5, 1] rotateAnimation.timingFunctions = [timingFunction, timingFunction] rotateAnimation.values = [0, Double.pi, 2 * Double.pi] rotateAnimation.duration = duration // Animation let animation = CAAnimationGroup() animation.animations = [scaleAnimation, rotateAnimation] animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw circles let leftCircle = NVActivityIndicatorShape.circle.layerWith(size: CGSize(width: circleSize, height: circleSize), color: color) let rightCircle = NVActivityIndicatorShape.circle.layerWith(size: CGSize(width: circleSize, height: circleSize), color: color) let centerCircle = NVActivityIndicatorShape.circle.layerWith(size: CGSize(width: circleSize, height: circleSize), color: color) leftCircle.opacity = 0.8 leftCircle.frame = CGRect(x: 0, y: (size.height - circleSize) / 2, width: circleSize, height: circleSize) rightCircle.opacity = 0.8 rightCircle.frame = CGRect(x: size.width - circleSize, y: (size.height - circleSize) / 2, width: circleSize, height: circleSize) centerCircle.frame = CGRect(x: (size.width - circleSize) / 2, y: (size.height - circleSize) / 2, width: circleSize, height: circleSize) let circle = CALayer() let frame = CGRect(x: (layer.bounds.size.width - size.width) / 2, y: (layer.bounds.size.height - size.height) / 2, width: size.width, height: size.height) circle.frame = frame circle.addSublayer(leftCircle) circle.addSublayer(rightCircle) circle.addSublayer(centerCircle) circle.add(animation, forKey: "animation") layer.addSublayer(circle) } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationBallRotateChase.swift ================================================ // // NVActivityIndicatorAnimationBallRotateChase.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationBallRotateChase: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let circleSize = size.width / 5 // Draw circles for i in 0 ..< 5 { let factor = Float(i) * 1 / 5 let circle = NVActivityIndicatorShape.circle.layerWith(size: CGSize(width: circleSize, height: circleSize), color: color) let animation = rotateAnimation(factor, x: layer.bounds.size.width / 2, y: layer.bounds.size.height / 2, size: CGSize(width: size.width - circleSize, height: size.height - circleSize)) circle.frame = CGRect(x: 0, y: 0, width: circleSize, height: circleSize) circle.add(animation, forKey: "animation") layer.addSublayer(circle) } } func rotateAnimation(_ rate: Float, x: CGFloat, y: CGFloat, size: CGSize) -> CAAnimationGroup { let duration: CFTimeInterval = 1.5 let fromScale = 1 - rate let toScale = 0.2 + rate let timeFunc = CAMediaTimingFunction(controlPoints: 0.5, 0.15 + rate, 0.25, 1) // Scale animation let scaleAnimation = CABasicAnimation(keyPath: "transform.scale") scaleAnimation.duration = duration scaleAnimation.repeatCount = HUGE scaleAnimation.fromValue = fromScale scaleAnimation.toValue = toScale // Position animation let positionAnimation = CAKeyframeAnimation(keyPath: "position") positionAnimation.duration = duration positionAnimation.repeatCount = HUGE positionAnimation.path = UIBezierPath(arcCenter: CGPoint(x: x, y: y), radius: size.width / 2, startAngle: CGFloat(3 * Double.pi * 0.5), endAngle: CGFloat(3 * Double.pi * 0.5 + 2 * Double.pi), clockwise: true).cgPath // Aniamtion let animation = CAAnimationGroup() animation.animations = [scaleAnimation, positionAnimation] animation.timingFunction = timeFunc animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false return animation } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationBallScale.swift ================================================ // // NVActivityIndicatorAnimationBallScale.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationBallScale: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let duration: CFTimeInterval = 1 // Scale animation let scaleAnimation = CABasicAnimation(keyPath: "transform.scale") scaleAnimation.duration = duration scaleAnimation.fromValue = 0 scaleAnimation.toValue = 1 // Opacity animation let opacityAnimation = CABasicAnimation(keyPath: "opacity") opacityAnimation.duration = duration opacityAnimation.fromValue = 1 opacityAnimation.toValue = 0 // Animation let animation = CAAnimationGroup() animation.animations = [scaleAnimation, opacityAnimation] animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw circle let circle = NVActivityIndicatorShape.circle.layerWith(size: size, color: color) circle.frame = CGRect(x: (layer.bounds.size.width - size.width) / 2, y: (layer.bounds.size.height - size.height) / 2, width: size.width, height: size.height) circle.add(animation, forKey: "animation") layer.addSublayer(circle) } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationBallScaleMultiple.swift ================================================ // // NVActivityIndicatorAnimationBallScaleMultiple.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationBallScaleMultiple: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let duration: CFTimeInterval = 1 let beginTime = CACurrentMediaTime() let beginTimes = [0, 0.2, 0.4] // Scale animation let scaleAnimation = CABasicAnimation(keyPath: "transform.scale") scaleAnimation.duration = duration scaleAnimation.fromValue = 0 scaleAnimation.toValue = 1 // Opacity animation let opacityAnimation = CAKeyframeAnimation(keyPath: "opacity") opacityAnimation.duration = duration opacityAnimation.keyTimes = [0, 0.05, 1] opacityAnimation.values = [0, 1, 0] // Animation let animation = CAAnimationGroup() animation.animations = [scaleAnimation, opacityAnimation] animation.timingFunction = CAMediaTimingFunction(name: .linear) animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw balls for i in 0 ..< 3 { let circle = NVActivityIndicatorShape.circle.layerWith(size: size, color: color) let frame = CGRect(x: (layer.bounds.size.width - size.width) / 2, y: (layer.bounds.size.height - size.height) / 2, width: size.width, height: size.height) animation.beginTime = beginTime + beginTimes[i] circle.frame = frame circle.opacity = 0 circle.add(animation, forKey: "animation") layer.addSublayer(circle) } } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationBallScaleRipple.swift ================================================ // // NVActivityIndicatorAnimationBallScaleRipple.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationBallScaleRipple: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let duration: CFTimeInterval = 1 let timingFunction = CAMediaTimingFunction(controlPoints: 0.21, 0.53, 0.56, 0.8) // Scale animation let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale") scaleAnimation.keyTimes = [0, 0.7] scaleAnimation.timingFunction = timingFunction scaleAnimation.values = [0.1, 1] scaleAnimation.duration = duration // Opacity animation let opacityAnimation = CAKeyframeAnimation(keyPath: "opacity") opacityAnimation.keyTimes = [0, 0.7, 1] opacityAnimation.timingFunctions = [timingFunction, timingFunction] opacityAnimation.values = [1, 0.7, 0] opacityAnimation.duration = duration // Animation let animation = CAAnimationGroup() animation.animations = [scaleAnimation, opacityAnimation] animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw circle let circle = NVActivityIndicatorShape.ring.layerWith(size: size, color: color) let frame = CGRect(x: (layer.bounds.size.width - size.width) / 2, y: (layer.bounds.size.height - size.height) / 2, width: size.width, height: size.height) circle.frame = frame circle.add(animation, forKey: "animation") layer.addSublayer(circle) } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationBallScaleRippleMultiple.swift ================================================ // // NVActivityIndicatorAnimationBallScaleRippleMultiple.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationBallScaleRippleMultiple: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let duration: CFTimeInterval = 1.25 let beginTime = CACurrentMediaTime() let beginTimes = [0, 0.2, 0.4] let timingFunction = CAMediaTimingFunction(controlPoints: 0.21, 0.53, 0.56, 0.8) // Scale animation let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale") scaleAnimation.keyTimes = [0, 0.7] scaleAnimation.timingFunction = timingFunction scaleAnimation.values = [0, 1] scaleAnimation.duration = duration // Opacity animation let opacityAnimation = CAKeyframeAnimation(keyPath: "opacity") opacityAnimation.keyTimes = [0, 0.7, 1] opacityAnimation.timingFunctions = [timingFunction, timingFunction] opacityAnimation.values = [1, 0.7, 0] opacityAnimation.duration = duration // Animation let animation = CAAnimationGroup() animation.animations = [scaleAnimation, opacityAnimation] animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw circles for i in 0 ..< 3 { let circle = NVActivityIndicatorShape.ring.layerWith(size: size, color: color) let frame = CGRect(x: (layer.bounds.size.width - size.width) / 2, y: (layer.bounds.size.height - size.height) / 2, width: size.width, height: size.height) animation.beginTime = beginTime + beginTimes[i] circle.frame = frame circle.add(animation, forKey: "animation") layer.addSublayer(circle) } } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationBallSpinFadeLoader.swift ================================================ // // NVActivityIndicatorAnimationBallSpinFadeLoader.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationBallSpinFadeLoader: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let circleSpacing: CGFloat = -2 let circleSize = (size.width - 4 * circleSpacing) / 5 let x = (layer.bounds.size.width - size.width) / 2 let y = (layer.bounds.size.height - size.height) / 2 let duration: CFTimeInterval = 1 let beginTime = CACurrentMediaTime() let beginTimes: [CFTimeInterval] = [0, 0.12, 0.24, 0.36, 0.48, 0.6, 0.72, 0.84] // Scale animation let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale") scaleAnimation.keyTimes = [0, 0.5, 1] scaleAnimation.values = [1, 0.4, 1] scaleAnimation.duration = duration // Opacity animation let opacityAnimaton = CAKeyframeAnimation(keyPath: "opacity") opacityAnimaton.keyTimes = [0, 0.5, 1] opacityAnimaton.values = [1, 0.3, 1] opacityAnimaton.duration = duration // Animation let animation = CAAnimationGroup() animation.animations = [scaleAnimation, opacityAnimaton] animation.timingFunction = CAMediaTimingFunction(name: .linear) animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw circles for i in 0 ..< 8 { let circle = circleAt(angle: CGFloat(Double.pi / 4) * CGFloat(i), size: circleSize, origin: CGPoint(x: x, y: y), containerSize: size, color: color) animation.beginTime = beginTime + beginTimes[i] circle.add(animation, forKey: "animation") layer.addSublayer(circle) } } func circleAt(angle: CGFloat, size: CGFloat, origin: CGPoint, containerSize: CGSize, color: UIColor) -> CALayer { let radius = containerSize.width / 2 - size / 2 let circle = NVActivityIndicatorShape.circle.layerWith(size: CGSize(width: size, height: size), color: color) let frame = CGRect( x: origin.x + radius * (cos(angle) + 1), y: origin.y + radius * (sin(angle) + 1), width: size, height: size) circle.frame = frame return circle } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationBallTrianglePath.swift ================================================ // // NVActivityIndicatorAnimationBallTrianglePath.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationBallTrianglePath: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let circleSize = size.width / 5 let deltaX = size.width / 2 - circleSize / 2 let deltaY = size.height / 2 - circleSize / 2 let x = (layer.bounds.size.width - size.width) / 2 let y = (layer.bounds.size.height - size.height) / 2 let duration: CFTimeInterval = 2 let timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) // Animation let animation = CAKeyframeAnimation(keyPath: "transform") animation.keyTimes = [0, 0.33, 0.66, 1] animation.timingFunctions = [timingFunction, timingFunction, timingFunction] animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Top-center circle let topCenterCircle = NVActivityIndicatorShape.ring.layerWith(size: CGSize(width: circleSize, height: circleSize), color: color) changeAnimation(animation, values: ["{0,0}", "{hx,fy}", "{-hx,fy}", "{0,0}"], deltaX: deltaX, deltaY: deltaY) topCenterCircle.frame = CGRect(x: x + size.width / 2 - circleSize / 2, y: y, width: circleSize, height: circleSize) topCenterCircle.add(animation, forKey: "animation") layer.addSublayer(topCenterCircle) // Bottom-left circle let bottomLeftCircle = NVActivityIndicatorShape.ring.layerWith(size: CGSize(width: circleSize, height: circleSize), color: color) changeAnimation(animation, values: ["{0,0}", "{hx,-fy}", "{fx,0}", "{0,0}"], deltaX: deltaX, deltaY: deltaY) bottomLeftCircle.frame = CGRect(x: x, y: y + size.height - circleSize, width: circleSize, height: circleSize) bottomLeftCircle.add(animation, forKey: "animation") layer.addSublayer(bottomLeftCircle) // Bottom-right circle let bottomRightCircle = NVActivityIndicatorShape.ring.layerWith(size: CGSize(width: circleSize, height: circleSize), color: color) changeAnimation(animation, values: ["{0,0}", "{-fx,0}", "{-hx,-fy}", "{0,0}"], deltaX: deltaX, deltaY: deltaY) bottomRightCircle.frame = CGRect(x: x + size.width - circleSize, y: y + size.height - circleSize, width: circleSize, height: circleSize) bottomRightCircle.add(animation, forKey: "animation") layer.addSublayer(bottomRightCircle) } func changeAnimation(_ animation: CAKeyframeAnimation, values rawValues: [String], deltaX: CGFloat, deltaY: CGFloat) { let values = NSMutableArray(capacity: 5) for rawValue in rawValues { let point = NSCoder.cgPoint(for: translateString(rawValue, deltaX: deltaX, deltaY: deltaY)) values.add(NSValue(caTransform3D: CATransform3DMakeTranslation(point.x, point.y, 0))) } animation.values = values as [AnyObject] } func translateString(_ valueString: String, deltaX: CGFloat, deltaY: CGFloat) -> String { let valueMutableString = NSMutableString(string: valueString) let fullDeltaX = 2 * deltaX let fullDeltaY = 2 * deltaY var range = NSRange(location: 0, length: valueMutableString.length) valueMutableString.replaceOccurrences(of: "hx", with: "\(deltaX)", options: NSString.CompareOptions.caseInsensitive, range: range) range.length = valueMutableString.length valueMutableString.replaceOccurrences(of: "fx", with: "\(fullDeltaX)", options: NSString.CompareOptions.caseInsensitive, range: range) range.length = valueMutableString.length valueMutableString.replaceOccurrences(of: "hy", with: "\(deltaY)", options: NSString.CompareOptions.caseInsensitive, range: range) range.length = valueMutableString.length valueMutableString.replaceOccurrences(of: "fy", with: "\(fullDeltaY)", options: NSString.CompareOptions.caseInsensitive, range: range) return valueMutableString as String } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationBallZigZag.swift ================================================ // // NVActivityIndicatorAnimationBallZigZag.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit import QuartzCore class NVActivityIndicatorAnimationBallZigZag: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let circleSize: CGFloat = size.width / 5 let duration: CFTimeInterval = 0.7 let deltaX = size.width / 2 - circleSize / 2 let deltaY = size.height / 2 - circleSize / 2 let frame = CGRect(x: (layer.bounds.size.width - circleSize) / 2, y: (layer.bounds.size.height - circleSize) / 2, width: circleSize, height: circleSize) // Circle 1 animation let animation = CAKeyframeAnimation(keyPath: "transform") animation.keyTimes = [0, 0.33, 0.66, 1] animation.timingFunction = CAMediaTimingFunction(name: .linear) animation.values = [ NSValue(caTransform3D: CATransform3DMakeTranslation(0, 0, 0)), NSValue(caTransform3D: CATransform3DMakeTranslation(-deltaX, -deltaY, 0)), NSValue(caTransform3D: CATransform3DMakeTranslation(deltaX, -deltaY, 0)), NSValue(caTransform3D: CATransform3DMakeTranslation(0, 0, 0)) ] animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw circle 1 circleAt(frame: frame, layer: layer, size: CGSize(width: circleSize, height: circleSize), color: color, animation: animation) // Circle 2 animation animation.values = [ NSValue(caTransform3D: CATransform3DMakeTranslation(0, 0, 0)), NSValue(caTransform3D: CATransform3DMakeTranslation(deltaX, deltaY, 0)), NSValue(caTransform3D: CATransform3DMakeTranslation(-deltaX, deltaY, 0)), NSValue(caTransform3D: CATransform3DMakeTranslation(0, 0, 0)) ] // Draw circle 2 circleAt(frame: frame, layer: layer, size: CGSize(width: circleSize, height: circleSize), color: color, animation: animation) } func circleAt(frame: CGRect, layer: CALayer, size: CGSize, color: UIColor, animation: CAAnimation) { let circle = NVActivityIndicatorShape.circle.layerWith(size: size, color: color) circle.frame = frame circle.add(animation, forKey: "animation") layer.addSublayer(circle) } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationBallZigZagDeflect.swift ================================================ // // NVActivityIndicatorAnimationBallZigZagDeflect.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationBallZigZagDeflect: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let circleSize: CGFloat = size.width / 5 let duration: CFTimeInterval = 0.75 let deltaX = size.width / 2 - circleSize / 2 let deltaY = size.height / 2 - circleSize / 2 let frame = CGRect(x: (layer.bounds.size.width - circleSize) / 2, y: (layer.bounds.size.height - circleSize) / 2, width: circleSize, height: circleSize) // Circle 1 animation let animation = CAKeyframeAnimation(keyPath: "transform") animation.keyTimes = [0, 0.33, 0.66, 1] animation.timingFunction = CAMediaTimingFunction(name: .linear) animation.values = [ NSValue(caTransform3D: CATransform3DMakeTranslation(0, 0, 0)), NSValue(caTransform3D: CATransform3DMakeTranslation(-deltaX, -deltaY, 0)), NSValue(caTransform3D: CATransform3DMakeTranslation(deltaX, -deltaY, 0)), NSValue(caTransform3D: CATransform3DMakeTranslation(0, 0, 0)) ] animation.duration = duration animation.repeatCount = HUGE animation.autoreverses = true animation.isRemovedOnCompletion = false // Draw circle 1 circleAt(frame: frame, layer: layer, size: CGSize(width: circleSize, height: circleSize), color: color, animation: animation) // Circle 2 animation animation.values = [ NSValue(caTransform3D: CATransform3DMakeTranslation(0, 0, 0)), NSValue(caTransform3D: CATransform3DMakeTranslation(deltaX, deltaY, 0)), NSValue(caTransform3D: CATransform3DMakeTranslation(-deltaX, deltaY, 0)), NSValue(caTransform3D: CATransform3DMakeTranslation(0, 0, 0)) ] // Draw circle 2 circleAt(frame: frame, layer: layer, size: CGSize(width: circleSize, height: circleSize), color: color, animation: animation) } func circleAt(frame: CGRect, layer: CALayer, size: CGSize, color: UIColor, animation: CAAnimation) { let circle = NVActivityIndicatorShape.circle.layerWith(size: size, color: color) circle.frame = frame circle.add(animation, forKey: "animation") layer.addSublayer(circle) } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationBlank.swift ================================================ // // NVActivityIndicatorAnimationBlank.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationBlank: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in _: CALayer, size _: CGSize, color _: UIColor) { // Do nothing } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationCircleStrokeSpin.swift ================================================ // // NVActivityIndicatorAnimationCircleStrokeSpin.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationCircleStrokeSpin: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let beginTime: Double = 0.5 let strokeStartDuration: Double = 1.2 let strokeEndDuration: Double = 0.7 let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation") rotationAnimation.byValue = Float.pi * 2 rotationAnimation.timingFunction = CAMediaTimingFunction(name: .linear) let strokeEndAnimation = CABasicAnimation(keyPath: "strokeEnd") strokeEndAnimation.duration = strokeEndDuration strokeEndAnimation.timingFunction = CAMediaTimingFunction(controlPoints: 0.4, 0.0, 0.2, 1.0) strokeEndAnimation.fromValue = 0 strokeEndAnimation.toValue = 1 let strokeStartAnimation = CABasicAnimation(keyPath: "strokeStart") strokeStartAnimation.duration = strokeStartDuration strokeStartAnimation.timingFunction = CAMediaTimingFunction(controlPoints: 0.4, 0.0, 0.2, 1.0) strokeStartAnimation.fromValue = 0 strokeStartAnimation.toValue = 1 strokeStartAnimation.beginTime = beginTime let groupAnimation = CAAnimationGroup() groupAnimation.animations = [rotationAnimation, strokeEndAnimation, strokeStartAnimation] groupAnimation.duration = strokeStartDuration + beginTime groupAnimation.repeatCount = .infinity groupAnimation.isRemovedOnCompletion = false groupAnimation.fillMode = .forwards let circle = NVActivityIndicatorShape.stroke.layerWith(size: size, color: color) let frame = CGRect( x: (layer.bounds.width - size.width) / 2, y: (layer.bounds.height - size.height) / 2, width: size.width, height: size.height ) circle.frame = frame circle.add(groupAnimation, forKey: "animation") layer.addSublayer(circle) } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationCubeTransition.swift ================================================ // // NVActivityIndicatorAnimationCubeTransition.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationCubeTransition: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let squareSize = size.width / 5 let x = (layer.bounds.size.width - size.width) / 2 let y = (layer.bounds.size.height - size.height) / 2 let deltaX = size.width - squareSize let deltaY = size.height - squareSize let duration: CFTimeInterval = 1.6 let beginTime = CACurrentMediaTime() let beginTimes: [CFTimeInterval] = [0, -0.8] let timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) // Scale animation let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale") scaleAnimation.keyTimes = [0, 0.25, 0.5, 0.75, 1] scaleAnimation.timingFunctions = [timingFunction, timingFunction, timingFunction, timingFunction] scaleAnimation.values = [1, 0.5, 1, 0.5, 1] scaleAnimation.duration = duration // Translate animation let translateAnimation = CAKeyframeAnimation(keyPath: "transform.translation") translateAnimation.keyTimes = scaleAnimation.keyTimes translateAnimation.timingFunctions = scaleAnimation.timingFunctions translateAnimation.values = [ NSValue(cgSize: CGSize(width: 0, height: 0)), NSValue(cgSize: CGSize(width: deltaX, height: 0)), NSValue(cgSize: CGSize(width: deltaX, height: deltaY)), NSValue(cgSize: CGSize(width: 0, height: deltaY)), NSValue(cgSize: CGSize(width: 0, height: 0)) ] translateAnimation.duration = duration // Rotate animation let rotateAnimation = CAKeyframeAnimation(keyPath: "transform.rotation.z") rotateAnimation.keyTimes = scaleAnimation.keyTimes rotateAnimation.timingFunctions = scaleAnimation.timingFunctions rotateAnimation.values = [0, -Double.pi / 2, -Double.pi, -1.5 * Double.pi, -2 * Double.pi] rotateAnimation.duration = duration // Animation let animation = CAAnimationGroup() animation.animations = [scaleAnimation, translateAnimation, rotateAnimation] animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw squares for i in 0 ..< 2 { let square = NVActivityIndicatorShape.rectangle.layerWith(size: CGSize(width: squareSize, height: squareSize), color: color) let frame = CGRect(x: x, y: y, width: squareSize, height: squareSize) animation.beginTime = beginTime + beginTimes[i] square.frame = frame square.add(animation, forKey: "animation") layer.addSublayer(square) } } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationLineScale.swift ================================================ // // NVActivityIndicatorAnimationBarScale.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationLineScale: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let lineSize = size.width / 9 let x = (layer.bounds.size.width - size.width) / 2 let y = (layer.bounds.size.height - size.height) / 2 let duration: CFTimeInterval = 1 let beginTime = CACurrentMediaTime() let beginTimes = [0.1, 0.2, 0.3, 0.4, 0.5] let timingFunction = CAMediaTimingFunction(controlPoints: 0.2, 0.68, 0.18, 1.08) // Animation let animation = CAKeyframeAnimation(keyPath: "transform.scale.y") animation.keyTimes = [0, 0.5, 1] animation.timingFunctions = [timingFunction, timingFunction] animation.values = [1, 0.4, 1] animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw lines for i in 0 ..< 5 { let line = NVActivityIndicatorShape.line.layerWith(size: CGSize(width: lineSize, height: size.height), color: color) let frame = CGRect(x: x + lineSize * 2 * CGFloat(i), y: y, width: lineSize, height: size.height) animation.beginTime = beginTime + beginTimes[i] line.frame = frame line.add(animation, forKey: "animation") layer.addSublayer(line) } } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationLineScaleParty.swift ================================================ // // NVActivityIndicatorAnimationLineScaleParty.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationLineScaleParty: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let lineSize = size.width / 7 let x = (layer.bounds.size.width - size.width) / 2 let y = (layer.bounds.size.height - size.height) / 2 let durations: [CFTimeInterval] = [1.26, 0.43, 1.01, 0.73] let beginTime = CACurrentMediaTime() let beginTimes: [CFTimeInterval] = [0.77, 0.29, 0.28, 0.74] let timingFunction = CAMediaTimingFunction(name: .default) // Animation let animation = CAKeyframeAnimation(keyPath: "transform.scale") animation.keyTimes = [0, 0.5, 1] animation.timingFunctions = [timingFunction, timingFunction] animation.values = [1, 0.5, 1] animation.repeatCount = HUGE animation.isRemovedOnCompletion = false for i in 0 ..< 4 { let line = NVActivityIndicatorShape.line.layerWith(size: CGSize(width: lineSize, height: size.height), color: color) let frame = CGRect(x: x + lineSize * 2 * CGFloat(i), y: y, width: lineSize, height: size.height) animation.beginTime = beginTime + beginTimes[i] animation.duration = durations[i] line.frame = frame line.add(animation, forKey: "animation") layer.addSublayer(line) } } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationLineScalePulseOut.swift ================================================ // // NVActivityIndicatorAnimationLineScalePulseOut.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationLineScalePulseOut: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let lineSize = size.width / 9 let x = (layer.bounds.size.width - size.width) / 2 let y = (layer.bounds.size.height - size.height) / 2 let duration: CFTimeInterval = 1 let beginTime = CACurrentMediaTime() let beginTimes = [0.4, 0.2, 0, 0.2, 0.4] let timingFunction = CAMediaTimingFunction(controlPoints: 0.85, 0.25, 0.37, 0.85) // Animation let animation = CAKeyframeAnimation(keyPath: "transform.scale.y") animation.keyTimes = [0, 0.5, 1] animation.timingFunctions = [timingFunction, timingFunction] animation.values = [1, 0.4, 1] animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw lines for i in 0 ..< 5 { let line = NVActivityIndicatorShape.line.layerWith(size: CGSize(width: lineSize, height: size.height), color: color) let frame = CGRect(x: x + lineSize * 2 * CGFloat(i), y: y, width: lineSize, height: size.height) animation.beginTime = beginTime + beginTimes[i] line.frame = frame line.add(animation, forKey: "animation") layer.addSublayer(line) } } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationLineScalePulseOutRapid.swift ================================================ // // NVActivityIndicatorAnimationLineScalePulseOutRapid.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationLineScalePulseOutRapid: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let lineSize = size.width / 9 let x = (layer.bounds.size.width - size.width) / 2 let y = (layer.bounds.size.height - size.height) / 2 let duration: CFTimeInterval = 0.9 let beginTime = CACurrentMediaTime() let beginTimes = [0.5, 0.25, 0, 0.25, 0.5] let timingFunction = CAMediaTimingFunction(controlPoints: 0.11, 0.49, 0.38, 0.78) // Animation let animation = CAKeyframeAnimation(keyPath: "transform.scale.y") animation.keyTimes = [0, 0.8, 0.9] animation.timingFunctions = [timingFunction, timingFunction] animation.beginTime = beginTime animation.values = [1, 0.3, 1] animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw lines for i in 0 ..< 5 { let line = NVActivityIndicatorShape.line.layerWith(size: CGSize(width: lineSize, height: size.height), color: color) let frame = CGRect(x: x + lineSize * 2 * CGFloat(i), y: y, width: lineSize, height: size.height) animation.beginTime = beginTime + beginTimes[i] line.frame = frame line.add(animation, forKey: "animation") layer.addSublayer(line) } } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationLineSpinFadeLoader.swift ================================================ // // NVActivityIndicatorAnimationLineSpinFadeLoader.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationLineSpinFadeLoader: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let lineSpacing: CGFloat = 2 let lineSize = CGSize(width: (size.width - 4 * lineSpacing) / 5, height: (size.height - 2 * lineSpacing) / 3) let x = (layer.bounds.size.width - size.width) / 2 let y = (layer.bounds.size.height - size.height) / 2 let duration: CFTimeInterval = 1.2 let beginTime = CACurrentMediaTime() let beginTimes: [CFTimeInterval] = [0.12, 0.24, 0.36, 0.48, 0.6, 0.72, 0.84, 0.96] let timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) // Animation let animation = CAKeyframeAnimation(keyPath: "opacity") animation.keyTimes = [0, 0.5, 1] animation.timingFunctions = [timingFunction, timingFunction] animation.values = [1, 0.3, 1] animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw lines for i in 0 ..< 8 { let line = lineAt(angle: CGFloat(Double.pi / 4 * Double(i)), size: lineSize, origin: CGPoint(x: x, y: y), containerSize: size, color: color) animation.beginTime = beginTime + beginTimes[i] line.add(animation, forKey: "animation") layer.addSublayer(line) } } func lineAt(angle: CGFloat, size: CGSize, origin: CGPoint, containerSize: CGSize, color: UIColor) -> CALayer { let radius = containerSize.width / 2 - max(size.width, size.height) / 2 let lineContainerSize = CGSize(width: max(size.width, size.height), height: max(size.width, size.height)) let lineContainer = CALayer() let lineContainerFrame = CGRect( x: origin.x + radius * (cos(angle) + 1), y: origin.y + radius * (sin(angle) + 1), width: lineContainerSize.width, height: lineContainerSize.height) let line = NVActivityIndicatorShape.line.layerWith(size: size, color: color) let lineFrame = CGRect( x: (lineContainerSize.width - size.width) / 2, y: (lineContainerSize.height - size.height) / 2, width: size.width, height: size.height) lineContainer.frame = lineContainerFrame line.frame = lineFrame lineContainer.addSublayer(line) lineContainer.sublayerTransform = CATransform3DMakeRotation(CGFloat(Double.pi / 2) + angle, 0, 0, 1) return lineContainer } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationOrbit.swift ================================================ // // NVActivityIndicatorAnimationOrbit.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationOrbit: NVActivityIndicatorAnimationDelegate { let duration: CFTimeInterval = 1.9 let satelliteCoreRatio: CGFloat = 0.25 let distanceRatio: CGFloat = 1.5 // distance / core size var coreSize: CGFloat = 0 var satelliteSize: CGFloat = 0 func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { coreSize = size.width / (1 + satelliteCoreRatio + distanceRatio) satelliteSize = coreSize * satelliteCoreRatio ring1InLayer(layer, size: size, color: color) ring2InLayer(layer, size: size, color: color) coreInLayer(layer, size: size, color: color) satelliteInLayer(layer, size: size, color: color) } func ring1InLayer(_ layer: CALayer, size _: CGSize, color: UIColor) { // Scale animation let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale") scaleAnimation.keyTimes = [0, 0.45, 0.45, 1] scaleAnimation.timingFunction = CAMediaTimingFunction(name: .linear) scaleAnimation.values = [0, 0, 1.3, 2] scaleAnimation.duration = duration // Opacity animation let opacityAnimation = CAKeyframeAnimation(keyPath: "opacity") let timingFunction = CAMediaTimingFunction(controlPoints: 0.19, 1, 0.22, 1) opacityAnimation.keyTimes = [0, 0.45, 1] scaleAnimation.timingFunctions = [CAMediaTimingFunction(name: .linear), timingFunction] opacityAnimation.values = [0.8, 0.8, 0] opacityAnimation.duration = duration // Animation let animation = CAAnimationGroup() animation.animations = [scaleAnimation, opacityAnimation] animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw circle let circle = NVActivityIndicatorShape.circle.layerWith(size: CGSize(width: coreSize, height: coreSize), color: color) let frame = CGRect(x: (layer.bounds.size.width - coreSize) / 2, y: (layer.bounds.size.height - coreSize) / 2, width: coreSize, height: coreSize) circle.frame = frame circle.add(animation, forKey: "animation") layer.addSublayer(circle) } func ring2InLayer(_ layer: CALayer, size _: CGSize, color: UIColor) { // Scale animation let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale") scaleAnimation.keyTimes = [0, 0.55, 0.55, 1] scaleAnimation.timingFunction = CAMediaTimingFunction(name: .linear) scaleAnimation.values = [0, 0, 1.3, 2.1] scaleAnimation.duration = duration // Opacity animation let opacityAnimation = CAKeyframeAnimation(keyPath: "opacity") let timingFunction = CAMediaTimingFunction(controlPoints: 0.19, 1, 0.22, 1) opacityAnimation.keyTimes = [0, 0.55, 0.65, 1] scaleAnimation.timingFunctions = [CAMediaTimingFunction(name: .linear), timingFunction] opacityAnimation.values = [0.7, 0.7, 0, 0] opacityAnimation.duration = duration // Animation let animation = CAAnimationGroup() animation.animations = [scaleAnimation, opacityAnimation] animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw circle let circle = NVActivityIndicatorShape.circle.layerWith(size: CGSize(width: coreSize, height: coreSize), color: color) let frame = CGRect(x: (layer.bounds.size.width - coreSize) / 2, y: (layer.bounds.size.height - coreSize) / 2, width: coreSize, height: coreSize) circle.frame = frame circle.add(animation, forKey: "animation") layer.addSublayer(circle) } func coreInLayer(_ layer: CALayer, size _: CGSize, color: UIColor) { let inTimingFunction = CAMediaTimingFunction(controlPoints: 0.7, 0, 1, 0.5) let outTimingFunction = CAMediaTimingFunction(controlPoints: 0, 0.7, 0.5, 1) let standByTimingFunction = CAMediaTimingFunction(name: .linear) // Scale animation let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale") scaleAnimation.keyTimes = [0, 0.45, 0.55, 1] scaleAnimation.timingFunctions = [inTimingFunction, standByTimingFunction, outTimingFunction] scaleAnimation.values = [1, 1.3, 1.3, 1] scaleAnimation.duration = duration scaleAnimation.repeatCount = HUGE scaleAnimation.isRemovedOnCompletion = false // Draw circle let circle = NVActivityIndicatorShape.circle.layerWith(size: CGSize(width: coreSize, height: coreSize), color: color) let frame = CGRect(x: (layer.bounds.size.width - coreSize) / 2, y: (layer.bounds.size.height - coreSize) / 2, width: coreSize, height: coreSize) circle.frame = frame circle.add(scaleAnimation, forKey: "animation") layer.addSublayer(circle) } func satelliteInLayer(_ layer: CALayer, size: CGSize, color: UIColor) { // Rotate animation let rotateAnimation = CAKeyframeAnimation(keyPath: "position") rotateAnimation.path = UIBezierPath(arcCenter: CGPoint(x: layer.bounds.midX, y: layer.bounds.midY), radius: (size.width - satelliteSize) / 2, startAngle: CGFloat(Double.pi * 1.5), endAngle: CGFloat(Double.pi * 1.5 + 4 * Double.pi), clockwise: true).cgPath rotateAnimation.duration = duration * 2 rotateAnimation.repeatCount = HUGE rotateAnimation.isRemovedOnCompletion = false // Draw circle let circle = NVActivityIndicatorShape.circle.layerWith(size: CGSize(width: satelliteSize, height: satelliteSize), color: color) let frame = CGRect(x: 0, y: 0, width: satelliteSize, height: satelliteSize) circle.frame = frame circle.add(rotateAnimation, forKey: "animation") layer.addSublayer(circle) } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationPacman.swift ================================================ // // NVActivityIndicatorAnimationPacman.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationPacman: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { circleInLayer(layer, size: size, color: color) pacmanInLayer(layer, size: size, color: color) } func pacmanInLayer(_ layer: CALayer, size: CGSize, color: UIColor) { let pacmanSize = 2 * size.width / 3 let pacmanDuration: CFTimeInterval = 0.5 let timingFunction = CAMediaTimingFunction(name: .default) // Stroke start animation let strokeStartAnimation = CAKeyframeAnimation(keyPath: "strokeStart") strokeStartAnimation.keyTimes = [0, 0.5, 1] strokeStartAnimation.timingFunctions = [timingFunction, timingFunction] strokeStartAnimation.values = [0.125, 0, 0.125] strokeStartAnimation.duration = pacmanDuration // Stroke end animation let strokeEndAnimation = CAKeyframeAnimation(keyPath: "strokeEnd") strokeEndAnimation.keyTimes = [0, 0.5, 1] strokeEndAnimation.timingFunctions = [timingFunction, timingFunction] strokeEndAnimation.values = [0.875, 1, 0.875] strokeEndAnimation.duration = pacmanDuration // Animation let animation = CAAnimationGroup() animation.animations = [strokeStartAnimation, strokeEndAnimation] animation.duration = pacmanDuration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw pacman let pacman = NVActivityIndicatorShape.pacman.layerWith(size: CGSize(width: pacmanSize, height: pacmanSize), color: color) let frame = CGRect( x: (layer.bounds.size.width - size.width) / 2, y: (layer.bounds.size.height - pacmanSize) / 2, width: pacmanSize, height: pacmanSize ) pacman.frame = frame pacman.add(animation, forKey: "animation") layer.addSublayer(pacman) } func circleInLayer(_ layer: CALayer, size: CGSize, color: UIColor) { let circleSize = size.width / 5 let circleDuration: CFTimeInterval = 1 // Translate animation let translateAnimation = CABasicAnimation(keyPath: "transform.translation.x") translateAnimation.fromValue = 0 translateAnimation.toValue = -size.width / 2 translateAnimation.duration = circleDuration // Opacity animation let opacityAnimation = CABasicAnimation(keyPath: "opacity") opacityAnimation.fromValue = 1 opacityAnimation.toValue = 0.7 opacityAnimation.duration = circleDuration // Animation let animation = CAAnimationGroup() animation.animations = [translateAnimation, opacityAnimation] animation.timingFunction = CAMediaTimingFunction(name: .linear) animation.duration = circleDuration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw circles let circle = NVActivityIndicatorShape.circle.layerWith(size: CGSize(width: circleSize, height: circleSize), color: color) let frame = CGRect( x: (layer.bounds.size.width - size.width) / 2 + size.width - circleSize, y: (layer.bounds.size.height - circleSize) / 2, width: circleSize, height: circleSize ) circle.frame = frame circle.add(animation, forKey: "animation") layer.addSublayer(circle) } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationSemiCircleSpin.swift ================================================ // // NVActivityIndicatorAnimationSemiCircleSpin.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationSemiCircleSpin: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let duration: CFTimeInterval = 0.6 // Animation let animation = CAKeyframeAnimation(keyPath: "transform.rotation.z") animation.keyTimes = [0, 0.5, 1] animation.values = [0, Double.pi, 2 * Double.pi] animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw circle let circle = NVActivityIndicatorShape.circleSemi.layerWith(size: size, color: color) let frame = CGRect( x: (layer.bounds.width - size.width) / 2, y: (layer.bounds.height - size.height) / 2, width: size.width, height: size.height ) circle.frame = frame circle.add(animation, forKey: "animation") layer.addSublayer(circle) } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationSquareSpin.swift ================================================ // // NVActivityIndicatorAnimationSquareSpin.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationSquareSpin: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let duration: CFTimeInterval = 3 let timingFunction = CAMediaTimingFunction(controlPoints: 0.09, 0.57, 0.49, 0.9) // Animation let animation = CAKeyframeAnimation(keyPath: "transform") animation.keyTimes = [0, 0.25, 0.5, 0.75, 1] animation.timingFunctions = [timingFunction, timingFunction, timingFunction, timingFunction] animation.values = [ NSValue(caTransform3D: CATransform3DConcat(createRotateXTransform(angle: 0), createRotateYTransform(angle: 0))), NSValue(caTransform3D: CATransform3DConcat(createRotateXTransform(angle: CGFloat(Double.pi)), createRotateYTransform(angle: 0))), NSValue(caTransform3D: CATransform3DConcat(createRotateXTransform(angle: CGFloat(Double.pi)), createRotateYTransform(angle: CGFloat(Double.pi)))), NSValue(caTransform3D: CATransform3DConcat(createRotateXTransform(angle: 0), createRotateYTransform(angle: CGFloat(Double.pi)))), NSValue(caTransform3D: CATransform3DConcat(createRotateXTransform(angle: 0), createRotateYTransform(angle: 0))) ] animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw square let square = NVActivityIndicatorShape.rectangle.layerWith(size: size, color: color) let frame = CGRect(x: (layer.bounds.size.width - size.width) / 2, y: (layer.bounds.size.height - size.height) / 2, width: size.width, height: size.height) square.frame = frame square.add(animation, forKey: "animation") layer.addSublayer(square) } func createRotateXTransform(angle: CGFloat) -> CATransform3D { var transform = CATransform3DMakeRotation(angle, 1, 0, 0) transform.m34 = CGFloat(-1) / 100 return transform } func createRotateYTransform(angle: CGFloat) -> CATransform3D { var transform = CATransform3DMakeRotation(angle, 0, 1, 0) transform.m34 = CGFloat(-1) / 100 return transform } } #endif ================================================ FILE: Sources/Base/Animations/NVActivityIndicatorAnimationTriangleSkewSpin.swift ================================================ // // NVActivityIndicatorAnimationTriangleSkewSpin.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit class NVActivityIndicatorAnimationTriangleSkewSpin: NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) { let x = (layer.bounds.size.width - size.width) / 2 let y = (layer.bounds.size.height - size.height) / 2 let duration: CFTimeInterval = 3 let timingFunction = CAMediaTimingFunction(controlPoints: 0.09, 0.57, 0.49, 0.9) // Animation let animation = CAKeyframeAnimation(keyPath: "transform") animation.keyTimes = [0, 0.25, 0.5, 0.75, 1] animation.timingFunctions = [timingFunction, timingFunction, timingFunction, timingFunction] animation.values = [ NSValue(caTransform3D: CATransform3DConcat(createRotateXTransform(angle: 0), createRotateYTransform(angle: 0))), NSValue(caTransform3D: CATransform3DConcat(createRotateXTransform(angle: CGFloat(Double.pi)), createRotateYTransform(angle: 0))), NSValue(caTransform3D: CATransform3DConcat(createRotateXTransform(angle: CGFloat(Double.pi)), createRotateYTransform(angle: CGFloat(Double.pi)))), NSValue(caTransform3D: CATransform3DConcat(createRotateXTransform(angle: 0), createRotateYTransform(angle: CGFloat(Double.pi)))), NSValue(caTransform3D: CATransform3DConcat(createRotateXTransform(angle: 0), createRotateYTransform(angle: 0))) ] animation.duration = duration animation.repeatCount = HUGE animation.isRemovedOnCompletion = false // Draw triangle let triangle = NVActivityIndicatorShape.triangle.layerWith(size: size, color: color) triangle.frame = CGRect(x: x, y: y, width: size.width, height: size.height) triangle.add(animation, forKey: "animation") layer.addSublayer(triangle) } func createRotateXTransform(angle: CGFloat) -> CATransform3D { var transform = CATransform3DMakeRotation(angle, 1, 0, 0) transform.m34 = CGFloat(-1) / 100 return transform } func createRotateYTransform(angle: CGFloat) -> CATransform3D { var transform = CATransform3DMakeRotation(angle, 0, 1, 0) transform.m34 = CGFloat(-1) / 100 return transform } } #endif ================================================ FILE: Sources/Base/NVActivityIndicatorAnimationDelegate.swift ================================================ // // NVActivityIndicatorDelegate.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit // swiftlint:disable:next class_delegate_protocol protocol NVActivityIndicatorAnimationDelegate { func setUpAnimation(in layer: CALayer, size: CGSize, color: UIColor) } #endif ================================================ FILE: Sources/Base/NVActivityIndicatorShape.swift ================================================ // // NVActivityIndicatorShape.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit enum NVActivityIndicatorShape { case circle case circleSemi case ring case ringTwoHalfVertical case ringTwoHalfHorizontal case ringThirdFour case rectangle case triangle case line case pacman case stroke // swiftlint:disable:next cyclomatic_complexity function_body_length func layerWith(size: CGSize, color: UIColor) -> CALayer { let layer: CAShapeLayer = CAShapeLayer() var path: UIBezierPath = UIBezierPath() let lineWidth: CGFloat = 2 switch self { case .circle: path.addArc(withCenter: CGPoint(x: size.width / 2, y: size.height / 2), radius: size.width / 2, startAngle: 0, endAngle: CGFloat(2 * Double.pi), clockwise: false) layer.fillColor = color.cgColor case .circleSemi: path.addArc(withCenter: CGPoint(x: size.width / 2, y: size.height / 2), radius: size.width / 2, startAngle: CGFloat(-Double.pi / 6), endAngle: CGFloat(-5 * Double.pi / 6), clockwise: false) path.close() layer.fillColor = color.cgColor case .ring: path.addArc(withCenter: CGPoint(x: size.width / 2, y: size.height / 2), radius: size.width / 2, startAngle: 0, endAngle: CGFloat(2 * Double.pi), clockwise: false) layer.fillColor = nil layer.strokeColor = color.cgColor layer.lineWidth = lineWidth case .ringTwoHalfVertical: path.addArc(withCenter: CGPoint(x: size.width / 2, y: size.height / 2), radius: size.width / 2, startAngle: CGFloat(-3 * Double.pi / 4), endAngle: CGFloat(-Double.pi / 4), clockwise: true) path.move( to: CGPoint(x: size.width / 2 - size.width / 2 * cos(CGFloat(Double.pi / 4)), y: size.height / 2 + size.height / 2 * sin(CGFloat(Double.pi / 4))) ) path.addArc(withCenter: CGPoint(x: size.width / 2, y: size.height / 2), radius: size.width / 2, startAngle: CGFloat(-5 * Double.pi / 4), endAngle: CGFloat(-7 * Double.pi / 4), clockwise: false) layer.fillColor = nil layer.strokeColor = color.cgColor layer.lineWidth = lineWidth case .ringTwoHalfHorizontal: path.addArc(withCenter: CGPoint(x: size.width / 2, y: size.height / 2), radius: size.width / 2, startAngle: CGFloat(3 * Double.pi / 4), endAngle: CGFloat(5 * Double.pi / 4), clockwise: true) path.move( to: CGPoint(x: size.width / 2 + size.width / 2 * cos(CGFloat(Double.pi / 4)), y: size.height / 2 - size.height / 2 * sin(CGFloat(Double.pi / 4))) ) path.addArc(withCenter: CGPoint(x: size.width / 2, y: size.height / 2), radius: size.width / 2, startAngle: CGFloat(-Double.pi / 4), endAngle: CGFloat(Double.pi / 4), clockwise: true) layer.fillColor = nil layer.strokeColor = color.cgColor layer.lineWidth = lineWidth case .ringThirdFour: path.addArc(withCenter: CGPoint(x: size.width / 2, y: size.height / 2), radius: size.width / 2, startAngle: CGFloat(-3 * Double.pi / 4), endAngle: CGFloat(-Double.pi / 4), clockwise: false) layer.fillColor = nil layer.strokeColor = color.cgColor layer.lineWidth = 2 case .rectangle: path.move(to: CGPoint(x: 0, y: 0)) path.addLine(to: CGPoint(x: size.width, y: 0)) path.addLine(to: CGPoint(x: size.width, y: size.height)) path.addLine(to: CGPoint(x: 0, y: size.height)) layer.fillColor = color.cgColor case .triangle: let offsetY = size.height / 4 path.move(to: CGPoint(x: 0, y: size.height - offsetY)) path.addLine(to: CGPoint(x: size.width / 2, y: size.height / 2 - offsetY)) path.addLine(to: CGPoint(x: size.width, y: size.height - offsetY)) path.close() layer.fillColor = color.cgColor case .line: path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: size.width, height: size.height), cornerRadius: size.width / 2) layer.fillColor = color.cgColor case .pacman: path.addArc(withCenter: CGPoint(x: size.width / 2, y: size.height / 2), radius: size.width / 4, startAngle: 0, endAngle: CGFloat(2 * Double.pi), clockwise: true) layer.fillColor = nil layer.strokeColor = color.cgColor layer.lineWidth = size.width / 2 case .stroke: path.addArc(withCenter: CGPoint(x: size.width / 2, y: size.height / 2), radius: size.width / 2, startAngle: -(.pi / 2), endAngle: .pi + .pi / 2, clockwise: true) layer.fillColor = nil layer.strokeColor = color.cgColor layer.lineWidth = 2 } layer.backgroundColor = nil layer.path = path.cgPath layer.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height) return layer } } #endif ================================================ FILE: Sources/Base/NVActivityIndicatorView.swift ================================================ // // NVActivityIndicatorView.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit /** Enum of animation types used for activity indicator view. - Blank: Blank animation. - BallPulse: BallPulse animation. - BallGridPulse: BallGridPulse animation. - BallClipRotate: BallClipRotate animation. - SquareSpin: SquareSpin animation. - BallClipRotatePulse: BallClipRotatePulse animation. - BallClipRotateMultiple: BallClipRotateMultiple animation. - BallPulseRise: BallPulseRise animation. - BallRotate: BallRotate animation. - CubeTransition: CubeTransition animation. - BallZigZag: BallZigZag animation. - BallZigZagDeflect: BallZigZagDeflect animation. - BallTrianglePath: BallTrianglePath animation. - BallScale: BallScale animation. - LineScale: LineScale animation. - LineScaleParty: LineScaleParty animation. - BallScaleMultiple: BallScaleMultiple animation. - BallPulseSync: BallPulseSync animation. - BallBeat: BallBeat animation. - BallDoubleBounce: BallDoubleBounce animation. - LineScalePulseOut: LineScalePulseOut animation. - LineScalePulseOutRapid: LineScalePulseOutRapid animation. - BallScaleRipple: BallScaleRipple animation. - BallScaleRippleMultiple: BallScaleRippleMultiple animation. - BallSpinFadeLoader: BallSpinFadeLoader animation. - LineSpinFadeLoader: LineSpinFadeLoader animation. - TriangleSkewSpin: TriangleSkewSpin animation. - Pacman: Pacman animation. - BallGridBeat: BallGridBeat animation. - SemiCircleSpin: SemiCircleSpin animation. - BallRotateChase: BallRotateChase animation. - Orbit: Orbit animation. - AudioEqualizer: AudioEqualizer animation. - CircleStrokeSpin: CircleStrokeSpin animation. */ public enum NVActivityIndicatorType: CaseIterable { /** Blank. - returns: Instance of NVActivityIndicatorAnimationBlank. */ case blank /** BallPulse. - returns: Instance of NVActivityIndicatorAnimationBallPulse. */ case ballPulse /** BallGridPulse. - returns: Instance of NVActivityIndicatorAnimationBallGridPulse. */ case ballGridPulse /** BallClipRotate. - returns: Instance of NVActivityIndicatorAnimationBallClipRotate. */ case ballClipRotate /** SquareSpin. - returns: Instance of NVActivityIndicatorAnimationSquareSpin. */ case squareSpin /** BallClipRotatePulse. - returns: Instance of NVActivityIndicatorAnimationBallClipRotatePulse. */ case ballClipRotatePulse /** BallClipRotateMultiple. - returns: Instance of NVActivityIndicatorAnimationBallClipRotateMultiple. */ case ballClipRotateMultiple /** BallPulseRise. - returns: Instance of NVActivityIndicatorAnimationBallPulseRise. */ case ballPulseRise /** BallRotate. - returns: Instance of NVActivityIndicatorAnimationBallRotate. */ case ballRotate /** CubeTransition. - returns: Instance of NVActivityIndicatorAnimationCubeTransition. */ case cubeTransition /** BallZigZag. - returns: Instance of NVActivityIndicatorAnimationBallZigZag. */ case ballZigZag /** BallZigZagDeflect - returns: Instance of NVActivityIndicatorAnimationBallZigZagDeflect */ case ballZigZagDeflect /** BallTrianglePath. - returns: Instance of NVActivityIndicatorAnimationBallTrianglePath. */ case ballTrianglePath /** BallScale. - returns: Instance of NVActivityIndicatorAnimationBallScale. */ case ballScale /** LineScale. - returns: Instance of NVActivityIndicatorAnimationLineScale. */ case lineScale /** LineScaleParty. - returns: Instance of NVActivityIndicatorAnimationLineScaleParty. */ case lineScaleParty /** BallScaleMultiple. - returns: Instance of NVActivityIndicatorAnimationBallScaleMultiple. */ case ballScaleMultiple /** BallPulseSync. - returns: Instance of NVActivityIndicatorAnimationBallPulseSync. */ case ballPulseSync /** BallBeat. - returns: Instance of NVActivityIndicatorAnimationBallBeat. */ case ballBeat /** BallDoubleBounce. - returns: Instance of NVActivityIndicatorAnimationBallDoubleBounce. */ case ballDoubleBounce /** LineScalePulseOut. - returns: Instance of NVActivityIndicatorAnimationLineScalePulseOut. */ case lineScalePulseOut /** LineScalePulseOutRapid. - returns: Instance of NVActivityIndicatorAnimationLineScalePulseOutRapid. */ case lineScalePulseOutRapid /** BallScaleRipple. - returns: Instance of NVActivityIndicatorAnimationBallScaleRipple. */ case ballScaleRipple /** BallScaleRippleMultiple. - returns: Instance of NVActivityIndicatorAnimationBallScaleRippleMultiple. */ case ballScaleRippleMultiple /** BallSpinFadeLoader. - returns: Instance of NVActivityIndicatorAnimationBallSpinFadeLoader. */ case ballSpinFadeLoader /** LineSpinFadeLoader. - returns: Instance of NVActivityIndicatorAnimationLineSpinFadeLoader. */ case lineSpinFadeLoader /** TriangleSkewSpin. - returns: Instance of NVActivityIndicatorAnimationTriangleSkewSpin. */ case triangleSkewSpin /** Pacman. - returns: Instance of NVActivityIndicatorAnimationPacman. */ case pacman /** BallGridBeat. - returns: Instance of NVActivityIndicatorAnimationBallGridBeat. */ case ballGridBeat /** SemiCircleSpin. - returns: Instance of NVActivityIndicatorAnimationSemiCircleSpin. */ case semiCircleSpin /** BallRotateChase. - returns: Instance of NVActivityIndicatorAnimationBallRotateChase. */ case ballRotateChase /** Orbit. - returns: Instance of NVActivityIndicatorAnimationOrbit. */ case orbit /** AudioEqualizer. - returns: Instance of NVActivityIndicatorAnimationAudioEqualizer. */ case audioEqualizer /** Stroke. - returns: Instance of NVActivityIndicatorAnimationCircleStrokeSpin. */ case circleStrokeSpin // swiftlint:disable:next cyclomatic_complexity function_body_length func animation() -> NVActivityIndicatorAnimationDelegate { switch self { case .blank: return NVActivityIndicatorAnimationBlank() case .ballPulse: return NVActivityIndicatorAnimationBallPulse() case .ballGridPulse: return NVActivityIndicatorAnimationBallGridPulse() case .ballClipRotate: return NVActivityIndicatorAnimationBallClipRotate() case .squareSpin: return NVActivityIndicatorAnimationSquareSpin() case .ballClipRotatePulse: return NVActivityIndicatorAnimationBallClipRotatePulse() case .ballClipRotateMultiple: return NVActivityIndicatorAnimationBallClipRotateMultiple() case .ballPulseRise: return NVActivityIndicatorAnimationBallPulseRise() case .ballRotate: return NVActivityIndicatorAnimationBallRotate() case .cubeTransition: return NVActivityIndicatorAnimationCubeTransition() case .ballZigZag: return NVActivityIndicatorAnimationBallZigZag() case .ballZigZagDeflect: return NVActivityIndicatorAnimationBallZigZagDeflect() case .ballTrianglePath: return NVActivityIndicatorAnimationBallTrianglePath() case .ballScale: return NVActivityIndicatorAnimationBallScale() case .lineScale: return NVActivityIndicatorAnimationLineScale() case .lineScaleParty: return NVActivityIndicatorAnimationLineScaleParty() case .ballScaleMultiple: return NVActivityIndicatorAnimationBallScaleMultiple() case .ballPulseSync: return NVActivityIndicatorAnimationBallPulseSync() case .ballBeat: return NVActivityIndicatorAnimationBallBeat() case .ballDoubleBounce: return NVActivityIndicatorAnimationBallDoubleBounce() case .lineScalePulseOut: return NVActivityIndicatorAnimationLineScalePulseOut() case .lineScalePulseOutRapid: return NVActivityIndicatorAnimationLineScalePulseOutRapid() case .ballScaleRipple: return NVActivityIndicatorAnimationBallScaleRipple() case .ballScaleRippleMultiple: return NVActivityIndicatorAnimationBallScaleRippleMultiple() case .ballSpinFadeLoader: return NVActivityIndicatorAnimationBallSpinFadeLoader() case .lineSpinFadeLoader: return NVActivityIndicatorAnimationLineSpinFadeLoader() case .triangleSkewSpin: return NVActivityIndicatorAnimationTriangleSkewSpin() case .pacman: return NVActivityIndicatorAnimationPacman() case .ballGridBeat: return NVActivityIndicatorAnimationBallGridBeat() case .semiCircleSpin: return NVActivityIndicatorAnimationSemiCircleSpin() case .ballRotateChase: return NVActivityIndicatorAnimationBallRotateChase() case .orbit: return NVActivityIndicatorAnimationOrbit() case .audioEqualizer: return NVActivityIndicatorAnimationAudioEqualizer() case .circleStrokeSpin: return NVActivityIndicatorAnimationCircleStrokeSpin() } } } /// Function that performs fade in/out animation. public typealias FadeInAnimation = (UIView) -> Void /// Function that performs fade out animation. /// /// - Note: Must call the second parameter on the animation completion. public typealias FadeOutAnimation = (UIView, @escaping () -> Void) -> Void // swiftlint:disable file_length /// Activity indicator view with nice animations public final class NVActivityIndicatorView: UIView { // swiftlint:disable identifier_name /// Default type. Default value is .BallSpinFadeLoader. public static var DEFAULT_TYPE: NVActivityIndicatorType = .ballSpinFadeLoader /// Default color of activity indicator. Default value is UIColor.white. public static var DEFAULT_COLOR = UIColor.white /// Default color of text. Default value is UIColor.white. public static var DEFAULT_TEXT_COLOR = UIColor.white /// Default padding. Default value is 0. public static var DEFAULT_PADDING: CGFloat = 0 /// Default size of activity indicator view in UI blocker. Default value is 60x60. public static var DEFAULT_BLOCKER_SIZE = CGSize(width: 60, height: 60) /// Default display time threshold to actually display UI blocker. Default value is 0 ms. /// /// - note: /// Default time that has to be elapsed (between calls of `startAnimating()` and `stopAnimating()`) in order to actually display UI blocker. It should be set thinking about what the minimum duration of an activity is to be worth showing it to the user. If the activity ends before this time threshold, then it will not be displayed at all. public static var DEFAULT_BLOCKER_DISPLAY_TIME_THRESHOLD = 0 /// Default minimum display time of UI blocker. Default value is 0 ms. /// /// - note: /// Default minimum display time of UI blocker. Its main purpose is to avoid flashes showing and hiding it so fast. For instance, setting it to 200ms will force UI blocker to be shown for at least this time (regardless of calling `stopAnimating()` ealier). public static var DEFAULT_BLOCKER_MINIMUM_DISPLAY_TIME = 0 /// Default message displayed in UI blocker. Default value is nil. public static var DEFAULT_BLOCKER_MESSAGE: String? /// Default message spacing to activity indicator view in UI blocker. Default value is 8. public static var DEFAULT_BLOCKER_MESSAGE_SPACING = CGFloat(8.0) /// Default font of message displayed in UI blocker. Default value is bold system font, size 20. public static var DEFAULT_BLOCKER_MESSAGE_FONT = UIFont.boldSystemFont(ofSize: 20) /// Default background color of UI blocker. Default value is UIColor(red: 0, green: 0, blue: 0, alpha: 0.5) public static var DEFAULT_BLOCKER_BACKGROUND_COLOR = UIColor(red: 0, green: 0, blue: 0, alpha: 0.5) /// Default fade in animation. public static var DEFAULT_FADE_IN_ANIMATION: FadeInAnimation = { view in view.alpha = 0 UIView.animate(withDuration: 0.25) { view.alpha = 1 } } /// Default fade out animation. public static var DEFAULT_FADE_OUT_ANIMATION: FadeOutAnimation = { (view, complete) in UIView.animate(withDuration: 0.25, animations: { view.alpha = 0 }, completion: { completed in if completed { complete() } }) } // swiftlint:enable identifier_name /// Animation type. public var type: NVActivityIndicatorType = NVActivityIndicatorView.DEFAULT_TYPE @available(*, unavailable, message: "This property is reserved for Interface Builder. Use 'type' instead.") @IBInspectable var typeName: String { get { return getTypeName() } set { _setTypeName(newValue) } } /// Color of activity indicator view. @IBInspectable public var color: UIColor = NVActivityIndicatorView.DEFAULT_COLOR /// Padding of activity indicator view. @IBInspectable public var padding: CGFloat = NVActivityIndicatorView.DEFAULT_PADDING /// Current status of animation, read-only. @available(*, deprecated) public var animating: Bool { return isAnimating } /// Current status of animation, read-only. private(set) public var isAnimating: Bool = false /** Returns an object initialized from data in a given unarchiver. self, initialized using the data in decoder. - parameter decoder: an unarchiver object. - returns: self, initialized using the data in decoder. */ public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) backgroundColor = UIColor.clear isHidden = true } /** Create a activity indicator view. Appropriate NVActivityIndicatorView.DEFAULT_* values are used for omitted params. - parameter frame: view's frame. - parameter type: animation type. - parameter color: color of activity indicator view. - parameter padding: padding of activity indicator view. - returns: The activity indicator view. */ public init(frame: CGRect, type: NVActivityIndicatorType? = nil, color: UIColor? = nil, padding: CGFloat? = nil) { self.type = type ?? NVActivityIndicatorView.DEFAULT_TYPE self.color = color ?? NVActivityIndicatorView.DEFAULT_COLOR self.padding = padding ?? NVActivityIndicatorView.DEFAULT_PADDING super.init(frame: frame) isHidden = true } // Fix issue #62 // Intrinsic content size is used in autolayout // that causes mislayout when using with MBProgressHUD. /** Returns the natural size for the receiving view, considering only properties of the view itself. A size indicating the natural size for the receiving view based on its intrinsic properties. - returns: A size indicating the natural size for the receiving view based on its intrinsic properties. */ public override var intrinsicContentSize: CGSize { return CGSize(width: bounds.width, height: bounds.height) } public override var bounds: CGRect { didSet { // setup the animation again for the new bounds if oldValue != bounds && isAnimating { setUpAnimation() } } } /** Start animating. */ public final func startAnimating() { guard !isAnimating else { return } isHidden = false isAnimating = true layer.speed = 1 setUpAnimation() } /** Stop animating. */ public final func stopAnimating() { guard isAnimating else { return } isHidden = true isAnimating = false layer.sublayers?.removeAll() } // MARK: Internal // swiftlint:disable:next identifier_name func _setTypeName(_ typeName: String) { for item in NVActivityIndicatorType.allCases where String(describing: item).caseInsensitiveCompare(typeName) == .orderedSame { type = item break } } func getTypeName() -> String { return String(describing: type) } // MARK: Privates private final func setUpAnimation() { let animation: NVActivityIndicatorAnimationDelegate = type.animation() var animationRect = frame.inset(by: UIEdgeInsets(top: padding, left: padding, bottom: padding, right: padding)) let minEdge = min(animationRect.width, animationRect.height) layer.sublayers = nil animationRect.size = CGSize(width: minEdge, height: minEdge) animation.setUpAnimation(in: layer, size: animationRect.size, color: color) } } #endif ================================================ FILE: Sources/Extended/NVActivityIndicatorPresenter.swift ================================================ // // NVActivityIndicatorPresenter.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit import NVActivityIndicatorView /// Class packages information used to display UI blocker. @available(*, deprecated, message: "") public final class ActivityData { /// Size of activity indicator view. let size: CGSize /// Message displayed under activity indicator view. let message: String? /// Font of message displayed under activity indicator view. let messageFont: UIFont /// Message spacing to activity indicator view. let messageSpacing: CGFloat /// Animation type. let type: NVActivityIndicatorType /// Color of activity indicator view. let color: UIColor /// Color of text. let textColor: UIColor /// Padding of activity indicator view. let padding: CGFloat /// Display time threshold to actually display UI blocker. let displayTimeThreshold: Int /// Minimum display time of UI blocker. let minimumDisplayTime: Int /// Background color of the UI blocker let backgroundColor: UIColor /** Create information package used to display UI blocker. Appropriate NVActivityIndicatorView.DEFAULT_* values are used for omitted params. - parameter size: size of activity indicator view. - parameter message: message displayed under activity indicator view. - parameter messageFont: font of message displayed under activity indicator view. - parameter messageSpacing: message spacing to activity indicator view. - parameter type: animation type. - parameter color: color of activity indicator view. - parameter padding: padding of activity indicator view. - parameter displayTimeThreshold: display time threshold to actually display UI blocker. - parameter minimumDisplayTime: minimum display time of UI blocker. - parameter textColor: color of the text below the activity indicator view. Will match color parameter if not set, otherwise DEFAULT_TEXT_COLOR if color is not set. - returns: The information package used to display UI blocker. */ public init(size: CGSize? = nil, message: String? = nil, messageFont: UIFont? = nil, messageSpacing: CGFloat? = nil, type: NVActivityIndicatorType? = nil, color: UIColor? = nil, padding: CGFloat? = nil, displayTimeThreshold: Int? = nil, minimumDisplayTime: Int? = nil, backgroundColor: UIColor? = nil, textColor: UIColor? = nil) { self.size = size ?? NVActivityIndicatorView.DEFAULT_BLOCKER_SIZE self.message = message ?? NVActivityIndicatorView.DEFAULT_BLOCKER_MESSAGE self.messageFont = messageFont ?? NVActivityIndicatorView.DEFAULT_BLOCKER_MESSAGE_FONT self.messageSpacing = messageSpacing ?? NVActivityIndicatorView.DEFAULT_BLOCKER_MESSAGE_SPACING self.type = type ?? NVActivityIndicatorView.DEFAULT_TYPE self.color = color ?? NVActivityIndicatorView.DEFAULT_COLOR self.padding = padding ?? NVActivityIndicatorView.DEFAULT_PADDING self.displayTimeThreshold = displayTimeThreshold ?? NVActivityIndicatorView.DEFAULT_BLOCKER_DISPLAY_TIME_THRESHOLD self.minimumDisplayTime = minimumDisplayTime ?? NVActivityIndicatorView.DEFAULT_BLOCKER_MINIMUM_DISPLAY_TIME self.backgroundColor = backgroundColor ?? NVActivityIndicatorView.DEFAULT_BLOCKER_BACKGROUND_COLOR self.textColor = textColor ?? color ?? NVActivityIndicatorView.DEFAULT_TEXT_COLOR } } private protocol NVActivityIndicatorPresenterState { func startAnimating(presenter: NVActivityIndicatorPresenter, _ fadeInAnimation: FadeInAnimation?) func stopAnimating(presenter: NVActivityIndicatorPresenter, _ fadeOutAnimation: FadeOutAnimation?) } private struct NVActivityIndicatorPresenterStateWaitingToStart: NVActivityIndicatorPresenterState { func startAnimating(presenter: NVActivityIndicatorPresenter, _ fadeInAnimation: FadeInAnimation?) { guard let activityData = presenter.data else { return } presenter.show(with: activityData, fadeInAnimation) presenter.state = .animating presenter.waitingToStartGroup.leave() } func stopAnimating(presenter: NVActivityIndicatorPresenter, _ fadeOutAnimation: FadeOutAnimation?) { presenter.state = .stopped presenter.waitingToStartGroup.leave() } } private struct NVActivityIndicatorPresenterStateAnimating: NVActivityIndicatorPresenterState { func startAnimating(presenter: NVActivityIndicatorPresenter, _ fadeInAnimation: FadeInAnimation?) { // Do nothing } func stopAnimating(presenter: NVActivityIndicatorPresenter, _ fadeOutAnimation: FadeOutAnimation?) { guard let activityData = presenter.data else { return } DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(activityData.minimumDisplayTime)) { guard presenter.state == .waitingToStop else { return } presenter.stopAnimating(fadeOutAnimation) } presenter.state = .waitingToStop } } private struct NVActivityIndicatorPresenterStateWaitingToStop: NVActivityIndicatorPresenterState { func startAnimating(presenter: NVActivityIndicatorPresenter, _ fadeInAnimation: FadeInAnimation?) { presenter.stopAnimating(nil) guard let activityData = presenter.data else { return } presenter.startAnimating(activityData, fadeInAnimation) } func stopAnimating(presenter: NVActivityIndicatorPresenter, _ fadeOutAnimation: FadeOutAnimation?) { presenter.hide(fadeOutAnimation) presenter.state = .stopped } } private struct NVActivityIndicatorPresenterStateStopped: NVActivityIndicatorPresenterState { func startAnimating(presenter: NVActivityIndicatorPresenter, _ fadeInAnimation: FadeInAnimation?) { guard let activityData = presenter.data else { return } DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(activityData.displayTimeThreshold)) { guard presenter.state == .waitingToStart else { return } presenter.startAnimating(activityData, fadeInAnimation) } presenter.state = .waitingToStart presenter.waitingToStartGroup.enter() } func stopAnimating(presenter: NVActivityIndicatorPresenter, _ fadeOutAnimation: FadeOutAnimation?) { // Do nothing } } /// Presenter that displays NVActivityIndicatorView as UI blocker. @available(*, deprecated, message: "") public final class NVActivityIndicatorPresenter { fileprivate enum State: NVActivityIndicatorPresenterState { case waitingToStart case animating case waitingToStop case stopped var performer: NVActivityIndicatorPresenterState { switch self { case .waitingToStart: return NVActivityIndicatorPresenterStateWaitingToStart() case .animating: return NVActivityIndicatorPresenterStateAnimating() case .waitingToStop: return NVActivityIndicatorPresenterStateWaitingToStop() case .stopped: return NVActivityIndicatorPresenterStateStopped() } } func startAnimating(presenter: NVActivityIndicatorPresenter, _ fadeInAnimation: FadeInAnimation?) { performer.startAnimating(presenter: presenter, fadeInAnimation) } func stopAnimating(presenter: NVActivityIndicatorPresenter, _ fadeOutAnimation: FadeOutAnimation?) { performer.stopAnimating(presenter: presenter, fadeOutAnimation) } } private let restorationIdentifier = "NVActivityIndicatorViewContainer" private let messageLabel: UILabel = { let label = UILabel() label.textAlignment = .center label.numberOfLines = 0 label.translatesAutoresizingMaskIntoConstraints = false return label }() fileprivate var state: State = .stopped fileprivate var data: ActivityData? // Shared activity data across states /// The group to synchronize the message so that the one set by `setMessage` is always displayed after the initial message passed to `startAnimating(_:)`. fileprivate let waitingToStartGroup = DispatchGroup() /// Shared instance of `NVActivityIndicatorPresenter`. public static let sharedInstance = NVActivityIndicatorPresenter() /// Current status of animation, read-only. public var isAnimating: Bool { return state == .animating || state == .waitingToStop } private init() {} // MARK: - Public interface /** Display UI blocker. - parameter data: Information package used to display UI blocker. - parameter fadeInAnimation: Fade in animation. */ public final func startAnimating(_ data: ActivityData, _ fadeInAnimation: FadeInAnimation? = nil) { self.data = data state.startAnimating(presenter: self, fadeInAnimation) } /** Remove UI blocker. - parameter fadeOutAnimation: Fade out animation. */ public final func stopAnimating(_ fadeOutAnimation: FadeOutAnimation? = nil) { state.stopAnimating(presenter: self, fadeOutAnimation) } /// Set message displayed under activity indicator view. /// /// - Parameter message: message displayed under activity indicator view. public final func setMessage(_ message: String?) { waitingToStartGroup.notify(queue: DispatchQueue.main) { self.messageLabel.text = message } } // MARK: - Helpers fileprivate func show(with activityData: ActivityData, _ fadeInAnimation: FadeInAnimation?) { let containerView = UIView(frame: UIScreen.main.bounds) containerView.backgroundColor = activityData.backgroundColor containerView.restorationIdentifier = restorationIdentifier containerView.translatesAutoresizingMaskIntoConstraints = false fadeInAnimation?(containerView) let activityIndicatorView = NVActivityIndicatorView( frame: CGRect(x: 0, y: 0, width: activityData.size.width, height: activityData.size.height), type: activityData.type, color: activityData.color, padding: activityData.padding) activityIndicatorView.startAnimating() activityIndicatorView.translatesAutoresizingMaskIntoConstraints = false containerView.addSubview(activityIndicatorView) // Add constraints for `activityIndicatorView`. ({ let xConstraint = NSLayoutConstraint(item: containerView, attribute: .centerX, relatedBy: .equal, toItem: activityIndicatorView, attribute: .centerX, multiplier: 1, constant: 0) let yConstraint = NSLayoutConstraint(item: containerView, attribute: .centerY, relatedBy: .equal, toItem: activityIndicatorView, attribute: .centerY, multiplier: 1, constant: 0) containerView.addConstraints([xConstraint, yConstraint]) }()) messageLabel.font = activityData.messageFont messageLabel.textColor = activityData.textColor messageLabel.text = activityData.message containerView.addSubview(messageLabel) // Add constraints for `messageLabel`. ({ let leadingConstraint = NSLayoutConstraint(item: containerView, attribute: .leading, relatedBy: .equal, toItem: messageLabel, attribute: .leading, multiplier: 1, constant: -8) let trailingConstraint = NSLayoutConstraint(item: containerView, attribute: .trailing, relatedBy: .equal, toItem: messageLabel, attribute: .trailing, multiplier: 1, constant: 8) containerView.addConstraints([leadingConstraint, trailingConstraint]) }()) ({ let spacingConstraint = NSLayoutConstraint(item: messageLabel, attribute: .top, relatedBy: .equal, toItem: activityIndicatorView, attribute: .bottom, multiplier: 1, constant: activityData.messageSpacing) containerView.addConstraint(spacingConstraint) }()) guard let keyWindow = UIApplication.shared.keyWindow else { return } keyWindow.addSubview(containerView) // Add constraints for `containerView`. ({ let leadingConstraint = NSLayoutConstraint(item: keyWindow, attribute: .leading, relatedBy: .equal, toItem: containerView, attribute: .leading, multiplier: 1, constant: 0) let trailingConstraint = NSLayoutConstraint(item: keyWindow, attribute: .trailing, relatedBy: .equal, toItem: containerView, attribute: .trailing, multiplier: 1, constant: 0) let topConstraint = NSLayoutConstraint(item: keyWindow, attribute: .top, relatedBy: .equal, toItem: containerView, attribute: .top, multiplier: 1, constant: 0) let bottomConstraint = NSLayoutConstraint(item: keyWindow, attribute: .bottom, relatedBy: .equal, toItem: containerView, attribute: .bottom, multiplier: 1, constant: 0) keyWindow.addConstraints([leadingConstraint, trailingConstraint, topConstraint, bottomConstraint]) }()) } fileprivate func hide(_ fadeOutAnimation: FadeOutAnimation?) { for window in UIApplication.shared.windows { for item in window.subviews where item.restorationIdentifier == restorationIdentifier { if let fadeOutAnimation = fadeOutAnimation { fadeOutAnimation(item) { item.removeFromSuperview() } } else { item.removeFromSuperview() } } } } } #endif ================================================ FILE: Sources/Extended/NVActivityIndicatorViewable.swift ================================================ // // NVActivityIndicatorViewable.swift // NVActivityIndicatorView // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #if canImport(UIKit) import UIKit import NVActivityIndicatorView /** * UIViewController conforms this protocol to be able to display NVActivityIndicatorView as UI blocker. * * This extends abilities of UIViewController to display and remove UI blocker. */ @available(*, deprecated, message: "") public protocol NVActivityIndicatorViewable {} @available(*, deprecated, message: "") public extension NVActivityIndicatorViewable where Self: UIViewController { /// Current status of animation, read-only. var isAnimating: Bool { return NVActivityIndicatorPresenter.sharedInstance.isAnimating } /** Display UI blocker. Appropriate NVActivityIndicatorView.DEFAULT_* values are used for omitted params. - parameter size: size of activity indicator view. - parameter message: message displayed under activity indicator view. - parameter messageFont: font of message displayed under activity indicator view. - parameter type: animation type. - parameter color: color of activity indicator view. - parameter padding: padding of activity indicator view. - parameter displayTimeThreshold: display time threshold to actually display UI blocker. - parameter minimumDisplayTime: minimum display time of UI blocker. - parameter fadeInAnimation: fade in animation. */ func startAnimating( _ size: CGSize? = nil, message: String? = nil, messageFont: UIFont? = nil, type: NVActivityIndicatorType? = nil, color: UIColor? = nil, padding: CGFloat? = nil, displayTimeThreshold: Int? = nil, minimumDisplayTime: Int? = nil, backgroundColor: UIColor? = nil, textColor: UIColor? = nil, fadeInAnimation: FadeInAnimation? = NVActivityIndicatorView.DEFAULT_FADE_IN_ANIMATION) { let activityData = ActivityData(size: size, message: message, messageFont: messageFont, type: type, color: color, padding: padding, displayTimeThreshold: displayTimeThreshold, minimumDisplayTime: minimumDisplayTime, backgroundColor: backgroundColor, textColor: textColor) NVActivityIndicatorPresenter.sharedInstance.startAnimating(activityData, fadeInAnimation) } /** Remove UI blocker. - parameter fadeOutAnimation: fade out animation. */ func stopAnimating(_ fadeOutAnimation: FadeOutAnimation? = NVActivityIndicatorView.DEFAULT_FADE_OUT_ANIMATION) { NVActivityIndicatorPresenter.sharedInstance.stopAnimating(fadeOutAnimation) } } #endif ================================================ FILE: Tests/ActivityDataTests.swift ================================================ // // ActivityDataTests.swift // NVActivityIndicatorViewDemo // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // import XCTest @testable import NVActivityIndicatorView @testable import NVActivityIndicatorViewExtended class ActivityDataTests: XCTestCase { func testInitWithoutParams() { let activityData = ActivityData() XCTAssertEqual(activityData.size, NVActivityIndicatorView.DEFAULT_BLOCKER_SIZE) XCTAssertNil(activityData.message) XCTAssertEqual(activityData.type, NVActivityIndicatorView.DEFAULT_TYPE) XCTAssertEqual(activityData.color, NVActivityIndicatorView.DEFAULT_COLOR) XCTAssertEqual(activityData.padding, NVActivityIndicatorView.DEFAULT_PADDING) XCTAssertEqual(activityData.displayTimeThreshold, NVActivityIndicatorView.DEFAULT_BLOCKER_DISPLAY_TIME_THRESHOLD) XCTAssertEqual(activityData.minimumDisplayTime, NVActivityIndicatorView.DEFAULT_BLOCKER_MINIMUM_DISPLAY_TIME) XCTAssertEqual(activityData.backgroundColor, NVActivityIndicatorView.DEFAULT_BLOCKER_BACKGROUND_COLOR) } func testInitWithParams() { let size = CGSize(width: 100, height: 100) let message = "Loading..." let type = NVActivityIndicatorType.ballBeat let color = UIColor.red let padding: CGFloat = 10 let displayTimeThreshold = 100 let minimumDisplayTime = 150 let backgroundColor = UIColor.red let textColor = UIColor.purple let activityData = ActivityData(size: size, message: message, type: type, color: color, padding: padding, displayTimeThreshold: displayTimeThreshold, minimumDisplayTime: minimumDisplayTime, backgroundColor: backgroundColor, textColor: textColor) XCTAssertEqual(activityData.size, size) XCTAssertEqual(activityData.message, message) XCTAssertEqual(activityData.type, type) XCTAssertEqual(activityData.color, color) XCTAssertEqual(activityData.padding, padding) XCTAssertEqual(activityData.displayTimeThreshold, displayTimeThreshold) XCTAssertEqual(activityData.minimumDisplayTime, minimumDisplayTime) XCTAssertEqual(activityData.backgroundColor, backgroundColor) XCTAssertEqual(activityData.textColor, textColor) } func testTextColorInitWithColor() { let color = UIColor.red let activityData = ActivityData(color: color) XCTAssertEqual(activityData.color, color) XCTAssertEqual(activityData.textColor, color) // textColor matches color } func testNoColorOrTextColorInit() { let activityData = ActivityData() XCTAssertEqual(activityData.textColor, NVActivityIndicatorView.DEFAULT_TEXT_COLOR) } } ================================================ FILE: Tests/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString 5.2.0 CFBundleVersion 48 ================================================ FILE: Tests/NVActivityIndicatorPresenterTests.swift ================================================ // // NVActivityIndicatorPresenterTests.swift // NVActivityIndicatorViewDemo // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // import XCTest @testable import NVActivityIndicatorView @testable import NVActivityIndicatorViewExtended class NVActivityIndicatorPresenterTests: XCTestCase { let approximateZero: Int = 20 func testZeroDisplayTimeThreshold() { let activityData = createActivityData(displayTimeThreshold: 0, minimumDisplayTime: 0) XCTAssertFalse(checkActivityViewAppeared()) NVActivityIndicatorPresenter.sharedInstance.startAnimating(activityData, nil) doAfter(approximateZero) { XCTAssertTrue(self.checkActivityViewAppeared()) } NVActivityIndicatorPresenter.sharedInstance.stopAnimating(nil) doAfter(approximateZero) { XCTAssertFalse(self.checkActivityViewAppeared()) } } func testNonZeroDisplayTimeThreshold() { let activityData = createActivityData(displayTimeThreshold: 100, minimumDisplayTime: 0) XCTAssertFalse(checkActivityViewAppeared()) NVActivityIndicatorPresenter.sharedInstance.startAnimating(activityData, nil) doAfter(50) { XCTAssertFalse(self.checkActivityViewAppeared()) } NVActivityIndicatorPresenter.sharedInstance.stopAnimating(nil) doAfter(approximateZero) { XCTAssertFalse(self.checkActivityViewAppeared()) } NVActivityIndicatorPresenter.sharedInstance.startAnimating(activityData, nil) doAfter(100) { XCTAssertTrue(self.checkActivityViewAppeared()) } NVActivityIndicatorPresenter.sharedInstance.stopAnimating(nil) doAfter(approximateZero) { XCTAssertFalse(self.checkActivityViewAppeared()) } } func testZeroMinimumDisplayTime() { let activityData = createActivityData(displayTimeThreshold: 0, minimumDisplayTime: 0) XCTAssertFalse(checkActivityViewAppeared()) NVActivityIndicatorPresenter.sharedInstance.startAnimating(activityData, nil) doAfter(approximateZero) { XCTAssertTrue(self.checkActivityViewAppeared()) } NVActivityIndicatorPresenter.sharedInstance.stopAnimating(nil) doAfter(approximateZero) { XCTAssertFalse(self.checkActivityViewAppeared()) } } func testNonZeroMinimumDisplayTime() { let activityData = createActivityData(displayTimeThreshold: 0, minimumDisplayTime: 100) XCTAssertFalse(checkActivityViewAppeared()) NVActivityIndicatorPresenter.sharedInstance.startAnimating(activityData, nil) doAfter(approximateZero) { XCTAssertTrue(self.checkActivityViewAppeared()) } NVActivityIndicatorPresenter.sharedInstance.stopAnimating(nil) doAfter(50) { XCTAssertTrue(self.checkActivityViewAppeared()) } doAfter(50) { XCTAssertFalse(self.checkActivityViewAppeared()) } } func testStartStop() { let activityData = createActivityData(displayTimeThreshold: 0, minimumDisplayTime: 0) XCTAssertFalse(checkActivityViewAppeared()) NVActivityIndicatorPresenter.sharedInstance.startAnimating(activityData, nil) NVActivityIndicatorPresenter.sharedInstance.stopAnimating(nil) doAfter(approximateZero) { XCTAssertFalse(self.checkActivityViewAppeared()) } } func testStartStopStart() { let activityData = createActivityData(displayTimeThreshold: 0, minimumDisplayTime: 0) XCTAssertFalse(checkActivityViewAppeared()) NVActivityIndicatorPresenter.sharedInstance.startAnimating(activityData, nil) doAfter(approximateZero) { XCTAssertTrue(self.checkActivityViewAppeared()) NVActivityIndicatorPresenter.sharedInstance.stopAnimating(nil) NVActivityIndicatorPresenter.sharedInstance.startAnimating(activityData, nil) } doAfter(approximateZero * 2) { XCTAssertTrue(self.checkActivityViewAppeared()) NVActivityIndicatorPresenter.sharedInstance.stopAnimating(nil) } doAfter(approximateZero * 3) { XCTAssertFalse(self.checkActivityViewAppeared()) } } func testIsAnimating() { let activityData = createActivityData(displayTimeThreshold: 100, minimumDisplayTime: 100) // .stopped XCTAssertFalse(NVActivityIndicatorPresenter.sharedInstance.isAnimating) // .waitingToStart NVActivityIndicatorPresenter.sharedInstance.startAnimating(activityData, nil) doAfter(50) { XCTAssertFalse(NVActivityIndicatorPresenter.sharedInstance.isAnimating) } // .animating doAfter(50) { XCTAssertTrue(NVActivityIndicatorPresenter.sharedInstance.isAnimating) } // .waitingToStop NVActivityIndicatorPresenter.sharedInstance.stopAnimating(nil) doAfter(50) { XCTAssertTrue(NVActivityIndicatorPresenter.sharedInstance.isAnimating) } // .stopped doAfter(50) { XCTAssertFalse(NVActivityIndicatorPresenter.sharedInstance.isAnimating) } } // MARK: Helpers func createActivityData(displayTimeThreshold: Int, minimumDisplayTime: Int) -> ActivityData { return ActivityData(size: CGSize.zero, message: "", type: nil, color: nil, padding: 0, displayTimeThreshold: displayTimeThreshold, minimumDisplayTime: minimumDisplayTime, textColor: nil) } func checkActivityViewAppeared() -> Bool { for item in UIApplication.shared.keyWindow!.subviews where item.restorationIdentifier == "NVActivityIndicatorViewContainer" { return true } return false } func doAfter(_ after: Int, thing: @escaping () -> Void) { let expectation = self.expectation(description: "") DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(after)) { thing() expectation.fulfill() } waitForExpectations(timeout: Double(after) * 1.5 / 1000) { error in print(error ?? "Timeout") } } } ================================================ FILE: Tests/NVActivityIndicatorTypeTests.swift ================================================ // // NVActivityIndicatorTypeTests.swift // NVActivityIndicatorViewDemo // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // import XCTest @testable import NVActivityIndicatorView @testable import NVActivityIndicatorViewExtended class NVActivityIndicatorTypeTests: XCTestCase { func testAnimation() { let enumClass: [NVActivityIndicatorType: NVActivityIndicatorAnimationDelegate.Type] = [ .blank: NVActivityIndicatorAnimationBlank.self, .ballPulse: NVActivityIndicatorAnimationBallPulse.self, .ballGridPulse: NVActivityIndicatorAnimationBallGridPulse.self, .ballClipRotate: NVActivityIndicatorAnimationBallClipRotate.self, .squareSpin: NVActivityIndicatorAnimationSquareSpin.self, .ballClipRotatePulse: NVActivityIndicatorAnimationBallClipRotatePulse.self, .ballClipRotateMultiple: NVActivityIndicatorAnimationBallClipRotateMultiple.self, .ballPulseRise: NVActivityIndicatorAnimationBallPulseRise.self, .ballRotate: NVActivityIndicatorAnimationBallRotate.self, .cubeTransition: NVActivityIndicatorAnimationCubeTransition.self, .ballZigZag: NVActivityIndicatorAnimationBallZigZag.self, .ballZigZagDeflect: NVActivityIndicatorAnimationBallZigZagDeflect.self, .ballTrianglePath: NVActivityIndicatorAnimationBallTrianglePath.self, .ballScale: NVActivityIndicatorAnimationBallScale.self, .lineScale: NVActivityIndicatorAnimationLineScale.self, .lineScaleParty: NVActivityIndicatorAnimationLineScaleParty.self, .ballScaleMultiple: NVActivityIndicatorAnimationBallScaleMultiple.self, .ballPulseSync: NVActivityIndicatorAnimationBallPulseSync.self, .ballBeat: NVActivityIndicatorAnimationBallBeat.self, .lineScalePulseOut: NVActivityIndicatorAnimationLineScalePulseOut.self, .lineScalePulseOutRapid: NVActivityIndicatorAnimationLineScalePulseOutRapid.self, .ballScaleRipple: NVActivityIndicatorAnimationBallScaleRipple.self, .ballScaleRippleMultiple: NVActivityIndicatorAnimationBallScaleRippleMultiple.self, .ballSpinFadeLoader: NVActivityIndicatorAnimationBallSpinFadeLoader.self, .lineSpinFadeLoader: NVActivityIndicatorAnimationLineSpinFadeLoader.self, .triangleSkewSpin: NVActivityIndicatorAnimationTriangleSkewSpin.self, .pacman: NVActivityIndicatorAnimationPacman.self, .ballGridBeat: NVActivityIndicatorAnimationBallGridBeat.self, .semiCircleSpin: NVActivityIndicatorAnimationSemiCircleSpin.self, .ballRotateChase: NVActivityIndicatorAnimationBallRotateChase.self, .orbit: NVActivityIndicatorAnimationOrbit.self, .audioEqualizer: NVActivityIndicatorAnimationAudioEqualizer.self, .circleStrokeSpin: NVActivityIndicatorAnimationCircleStrokeSpin.self ] enumClass.forEach { enumm, clazz in let enummClassString = String(describing: enumm.animation()) let clazzString = String(describing: clazz) XCTAssertTrue(enummClassString.hasSuffix(clazzString)) } } func testAllCases() { XCTAssertEqual(NVActivityIndicatorType.allCases.last, NVActivityIndicatorType.circleStrokeSpin) } } ================================================ FILE: Tests/NVActivityIndicatorViewTests.swift ================================================ // // NVActivityIndicatorViewTests.swift // NVActivityIndicatorViewTests // // The MIT License (MIT) // Copyright (c) 2016 Vinh Nguyen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // import XCTest @testable import NVActivityIndicatorView @testable import NVActivityIndicatorViewExtended class NVActivityIndicatorViewTests: XCTestCase { var activityIndicatorView: NVActivityIndicatorView! override func setUp() { super.setUp() activityIndicatorView = NVActivityIndicatorView(frame: CGRect.zero) } override func tearDown() { activityIndicatorView = nil super.tearDown() } func testDefaultValues() { XCTAssertEqual(NVActivityIndicatorView.DEFAULT_TYPE, NVActivityIndicatorType.ballSpinFadeLoader) XCTAssertEqual(NVActivityIndicatorView.DEFAULT_COLOR, UIColor.white) XCTAssertEqual(NVActivityIndicatorView.DEFAULT_PADDING, 0) XCTAssertEqual(NVActivityIndicatorView.DEFAULT_BLOCKER_SIZE, CGSize(width: 60, height: 60)) XCTAssertEqual(NVActivityIndicatorView.DEFAULT_BLOCKER_MINIMUM_DISPLAY_TIME, 0) XCTAssertEqual(NVActivityIndicatorView.DEFAULT_BLOCKER_DISPLAY_TIME_THRESHOLD, 0) XCTAssertEqual(NVActivityIndicatorView.DEFAULT_BLOCKER_BACKGROUND_COLOR, UIColor(red: 0, green: 0, blue: 0, alpha: 0.5)) } func testSetTypeName() { activityIndicatorView._setTypeName("unknown") XCTAssertEqual(activityIndicatorView.type, NVActivityIndicatorView.DEFAULT_TYPE) activityIndicatorView._setTypeName("Orbit") XCTAssertEqual(activityIndicatorView.type, NVActivityIndicatorType.orbit) activityIndicatorView._setTypeName("orbit") XCTAssertEqual(activityIndicatorView.type, NVActivityIndicatorType.orbit) activityIndicatorView._setTypeName("orbiT") XCTAssertEqual(activityIndicatorView.type, NVActivityIndicatorType.orbit) } func testGetTypeName() { activityIndicatorView.type = .audioEqualizer XCTAssertEqual(activityIndicatorView.getTypeName(), String(describing: NVActivityIndicatorType.audioEqualizer)) } func testInitWithParams() { let type: NVActivityIndicatorType = .audioEqualizer let color = UIColor.red let padding: CGFloat = 10 activityIndicatorView = NVActivityIndicatorView(frame: CGRect.zero, type: type) XCTAssertEqual(activityIndicatorView.type, type) activityIndicatorView = NVActivityIndicatorView(frame: CGRect.zero, color: color) XCTAssertEqual(activityIndicatorView.color, color) activityIndicatorView = NVActivityIndicatorView(frame: CGRect.zero, padding: padding) XCTAssertEqual(activityIndicatorView.padding, padding) activityIndicatorView = NVActivityIndicatorView(frame: CGRect.zero, type: type, color: color, padding: padding) XCTAssertEqual(activityIndicatorView.type, type) XCTAssertEqual(activityIndicatorView.color, color) XCTAssertEqual(activityIndicatorView.padding, padding) } func testInitWithFrame() { activityIndicatorView = NVActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 50, height: 50)) XCTAssertEqual(activityIndicatorView.type, NVActivityIndicatorView.DEFAULT_TYPE) XCTAssertEqual(activityIndicatorView.color, NVActivityIndicatorView.DEFAULT_COLOR) XCTAssertEqual(activityIndicatorView.padding, NVActivityIndicatorView.DEFAULT_PADDING) } func testStartAnimating() { XCTAssertFalse(activityIndicatorView.isAnimating) XCTAssertTrue(activityIndicatorView.isHidden) activityIndicatorView.startAnimating() XCTAssertEqual(activityIndicatorView.layer.speed, 1) XCTAssertTrue(activityIndicatorView.isAnimating) XCTAssertFalse(activityIndicatorView.isHidden) } func testStopAnimating() { activityIndicatorView.startAnimating() activityIndicatorView.stopAnimating() XCTAssertFalse(activityIndicatorView.isAnimating) XCTAssertTrue(activityIndicatorView.isHidden) XCTAssertTrue(activityIndicatorView.layer.sublayers == nil) } } ================================================ FILE: Tests/Tests-Bridging-Header.h ================================================ // // Use this file to import your target's public headers that you would like to expose to Swift. // ================================================ FILE: docs/CNAME ================================================ nvactivityindicatorview.vinhis.me ================================================ FILE: docs/Classes/NVActivityIndicatorView.html ================================================ NVActivityIndicatorView Class Reference

NVActivityIndicatorView 5.2.0 Docs

GitHub View on GitHub

NVActivityIndicatorView

Activity indicator view with nice animations

  • Default type. Default value is .BallSpinFadeLoader.

  • Default color of activity indicator. Default value is UIColor.white.

  • Default color of text. Default value is UIColor.white.

  • Default padding. Default value is 0.

  • Default size of activity indicator view in UI blocker. Default value is 60x60.

  • Default display time threshold to actually display UI blocker. Default value is 0 ms.

    Note

    Default time that has to be elapsed (between calls of startAnimating() and stopAnimating()) in order to actually display UI blocker. It should be set thinking about what the minimum duration of an activity is to be worth showing it to the user. If the activity ends before this time threshold, then it will not be displayed at all.
  • Default minimum display time of UI blocker. Default value is 0 ms.

    Note

    Default minimum display time of UI blocker. Its main purpose is to avoid flashes showing and hiding it so fast. For instance, setting it to 200ms will force UI blocker to be shown for at least this time (regardless of calling stopAnimating() ealier).
  • Default message displayed in UI blocker. Default value is nil.

  • Default message spacing to activity indicator view in UI blocker. Default value is 8.

  • Default font of message displayed in UI blocker. Default value is bold system font, size 20.

  • Default background color of UI blocker. Default value is UIColor(red: 0, green: 0, blue: 0, alpha: 0.5)

  • Default fade in animation.

  • Default fade out animation.

  • Animation type.

  • Color of activity indicator view.

  • Padding of activity indicator view.

  • Current status of animation, read-only.

  • Returns an object initialized from data in a given unarchiver. self, initialized using the data in decoder.

  • Create a activity indicator view.

    Appropriate NVActivityIndicatorView.DEFAULT_* values are used for omitted params.

  • Returns the natural size for the receiving view, considering only properties of the view itself.

    A size indicating the natural size for the receiving view based on its intrinsic properties.

  • Start animating.

  • Stop animating.

================================================ FILE: docs/Classes.html ================================================ Classes Reference

NVActivityIndicatorView 5.2.0 Docs

GitHub View on GitHub

================================================ FILE: docs/Enums/NVActivityIndicatorType.html ================================================ NVActivityIndicatorType Enumeration Reference

NVActivityIndicatorView 5.2.0 Docs

GitHub View on GitHub

NVActivityIndicatorType

Enum of animation types used for activity indicator view.

  • Blank: Blank animation.
  • BallPulse: BallPulse animation.
  • BallGridPulse: BallGridPulse animation.
  • BallClipRotate: BallClipRotate animation.
  • SquareSpin: SquareSpin animation.
  • BallClipRotatePulse: BallClipRotatePulse animation.
  • BallClipRotateMultiple: BallClipRotateMultiple animation.
  • BallPulseRise: BallPulseRise animation.
  • BallRotate: BallRotate animation.
  • CubeTransition: CubeTransition animation.
  • BallZigZag: BallZigZag animation.
  • BallZigZagDeflect: BallZigZagDeflect animation.
  • BallTrianglePath: BallTrianglePath animation.
  • BallScale: BallScale animation.
  • LineScale: LineScale animation.
  • LineScaleParty: LineScaleParty animation.
  • BallScaleMultiple: BallScaleMultiple animation.
  • BallPulseSync: BallPulseSync animation.
  • BallBeat: BallBeat animation.
  • BallDoubleBounce: BallDoubleBounce animation.
  • LineScalePulseOut: LineScalePulseOut animation.
  • LineScalePulseOutRapid: LineScalePulseOutRapid animation.
  • BallScaleRipple: BallScaleRipple animation.
  • BallScaleRippleMultiple: BallScaleRippleMultiple animation.
  • BallSpinFadeLoader: BallSpinFadeLoader animation.
  • LineSpinFadeLoader: LineSpinFadeLoader animation.
  • TriangleSkewSpin: TriangleSkewSpin animation.
  • Pacman: Pacman animation.
  • BallGridBeat: BallGridBeat animation.
  • SemiCircleSpin: SemiCircleSpin animation.
  • BallRotateChase: BallRotateChase animation.
  • Orbit: Orbit animation.
  • AudioEqualizer: AudioEqualizer animation.
  • CircleStrokeSpin: CircleStrokeSpin animation.
================================================ FILE: docs/Enums.html ================================================ Enumerations Reference

NVActivityIndicatorView 5.2.0 Docs

GitHub View on GitHub

Enumerations

The following enumerations are available globally.

  • Enum of animation types used for activity indicator view.

    • Blank: Blank animation.
    • BallPulse: BallPulse animation.
    • BallGridPulse: BallGridPulse animation.
    • BallClipRotate: BallClipRotate animation.
    • SquareSpin: SquareSpin animation.
    • BallClipRotatePulse: BallClipRotatePulse animation.
    • BallClipRotateMultiple: BallClipRotateMultiple animation.
    • BallPulseRise: BallPulseRise animation.
    • BallRotate: BallRotate animation.
    • CubeTransition: CubeTransition animation.
    • BallZigZag: BallZigZag animation.
    • BallZigZagDeflect: BallZigZagDeflect animation.
    • BallTrianglePath: BallTrianglePath animation.
    • BallScale: BallScale animation.
    • LineScale: LineScale animation.
    • LineScaleParty: LineScaleParty animation.
    • BallScaleMultiple: BallScaleMultiple animation.
    • BallPulseSync: BallPulseSync animation.
    • BallBeat: BallBeat animation.
    • BallDoubleBounce: BallDoubleBounce animation.
    • LineScalePulseOut: LineScalePulseOut animation.
    • LineScalePulseOutRapid: LineScalePulseOutRapid animation.
    • BallScaleRipple: BallScaleRipple animation.
    • BallScaleRippleMultiple: BallScaleRippleMultiple animation.
    • BallSpinFadeLoader: BallSpinFadeLoader animation.
    • LineSpinFadeLoader: LineSpinFadeLoader animation.
    • TriangleSkewSpin: TriangleSkewSpin animation.
    • Pacman: Pacman animation.
    • BallGridBeat: BallGridBeat animation.
    • SemiCircleSpin: SemiCircleSpin animation.
    • BallRotateChase: BallRotateChase animation.
    • Orbit: Orbit animation.
    • AudioEqualizer: AudioEqualizer animation.
    • CircleStrokeSpin: CircleStrokeSpin animation.
    See more
================================================ FILE: docs/Typealiases.html ================================================ Type Aliases Reference

NVActivityIndicatorView 5.2.0 Docs

GitHub View on GitHub

Type Aliases

The following type aliases are available globally.

  • Function that performs fade in/out animation.

  • Function that performs fade out animation.

    Note

    Must call the second parameter on the animation completion.
================================================ FILE: docs/css/highlight.css ================================================ /*! Jazzy - https://github.com/realm/jazzy * Copyright Realm Inc. * SPDX-License-Identifier: MIT */ /* Credit to https://gist.github.com/wataru420/2048287 */ .highlight .c { color: #999988; font-style: italic; } .highlight .err { color: #a61717; background-color: #e3d2d2; } .highlight .k { color: #000000; font-weight: bold; } .highlight .o { color: #000000; font-weight: bold; } .highlight .cm { color: #999988; font-style: italic; } .highlight .cp { color: #999999; font-weight: bold; } .highlight .c1 { color: #999988; font-style: italic; } .highlight .cs { color: #999999; font-weight: bold; font-style: italic; } .highlight .gd { color: #000000; background-color: #ffdddd; } .highlight .gd .x { color: #000000; background-color: #ffaaaa; } .highlight .ge { color: #000000; font-style: italic; } .highlight .gr { color: #aa0000; } .highlight .gh { color: #999999; } .highlight .gi { color: #000000; background-color: #ddffdd; } .highlight .gi .x { color: #000000; background-color: #aaffaa; } .highlight .go { color: #888888; } .highlight .gp { color: #555555; } .highlight .gs { font-weight: bold; } .highlight .gu { color: #aaaaaa; } .highlight .gt { color: #aa0000; } .highlight .kc { color: #000000; font-weight: bold; } .highlight .kd { color: #000000; font-weight: bold; } .highlight .kp { color: #000000; font-weight: bold; } .highlight .kr { color: #000000; font-weight: bold; } .highlight .kt { color: #445588; } .highlight .m { color: #009999; } .highlight .s { color: #d14; } .highlight .na { color: #008080; } .highlight .nb { color: #0086B3; } .highlight .nc { color: #445588; font-weight: bold; } .highlight .no { color: #008080; } .highlight .ni { color: #800080; } .highlight .ne { color: #990000; font-weight: bold; } .highlight .nf { color: #990000; } .highlight .nn { color: #555555; } .highlight .nt { color: #000080; } .highlight .nv { color: #008080; } .highlight .ow { color: #000000; font-weight: bold; } .highlight .w { color: #bbbbbb; } .highlight .mf { color: #009999; } .highlight .mh { color: #009999; } .highlight .mi { color: #009999; } .highlight .mo { color: #009999; } .highlight .sb { color: #d14; } .highlight .sc { color: #d14; } .highlight .sd { color: #d14; } .highlight .s2 { color: #d14; } .highlight .se { color: #d14; } .highlight .sh { color: #d14; } .highlight .si { color: #d14; } .highlight .sx { color: #d14; } .highlight .sr { color: #009926; } .highlight .s1 { color: #d14; } .highlight .ss { color: #990073; } .highlight .bp { color: #999999; } .highlight .vc { color: #008080; } .highlight .vg { color: #008080; } .highlight .vi { color: #008080; } .highlight .il { color: #009999; } ================================================ FILE: docs/css/jazzy.css ================================================ /*! Jazzy - https://github.com/realm/jazzy * Copyright Realm Inc. * SPDX-License-Identifier: MIT */ *, *:before, *:after { box-sizing: inherit; } body { margin: 0; background: #fff; color: #333; font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: .2px; -webkit-font-smoothing: antialiased; box-sizing: border-box; } h1 { font-size: 2rem; font-weight: 700; margin: 1.275em 0 0.6em; } h2 { font-size: 1.75rem; font-weight: 700; margin: 1.275em 0 0.3em; } h3 { font-size: 1.5rem; font-weight: 700; margin: 1em 0 0.3em; } h4 { font-size: 1.25rem; font-weight: 700; margin: 1.275em 0 0.85em; } h5 { font-size: 1rem; font-weight: 700; margin: 1.275em 0 0.85em; } h6 { font-size: 1rem; font-weight: 700; margin: 1.275em 0 0.85em; color: #777; } p { margin: 0 0 1em; } ul, ol { padding: 0 0 0 2em; margin: 0 0 0.85em; } blockquote { margin: 0 0 0.85em; padding: 0 15px; color: #858585; border-left: 4px solid #e5e5e5; } img { max-width: 100%; } a { color: #4183c4; text-decoration: none; } a:hover, a:focus { outline: 0; text-decoration: underline; } a.discouraged { text-decoration: line-through; } a.discouraged:hover, a.discouraged:focus { text-decoration: underline line-through; } table { background: #fff; width: 100%; border-collapse: collapse; border-spacing: 0; overflow: auto; margin: 0 0 0.85em; } tr:nth-child(2n) { background-color: #fbfbfb; } th, td { padding: 6px 13px; border: 1px solid #ddd; } hr { height: 1px; border: none; background-color: #ddd; } pre { margin: 0 0 1.275em; padding: .85em 1em; overflow: auto; background: #f7f7f7; font-size: .85em; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } code { font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } .item-container p > code, .item-container li > code, .top-matter p > code, .top-matter li > code { background: #f7f7f7; padding: .2em; } .item-container p > code:before, .item-container p > code:after, .item-container li > code:before, .item-container li > code:after, .top-matter p > code:before, .top-matter p > code:after, .top-matter li > code:before, .top-matter li > code:after { letter-spacing: -.2em; content: "\00a0"; } pre code { padding: 0; white-space: pre; } .content-wrapper { display: flex; flex-direction: column; } @media (min-width: 768px) { .content-wrapper { flex-direction: row; } } .header { display: flex; padding: 8px; font-size: 0.875em; background: #444; color: #999; } .header-col { margin: 0; padding: 0 8px; } .header-col--primary { flex: 1; } .header-link { color: #fff; } .header-icon { padding-right: 2px; vertical-align: -3px; height: 16px; } .breadcrumbs { font-size: 0.875em; padding: 8px 16px; margin: 0; background: #fbfbfb; border-bottom: 1px solid #ddd; } .carat { height: 10px; margin: 0 5px; } .navigation { order: 2; } @media (min-width: 768px) { .navigation { order: 1; width: 25%; max-width: 300px; padding-bottom: 64px; overflow: hidden; word-wrap: normal; background: #fbfbfb; border-right: 1px solid #ddd; } } .nav-groups { list-style-type: none; padding-left: 0; } .nav-group-name { border-bottom: 1px solid #ddd; padding: 8px 0 8px 16px; } .nav-group-name-link { color: #333; } .nav-group-tasks { margin: 8px 0; padding: 0 0 0 8px; } .nav-group-task { font-size: 1em; list-style-type: none; white-space: nowrap; } .nav-group-task-link { color: #808080; } .main-content { order: 1; } @media (min-width: 768px) { .main-content { order: 2; flex: 1; padding-bottom: 60px; } } .section { padding: 0 32px; border-bottom: 1px solid #ddd; } .section-content { max-width: 834px; margin: 0 auto; padding: 16px 0; } .section-name { color: #666; display: block; } .section-name p { margin-bottom: inherit; } .declaration .highlight { overflow-x: initial; padding: 8px 0; margin: 0; background-color: transparent; border: none; } .task-group-section { border-top: 1px solid #ddd; } .task-group { padding-top: 0px; } .task-name-container a[name]:before { content: ""; display: block; } .section-name-container { position: relative; } .section-name-container .section-name-link { position: absolute; top: 0; left: 0; bottom: 0; right: 0; margin-bottom: 0; } .section-name-container .section-name { position: relative; pointer-events: none; z-index: 1; } .section-name-container .section-name a { pointer-events: auto; } .item-container { padding: 0; } .item { padding-top: 8px; width: 100%; list-style-type: none; } .item a[name]:before { content: ""; display: block; } .item .token, .item .direct-link { display: inline-block; text-indent: -20px; padding-left: 3px; margin-left: 20px; font-size: 1rem; } .declaration-note { font-size: .85em; color: #808080; font-style: italic; } .pointer-container { border-bottom: 1px solid #ddd; left: -23px; padding-bottom: 13px; position: relative; width: 110%; } .pointer { left: 21px; top: 7px; display: block; position: absolute; width: 12px; height: 12px; border-left: 1px solid #ddd; border-top: 1px solid #ddd; background: #fff; transform: rotate(45deg); } .height-container { display: none; position: relative; width: 100%; overflow: hidden; } .height-container .section { background: #fff; border: 1px solid #ddd; border-top-width: 0; padding-top: 10px; padding-bottom: 5px; padding: 8px 16px; } .aside, .language { padding: 6px 12px; margin: 12px 0; border-left: 5px solid #dddddd; overflow-y: hidden; } .aside .aside-title, .language .aside-title { font-size: 9px; letter-spacing: 2px; text-transform: uppercase; padding-bottom: 0; margin: 0; color: #aaa; -webkit-user-select: none; } .aside p:last-child, .language p:last-child { margin-bottom: 0; } .language { border-left: 5px solid #cde9f4; } .language .aside-title { color: #4183c4; } .aside-warning, .aside-deprecated, .aside-unavailable { border-left: 5px solid #ff6666; } .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { color: #ff0000; } .graybox { border-collapse: collapse; width: 100%; } .graybox p { margin: 0; word-break: break-word; min-width: 50px; } .graybox td { border: 1px solid #ddd; padding: 5px 25px 5px 10px; vertical-align: middle; } .graybox tr td:first-of-type { text-align: right; padding: 7px; vertical-align: top; word-break: normal; width: 40px; } .slightly-smaller { font-size: 0.9em; } .footer { padding: 8px 16px; background: #444; color: #ddd; font-size: 0.8em; } .footer p { margin: 8px 0; } .footer a { color: #fff; } html.dash .header, html.dash .breadcrumbs, html.dash .navigation { display: none; } html.dash .height-container { display: block; } form[role=search] input { font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24px; padding: 0 10px; margin: 0; border: none; border-radius: 1em; } .loading form[role=search] input { background: white url(../img/spinner.gif) center right 4px no-repeat; } form[role=search] .tt-menu { margin: 0; min-width: 300px; background: #fbfbfb; color: #333; border: 1px solid #ddd; } form[role=search] .tt-highlight { font-weight: bold; } form[role=search] .tt-suggestion { font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; padding: 0 8px; } form[role=search] .tt-suggestion span { display: table-cell; white-space: nowrap; } form[role=search] .tt-suggestion .doc-parent-name { width: 100%; text-align: right; font-weight: normal; font-size: 0.9em; padding-left: 16px; } form[role=search] .tt-suggestion:hover, form[role=search] .tt-suggestion.tt-cursor { cursor: pointer; background-color: #4183c4; color: #fff; } form[role=search] .tt-suggestion:hover .doc-parent-name, form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { color: #fff; } ================================================ FILE: docs/docsets/NVActivityIndicatorView.docset/Contents/Info.plist ================================================ CFBundleIdentifier com.jazzy.nvactivityindicatorview CFBundleName NVActivityIndicatorView DocSetPlatformFamily nvactivityindicatorview isDashDocset dashIndexFilePath index.html isJavaScriptEnabled DashDocSetFamily dashtoc ================================================ FILE: docs/docsets/NVActivityIndicatorView.docset/Contents/Resources/Documents/Classes/NVActivityIndicatorView.html ================================================ NVActivityIndicatorView Class Reference

NVActivityIndicatorView 5.2.0 Docs

GitHub View on GitHub

NVActivityIndicatorView

Activity indicator view with nice animations

  • Default type. Default value is .BallSpinFadeLoader.

  • Default color of activity indicator. Default value is UIColor.white.

  • Default color of text. Default value is UIColor.white.

  • Default padding. Default value is 0.

  • Default size of activity indicator view in UI blocker. Default value is 60x60.

  • Default display time threshold to actually display UI blocker. Default value is 0 ms.

    Note

    Default time that has to be elapsed (between calls of startAnimating() and stopAnimating()) in order to actually display UI blocker. It should be set thinking about what the minimum duration of an activity is to be worth showing it to the user. If the activity ends before this time threshold, then it will not be displayed at all.
  • Default minimum display time of UI blocker. Default value is 0 ms.

    Note

    Default minimum display time of UI blocker. Its main purpose is to avoid flashes showing and hiding it so fast. For instance, setting it to 200ms will force UI blocker to be shown for at least this time (regardless of calling stopAnimating() ealier).
  • Default message displayed in UI blocker. Default value is nil.

  • Default message spacing to activity indicator view in UI blocker. Default value is 8.

  • Default font of message displayed in UI blocker. Default value is bold system font, size 20.

  • Default background color of UI blocker. Default value is UIColor(red: 0, green: 0, blue: 0, alpha: 0.5)

  • Default fade in animation.

  • Default fade out animation.

  • Animation type.

  • Color of activity indicator view.

  • Padding of activity indicator view.

  • Current status of animation, read-only.

  • Returns an object initialized from data in a given unarchiver. self, initialized using the data in decoder.

  • Create a activity indicator view.

    Appropriate NVActivityIndicatorView.DEFAULT_* values are used for omitted params.

  • Returns the natural size for the receiving view, considering only properties of the view itself.

    A size indicating the natural size for the receiving view based on its intrinsic properties.

  • Start animating.

  • Stop animating.

================================================ FILE: docs/docsets/NVActivityIndicatorView.docset/Contents/Resources/Documents/Classes.html ================================================ Classes Reference

NVActivityIndicatorView 5.2.0 Docs

GitHub View on GitHub

================================================ FILE: docs/docsets/NVActivityIndicatorView.docset/Contents/Resources/Documents/Enums/NVActivityIndicatorType.html ================================================ NVActivityIndicatorType Enumeration Reference

NVActivityIndicatorView 5.2.0 Docs

GitHub View on GitHub

NVActivityIndicatorType

Enum of animation types used for activity indicator view.

  • Blank: Blank animation.
  • BallPulse: BallPulse animation.
  • BallGridPulse: BallGridPulse animation.
  • BallClipRotate: BallClipRotate animation.
  • SquareSpin: SquareSpin animation.
  • BallClipRotatePulse: BallClipRotatePulse animation.
  • BallClipRotateMultiple: BallClipRotateMultiple animation.
  • BallPulseRise: BallPulseRise animation.
  • BallRotate: BallRotate animation.
  • CubeTransition: CubeTransition animation.
  • BallZigZag: BallZigZag animation.
  • BallZigZagDeflect: BallZigZagDeflect animation.
  • BallTrianglePath: BallTrianglePath animation.
  • BallScale: BallScale animation.
  • LineScale: LineScale animation.
  • LineScaleParty: LineScaleParty animation.
  • BallScaleMultiple: BallScaleMultiple animation.
  • BallPulseSync: BallPulseSync animation.
  • BallBeat: BallBeat animation.
  • BallDoubleBounce: BallDoubleBounce animation.
  • LineScalePulseOut: LineScalePulseOut animation.
  • LineScalePulseOutRapid: LineScalePulseOutRapid animation.
  • BallScaleRipple: BallScaleRipple animation.
  • BallScaleRippleMultiple: BallScaleRippleMultiple animation.
  • BallSpinFadeLoader: BallSpinFadeLoader animation.
  • LineSpinFadeLoader: LineSpinFadeLoader animation.
  • TriangleSkewSpin: TriangleSkewSpin animation.
  • Pacman: Pacman animation.
  • BallGridBeat: BallGridBeat animation.
  • SemiCircleSpin: SemiCircleSpin animation.
  • BallRotateChase: BallRotateChase animation.
  • Orbit: Orbit animation.
  • AudioEqualizer: AudioEqualizer animation.
  • CircleStrokeSpin: CircleStrokeSpin animation.
================================================ FILE: docs/docsets/NVActivityIndicatorView.docset/Contents/Resources/Documents/Enums.html ================================================ Enumerations Reference

NVActivityIndicatorView 5.2.0 Docs

GitHub View on GitHub

Enumerations

The following enumerations are available globally.

  • Enum of animation types used for activity indicator view.

    • Blank: Blank animation.
    • BallPulse: BallPulse animation.
    • BallGridPulse: BallGridPulse animation.
    • BallClipRotate: BallClipRotate animation.
    • SquareSpin: SquareSpin animation.
    • BallClipRotatePulse: BallClipRotatePulse animation.
    • BallClipRotateMultiple: BallClipRotateMultiple animation.
    • BallPulseRise: BallPulseRise animation.
    • BallRotate: BallRotate animation.
    • CubeTransition: CubeTransition animation.
    • BallZigZag: BallZigZag animation.
    • BallZigZagDeflect: BallZigZagDeflect animation.
    • BallTrianglePath: BallTrianglePath animation.
    • BallScale: BallScale animation.
    • LineScale: LineScale animation.
    • LineScaleParty: LineScaleParty animation.
    • BallScaleMultiple: BallScaleMultiple animation.
    • BallPulseSync: BallPulseSync animation.
    • BallBeat: BallBeat animation.
    • BallDoubleBounce: BallDoubleBounce animation.
    • LineScalePulseOut: LineScalePulseOut animation.
    • LineScalePulseOutRapid: LineScalePulseOutRapid animation.
    • BallScaleRipple: BallScaleRipple animation.
    • BallScaleRippleMultiple: BallScaleRippleMultiple animation.
    • BallSpinFadeLoader: BallSpinFadeLoader animation.
    • LineSpinFadeLoader: LineSpinFadeLoader animation.
    • TriangleSkewSpin: TriangleSkewSpin animation.
    • Pacman: Pacman animation.
    • BallGridBeat: BallGridBeat animation.
    • SemiCircleSpin: SemiCircleSpin animation.
    • BallRotateChase: BallRotateChase animation.
    • Orbit: Orbit animation.
    • AudioEqualizer: AudioEqualizer animation.
    • CircleStrokeSpin: CircleStrokeSpin animation.
    See more
================================================ FILE: docs/docsets/NVActivityIndicatorView.docset/Contents/Resources/Documents/Typealiases.html ================================================ Type Aliases Reference

NVActivityIndicatorView 5.2.0 Docs

GitHub View on GitHub

Type Aliases

The following type aliases are available globally.

  • Function that performs fade in/out animation.

  • Function that performs fade out animation.

    Note

    Must call the second parameter on the animation completion.
================================================ FILE: docs/docsets/NVActivityIndicatorView.docset/Contents/Resources/Documents/css/highlight.css ================================================ /*! Jazzy - https://github.com/realm/jazzy * Copyright Realm Inc. * SPDX-License-Identifier: MIT */ /* Credit to https://gist.github.com/wataru420/2048287 */ .highlight .c { color: #999988; font-style: italic; } .highlight .err { color: #a61717; background-color: #e3d2d2; } .highlight .k { color: #000000; font-weight: bold; } .highlight .o { color: #000000; font-weight: bold; } .highlight .cm { color: #999988; font-style: italic; } .highlight .cp { color: #999999; font-weight: bold; } .highlight .c1 { color: #999988; font-style: italic; } .highlight .cs { color: #999999; font-weight: bold; font-style: italic; } .highlight .gd { color: #000000; background-color: #ffdddd; } .highlight .gd .x { color: #000000; background-color: #ffaaaa; } .highlight .ge { color: #000000; font-style: italic; } .highlight .gr { color: #aa0000; } .highlight .gh { color: #999999; } .highlight .gi { color: #000000; background-color: #ddffdd; } .highlight .gi .x { color: #000000; background-color: #aaffaa; } .highlight .go { color: #888888; } .highlight .gp { color: #555555; } .highlight .gs { font-weight: bold; } .highlight .gu { color: #aaaaaa; } .highlight .gt { color: #aa0000; } .highlight .kc { color: #000000; font-weight: bold; } .highlight .kd { color: #000000; font-weight: bold; } .highlight .kp { color: #000000; font-weight: bold; } .highlight .kr { color: #000000; font-weight: bold; } .highlight .kt { color: #445588; } .highlight .m { color: #009999; } .highlight .s { color: #d14; } .highlight .na { color: #008080; } .highlight .nb { color: #0086B3; } .highlight .nc { color: #445588; font-weight: bold; } .highlight .no { color: #008080; } .highlight .ni { color: #800080; } .highlight .ne { color: #990000; font-weight: bold; } .highlight .nf { color: #990000; } .highlight .nn { color: #555555; } .highlight .nt { color: #000080; } .highlight .nv { color: #008080; } .highlight .ow { color: #000000; font-weight: bold; } .highlight .w { color: #bbbbbb; } .highlight .mf { color: #009999; } .highlight .mh { color: #009999; } .highlight .mi { color: #009999; } .highlight .mo { color: #009999; } .highlight .sb { color: #d14; } .highlight .sc { color: #d14; } .highlight .sd { color: #d14; } .highlight .s2 { color: #d14; } .highlight .se { color: #d14; } .highlight .sh { color: #d14; } .highlight .si { color: #d14; } .highlight .sx { color: #d14; } .highlight .sr { color: #009926; } .highlight .s1 { color: #d14; } .highlight .ss { color: #990073; } .highlight .bp { color: #999999; } .highlight .vc { color: #008080; } .highlight .vg { color: #008080; } .highlight .vi { color: #008080; } .highlight .il { color: #009999; } ================================================ FILE: docs/docsets/NVActivityIndicatorView.docset/Contents/Resources/Documents/css/jazzy.css ================================================ /*! Jazzy - https://github.com/realm/jazzy * Copyright Realm Inc. * SPDX-License-Identifier: MIT */ *, *:before, *:after { box-sizing: inherit; } body { margin: 0; background: #fff; color: #333; font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: .2px; -webkit-font-smoothing: antialiased; box-sizing: border-box; } h1 { font-size: 2rem; font-weight: 700; margin: 1.275em 0 0.6em; } h2 { font-size: 1.75rem; font-weight: 700; margin: 1.275em 0 0.3em; } h3 { font-size: 1.5rem; font-weight: 700; margin: 1em 0 0.3em; } h4 { font-size: 1.25rem; font-weight: 700; margin: 1.275em 0 0.85em; } h5 { font-size: 1rem; font-weight: 700; margin: 1.275em 0 0.85em; } h6 { font-size: 1rem; font-weight: 700; margin: 1.275em 0 0.85em; color: #777; } p { margin: 0 0 1em; } ul, ol { padding: 0 0 0 2em; margin: 0 0 0.85em; } blockquote { margin: 0 0 0.85em; padding: 0 15px; color: #858585; border-left: 4px solid #e5e5e5; } img { max-width: 100%; } a { color: #4183c4; text-decoration: none; } a:hover, a:focus { outline: 0; text-decoration: underline; } a.discouraged { text-decoration: line-through; } a.discouraged:hover, a.discouraged:focus { text-decoration: underline line-through; } table { background: #fff; width: 100%; border-collapse: collapse; border-spacing: 0; overflow: auto; margin: 0 0 0.85em; } tr:nth-child(2n) { background-color: #fbfbfb; } th, td { padding: 6px 13px; border: 1px solid #ddd; } hr { height: 1px; border: none; background-color: #ddd; } pre { margin: 0 0 1.275em; padding: .85em 1em; overflow: auto; background: #f7f7f7; font-size: .85em; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } code { font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } .item-container p > code, .item-container li > code, .top-matter p > code, .top-matter li > code { background: #f7f7f7; padding: .2em; } .item-container p > code:before, .item-container p > code:after, .item-container li > code:before, .item-container li > code:after, .top-matter p > code:before, .top-matter p > code:after, .top-matter li > code:before, .top-matter li > code:after { letter-spacing: -.2em; content: "\00a0"; } pre code { padding: 0; white-space: pre; } .content-wrapper { display: flex; flex-direction: column; } @media (min-width: 768px) { .content-wrapper { flex-direction: row; } } .header { display: flex; padding: 8px; font-size: 0.875em; background: #444; color: #999; } .header-col { margin: 0; padding: 0 8px; } .header-col--primary { flex: 1; } .header-link { color: #fff; } .header-icon { padding-right: 2px; vertical-align: -3px; height: 16px; } .breadcrumbs { font-size: 0.875em; padding: 8px 16px; margin: 0; background: #fbfbfb; border-bottom: 1px solid #ddd; } .carat { height: 10px; margin: 0 5px; } .navigation { order: 2; } @media (min-width: 768px) { .navigation { order: 1; width: 25%; max-width: 300px; padding-bottom: 64px; overflow: hidden; word-wrap: normal; background: #fbfbfb; border-right: 1px solid #ddd; } } .nav-groups { list-style-type: none; padding-left: 0; } .nav-group-name { border-bottom: 1px solid #ddd; padding: 8px 0 8px 16px; } .nav-group-name-link { color: #333; } .nav-group-tasks { margin: 8px 0; padding: 0 0 0 8px; } .nav-group-task { font-size: 1em; list-style-type: none; white-space: nowrap; } .nav-group-task-link { color: #808080; } .main-content { order: 1; } @media (min-width: 768px) { .main-content { order: 2; flex: 1; padding-bottom: 60px; } } .section { padding: 0 32px; border-bottom: 1px solid #ddd; } .section-content { max-width: 834px; margin: 0 auto; padding: 16px 0; } .section-name { color: #666; display: block; } .section-name p { margin-bottom: inherit; } .declaration .highlight { overflow-x: initial; padding: 8px 0; margin: 0; background-color: transparent; border: none; } .task-group-section { border-top: 1px solid #ddd; } .task-group { padding-top: 0px; } .task-name-container a[name]:before { content: ""; display: block; } .section-name-container { position: relative; } .section-name-container .section-name-link { position: absolute; top: 0; left: 0; bottom: 0; right: 0; margin-bottom: 0; } .section-name-container .section-name { position: relative; pointer-events: none; z-index: 1; } .section-name-container .section-name a { pointer-events: auto; } .item-container { padding: 0; } .item { padding-top: 8px; width: 100%; list-style-type: none; } .item a[name]:before { content: ""; display: block; } .item .token, .item .direct-link { display: inline-block; text-indent: -20px; padding-left: 3px; margin-left: 20px; font-size: 1rem; } .declaration-note { font-size: .85em; color: #808080; font-style: italic; } .pointer-container { border-bottom: 1px solid #ddd; left: -23px; padding-bottom: 13px; position: relative; width: 110%; } .pointer { left: 21px; top: 7px; display: block; position: absolute; width: 12px; height: 12px; border-left: 1px solid #ddd; border-top: 1px solid #ddd; background: #fff; transform: rotate(45deg); } .height-container { display: none; position: relative; width: 100%; overflow: hidden; } .height-container .section { background: #fff; border: 1px solid #ddd; border-top-width: 0; padding-top: 10px; padding-bottom: 5px; padding: 8px 16px; } .aside, .language { padding: 6px 12px; margin: 12px 0; border-left: 5px solid #dddddd; overflow-y: hidden; } .aside .aside-title, .language .aside-title { font-size: 9px; letter-spacing: 2px; text-transform: uppercase; padding-bottom: 0; margin: 0; color: #aaa; -webkit-user-select: none; } .aside p:last-child, .language p:last-child { margin-bottom: 0; } .language { border-left: 5px solid #cde9f4; } .language .aside-title { color: #4183c4; } .aside-warning, .aside-deprecated, .aside-unavailable { border-left: 5px solid #ff6666; } .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { color: #ff0000; } .graybox { border-collapse: collapse; width: 100%; } .graybox p { margin: 0; word-break: break-word; min-width: 50px; } .graybox td { border: 1px solid #ddd; padding: 5px 25px 5px 10px; vertical-align: middle; } .graybox tr td:first-of-type { text-align: right; padding: 7px; vertical-align: top; word-break: normal; width: 40px; } .slightly-smaller { font-size: 0.9em; } .footer { padding: 8px 16px; background: #444; color: #ddd; font-size: 0.8em; } .footer p { margin: 8px 0; } .footer a { color: #fff; } html.dash .header, html.dash .breadcrumbs, html.dash .navigation { display: none; } html.dash .height-container { display: block; } form[role=search] input { font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24px; padding: 0 10px; margin: 0; border: none; border-radius: 1em; } .loading form[role=search] input { background: white url(../img/spinner.gif) center right 4px no-repeat; } form[role=search] .tt-menu { margin: 0; min-width: 300px; background: #fbfbfb; color: #333; border: 1px solid #ddd; } form[role=search] .tt-highlight { font-weight: bold; } form[role=search] .tt-suggestion { font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; padding: 0 8px; } form[role=search] .tt-suggestion span { display: table-cell; white-space: nowrap; } form[role=search] .tt-suggestion .doc-parent-name { width: 100%; text-align: right; font-weight: normal; font-size: 0.9em; padding-left: 16px; } form[role=search] .tt-suggestion:hover, form[role=search] .tt-suggestion.tt-cursor { cursor: pointer; background-color: #4183c4; color: #fff; } form[role=search] .tt-suggestion:hover .doc-parent-name, form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { color: #fff; } ================================================ FILE: docs/docsets/NVActivityIndicatorView.docset/Contents/Resources/Documents/index.html ================================================ NVActivityIndicatorView Reference

NVActivityIndicatorView 5.2.0 Docs

GitHub View on GitHub

NVActivityIndicatorView

Build Status Cocoapods Compatible Carthage Compatible

⚠️ Check out LoaderUI (ready to use with Swift Package Mananger supported) for SwiftUI implementation of this. 🎉

Introduction

NVActivityIndicatorView is a collection of awesome loading animations.

Demo

Animation types

Type Type Type Type
1. ballPulse 2. ballGridPulse 3. ballClipRotate 4. squareSpin
5. ballClipRotatePulse 6. ballClipRotateMultiple 7. ballPulseRise 8. ballRotate
9. cubeTransition 10. ballZigZag 11. ballZigZagDeflect 12. ballTrianglePath
13. ballScale 14. lineScale 15. lineScaleParty 16. ballScaleMultiple
17. ballPulseSync 18. ballBeat 19. lineScalePulseOut 20. lineScalePulseOutRapid
21. ballScaleRipple 22. ballScaleRippleMultiple 23. ballSpinFadeLoader 24. lineSpinFadeLoader
25. triangleSkewSpin 26. pacman 27. ballGridBeat 28. semiCircleSpin
29. ballRotateChase 30. orbit 31. audioEqualizer 32. circleStrokeSpin

Installation

Cocoapods

Cocoapods is a dependency manager for Swift and Objective-C Cocoa projects. To use NVActivityIndicatorView with CocoaPods, add it in your Podfile.

pod 'NVActivityIndicatorView'

Carthage

Carthage is intended to be the simplest way to add frameworks to your Cocoa application. To use NVActivityIndicatorView with Carthage, add it in your Cartfile.

github "ninjaprox/NVActivityIndicatorView"

Swift Package Manager

The Swift Package Manager is a tool for managing the distribution of Swift code. To use NVActivityIndicatorView with Swift Package Manger, add it to dependencies in your Package.swift

dependencies: [
    .package(url: "https://github.com/ninjaprox/NVActivityIndicatorView.git")
]

Migration

Version 5.0.0 comes with breaking changes. Please refer to the release note for details.

Usage

Firstly, import NVActivityIndicatorView.

import NVActivityIndicatorView

Initialization

Then, there are two ways you can create NVActivityIndicatorView:

Note: Set Module to NVActivityIndicatorView.

NVActivityIndicatorView(frame: frame, type: type, color: color, padding: padding)

Control

Start animating.

activityIndicatorView.startAnimating()

Stop animating.

activityIndicatorView.stopAnimating()

Determine if it is animating.

animating = activityIndicatorView.isAnimating

Change properties

In storyboard, you can change all properties in Attributes inspector tab of Utilities panel.

Note: Use one of values (case-insensitive) in Animation types for Type Name.

All properties are public so you can change them after initializing.

Note: All changes must be made before calling startAnimating().

Documentation

https://nvactivityindicatorview.vinhis.me/

Acknowledgment

Thanks Connor Atherton for inspired Loaders.css and Danil Gontovnik for DGActivityIndicatorView.

License

The MIT License (MIT)

Copyright © 2016 Vinh Nguyen @ninjaprox

================================================ FILE: docs/docsets/NVActivityIndicatorView.docset/Contents/Resources/Documents/js/jazzy.js ================================================ // Jazzy - https://github.com/realm/jazzy // Copyright Realm Inc. // SPDX-License-Identifier: MIT window.jazzy = {'docset': false} if (typeof window.dash != 'undefined') { document.documentElement.className += ' dash' window.jazzy.docset = true } if (navigator.userAgent.match(/xcode/i)) { document.documentElement.className += ' xcode' window.jazzy.docset = true } function toggleItem($link, $content) { var animationDuration = 300; $link.toggleClass('token-open'); $content.slideToggle(animationDuration); } function itemLinkToContent($link) { return $link.parent().parent().next(); } // On doc load + hash-change, open any targeted item function openCurrentItemIfClosed() { if (window.jazzy.docset) { return; } var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); $content = itemLinkToContent($link); if ($content.is(':hidden')) { toggleItem($link, $content); } } $(openCurrentItemIfClosed); $(window).on('hashchange', openCurrentItemIfClosed); // On item link ('token') click, toggle its discussion $('.token').on('click', function(event) { if (window.jazzy.docset) { return; } var $link = $(this); toggleItem($link, itemLinkToContent($link)); // Keeps the document from jumping to the hash. var href = $link.attr('href'); if (history.pushState) { history.pushState({}, '', href); } else { location.hash = href; } event.preventDefault(); }); // Clicks on links to the current, closed, item need to open the item $("a:not('.token')").on('click', function() { if (location == this.href) { openCurrentItemIfClosed(); } }); // KaTeX rendering if ("katex" in window) { $($('.math').each( (_, element) => { katex.render(element.textContent, element, { displayMode: $(element).hasClass('m-block'), throwOnError: false, trust: true }); })) } ================================================ FILE: docs/docsets/NVActivityIndicatorView.docset/Contents/Resources/Documents/js/jazzy.search.js ================================================ // Jazzy - https://github.com/realm/jazzy // Copyright Realm Inc. // SPDX-License-Identifier: MIT $(function(){ var $typeahead = $('[data-typeahead]'); var $form = $typeahead.parents('form'); var searchURL = $form.attr('action'); function displayTemplate(result) { return result.name; } function suggestionTemplate(result) { var t = '
'; t += '' + result.name + ''; if (result.parent_name) { t += '' + result.parent_name + ''; } t += '
'; return t; } $typeahead.one('focus', function() { $form.addClass('loading'); $.getJSON(searchURL).then(function(searchData) { const searchIndex = lunr(function() { this.ref('url'); this.field('name'); this.field('abstract'); for (const [url, doc] of Object.entries(searchData)) { this.add({url: url, name: doc.name, abstract: doc.abstract}); } }); $typeahead.typeahead( { highlight: true, minLength: 3, autoselect: true }, { limit: 10, display: displayTemplate, templates: { suggestion: suggestionTemplate }, source: function(query, sync) { const lcSearch = query.toLowerCase(); const results = searchIndex.query(function(q) { q.term(lcSearch, { boost: 100 }); q.term(lcSearch, { boost: 10, wildcard: lunr.Query.wildcard.TRAILING }); }).map(function(result) { var doc = searchData[result.ref]; doc.url = result.ref; return doc; }); sync(results); } } ); $form.removeClass('loading'); $typeahead.trigger('focus'); }); }); var baseURL = searchURL.slice(0, -"search.json".length); $typeahead.on('typeahead:select', function(e, result) { window.location = baseURL + result.url; }); }); ================================================ FILE: docs/docsets/NVActivityIndicatorView.docset/Contents/Resources/Documents/js/typeahead.jquery.js ================================================ /*! * typeahead.js 1.3.3 * https://github.com/corejavascript/typeahead.js * Copyright 2013-2024 Twitter, Inc. and other contributors; Licensed MIT */ (function(root, factory) { if (typeof define === "function" && define.amd) { define([ "jquery" ], function(a0) { return factory(a0); }); } else if (typeof module === "object" && module.exports) { module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } })(this, function($) { var _ = function() { "use strict"; return { isMsie: function() { return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false; }, isBlankString: function(str) { return !str || /^\s*$/.test(str); }, escapeRegExChars: function(str) { return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); }, isString: function(obj) { return typeof obj === "string"; }, isNumber: function(obj) { return typeof obj === "number"; }, isArray: $.isArray, isFunction: $.isFunction, isObject: $.isPlainObject, isUndefined: function(obj) { return typeof obj === "undefined"; }, isElement: function(obj) { return !!(obj && obj.nodeType === 1); }, isJQuery: function(obj) { return obj instanceof $; }, toStr: function toStr(s) { return _.isUndefined(s) || s === null ? "" : s + ""; }, bind: $.proxy, each: function(collection, cb) { $.each(collection, reverseArgs); function reverseArgs(index, value) { return cb(value, index); } }, map: $.map, filter: $.grep, every: function(obj, test) { var result = true; if (!obj) { return result; } $.each(obj, function(key, val) { if (!(result = test.call(null, val, key, obj))) { return false; } }); return !!result; }, some: function(obj, test) { var result = false; if (!obj) { return result; } $.each(obj, function(key, val) { if (result = test.call(null, val, key, obj)) { return false; } }); return !!result; }, mixin: $.extend, identity: function(x) { return x; }, clone: function(obj) { return $.extend(true, {}, obj); }, getIdGenerator: function() { var counter = 0; return function() { return counter++; }; }, templatify: function templatify(obj) { return $.isFunction(obj) ? obj : template; function template() { return String(obj); } }, defer: function(fn) { setTimeout(fn, 0); }, debounce: function(func, wait, immediate) { var timeout, result; return function() { var context = this, args = arguments, later, callNow; later = function() { timeout = null; if (!immediate) { result = func.apply(context, args); } }; callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) { result = func.apply(context, args); } return result; }; }, throttle: function(func, wait) { var context, args, timeout, result, previous, later; previous = 0; later = function() { previous = new Date(); timeout = null; result = func.apply(context, args); }; return function() { var now = new Date(), remaining = wait - (now - previous); context = this; args = arguments; if (remaining <= 0) { clearTimeout(timeout); timeout = null; previous = now; result = func.apply(context, args); } else if (!timeout) { timeout = setTimeout(later, remaining); } return result; }; }, stringify: function(val) { return _.isString(val) ? val : JSON.stringify(val); }, guid: function() { function _p8(s) { var p = (Math.random().toString(16) + "000000000").substr(2, 8); return s ? "-" + p.substr(0, 4) + "-" + p.substr(4, 4) : p; } return "tt-" + _p8() + _p8(true) + _p8(true) + _p8(); }, noop: function() {} }; }(); var WWW = function() { "use strict"; var defaultClassNames = { wrapper: "twitter-typeahead", input: "tt-input", hint: "tt-hint", menu: "tt-menu", dataset: "tt-dataset", suggestion: "tt-suggestion", selectable: "tt-selectable", empty: "tt-empty", open: "tt-open", cursor: "tt-cursor", highlight: "tt-highlight" }; return build; function build(o) { var www, classes; classes = _.mixin({}, defaultClassNames, o); www = { css: buildCss(), classes: classes, html: buildHtml(classes), selectors: buildSelectors(classes) }; return { css: www.css, html: www.html, classes: www.classes, selectors: www.selectors, mixin: function(o) { _.mixin(o, www); } }; } function buildHtml(c) { return { wrapper: '', menu: '
' }; } function buildSelectors(classes) { var selectors = {}; _.each(classes, function(v, k) { selectors[k] = "." + v; }); return selectors; } function buildCss() { var css = { wrapper: { position: "relative", display: "inline-block" }, hint: { position: "absolute", top: "0", left: "0", borderColor: "transparent", boxShadow: "none", opacity: "1" }, input: { position: "relative", verticalAlign: "top", backgroundColor: "transparent" }, inputWithNoHint: { position: "relative", verticalAlign: "top" }, menu: { position: "absolute", top: "100%", left: "0", zIndex: "100", display: "none" }, ltr: { left: "0", right: "auto" }, rtl: { left: "auto", right: " 0" } }; if (_.isMsie()) { _.mixin(css.input, { backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)" }); } return css; } }(); var EventBus = function() { "use strict"; var namespace, deprecationMap; namespace = "typeahead:"; deprecationMap = { render: "rendered", cursorchange: "cursorchanged", select: "selected", autocomplete: "autocompleted" }; function EventBus(o) { if (!o || !o.el) { $.error("EventBus initialized without el"); } this.$el = $(o.el); } _.mixin(EventBus.prototype, { _trigger: function(type, args) { var $e = $.Event(namespace + type); this.$el.trigger.call(this.$el, $e, args || []); return $e; }, before: function(type) { var args, $e; args = [].slice.call(arguments, 1); $e = this._trigger("before" + type, args); return $e.isDefaultPrevented(); }, trigger: function(type) { var deprecatedType; this._trigger(type, [].slice.call(arguments, 1)); if (deprecatedType = deprecationMap[type]) { this._trigger(deprecatedType, [].slice.call(arguments, 1)); } } }); return EventBus; }(); var EventEmitter = function() { "use strict"; var splitter = /\s+/, nextTick = getNextTick(); return { onSync: onSync, onAsync: onAsync, off: off, trigger: trigger }; function on(method, types, cb, context) { var type; if (!cb) { return this; } types = types.split(splitter); cb = context ? bindContext(cb, context) : cb; this._callbacks = this._callbacks || {}; while (type = types.shift()) { this._callbacks[type] = this._callbacks[type] || { sync: [], async: [] }; this._callbacks[type][method].push(cb); } return this; } function onAsync(types, cb, context) { return on.call(this, "async", types, cb, context); } function onSync(types, cb, context) { return on.call(this, "sync", types, cb, context); } function off(types) { var type; if (!this._callbacks) { return this; } types = types.split(splitter); while (type = types.shift()) { delete this._callbacks[type]; } return this; } function trigger(types) { var type, callbacks, args, syncFlush, asyncFlush; if (!this._callbacks) { return this; } types = types.split(splitter); args = [].slice.call(arguments, 1); while ((type = types.shift()) && (callbacks = this._callbacks[type])) { syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args)); asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args)); syncFlush() && nextTick(asyncFlush); } return this; } function getFlush(callbacks, context, args) { return flush; function flush() { var cancelled; for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) { cancelled = callbacks[i].apply(context, args) === false; } return !cancelled; } } function getNextTick() { var nextTickFn; if (window.setImmediate) { nextTickFn = function nextTickSetImmediate(fn) { setImmediate(function() { fn(); }); }; } else { nextTickFn = function nextTickSetTimeout(fn) { setTimeout(function() { fn(); }, 0); }; } return nextTickFn; } function bindContext(fn, context) { return fn.bind ? fn.bind(context) : function() { fn.apply(context, [].slice.call(arguments, 0)); }; } }(); var highlight = function(doc) { "use strict"; var defaults = { node: null, pattern: null, tagName: "strong", className: null, wordsOnly: false, caseSensitive: false, diacriticInsensitive: false }; var accented = { A: "[AaªÀ-Åà-åĀ-ąǍǎȀ-ȃȦȧᴬᵃḀḁẚẠ-ảₐ℀℁℻⒜Ⓐⓐ㍱-㍴㎀-㎄㎈㎉㎩-㎯㏂㏊㏟㏿Aa]", B: "[BbᴮᵇḂ-ḇℬ⒝Ⓑⓑ㍴㎅-㎇㏃㏈㏔㏝Bb]", C: "[CcÇçĆ-čᶜ℀ℂ℃℅℆ℭⅭⅽ⒞Ⓒⓒ㍶㎈㎉㎝㎠㎤㏄-㏇Cc]", D: "[DdĎďDŽ-džDZ-dzᴰᵈḊ-ḓⅅⅆⅮⅾ⒟Ⓓⓓ㋏㍲㍷-㍹㎗㎭-㎯㏅㏈Dd]", E: "[EeÈ-Ëè-ëĒ-ěȄ-ȇȨȩᴱᵉḘ-ḛẸ-ẽₑ℡ℯℰⅇ⒠Ⓔⓔ㉐㋍㋎Ee]", F: "[FfᶠḞḟ℉ℱ℻⒡Ⓕⓕ㎊-㎌㎙ff-fflFf]", G: "[GgĜ-ģǦǧǴǵᴳᵍḠḡℊ⒢Ⓖⓖ㋌㋍㎇㎍-㎏㎓㎬㏆㏉㏒㏿Gg]", H: "[HhĤĥȞȟʰᴴḢ-ḫẖℋ-ℎ⒣Ⓗⓗ㋌㍱㎐-㎔㏊㏋㏗Hh]", I: "[IiÌ-Ïì-ïĨ-İIJijǏǐȈ-ȋᴵᵢḬḭỈ-ịⁱℐℑℹⅈⅠ-ⅣⅥ-ⅨⅪⅫⅰ-ⅳⅵ-ⅸⅺⅻ⒤Ⓘⓘ㍺㏌㏕fiffiIi]", J: "[JjIJ-ĵLJ-njǰʲᴶⅉ⒥ⒿⓙⱼJj]", K: "[KkĶķǨǩᴷᵏḰ-ḵK⒦Ⓚⓚ㎄㎅㎉㎏㎑㎘㎞㎢㎦㎪㎸㎾㏀㏆㏍-㏏Kk]", L: "[LlĹ-ŀLJ-ljˡᴸḶḷḺ-ḽℒℓ℡Ⅼⅼ⒧Ⓛⓛ㋏㎈㎉㏐-㏓㏕㏖㏿flfflLl]", M: "[MmᴹᵐḾ-ṃ℠™ℳⅯⅿ⒨Ⓜⓜ㍷-㍹㎃㎆㎎㎒㎖㎙-㎨㎫㎳㎷㎹㎽㎿㏁㏂㏎㏐㏔-㏖㏘㏙㏞㏟Mm]", N: "[NnÑñŃ-ʼnNJ-njǸǹᴺṄ-ṋⁿℕ№⒩Ⓝⓝ㎁㎋㎚㎱㎵㎻㏌㏑Nn]", O: "[OoºÒ-Öò-öŌ-őƠơǑǒǪǫȌ-ȏȮȯᴼᵒỌ-ỏₒ℅№ℴ⒪Ⓞⓞ㍵㏇㏒㏖Oo]", P: "[PpᴾᵖṔ-ṗℙ⒫Ⓟⓟ㉐㍱㍶㎀㎊㎩-㎬㎰㎴㎺㏋㏗-㏚Pp]", Q: "[Qqℚ⒬Ⓠⓠ㏃Qq]", R: "[RrŔ-řȐ-ȓʳᴿᵣṘ-ṛṞṟ₨ℛ-ℝ⒭Ⓡⓡ㋍㍴㎭-㎯㏚㏛Rr]", S: "[SsŚ-šſȘșˢṠ-ṣ₨℁℠⒮Ⓢⓢ㎧㎨㎮-㎳㏛㏜stSs]", T: "[TtŢ-ťȚțᵀᵗṪ-ṱẗ℡™⒯Ⓣⓣ㉐㋏㎔㏏ſtstTt]", U: "[UuÙ-Üù-üŨ-ųƯưǓǔȔ-ȗᵁᵘᵤṲ-ṷỤ-ủ℆⒰Ⓤⓤ㍳㍺Uu]", V: "[VvᵛᵥṼ-ṿⅣ-Ⅷⅳ-ⅷ⒱Ⓥⓥⱽ㋎㍵㎴-㎹㏜㏞Vv]", W: "[WwŴŵʷᵂẀ-ẉẘ⒲Ⓦⓦ㎺-㎿㏝Ww]", X: "[XxˣẊ-ẍₓ℻Ⅸ-Ⅻⅸ-ⅻ⒳Ⓧⓧ㏓Xx]", Y: "[YyÝýÿŶ-ŸȲȳʸẎẏẙỲ-ỹ⒴Ⓨⓨ㏉Yy]", Z: "[ZzŹ-žDZ-dzᶻẐ-ẕℤℨ⒵Ⓩⓩ㎐-㎔Zz]" }; return function hightlight(o) { var regex; o = _.mixin({}, defaults, o); if (!o.node || !o.pattern) { return; } o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ]; regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly, o.diacriticInsensitive); traverse(o.node, hightlightTextNode); function hightlightTextNode(textNode) { var match, patternNode, wrapperNode; if (match = regex.exec(textNode.data)) { wrapperNode = doc.createElement(o.tagName); o.className && (wrapperNode.className = o.className); patternNode = textNode.splitText(match.index); patternNode.splitText(match[0].length); wrapperNode.appendChild(patternNode.cloneNode(true)); textNode.parentNode.replaceChild(wrapperNode, patternNode); } return !!match; } function traverse(el, hightlightTextNode) { var childNode, TEXT_NODE_TYPE = 3; for (var i = 0; i < el.childNodes.length; i++) { childNode = el.childNodes[i]; if (childNode.nodeType === TEXT_NODE_TYPE) { i += hightlightTextNode(childNode) ? 1 : 0; } else { traverse(childNode, hightlightTextNode); } } } }; function accent_replacer(chr) { return accented[chr.toUpperCase()] || chr; } function getRegex(patterns, caseSensitive, wordsOnly, diacriticInsensitive) { var escapedPatterns = [], regexStr; for (var i = 0, len = patterns.length; i < len; i++) { var escapedWord = _.escapeRegExChars(patterns[i]); if (diacriticInsensitive) { escapedWord = escapedWord.replace(/\S/g, accent_replacer); } escapedPatterns.push(escapedWord); } regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")"; return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i"); } }(window.document); var Input = function() { "use strict"; var specialKeyCodeMap; specialKeyCodeMap = { 9: "tab", 27: "esc", 37: "left", 39: "right", 13: "enter", 38: "up", 40: "down" }; function Input(o, www) { var id; o = o || {}; if (!o.input) { $.error("input is missing"); } www.mixin(this); this.$hint = $(o.hint); this.$input = $(o.input); this.$menu = $(o.menu); id = this.$input.attr("id") || _.guid(); this.$menu.attr("id", id + "_listbox"); this.$hint.attr({ "aria-hidden": true }); this.$input.attr({ "aria-owns": id + "_listbox", "aria-controls": id + "_listbox", role: "combobox", "aria-autocomplete": "list", "aria-expanded": false }); this.query = this.$input.val(); this.queryWhenFocused = this.hasFocus() ? this.query : null; this.$overflowHelper = buildOverflowHelper(this.$input); this._checkLanguageDirection(); if (this.$hint.length === 0) { this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; } this.onSync("cursorchange", this._updateDescendent); } Input.normalizeQuery = function(str) { return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " "); }; _.mixin(Input.prototype, EventEmitter, { _onBlur: function onBlur() { this.resetInputValue(); this.trigger("blurred"); }, _onFocus: function onFocus() { this.queryWhenFocused = this.query; this.trigger("focused"); }, _onKeydown: function onKeydown($e) { var keyName = specialKeyCodeMap[$e.which || $e.keyCode]; this._managePreventDefault(keyName, $e); if (keyName && this._shouldTrigger(keyName, $e)) { this.trigger(keyName + "Keyed", $e); } }, _onInput: function onInput() { this._setQuery(this.getInputValue()); this.clearHintIfInvalid(); this._checkLanguageDirection(); }, _managePreventDefault: function managePreventDefault(keyName, $e) { var preventDefault; switch (keyName) { case "up": case "down": preventDefault = !withModifier($e); break; default: preventDefault = false; } preventDefault && $e.preventDefault(); }, _shouldTrigger: function shouldTrigger(keyName, $e) { var trigger; switch (keyName) { case "tab": trigger = !withModifier($e); break; default: trigger = true; } return trigger; }, _checkLanguageDirection: function checkLanguageDirection() { var dir = (this.$input.css("direction") || "ltr").toLowerCase(); if (this.dir !== dir) { this.dir = dir; this.$hint.attr("dir", dir); this.trigger("langDirChanged", dir); } }, _setQuery: function setQuery(val, silent) { var areEquivalent, hasDifferentWhitespace; areEquivalent = areQueriesEquivalent(val, this.query); hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false; this.query = val; if (!silent && !areEquivalent) { this.trigger("queryChanged", this.query); } else if (!silent && hasDifferentWhitespace) { this.trigger("whitespaceChanged", this.query); } }, _updateDescendent: function updateDescendent(event, id) { this.$input.attr("aria-activedescendant", id); }, bind: function() { var that = this, onBlur, onFocus, onKeydown, onInput; onBlur = _.bind(this._onBlur, this); onFocus = _.bind(this._onFocus, this); onKeydown = _.bind(this._onKeydown, this); onInput = _.bind(this._onInput, this); this.$input.on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown); if (!_.isMsie() || _.isMsie() > 9) { this.$input.on("input.tt", onInput); } else { this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) { if (specialKeyCodeMap[$e.which || $e.keyCode]) { return; } _.defer(_.bind(that._onInput, that, $e)); }); } return this; }, focus: function focus() { this.$input.focus(); }, blur: function blur() { this.$input.blur(); }, getLangDir: function getLangDir() { return this.dir; }, getQuery: function getQuery() { return this.query || ""; }, setQuery: function setQuery(val, silent) { this.setInputValue(val); this._setQuery(val, silent); }, hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() { return this.query !== this.queryWhenFocused; }, getInputValue: function getInputValue() { return this.$input.val(); }, setInputValue: function setInputValue(value) { this.$input.val(value); this.clearHintIfInvalid(); this._checkLanguageDirection(); }, resetInputValue: function resetInputValue() { this.setInputValue(this.query); }, getHint: function getHint() { return this.$hint.val(); }, setHint: function setHint(value) { this.$hint.val(value); }, clearHint: function clearHint() { this.setHint(""); }, clearHintIfInvalid: function clearHintIfInvalid() { var val, hint, valIsPrefixOfHint, isValid; val = this.getInputValue(); hint = this.getHint(); valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0; isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow(); !isValid && this.clearHint(); }, hasFocus: function hasFocus() { return this.$input.is(":focus"); }, hasOverflow: function hasOverflow() { var constraint = this.$input.width() - 2; this.$overflowHelper.text(this.getInputValue()); return this.$overflowHelper.width() >= constraint; }, isCursorAtEnd: function() { var valueLength, selectionStart, range; valueLength = this.$input.val().length; selectionStart = this.$input[0].selectionStart; if (_.isNumber(selectionStart)) { return selectionStart === valueLength; } else if (document.selection) { range = document.selection.createRange(); range.moveStart("character", -valueLength); return valueLength === range.text.length; } return true; }, destroy: function destroy() { this.$hint.off(".tt"); this.$input.off(".tt"); this.$overflowHelper.remove(); this.$hint = this.$input = this.$overflowHelper = $("
"); }, setAriaExpanded: function setAriaExpanded(value) { this.$input.attr("aria-expanded", value); } }); return Input; function buildOverflowHelper($input) { return $('').css({ position: "absolute", visibility: "hidden", whiteSpace: "pre", fontFamily: $input.css("font-family"), fontSize: $input.css("font-size"), fontStyle: $input.css("font-style"), fontVariant: $input.css("font-variant"), fontWeight: $input.css("font-weight"), wordSpacing: $input.css("word-spacing"), letterSpacing: $input.css("letter-spacing"), textIndent: $input.css("text-indent"), textRendering: $input.css("text-rendering"), textTransform: $input.css("text-transform") }).insertAfter($input); } function areQueriesEquivalent(a, b) { return Input.normalizeQuery(a) === Input.normalizeQuery(b); } function withModifier($e) { return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey; } }(); var Dataset = function() { "use strict"; var keys, nameGenerator; keys = { dataset: "tt-selectable-dataset", val: "tt-selectable-display", obj: "tt-selectable-object" }; nameGenerator = _.getIdGenerator(); function Dataset(o, www) { o = o || {}; o.templates = o.templates || {}; o.templates.notFound = o.templates.notFound || o.templates.empty; if (!o.source) { $.error("missing source"); } if (!o.node) { $.error("missing node"); } if (o.name && !isValidName(o.name)) { $.error("invalid dataset name: " + o.name); } www.mixin(this); this.highlight = !!o.highlight; this.name = _.toStr(o.name || nameGenerator()); this.limit = o.limit || 5; this.displayFn = getDisplayFn(o.display || o.displayKey); this.templates = getTemplates(o.templates, this.displayFn); this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source; this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async; this._resetLastSuggestion(); this.$el = $(o.node).attr("role", "presentation").addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name); } Dataset.extractData = function extractData(el) { var $el = $(el); if ($el.data(keys.obj)) { return { dataset: $el.data(keys.dataset) || "", val: $el.data(keys.val) || "", obj: $el.data(keys.obj) || null }; } return null; }; _.mixin(Dataset.prototype, EventEmitter, { _overwrite: function overwrite(query, suggestions) { suggestions = suggestions || []; if (suggestions.length) { this._renderSuggestions(query, suggestions); } else if (this.async && this.templates.pending) { this._renderPending(query); } else if (!this.async && this.templates.notFound) { this._renderNotFound(query); } else { this._empty(); } this.trigger("rendered", suggestions, false, this.name); }, _append: function append(query, suggestions) { suggestions = suggestions || []; if (suggestions.length && this.$lastSuggestion.length) { this._appendSuggestions(query, suggestions); } else if (suggestions.length) { this._renderSuggestions(query, suggestions); } else if (!this.$lastSuggestion.length && this.templates.notFound) { this._renderNotFound(query); } this.trigger("rendered", suggestions, true, this.name); }, _renderSuggestions: function renderSuggestions(query, suggestions) { var $fragment; $fragment = this._getSuggestionsFragment(query, suggestions); this.$lastSuggestion = $fragment.children().last(); this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions)); }, _appendSuggestions: function appendSuggestions(query, suggestions) { var $fragment, $lastSuggestion; $fragment = this._getSuggestionsFragment(query, suggestions); $lastSuggestion = $fragment.children().last(); this.$lastSuggestion.after($fragment); this.$lastSuggestion = $lastSuggestion; }, _renderPending: function renderPending(query) { var template = this.templates.pending; this._resetLastSuggestion(); template && this.$el.html(template({ query: query, dataset: this.name })); }, _renderNotFound: function renderNotFound(query) { var template = this.templates.notFound; this._resetLastSuggestion(); template && this.$el.html(template({ query: query, dataset: this.name })); }, _empty: function empty() { this.$el.empty(); this._resetLastSuggestion(); }, _getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) { var that = this, fragment; fragment = document.createDocumentFragment(); _.each(suggestions, function getSuggestionNode(suggestion) { var $el, context; context = that._injectQuery(query, suggestion); $el = $(that.templates.suggestion(context)).data(keys.dataset, that.name).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable); fragment.appendChild($el[0]); }); this.highlight && highlight({ className: this.classes.highlight, node: fragment, pattern: query }); return $(fragment); }, _getFooter: function getFooter(query, suggestions) { return this.templates.footer ? this.templates.footer({ query: query, suggestions: suggestions, dataset: this.name }) : null; }, _getHeader: function getHeader(query, suggestions) { return this.templates.header ? this.templates.header({ query: query, suggestions: suggestions, dataset: this.name }) : null; }, _resetLastSuggestion: function resetLastSuggestion() { this.$lastSuggestion = $(); }, _injectQuery: function injectQuery(query, obj) { return _.isObject(obj) ? _.mixin({ _query: query }, obj) : obj; }, update: function update(query) { var that = this, canceled = false, syncCalled = false, rendered = 0; this.cancel(); this.cancel = function cancel() { canceled = true; that.cancel = $.noop; that.async && that.trigger("asyncCanceled", query, that.name); }; this.source(query, sync, async); !syncCalled && sync([]); function sync(suggestions) { if (syncCalled) { return; } syncCalled = true; suggestions = (suggestions || []).slice(0, that.limit); rendered = suggestions.length; that._overwrite(query, suggestions); if (rendered < that.limit && that.async) { that.trigger("asyncRequested", query, that.name); } } function async(suggestions) { suggestions = suggestions || []; if (!canceled && rendered < that.limit) { that.cancel = $.noop; var idx = Math.abs(rendered - that.limit); rendered += idx; that._append(query, suggestions.slice(0, idx)); that.async && that.trigger("asyncReceived", query, that.name); } } }, cancel: $.noop, clear: function clear() { this._empty(); this.cancel(); this.trigger("cleared"); }, isEmpty: function isEmpty() { return this.$el.is(":empty"); }, destroy: function destroy() { this.$el = $("
"); } }); return Dataset; function getDisplayFn(display) { display = display || _.stringify; return _.isFunction(display) ? display : displayFn; function displayFn(obj) { return obj[display]; } } function getTemplates(templates, displayFn) { return { notFound: templates.notFound && _.templatify(templates.notFound), pending: templates.pending && _.templatify(templates.pending), header: templates.header && _.templatify(templates.header), footer: templates.footer && _.templatify(templates.footer), suggestion: templates.suggestion ? userSuggestionTemplate : suggestionTemplate }; function userSuggestionTemplate(context) { var template = templates.suggestion; return $(template(context)).attr("id", _.guid()); } function suggestionTemplate(context) { return $('
').attr("id", _.guid()).text(displayFn(context)); } } function isValidName(str) { return /^[_a-zA-Z0-9-]+$/.test(str); } }(); var Menu = function() { "use strict"; function Menu(o, www) { var that = this; o = o || {}; if (!o.node) { $.error("node is required"); } www.mixin(this); this.$node = $(o.node); this.query = null; this.datasets = _.map(o.datasets, initializeDataset); function initializeDataset(oDataset) { var node = that.$node.find(oDataset.node).first(); oDataset.node = node.length ? node : $("
").appendTo(that.$node); return new Dataset(oDataset, www); } } _.mixin(Menu.prototype, EventEmitter, { _onSelectableClick: function onSelectableClick($e) { this.trigger("selectableClicked", $($e.currentTarget)); }, _onRendered: function onRendered(type, dataset, suggestions, async) { this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); this.trigger("datasetRendered", dataset, suggestions, async); }, _onCleared: function onCleared() { this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); this.trigger("datasetCleared"); }, _propagate: function propagate() { this.trigger.apply(this, arguments); }, _allDatasetsEmpty: function allDatasetsEmpty() { return _.every(this.datasets, _.bind(function isDatasetEmpty(dataset) { var isEmpty = dataset.isEmpty(); this.$node.attr("aria-expanded", !isEmpty); return isEmpty; }, this)); }, _getSelectables: function getSelectables() { return this.$node.find(this.selectors.selectable); }, _removeCursor: function _removeCursor() { var $selectable = this.getActiveSelectable(); $selectable && $selectable.removeClass(this.classes.cursor); }, _ensureVisible: function ensureVisible($el) { var elTop, elBottom, nodeScrollTop, nodeHeight; elTop = $el.position().top; elBottom = elTop + $el.outerHeight(true); nodeScrollTop = this.$node.scrollTop(); nodeHeight = this.$node.height() + parseInt(this.$node.css("paddingTop"), 10) + parseInt(this.$node.css("paddingBottom"), 10); if (elTop < 0) { this.$node.scrollTop(nodeScrollTop + elTop); } else if (nodeHeight < elBottom) { this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight)); } }, bind: function() { var that = this, onSelectableClick; onSelectableClick = _.bind(this._onSelectableClick, this); this.$node.on("click.tt", this.selectors.selectable, onSelectableClick); this.$node.on("mouseover", this.selectors.selectable, function() { that.setCursor($(this)); }); this.$node.on("mouseleave", function() { that._removeCursor(); }); _.each(this.datasets, function(dataset) { dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that); }); return this; }, isOpen: function isOpen() { return this.$node.hasClass(this.classes.open); }, open: function open() { this.$node.scrollTop(0); this.$node.addClass(this.classes.open); }, close: function close() { this.$node.attr("aria-expanded", false); this.$node.removeClass(this.classes.open); this._removeCursor(); }, setLanguageDirection: function setLanguageDirection(dir) { this.$node.attr("dir", dir); }, selectableRelativeToCursor: function selectableRelativeToCursor(delta) { var $selectables, $oldCursor, oldIndex, newIndex; $oldCursor = this.getActiveSelectable(); $selectables = this._getSelectables(); oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1; newIndex = oldIndex + delta; newIndex = (newIndex + 1) % ($selectables.length + 1) - 1; newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex; return newIndex === -1 ? null : $selectables.eq(newIndex); }, setCursor: function setCursor($selectable) { this._removeCursor(); if ($selectable = $selectable && $selectable.first()) { $selectable.addClass(this.classes.cursor); this._ensureVisible($selectable); } }, getSelectableData: function getSelectableData($el) { return $el && $el.length ? Dataset.extractData($el) : null; }, getActiveSelectable: function getActiveSelectable() { var $selectable = this._getSelectables().filter(this.selectors.cursor).first(); return $selectable.length ? $selectable : null; }, getTopSelectable: function getTopSelectable() { var $selectable = this._getSelectables().first(); return $selectable.length ? $selectable : null; }, update: function update(query) { var isValidUpdate = query !== this.query; if (isValidUpdate) { this.query = query; _.each(this.datasets, updateDataset); } return isValidUpdate; function updateDataset(dataset) { dataset.update(query); } }, empty: function empty() { _.each(this.datasets, clearDataset); this.query = null; this.$node.addClass(this.classes.empty); function clearDataset(dataset) { dataset.clear(); } }, destroy: function destroy() { this.$node.off(".tt"); this.$node = $("
"); _.each(this.datasets, destroyDataset); function destroyDataset(dataset) { dataset.destroy(); } } }); return Menu; }(); var Status = function() { "use strict"; function Status(options) { this.$el = $("", { role: "status", "aria-live": "polite" }).css({ position: "absolute", padding: "0", border: "0", height: "1px", width: "1px", "margin-bottom": "-1px", "margin-right": "-1px", overflow: "hidden", clip: "rect(0 0 0 0)", "white-space": "nowrap" }); options.$input.after(this.$el); _.each(options.menu.datasets, _.bind(function(dataset) { if (dataset.onSync) { dataset.onSync("rendered", _.bind(this.update, this)); dataset.onSync("cleared", _.bind(this.cleared, this)); } }, this)); } _.mixin(Status.prototype, { update: function update(event, suggestions) { var length = suggestions.length; var words; if (length === 1) { words = { result: "result", is: "is" }; } else { words = { result: "results", is: "are" }; } this.$el.text(length + " " + words.result + " " + words.is + " available, use up and down arrow keys to navigate."); }, cleared: function() { this.$el.text(""); } }); return Status; }(); var DefaultMenu = function() { "use strict"; var s = Menu.prototype; function DefaultMenu() { Menu.apply(this, [].slice.call(arguments, 0)); } _.mixin(DefaultMenu.prototype, Menu.prototype, { open: function open() { !this._allDatasetsEmpty() && this._show(); return s.open.apply(this, [].slice.call(arguments, 0)); }, close: function close() { this._hide(); return s.close.apply(this, [].slice.call(arguments, 0)); }, _onRendered: function onRendered() { if (this._allDatasetsEmpty()) { this._hide(); } else { this.isOpen() && this._show(); } return s._onRendered.apply(this, [].slice.call(arguments, 0)); }, _onCleared: function onCleared() { if (this._allDatasetsEmpty()) { this._hide(); } else { this.isOpen() && this._show(); } return s._onCleared.apply(this, [].slice.call(arguments, 0)); }, setLanguageDirection: function setLanguageDirection(dir) { this.$node.css(dir === "ltr" ? this.css.ltr : this.css.rtl); return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0)); }, _hide: function hide() { this.$node.hide(); }, _show: function show() { this.$node.css("display", "block"); } }); return DefaultMenu; }(); var Typeahead = function() { "use strict"; function Typeahead(o, www) { var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed, onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged, onWhitespaceChanged; o = o || {}; if (!o.input) { $.error("missing input"); } if (!o.menu) { $.error("missing menu"); } if (!o.eventBus) { $.error("missing event bus"); } www.mixin(this); this.eventBus = o.eventBus; this.minLength = _.isNumber(o.minLength) ? o.minLength : 1; this.input = o.input; this.menu = o.menu; this.enabled = true; this.autoselect = !!o.autoselect; this.active = false; this.input.hasFocus() && this.activate(); this.dir = this.input.getLangDir(); this._hacks(); this.menu.bind().onSync("selectableClicked", this._onSelectableClicked, this).onSync("asyncRequested", this._onAsyncRequested, this).onSync("asyncCanceled", this._onAsyncCanceled, this).onSync("asyncReceived", this._onAsyncReceived, this).onSync("datasetRendered", this._onDatasetRendered, this).onSync("datasetCleared", this._onDatasetCleared, this); onFocused = c(this, "activate", "open", "_onFocused"); onBlurred = c(this, "deactivate", "_onBlurred"); onEnterKeyed = c(this, "isActive", "isOpen", "_onEnterKeyed"); onTabKeyed = c(this, "isActive", "isOpen", "_onTabKeyed"); onEscKeyed = c(this, "isActive", "_onEscKeyed"); onUpKeyed = c(this, "isActive", "open", "_onUpKeyed"); onDownKeyed = c(this, "isActive", "open", "_onDownKeyed"); onLeftKeyed = c(this, "isActive", "isOpen", "_onLeftKeyed"); onRightKeyed = c(this, "isActive", "isOpen", "_onRightKeyed"); onQueryChanged = c(this, "_openIfActive", "_onQueryChanged"); onWhitespaceChanged = c(this, "_openIfActive", "_onWhitespaceChanged"); this.input.bind().onSync("focused", onFocused, this).onSync("blurred", onBlurred, this).onSync("enterKeyed", onEnterKeyed, this).onSync("tabKeyed", onTabKeyed, this).onSync("escKeyed", onEscKeyed, this).onSync("upKeyed", onUpKeyed, this).onSync("downKeyed", onDownKeyed, this).onSync("leftKeyed", onLeftKeyed, this).onSync("rightKeyed", onRightKeyed, this).onSync("queryChanged", onQueryChanged, this).onSync("whitespaceChanged", onWhitespaceChanged, this).onSync("langDirChanged", this._onLangDirChanged, this); } _.mixin(Typeahead.prototype, { _hacks: function hacks() { var $input, $menu; $input = this.input.$input || $("
"); $menu = this.menu.$node || $("
"); $input.on("blur.tt", function($e) { var active, isActive, hasActive; active = document.activeElement; isActive = $menu.is(active); hasActive = $menu.has(active).length > 0; if (_.isMsie() && (isActive || hasActive)) { $e.preventDefault(); $e.stopImmediatePropagation(); _.defer(function() { $input.focus(); }); } }); $menu.on("mousedown.tt", function($e) { $e.preventDefault(); }); }, _onSelectableClicked: function onSelectableClicked(type, $el) { this.select($el); }, _onDatasetCleared: function onDatasetCleared() { this._updateHint(); }, _onDatasetRendered: function onDatasetRendered(type, suggestions, async, dataset) { this._updateHint(); if (this.autoselect) { var cursorClass = this.selectors.cursor.substr(1); this.menu.$node.find(this.selectors.suggestion).first().addClass(cursorClass); } this.eventBus.trigger("render", suggestions, async, dataset); }, _onAsyncRequested: function onAsyncRequested(type, dataset, query) { this.eventBus.trigger("asyncrequest", query, dataset); }, _onAsyncCanceled: function onAsyncCanceled(type, dataset, query) { this.eventBus.trigger("asynccancel", query, dataset); }, _onAsyncReceived: function onAsyncReceived(type, dataset, query) { this.eventBus.trigger("asyncreceive", query, dataset); }, _onFocused: function onFocused() { this._minLengthMet() && this.menu.update(this.input.getQuery()); }, _onBlurred: function onBlurred() { if (this.input.hasQueryChangedSinceLastFocus()) { this.eventBus.trigger("change", this.input.getQuery()); } }, _onEnterKeyed: function onEnterKeyed(type, $e) { var $selectable; if ($selectable = this.menu.getActiveSelectable()) { if (this.select($selectable)) { $e.preventDefault(); $e.stopPropagation(); } } else if (this.autoselect) { if (this.select(this.menu.getTopSelectable())) { $e.preventDefault(); $e.stopPropagation(); } } }, _onTabKeyed: function onTabKeyed(type, $e) { var $selectable; if ($selectable = this.menu.getActiveSelectable()) { this.select($selectable) && $e.preventDefault(); } else if (this.autoselect) { if ($selectable = this.menu.getTopSelectable()) { this.autocomplete($selectable) && $e.preventDefault(); } } }, _onEscKeyed: function onEscKeyed() { this.close(); }, _onUpKeyed: function onUpKeyed() { this.moveCursor(-1); }, _onDownKeyed: function onDownKeyed() { this.moveCursor(+1); }, _onLeftKeyed: function onLeftKeyed() { if (this.dir === "rtl" && this.input.isCursorAtEnd()) { this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); } }, _onRightKeyed: function onRightKeyed() { if (this.dir === "ltr" && this.input.isCursorAtEnd()) { this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); } }, _onQueryChanged: function onQueryChanged(e, query) { this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty(); }, _onWhitespaceChanged: function onWhitespaceChanged() { this._updateHint(); }, _onLangDirChanged: function onLangDirChanged(e, dir) { if (this.dir !== dir) { this.dir = dir; this.menu.setLanguageDirection(dir); } }, _openIfActive: function openIfActive() { this.isActive() && this.open(); }, _minLengthMet: function minLengthMet(query) { query = _.isString(query) ? query : this.input.getQuery() || ""; return query.length >= this.minLength; }, _updateHint: function updateHint() { var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match; $selectable = this.menu.getTopSelectable(); data = this.menu.getSelectableData($selectable); val = this.input.getInputValue(); if (data && !_.isBlankString(val) && !this.input.hasOverflow()) { query = Input.normalizeQuery(val); escapedQuery = _.escapeRegExChars(query); frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i"); match = frontMatchRegEx.exec(data.val); match && this.input.setHint(val + match[1]); } else { this.input.clearHint(); } }, isEnabled: function isEnabled() { return this.enabled; }, enable: function enable() { this.enabled = true; }, disable: function disable() { this.enabled = false; }, isActive: function isActive() { return this.active; }, activate: function activate() { if (this.isActive()) { return true; } else if (!this.isEnabled() || this.eventBus.before("active")) { return false; } else { this.active = true; this.eventBus.trigger("active"); return true; } }, deactivate: function deactivate() { if (!this.isActive()) { return true; } else if (this.eventBus.before("idle")) { return false; } else { this.active = false; this.close(); this.eventBus.trigger("idle"); return true; } }, isOpen: function isOpen() { return this.menu.isOpen(); }, open: function open() { if (!this.isOpen() && !this.eventBus.before("open")) { this.input.setAriaExpanded(true); this.menu.open(); this._updateHint(); this.eventBus.trigger("open"); } return this.isOpen(); }, close: function close() { if (this.isOpen() && !this.eventBus.before("close")) { this.input.setAriaExpanded(false); this.menu.close(); this.input.clearHint(); this.input.resetInputValue(); this.eventBus.trigger("close"); } return !this.isOpen(); }, setVal: function setVal(val) { this.input.setQuery(_.toStr(val)); }, getVal: function getVal() { return this.input.getQuery(); }, select: function select($selectable) { var data = this.menu.getSelectableData($selectable); if (data && !this.eventBus.before("select", data.obj, data.dataset)) { this.input.setQuery(data.val, true); this.eventBus.trigger("select", data.obj, data.dataset); this.close(); return true; } return false; }, autocomplete: function autocomplete($selectable) { var query, data, isValid; query = this.input.getQuery(); data = this.menu.getSelectableData($selectable); isValid = data && query !== data.val; if (isValid && !this.eventBus.before("autocomplete", data.obj, data.dataset)) { this.input.setQuery(data.val); this.eventBus.trigger("autocomplete", data.obj, data.dataset); return true; } return false; }, moveCursor: function moveCursor(delta) { var query, $candidate, data, suggestion, datasetName, cancelMove, id; query = this.input.getQuery(); $candidate = this.menu.selectableRelativeToCursor(delta); data = this.menu.getSelectableData($candidate); suggestion = data ? data.obj : null; datasetName = data ? data.dataset : null; id = $candidate ? $candidate.attr("id") : null; this.input.trigger("cursorchange", id); cancelMove = this._minLengthMet() && this.menu.update(query); if (!cancelMove && !this.eventBus.before("cursorchange", suggestion, datasetName)) { this.menu.setCursor($candidate); if (data) { if (typeof data.val === "string") { this.input.setInputValue(data.val); } } else { this.input.resetInputValue(); this._updateHint(); } this.eventBus.trigger("cursorchange", suggestion, datasetName); return true; } return false; }, destroy: function destroy() { this.input.destroy(); this.menu.destroy(); } }); return Typeahead; function c(ctx) { var methods = [].slice.call(arguments, 1); return function() { var args = [].slice.call(arguments); _.each(methods, function(method) { return ctx[method].apply(ctx, args); }); }; } }(); (function() { "use strict"; var old, keys, methods; old = $.fn.typeahead; keys = { www: "tt-www", attrs: "tt-attrs", typeahead: "tt-typeahead" }; methods = { initialize: function initialize(o, datasets) { var www; datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1); o = o || {}; www = WWW(o.classNames); return this.each(attach); function attach() { var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, status, typeahead, MenuConstructor; _.each(datasets, function(d) { d.highlight = !!o.highlight; }); $input = $(this); $wrapper = $(www.html.wrapper); $hint = $elOrNull(o.hint); $menu = $elOrNull(o.menu); defaultHint = o.hint !== false && !$hint; defaultMenu = o.menu !== false && !$menu; defaultHint && ($hint = buildHintFromInput($input, www)); defaultMenu && ($menu = $(www.html.menu).css(www.css.menu)); $hint && $hint.val(""); $input = prepInput($input, www); if (defaultHint || defaultMenu) { $wrapper.css(www.css.wrapper); $input.css(defaultHint ? www.css.input : www.css.inputWithNoHint); $input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null); } MenuConstructor = defaultMenu ? DefaultMenu : Menu; eventBus = new EventBus({ el: $input }); input = new Input({ hint: $hint, input: $input, menu: $menu }, www); menu = new MenuConstructor({ node: $menu, datasets: datasets }, www); status = new Status({ $input: $input, menu: menu }); typeahead = new Typeahead({ input: input, menu: menu, eventBus: eventBus, minLength: o.minLength, autoselect: o.autoselect }, www); $input.data(keys.www, www); $input.data(keys.typeahead, typeahead); } }, isEnabled: function isEnabled() { var enabled; ttEach(this.first(), function(t) { enabled = t.isEnabled(); }); return enabled; }, enable: function enable() { ttEach(this, function(t) { t.enable(); }); return this; }, disable: function disable() { ttEach(this, function(t) { t.disable(); }); return this; }, isActive: function isActive() { var active; ttEach(this.first(), function(t) { active = t.isActive(); }); return active; }, activate: function activate() { ttEach(this, function(t) { t.activate(); }); return this; }, deactivate: function deactivate() { ttEach(this, function(t) { t.deactivate(); }); return this; }, isOpen: function isOpen() { var open; ttEach(this.first(), function(t) { open = t.isOpen(); }); return open; }, open: function open() { ttEach(this, function(t) { t.open(); }); return this; }, close: function close() { ttEach(this, function(t) { t.close(); }); return this; }, select: function select(el) { var success = false, $el = $(el); ttEach(this.first(), function(t) { success = t.select($el); }); return success; }, autocomplete: function autocomplete(el) { var success = false, $el = $(el); ttEach(this.first(), function(t) { success = t.autocomplete($el); }); return success; }, moveCursor: function moveCursoe(delta) { var success = false; ttEach(this.first(), function(t) { success = t.moveCursor(delta); }); return success; }, val: function val(newVal) { var query; if (!arguments.length) { ttEach(this.first(), function(t) { query = t.getVal(); }); return query; } else { ttEach(this, function(t) { t.setVal(_.toStr(newVal)); }); return this; } }, destroy: function destroy() { ttEach(this, function(typeahead, $input) { revert($input); typeahead.destroy(); }); return this; } }; $.fn.typeahead = function(method) { if (methods[method]) { return methods[method].apply(this, [].slice.call(arguments, 1)); } else { return methods.initialize.apply(this, arguments); } }; $.fn.typeahead.noConflict = function noConflict() { $.fn.typeahead = old; return this; }; function ttEach($els, fn) { $els.each(function() { var $input = $(this), typeahead; (typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input); }); } function buildHintFromInput($input, www) { return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop({ readonly: true, required: false }).removeAttr("id name placeholder").removeClass("required").attr({ spellcheck: "false", tabindex: -1 }); } function prepInput($input, www) { $input.data(keys.attrs, { dir: $input.attr("dir"), autocomplete: $input.attr("autocomplete"), spellcheck: $input.attr("spellcheck"), style: $input.attr("style") }); $input.addClass(www.classes.input).attr({ spellcheck: false }); try { !$input.attr("dir") && $input.attr("dir", "auto"); } catch (e) {} return $input; } function getBackgroundStyles($el) { return { backgroundAttachment: $el.css("background-attachment"), backgroundClip: $el.css("background-clip"), backgroundColor: $el.css("background-color"), backgroundImage: $el.css("background-image"), backgroundOrigin: $el.css("background-origin"), backgroundPosition: $el.css("background-position"), backgroundRepeat: $el.css("background-repeat"), backgroundSize: $el.css("background-size") }; } function revert($input) { var www, $wrapper; www = $input.data(keys.www); $wrapper = $input.parent().filter(www.selectors.wrapper); _.each($input.data(keys.attrs), function(val, key) { _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val); }); $input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input); if ($wrapper.length) { $input.detach().insertAfter($wrapper); $wrapper.remove(); } } function $elOrNull(obj) { var isValid, $el; isValid = _.isJQuery(obj) || _.isElement(obj); $el = isValid ? $(obj).first() : []; return $el.length ? $el : null; } })(); }); ================================================ FILE: docs/index.html ================================================ NVActivityIndicatorView Reference

NVActivityIndicatorView 5.2.0 Docs

GitHub View on GitHub

NVActivityIndicatorView

Build Status Cocoapods Compatible Carthage Compatible

⚠️ Check out LoaderUI (ready to use with Swift Package Mananger supported) for SwiftUI implementation of this. 🎉

Introduction

NVActivityIndicatorView is a collection of awesome loading animations.

Demo

Animation types

Type Type Type Type
1. ballPulse 2. ballGridPulse 3. ballClipRotate 4. squareSpin
5. ballClipRotatePulse 6. ballClipRotateMultiple 7. ballPulseRise 8. ballRotate
9. cubeTransition 10. ballZigZag 11. ballZigZagDeflect 12. ballTrianglePath
13. ballScale 14. lineScale 15. lineScaleParty 16. ballScaleMultiple
17. ballPulseSync 18. ballBeat 19. lineScalePulseOut 20. lineScalePulseOutRapid
21. ballScaleRipple 22. ballScaleRippleMultiple 23. ballSpinFadeLoader 24. lineSpinFadeLoader
25. triangleSkewSpin 26. pacman 27. ballGridBeat 28. semiCircleSpin
29. ballRotateChase 30. orbit 31. audioEqualizer 32. circleStrokeSpin

Installation

Cocoapods

Cocoapods is a dependency manager for Swift and Objective-C Cocoa projects. To use NVActivityIndicatorView with CocoaPods, add it in your Podfile.

pod 'NVActivityIndicatorView'

Carthage

Carthage is intended to be the simplest way to add frameworks to your Cocoa application. To use NVActivityIndicatorView with Carthage, add it in your Cartfile.

github "ninjaprox/NVActivityIndicatorView"

Swift Package Manager

The Swift Package Manager is a tool for managing the distribution of Swift code. To use NVActivityIndicatorView with Swift Package Manger, add it to dependencies in your Package.swift

dependencies: [
    .package(url: "https://github.com/ninjaprox/NVActivityIndicatorView.git")
]

Migration

Version 5.0.0 comes with breaking changes. Please refer to the release note for details.

Usage

Firstly, import NVActivityIndicatorView.

import NVActivityIndicatorView

Initialization

Then, there are two ways you can create NVActivityIndicatorView:

Note: Set Module to NVActivityIndicatorView.

NVActivityIndicatorView(frame: frame, type: type, color: color, padding: padding)

Control

Start animating.

activityIndicatorView.startAnimating()

Stop animating.

activityIndicatorView.stopAnimating()

Determine if it is animating.

animating = activityIndicatorView.isAnimating

Change properties

In storyboard, you can change all properties in Attributes inspector tab of Utilities panel.

Note: Use one of values (case-insensitive) in Animation types for Type Name.

All properties are public so you can change them after initializing.

Note: All changes must be made before calling startAnimating().

Documentation

https://nvactivityindicatorview.vinhis.me/

Acknowledgment

Thanks Connor Atherton for inspired Loaders.css and Danil Gontovnik for DGActivityIndicatorView.

License

The MIT License (MIT)

Copyright © 2016 Vinh Nguyen @ninjaprox

================================================ FILE: docs/js/jazzy.js ================================================ // Jazzy - https://github.com/realm/jazzy // Copyright Realm Inc. // SPDX-License-Identifier: MIT window.jazzy = {'docset': false} if (typeof window.dash != 'undefined') { document.documentElement.className += ' dash' window.jazzy.docset = true } if (navigator.userAgent.match(/xcode/i)) { document.documentElement.className += ' xcode' window.jazzy.docset = true } function toggleItem($link, $content) { var animationDuration = 300; $link.toggleClass('token-open'); $content.slideToggle(animationDuration); } function itemLinkToContent($link) { return $link.parent().parent().next(); } // On doc load + hash-change, open any targeted item function openCurrentItemIfClosed() { if (window.jazzy.docset) { return; } var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); $content = itemLinkToContent($link); if ($content.is(':hidden')) { toggleItem($link, $content); } } $(openCurrentItemIfClosed); $(window).on('hashchange', openCurrentItemIfClosed); // On item link ('token') click, toggle its discussion $('.token').on('click', function(event) { if (window.jazzy.docset) { return; } var $link = $(this); toggleItem($link, itemLinkToContent($link)); // Keeps the document from jumping to the hash. var href = $link.attr('href'); if (history.pushState) { history.pushState({}, '', href); } else { location.hash = href; } event.preventDefault(); }); // Clicks on links to the current, closed, item need to open the item $("a:not('.token')").on('click', function() { if (location == this.href) { openCurrentItemIfClosed(); } }); // KaTeX rendering if ("katex" in window) { $($('.math').each( (_, element) => { katex.render(element.textContent, element, { displayMode: $(element).hasClass('m-block'), throwOnError: false, trust: true }); })) } ================================================ FILE: docs/js/jazzy.search.js ================================================ // Jazzy - https://github.com/realm/jazzy // Copyright Realm Inc. // SPDX-License-Identifier: MIT $(function(){ var $typeahead = $('[data-typeahead]'); var $form = $typeahead.parents('form'); var searchURL = $form.attr('action'); function displayTemplate(result) { return result.name; } function suggestionTemplate(result) { var t = '
'; t += '' + result.name + ''; if (result.parent_name) { t += '' + result.parent_name + ''; } t += '
'; return t; } $typeahead.one('focus', function() { $form.addClass('loading'); $.getJSON(searchURL).then(function(searchData) { const searchIndex = lunr(function() { this.ref('url'); this.field('name'); this.field('abstract'); for (const [url, doc] of Object.entries(searchData)) { this.add({url: url, name: doc.name, abstract: doc.abstract}); } }); $typeahead.typeahead( { highlight: true, minLength: 3, autoselect: true }, { limit: 10, display: displayTemplate, templates: { suggestion: suggestionTemplate }, source: function(query, sync) { const lcSearch = query.toLowerCase(); const results = searchIndex.query(function(q) { q.term(lcSearch, { boost: 100 }); q.term(lcSearch, { boost: 10, wildcard: lunr.Query.wildcard.TRAILING }); }).map(function(result) { var doc = searchData[result.ref]; doc.url = result.ref; return doc; }); sync(results); } } ); $form.removeClass('loading'); $typeahead.trigger('focus'); }); }); var baseURL = searchURL.slice(0, -"search.json".length); $typeahead.on('typeahead:select', function(e, result) { window.location = baseURL + result.url; }); }); ================================================ FILE: docs/js/typeahead.jquery.js ================================================ /*! * typeahead.js 1.3.3 * https://github.com/corejavascript/typeahead.js * Copyright 2013-2024 Twitter, Inc. and other contributors; Licensed MIT */ (function(root, factory) { if (typeof define === "function" && define.amd) { define([ "jquery" ], function(a0) { return factory(a0); }); } else if (typeof module === "object" && module.exports) { module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } })(this, function($) { var _ = function() { "use strict"; return { isMsie: function() { return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false; }, isBlankString: function(str) { return !str || /^\s*$/.test(str); }, escapeRegExChars: function(str) { return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); }, isString: function(obj) { return typeof obj === "string"; }, isNumber: function(obj) { return typeof obj === "number"; }, isArray: $.isArray, isFunction: $.isFunction, isObject: $.isPlainObject, isUndefined: function(obj) { return typeof obj === "undefined"; }, isElement: function(obj) { return !!(obj && obj.nodeType === 1); }, isJQuery: function(obj) { return obj instanceof $; }, toStr: function toStr(s) { return _.isUndefined(s) || s === null ? "" : s + ""; }, bind: $.proxy, each: function(collection, cb) { $.each(collection, reverseArgs); function reverseArgs(index, value) { return cb(value, index); } }, map: $.map, filter: $.grep, every: function(obj, test) { var result = true; if (!obj) { return result; } $.each(obj, function(key, val) { if (!(result = test.call(null, val, key, obj))) { return false; } }); return !!result; }, some: function(obj, test) { var result = false; if (!obj) { return result; } $.each(obj, function(key, val) { if (result = test.call(null, val, key, obj)) { return false; } }); return !!result; }, mixin: $.extend, identity: function(x) { return x; }, clone: function(obj) { return $.extend(true, {}, obj); }, getIdGenerator: function() { var counter = 0; return function() { return counter++; }; }, templatify: function templatify(obj) { return $.isFunction(obj) ? obj : template; function template() { return String(obj); } }, defer: function(fn) { setTimeout(fn, 0); }, debounce: function(func, wait, immediate) { var timeout, result; return function() { var context = this, args = arguments, later, callNow; later = function() { timeout = null; if (!immediate) { result = func.apply(context, args); } }; callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) { result = func.apply(context, args); } return result; }; }, throttle: function(func, wait) { var context, args, timeout, result, previous, later; previous = 0; later = function() { previous = new Date(); timeout = null; result = func.apply(context, args); }; return function() { var now = new Date(), remaining = wait - (now - previous); context = this; args = arguments; if (remaining <= 0) { clearTimeout(timeout); timeout = null; previous = now; result = func.apply(context, args); } else if (!timeout) { timeout = setTimeout(later, remaining); } return result; }; }, stringify: function(val) { return _.isString(val) ? val : JSON.stringify(val); }, guid: function() { function _p8(s) { var p = (Math.random().toString(16) + "000000000").substr(2, 8); return s ? "-" + p.substr(0, 4) + "-" + p.substr(4, 4) : p; } return "tt-" + _p8() + _p8(true) + _p8(true) + _p8(); }, noop: function() {} }; }(); var WWW = function() { "use strict"; var defaultClassNames = { wrapper: "twitter-typeahead", input: "tt-input", hint: "tt-hint", menu: "tt-menu", dataset: "tt-dataset", suggestion: "tt-suggestion", selectable: "tt-selectable", empty: "tt-empty", open: "tt-open", cursor: "tt-cursor", highlight: "tt-highlight" }; return build; function build(o) { var www, classes; classes = _.mixin({}, defaultClassNames, o); www = { css: buildCss(), classes: classes, html: buildHtml(classes), selectors: buildSelectors(classes) }; return { css: www.css, html: www.html, classes: www.classes, selectors: www.selectors, mixin: function(o) { _.mixin(o, www); } }; } function buildHtml(c) { return { wrapper: '', menu: '
' }; } function buildSelectors(classes) { var selectors = {}; _.each(classes, function(v, k) { selectors[k] = "." + v; }); return selectors; } function buildCss() { var css = { wrapper: { position: "relative", display: "inline-block" }, hint: { position: "absolute", top: "0", left: "0", borderColor: "transparent", boxShadow: "none", opacity: "1" }, input: { position: "relative", verticalAlign: "top", backgroundColor: "transparent" }, inputWithNoHint: { position: "relative", verticalAlign: "top" }, menu: { position: "absolute", top: "100%", left: "0", zIndex: "100", display: "none" }, ltr: { left: "0", right: "auto" }, rtl: { left: "auto", right: " 0" } }; if (_.isMsie()) { _.mixin(css.input, { backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)" }); } return css; } }(); var EventBus = function() { "use strict"; var namespace, deprecationMap; namespace = "typeahead:"; deprecationMap = { render: "rendered", cursorchange: "cursorchanged", select: "selected", autocomplete: "autocompleted" }; function EventBus(o) { if (!o || !o.el) { $.error("EventBus initialized without el"); } this.$el = $(o.el); } _.mixin(EventBus.prototype, { _trigger: function(type, args) { var $e = $.Event(namespace + type); this.$el.trigger.call(this.$el, $e, args || []); return $e; }, before: function(type) { var args, $e; args = [].slice.call(arguments, 1); $e = this._trigger("before" + type, args); return $e.isDefaultPrevented(); }, trigger: function(type) { var deprecatedType; this._trigger(type, [].slice.call(arguments, 1)); if (deprecatedType = deprecationMap[type]) { this._trigger(deprecatedType, [].slice.call(arguments, 1)); } } }); return EventBus; }(); var EventEmitter = function() { "use strict"; var splitter = /\s+/, nextTick = getNextTick(); return { onSync: onSync, onAsync: onAsync, off: off, trigger: trigger }; function on(method, types, cb, context) { var type; if (!cb) { return this; } types = types.split(splitter); cb = context ? bindContext(cb, context) : cb; this._callbacks = this._callbacks || {}; while (type = types.shift()) { this._callbacks[type] = this._callbacks[type] || { sync: [], async: [] }; this._callbacks[type][method].push(cb); } return this; } function onAsync(types, cb, context) { return on.call(this, "async", types, cb, context); } function onSync(types, cb, context) { return on.call(this, "sync", types, cb, context); } function off(types) { var type; if (!this._callbacks) { return this; } types = types.split(splitter); while (type = types.shift()) { delete this._callbacks[type]; } return this; } function trigger(types) { var type, callbacks, args, syncFlush, asyncFlush; if (!this._callbacks) { return this; } types = types.split(splitter); args = [].slice.call(arguments, 1); while ((type = types.shift()) && (callbacks = this._callbacks[type])) { syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args)); asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args)); syncFlush() && nextTick(asyncFlush); } return this; } function getFlush(callbacks, context, args) { return flush; function flush() { var cancelled; for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) { cancelled = callbacks[i].apply(context, args) === false; } return !cancelled; } } function getNextTick() { var nextTickFn; if (window.setImmediate) { nextTickFn = function nextTickSetImmediate(fn) { setImmediate(function() { fn(); }); }; } else { nextTickFn = function nextTickSetTimeout(fn) { setTimeout(function() { fn(); }, 0); }; } return nextTickFn; } function bindContext(fn, context) { return fn.bind ? fn.bind(context) : function() { fn.apply(context, [].slice.call(arguments, 0)); }; } }(); var highlight = function(doc) { "use strict"; var defaults = { node: null, pattern: null, tagName: "strong", className: null, wordsOnly: false, caseSensitive: false, diacriticInsensitive: false }; var accented = { A: "[AaªÀ-Åà-åĀ-ąǍǎȀ-ȃȦȧᴬᵃḀḁẚẠ-ảₐ℀℁℻⒜Ⓐⓐ㍱-㍴㎀-㎄㎈㎉㎩-㎯㏂㏊㏟㏿Aa]", B: "[BbᴮᵇḂ-ḇℬ⒝Ⓑⓑ㍴㎅-㎇㏃㏈㏔㏝Bb]", C: "[CcÇçĆ-čᶜ℀ℂ℃℅℆ℭⅭⅽ⒞Ⓒⓒ㍶㎈㎉㎝㎠㎤㏄-㏇Cc]", D: "[DdĎďDŽ-džDZ-dzᴰᵈḊ-ḓⅅⅆⅮⅾ⒟Ⓓⓓ㋏㍲㍷-㍹㎗㎭-㎯㏅㏈Dd]", E: "[EeÈ-Ëè-ëĒ-ěȄ-ȇȨȩᴱᵉḘ-ḛẸ-ẽₑ℡ℯℰⅇ⒠Ⓔⓔ㉐㋍㋎Ee]", F: "[FfᶠḞḟ℉ℱ℻⒡Ⓕⓕ㎊-㎌㎙ff-fflFf]", G: "[GgĜ-ģǦǧǴǵᴳᵍḠḡℊ⒢Ⓖⓖ㋌㋍㎇㎍-㎏㎓㎬㏆㏉㏒㏿Gg]", H: "[HhĤĥȞȟʰᴴḢ-ḫẖℋ-ℎ⒣Ⓗⓗ㋌㍱㎐-㎔㏊㏋㏗Hh]", I: "[IiÌ-Ïì-ïĨ-İIJijǏǐȈ-ȋᴵᵢḬḭỈ-ịⁱℐℑℹⅈⅠ-ⅣⅥ-ⅨⅪⅫⅰ-ⅳⅵ-ⅸⅺⅻ⒤Ⓘⓘ㍺㏌㏕fiffiIi]", J: "[JjIJ-ĵLJ-njǰʲᴶⅉ⒥ⒿⓙⱼJj]", K: "[KkĶķǨǩᴷᵏḰ-ḵK⒦Ⓚⓚ㎄㎅㎉㎏㎑㎘㎞㎢㎦㎪㎸㎾㏀㏆㏍-㏏Kk]", L: "[LlĹ-ŀLJ-ljˡᴸḶḷḺ-ḽℒℓ℡Ⅼⅼ⒧Ⓛⓛ㋏㎈㎉㏐-㏓㏕㏖㏿flfflLl]", M: "[MmᴹᵐḾ-ṃ℠™ℳⅯⅿ⒨Ⓜⓜ㍷-㍹㎃㎆㎎㎒㎖㎙-㎨㎫㎳㎷㎹㎽㎿㏁㏂㏎㏐㏔-㏖㏘㏙㏞㏟Mm]", N: "[NnÑñŃ-ʼnNJ-njǸǹᴺṄ-ṋⁿℕ№⒩Ⓝⓝ㎁㎋㎚㎱㎵㎻㏌㏑Nn]", O: "[OoºÒ-Öò-öŌ-őƠơǑǒǪǫȌ-ȏȮȯᴼᵒỌ-ỏₒ℅№ℴ⒪Ⓞⓞ㍵㏇㏒㏖Oo]", P: "[PpᴾᵖṔ-ṗℙ⒫Ⓟⓟ㉐㍱㍶㎀㎊㎩-㎬㎰㎴㎺㏋㏗-㏚Pp]", Q: "[Qqℚ⒬Ⓠⓠ㏃Qq]", R: "[RrŔ-řȐ-ȓʳᴿᵣṘ-ṛṞṟ₨ℛ-ℝ⒭Ⓡⓡ㋍㍴㎭-㎯㏚㏛Rr]", S: "[SsŚ-šſȘșˢṠ-ṣ₨℁℠⒮Ⓢⓢ㎧㎨㎮-㎳㏛㏜stSs]", T: "[TtŢ-ťȚțᵀᵗṪ-ṱẗ℡™⒯Ⓣⓣ㉐㋏㎔㏏ſtstTt]", U: "[UuÙ-Üù-üŨ-ųƯưǓǔȔ-ȗᵁᵘᵤṲ-ṷỤ-ủ℆⒰Ⓤⓤ㍳㍺Uu]", V: "[VvᵛᵥṼ-ṿⅣ-Ⅷⅳ-ⅷ⒱Ⓥⓥⱽ㋎㍵㎴-㎹㏜㏞Vv]", W: "[WwŴŵʷᵂẀ-ẉẘ⒲Ⓦⓦ㎺-㎿㏝Ww]", X: "[XxˣẊ-ẍₓ℻Ⅸ-Ⅻⅸ-ⅻ⒳Ⓧⓧ㏓Xx]", Y: "[YyÝýÿŶ-ŸȲȳʸẎẏẙỲ-ỹ⒴Ⓨⓨ㏉Yy]", Z: "[ZzŹ-žDZ-dzᶻẐ-ẕℤℨ⒵Ⓩⓩ㎐-㎔Zz]" }; return function hightlight(o) { var regex; o = _.mixin({}, defaults, o); if (!o.node || !o.pattern) { return; } o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ]; regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly, o.diacriticInsensitive); traverse(o.node, hightlightTextNode); function hightlightTextNode(textNode) { var match, patternNode, wrapperNode; if (match = regex.exec(textNode.data)) { wrapperNode = doc.createElement(o.tagName); o.className && (wrapperNode.className = o.className); patternNode = textNode.splitText(match.index); patternNode.splitText(match[0].length); wrapperNode.appendChild(patternNode.cloneNode(true)); textNode.parentNode.replaceChild(wrapperNode, patternNode); } return !!match; } function traverse(el, hightlightTextNode) { var childNode, TEXT_NODE_TYPE = 3; for (var i = 0; i < el.childNodes.length; i++) { childNode = el.childNodes[i]; if (childNode.nodeType === TEXT_NODE_TYPE) { i += hightlightTextNode(childNode) ? 1 : 0; } else { traverse(childNode, hightlightTextNode); } } } }; function accent_replacer(chr) { return accented[chr.toUpperCase()] || chr; } function getRegex(patterns, caseSensitive, wordsOnly, diacriticInsensitive) { var escapedPatterns = [], regexStr; for (var i = 0, len = patterns.length; i < len; i++) { var escapedWord = _.escapeRegExChars(patterns[i]); if (diacriticInsensitive) { escapedWord = escapedWord.replace(/\S/g, accent_replacer); } escapedPatterns.push(escapedWord); } regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")"; return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i"); } }(window.document); var Input = function() { "use strict"; var specialKeyCodeMap; specialKeyCodeMap = { 9: "tab", 27: "esc", 37: "left", 39: "right", 13: "enter", 38: "up", 40: "down" }; function Input(o, www) { var id; o = o || {}; if (!o.input) { $.error("input is missing"); } www.mixin(this); this.$hint = $(o.hint); this.$input = $(o.input); this.$menu = $(o.menu); id = this.$input.attr("id") || _.guid(); this.$menu.attr("id", id + "_listbox"); this.$hint.attr({ "aria-hidden": true }); this.$input.attr({ "aria-owns": id + "_listbox", "aria-controls": id + "_listbox", role: "combobox", "aria-autocomplete": "list", "aria-expanded": false }); this.query = this.$input.val(); this.queryWhenFocused = this.hasFocus() ? this.query : null; this.$overflowHelper = buildOverflowHelper(this.$input); this._checkLanguageDirection(); if (this.$hint.length === 0) { this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; } this.onSync("cursorchange", this._updateDescendent); } Input.normalizeQuery = function(str) { return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " "); }; _.mixin(Input.prototype, EventEmitter, { _onBlur: function onBlur() { this.resetInputValue(); this.trigger("blurred"); }, _onFocus: function onFocus() { this.queryWhenFocused = this.query; this.trigger("focused"); }, _onKeydown: function onKeydown($e) { var keyName = specialKeyCodeMap[$e.which || $e.keyCode]; this._managePreventDefault(keyName, $e); if (keyName && this._shouldTrigger(keyName, $e)) { this.trigger(keyName + "Keyed", $e); } }, _onInput: function onInput() { this._setQuery(this.getInputValue()); this.clearHintIfInvalid(); this._checkLanguageDirection(); }, _managePreventDefault: function managePreventDefault(keyName, $e) { var preventDefault; switch (keyName) { case "up": case "down": preventDefault = !withModifier($e); break; default: preventDefault = false; } preventDefault && $e.preventDefault(); }, _shouldTrigger: function shouldTrigger(keyName, $e) { var trigger; switch (keyName) { case "tab": trigger = !withModifier($e); break; default: trigger = true; } return trigger; }, _checkLanguageDirection: function checkLanguageDirection() { var dir = (this.$input.css("direction") || "ltr").toLowerCase(); if (this.dir !== dir) { this.dir = dir; this.$hint.attr("dir", dir); this.trigger("langDirChanged", dir); } }, _setQuery: function setQuery(val, silent) { var areEquivalent, hasDifferentWhitespace; areEquivalent = areQueriesEquivalent(val, this.query); hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false; this.query = val; if (!silent && !areEquivalent) { this.trigger("queryChanged", this.query); } else if (!silent && hasDifferentWhitespace) { this.trigger("whitespaceChanged", this.query); } }, _updateDescendent: function updateDescendent(event, id) { this.$input.attr("aria-activedescendant", id); }, bind: function() { var that = this, onBlur, onFocus, onKeydown, onInput; onBlur = _.bind(this._onBlur, this); onFocus = _.bind(this._onFocus, this); onKeydown = _.bind(this._onKeydown, this); onInput = _.bind(this._onInput, this); this.$input.on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown); if (!_.isMsie() || _.isMsie() > 9) { this.$input.on("input.tt", onInput); } else { this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) { if (specialKeyCodeMap[$e.which || $e.keyCode]) { return; } _.defer(_.bind(that._onInput, that, $e)); }); } return this; }, focus: function focus() { this.$input.focus(); }, blur: function blur() { this.$input.blur(); }, getLangDir: function getLangDir() { return this.dir; }, getQuery: function getQuery() { return this.query || ""; }, setQuery: function setQuery(val, silent) { this.setInputValue(val); this._setQuery(val, silent); }, hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() { return this.query !== this.queryWhenFocused; }, getInputValue: function getInputValue() { return this.$input.val(); }, setInputValue: function setInputValue(value) { this.$input.val(value); this.clearHintIfInvalid(); this._checkLanguageDirection(); }, resetInputValue: function resetInputValue() { this.setInputValue(this.query); }, getHint: function getHint() { return this.$hint.val(); }, setHint: function setHint(value) { this.$hint.val(value); }, clearHint: function clearHint() { this.setHint(""); }, clearHintIfInvalid: function clearHintIfInvalid() { var val, hint, valIsPrefixOfHint, isValid; val = this.getInputValue(); hint = this.getHint(); valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0; isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow(); !isValid && this.clearHint(); }, hasFocus: function hasFocus() { return this.$input.is(":focus"); }, hasOverflow: function hasOverflow() { var constraint = this.$input.width() - 2; this.$overflowHelper.text(this.getInputValue()); return this.$overflowHelper.width() >= constraint; }, isCursorAtEnd: function() { var valueLength, selectionStart, range; valueLength = this.$input.val().length; selectionStart = this.$input[0].selectionStart; if (_.isNumber(selectionStart)) { return selectionStart === valueLength; } else if (document.selection) { range = document.selection.createRange(); range.moveStart("character", -valueLength); return valueLength === range.text.length; } return true; }, destroy: function destroy() { this.$hint.off(".tt"); this.$input.off(".tt"); this.$overflowHelper.remove(); this.$hint = this.$input = this.$overflowHelper = $("
"); }, setAriaExpanded: function setAriaExpanded(value) { this.$input.attr("aria-expanded", value); } }); return Input; function buildOverflowHelper($input) { return $('').css({ position: "absolute", visibility: "hidden", whiteSpace: "pre", fontFamily: $input.css("font-family"), fontSize: $input.css("font-size"), fontStyle: $input.css("font-style"), fontVariant: $input.css("font-variant"), fontWeight: $input.css("font-weight"), wordSpacing: $input.css("word-spacing"), letterSpacing: $input.css("letter-spacing"), textIndent: $input.css("text-indent"), textRendering: $input.css("text-rendering"), textTransform: $input.css("text-transform") }).insertAfter($input); } function areQueriesEquivalent(a, b) { return Input.normalizeQuery(a) === Input.normalizeQuery(b); } function withModifier($e) { return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey; } }(); var Dataset = function() { "use strict"; var keys, nameGenerator; keys = { dataset: "tt-selectable-dataset", val: "tt-selectable-display", obj: "tt-selectable-object" }; nameGenerator = _.getIdGenerator(); function Dataset(o, www) { o = o || {}; o.templates = o.templates || {}; o.templates.notFound = o.templates.notFound || o.templates.empty; if (!o.source) { $.error("missing source"); } if (!o.node) { $.error("missing node"); } if (o.name && !isValidName(o.name)) { $.error("invalid dataset name: " + o.name); } www.mixin(this); this.highlight = !!o.highlight; this.name = _.toStr(o.name || nameGenerator()); this.limit = o.limit || 5; this.displayFn = getDisplayFn(o.display || o.displayKey); this.templates = getTemplates(o.templates, this.displayFn); this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source; this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async; this._resetLastSuggestion(); this.$el = $(o.node).attr("role", "presentation").addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name); } Dataset.extractData = function extractData(el) { var $el = $(el); if ($el.data(keys.obj)) { return { dataset: $el.data(keys.dataset) || "", val: $el.data(keys.val) || "", obj: $el.data(keys.obj) || null }; } return null; }; _.mixin(Dataset.prototype, EventEmitter, { _overwrite: function overwrite(query, suggestions) { suggestions = suggestions || []; if (suggestions.length) { this._renderSuggestions(query, suggestions); } else if (this.async && this.templates.pending) { this._renderPending(query); } else if (!this.async && this.templates.notFound) { this._renderNotFound(query); } else { this._empty(); } this.trigger("rendered", suggestions, false, this.name); }, _append: function append(query, suggestions) { suggestions = suggestions || []; if (suggestions.length && this.$lastSuggestion.length) { this._appendSuggestions(query, suggestions); } else if (suggestions.length) { this._renderSuggestions(query, suggestions); } else if (!this.$lastSuggestion.length && this.templates.notFound) { this._renderNotFound(query); } this.trigger("rendered", suggestions, true, this.name); }, _renderSuggestions: function renderSuggestions(query, suggestions) { var $fragment; $fragment = this._getSuggestionsFragment(query, suggestions); this.$lastSuggestion = $fragment.children().last(); this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions)); }, _appendSuggestions: function appendSuggestions(query, suggestions) { var $fragment, $lastSuggestion; $fragment = this._getSuggestionsFragment(query, suggestions); $lastSuggestion = $fragment.children().last(); this.$lastSuggestion.after($fragment); this.$lastSuggestion = $lastSuggestion; }, _renderPending: function renderPending(query) { var template = this.templates.pending; this._resetLastSuggestion(); template && this.$el.html(template({ query: query, dataset: this.name })); }, _renderNotFound: function renderNotFound(query) { var template = this.templates.notFound; this._resetLastSuggestion(); template && this.$el.html(template({ query: query, dataset: this.name })); }, _empty: function empty() { this.$el.empty(); this._resetLastSuggestion(); }, _getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) { var that = this, fragment; fragment = document.createDocumentFragment(); _.each(suggestions, function getSuggestionNode(suggestion) { var $el, context; context = that._injectQuery(query, suggestion); $el = $(that.templates.suggestion(context)).data(keys.dataset, that.name).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable); fragment.appendChild($el[0]); }); this.highlight && highlight({ className: this.classes.highlight, node: fragment, pattern: query }); return $(fragment); }, _getFooter: function getFooter(query, suggestions) { return this.templates.footer ? this.templates.footer({ query: query, suggestions: suggestions, dataset: this.name }) : null; }, _getHeader: function getHeader(query, suggestions) { return this.templates.header ? this.templates.header({ query: query, suggestions: suggestions, dataset: this.name }) : null; }, _resetLastSuggestion: function resetLastSuggestion() { this.$lastSuggestion = $(); }, _injectQuery: function injectQuery(query, obj) { return _.isObject(obj) ? _.mixin({ _query: query }, obj) : obj; }, update: function update(query) { var that = this, canceled = false, syncCalled = false, rendered = 0; this.cancel(); this.cancel = function cancel() { canceled = true; that.cancel = $.noop; that.async && that.trigger("asyncCanceled", query, that.name); }; this.source(query, sync, async); !syncCalled && sync([]); function sync(suggestions) { if (syncCalled) { return; } syncCalled = true; suggestions = (suggestions || []).slice(0, that.limit); rendered = suggestions.length; that._overwrite(query, suggestions); if (rendered < that.limit && that.async) { that.trigger("asyncRequested", query, that.name); } } function async(suggestions) { suggestions = suggestions || []; if (!canceled && rendered < that.limit) { that.cancel = $.noop; var idx = Math.abs(rendered - that.limit); rendered += idx; that._append(query, suggestions.slice(0, idx)); that.async && that.trigger("asyncReceived", query, that.name); } } }, cancel: $.noop, clear: function clear() { this._empty(); this.cancel(); this.trigger("cleared"); }, isEmpty: function isEmpty() { return this.$el.is(":empty"); }, destroy: function destroy() { this.$el = $("
"); } }); return Dataset; function getDisplayFn(display) { display = display || _.stringify; return _.isFunction(display) ? display : displayFn; function displayFn(obj) { return obj[display]; } } function getTemplates(templates, displayFn) { return { notFound: templates.notFound && _.templatify(templates.notFound), pending: templates.pending && _.templatify(templates.pending), header: templates.header && _.templatify(templates.header), footer: templates.footer && _.templatify(templates.footer), suggestion: templates.suggestion ? userSuggestionTemplate : suggestionTemplate }; function userSuggestionTemplate(context) { var template = templates.suggestion; return $(template(context)).attr("id", _.guid()); } function suggestionTemplate(context) { return $('
').attr("id", _.guid()).text(displayFn(context)); } } function isValidName(str) { return /^[_a-zA-Z0-9-]+$/.test(str); } }(); var Menu = function() { "use strict"; function Menu(o, www) { var that = this; o = o || {}; if (!o.node) { $.error("node is required"); } www.mixin(this); this.$node = $(o.node); this.query = null; this.datasets = _.map(o.datasets, initializeDataset); function initializeDataset(oDataset) { var node = that.$node.find(oDataset.node).first(); oDataset.node = node.length ? node : $("
").appendTo(that.$node); return new Dataset(oDataset, www); } } _.mixin(Menu.prototype, EventEmitter, { _onSelectableClick: function onSelectableClick($e) { this.trigger("selectableClicked", $($e.currentTarget)); }, _onRendered: function onRendered(type, dataset, suggestions, async) { this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); this.trigger("datasetRendered", dataset, suggestions, async); }, _onCleared: function onCleared() { this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); this.trigger("datasetCleared"); }, _propagate: function propagate() { this.trigger.apply(this, arguments); }, _allDatasetsEmpty: function allDatasetsEmpty() { return _.every(this.datasets, _.bind(function isDatasetEmpty(dataset) { var isEmpty = dataset.isEmpty(); this.$node.attr("aria-expanded", !isEmpty); return isEmpty; }, this)); }, _getSelectables: function getSelectables() { return this.$node.find(this.selectors.selectable); }, _removeCursor: function _removeCursor() { var $selectable = this.getActiveSelectable(); $selectable && $selectable.removeClass(this.classes.cursor); }, _ensureVisible: function ensureVisible($el) { var elTop, elBottom, nodeScrollTop, nodeHeight; elTop = $el.position().top; elBottom = elTop + $el.outerHeight(true); nodeScrollTop = this.$node.scrollTop(); nodeHeight = this.$node.height() + parseInt(this.$node.css("paddingTop"), 10) + parseInt(this.$node.css("paddingBottom"), 10); if (elTop < 0) { this.$node.scrollTop(nodeScrollTop + elTop); } else if (nodeHeight < elBottom) { this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight)); } }, bind: function() { var that = this, onSelectableClick; onSelectableClick = _.bind(this._onSelectableClick, this); this.$node.on("click.tt", this.selectors.selectable, onSelectableClick); this.$node.on("mouseover", this.selectors.selectable, function() { that.setCursor($(this)); }); this.$node.on("mouseleave", function() { that._removeCursor(); }); _.each(this.datasets, function(dataset) { dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that); }); return this; }, isOpen: function isOpen() { return this.$node.hasClass(this.classes.open); }, open: function open() { this.$node.scrollTop(0); this.$node.addClass(this.classes.open); }, close: function close() { this.$node.attr("aria-expanded", false); this.$node.removeClass(this.classes.open); this._removeCursor(); }, setLanguageDirection: function setLanguageDirection(dir) { this.$node.attr("dir", dir); }, selectableRelativeToCursor: function selectableRelativeToCursor(delta) { var $selectables, $oldCursor, oldIndex, newIndex; $oldCursor = this.getActiveSelectable(); $selectables = this._getSelectables(); oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1; newIndex = oldIndex + delta; newIndex = (newIndex + 1) % ($selectables.length + 1) - 1; newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex; return newIndex === -1 ? null : $selectables.eq(newIndex); }, setCursor: function setCursor($selectable) { this._removeCursor(); if ($selectable = $selectable && $selectable.first()) { $selectable.addClass(this.classes.cursor); this._ensureVisible($selectable); } }, getSelectableData: function getSelectableData($el) { return $el && $el.length ? Dataset.extractData($el) : null; }, getActiveSelectable: function getActiveSelectable() { var $selectable = this._getSelectables().filter(this.selectors.cursor).first(); return $selectable.length ? $selectable : null; }, getTopSelectable: function getTopSelectable() { var $selectable = this._getSelectables().first(); return $selectable.length ? $selectable : null; }, update: function update(query) { var isValidUpdate = query !== this.query; if (isValidUpdate) { this.query = query; _.each(this.datasets, updateDataset); } return isValidUpdate; function updateDataset(dataset) { dataset.update(query); } }, empty: function empty() { _.each(this.datasets, clearDataset); this.query = null; this.$node.addClass(this.classes.empty); function clearDataset(dataset) { dataset.clear(); } }, destroy: function destroy() { this.$node.off(".tt"); this.$node = $("
"); _.each(this.datasets, destroyDataset); function destroyDataset(dataset) { dataset.destroy(); } } }); return Menu; }(); var Status = function() { "use strict"; function Status(options) { this.$el = $("", { role: "status", "aria-live": "polite" }).css({ position: "absolute", padding: "0", border: "0", height: "1px", width: "1px", "margin-bottom": "-1px", "margin-right": "-1px", overflow: "hidden", clip: "rect(0 0 0 0)", "white-space": "nowrap" }); options.$input.after(this.$el); _.each(options.menu.datasets, _.bind(function(dataset) { if (dataset.onSync) { dataset.onSync("rendered", _.bind(this.update, this)); dataset.onSync("cleared", _.bind(this.cleared, this)); } }, this)); } _.mixin(Status.prototype, { update: function update(event, suggestions) { var length = suggestions.length; var words; if (length === 1) { words = { result: "result", is: "is" }; } else { words = { result: "results", is: "are" }; } this.$el.text(length + " " + words.result + " " + words.is + " available, use up and down arrow keys to navigate."); }, cleared: function() { this.$el.text(""); } }); return Status; }(); var DefaultMenu = function() { "use strict"; var s = Menu.prototype; function DefaultMenu() { Menu.apply(this, [].slice.call(arguments, 0)); } _.mixin(DefaultMenu.prototype, Menu.prototype, { open: function open() { !this._allDatasetsEmpty() && this._show(); return s.open.apply(this, [].slice.call(arguments, 0)); }, close: function close() { this._hide(); return s.close.apply(this, [].slice.call(arguments, 0)); }, _onRendered: function onRendered() { if (this._allDatasetsEmpty()) { this._hide(); } else { this.isOpen() && this._show(); } return s._onRendered.apply(this, [].slice.call(arguments, 0)); }, _onCleared: function onCleared() { if (this._allDatasetsEmpty()) { this._hide(); } else { this.isOpen() && this._show(); } return s._onCleared.apply(this, [].slice.call(arguments, 0)); }, setLanguageDirection: function setLanguageDirection(dir) { this.$node.css(dir === "ltr" ? this.css.ltr : this.css.rtl); return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0)); }, _hide: function hide() { this.$node.hide(); }, _show: function show() { this.$node.css("display", "block"); } }); return DefaultMenu; }(); var Typeahead = function() { "use strict"; function Typeahead(o, www) { var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed, onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged, onWhitespaceChanged; o = o || {}; if (!o.input) { $.error("missing input"); } if (!o.menu) { $.error("missing menu"); } if (!o.eventBus) { $.error("missing event bus"); } www.mixin(this); this.eventBus = o.eventBus; this.minLength = _.isNumber(o.minLength) ? o.minLength : 1; this.input = o.input; this.menu = o.menu; this.enabled = true; this.autoselect = !!o.autoselect; this.active = false; this.input.hasFocus() && this.activate(); this.dir = this.input.getLangDir(); this._hacks(); this.menu.bind().onSync("selectableClicked", this._onSelectableClicked, this).onSync("asyncRequested", this._onAsyncRequested, this).onSync("asyncCanceled", this._onAsyncCanceled, this).onSync("asyncReceived", this._onAsyncReceived, this).onSync("datasetRendered", this._onDatasetRendered, this).onSync("datasetCleared", this._onDatasetCleared, this); onFocused = c(this, "activate", "open", "_onFocused"); onBlurred = c(this, "deactivate", "_onBlurred"); onEnterKeyed = c(this, "isActive", "isOpen", "_onEnterKeyed"); onTabKeyed = c(this, "isActive", "isOpen", "_onTabKeyed"); onEscKeyed = c(this, "isActive", "_onEscKeyed"); onUpKeyed = c(this, "isActive", "open", "_onUpKeyed"); onDownKeyed = c(this, "isActive", "open", "_onDownKeyed"); onLeftKeyed = c(this, "isActive", "isOpen", "_onLeftKeyed"); onRightKeyed = c(this, "isActive", "isOpen", "_onRightKeyed"); onQueryChanged = c(this, "_openIfActive", "_onQueryChanged"); onWhitespaceChanged = c(this, "_openIfActive", "_onWhitespaceChanged"); this.input.bind().onSync("focused", onFocused, this).onSync("blurred", onBlurred, this).onSync("enterKeyed", onEnterKeyed, this).onSync("tabKeyed", onTabKeyed, this).onSync("escKeyed", onEscKeyed, this).onSync("upKeyed", onUpKeyed, this).onSync("downKeyed", onDownKeyed, this).onSync("leftKeyed", onLeftKeyed, this).onSync("rightKeyed", onRightKeyed, this).onSync("queryChanged", onQueryChanged, this).onSync("whitespaceChanged", onWhitespaceChanged, this).onSync("langDirChanged", this._onLangDirChanged, this); } _.mixin(Typeahead.prototype, { _hacks: function hacks() { var $input, $menu; $input = this.input.$input || $("
"); $menu = this.menu.$node || $("
"); $input.on("blur.tt", function($e) { var active, isActive, hasActive; active = document.activeElement; isActive = $menu.is(active); hasActive = $menu.has(active).length > 0; if (_.isMsie() && (isActive || hasActive)) { $e.preventDefault(); $e.stopImmediatePropagation(); _.defer(function() { $input.focus(); }); } }); $menu.on("mousedown.tt", function($e) { $e.preventDefault(); }); }, _onSelectableClicked: function onSelectableClicked(type, $el) { this.select($el); }, _onDatasetCleared: function onDatasetCleared() { this._updateHint(); }, _onDatasetRendered: function onDatasetRendered(type, suggestions, async, dataset) { this._updateHint(); if (this.autoselect) { var cursorClass = this.selectors.cursor.substr(1); this.menu.$node.find(this.selectors.suggestion).first().addClass(cursorClass); } this.eventBus.trigger("render", suggestions, async, dataset); }, _onAsyncRequested: function onAsyncRequested(type, dataset, query) { this.eventBus.trigger("asyncrequest", query, dataset); }, _onAsyncCanceled: function onAsyncCanceled(type, dataset, query) { this.eventBus.trigger("asynccancel", query, dataset); }, _onAsyncReceived: function onAsyncReceived(type, dataset, query) { this.eventBus.trigger("asyncreceive", query, dataset); }, _onFocused: function onFocused() { this._minLengthMet() && this.menu.update(this.input.getQuery()); }, _onBlurred: function onBlurred() { if (this.input.hasQueryChangedSinceLastFocus()) { this.eventBus.trigger("change", this.input.getQuery()); } }, _onEnterKeyed: function onEnterKeyed(type, $e) { var $selectable; if ($selectable = this.menu.getActiveSelectable()) { if (this.select($selectable)) { $e.preventDefault(); $e.stopPropagation(); } } else if (this.autoselect) { if (this.select(this.menu.getTopSelectable())) { $e.preventDefault(); $e.stopPropagation(); } } }, _onTabKeyed: function onTabKeyed(type, $e) { var $selectable; if ($selectable = this.menu.getActiveSelectable()) { this.select($selectable) && $e.preventDefault(); } else if (this.autoselect) { if ($selectable = this.menu.getTopSelectable()) { this.autocomplete($selectable) && $e.preventDefault(); } } }, _onEscKeyed: function onEscKeyed() { this.close(); }, _onUpKeyed: function onUpKeyed() { this.moveCursor(-1); }, _onDownKeyed: function onDownKeyed() { this.moveCursor(+1); }, _onLeftKeyed: function onLeftKeyed() { if (this.dir === "rtl" && this.input.isCursorAtEnd()) { this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); } }, _onRightKeyed: function onRightKeyed() { if (this.dir === "ltr" && this.input.isCursorAtEnd()) { this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); } }, _onQueryChanged: function onQueryChanged(e, query) { this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty(); }, _onWhitespaceChanged: function onWhitespaceChanged() { this._updateHint(); }, _onLangDirChanged: function onLangDirChanged(e, dir) { if (this.dir !== dir) { this.dir = dir; this.menu.setLanguageDirection(dir); } }, _openIfActive: function openIfActive() { this.isActive() && this.open(); }, _minLengthMet: function minLengthMet(query) { query = _.isString(query) ? query : this.input.getQuery() || ""; return query.length >= this.minLength; }, _updateHint: function updateHint() { var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match; $selectable = this.menu.getTopSelectable(); data = this.menu.getSelectableData($selectable); val = this.input.getInputValue(); if (data && !_.isBlankString(val) && !this.input.hasOverflow()) { query = Input.normalizeQuery(val); escapedQuery = _.escapeRegExChars(query); frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i"); match = frontMatchRegEx.exec(data.val); match && this.input.setHint(val + match[1]); } else { this.input.clearHint(); } }, isEnabled: function isEnabled() { return this.enabled; }, enable: function enable() { this.enabled = true; }, disable: function disable() { this.enabled = false; }, isActive: function isActive() { return this.active; }, activate: function activate() { if (this.isActive()) { return true; } else if (!this.isEnabled() || this.eventBus.before("active")) { return false; } else { this.active = true; this.eventBus.trigger("active"); return true; } }, deactivate: function deactivate() { if (!this.isActive()) { return true; } else if (this.eventBus.before("idle")) { return false; } else { this.active = false; this.close(); this.eventBus.trigger("idle"); return true; } }, isOpen: function isOpen() { return this.menu.isOpen(); }, open: function open() { if (!this.isOpen() && !this.eventBus.before("open")) { this.input.setAriaExpanded(true); this.menu.open(); this._updateHint(); this.eventBus.trigger("open"); } return this.isOpen(); }, close: function close() { if (this.isOpen() && !this.eventBus.before("close")) { this.input.setAriaExpanded(false); this.menu.close(); this.input.clearHint(); this.input.resetInputValue(); this.eventBus.trigger("close"); } return !this.isOpen(); }, setVal: function setVal(val) { this.input.setQuery(_.toStr(val)); }, getVal: function getVal() { return this.input.getQuery(); }, select: function select($selectable) { var data = this.menu.getSelectableData($selectable); if (data && !this.eventBus.before("select", data.obj, data.dataset)) { this.input.setQuery(data.val, true); this.eventBus.trigger("select", data.obj, data.dataset); this.close(); return true; } return false; }, autocomplete: function autocomplete($selectable) { var query, data, isValid; query = this.input.getQuery(); data = this.menu.getSelectableData($selectable); isValid = data && query !== data.val; if (isValid && !this.eventBus.before("autocomplete", data.obj, data.dataset)) { this.input.setQuery(data.val); this.eventBus.trigger("autocomplete", data.obj, data.dataset); return true; } return false; }, moveCursor: function moveCursor(delta) { var query, $candidate, data, suggestion, datasetName, cancelMove, id; query = this.input.getQuery(); $candidate = this.menu.selectableRelativeToCursor(delta); data = this.menu.getSelectableData($candidate); suggestion = data ? data.obj : null; datasetName = data ? data.dataset : null; id = $candidate ? $candidate.attr("id") : null; this.input.trigger("cursorchange", id); cancelMove = this._minLengthMet() && this.menu.update(query); if (!cancelMove && !this.eventBus.before("cursorchange", suggestion, datasetName)) { this.menu.setCursor($candidate); if (data) { if (typeof data.val === "string") { this.input.setInputValue(data.val); } } else { this.input.resetInputValue(); this._updateHint(); } this.eventBus.trigger("cursorchange", suggestion, datasetName); return true; } return false; }, destroy: function destroy() { this.input.destroy(); this.menu.destroy(); } }); return Typeahead; function c(ctx) { var methods = [].slice.call(arguments, 1); return function() { var args = [].slice.call(arguments); _.each(methods, function(method) { return ctx[method].apply(ctx, args); }); }; } }(); (function() { "use strict"; var old, keys, methods; old = $.fn.typeahead; keys = { www: "tt-www", attrs: "tt-attrs", typeahead: "tt-typeahead" }; methods = { initialize: function initialize(o, datasets) { var www; datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1); o = o || {}; www = WWW(o.classNames); return this.each(attach); function attach() { var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, status, typeahead, MenuConstructor; _.each(datasets, function(d) { d.highlight = !!o.highlight; }); $input = $(this); $wrapper = $(www.html.wrapper); $hint = $elOrNull(o.hint); $menu = $elOrNull(o.menu); defaultHint = o.hint !== false && !$hint; defaultMenu = o.menu !== false && !$menu; defaultHint && ($hint = buildHintFromInput($input, www)); defaultMenu && ($menu = $(www.html.menu).css(www.css.menu)); $hint && $hint.val(""); $input = prepInput($input, www); if (defaultHint || defaultMenu) { $wrapper.css(www.css.wrapper); $input.css(defaultHint ? www.css.input : www.css.inputWithNoHint); $input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null); } MenuConstructor = defaultMenu ? DefaultMenu : Menu; eventBus = new EventBus({ el: $input }); input = new Input({ hint: $hint, input: $input, menu: $menu }, www); menu = new MenuConstructor({ node: $menu, datasets: datasets }, www); status = new Status({ $input: $input, menu: menu }); typeahead = new Typeahead({ input: input, menu: menu, eventBus: eventBus, minLength: o.minLength, autoselect: o.autoselect }, www); $input.data(keys.www, www); $input.data(keys.typeahead, typeahead); } }, isEnabled: function isEnabled() { var enabled; ttEach(this.first(), function(t) { enabled = t.isEnabled(); }); return enabled; }, enable: function enable() { ttEach(this, function(t) { t.enable(); }); return this; }, disable: function disable() { ttEach(this, function(t) { t.disable(); }); return this; }, isActive: function isActive() { var active; ttEach(this.first(), function(t) { active = t.isActive(); }); return active; }, activate: function activate() { ttEach(this, function(t) { t.activate(); }); return this; }, deactivate: function deactivate() { ttEach(this, function(t) { t.deactivate(); }); return this; }, isOpen: function isOpen() { var open; ttEach(this.first(), function(t) { open = t.isOpen(); }); return open; }, open: function open() { ttEach(this, function(t) { t.open(); }); return this; }, close: function close() { ttEach(this, function(t) { t.close(); }); return this; }, select: function select(el) { var success = false, $el = $(el); ttEach(this.first(), function(t) { success = t.select($el); }); return success; }, autocomplete: function autocomplete(el) { var success = false, $el = $(el); ttEach(this.first(), function(t) { success = t.autocomplete($el); }); return success; }, moveCursor: function moveCursoe(delta) { var success = false; ttEach(this.first(), function(t) { success = t.moveCursor(delta); }); return success; }, val: function val(newVal) { var query; if (!arguments.length) { ttEach(this.first(), function(t) { query = t.getVal(); }); return query; } else { ttEach(this, function(t) { t.setVal(_.toStr(newVal)); }); return this; } }, destroy: function destroy() { ttEach(this, function(typeahead, $input) { revert($input); typeahead.destroy(); }); return this; } }; $.fn.typeahead = function(method) { if (methods[method]) { return methods[method].apply(this, [].slice.call(arguments, 1)); } else { return methods.initialize.apply(this, arguments); } }; $.fn.typeahead.noConflict = function noConflict() { $.fn.typeahead = old; return this; }; function ttEach($els, fn) { $els.each(function() { var $input = $(this), typeahead; (typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input); }); } function buildHintFromInput($input, www) { return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop({ readonly: true, required: false }).removeAttr("id name placeholder").removeClass("required").attr({ spellcheck: "false", tabindex: -1 }); } function prepInput($input, www) { $input.data(keys.attrs, { dir: $input.attr("dir"), autocomplete: $input.attr("autocomplete"), spellcheck: $input.attr("spellcheck"), style: $input.attr("style") }); $input.addClass(www.classes.input).attr({ spellcheck: false }); try { !$input.attr("dir") && $input.attr("dir", "auto"); } catch (e) {} return $input; } function getBackgroundStyles($el) { return { backgroundAttachment: $el.css("background-attachment"), backgroundClip: $el.css("background-clip"), backgroundColor: $el.css("background-color"), backgroundImage: $el.css("background-image"), backgroundOrigin: $el.css("background-origin"), backgroundPosition: $el.css("background-position"), backgroundRepeat: $el.css("background-repeat"), backgroundSize: $el.css("background-size") }; } function revert($input) { var www, $wrapper; www = $input.data(keys.www); $wrapper = $input.parent().filter(www.selectors.wrapper); _.each($input.data(keys.attrs), function(val, key) { _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val); }); $input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input); if ($wrapper.length) { $input.detach().insertAfter($wrapper); $wrapper.remove(); } } function $elOrNull(obj) { var isValid, $el; isValid = _.isJQuery(obj) || _.isElement(obj); $el = isValid ? $(obj).first() : []; return $el.length ? $el : null; } })(); }); ================================================ FILE: docs/undocumented.json ================================================ { "warnings": [ ], "source_directory": "/Users/vinhnguyen/Workspace/NVActivityIndicatorView" } ================================================ FILE: scripts/bump-version.sh ================================================ #!/bin/bash type="${1:-patch}" fastlane run increment_build_number fastlane run increment_version_number bump_type:"$type" fastlane run version_bump_podspec bump_type:"$type" path:"NVActivityIndicatorView.podspec"