├── .gitignore ├── .swiftpm └── xcode │ └── package.xcworkspace │ └── contents.xcworkspacedata ├── Carthage └── Build │ ├── .Switches.version │ └── iOS │ ├── 7C963147-D6EB-33ED-821F-9125EE2671FC.bcsymbolmap │ ├── Switches.framework.dSYM │ └── Contents │ │ ├── Info.plist │ │ └── Resources │ │ └── DWARF │ │ └── Switches │ └── Switches.framework │ ├── Headers │ ├── Switches-Swift.h │ └── Switches.h │ ├── Info.plist │ ├── Modules │ ├── Switches.swiftmodule │ │ ├── Project │ │ │ ├── x86_64-apple-ios-simulator.swiftsourceinfo │ │ │ └── x86_64.swiftsourceinfo │ │ ├── arm64-apple-ios.swiftdoc │ │ ├── arm64-apple-ios.swiftmodule │ │ ├── arm64.swiftdoc │ │ ├── arm64.swiftmodule │ │ ├── x86_64-apple-ios-simulator.swiftdoc │ │ ├── x86_64-apple-ios-simulator.swiftmodule │ │ ├── x86_64.swiftdoc │ │ └── x86_64.swiftmodule │ └── module.modulemap │ └── Switches ├── LICENSE ├── Package.swift ├── README.md ├── Sources ├── Info.plist ├── Switches.h ├── Switches.swift └── Switches │ ├── BaseControl.swift │ ├── Extensions │ ├── CALayer+Extension.swift │ ├── CGRect+Extension.swift │ └── UIImage+Extension.swift │ ├── JDSwitch.swift │ ├── SDSwitch.swift │ ├── Switcher.swift │ ├── YapBaseSwitch.swift │ ├── YapLiquidSwitch.swift │ ├── YapSmileSwitch.swift │ └── YapSwitch.swift ├── Switches.podspec ├── Switches.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcshareddata │ └── xcschemes │ │ ├── Switches.xcscheme │ │ └── SwitchesDemo.xcscheme └── xcuserdata │ └── jawadali.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── SwitchesDemo ├── Application │ ├── AppDelegate.swift │ └── SceneDelegate.swift ├── Controllers │ ├── AnimatedSwitchesViewController.swift │ ├── CollectionViewCell │ │ └── CollectionViewCell.swift │ ├── GradientSwitchesViewController.swift │ ├── JellySwitchesViewController.swift │ ├── LoadingSwitchesViewController.swift │ ├── ModeBSwitchesViewController.swift │ ├── ModeSwitchesViewController.swift │ ├── RandomViewController.swift │ ├── SmileyViewController.swift │ ├── SwiftyViewController.swift │ ├── ThumbImageSwitchesViewController.swift │ └── ViewController.swift ├── Model │ └── SwitchType.swift ├── Resources │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ └── ItunesArtwork@2x.png │ │ ├── Contents.json │ │ ├── check.imageset │ │ │ ├── Contents.json │ │ │ └── check@3x.png │ │ ├── check1.imageset │ │ │ ├── Contents.json │ │ │ └── check1.png │ │ ├── check9.imageset │ │ │ ├── Contents.json │ │ │ └── check9.png │ │ ├── close.imageset │ │ │ ├── Contents.json │ │ │ └── close.pdf │ │ ├── cross1.imageset │ │ │ ├── Contents.json │ │ │ └── cross1.png │ │ ├── cross24.imageset │ │ │ ├── Contents.json │ │ │ └── cross24.png │ │ ├── cross7.imageset │ │ │ ├── Contents.json │ │ │ └── cross7.png │ │ ├── go1.imageset │ │ │ ├── Contents.json │ │ │ └── go1.png │ │ ├── go2.imageset │ │ │ ├── Contents.json │ │ │ └── go2.png │ │ ├── icon_check_primary_dark.imageset │ │ │ ├── Contents.json │ │ │ └── icon_check_primary_dark.pdf │ │ ├── icon_close_dark_purple.imageset │ │ │ ├── Contents.json │ │ │ └── icon_close_dark_purple.pdf │ │ ├── left.imageset │ │ │ ├── Contents.json │ │ │ └── left.png │ │ ├── off1.imageset │ │ │ ├── Contents.json │ │ │ └── off1.png │ │ ├── off2.imageset │ │ │ ├── Contents.json │ │ │ └── off2.png │ │ ├── onTick.imageset │ │ │ ├── Contents.json │ │ │ └── onTick.png │ │ ├── q1.imageset │ │ │ ├── Contents.json │ │ │ └── q1.png │ │ ├── right.imageset │ │ │ ├── Contents.json │ │ │ └── right.png │ │ ├── switch-animation-post.imageset │ │ │ ├── Contents.json │ │ │ └── switch-animation-post.jpg │ │ ├── thumb.imageset │ │ │ ├── Contents.json │ │ │ └── thumb.png │ │ ├── thumb1.imageset │ │ │ ├── Contents.json │ │ │ └── thumb1.png │ │ ├── thumb12.imageset │ │ │ ├── Contents.json │ │ │ └── thumb12.png │ │ ├── thumb4.imageset │ │ │ ├── Contents.json │ │ │ └── thumb4.png │ │ ├── thumb7.imageset │ │ │ ├── Contents.json │ │ │ └── thumb7.png │ │ └── w1.imageset │ │ │ ├── Contents.json │ │ │ └── w1.png │ └── Info.plist └── Storyboard │ └── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── SwitchesDemoTests ├── Info.plist └── SwitchesDemoTests.swift ├── SwitchesDemoUITests ├── Info.plist └── SwitchesDemoUITests.swift └── images ├── Screen Recording 2020-08-31 at 3.39.54 PM.gif ├── abcd ├── ezgif.com-video-to-gif-23.gif ├── ezgif.com-video-to-gif-24.gif ├── ezgif.com-video-to-gif-25.gif └── ezgif.com-video-to-gif-26.gif /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Carthage/Build/.Switches.version: -------------------------------------------------------------------------------- 1 | { 2 | "Mac" : [ 3 | 4 | ], 5 | "watchOS" : [ 6 | 7 | ], 8 | "tvOS" : [ 9 | 10 | ], 11 | "commitish" : "1.0.2", 12 | "iOS" : [ 13 | { 14 | "name" : "Switches", 15 | "hash" : "76e27d2ac2a328b3a1a6e2fc99ff1d6159a11e85e9f2f1921ce415617b0fda32", 16 | "swiftToolchainVersion" : "5.2 (swiftlang-1103.0.32.1 clang-1103.0.32.29)" 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /Carthage/Build/iOS/Switches.framework.dSYM/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleIdentifier 8 | com.apple.xcode.dsym.XeroT.Switches 9 | CFBundleInfoDictionaryVersion 10 | 6.0 11 | CFBundlePackageType 12 | dSYM 13 | CFBundleSignature 14 | ???? 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleVersion 18 | 1 19 | 20 | 21 | -------------------------------------------------------------------------------- /Carthage/Build/iOS/Switches.framework.dSYM/Contents/Resources/DWARF/Switches: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/Carthage/Build/iOS/Switches.framework.dSYM/Contents/Resources/DWARF/Switches -------------------------------------------------------------------------------- /Carthage/Build/iOS/Switches.framework/Headers/Switches.h: -------------------------------------------------------------------------------- 1 | // 2 | // Switches.h 3 | // Switches 4 | // 5 | // Created by Jawad Ali on 30/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Switches. 12 | FOUNDATION_EXPORT double SwitchesVersionNumber; 13 | 14 | //! Project version string for Switches. 15 | FOUNDATION_EXPORT const unsigned char SwitchesVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Carthage/Build/iOS/Switches.framework/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/Carthage/Build/iOS/Switches.framework/Info.plist -------------------------------------------------------------------------------- /Carthage/Build/iOS/Switches.framework/Modules/Switches.swiftmodule/Project/x86_64-apple-ios-simulator.swiftsourceinfo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/Carthage/Build/iOS/Switches.framework/Modules/Switches.swiftmodule/Project/x86_64-apple-ios-simulator.swiftsourceinfo -------------------------------------------------------------------------------- /Carthage/Build/iOS/Switches.framework/Modules/Switches.swiftmodule/Project/x86_64.swiftsourceinfo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/Carthage/Build/iOS/Switches.framework/Modules/Switches.swiftmodule/Project/x86_64.swiftsourceinfo -------------------------------------------------------------------------------- /Carthage/Build/iOS/Switches.framework/Modules/Switches.swiftmodule/arm64-apple-ios.swiftdoc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/Carthage/Build/iOS/Switches.framework/Modules/Switches.swiftmodule/arm64-apple-ios.swiftdoc -------------------------------------------------------------------------------- /Carthage/Build/iOS/Switches.framework/Modules/Switches.swiftmodule/arm64-apple-ios.swiftmodule: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/Carthage/Build/iOS/Switches.framework/Modules/Switches.swiftmodule/arm64-apple-ios.swiftmodule -------------------------------------------------------------------------------- /Carthage/Build/iOS/Switches.framework/Modules/Switches.swiftmodule/arm64.swiftdoc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/Carthage/Build/iOS/Switches.framework/Modules/Switches.swiftmodule/arm64.swiftdoc -------------------------------------------------------------------------------- /Carthage/Build/iOS/Switches.framework/Modules/Switches.swiftmodule/arm64.swiftmodule: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/Carthage/Build/iOS/Switches.framework/Modules/Switches.swiftmodule/arm64.swiftmodule -------------------------------------------------------------------------------- /Carthage/Build/iOS/Switches.framework/Modules/Switches.swiftmodule/x86_64-apple-ios-simulator.swiftdoc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/Carthage/Build/iOS/Switches.framework/Modules/Switches.swiftmodule/x86_64-apple-ios-simulator.swiftdoc -------------------------------------------------------------------------------- /Carthage/Build/iOS/Switches.framework/Modules/Switches.swiftmodule/x86_64-apple-ios-simulator.swiftmodule: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/Carthage/Build/iOS/Switches.framework/Modules/Switches.swiftmodule/x86_64-apple-ios-simulator.swiftmodule -------------------------------------------------------------------------------- /Carthage/Build/iOS/Switches.framework/Modules/Switches.swiftmodule/x86_64.swiftdoc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/Carthage/Build/iOS/Switches.framework/Modules/Switches.swiftmodule/x86_64.swiftdoc -------------------------------------------------------------------------------- /Carthage/Build/iOS/Switches.framework/Modules/Switches.swiftmodule/x86_64.swiftmodule: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/Carthage/Build/iOS/Switches.framework/Modules/Switches.swiftmodule/x86_64.swiftmodule -------------------------------------------------------------------------------- /Carthage/Build/iOS/Switches.framework/Modules/module.modulemap: -------------------------------------------------------------------------------- 1 | framework module Switches { 2 | umbrella header "Switches.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | 8 | module Switches.Swift { 9 | header "Switches-Swift.h" 10 | requires objc 11 | } 12 | -------------------------------------------------------------------------------- /Carthage/Build/iOS/Switches.framework/Switches: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/Carthage/Build/iOS/Switches.framework/Switches -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jawad Ali 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.2 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "Switches", 8 | platforms: [ 9 | .iOS(.v11), 10 | ], 11 | products: [ 12 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 13 | .library( 14 | name: "Switches", 15 | targets: ["Switches"]), 16 | ], 17 | dependencies: [ 18 | // Dependencies declare other packages that this package depends on. 19 | // .package(url: /* package url */, from: "1.0.0"), 20 | ], 21 | targets: [ 22 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 23 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 24 | .target( 25 | name: "Switches", 26 | dependencies: []), 27 | .testTarget( 28 | name: "SwitchesTests", 29 | dependencies: ["Switches"]), 30 | ] 31 | ) 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | 6 | # Switches Animations 7 | > An animated switch collection 8 | 9 | 10 | [![CI Status](https://travis-ci.org/jwd-ali/RingPieChart.svg)](https://travis-ci.org/jwd-ali/RingPieChart) 11 | [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-0473B3.svg?style=flat)](https://github.com/Carthage/Carthage) 12 | [![Swift 5.1](https://img.shields.io/badge/swift-5.1-orange)](https://swift.org) 13 | 14 |

Add Beautiful Customise Switch in your application to enhance your application UI and UX

15 | 16 | ___ 17 | 18 | # Medium Article: 19 | > https://medium.com/@l060214/how-to-create-loading-uiswitch-with-calayers-and-caanimations-4667c1fa845 20 | 21 | ___ 22 | 23 |

24 | From time to time I browse Dribbble to get inspiration from UI/UX designers. You often see non-native control elements in these designs. Some, such as checkboxes, have no native counterpart in UIKit while others like switches are— except they’re nearly impossible to customize. You cannot even change the size of a UISwitch. What if you need to implement a completely custom designed switch for your app like this one 25 | 26 | 27 | If you wanted to implement any of these designs it would be a poor choice to subclass `UISwitch` as you can hardly customize the colours — aside from the on/off tint. 28 | So I thought I would try to re-engineer the UISwitch so that I would have complete control over each aspect, making it easy to style in the future.Turns out its actually pretty easy (and a lot of fun!) to build these control elements from scratch. I even went so far as to add the ability to set on/off text or images. For those who don’t know, UISwitch has onImage and offImage which when set don’t do anything anymore. 29 | 30 | So i take few of the Dribble switches Daily UI challenges as challenge and implemented them in this library.This library has cool and sophisticated animations,Designs and effects. Also, customizable properties can be tweaked behaviors and enhance your application UI cool. With this library, you can easily implement material design switch to your app. 31 |

32 | 33 | > You can play with the attributes and can customise them according to your UI demands. Below are the few examples 34 | ## Usage example 35 | 36 | |Class|Example| 37 | |-| -------- | 38 | |Switcher| Dribble UI Challenge: https://dribbble.com/shots/4148855-Switcher-XXXIII| 39 | |SwitcherFullStrtech|Dribble UI Challenge:https://dribbble.com/shots/3844909-On-Off| 40 | |SDSwitch| Dribble UI Challenge:https://dribbble.com/shots/3545882-Switch-with-server-calls| 41 | |YapDarkAndLightModeSwitch | Dribble UI Challenge:https://dribbble.com/shots/2484722-Daily-Ui-Day-15-On-Off-Switch| 42 | |YapAnimatedSwitch| Dribble UI Challenge:https://dribbble.com/shots/2309834-Yet-another-toggle-animation| 43 | |SDSwitch |Dribble UI Challenge:https://dribbble.com/shots/5192899-Simple-toggle| 44 | |YapFullTextSwitch | Dribble UI Challenge:https://dribbble.com/shots/2330566-Morph-Switch| 45 | |JDSwitch | Dribble UI Challenge:https://dribbble.com/shots/2346044-Switch-on-off| 46 | |YapSmileSwitch | Dribble UI Challenge:https://dribbble.com/shots/2011284-Switcher-ll| 47 | |YapLiquidSwitch | Dribble UI Challenge:https://dribbble.com/shots/2028065-Switcher-lll| 48 | |YapSwitchSlim | Dribble UI Challenge:https://dribbble.com/shots/2158763-simple-toggle| 49 | |YapGradientSwitch | Dribble UI Challenge:https://dribbble.com/shots/2603107-toggle-switch-micro-interaction| 50 | |YapHalfStretchSwitch || 51 | |YapSwitch || 52 | |YapFullStretchSwitch || 53 | |YapSwitch || 54 | 55 | ## Requirements 56 | 57 | - iOS 10.0+ / Mac OS X 10.9+ / watchOS 2.0+ / tvOS 9.0+ 58 | - Xcode 8.0+ 59 | 60 | ## Installation 61 | 62 | ### [CocoaPods](http://cocoapods.org) 63 | 64 | To integrate **Switches Animations** into your Xcode project using CocoaPods, specify it in your `Podfile`: 65 | 66 | ```ruby 67 | use_frameworks! 68 | 69 | pod 'Switches' 70 | ``` 71 | 72 | Then, run the following command: 73 | 74 | ```bash 75 | $ pod install 76 | ``` 77 | 78 | ### [Swift Package Manager (SPM)](https://swift.org/package-manager) 79 | 80 | #### Prerequisites 81 | - OSX 82 | 83 | 84 | #### Update `Package.swift` 85 | To integrate `Switches Animations` in your project, add the proper description to your `Package.swift` file: 86 | ```swift 87 | // swift-tools-version:5.0 88 | import PackageDescription 89 | 90 | let package = Package( 91 | name: "YOUR_PROJECT_NAME", 92 | dependencies: [ 93 | .package(url: "https://github.com/jwd-ali/Switch-Animations.git") 94 | ], 95 | targets: [ 96 | .target( 97 | name: "YOUR_TARGET_NAME", 98 | dependencies: ["Switch-Animations"] 99 | ), 100 | ... 101 | ] 102 | ) 103 | ``` 104 | 105 | 106 | ### [Carthage](http://github.com/Carthage/Carthage) 107 | 108 | To integrate `Switches Animations` into your Xcode project using Carthage, specify it in your `Cartfile`: 109 | 110 | ```ogdl 111 | github "jwd-ali/Switch-Animations" 112 | ``` 113 | ### Manually 114 | 115 | If you prefer not to use a dependency manager, you can integrate Switches Animations into your project manually. 116 | 117 | - Add Sources folder into your project 118 | 119 | ## Integration 120 | See the Demo Xcode project its easy to understand with proper comments on properties .. write me if you didn't get anything L060214@gmail.com 121 | 122 |

Be careful!!!

123 | Irreversible consequences are possible during long work with the framework. 124 | 125 | 126 | 127 | 128 | 129 | ## Contributing 130 | 131 | I’d love to have help on this project. For small changes please [open a pull request](https://github.com/jwd-ali/Switch-Animations/pulls), for larger changes please [open an issue](https://github.com/jwd-ali/Switch-Animations/issues) first to discuss what you’d like to see. 132 | 133 | 134 | License 135 | ------- 136 | 137 | Switch-Animations is under [MIT](https://opensource.org/licenses/MIT). See [LICENSE](LICENSE) file for more info. 138 | -------------------------------------------------------------------------------- /Sources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /Sources/Switches.h: -------------------------------------------------------------------------------- 1 | // 2 | // Switches.h 3 | // Switches 4 | // 5 | // Created by Jawad Ali on 30/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Switches. 12 | FOUNDATION_EXPORT double SwitchesVersionNumber; 13 | 14 | //! Project version string for Switches. 15 | FOUNDATION_EXPORT const unsigned char SwitchesVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Sources/Switches.swift: -------------------------------------------------------------------------------- 1 | struct Switches { 2 | var text = "Hello, World!" 3 | } 4 | -------------------------------------------------------------------------------- /Sources/Switches/BaseControl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseControl.swift 3 | // testi 4 | // 5 | // Created by Jawad Ali on 28/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public typealias SDSwitchValueChange = (_ value: Bool) -> Void 12 | open class BaseControl: UIControl { 13 | // MARK: - Property 14 | 15 | open var valueChange: SDSwitchValueChange? 16 | 17 | open var isOn: Bool = false 18 | } 19 | -------------------------------------------------------------------------------- /Sources/Switches/Extensions/CALayer+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CALayer+Extension.swift 3 | // testi 4 | // 5 | // Created by Jawad Ali on 20/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | public extension CALayer { 11 | var areAnimationsEnabled: Bool { 12 | get { delegate == nil } 13 | set { delegate = newValue ? nil : CALayerAnimationsDisablingDelegate.shared } 14 | } 15 | 16 | private class CALayerAnimationsDisablingDelegate: NSObject, CALayerDelegate { 17 | static let shared = CALayerAnimationsDisablingDelegate() 18 | private let null = NSNull() 19 | 20 | func action(for layer: CALayer, forKey event: String) -> CAAction? { 21 | self.null 22 | } 23 | } 24 | 25 | func bringToFront() { 26 | guard let sLayer = superlayer else { 27 | return 28 | } 29 | removeFromSuperlayer() 30 | sLayer.insertSublayer(self, at: UInt32(sLayer.sublayers?.count ?? 0)) 31 | } 32 | 33 | func sendToBack() { 34 | guard let sLayer = superlayer else { 35 | return 36 | } 37 | removeFromSuperlayer() 38 | sLayer.insertSublayer(self, at: 0) 39 | } 40 | 41 | func animateGradientColors(from: [CGColor], to: [CGColor], duration: Double) { 42 | let animation = CABasicAnimation(keyPath: "colors") 43 | animation.fromValue = from 44 | animation.toValue = to 45 | animation.duration = duration 46 | animation.fillMode = .forwards 47 | animation.isRemovedOnCompletion = false 48 | 49 | // add the animation to the gradient 50 | self.add(animation, forKey: nil) 51 | } 52 | 53 | func strokeAnimation(duration: Double) { 54 | let animation = CABasicAnimation(keyPath: "strokeEnd") 55 | animation.fromValue = 0 56 | animation.toValue = 1 57 | animation.duration = duration 58 | animation.fillMode = .forwards 59 | animation.isRemovedOnCompletion = false 60 | self.add(animation, forKey: "line") 61 | } 62 | 63 | func rotateAnimation(angal: CGFloat, duration: Double, repeatAnimation: Bool = false) { 64 | let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z") 65 | rotationAnimation.fromValue = 0 66 | rotationAnimation.toValue = angal 67 | rotationAnimation.duration = duration 68 | rotationAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) 69 | rotationAnimation.fillMode = .forwards 70 | rotationAnimation.isRemovedOnCompletion = false 71 | rotationAnimation.repeatCount = repeatAnimation ? .infinity : 0 72 | self.add(rotationAnimation, forKey: "rotation") 73 | } 74 | 75 | func removeRotationAnimation() { 76 | self.removeAnimation(forKey: "rotation") 77 | } 78 | 79 | func animateShape(path: CGPath, duration: Double) { 80 | let animation = CABasicAnimation(keyPath: "path") 81 | animation.duration = duration 82 | animation.toValue = path 83 | animation.timingFunction = CAMediaTimingFunction(name: .linear) 84 | animation.isRemovedOnCompletion = false 85 | animation.fillMode = .forwards 86 | 87 | self.add(animation, forKey: nil) 88 | } 89 | 90 | func doMask(by imageMask: UIImage) { 91 | let maskLayer = CAShapeLayer() 92 | maskLayer.bounds = CGRect(x: 0, y: 0, width: imageMask.size.width, height: imageMask.size.height) 93 | bounds = maskLayer.bounds 94 | maskLayer.contents = imageMask.cgImage 95 | maskLayer.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height) 96 | mask = maskLayer 97 | } 98 | 99 | func toImage() -> UIImage? { 100 | UIGraphicsBeginImageContextWithOptions(bounds.size, 101 | isOpaque, 102 | UIScreen.main.scale) 103 | guard let context = UIGraphicsGetCurrentContext() else { 104 | UIGraphicsEndImageContext() 105 | return nil 106 | } 107 | render(in: context) 108 | let image = UIGraphicsGetImageFromCurrentImageContext() 109 | UIGraphicsEndImageContext() 110 | return image 111 | } 112 | } 113 | 114 | extension BinaryInteger { 115 | var degreesToRadians: CGFloat { CGFloat(self) * .pi / 180 } 116 | } 117 | -------------------------------------------------------------------------------- /Sources/Switches/Extensions/CGRect+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGRect+Extension.swift 3 | // Switches 4 | // 5 | // Created by Jawad Ali on 05/12/2021. 6 | // Copyright © 2021 Jawad Ali. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | extension CGRect { 11 | var drawOnPath: CGPath { 12 | let bezierPath = UIBezierPath() 13 | 14 | let radius = self.maxX/5 15 | let center = CGPoint(x: self.midX, y: self.midY) 16 | 17 | let x1 = center.x + radius * cos(-45.degreesToRadians) 18 | let y1 = center.y + radius * sin(-45.degreesToRadians) 19 | 20 | let x2 = center.x + radius * cos(135.degreesToRadians) 21 | let y2 = center.y + radius * sin(135.degreesToRadians) 22 | 23 | let x3 = center.x + (radius + radius * 0.5) * cos(-175.degreesToRadians) 24 | let y3 = center.y + (radius + radius * 0.5) * sin(-175.degreesToRadians) 25 | 26 | bezierPath.move(to: CGPoint(x: x3, y: y3)) 27 | bezierPath.addLine(to: CGPoint(x: x2, y: y2)) 28 | 29 | bezierPath.addLine(to: CGPoint(x: x1, y: y1)) 30 | 31 | return bezierPath.cgPath 32 | } 33 | 34 | var drawOffPath: CGPath { 35 | let bezierPath = UIBezierPath() 36 | 37 | let radius = self.maxX/5 38 | let center = CGPoint(x: self.midX, y: self.midY) 39 | 40 | let x1 = center.x + radius * cos(-45.degreesToRadians) 41 | let y1 = center.y + radius * sin(-45.degreesToRadians) 42 | 43 | let x2 = center.x + radius * cos(135.degreesToRadians) 44 | let y2 = center.y + radius * sin(135.degreesToRadians) 45 | 46 | let x3 = center.x + radius * cos(-135.degreesToRadians) 47 | let y3 = center.y + radius * sin(-135.degreesToRadians) 48 | 49 | let x4 = center.x + radius * cos(45.degreesToRadians) 50 | let y4 = center.y + radius * sin(45.degreesToRadians) 51 | 52 | bezierPath.move(to: CGPoint(x: x3, y: y3)) 53 | bezierPath.addLine(to: CGPoint(x: x4, y: y4)) 54 | 55 | bezierPath.move(to: CGPoint(x: x1, y: y1)) 56 | bezierPath.addLine(to: CGPoint(x: x2, y: y2)) 57 | 58 | return bezierPath.cgPath 59 | } 60 | 61 | var bubbleShapePath: CGPath { 62 | let bubblePath = UIBezierPath() 63 | let sR = (size.width - size.height)/4 64 | let lR = size.height/2 65 | 66 | let l1 = CGPoint(x: sR, y: lR - sR) 67 | let l2 = CGPoint(x: sR, y: lR + sR) 68 | 69 | let c1 = CGPoint(x: sR * 2 + lR, y: 0) 70 | let c2 = CGPoint(x: sR * 2 + lR, y: lR * 2) 71 | 72 | let r1 = CGPoint(x: sR * 3 + lR * 2, y: lR - sR) 73 | let r2 = CGPoint(x: sR * 3 + lR * 2, y: lR + sR) 74 | 75 | let o1 = CGPoint(x: (lR + sR * 2)/4, y: lR - sR) 76 | let o2 = CGPoint(x: (lR + sR * 2)/4, y: lR + sR) 77 | let o3 = CGPoint(x: (lR * 2 + sR * 4) - (lR + sR * 2)/4, y: lR - sR) 78 | let o4 = CGPoint(x: (lR * 2 + sR * 4) - (lR + sR * 2)/4, y: lR + sR) 79 | 80 | // let cL = CGPoint(x: sR, y: lR) 81 | let cC = CGPoint(x: sR * 2 + lR, y: lR) 82 | // let cR = CGPoint(x: sR * 3 + lR * 2, y: lR) 83 | 84 | bubblePath.move(to: l1) 85 | bubblePath.addQuadCurve(to: c1, controlPoint: o1) 86 | bubblePath.addArc(withCenter: cC, radius: lR, startAngle: -CGFloat.pi/2, endAngle: CGFloat.pi * 3/2, clockwise: true) 87 | bubblePath.addQuadCurve(to: r1, controlPoint: o3) 88 | bubblePath.addLine(to: r2) 89 | 90 | bubblePath.addQuadCurve(to: c2, controlPoint: o4) 91 | bubblePath.addQuadCurve(to: l2, controlPoint: o2) 92 | bubblePath.addLine(to: l1) 93 | bubblePath.close() 94 | 95 | return bubblePath.cgPath 96 | } 97 | 98 | var drawModeOnPath: CGPath { 99 | let radius = self.maxX/2 100 | let center = CGPoint(x: self.midX, y: self.midY) 101 | 102 | let bezierPath = UIBezierPath() 103 | bezierPath.addArc(withCenter: center, radius: radius, startAngle: 301.degreesToRadians, endAngle: 149.degreesToRadians, clockwise: true) 104 | bezierPath.addArc(withCenter: center, radius: radius, startAngle: 149.degreesToRadians, endAngle: 301.degreesToRadians, clockwise: true) 105 | bezierPath.close() 106 | 107 | return bezierPath.cgPath 108 | } 109 | 110 | var drawModeOffPath: CGPath { 111 | let radius: CGFloat = self.maxX/2 112 | 113 | let centerA = CGPoint(x: self.midX, y: self.midY) 114 | let offset = radius * 1.0/3.0 115 | let centerB = CGPoint(x: centerA.x - offset, y: centerA.y - offset) 116 | 117 | let bezierPath = UIBezierPath() 118 | bezierPath.addArc(withCenter: centerA, radius: radius, startAngle: 301.degreesToRadians, endAngle: 149.degreesToRadians, clockwise: true) 119 | bezierPath.addArc(withCenter: centerB, radius: radius, startAngle: 121.degreesToRadians, endAngle: 329.degreesToRadians, clockwise: false) 120 | bezierPath.close() 121 | 122 | return bezierPath.cgPath 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Sources/Switches/Extensions/UIImage+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+Extension.swift 3 | // testi 4 | // 5 | // Created by Jawad Ali on 29/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | public extension UIImage { 11 | func maskWithColor(color: UIColor) -> UIImage? { 12 | let maskLayer = CALayer() 13 | maskLayer.bounds = CGRect(x: 0, y: 0, width: size.width, height: size.height) 14 | maskLayer.backgroundColor = color.cgColor 15 | maskLayer.doMask(by: self) 16 | let maskImage = maskLayer.toImage() 17 | return maskImage 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Sources/Switches/JDSwitch.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JDSwitch.swift 3 | // testi 4 | // 5 | // Created by Jawad Ali on 22/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | /// `Design by` : Oleg Frolov 10 | // https://dribbble.com/shots/2346044-Switch-on-off 11 | 12 | import UIKit 13 | @IBDesignable open class JDSwitch: BaseControl { 14 | // MARK: - Views 15 | 16 | private lazy var thumbLayer: CALayer = { 17 | let layer = CALayer() 18 | layer.shadowColor = UIColor.black.cgColor 19 | layer.shadowRadius = 2 20 | layer.shadowOpacity = 0.4 21 | layer.shadowOffset = CGSize(width: 0.75, height: 2) 22 | layer.contentsGravity = .resizeAspect 23 | layer.backgroundColor = UIColor.white.cgColor 24 | return layer 25 | }() 26 | 27 | private lazy var trackLayer: CAShapeLayer = { 28 | let shape = CAShapeLayer() 29 | 30 | shape.borderWidth = borderWidth 31 | shape.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1) 32 | return shape 33 | }() 34 | 35 | var changeThumbColor: Bool = false { 36 | didSet { 37 | setThumbColor() 38 | } 39 | } 40 | 41 | private var isTouchDown: Bool = false { 42 | didSet { 43 | layoutSublayers(of: layer) 44 | changeThumbColor = true 45 | } 46 | } 47 | 48 | // MARK: - Public Properties 49 | 50 | public var shape: shapeType = .rounded { 51 | didSet { 52 | layoutSublayers(of: layer) 53 | } 54 | } 55 | 56 | public var borderWidth: CGFloat = 0 { 57 | didSet { 58 | layoutSublayers(of: layer) 59 | } 60 | } 61 | 62 | public var thumbRadiusPadding: CGFloat = 0 { 63 | didSet { 64 | layoutThumbLayer(for: layer.bounds) 65 | } 66 | } 67 | 68 | public var thumbCornerRadius: CGFloat = 0 { 69 | didSet { 70 | layoutThumbLayer(for: layer.bounds) 71 | } 72 | } 73 | 74 | public var thumbTintColor: UIColor? { 75 | didSet { setThumbColor() } 76 | } 77 | 78 | public var onThumbTintColor: UIColor = #colorLiteral(red: 0.5438016653, green: 0.7640405893, blue: 0.291983664, alpha: 1) { 79 | didSet { setThumbColor() } 80 | } 81 | 82 | public var offThumbTintColor: UIColor = #colorLiteral(red: 0.864574194, green: 0.8753482103, blue: 0.848641932, alpha: 1) { 83 | didSet { setThumbColor() } 84 | } 85 | 86 | // MARK: - initializers 87 | 88 | convenience init() { 89 | self.init(frame: .zero) 90 | } 91 | 92 | override init(frame: CGRect) { 93 | super.init(frame: frame) 94 | controlDidLoad() 95 | } 96 | 97 | public required init?(coder aDecoder: NSCoder) { 98 | super.init(coder: aDecoder) 99 | controlDidLoad() 100 | } 101 | 102 | // MARK: - Common Init 103 | 104 | private func controlDidLoad() { 105 | layer.addSublayer(trackLayer) 106 | layer.addSublayer(thumbLayer) 107 | 108 | layer.shadowOffset = .zero 109 | layer.shadowOpacity = 0.3 110 | layer.shadowRadius = 5 111 | backgroundColor = .clear 112 | 113 | setThumbColor() 114 | addTouchHandlers() 115 | } 116 | 117 | final func getThumbSize() -> CGSize { 118 | let height = bounds.height - 2 * (borderWidth + thumbRadiusPadding) 119 | return CGSize(width: height, height: height) 120 | } 121 | 122 | final func getThumbOrigin(for width: CGFloat) -> CGPoint { 123 | let inset = borderWidth + thumbRadiusPadding 124 | if isTouchDown { 125 | let x = !isOn ? bounds.width - width - inset : inset 126 | return CGPoint(x: x, y: inset) 127 | } else { 128 | let x = isOn ? bounds.width - width - inset : inset 129 | return CGPoint(x: x, y: inset) 130 | } 131 | } 132 | 133 | private func getTackSize() -> CGSize { 134 | isTouchDown ? CGSize( 135 | width: bounds.height, 136 | height: bounds.height 137 | ) : bounds.size 138 | } 139 | 140 | private func getTrackOrigin(for width: CGFloat) -> CGPoint { 141 | let inset: CGFloat = 0.0 142 | let x = !isOn ? bounds.width - width : inset 143 | return CGPoint(x: x, y: inset) 144 | } 145 | 146 | override public func layoutSublayers(of layer: CALayer) { 147 | super.layoutSublayers(of: layer) 148 | 149 | layoutTrackLayer(for: layer.bounds) 150 | layoutThumbLayer(for: layer.bounds) 151 | } 152 | } 153 | 154 | // MARK: - layout Layers 155 | 156 | private extension JDSwitch { 157 | func layoutTrackLayer(for bounds: CGRect) { 158 | let size = getTackSize() 159 | let origin = getTrackOrigin(for: size.width) 160 | trackLayer.frame = CGRect(origin: origin, size: size) 161 | 162 | trackLayer.cornerRadius = shape == .rounded ? trackLayer.bounds.height / 2 : 5 163 | } 164 | 165 | func layoutThumbLayer(for bounds: CGRect) { 166 | let size = getThumbSize() 167 | let origin = getThumbOrigin(for: size.width) 168 | thumbLayer.frame = CGRect(origin: origin, size: size) 169 | 170 | shape == .rounded ? (thumbLayer.cornerRadius = size.height / 2) : (thumbLayer.cornerRadius = thumbCornerRadius) 171 | } 172 | } 173 | 174 | // MARK: - Touches 175 | 176 | private extension JDSwitch { 177 | private func addTouchHandlers() { 178 | addTarget(self, action: #selector(touchDown), for: [.touchDown, .touchDragEnter]) 179 | addTarget(self, action: #selector(touchUp), for: [.touchUpInside]) 180 | addTarget(self, action: #selector(touchEnded), for: [.touchDragExit, .touchCancel]) 181 | 182 | let leftSwipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(swipeLeftRight(_:))) 183 | leftSwipeGesture.direction = [.left] 184 | addGestureRecognizer(leftSwipeGesture) 185 | 186 | let rightSwipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(swipeLeftRight(_:))) 187 | rightSwipeGesture.direction = [.right] 188 | addGestureRecognizer(rightSwipeGesture) 189 | } 190 | 191 | @objc 192 | func swipeLeftRight(_ gesture: UISwipeGestureRecognizer) { 193 | let canLeftSwipe = isOn && gesture.direction == .left 194 | let canRightSwipe = !isOn && gesture.direction == .right 195 | guard canLeftSwipe || canRightSwipe else { return } 196 | touchUp() 197 | } 198 | 199 | @objc 200 | func touchDown() { 201 | isTouchDown = true 202 | } 203 | 204 | @objc 205 | func touchUp() { 206 | isOn.toggle() 207 | touchEnded() 208 | } 209 | 210 | @objc 211 | func touchEnded() { 212 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) { 213 | self.isTouchDown = false 214 | } 215 | } 216 | } 217 | 218 | // MARK: - color setting 219 | 220 | extension JDSwitch { 221 | private func setThumbColor() { 222 | if let thumbColor = thumbTintColor { 223 | thumbLayer.backgroundColor = thumbColor.cgColor 224 | } else { 225 | thumbLayer.backgroundColor = (isOn ? onThumbTintColor : offThumbTintColor).cgColor 226 | } 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /Sources/Switches/SDSwitch.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SDSwitch.swift 3 | // testi 4 | // 5 | // Created by Jawad Ali on 22/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | // Crdit design by 10 | /// Mauricio Bucardo 11 | // https://dribbble.com/shots/9841408-Loading-switch-interaction 12 | 13 | /// Alexis Doreau 14 | // https://dribbble.com/shots/3545882-Switch-with-server-calls 15 | 16 | //// George Assan 17 | // https://dribbble.com/shots/5192899-Simple-toggle 18 | 19 | import UIKit 20 | enum LoadingResult { 21 | case success 22 | case failed 23 | } 24 | 25 | public typealias SDSwitchLoadingStarted = () -> Void 26 | 27 | @IBDesignable open class SDSwitch: BaseControl { 28 | open var loadingStarted: SDSwitchLoadingStarted? 29 | 30 | // MARK: - Views 31 | 32 | fileprivate lazy var thumbLayer: CALayer = { 33 | let layer = CALayer() 34 | layer.shadowColor = UIColor.black.cgColor 35 | layer.shadowRadius = 2 36 | layer.shadowOpacity = 0.4 37 | layer.shadowOffset = CGSize(width: 0.75, height: 2) 38 | layer.contentsGravity = .resizeAspect 39 | layer.backgroundColor = UIColor.white.cgColor 40 | return layer 41 | }() 42 | 43 | fileprivate lazy var trackLayer: CALayer = { 44 | let shape = CALayer() 45 | shape.borderWidth = borderWidth 46 | return shape 47 | }() 48 | 49 | private lazy var thumbLoadingCompleteLayer: CAShapeLayer = { 50 | let shape = CAShapeLayer() 51 | shape.lineWidth = thumbRadiusPadding 52 | shape.strokeColor = loadingColor.cgColor 53 | shape.fillColor = UIColor.clear.cgColor 54 | return shape 55 | }() 56 | 57 | private lazy var loadingLayer: CAShapeLayer = { 58 | let shape = CAShapeLayer() 59 | shape.lineWidth = thumbRadiusPadding 60 | shape.strokeColor = loadingColor.cgColor 61 | return shape 62 | }() 63 | 64 | var changeThumbColor: Bool = false { 65 | didSet { 66 | setThumbColor() 67 | } 68 | } 69 | 70 | private var isLoadingCompleted = false 71 | 72 | private var loadingResult: LoadingResult = .success 73 | 74 | fileprivate var isTouchDown: Bool = false { 75 | didSet { 76 | layoutSublayers(of: layer) 77 | changeThumbColor = true 78 | stateDidChange() 79 | } 80 | } 81 | 82 | // MARK: - Public Properties 83 | 84 | public var borderColor: UIColor? { 85 | didSet { setBorderColor() } 86 | } 87 | 88 | public var onBorderColor: UIColor = .white { 89 | didSet { setBorderColor() } 90 | } 91 | 92 | public var offBorderColor: UIColor = .white { 93 | didSet { setBorderColor() } 94 | } 95 | 96 | public var isLoadingEnabled: Bool = false 97 | 98 | public var borderWidth: CGFloat = 2 { 99 | didSet { 100 | trackLayer.borderWidth = borderWidth 101 | layoutSublayers(of: layer) 102 | } 103 | } 104 | 105 | public var thumbRadiusPadding: CGFloat = 4 { 106 | didSet { 107 | layoutSublayers(of: layer) 108 | } 109 | } 110 | 111 | public var thumbCornerRadius: CGFloat = 0 { 112 | didSet { 113 | layoutThumbLayer(for: layer.bounds) 114 | } 115 | } 116 | 117 | public var thumbTintColor: UIColor? { 118 | didSet { setThumbColor() } 119 | } 120 | 121 | public var onThumbTintColor: UIColor = #colorLiteral(red: 0.9999018312, green: 1, blue: 0.9998798966, alpha: 1) { 122 | didSet { setThumbColor() } 123 | } 124 | 125 | public var offThumbTintColor: UIColor = #colorLiteral(red: 0.9999018312, green: 1, blue: 0.9998798966, alpha: 1) { 126 | didSet { setThumbColor() } 127 | } 128 | 129 | public var loadingColor: UIColor = #colorLiteral(red: 0.2855720818, green: 0.8943144679, blue: 0.6083024144, alpha: 1) { 130 | didSet { loadingLayer.strokeColor = loadingColor.cgColor } 131 | } 132 | 133 | public var onTintColor: UIColor = #colorLiteral(red: 0.2855720818, green: 0.8943144679, blue: 0.6083024144, alpha: 1) { 134 | didSet { 135 | trackLayer.backgroundColor = getBackgroundColor() 136 | 137 | setNeedsLayout() 138 | } 139 | } 140 | 141 | public var offTintColor: UIColor = #colorLiteral(red: 0.823615551, green: 0.8911703229, blue: 0.9554550052, alpha: 1) { 142 | didSet { 143 | trackLayer.backgroundColor = getBackgroundColor() 144 | setNeedsLayout() 145 | } 146 | } 147 | 148 | // MARK: - initializers 149 | 150 | convenience init() { 151 | self.init(frame: .zero) 152 | } 153 | 154 | override init(frame: CGRect) { 155 | super.init(frame: frame) 156 | controlDidLoad() 157 | } 158 | 159 | public required init?(coder aDecoder: NSCoder) { 160 | super.init(coder: aDecoder) 161 | controlDidLoad() 162 | } 163 | 164 | // MARK: - Common Init 165 | 166 | fileprivate func controlDidLoad() { 167 | [trackLayer, thumbLayer].forEach(layer.addSublayer) 168 | 169 | layer.shadowColor = UIColor.black.cgColor 170 | layer.shadowOffset = .zero 171 | layer.shadowOpacity = 0.3 172 | layer.shadowRadius = 10 173 | backgroundColor = .clear 174 | 175 | trackLayer.backgroundColor = getBackgroundColor() 176 | setThumbColor() 177 | addTouchHandlers() 178 | } 179 | 180 | fileprivate func getThumbSize() -> CGSize { 181 | let height = bounds.height - 2 * (borderWidth + thumbRadiusPadding) 182 | return CGSize(width: height, height: height) 183 | } 184 | 185 | final func getThumbOrigin(for width: CGFloat) -> CGPoint { 186 | let inset = borderWidth + thumbRadiusPadding 187 | if isTouchDown { 188 | return CGPoint(x: bounds.midX - width/2, y: inset) 189 | } else { 190 | let x = isOn ? bounds.width - width - inset : inset 191 | return CGPoint(x: x, y: inset) 192 | } 193 | } 194 | 195 | fileprivate func getTackSize() -> CGSize { 196 | isTouchDown ? CGSize(width: bounds.height, height: bounds.height) : bounds.size 197 | } 198 | 199 | final func getTrackOrigin(for width: CGFloat) -> CGPoint { 200 | if isTouchDown { 201 | return CGPoint(x: bounds.midX - width/2, y: 0) 202 | } 203 | 204 | let inset: CGFloat = 0.0 205 | let x = !isOn ? bounds.width - width : inset 206 | return CGPoint(x: x, y: inset) 207 | } 208 | 209 | private func stateDidChange() { 210 | trackLayer.backgroundColor = getBackgroundColor() 211 | } 212 | 213 | override public func layoutSublayers(of layer: CALayer) { 214 | super.layoutSublayers(of: layer) 215 | 216 | layoutTrackLayer(for: layer.bounds) 217 | layoutThumbLayer(for: layer.bounds) 218 | layoutLoadingLayer(for: layer.bounds) 219 | 220 | setBorderColor() 221 | } 222 | 223 | @objc public final func loadingCompleted(isSuccessFull: Bool) { 224 | isLoadingCompleted = true 225 | isSuccessFull ? (loadingResult = .success) : (loadingResult = .failed) 226 | if !isSuccessFull { 227 | isOn.toggle() 228 | } 229 | 230 | loadingLayer.removeRotationAnimation() 231 | loadingLayer.removeFromSuperlayer() 232 | 233 | thumbLoadingCompleteLayer.path = (loadingResult == LoadingResult.success) ? thumbLayer.bounds.drawOnPath : thumbLayer.bounds.drawOffPath 234 | thumbLoadingCompleteLayer.lineWidth = thumbRadiusPadding/3 235 | thumbLoadingCompleteLayer.strokeColor = (isSuccessFull ? loadingColor : UIColor.red).cgColor 236 | thumbLoadingCompleteLayer.strokeEnd = 0 237 | thumbLayer.addSublayer(thumbLoadingCompleteLayer) 238 | 239 | CATransaction.setCompletionBlock { 240 | print("completed") 241 | self.isTouchDown = false 242 | self.sendActions(for: .valueChanged) 243 | self.valueChange?(self.isOn) 244 | self.isUserInteractionEnabled = true 245 | } 246 | 247 | CATransaction.begin() 248 | thumbLoadingCompleteLayer.strokeAnimation(duration: 0.3) 249 | CATransaction.commit() 250 | } 251 | } 252 | 253 | // MARK: - layout Layers 254 | 255 | private extension SDSwitch { 256 | func layoutTrackLayer(for bounds: CGRect) { 257 | let size = getTackSize() 258 | let origin = getTrackOrigin(for: size.width) 259 | trackLayer.frame = CGRect(origin: origin, size: size) 260 | trackLayer.cornerRadius = trackLayer.bounds.midY 261 | } 262 | 263 | func layoutThumbLayer(for bounds: CGRect) { 264 | let size = getThumbSize() 265 | let origin = getThumbOrigin(for: size.width) 266 | thumbLayer.frame = CGRect(origin: origin, size: size) 267 | thumbLayer.cornerRadius = thumbLayer.bounds.midY 268 | } 269 | 270 | func layoutLoadingLayer(for bounds: CGRect) { 271 | loadingLayer.lineWidth = thumbRadiusPadding 272 | 273 | let loadingBounds = bounds.insetBy(dx: 0, dy: borderWidth + loadingLayer.lineWidth/2) 274 | let center = CGPoint(x: bounds.midX, y: bounds.midY) 275 | let bezierPath = UIBezierPath(arcCenter: center, radius: bounds.midY, startAngle: -110.degreesToRadians, endAngle: -60.degreesToRadians, clockwise: true) 276 | loadingLayer.frame = loadingBounds 277 | loadingLayer.path = bezierPath.cgPath 278 | } 279 | } 280 | 281 | // MARK: - Touches 282 | 283 | private extension SDSwitch { 284 | private func addTouchHandlers() { 285 | addTarget(self, action: #selector(touchDown), for: [.touchDown, .touchDragEnter]) 286 | addTarget(self, action: #selector(touchUp), for: [.touchUpInside]) 287 | addTarget(self, action: #selector(touchEnded), for: [.touchDragExit, .touchCancel]) 288 | 289 | let leftSwipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(swipeLeftRight(_:))) 290 | leftSwipeGesture.direction = [.left] 291 | addGestureRecognizer(leftSwipeGesture) 292 | 293 | let rightSwipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(swipeLeftRight(_:))) 294 | rightSwipeGesture.direction = [.right] 295 | addGestureRecognizer(rightSwipeGesture) 296 | } 297 | 298 | @objc 299 | func swipeLeftRight(_ gesture: UISwipeGestureRecognizer) { 300 | let canLeftSwipe = isOn && gesture.direction == .left 301 | let canRightSwipe = !isOn && gesture.direction == .right 302 | guard canLeftSwipe || canRightSwipe else { return } 303 | touchUp() 304 | } 305 | 306 | @objc 307 | func touchDown() { 308 | isLoadingCompleted = false 309 | if thumbLayer.sublayers?.contains(thumbLoadingCompleteLayer) ?? false { 310 | thumbLoadingCompleteLayer.removeFromSuperlayer() 311 | } 312 | 313 | isTouchDown = true 314 | } 315 | 316 | @objc 317 | func touchUp() { 318 | isOn.toggle() 319 | touchEnded() 320 | } 321 | 322 | @objc 323 | func touchEnded() { 324 | if isLoadingEnabled, isOn { 325 | performLoading() 326 | 327 | } else { 328 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { 329 | self.isTouchDown = false 330 | self.valueChange?(self.isOn) 331 | } 332 | } 333 | } 334 | } 335 | 336 | // MARK: - color setting 337 | 338 | private extension SDSwitch { 339 | func setThumbColor() { 340 | if let thumbColor = thumbTintColor { 341 | thumbLayer.backgroundColor = thumbColor.cgColor 342 | } else { 343 | thumbLayer.backgroundColor = (isOn ? onThumbTintColor : offThumbTintColor).cgColor 344 | } 345 | } 346 | 347 | func getBackgroundColor() -> CGColor { 348 | return ((isOn && !isTouchDown) ? onTintColor : offTintColor).cgColor 349 | } 350 | 351 | func setBorderColor() { 352 | if let borderClor = borderColor { 353 | trackLayer.borderColor = borderClor.cgColor 354 | } else { 355 | trackLayer.borderColor = (isOn ? onBorderColor : offBorderColor).cgColor 356 | } 357 | } 358 | } 359 | 360 | // MARK: - Loading Animation 361 | 362 | private extension SDSwitch { 363 | func performLoading() { 364 | loadingStarted?() 365 | layer.addSublayer(loadingLayer) 366 | isUserInteractionEnabled = false 367 | loadingLayer.rotateAnimation(angal: 360.degreesToRadians, duration: 1, repeatAnimation: true) 368 | } 369 | } 370 | 371 | ////`Crdit` :-Morph Switch 372 | // https://dribbble.com/shots/2330566-Morph-Switch 373 | @IBDesignable public class YapFullTextSwitch: SDSwitch { 374 | private lazy var contentsLayer = CATextLayer() 375 | 376 | public var onText: String? { 377 | didSet { 378 | layoutTextLayerIfNeeded() 379 | } 380 | } 381 | 382 | public var offText: String? { 383 | didSet { 384 | layoutTextLayerIfNeeded() 385 | } 386 | } 387 | 388 | public var onTextColor: UIColor = .white 389 | 390 | public var offTextColor: UIColor = .white 391 | 392 | override func controlDidLoad() { 393 | super.controlDidLoad() 394 | 395 | thumbLayer.addSublayer(contentsLayer) 396 | thumbRadiusPadding = 0 397 | } 398 | 399 | override func getThumbSize() -> CGSize { 400 | let height = bounds.height - 2 * borderWidth 401 | let width = isTouchDown ? bounds.width - 2 * borderWidth : height 402 | return CGSize(width: width, height: height) 403 | } 404 | 405 | override func getTackSize() -> CGSize { 406 | bounds.size 407 | } 408 | 409 | override func touchEnded() { 410 | if isOn { 411 | contentsLayer.string = onText 412 | contentsLayer.foregroundColor = onTextColor.cgColor 413 | 414 | } else { 415 | contentsLayer.string = offText 416 | contentsLayer.foregroundColor = offTextColor.cgColor 417 | } 418 | 419 | setBorderColor() 420 | trackLayer.backgroundColor = getBackgroundColor() 421 | 422 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { 423 | self.contentsLayer.string = nil 424 | self.isTouchDown = false 425 | self.valueChange?(self.isOn) 426 | } 427 | } 428 | 429 | override public func layoutSublayers(of layer: CALayer) { 430 | super.layoutSublayers(of: layer) 431 | layoutContentLayer(for: bounds) 432 | } 433 | 434 | private func layoutContentLayer(for bounds: CGRect) { 435 | let inset = 2 * borderWidth 436 | let y = bounds.maxY - inset 437 | let leading = borderWidth/2 438 | let x = bounds.maxX - inset 439 | let origin = CGPoint(x: leading, y: leading + bounds.midY/4) 440 | 441 | contentsLayer.frame = CGRect(origin: origin, size: CGSize(width: x, height: y)) 442 | contentsLayer.contentsGravity = .center 443 | contentsLayer.fontSize = bounds.height * 0.4 444 | } 445 | 446 | // MARK: - Content Layers 447 | 448 | private func layoutTextLayerIfNeeded() { 449 | contentsLayer.alignmentMode = .center 450 | contentsLayer.fontSize = bounds.height * 0.4 451 | contentsLayer.font = UIFont.systemFont(ofSize: 10, weight: .bold) 452 | contentsLayer.contentsScale = UIScreen.main.scale 453 | } 454 | } 455 | -------------------------------------------------------------------------------- /Sources/Switches/Switcher.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Switcher.swift 3 | // testi 4 | // 5 | // Created by Jawad Ali on 24/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | /// `Design by` : Oleg Frolov 11 | // https://dribbble.com/shots/3844909-On-Off 12 | // https://dribbble.com/shots/4148855-Switcher-XXXIII 13 | 14 | @IBDesignable public class Switcher: BaseControl { 15 | // MARK: - Properties 16 | 17 | @IBInspectable public var onTintColor: UIColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1) { 18 | didSet { 19 | onThumbLayerColorChange() 20 | } 21 | } 22 | 23 | @IBInspectable public var offTintColor: UIColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1) { 24 | didSet { 25 | offThumbLayerColorChange() 26 | } 27 | } 28 | 29 | // MARK: - Views 30 | 31 | fileprivate lazy var thumbOnLayer: CALayer = { 32 | let layer = CALayer() 33 | layer.contentsGravity = .resizeAspect 34 | layer.backgroundColor = onTintColor.cgColor 35 | return layer 36 | }() 37 | 38 | fileprivate lazy var thumbOffLayer: CALayer = { 39 | let layer = CALayer() 40 | 41 | layer.contentsGravity = .resizeAspect 42 | layer.backgroundColor = offTintColor.cgColor 43 | return layer 44 | }() 45 | 46 | private lazy var trackLayer: CAShapeLayer = { 47 | let shape = CAShapeLayer() 48 | shape.backgroundColor = UIColor.white.cgColor 49 | return shape 50 | }() 51 | 52 | fileprivate var thumbRadiusPadding: CGFloat = 10 53 | fileprivate var duration: Double = 1 54 | fileprivate var isTouchDown: Bool = false { 55 | didSet { 56 | layoutSublayers(of: layer) 57 | } 58 | } 59 | 60 | // MARK: - initializers 61 | 62 | convenience init() { 63 | self.init(frame: .zero) 64 | } 65 | 66 | override init(frame: CGRect) { 67 | super.init(frame: frame) 68 | controlDidLoad() 69 | } 70 | 71 | required init?(coder aDecoder: NSCoder) { 72 | super.init(coder: aDecoder) 73 | controlDidLoad() 74 | } 75 | 76 | fileprivate func controlDidLoad() { 77 | layer.addSublayer(trackLayer) 78 | layer.addSublayer(thumbOffLayer) 79 | layer.addSublayer(thumbOnLayer) 80 | 81 | layer.shadowOffset = .zero 82 | layer.shadowOpacity = 0.3 83 | layer.shadowRadius = 5 84 | layer.masksToBounds = true 85 | 86 | isOn ? (trackLayer.backgroundColor = offTintColor.cgColor) : (trackLayer.backgroundColor = onTintColor.cgColor) 87 | addTouchHandlers() 88 | backgroundColor = .clear 89 | } 90 | 91 | override public func layoutSublayers(of layer: CALayer) { 92 | super.layoutSublayers(of: layer) 93 | layer.cornerRadius = bounds.midY 94 | CATransaction.setAnimationDuration(duration) 95 | CATransaction.begin() 96 | 97 | layoutLayers() 98 | 99 | CATransaction.commit() 100 | } 101 | 102 | fileprivate func layoutLayers() { 103 | layoutTrackLayer(for: layer.bounds) 104 | layoutOnThumbLayer(for: layer.bounds) 105 | layoutOffThumbLayer(for: layer.bounds) 106 | } 107 | 108 | fileprivate func onThumbLayerColorChange() { 109 | thumbOnLayer.backgroundColor = onTintColor.cgColor 110 | isOn ? (trackLayer.backgroundColor = offTintColor.cgColor) : (trackLayer.backgroundColor = onTintColor.cgColor) 111 | } 112 | 113 | fileprivate func offThumbLayerColorChange() { 114 | thumbOffLayer.backgroundColor = offTintColor.cgColor 115 | isOn ? (trackLayer.backgroundColor = offTintColor.cgColor) : (trackLayer.backgroundColor = onTintColor.cgColor) 116 | } 117 | } 118 | 119 | // MARK: - layout Layers 120 | 121 | private extension Switcher { 122 | func layoutTrackLayer(for bounds: CGRect) { 123 | trackLayer.frame = bounds 124 | trackLayer.cornerRadius = trackLayer.bounds.midY 125 | } 126 | 127 | @objc func layoutOnThumbLayer(for bounds: CGRect) { 128 | let size = getOnThumbSize() 129 | let origin = getOnThumbOrigin(for: size.width) 130 | thumbOnLayer.frame = CGRect(origin: origin, size: size) 131 | thumbOnLayer.cornerRadius = size.height/2 132 | } 133 | 134 | @objc func layoutOffThumbLayer(for bounds: CGRect) { 135 | let size = getOffThumbSize() 136 | let origin = getOffThumbOrigin(for: size.width) 137 | thumbOffLayer.frame = CGRect(origin: origin, size: size) 138 | thumbOffLayer.cornerRadius = size.height/2 139 | } 140 | 141 | final func getOnThumbSize() -> CGSize { 142 | let height = bounds.height - 2 * thumbRadiusPadding 143 | return (isTouchDown && !isOn) ? CGSize(width: bounds.maxX * 1.5, height: bounds.maxY * 2) : CGSize(width: height, height: height) 144 | } 145 | 146 | final func getOffThumbSize() -> CGSize { 147 | let height = bounds.height - 2 * thumbRadiusPadding 148 | return (isTouchDown && isOn) ? CGSize(width: bounds.maxX * 1.5, height: bounds.maxY * 2) : CGSize(width: height, height: height) 149 | } 150 | 151 | final func getOnThumbOrigin(for width: CGFloat) -> CGPoint { 152 | let inset = thumbRadiusPadding 153 | return (isTouchDown && !isOn) ? CGPoint(x: -bounds.maxX/3, y: -bounds.midY * 0.8) : CGPoint(x: bounds.width - width - inset, y: inset) 154 | } 155 | 156 | final func getOffThumbOrigin(for width: CGFloat) -> CGPoint { 157 | let inset = thumbRadiusPadding 158 | return (isTouchDown && isOn) ? CGPoint(x: -bounds.maxX/3, y: -bounds.midY * 0.8) : CGPoint(x: inset, y: inset) 159 | } 160 | } 161 | 162 | private extension Switcher { 163 | private func addTouchHandlers() { 164 | addTarget(self, action: #selector(touchDown), for: [.touchDown, .touchDragEnter]) 165 | addTarget(self, action: #selector(touchUp), for: [.touchUpInside]) 166 | addTarget(self, action: #selector(touchEnded), for: [.touchDragExit, .touchCancel]) 167 | 168 | let leftSwipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(swipeLeftRight(_:))) 169 | leftSwipeGesture.direction = [.left] 170 | addGestureRecognizer(leftSwipeGesture) 171 | 172 | let rightSwipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(swipeLeftRight(_:))) 173 | rightSwipeGesture.direction = [.right] 174 | addGestureRecognizer(rightSwipeGesture) 175 | } 176 | 177 | @objc 178 | func swipeLeftRight(_ gesture: UISwipeGestureRecognizer) { 179 | let canLeftSwipe = isOn && gesture.direction == .left 180 | let canRightSwipe = !isOn && gesture.direction == .right 181 | guard canLeftSwipe || canRightSwipe else { return } 182 | touchUp() 183 | } 184 | 185 | @objc 186 | func touchDown() { 187 | isUserInteractionEnabled = false 188 | 189 | UIView.animate(withDuration: 0.2, 190 | animations: { 191 | self.transform = CGAffineTransform(scaleX: 0.95, y: 0.95) 192 | }) 193 | } 194 | 195 | @objc 196 | func touchUp() { 197 | isOn.toggle() 198 | touchEnded() 199 | } 200 | 201 | @objc 202 | func touchEnded() { 203 | isTouchDown = true 204 | 205 | UIView.animate(withDuration: duration/3, 206 | animations: { 207 | self.transform = CGAffineTransform(scaleX: 1.05, y: 1.05) 208 | }, 209 | completion: { _ in 210 | UIView.animate(withDuration: self.duration/3) { 211 | self.transform = CGAffineTransform.identity 212 | } 213 | }) 214 | 215 | DispatchQueue.main.asyncAfter(deadline: .now() + duration) { 216 | self.isOn ? (self.trackLayer.backgroundColor = self.offTintColor.cgColor) : (self.trackLayer.backgroundColor = self.onTintColor.cgColor) 217 | 218 | self.isOn ? (self.thumbOffLayer.areAnimationsEnabled = false) : (self.thumbOnLayer.areAnimationsEnabled = false) 219 | self.sendActions(for: .valueChanged) 220 | 221 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { 222 | self.isUserInteractionEnabled = true 223 | 224 | self.isTouchDown = false 225 | self.isOn ? self.thumbOffLayer.bringToFront() : self.thumbOnLayer.bringToFront() 226 | self.isOn ? (self.thumbOffLayer.areAnimationsEnabled = true) : (self.thumbOnLayer.areAnimationsEnabled = true) 227 | } 228 | } 229 | } 230 | } 231 | 232 | @IBDesignable public class SwitcherFullStrtech: Switcher { 233 | @IBInspectable public var thumbTintColor: UIColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1) { 234 | didSet { 235 | ThumbLayerColorChange() 236 | } 237 | } 238 | 239 | private lazy var interactionLayer: CALayer = { 240 | let shape = CALayer() 241 | shape.backgroundColor = thumbTintColor.cgColor 242 | return shape 243 | 244 | }() 245 | 246 | override func controlDidLoad() { 247 | super.controlDidLoad() 248 | layer.addSublayer(interactionLayer) 249 | 250 | thumbOnLayer.bringToFront() 251 | thumbOffLayer.bringToFront() 252 | duration = 0.5 253 | thumbRadiusPadding = 0 254 | 255 | layer.masksToBounds = false 256 | } 257 | 258 | override func layoutLayers() { 259 | super.layoutLayers() 260 | layoutInteractionLayerLayer(for: bounds) 261 | } 262 | 263 | override func layoutOnThumbLayer(for bounds: CGRect) { 264 | let percent: CGFloat = bounds.maxY * 0.2 265 | 266 | thumbOnLayer.frame = CGRect(x: bounds.midY - percent/2, y: bounds.midY - percent/2, width: percent, height: percent) 267 | thumbOnLayer.cornerRadius = thumbOnLayer.bounds.midY 268 | } 269 | 270 | override func layoutOffThumbLayer(for bounds: CGRect) { 271 | let heightPercent: CGFloat = bounds.maxY * 0.4 272 | let widthPercent: CGFloat = bounds.maxY * 0.12 273 | 274 | thumbOffLayer.frame = CGRect(x: bounds.maxX - bounds.midY - widthPercent/2, y: bounds.midY - heightPercent/2, width: widthPercent, height: heightPercent) 275 | thumbOffLayer.cornerRadius = thumbOffLayer.bounds.midX 276 | } 277 | 278 | override fileprivate func onThumbLayerColorChange() { 279 | thumbOnLayer.backgroundColor = onTintColor.cgColor 280 | } 281 | 282 | override fileprivate func offThumbLayerColorChange() { 283 | thumbOffLayer.backgroundColor = offTintColor.cgColor 284 | } 285 | 286 | override func touchEnded() { 287 | isTouchDown = true 288 | DispatchQueue.main.asyncAfter(deadline: .now() + duration) { 289 | self.isTouchDown = false 290 | self.isUserInteractionEnabled = true 291 | 292 | let onTint = self.onTintColor 293 | self.onTintColor = self.offTintColor 294 | self.offTintColor = onTint 295 | } 296 | } 297 | 298 | func ThumbLayerColorChange() { 299 | interactionLayer.backgroundColor = thumbTintColor.cgColor 300 | } 301 | } 302 | 303 | extension SwitcherFullStrtech { 304 | func layoutInteractionLayerLayer(for bounds: CGRect) { 305 | interactionLayer.cornerRadius = bounds.midY 306 | 307 | isTouchDown ? (interactionLayer.frame = bounds) : (isOn ? (interactionLayer.frame = CGRect(x: bounds.maxX - bounds.maxY, y: 0, width: bounds.maxY, height: bounds.maxY)) : (interactionLayer.frame = CGRect(x: 0, y: 0, width: bounds.maxY, height: bounds.maxY))) 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /Sources/Switches/YapBaseSwitch.swift: -------------------------------------------------------------------------------- 1 | // 2 | // YapBaseSwitch.swift 3 | // testi 4 | // 5 | // Created by Jawad Ali on 13/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | //// `Design by`: Oleg Frolov 10 | // https://dribbble.com/shots/2028065-Switcher-lll 11 | 12 | import UIKit 13 | 14 | @IBDesignable 15 | open class YapBaseSwitch: BaseControl { 16 | // MARK: - Property 17 | 18 | @IBInspectable open var animateDuration: Double = 0.4 19 | 20 | internal var on: Bool = false 21 | internal var sizeScale: CGFloat { 22 | return min(self.bounds.width, self.bounds.height) / 100.0 23 | } 24 | 25 | override open var frame: CGRect { 26 | didSet { 27 | guard frame.size != oldValue.size else { return } 28 | resetView() 29 | } 30 | } 31 | 32 | override open var bounds: CGRect { 33 | didSet { 34 | guard frame.size != oldValue.size else { return } 35 | resetView() 36 | } 37 | } 38 | 39 | // MARK: - Getter 40 | 41 | public final func setOn(_ on: Bool, animate: Bool = true) { 42 | guard on != isOn else { return } 43 | toggleValue() 44 | } 45 | 46 | // MARK: - Init 47 | 48 | public convenience init() { 49 | self.init(frame: CGRect(x: 0, y: 0, width: 80, height: 40)) 50 | } 51 | 52 | override public init(frame: CGRect) { 53 | super.init(frame: frame) 54 | setUpView() 55 | } 56 | 57 | public required init?(coder aDecoder: NSCoder) { 58 | super.init(coder: aDecoder) 59 | setUpView() 60 | } 61 | 62 | // MARK: - Internal 63 | 64 | internal func resetView() { 65 | gestureRecognizers?.forEach(removeGestureRecognizer) 66 | layer.sublayers?.forEach { $0.removeFromSuperlayer() } 67 | setUpView() 68 | } 69 | 70 | internal func setUpView() { 71 | let tap = UITapGestureRecognizer(target: self, action: #selector(YapBaseSwitch.toggleValue)) 72 | addGestureRecognizer(tap) 73 | 74 | for view in subviews { 75 | view.removeFromSuperview() 76 | } 77 | } 78 | 79 | @objc internal func toggleValue() { 80 | on.toggle() 81 | isOn.toggle() 82 | valueChange?(isOn) 83 | sendActions(for: .valueChanged) 84 | changeValueAnimate(isOn, duration: animateDuration) 85 | } 86 | 87 | internal func changeValueAnimate(_ value: Bool, duration: Double) {} 88 | } 89 | -------------------------------------------------------------------------------- /Sources/Switches/YapLiquidSwitch.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TKLiquidSwitch.swift 3 | // testi 4 | // 5 | // Created by Jawad Ali on 13/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // Dedign by Oleg Frolov 12 | // https://dribbble.com/shots/2028065-Switcher-lll 13 | 14 | @IBDesignable 15 | open class YapLiquidSwitch: YapBaseSwitch { 16 | private var bubbleLayer = CAShapeLayer() 17 | private var lineLayer = CAShapeLayer() 18 | @IBInspectable open var onColor = UIColor(red: 0.373, green: 0.843, blue: 0.596, alpha: 1) { 19 | didSet { 20 | resetView() 21 | } 22 | } 23 | 24 | @IBInspectable open var offColor = UIColor(red: 0.871, green: 0.871, blue: 0.871, alpha: 1) { 25 | didSet { 26 | resetView() 27 | } 28 | } 29 | 30 | override internal func setUpView() { 31 | super.setUpView() 32 | 33 | clipsToBounds = true 34 | 35 | lineLayer.path = UIBezierPath(roundedRect: CGRect(x: 0, y: (bounds.height - 20 * sizeScale)/2, width: bounds.width, height: 20 * sizeScale), cornerRadius: 10 * sizeScale).cgPath 36 | lineLayer.fillColor = switchColor(isOn).cgColor 37 | layer.addSublayer(lineLayer) 38 | 39 | bubbleLayer.frame = bounds 40 | bubbleLayer.position = bubblePosition(isOn) 41 | bubbleLayer.path = CGRect(origin: .zero, size: bubbleSize).bubbleShapePath 42 | bubbleLayer.fillColor = switchColor(isOn).cgColor 43 | layer.addSublayer(bubbleLayer) 44 | } 45 | 46 | override func changeValueAnimate(_ value: Bool, duration: Double) { 47 | let bubbleTransformAnim = CAKeyframeAnimation(keyPath: "transform") 48 | bubbleTransformAnim.values = [NSValue(caTransform3D: CATransform3DIdentity), 49 | NSValue(caTransform3D: CATransform3DMakeScale(1, 0.8, 1)), 50 | NSValue(caTransform3D: CATransform3DMakeScale(0.8, 1, 1)), 51 | NSValue(caTransform3D: CATransform3DIdentity)] 52 | bubbleTransformAnim.keyTimes = [NSNumber(value: 0), NSNumber(value: 1.0/3.0), NSNumber(value: 2.0/3.0), NSNumber(value: 1)] 53 | bubbleTransformAnim.duration = duration 54 | 55 | let bubblePositionAnim = CABasicAnimation(keyPath: "position") 56 | bubblePositionAnim.fromValue = NSValue(cgPoint: bubblePosition(!isOn)) 57 | bubblePositionAnim.toValue = NSValue(cgPoint: bubblePosition(isOn)) 58 | bubblePositionAnim.duration = duration 59 | 60 | let bubbleGroupAnim = CAAnimationGroup() 61 | bubbleGroupAnim.animations = [bubbleTransformAnim, bubblePositionAnim] 62 | bubbleGroupAnim.isRemovedOnCompletion = false 63 | bubbleGroupAnim.fillMode = CAMediaTimingFillMode.forwards 64 | bubbleGroupAnim.duration = duration 65 | 66 | bubbleLayer.add(bubbleGroupAnim, forKey: "Bubble") 67 | 68 | let color = switchColor(value).cgColor 69 | UIView.animate(withDuration: duration, animations: { () -> Void in 70 | self.lineLayer.fillColor = color 71 | self.bubbleLayer.fillColor = color 72 | }) 73 | } 74 | } 75 | 76 | // Getter 77 | extension YapLiquidSwitch { 78 | var bubbleSize: CGSize { 79 | let lineH = 20 * sizeScale 80 | let w = lineH * 2 + bounds.height 81 | let h = bounds.height 82 | return CGSize(width: w, height: h) 83 | } 84 | 85 | func switchColor(_ isOn: Bool) -> UIColor { 86 | if isOn { 87 | return onColor 88 | } else { 89 | return offColor 90 | } 91 | } 92 | 93 | func bubblePosition(_ isOn: Bool) -> CGPoint { 94 | let h = bounds.height 95 | let w = bounds.width 96 | let bW = bubbleSize.width 97 | 98 | if isOn { 99 | return CGPoint(x: bW * 0.8, y: h/2) 100 | } else { 101 | return CGPoint(x: w - bW * 0.2, y: h/2) 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Sources/Switches/YapSmileSwitch.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SmileSwitch.swift 3 | // testi 4 | // 5 | // Created by Jawad Ali on 13/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // Dedign by Oleg Frolov 12 | // https://dribbble.com/shots/2011284-Switcher-ll 13 | 14 | open class YapSmileSwitch: YapBaseSwitch { 15 | // MARK: - Poperty 16 | 17 | private var smileFace: TKSmileFaceView? 18 | 19 | // MARK: - Init 20 | 21 | override internal func setUpView() { 22 | super.setUpView() 23 | backgroundColor = .clear 24 | let frame = CGRect(x: bounds.width - bounds.height, y: 0, width: bounds.height, height: bounds.height) 25 | smileFace = TKSmileFaceView(frame: frame) 26 | addSubview(smileFace!) 27 | } 28 | 29 | override open func draw(_ rect: CGRect) { 30 | let ctx = UIGraphicsGetCurrentContext() 31 | let lineWidth = 10 * sizeScale 32 | let path = UIBezierPath(roundedRect: rect.insetBy(dx: lineWidth, dy: lineWidth), cornerRadius: rect.width/2) 33 | ctx?.setLineWidth(lineWidth * 2) 34 | ctx?.addPath(path.cgPath) 35 | UIColor(white: 0.9, alpha: 1).setStroke() 36 | ctx?.strokePath() 37 | } 38 | 39 | override func changeValueAnimate(_ value: Bool, duration: Double) { 40 | let x = value ? 0 : (bounds.width - bounds.height) 41 | let frame = CGRect(x: x, y: 0, width: bounds.height, height: bounds.height) 42 | smileFace!.faceType = value ? TKSmileFaceView.FaceType.happy : TKSmileFaceView.FaceType.sad 43 | smileFace?.rotation(animateDuration, count: 2, clockwise: !isOn) 44 | UIView.animate(withDuration: duration, animations: { () -> Void in 45 | self.smileFace?.frame = frame 46 | }, completion: { complete -> Void in 47 | if complete { 48 | self.smileFace?.eyeWinkAnimate(duration/2) 49 | } 50 | }) 51 | } 52 | } 53 | 54 | // 脸 55 | private class TKSmileFaceView: UIView { 56 | enum FaceType { 57 | case happy 58 | case sad 59 | func toColor() -> UIColor { 60 | switch self { 61 | case .happy: 62 | return UIColor(red: 0.388, green: 0.839, blue: 0.608, alpha: 1.000) 63 | case .sad: 64 | return UIColor(red: 0.843, green: 0.369, blue: 0.373, alpha: 1) 65 | } 66 | } 67 | } 68 | 69 | // MARK: - Property 70 | 71 | let rightEye = CAShapeLayer() 72 | let leftEye = CAShapeLayer() 73 | let mouth = CAShapeLayer() 74 | let face = CAShapeLayer() 75 | 76 | var faceType: FaceType = .sad { 77 | didSet { 78 | let position: CGFloat = isHappy ? 20 * sizeScale : 35 * sizeScale 79 | mouth.path = mouthPath.cgPath 80 | mouth.frame = CGRect(x: 0, y: position, width: 60 * sizeScale, height: 20 * sizeScale) 81 | face.fillColor = faceType.toColor().cgColor 82 | } 83 | } 84 | 85 | // MARK: - Getter 86 | 87 | var isHappy: Bool { 88 | return (faceType == .happy) 89 | } 90 | 91 | var sizeScale: CGFloat { 92 | return min(bounds.width, bounds.height)/100.0 93 | } 94 | 95 | var mouthPath: UIBezierPath { 96 | let point: CGFloat = isHappy ? 70 * sizeScale : 10 97 | let path = UIBezierPath() 98 | path.move(to: CGPoint(x: 30 * sizeScale, y: 40 * sizeScale)) 99 | path.addCurve(to: CGPoint(x: 70 * sizeScale, y: 40 * sizeScale), controlPoint1: CGPoint(x: 30 * sizeScale, y: 40 * sizeScale), controlPoint2: CGPoint(x: 50 * sizeScale, y: point)) 100 | path.lineCapStyle = .round 101 | return path 102 | } 103 | 104 | // MARK: - Init 105 | 106 | override init(frame: CGRect) { 107 | super.init(frame: frame) 108 | setupLayers() 109 | } 110 | 111 | required init?(coder aDecoder: NSCoder) { 112 | super.init(coder: aDecoder) 113 | setupLayers() 114 | } 115 | 116 | // MARK: - Private Func 117 | 118 | private func setupLayers() { 119 | let facePath = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 100 * sizeScale, height: 100 * sizeScale)) 120 | let eyePath = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 20 * sizeScale, height: 20 * sizeScale)) 121 | 122 | face.frame = CGRect(x: 0, y: 0, width: 100 * sizeScale, height: 100 * sizeScale) 123 | face.path = facePath.cgPath 124 | face.fillColor = faceType.toColor().cgColor 125 | layer.addSublayer(face) 126 | 127 | leftEye.frame = CGRect(x: 20 * sizeScale, y: 28 * sizeScale, width: 10 * sizeScale, height: 10 * sizeScale) 128 | leftEye.path = eyePath.cgPath 129 | leftEye.fillColor = UIColor.white.cgColor 130 | layer.addSublayer(leftEye) 131 | 132 | rightEye.frame = CGRect(x: 60 * sizeScale, y: 28 * sizeScale, width: 10 * sizeScale, height: 10 * sizeScale) 133 | rightEye.path = eyePath.cgPath 134 | rightEye.fillColor = UIColor.white.cgColor 135 | layer.addSublayer(rightEye) 136 | 137 | mouth.path = mouthPath.cgPath 138 | mouth.strokeColor = UIColor.white.cgColor 139 | mouth.fillColor = UIColor.clear.cgColor 140 | mouth.lineCap = .round 141 | mouth.lineWidth = 10 * sizeScale 142 | layer.addSublayer(mouth) 143 | 144 | faceType = .sad 145 | } 146 | 147 | // MARK: - Animate 148 | 149 | func eyeWinkAnimate(_ duration: Double) { 150 | let eyeleftTransformAnim = CAKeyframeAnimation(keyPath: "transform") 151 | eyeleftTransformAnim.values = [NSValue(caTransform3D: CATransform3DIdentity), 152 | NSValue(caTransform3D: CATransform3DMakeScale(1, 0.1, 1)), 153 | NSValue(caTransform3D: CATransform3DIdentity)] 154 | eyeleftTransformAnim.keyTimes = [0, 0.5, 1] 155 | eyeleftTransformAnim.duration = duration 156 | leftEye.add(eyeleftTransformAnim, forKey: "Wink") 157 | rightEye.add(eyeleftTransformAnim, forKey: "Wink") 158 | } 159 | 160 | func rotation(_ duration: Double, count: Int, clockwise: Bool) { 161 | let rotationTransformAnim = CAKeyframeAnimation(keyPath: "transform.rotation.z") 162 | rotationTransformAnim.values = [0, 180 * CGFloat.pi/180 * CGFloat(count) * (clockwise ? 1 : -1)] 163 | rotationTransformAnim.keyTimes = [0, 1] 164 | rotationTransformAnim.isRemovedOnCompletion = false 165 | rotationTransformAnim.duration = duration 166 | layer.add(rotationTransformAnim, forKey: "Rotation") 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /Sources/Switches/YapSwitch.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Switch.swift 3 | // testi 4 | // 5 | // Created by Jawad Ali on 13/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | //// `Design by`: Anupam 10 | // https://dribbble.com/shots/4908263-Switch-UI 11 | 12 | //// `Design by` : Maria Kovalevich 13 | // https://dribbble.com/shots/4685786-Daily-UI-015-On-Off-Switch-clickable-prototype 14 | import Foundation 15 | import UIKit 16 | public enum shapeType { 17 | case square 18 | case rounded 19 | } 20 | 21 | @IBDesignable public class YapSwitch: BaseControl { 22 | // MARK: - Properties 23 | 24 | //// corner radius of thumbnail in case of square 25 | //// It has no effect if shape is rounded 26 | 27 | public var thumbCornerRadius: CGFloat = 0 { 28 | didSet { 29 | layoutThumbLayer(for: layer.bounds) 30 | } 31 | } 32 | 33 | //// if stretch is enable .. on touch down thumbnail increase its width.... 34 | public var isStretchEnable: Bool = true 35 | 36 | /// `shape` of your switch ... it can either be rounded or square .. you can set it accordingly 37 | 38 | public var shape: shapeType = .rounded { 39 | didSet { 40 | layoutSublayers(of: layer) 41 | } 42 | } 43 | 44 | /// Width of the border... it can have any `float` value 45 | public var borderWidth: CGFloat = 0 { 46 | didSet { 47 | trackLayer.borderWidth = borderWidth 48 | layoutSublayers(of: layer) 49 | } 50 | } 51 | 52 | public var borderColor: UIColor? { 53 | didSet { setBorderColor() } 54 | } 55 | 56 | public var onBorderColor: UIColor = .white { 57 | didSet { setBorderColor() } 58 | } 59 | 60 | public var offBorderColor: UIColor = .white { 61 | didSet { setBorderColor() } 62 | } 63 | 64 | public var textColor: UIColor? { 65 | didSet { 66 | (offContentLayer as? CATextLayer)?.foregroundColor = textColor?.cgColor 67 | (onContentLayer as? CATextLayer)?.foregroundColor = textColor?.cgColor 68 | } 69 | } 70 | 71 | public var onTextColor: UIColor = .white { 72 | didSet { 73 | (onContentLayer as? CATextLayer)?.foregroundColor = onTextColor.cgColor 74 | } 75 | } 76 | 77 | public var offTextColor: UIColor = .white { 78 | didSet { 79 | (offContentLayer as? CATextLayer)?.foregroundColor = offTextColor.cgColor 80 | } 81 | } 82 | 83 | public var trackTopBottomPadding: CGFloat = 0 { 84 | didSet { 85 | layoutSublayers(of: layer) 86 | } 87 | } 88 | 89 | public var contentLeadingTrailingPadding: CGFloat = 0 { 90 | didSet { 91 | layoutSublayers(of: layer) 92 | } 93 | } 94 | 95 | //// Distance of `thumb` from track layer 96 | public var thumbRadiusPadding: CGFloat = 3.5 { 97 | didSet { 98 | layoutThumbLayer(for: layer.bounds) 99 | } 100 | } 101 | 102 | public var onTintColor = UIColor(red: 0, green: 122/255, blue: 1, alpha: 1) { 103 | didSet { 104 | trackLayer.backgroundColor = getBackgroundColor() 105 | 106 | setNeedsLayout() 107 | } 108 | } 109 | 110 | public var offTintColor: UIColor = .white { 111 | didSet { 112 | trackLayer.backgroundColor = getBackgroundColor() 113 | setNeedsLayout() 114 | } 115 | } 116 | 117 | public var thumbTintColor: UIColor? { 118 | didSet { setThumbColor() } 119 | } 120 | 121 | public var onThumbTintColor: UIColor = .white { 122 | didSet { setThumbColor() } 123 | } 124 | 125 | public var offThumbTintColor: UIColor = .white { 126 | didSet { setThumbColor() } 127 | } 128 | 129 | public var onText: String? { 130 | didSet { 131 | addOnTextLayerIfNeeded() 132 | (onContentLayer as? CATextLayer)?.string = onText 133 | } 134 | } 135 | 136 | public var offText: String? { 137 | didSet { 138 | addOffTextLayerIfNeeded() 139 | (offContentLayer as? CATextLayer)?.string = offText 140 | } 141 | } 142 | 143 | public var onThumbImage: UIImage? { 144 | didSet { 145 | thumbLayer.contents = onThumbImage?.cgImage 146 | } 147 | } 148 | 149 | public var offThumbImage: UIImage? { 150 | didSet { 151 | thumbLayer.contents = offThumbImage?.cgImage 152 | } 153 | } 154 | 155 | public var thumbImage: UIImage? { 156 | didSet { 157 | thumbLayer.contents = thumbImage?.cgImage 158 | } 159 | } 160 | 161 | public var onImage: UIImage? { 162 | didSet { 163 | addOnImageLayerIfNeeded() 164 | onContentLayer?.contents = onImage?.cgImage 165 | } 166 | } 167 | 168 | public var offImage: UIImage? { 169 | didSet { 170 | addOffImageLayerIfNeeded() 171 | offContentLayer?.contents = offImage?.cgImage 172 | } 173 | } 174 | 175 | override public var intrinsicContentSize: CGSize { 176 | return CGSize(width: 52, height: 31) 177 | } 178 | 179 | // MARK: - Layers 180 | 181 | //// `trackLayer`:- is main track layer 182 | //// `innerLayer`:- over track layer 183 | ////`thumLayer` :- it is used for thumb 184 | 185 | fileprivate lazy var trackLayer = CALayer() 186 | fileprivate lazy var innerLayer = CALayer() 187 | fileprivate lazy var thumbLayer: CALayer = { 188 | let layer = CALayer() 189 | layer.shadowColor = UIColor.black.cgColor 190 | layer.shadowRadius = 2 191 | layer.shadowOpacity = 0.4 192 | layer.shadowOffset = CGSize(width: 0.75, height: 2) 193 | layer.contentsGravity = .resizeAspect 194 | return layer 195 | }() 196 | 197 | private lazy var contentsLayer = CALayer() 198 | 199 | private var onContentLayer: CALayer? { 200 | willSet { 201 | onContentLayer?.removeFromSuperlayer() 202 | } 203 | didSet { 204 | layoutOnContentLayer(for: layer.bounds) 205 | } 206 | } 207 | 208 | private var offContentLayer: CALayer? { 209 | willSet { 210 | offContentLayer?.removeFromSuperlayer() 211 | } 212 | didSet { 213 | layoutOffContentLayer(for: layer.bounds) 214 | } 215 | } 216 | 217 | fileprivate var isTouchDown: Bool = false 218 | 219 | // MARK: - initializers 220 | 221 | convenience init() { 222 | self.init(frame: .zero) 223 | frame.size = intrinsicContentSize 224 | } 225 | 226 | override init(frame: CGRect) { 227 | super.init(frame: frame) 228 | controlDidLoad() 229 | } 230 | 231 | required init?(coder aDecoder: NSCoder) { 232 | super.init(coder: aDecoder) 233 | controlDidLoad() 234 | } 235 | 236 | // MARK: - Common Init 237 | 238 | fileprivate func controlDidLoad() { 239 | layer.shadowOffset = .zero 240 | layer.shadowOpacity = 0.3 241 | layer.shadowRadius = 5 242 | 243 | backgroundColor = .clear 244 | 245 | layer.addSublayer(trackLayer) 246 | layer.addSublayer(innerLayer) 247 | layer.addSublayer(contentsLayer) 248 | layer.addSublayer(thumbLayer) 249 | 250 | trackLayer.backgroundColor = getBackgroundColor() 251 | setBorderColor() 252 | trackLayer.borderWidth = borderWidth 253 | 254 | innerLayer.backgroundColor = UIColor.white.cgColor 255 | 256 | contentsLayer.masksToBounds = true 257 | 258 | setThumbColor() 259 | addTouchHandlers() 260 | layoutSublayers(of: layer) 261 | } 262 | 263 | // MARK: - layoutSubviews 264 | 265 | override public func layoutSublayers(of layer: CALayer) { 266 | super.layoutSublayers(of: layer) 267 | 268 | layoutTrackLayer(for: layer.bounds) 269 | layoutThumbLayer(for: layer.bounds) 270 | contentsLayer.frame = layer.bounds 271 | layoutOffContentLayer(for: layer.bounds) 272 | layoutOnContentLayer(for: layer.bounds) 273 | } 274 | 275 | override public func didMoveToSuperview() { 276 | layoutSublayers(of: layer) 277 | } 278 | 279 | fileprivate func layoutTrackLayer(for bounds: CGRect) { 280 | trackLayer.frame = bounds.insetBy(dx: trackTopBottomPadding, dy: trackTopBottomPadding) 281 | shape == .rounded ? (trackLayer.cornerRadius = trackLayer.bounds.height/2) : (trackLayer.cornerRadius = bounds.height * 0.12) 282 | } 283 | 284 | fileprivate func layoutInnerLayer(for bounds: CGRect) { 285 | let inset = borderWidth + trackTopBottomPadding 286 | let isInnerHidden = isOn || (isTouchDown && isStretchEnable) 287 | 288 | innerLayer.frame = isInnerHidden 289 | ? CGRect(origin: trackLayer.position, size: .zero) 290 | : bounds.insetBy(dx: inset, dy: inset) 291 | 292 | shape == .rounded ? (innerLayer.cornerRadius = isInnerHidden ? 0 : bounds.height/2 - inset) : (innerLayer.cornerRadius = isInnerHidden ? 5 : 5) 293 | } 294 | 295 | fileprivate func layoutThumbLayer(for bounds: CGRect) { 296 | let size = getThumbSize() 297 | let origin = getThumbOrigin(for: size.width) 298 | thumbLayer.frame = CGRect(origin: origin, size: size) 299 | 300 | if let thumb = thumbImage { 301 | onThumbImage = thumb 302 | offThumbImage = thumb 303 | } 304 | 305 | thumbLayer.contents = isOn ? onThumbImage?.cgImage : offThumbImage?.cgImage 306 | 307 | shape == .rounded ? (thumbLayer.cornerRadius = size.height/2) : (thumbLayer.cornerRadius = thumbCornerRadius) 308 | } 309 | 310 | private func layoutOffContentLayer(for bounds: CGRect) { 311 | let size = getContentLayerSize(for: offContentLayer) 312 | let y = bounds.midY - size.height/2 313 | let leading = (bounds.maxX - (contentLeadingTrailingPadding + borderWidth + getThumbSize().width))/2 - size.width/2 314 | let x = !isOn ? bounds.width - size.width - leading : bounds.width 315 | let origin = CGPoint(x: x, y: y) 316 | offContentLayer?.frame = CGRect(origin: origin, size: size) 317 | bounds.height < 50 ? ((offContentLayer as? CATextLayer)?.fontSize = 12) : ((offContentLayer as? CATextLayer)?.fontSize = bounds.height * 0.2) 318 | } 319 | 320 | private func layoutOnContentLayer(for bounds: CGRect) { 321 | let size = getContentLayerSize(for: onContentLayer) 322 | let y = bounds.midY - size.height/2 323 | let leading = (bounds.maxX - (contentLeadingTrailingPadding + borderWidth + getThumbSize().width))/2 - size.width/2 324 | let x = isOn ? leading : -bounds.width/2 325 | let origin = CGPoint(x: x, y: y) 326 | onContentLayer?.frame = CGRect(origin: origin, size: size) 327 | onContentLayer?.contentsCenter = CGRect(origin: origin, size: size) 328 | bounds.height < 50 ? ((onContentLayer as? CATextLayer)?.fontSize = 12) : ((onContentLayer as? CATextLayer)?.fontSize = bounds.height * 0.2) 329 | } 330 | 331 | fileprivate func stateDidChange() { 332 | trackLayer.backgroundColor = getBackgroundColor() 333 | trackLayer.borderWidth = borderWidth 334 | thumbLayer.contents = isOn ? onThumbImage?.cgImage : offThumbImage?.cgImage 335 | setThumbColor() 336 | sendActions(for: .valueChanged) 337 | valueChange?(isOn) 338 | } 339 | 340 | public func setOn(_ on: Bool, animated: Bool) { 341 | CATransaction.begin() 342 | CATransaction.setDisableActions(!animated) 343 | isOn = on 344 | layoutSublayers(of: layer) 345 | sendActions(for: .valueChanged) 346 | valueChange?(isOn) 347 | CATransaction.commit() 348 | } 349 | 350 | // MARK: - Touches 351 | 352 | private func addTouchHandlers() { 353 | addTarget(self, action: #selector(touchDown), for: [.touchDown, .touchDragEnter]) 354 | addTarget(self, action: #selector(touchUp), for: [.touchUpInside]) 355 | addTarget(self, action: #selector(touchEnded), for: [.touchDragExit, .touchCancel]) 356 | 357 | let leftSwipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(swipeLeftRight(_:))) 358 | leftSwipeGesture.direction = [.left] 359 | addGestureRecognizer(leftSwipeGesture) 360 | 361 | let rightSwipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(swipeLeftRight(_:))) 362 | rightSwipeGesture.direction = [.right] 363 | addGestureRecognizer(rightSwipeGesture) 364 | } 365 | 366 | @objc 367 | private func swipeLeftRight(_ gesture: UISwipeGestureRecognizer) { 368 | let canLeftSwipe = isOn && gesture.direction == .left 369 | let canRightSwipe = !isOn && gesture.direction == .right 370 | guard canLeftSwipe || canRightSwipe else { return } 371 | touchUp() 372 | } 373 | 374 | @objc 375 | fileprivate func touchDown() { 376 | print("touch down") 377 | isTouchDown = true 378 | layoutSublayers(of: layer) 379 | } 380 | 381 | @objc 382 | fileprivate func touchUp() { 383 | isOn.toggle() 384 | stateDidChange() 385 | touchEnded() 386 | } 387 | 388 | @objc 389 | fileprivate func touchEnded() { 390 | isTouchDown = false 391 | layoutSublayers(of: layer) 392 | } 393 | 394 | // MARK: - Layout Helper 395 | 396 | fileprivate func setBorderColor() { 397 | if let borderClor = borderColor { 398 | trackLayer.borderColor = borderClor.cgColor 399 | } else { 400 | trackLayer.borderColor = (isOn ? onBorderColor : offBorderColor).cgColor 401 | } 402 | } 403 | 404 | private func setThumbColor() { 405 | if let thumbColor = thumbTintColor { 406 | thumbLayer.backgroundColor = thumbColor.cgColor 407 | } else { 408 | thumbLayer.backgroundColor = (isOn ? onThumbTintColor : offThumbTintColor).cgColor 409 | } 410 | } 411 | 412 | final func getBackgroundColor() -> CGColor { 413 | return (isOn ? onTintColor : offTintColor).cgColor 414 | } 415 | 416 | fileprivate func getThumbSize() -> CGSize { 417 | let height = bounds.height - 2 * (borderWidth + thumbRadiusPadding) 418 | let width = (isTouchDown && isStretchEnable) ? height * 1.2 : height 419 | return CGSize(width: width, height: height) 420 | } 421 | 422 | final func getThumbOrigin(for width: CGFloat) -> CGPoint { 423 | let inset = borderWidth + thumbRadiusPadding 424 | let x = isOn ? bounds.width - width - inset : inset 425 | return CGPoint(x: x, y: inset) 426 | } 427 | 428 | final func getContentLayerSize(for layer: CALayer?) -> CGSize { 429 | let inset = 2 * (borderWidth + trackTopBottomPadding) 430 | let diameter = bounds.height - inset - getThumbSize().height/2 431 | if let textLayer = layer as? CATextLayer { 432 | return textLayer.preferredFrameSize() 433 | } 434 | return CGSize(width: diameter, height: diameter) 435 | } 436 | 437 | // MARK: - Content Layers 438 | 439 | private func addOffTextLayerIfNeeded() { 440 | guard offText != nil else { 441 | offContentLayer = nil 442 | return 443 | } 444 | let textLayer = CATextLayer() 445 | textLayer.alignmentMode = .center 446 | textLayer.fontSize = bounds.height * 0.2 447 | textLayer.font = UIFont.systemFont(ofSize: 10, weight: .bold) 448 | 449 | textLayer.foregroundColor = (textColor == nil) ? offTextColor.cgColor : textColor?.cgColor 450 | textLayer.contentsScale = UIScreen.main.scale 451 | contentsLayer.addSublayer(textLayer) 452 | offContentLayer = textLayer 453 | } 454 | 455 | private func addOnTextLayerIfNeeded() { 456 | guard onText != nil else { 457 | onContentLayer = nil 458 | return 459 | } 460 | let textLayer = CATextLayer() 461 | textLayer.alignmentMode = .center 462 | textLayer.fontSize = bounds.height * 0.2 463 | textLayer.font = UIFont.systemFont(ofSize: 10, weight: .bold) 464 | textLayer.foregroundColor = (textColor == nil) ? onTextColor.cgColor : textColor?.cgColor 465 | textLayer.contentsScale = UIScreen.main.scale 466 | contentsLayer.addSublayer(textLayer) 467 | onContentLayer = textLayer 468 | } 469 | 470 | private func addOnImageLayerIfNeeded() { 471 | guard onImage != nil else { 472 | onContentLayer = nil 473 | return 474 | } 475 | let imageLayer = CALayer() 476 | imageLayer.contentsGravity = .center 477 | imageLayer.contentsScale = UIScreen.main.scale 478 | contentsLayer.addSublayer(imageLayer) 479 | onContentLayer = imageLayer 480 | } 481 | 482 | private func addOffImageLayerIfNeeded() { 483 | guard offImage != nil else { 484 | offContentLayer = nil 485 | return 486 | } 487 | let imageLayer = CALayer() 488 | imageLayer.contentsGravity = .resizeAspect 489 | imageLayer.contentsScale = UIScreen.main.scale 490 | contentsLayer.addSublayer(imageLayer) 491 | offContentLayer = imageLayer 492 | } 493 | } 494 | 495 | // https://dribbble.com/shots/2158763-simple-toggle 496 | @IBDesignable public class YapSwitchSlim: YapSwitch { 497 | public var slimTrack: CGFloat = 0 { 498 | didSet { 499 | layoutSublayers(of: layer) 500 | } 501 | } 502 | 503 | override func layoutTrackLayer(for bounds: CGRect) { 504 | trackLayer.frame = bounds.insetBy(dx: trackTopBottomPadding, dy: trackTopBottomPadding + slimTrack) 505 | shape == .rounded ? (trackLayer.cornerRadius = trackLayer.bounds.height/2) : (trackLayer.cornerRadius = bounds.height * 0.2) 506 | } 507 | 508 | override func layoutInnerLayer(for bounds: CGRect) { 509 | let inset = borderWidth + trackTopBottomPadding 510 | let isInnerHidden = isOn || (isTouchDown && isStretchEnable) 511 | 512 | innerLayer.frame = isInnerHidden 513 | ? CGRect(origin: trackLayer.position, size: .zero) 514 | : trackLayer.frame.insetBy(dx: inset, dy: inset) 515 | 516 | shape == .rounded ? (innerLayer.cornerRadius = isInnerHidden ? 0 : innerLayer.bounds.height/2 - inset) : (innerLayer.cornerRadius = isInnerHidden ? 5 : 5) 517 | } 518 | } 519 | 520 | ////`Credit`: Robin 521 | // https://dribbble.com/shots/2603107-toggle-switch-micro-interaction 522 | 523 | // MARK: - YapGradientSwitch 524 | 525 | public enum GradientPosition { 526 | case vertical 527 | case horizatal 528 | } 529 | 530 | @IBDesignable public class YapGradientSwitch: YapSwitch { 531 | private lazy var onGradientLayer = CAGradientLayer() 532 | private lazy var offGradientLayer = CAGradientLayer() 533 | 534 | public var gradientPosition: GradientPosition = .horizatal { 535 | didSet { 536 | setGradientPosition() 537 | layoutSublayers(of: layer) 538 | } 539 | } 540 | 541 | public var onColors: [UIColor] = [UIColor.red, UIColor.yellow] { 542 | didSet { 543 | onGradientLayer.colors = onColors.map { $0.cgColor } 544 | } 545 | } 546 | 547 | public var offColors: [UIColor] = [UIColor.red, UIColor.yellow] { 548 | didSet { 549 | offGradientLayer.colors = offColors.map { $0.cgColor } 550 | } 551 | } 552 | 553 | func createGradientLayer() { 554 | trackLayer.addSublayer(onGradientLayer) 555 | innerLayer.addSublayer(offGradientLayer) 556 | setGradientPosition() 557 | } 558 | 559 | private func setGradientPosition() { 560 | if gradientPosition == .horizatal { 561 | onGradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5) 562 | onGradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5) 563 | 564 | offGradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5) 565 | offGradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5) 566 | } else { 567 | onGradientLayer.startPoint = CGPoint(x: 0.5, y: 0.0) 568 | onGradientLayer.endPoint = CGPoint(x: 0.5, y: 1.0) 569 | 570 | offGradientLayer.startPoint = CGPoint(x: 0.5, y: 0.0) 571 | offGradientLayer.endPoint = CGPoint(x: 0.5, y: 1.0) 572 | } 573 | } 574 | 575 | override func controlDidLoad() { 576 | super.controlDidLoad() 577 | createGradientLayer() 578 | } 579 | 580 | override public func layoutSublayers(of layer: CALayer) { 581 | super.layoutSublayers(of: layer) 582 | layoutInnerLayer(for: bounds) 583 | 584 | onGradientLayer.frame = trackLayer.bounds 585 | offGradientLayer.frame = trackLayer.bounds 586 | 587 | innerLayer.masksToBounds = true 588 | 589 | onGradientLayer.cornerRadius = trackLayer.cornerRadius 590 | offGradientLayer.cornerRadius = innerLayer.cornerRadius 591 | } 592 | } 593 | 594 | private protocol AnimationPath { 595 | func drawOnPath(_ group: CGRect) -> CGPath 596 | func drawOffPath(_ group: CGRect) -> CGPath 597 | } 598 | 599 | //// `Credit Design` : Andrei Mironov 600 | // https://dribbble.com/shots/2309834-Yet-another-toggle-animation 601 | 602 | // MARK: - YapAnimatedSwitch 603 | 604 | @IBDesignable public class YapAnimatedSwitch: YapSwitch { 605 | public var animationColor: UIColor? { 606 | didSet { setShapeColor() } 607 | } 608 | 609 | public var onAnimationColor: UIColor = .white { 610 | didSet { setShapeColor() } 611 | } 612 | 613 | public var offAnimationColor: UIColor = .white { 614 | didSet { setShapeColor() } 615 | } 616 | 617 | public var animationWidth: CGFloat = 3 { 618 | didSet { shapeLayer.lineWidth = animationWidth } 619 | } 620 | 621 | private lazy var shapeLayer: CAShapeLayer = { 622 | let shape = CAShapeLayer() 623 | shape.fillColor = UIColor.clear.cgColor 624 | shape.lineCap = .round 625 | shape.lineWidth = animationWidth 626 | return shape 627 | }() 628 | 629 | public let duration: Double = 0.6 630 | 631 | override func controlDidLoad() { 632 | super.controlDidLoad() 633 | 634 | isStretchEnable = false 635 | thumbLayer.addSublayer(shapeLayer) 636 | setShapeColor() 637 | DispatchQueue.main.async { 638 | self.layoutThumbLayer(for: self.bounds) 639 | self.shapeLayer.path = self.isOn ? self.thumbLayer.bounds.drawOnPath : self.thumbLayer.bounds.drawOffPath 640 | } 641 | } 642 | 643 | private func setShapeColor() { 644 | shapeLayer.strokeColor = (animationColor ?? (isOn ? onAnimationColor : offAnimationColor)).cgColor 645 | } 646 | 647 | override public func layoutSublayers(of layer: CALayer) { 648 | CATransaction.setAnimationDuration(duration) 649 | CATransaction.begin() 650 | super.layoutSublayers(of: layer) 651 | CATransaction.commit() 652 | 653 | shapeLayer.frame = thumbLayer.bounds 654 | } 655 | 656 | override func stateDidChange() { 657 | super.stateDidChange() 658 | isOn ? shapeLayer.animateShape(path: thumbLayer.bounds.drawOnPath, duration: duration - duration/2) : shapeLayer.animateShape(path: thumbLayer.bounds.drawOffPath, duration: duration - duration/2) 659 | isOn ? shapeLayer.rotateAnimation(angal: 0, duration: duration) : shapeLayer.rotateAnimation(angal: .pi/2, duration: duration) 660 | setShapeColor() 661 | } 662 | } 663 | 664 | /// `Design by` : tomatree 665 | // https://dribbble.com/shots/2484722-Daily-Ui-Day-15-On-Off-Switch 666 | 667 | // MARK: - YapModeSwitch 668 | 669 | @IBDesignable public class YapModeSwitch: YapSwitch { 670 | private lazy var shapeLayer: CAShapeLayer = { 671 | let shape = CAShapeLayer() 672 | shape.strokeColor = UIColor.clear.cgColor 673 | 674 | return shape 675 | }() 676 | 677 | private let duration: Double = 0.6 678 | 679 | override public func layoutSublayers(of layer: CALayer) { 680 | CATransaction.setAnimationDuration(duration) 681 | CATransaction.begin() 682 | super.layoutSublayers(of: layer) 683 | CATransaction.commit() 684 | 685 | shapeLayer.fillColor = thumbTintColor?.cgColor 686 | shapeLayer.frame = thumbLayer.bounds 687 | thumbLayer.backgroundColor = UIColor.clear.cgColor 688 | } 689 | 690 | override func controlDidLoad() { 691 | super.controlDidLoad() 692 | 693 | isStretchEnable = false 694 | thumbLayer.addSublayer(shapeLayer) 695 | 696 | DispatchQueue.main.async { 697 | self.shapeLayer.path = self.isOn ? self.thumbLayer.bounds.drawModeOffPath : self.thumbLayer.bounds.drawModeOnPath 698 | } 699 | } 700 | 701 | override func stateDidChange() { 702 | super.stateDidChange() 703 | isOn ? shapeLayer.animateShape(path: thumbLayer.bounds.drawModeOffPath, duration: duration - duration/1.5) : shapeLayer.animateShape(path: thumbLayer.bounds.drawModeOnPath, duration: duration - duration/1.5) 704 | } 705 | } 706 | 707 | @IBDesignable public class YapDarkAndLightModeSwitch: YapSwitch { 708 | public let duration: Double = 0.6 709 | 710 | private lazy var shapeLayer: CAShapeLayer = { 711 | let shape = CAShapeLayer() 712 | shape.strokeColor = UIColor.clear.cgColor 713 | return shape 714 | }() 715 | 716 | private lazy var maskLayer = CAShapeLayer() 717 | 718 | override public func layoutSublayers(of layer: CALayer) { 719 | CATransaction.setAnimationDuration(duration) 720 | CATransaction.begin() 721 | super.layoutSublayers(of: layer) 722 | CATransaction.commit() 723 | 724 | maskLayer.frame = thumbLayer.bounds 725 | 726 | shapeLayer.frame = thumbLayer.bounds 727 | shapeLayer.path = drawSunShape(thumbLayer.bounds) 728 | shapeLayer.mask = maskLayer 729 | thumbLayer.backgroundColor = thumbTintColor?.cgColor 730 | } 731 | 732 | override func controlDidLoad() { 733 | super.controlDidLoad() 734 | 735 | isStretchEnable = false 736 | thumbLayer.addSublayer(shapeLayer) 737 | 738 | DispatchQueue.main.async { 739 | self.isOn ? (self.maskLayer.path = self.drawOnPath(self.thumbLayer.bounds)) : (self.maskLayer.path = self.drawOffPath(self.thumbLayer.bounds)) 740 | } 741 | thumbLayer.masksToBounds = true 742 | } 743 | 744 | override func stateDidChange() { 745 | super.stateDidChange() 746 | isOn ? maskLayer.animateShape(path: drawOnPath(thumbLayer.bounds), duration: duration) : maskLayer.animateShape(path: drawOffPath(thumbLayer.bounds), duration: duration) 747 | 748 | isOn ? thumbLayer.rotateAnimation(angal: 45.degreesToRadians, duration: duration) : thumbLayer.rotateAnimation(angal: 0, duration: duration) 749 | 750 | isOn ? animateFillColor(onTintColor) : animateFillColor(offTintColor) 751 | } 752 | 753 | private func animateFillColor(_ color: UIColor) { 754 | CATransaction.begin() 755 | CATransaction.setAnimationDuration(duration/2) 756 | shapeLayer.fillColor = color.cgColor 757 | CATransaction.commit() 758 | } 759 | } 760 | 761 | extension YapDarkAndLightModeSwitch: AnimationPath { 762 | private func drawSunShape(_ group: CGRect) -> CGPath { 763 | let bezierPath = UIBezierPath() 764 | let radius = group.maxX/2 765 | let center = CGPoint(x: group.midX, y: group.midY) 766 | bezierPath.addArc(withCenter: center, radius: radius, startAngle: -.pi/2, endAngle: 3 * .pi * 0.5, clockwise: true) 767 | return bezierPath.cgPath 768 | } 769 | 770 | func setMask(isFullSize: Bool) -> CGPath { 771 | var radius = thumbLayer.bounds.maxY/2 772 | let center = CGPoint(x: thumbLayer.bounds.midX, y: thumbLayer.bounds.midY) 773 | 774 | let x1 = center.x + radius * cos(180.degreesToRadians) 775 | let y1 = center.y + radius * sin(180.degreesToRadians) 776 | 777 | let bezierPath = UIBezierPath() 778 | 779 | isFullSize ? (radius = 0) : (radius += 0) 780 | 781 | bezierPath.addArc(withCenter: CGPoint(x: x1, y: y1), radius: radius, startAngle: -90.degreesToRadians, endAngle: 90.degreesToRadians, clockwise: true) 782 | 783 | return bezierPath.cgPath 784 | } 785 | 786 | func drawOnPath(_ group: CGRect) -> CGPath { 787 | return setMask(isFullSize: false) 788 | } 789 | 790 | func drawOffPath(_ group: CGRect) -> CGPath { 791 | return setMask(isFullSize: true) 792 | } 793 | } 794 | 795 | @IBDesignable public class YapFullStretchSwitch: YapSwitch { 796 | override func getThumbSize() -> CGSize { 797 | let height = bounds.height - 2 * (borderWidth + thumbRadiusPadding) 798 | let width = (isTouchDown && isStretchEnable) ? bounds.width - 2 * (borderWidth + thumbRadiusPadding) : height 799 | return CGSize(width: width, height: height) 800 | } 801 | } 802 | 803 | @IBDesignable public class YapHalfStretchSwitch: YapSwitch { 804 | override func getThumbSize() -> CGSize { 805 | let height = bounds.height - 2 * (borderWidth + thumbRadiusPadding) 806 | let width = (isTouchDown && isStretchEnable) ? bounds.midX + bounds.midX/2 - 2 * (borderWidth + thumbRadiusPadding) : height 807 | return CGSize(width: width, height: height) 808 | } 809 | 810 | override public func layoutSublayers(of layer: CALayer) { 811 | super.layoutSublayers(of: layer) 812 | if shape == .square { 813 | thumbLayer.cornerRadius = bounds.height * 0.1 814 | thumbLayer.shadowOffset = .zero 815 | } 816 | } 817 | } 818 | -------------------------------------------------------------------------------- /Switches.podspec: -------------------------------------------------------------------------------- 1 | 2 | Pod::Spec.new do |s| 3 | s.name = "Switches" 4 | s.version = "3.0.0" 5 | s.summary = "Switches framework" 6 | s.description = <<-DESC 7 | Switches Pod can create number of beautiful customised switch confirming from UIControl with awesome animations 8 | DESC 9 | s.homepage = "https://github.com/jwd-ali/IOS-Portfolio" 10 | s.license = "MIT" 11 | # s.license = { :type => "MIT", :file => "FILE_LICENSE" } 12 | s.authors = { "Jawad Ali" => "L060214@gmail.com" } 13 | s.platforms = { :ios => "9.0" } 14 | s.source = { :git => "https://github.com/jwd-ali/Switches.git", :tag => "#{s.version}" } 15 | 16 | s.source_files = "Sources/**/*.{h,m,swift}" 17 | s.swift_version = "5.0" 18 | end 19 | -------------------------------------------------------------------------------- /Switches.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Switches.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Switches.xcodeproj/xcshareddata/xcschemes/Switches.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 57 | 58 | 59 | 60 | 62 | 63 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /Switches.xcodeproj/xcshareddata/xcschemes/SwitchesDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /Switches.xcodeproj/xcuserdata/jawadali.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /Switches.xcodeproj/xcuserdata/jawadali.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Switches.xcscheme_^#shared#^_ 8 | 9 | isShown 10 | 11 | orderHint 12 | 1 13 | 14 | SwitchesDemo.xcscheme_^#shared#^_ 15 | 16 | isShown 17 | 18 | orderHint 19 | 0 20 | 21 | 22 | SuppressBuildableAutocreation 23 | 24 | E30C48B024FAE6930049D5C3 25 | 26 | primary 27 | 28 | 29 | E30C48B924FAE6930049D5C3 30 | 31 | primary 32 | 33 | 34 | E30C48E824FAE73F0049D5C3 35 | 36 | primary 37 | 38 | 39 | E30C48FD24FAE7420049D5C3 40 | 41 | primary 42 | 43 | 44 | E30C490824FAE7420049D5C3 45 | 46 | primary 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /SwitchesDemo/Application/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // testi 4 | // 5 | // Created by Jawad Ali on 11/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Switches 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | // Override point for customization after application launch. 18 | return true 19 | } 20 | 21 | // MARK: UISceneSession Lifecycle 22 | 23 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 24 | // Called when a new scene session is being created. 25 | // Use this method to select a configuration to create the new scene with. 26 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 27 | } 28 | 29 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 30 | // Called when the user discards a scene session. 31 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 32 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 33 | } 34 | 35 | 36 | } 37 | 38 | -------------------------------------------------------------------------------- /SwitchesDemo/Application/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // testi 4 | // 5 | // Created by Jawad Ali on 11/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 12 | 13 | var window: UIWindow? 14 | 15 | 16 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 17 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 18 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 19 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 20 | guard let _ = (scene as? UIWindowScene) else { return } 21 | } 22 | 23 | func sceneDidDisconnect(_ scene: UIScene) { 24 | // Called as the scene is being released by the system. 25 | // This occurs shortly after the scene enters the background, or when its session is discarded. 26 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 27 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 28 | } 29 | 30 | func sceneDidBecomeActive(_ scene: UIScene) { 31 | // Called when the scene has moved from an inactive state to an active state. 32 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 33 | } 34 | 35 | func sceneWillResignActive(_ scene: UIScene) { 36 | // Called when the scene will move from an active state to an inactive state. 37 | // This may occur due to temporary interruptions (ex. an incoming phone call). 38 | } 39 | 40 | func sceneWillEnterForeground(_ scene: UIScene) { 41 | // Called as the scene transitions from the background to the foreground. 42 | // Use this method to undo the changes made on entering the background. 43 | } 44 | 45 | func sceneDidEnterBackground(_ scene: UIScene) { 46 | // Called as the scene transitions from the foreground to the background. 47 | // Use this method to save data, release shared resources, and store enough scene-specific state information 48 | // to restore the scene back to its current state. 49 | } 50 | 51 | 52 | } 53 | 54 | -------------------------------------------------------------------------------- /SwitchesDemo/Controllers/AnimatedSwitchesViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AimatedSwitchedViewController.swift 3 | // testi 4 | // 5 | // Created by Jawad Ali on 21/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Switches 11 | class AnimatedSwitchesViewController: UIViewController { 12 | 13 | @IBOutlet weak var animatedStyleA: YapAnimatedSwitch! { 14 | didSet { 15 | animatedStyleA.thumbRadiusPadding = 6 16 | animatedStyleA.offThumbTintColor = #colorLiteral(red: 0.9540559649, green: 0.8972979188, blue: 0.8275271654, alpha: 1) 17 | animatedStyleA.onThumbTintColor = #colorLiteral(red: 0.4935741425, green: 0.5258994699, blue: 0.9827957749, alpha: 1) 18 | animatedStyleA.onTintColor = .white 19 | animatedStyleA.offTintColor = .white 20 | animatedStyleA.tintColor = .white 21 | } 22 | } 23 | @IBOutlet weak var animatedStyleB: YapAnimatedSwitch! { 24 | didSet { 25 | animatedStyleB.thumbRadiusPadding = 6 26 | animatedStyleB.thumbTintColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1) 27 | animatedStyleB.animationWidth = 5 28 | animatedStyleB.onTintColor = #colorLiteral(red: 0.2841374278, green: 0.651584208, blue: 0.2687274218, alpha: 1) 29 | animatedStyleB.isOn = true 30 | animatedStyleB.offTintColor = #colorLiteral(red: 0.1686089635, green: 0.1686392426, blue: 0.1686022878, alpha: 1) 31 | animatedStyleB.onAnimationColor = #colorLiteral(red: 0.2841374278, green: 0.651584208, blue: 0.2687274218, alpha: 1) 32 | animatedStyleB.offAnimationColor = #colorLiteral(red: 0.1686089635, green: 0.1686392426, blue: 0.1686022878, alpha: 1) 33 | } 34 | } 35 | override func viewDidLoad() { 36 | title = "Animated Switches" 37 | super.viewDidLoad() 38 | 39 | // Do any additional setup after loading the view. 40 | } 41 | 42 | 43 | } 44 | -------------------------------------------------------------------------------- /SwitchesDemo/Controllers/CollectionViewCell/CollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CollectionViewCell.swift 3 | // testi 4 | // 5 | // Created by Jawad Ali on 27/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Switches 11 | class CollectionViewCell: UICollectionViewCell { 12 | 13 | private var switchv : BaseControl! { 14 | didSet{ 15 | switchv.translatesAutoresizingMaskIntoConstraints = false 16 | } 17 | } 18 | 19 | //MARK: binding with controller to perform segue 20 | 21 | var performSgue : ((_ segue:String)-> Void)? 22 | 23 | @IBOutlet weak var displaySwitch: UIView! { 24 | didSet { 25 | displaySwitch.layer.shadowOffset = .zero 26 | displaySwitch.layer.shadowOpacity = 0.15 27 | displaySwitch.layer.shadowRadius = 7 28 | displaySwitch.layer.cornerRadius = 10 29 | } 30 | } 31 | 32 | @IBOutlet weak var collectionName: UILabel! 33 | @IBOutlet weak var viewAllButton: UIButton! 34 | @IBOutlet weak var holdingView: UIView! { 35 | didSet { 36 | holdingView.layer.shadowOffset = .zero 37 | holdingView.layer.shadowOpacity = 0.15 38 | holdingView.layer.shadowRadius = 7 39 | holdingView.layer.cornerRadius = 10 40 | } 41 | } 42 | 43 | private var type : SwitchType! 44 | 45 | override func awakeFromNib() { 46 | super.awakeFromNib() 47 | } 48 | 49 | override func layoutSubviews() { 50 | super.layoutSubviews() 51 | viewAllButton.layer.cornerRadius = viewAllButton.bounds.midY 52 | } 53 | 54 | @IBAction func viewAllTapped(_ sender: UIButton) { 55 | 56 | performSgue?(type.rawValue) 57 | 58 | } 59 | 60 | 61 | func config(type: SwitchType) { 62 | self.type = type 63 | collectionName.text = type.rawValue 64 | 65 | switchv = type.coverSwitch 66 | displaySwitch.addSubview(switchv) 67 | 68 | if type == SwitchType.loading { 69 | (self.switchv as? SDSwitch)?.loadingStarted = { 70 | DispatchQueue.main.asyncAfter(deadline: .now() + 3) { 71 | 72 | (self.switchv as? SDSwitch)?.loadingCompleted(isSuccessFull: true) 73 | } 74 | } 75 | 76 | } 77 | 78 | switchv.topAnchor.constraint(equalTo: displaySwitch.topAnchor).isActive = true 79 | switchv.bottomAnchor.constraint(equalTo: displaySwitch.bottomAnchor).isActive = true 80 | switchv.leadingAnchor.constraint(equalTo: displaySwitch.leadingAnchor).isActive = true 81 | switchv.trailingAnchor.constraint(equalTo: displaySwitch.trailingAnchor).isActive = true 82 | 83 | switchv.addTarget(self, action: #selector(changedSwitchVal), for: .valueChanged) 84 | 85 | } 86 | 87 | @objc func changedSwitchVal(_ sender: BaseControl) { 88 | print(sender.isOn) 89 | 90 | } 91 | 92 | override func prepareForReuse() { 93 | super.prepareForReuse() 94 | 95 | switchv.removeFromSuperview() 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /SwitchesDemo/Controllers/GradientSwitchesViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GradientSwitchesViewController.swift 3 | // testi 4 | // 5 | // Created by Jawad Ali on 23/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Switches 11 | class GradientSwitchesViewController: UIViewController { 12 | 13 | private lazy var gradientLayerVertical = CAGradientLayer() 14 | private lazy var gradientLayerHorizantal = CAGradientLayer() 15 | 16 | @IBOutlet weak var gradientSwitchHorizantal: YapGradientSwitch! { 17 | didSet { 18 | gradientSwitchHorizantal.gradientPosition = .horizatal 19 | gradientSwitchHorizantal.onColors = [#colorLiteral(red: 0.2394029498, green: 0.8576775193, blue: 0.6665194631, alpha: 1),#colorLiteral(red: 0.04418379813, green: 0.5335158706, blue: 0.5292400718, alpha: 1)] 20 | gradientSwitchHorizantal.offColors = [#colorLiteral(red: 0.1861540377, green: 0.2199867666, blue: 0.2542595267, alpha: 1),#colorLiteral(red: 0.1861540377, green: 0.2199867666, blue: 0.2542595267, alpha: 1)] 21 | 22 | gradientSwitchHorizantal.onText = "ON" 23 | gradientSwitchHorizantal.offText = "OFF" 24 | } 25 | } 26 | @IBOutlet weak var gradientSwitchVertical: YapGradientSwitch!{ 27 | didSet { 28 | gradientSwitchVertical.borderWidth = 2 29 | gradientSwitchVertical.gradientPosition = .vertical 30 | gradientSwitchVertical.onColors = [#colorLiteral(red: 0.722638309, green: 0.1586903036, blue: 0.2223222256, alpha: 1),#colorLiteral(red: 0.1921568662, green: 0.007843137719, blue: 0.09019608051, alpha: 1)] 31 | gradientSwitchVertical.offColors = [#colorLiteral(red: 0.1042157337, green: 0.4639918208, blue: 0.2866171598, alpha: 1),#colorLiteral(red: 0.1294117719, green: 0.1414276541, blue: 0.06666667014, alpha: 1)] 32 | 33 | gradientSwitchVertical.onText = "START" 34 | gradientSwitchVertical.offText = "STOP" 35 | } 36 | } 37 | override func viewDidLoad() { 38 | super.viewDidLoad() 39 | 40 | gradientSwitchVertical.superview?.layer.insertSublayer(gradientLayerVertical, at: 0) 41 | gradientLayerVertical.colors = gradientSwitchVertical.offColors.map{$0.withAlphaComponent(0.7).cgColor} 42 | 43 | gradientSwitchHorizantal.superview?.layer.insertSublayer(gradientLayerHorizantal, at: 0) 44 | gradientLayerHorizantal.colors = gradientSwitchHorizantal.offColors.map{$0.withAlphaComponent(0.3).cgColor} 45 | 46 | gradientSwitchVertical.addTarget(self, action: #selector(switchVerticalToggle(_:)), for: .valueChanged) 47 | gradientSwitchHorizantal.addTarget(self, action: #selector(switchHorizantalToggle(_:)), for: .valueChanged) 48 | 49 | title = "Gradient Switches" 50 | 51 | 52 | // Do any additional setup after loading the view. 53 | } 54 | 55 | 56 | 57 | override func viewDidLayoutSubviews() { 58 | gradientLayerVertical.frame = gradientSwitchVertical.superview?.bounds ?? .zero 59 | gradientLayerHorizantal.frame = gradientSwitchHorizantal.superview?.bounds ?? .zero 60 | } 61 | 62 | @objc func switchVerticalToggle(_ sender: YapDarkAndLightModeSwitch) { 63 | 64 | sender.isOn ? gradientLayerVertical.animateGradientColors(from: gradientSwitchVertical.offColors.map{$0.withAlphaComponent(0.7).cgColor}, to: gradientSwitchVertical.onColors.map{$0.withAlphaComponent(0.7).cgColor}, duration: 0.5) : gradientLayerVertical.animateGradientColors(from: gradientSwitchVertical.onColors.map{$0.withAlphaComponent(0.7).cgColor}, to: gradientSwitchVertical.offColors.map{$0.withAlphaComponent(0.7).cgColor}, duration: 0.5) 65 | 66 | 67 | 68 | } 69 | @objc func switchHorizantalToggle(_ sender: YapDarkAndLightModeSwitch) { 70 | 71 | sender.isOn ? gradientLayerHorizantal.animateGradientColors(from: gradientSwitchHorizantal.offColors.map{$0.withAlphaComponent(0.3).cgColor}, to: gradientSwitchHorizantal.onColors.map{$0.withAlphaComponent(0.3).cgColor}, duration: 0.5) : gradientLayerHorizantal.animateGradientColors(from: gradientSwitchHorizantal.onColors.map{$0.withAlphaComponent(0.3).cgColor}, to: gradientSwitchHorizantal.offColors.map{$0.withAlphaComponent(0.3).cgColor}, duration: 0.5) 72 | 73 | 74 | 75 | } 76 | 77 | 78 | 79 | } 80 | -------------------------------------------------------------------------------- /SwitchesDemo/Controllers/JellySwitchesViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JellySwitchesViewController.swift 3 | // testi 4 | // 5 | // Created by Jawad Ali on 24/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Switches 11 | class JellySwitchesViewController: UIViewController { 12 | 13 | @IBOutlet weak var fullStretchSwitch: YapFullStretchSwitch! { 14 | didSet { 15 | fullStretchSwitch.onThumbImage = UIImage(named: "check") 16 | fullStretchSwitch.thumbRadiusPadding = 0 17 | fullStretchSwitch.borderColor = #colorLiteral(red: 0.7999292612, green: 0.8000453115, blue: 0.7999040484, alpha: 1) 18 | fullStretchSwitch.borderWidth = 3 19 | fullStretchSwitch.onTintColor = #colorLiteral(red: 0.2392156869, green: 0.6745098233, blue: 0.9686274529, alpha: 1) 20 | fullStretchSwitch.offTintColor = #colorLiteral(red: 0.8078431487, green: 0.02745098062, blue: 0.3333333433, alpha: 1) 21 | } 22 | } 23 | @IBOutlet weak var halfStretchSwitch: YapHalfStretchSwitch! { 24 | didSet { 25 | 26 | halfStretchSwitch.shape = .square 27 | halfStretchSwitch.thumbRadiusPadding = 4 28 | halfStretchSwitch.borderColor = #colorLiteral(red: 0.7999292612, green: 0.8000453115, blue: 0.7999040484, alpha: 1) 29 | halfStretchSwitch.borderWidth = 2 30 | halfStretchSwitch.onTintColor = #colorLiteral(red: 0.999904573, green: 1, blue: 0.9998808503, alpha: 1) 31 | halfStretchSwitch.offTintColor = #colorLiteral(red: 0.345902741, green: 0.7542000413, blue: 0.4488890171, alpha: 1) 32 | 33 | } 34 | } 35 | @IBOutlet weak var jellySwitch: YapLiquidSwitch! 36 | 37 | override func viewDidLoad() { 38 | super.viewDidLoad() 39 | title = "Jelly Switch" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /SwitchesDemo/Controllers/LoadingSwitchesViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoadingSwitchesViewController.swift 3 | // testi 4 | // 5 | // Created by Jawad Ali on 23/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Switches 11 | class LoadingSwitchesViewController: UIViewController { 12 | @IBOutlet weak var loadingSwitch: SDSwitch! { 13 | didSet{ 14 | loadingSwitch.isLoadingEnabled = true 15 | loadingSwitch.thumbRadiusPadding = 10 16 | } 17 | } 18 | var count = 0 19 | @IBOutlet weak var followedMiddleSwitch: SDSwitch! { 20 | didSet { 21 | followedMiddleSwitch.offBorderColor = #colorLiteral(red: 0.3974077106, green: 0.6517546177, blue: 0.9074617624, alpha: 1) 22 | followedMiddleSwitch.offThumbTintColor = #colorLiteral(red: 0.3974077106, green: 0.6517546177, blue: 0.9074617624, alpha: 1) 23 | followedMiddleSwitch.borderWidth = 4 24 | followedMiddleSwitch.thumbRadiusPadding = 8 25 | followedMiddleSwitch.onThumbTintColor = #colorLiteral(red: 0.6347479224, green: 0.50891155, blue: 0.9531157613, alpha: 1) 26 | followedMiddleSwitch.onBorderColor = #colorLiteral(red: 0.6347479224, green: 0.50891155, blue: 0.9531157613, alpha: 1) 27 | followedMiddleSwitch.onTintColor = .white 28 | followedMiddleSwitch.offTintColor = .white 29 | } 30 | } 31 | @IBOutlet weak var followedLastSwitch: YapFullTextSwitch! { 32 | didSet{ 33 | followedLastSwitch.onTextColor = #colorLiteral(red: 0.2766842842, green: 0.8682026267, blue: 0.9832950234, alpha: 1) 34 | followedLastSwitch.offTextColor = #colorLiteral(red: 0.7480503321, green: 0.7532240748, blue: 0.7486134171, alpha: 1) 35 | followedLastSwitch.onText = "ON" 36 | followedLastSwitch.offText = "OFF" 37 | followedLastSwitch.onBorderColor = #colorLiteral(red: 0.2766842842, green: 0.8682026267, blue: 0.9832950234, alpha: 1) 38 | followedLastSwitch.offBorderColor = #colorLiteral(red: 0.9433093667, green: 0.9533157945, blue: 0.9529805779, alpha: 1) 39 | followedLastSwitch.onTintColor = #colorLiteral(red: 0.3614792228, green: 0.9322348237, blue: 0.9953452945, alpha: 1) 40 | followedLastSwitch.offTintColor = #colorLiteral(red: 0.948936522, green: 0.9490728974, blue: 0.9489069581, alpha: 1) 41 | followedLastSwitch.borderWidth = 5 42 | } 43 | } 44 | 45 | override func viewDidLoad() { 46 | super.viewDidLoad() 47 | 48 | title = "Loading Switches" 49 | 50 | loadingSwitch.loadingStarted = { 51 | DispatchQueue.main.asyncAfter(deadline: .now() + 4) { 52 | (self.count % 2 == 0) ? self.loadingSwitch.loadingCompleted(isSuccessFull: true) : self.loadingSwitch.loadingCompleted(isSuccessFull: false) 53 | self.count += 1 54 | } 55 | } 56 | } 57 | 58 | 59 | 60 | } 61 | -------------------------------------------------------------------------------- /SwitchesDemo/Controllers/ModeBSwitchesViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ModeBSwitchesViewController.swift 3 | // testi 4 | // 5 | // Created by Jawad Ali on 22/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Switches 11 | class ModeBSwitchesViewController: UIViewController { 12 | 13 | @IBOutlet weak var modeSwitchStyleA: YapModeSwitch! { 14 | didSet { 15 | modeSwitchStyleA.thumbRadiusPadding = 8 16 | modeSwitchStyleA.thumbTintColor = #colorLiteral(red: 0.9683158994, green: 0.501380682, blue: 0.4961987734, alpha: 1) 17 | modeSwitchStyleA.offTintColor = .white 18 | modeSwitchStyleA.onTintColor = #colorLiteral(red: 0.6057969332, green: 0.6360527277, blue: 0.9815277457, alpha: 1) 19 | } 20 | } 21 | @IBOutlet weak var modeSwitchStyleB: YapModeSwitch! { 22 | didSet { 23 | modeSwitchStyleB.thumbRadiusPadding = 8 24 | modeSwitchStyleB.thumbTintColor = .white 25 | modeSwitchStyleB.offTintColor = #colorLiteral(red: 0.2786044478, green: 0.5705410242, blue: 0.9243882298, alpha: 1) 26 | modeSwitchStyleB.onTintColor = #colorLiteral(red: 0.3037858903, green: 0.1899323463, blue: 0.6803773642, alpha: 1) 27 | } 28 | } 29 | override func viewDidLoad() { 30 | 31 | super.viewDidLoad() 32 | modeSwitchStyleB.addTarget(self, action: #selector(switchBToggle(_:)), for: .valueChanged) 33 | 34 | title = "Dark Light A Mode" 35 | } 36 | 37 | 38 | @objc func switchBToggle(_ sender: YapDarkAndLightModeSwitch) { 39 | 40 | let color = sender.isOn ? #colorLiteral(red: 0.5881829262, green: 0.4660730958, blue: 0.9921956658, alpha: 1) : .white 41 | 42 | UIView.animate(withDuration: sender.duration) { 43 | self.modeSwitchStyleB.superview?.backgroundColor = color 44 | } 45 | 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /SwitchesDemo/Controllers/ModeSwitchesViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ModeSwitchesViewController.swift 3 | // testi 4 | // 5 | // Created by Jawad Ali on 22/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Switches 11 | class ModeSwitchesViewController: UIViewController { 12 | 13 | @IBOutlet weak var modeSwitchStyleA: YapDarkAndLightModeSwitch! { 14 | didSet { 15 | modeSwitchStyleA.thumbRadiusPadding = 8 16 | modeSwitchStyleA.thumbTintColor = #colorLiteral(red: 0.9683158994, green: 0.501380682, blue: 0.4961987734, alpha: 1) 17 | modeSwitchStyleA.offTintColor = .white 18 | modeSwitchStyleA.onTintColor = #colorLiteral(red: 0.6057969332, green: 0.6360527277, blue: 0.9815277457, alpha: 1) 19 | } 20 | } 21 | @IBOutlet weak var modeSwitchStyleB: YapDarkAndLightModeSwitch! { 22 | didSet { 23 | modeSwitchStyleB.thumbRadiusPadding = 8 24 | modeSwitchStyleB.thumbTintColor = .white 25 | modeSwitchStyleB.offTintColor = #colorLiteral(red: 0.2786044478, green: 0.5705410242, blue: 0.9243882298, alpha: 1) 26 | modeSwitchStyleB.onTintColor = #colorLiteral(red: 0.3037858903, green: 0.1899323463, blue: 0.6803773642, alpha: 1) 27 | } 28 | } 29 | override func viewDidLoad() { 30 | 31 | super.viewDidLoad() 32 | modeSwitchStyleB.addTarget(self, action: #selector(switchBToggle(_:)), for: .valueChanged) 33 | 34 | title = "Dark Light B Mode" 35 | } 36 | 37 | 38 | @objc func switchBToggle(_ sender: YapDarkAndLightModeSwitch) { 39 | 40 | let color = sender.isOn ? #colorLiteral(red: 0.5881829262, green: 0.4660730958, blue: 0.9921956658, alpha: 1) : .white 41 | 42 | UIView.animate(withDuration: sender.duration) { 43 | self.modeSwitchStyleB.superview?.backgroundColor = color 44 | } 45 | 46 | } 47 | 48 | 49 | } 50 | -------------------------------------------------------------------------------- /SwitchesDemo/Controllers/RandomViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RandomViewController.swift 3 | // testi 4 | // 5 | // Created by Jawad Ali on 25/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Switches 11 | class RandomViewController: UIViewController { 12 | @IBOutlet weak var randomA: YapSwitch! { 13 | didSet { 14 | randomA.onText = "ON" 15 | randomA.offText = "OFF" 16 | randomA.onTextColor = .white 17 | randomA.offTextColor = #colorLiteral(red: 0.2125092149, green: 0.2516930103, blue: 0.2772026062, alpha: 1) 18 | randomA.borderColor = #colorLiteral(red: 0.2125092149, green: 0.2516930103, blue: 0.2772026062, alpha: 1) 19 | randomA.onTintColor = #colorLiteral(red: 0.2125092149, green: 0.2516930103, blue: 0.2772026062, alpha: 1) 20 | 21 | randomA.offTextColor = #colorLiteral(red: 0.2125092149, green: 0.2516930103, blue: 0.2772026062, alpha: 1) 22 | randomA.offTintColor = .white 23 | randomA.offThumbTintColor = #colorLiteral(red: 0.2125092149, green: 0.2516930103, blue: 0.2772026062, alpha: 1) 24 | randomA.borderWidth = 2 25 | } 26 | } 27 | @IBOutlet weak var randomB: YapSwitchSlim! { 28 | didSet { 29 | randomB.onThumbTintColor = #colorLiteral(red: 0.0864540413, green: 0.5291488171, blue: 0.5380262733, alpha: 1) 30 | randomB.onTintColor = #colorLiteral(red: 0.4112847149, green: 0.7173692584, blue: 0.7089804411, alpha: 1) 31 | randomB.offTintColor = #colorLiteral(red: 0.5372059941, green: 0.5372863412, blue: 0.5371884108, alpha: 1) 32 | randomB.slimTrack = 10 33 | randomB.thumbRadiusPadding = 0 34 | } 35 | } 36 | @IBOutlet weak var randomC: YapSwitch! { 37 | didSet { 38 | randomC.onThumbImage = UIImage(named: "check") 39 | randomC.onTintColor = .purple 40 | randomC.offTintColor = #colorLiteral(red: 0.8552142978, green: 0.8781172037, blue: 0.9388162494, alpha: 1) 41 | } 42 | } 43 | 44 | @IBOutlet weak var randomD: YapSwitch! { 45 | didSet { 46 | randomD.onThumbTintColor = #colorLiteral(red: 0.1970949173, green: 0.799264133, blue: 0.8020008206, alpha: 1) 47 | randomD.onThumbImage = #imageLiteral(resourceName: "check").maskWithColor(color: .white) 48 | randomD.offThumbImage = #imageLiteral(resourceName: "close").maskWithColor(color: #colorLiteral(red: 0.170032382, green: 0.2437844872, blue: 0.3120466471, alpha: 1)) 49 | randomD.offThumbTintColor = #colorLiteral(red: 0.8940391541, green: 0.8941679597, blue: 0.8940110803, alpha: 1) 50 | randomD.onTintColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1) 51 | randomD.offTintColor = #colorLiteral(red: 0.170032382, green: 0.2437844872, blue: 0.3120466471, alpha: 1) 52 | } 53 | } 54 | @IBOutlet weak var randomE: YapSwitch! { 55 | didSet { 56 | randomE.onTextColor = #colorLiteral(red: 0.2311643958, green: 0.8008779883, blue: 0.6899323463, alpha: 1) 57 | randomE.offTextColor = #colorLiteral(red: 0.9092465753, green: 0.8114833048, blue: 0.810734161, alpha: 1) 58 | randomE.onThumbTintColor = #colorLiteral(red: 0.127592504, green: 0.7816838622, blue: 0.6617996693, alpha: 1) 59 | randomE.offThumbTintColor = #colorLiteral(red: 0.9092465753, green: 0.8114833048, blue: 0.810734161, alpha: 1) 60 | randomE.onText = "ON" 61 | randomE.offText = "OFF" 62 | randomE.onTintColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0) 63 | randomE.offTintColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1) 64 | } 65 | } 66 | 67 | 68 | @IBOutlet weak var randomG: YapSwitch! { 69 | didSet { 70 | randomG.thumbTintColor = .yellow 71 | randomG.onText = "OFF" 72 | randomG.offText = "ON" 73 | 74 | randomG.onTintColor = .black 75 | randomG.borderWidth = 4 76 | randomG.textColor = .cyan 77 | randomG.offTintColor = .black 78 | randomG.onTextColor = .white 79 | } 80 | } 81 | @IBOutlet weak var randomH: YapSwitch! { 82 | didSet { 83 | randomH.onImage = UIImage(named: "icon_check_primary_dark")?.maskWithColor(color: .black) 84 | randomH.offImage = UIImage(named: "icon_close_dark_purple")?.maskWithColor(color: .red) 85 | randomH.shape = .square 86 | randomH.onTintColor = .yellow 87 | randomH.borderWidth = 5 88 | randomH.thumbCornerRadius = 0 89 | randomH.offTintColor = .green 90 | } 91 | } 92 | @IBOutlet weak var randomI: YapSwitch! { 93 | didSet { 94 | randomI.onThumbImage = UIImage(named: "check") 95 | randomI.offThumbImage = UIImage(named: "close") 96 | randomI.shape = .square 97 | randomI.onTintColor = .purple 98 | randomI.borderWidth = 2 99 | randomI.thumbCornerRadius = 0 100 | randomI.offTintColor = .red 101 | } 102 | } 103 | 104 | 105 | override func viewDidLoad() { 106 | super.viewDidLoad() 107 | title = "Random Switches" 108 | // Do any additional setup after loading the view. 109 | } 110 | 111 | 112 | 113 | } 114 | -------------------------------------------------------------------------------- /SwitchesDemo/Controllers/SmileyViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SmileyViewController.swift 3 | // testi 4 | // 5 | // Created by Jawad Ali on 25/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Switches 11 | class SmileyViewController: UIViewController { 12 | 13 | @IBOutlet weak var randomA: YapSwitch! { 14 | didSet { 15 | randomA.thumbImage = UIImage(named: "thumb12") 16 | randomA.onTintColor = #colorLiteral(red: 0.8426451087, green: 0.3289524913, blue: 0.2659203112, alpha: 1) 17 | randomA.offTintColor = #colorLiteral(red: 0.7276904583, green: 0.7375378013, blue: 0.7416281104, alpha: 1) 18 | randomA.thumbTintColor = .clear 19 | randomA.onImage = #imageLiteral(resourceName: "check").maskWithColor(color: .white) 20 | randomA.offImage = UIImage(named: "cross7") 21 | randomA.shape = .square 22 | randomA.frame = CGRect(x: 200, y: 650, width: 65, height: 36) 23 | randomA.borderWidth = 2 24 | } 25 | } 26 | @IBOutlet weak var randomB: YapSwitch! { 27 | didSet { 28 | randomB.thumbImage = UIImage(named: "thumb1") 29 | randomB.onTintColor = #colorLiteral(red: 0.5955576897, green: 0.7103857398, blue: 0.2235306501, alpha: 1) 30 | randomB.layer.shadowOpacity = 0.6 31 | randomB.layer.shadowOffset = .zero 32 | randomB.layer.shadowRadius = 5 33 | randomB.layer.shadowColor = UIColor.black.cgColor 34 | randomB.onImage = #imageLiteral(resourceName: "check").maskWithColor(color: .white) 35 | randomB.offImage = UIImage(named: "off2") 36 | randomB.offTintColor = #colorLiteral(red: 0.7764018178, green: 0.7765145898, blue: 0.7763771415, alpha: 1) 37 | randomB.borderWidth = 1 38 | } 39 | } 40 | override func viewDidLoad() { 41 | super.viewDidLoad() 42 | title = "Smiley Switch" 43 | // Do any additional setup after loading the view. 44 | } 45 | 46 | 47 | 48 | 49 | } 50 | -------------------------------------------------------------------------------- /SwitchesDemo/Controllers/SwiftyViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyViewController.swift 3 | // testi 4 | // 5 | // Created by Jawad Ali on 24/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Switches 11 | class SwiftyViewController: UIViewController { 12 | 13 | @IBOutlet weak var swiftyA: Switcher! { 14 | didSet { 15 | swiftyA.superview?.layer.shadowOffset = .zero 16 | swiftyA.superview?.layer.shadowOpacity = 0.3 17 | swiftyA.superview?.layer.shadowRadius = 5 18 | } 19 | } 20 | @IBOutlet weak var swiftyB: Switcher! { 21 | didSet { 22 | swiftyB.superview?.layer.shadowOffset = .zero 23 | swiftyB.superview?.layer.shadowOpacity = 0.3 24 | swiftyB.superview?.layer.shadowRadius = 5 25 | } 26 | } 27 | @IBOutlet weak var swiftyC: JDSwitch! { 28 | didSet { 29 | swiftyC.thumbRadiusPadding = 8 30 | } 31 | } 32 | override func viewDidLoad() { 33 | 34 | title = "Stretchy Switch" 35 | super.viewDidLoad() 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /SwitchesDemo/Controllers/ThumbImageSwitchesViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThumbImageSwitchesViewController.swift 3 | // testi 4 | // 5 | // Created by Jawad Ali on 24/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Switches 11 | class ThumbImageSwitchesViewController: UIViewController { 12 | 13 | @IBOutlet weak var thumbImageSwitchD: YapSwitch! { 14 | didSet { 15 | thumbImageSwitchD.onThumbImage = UIImage(named: "go1") 16 | thumbImageSwitchD.offThumbImage = UIImage(named: "go2") 17 | thumbImageSwitchD.onTintColor = #colorLiteral(red: 0.6870710254, green: 0.2686487734, blue: 0.2165684998, alpha: 1) 18 | thumbImageSwitchD.offTintColor = #colorLiteral(red: 0.3156668544, green: 0.4091051519, blue: 0.481534183, alpha: 1) 19 | thumbImageSwitchD.thumbRadiusPadding = 8 20 | thumbImageSwitchD.isStretchEnable = false 21 | } 22 | } 23 | @IBOutlet weak var thumbImageSwitchC: YapSwitch! { 24 | didSet { 25 | thumbImageSwitchC.onThumbImage = #imageLiteral(resourceName: "right") 26 | thumbImageSwitchC.offThumbImage = #imageLiteral(resourceName: "left") 27 | thumbImageSwitchC.onTintColor = #colorLiteral(red: 0.6210919023, green: 0.3142792583, blue: 0.6986252666, alpha: 1) 28 | thumbImageSwitchC.onText = "OFF" 29 | thumbImageSwitchC.offText = "ON" 30 | thumbImageSwitchC.offTextColor = #colorLiteral(red: 0.6210919023, green: 0.3142792583, blue: 0.6986252666, alpha: 1) 31 | thumbImageSwitchC.offBorderColor = #colorLiteral(red: 0.8901180029, green: 0.8902462125, blue: 0.8900898695, alpha: 1) 32 | thumbImageSwitchC.onBorderColor = #colorLiteral(red: 0.6210919023, green: 0.3142792583, blue: 0.6986252666, alpha: 1) 33 | thumbImageSwitchC.thumbRadiusPadding = 8 34 | thumbImageSwitchC.borderWidth = 3 35 | thumbImageSwitchC.offTintColor = #colorLiteral(red: 0.9568627451, green: 0.9587000478, blue: 0.9568627451, alpha: 1) 36 | } 37 | } 38 | @IBOutlet weak var thumbImageSwitchB: YapSwitch! { 39 | didSet { 40 | thumbImageSwitchB.onTextColor = #colorLiteral(red: 0.3056051731, green: 0.4627509713, blue: 0.01093915664, alpha: 1) 41 | thumbImageSwitchB.offTextColor = #colorLiteral(red: 0.5544068813, green: 0.08276418597, blue: 0.07942076772, alpha: 1) 42 | thumbImageSwitchB.thumbImage = UIImage(named: "thumb7") 43 | thumbImageSwitchB.thumbTintColor = .red 44 | thumbImageSwitchB.onText = "ON" 45 | thumbImageSwitchB.offText = "OFF" 46 | thumbImageSwitchB.thumbRadiusPadding = 1 47 | thumbImageSwitchB.thumbTintColor = .clear 48 | thumbImageSwitchB.borderWidth = 3 49 | thumbImageSwitchB.borderColor = #colorLiteral(red: 0.729346931, green: 0.7294533849, blue: 0.729323566, alpha: 1) 50 | thumbImageSwitchB.onTintColor = #colorLiteral(red: 0.6511958838, green: 0.8345313668, blue: 0.2781367004, alpha: 1) 51 | thumbImageSwitchB.offTintColor = #colorLiteral(red: 0.9100278616, green: 0.2768563628, blue: 0.2796021998, alpha: 1) 52 | } 53 | } 54 | @IBOutlet weak var thumbImageSwitchA: YapSwitchSlim! { 55 | didSet { 56 | thumbImageSwitchA.thumbImage = UIImage(named: "thumb4") 57 | thumbImageSwitchA.onText = "NO" 58 | thumbImageSwitchA.offText = "YES" 59 | thumbImageSwitchA.shape = .square 60 | thumbImageSwitchA.thumbRadiusPadding = 6 61 | thumbImageSwitchA.slimTrack = 5 62 | thumbImageSwitchA.thumbTintColor = .clear 63 | thumbImageSwitchA.borderWidth = 3 64 | thumbImageSwitchA.borderColor = #colorLiteral(red: 0.752874434, green: 0.7529839873, blue: 0.7528504729, alpha: 1) 65 | thumbImageSwitchA.onTintColor = #colorLiteral(red: 0.8561109304, green: 0.2607076168, blue: 0.5694340467, alpha: 1) 66 | thumbImageSwitchA.offTintColor = #colorLiteral(red: 0.09238574654, green: 0.4540643692, blue: 0.61872226, alpha: 1) 67 | thumbImageSwitchA.superview?.layer.shadowOffset = .zero 68 | thumbImageSwitchA.superview?.layer.shadowOpacity = 0.3 69 | thumbImageSwitchA.superview?.layer.shadowRadius = 5 70 | } 71 | } 72 | override func viewDidLoad() { 73 | 74 | // thumbImageSwitchA.valueChange = { [unowned self] isSuccess in 75 | // isSuccess ? (self.thumbImageSwitchA.superview?.superview?.backgroundColor = self.thumbImageSwitchA.onTintColor.withAlphaComponent(0.5)) : (self.thumbImageSwitchA.superview?.superview?.backgroundColor = self.thumbImageSwitchA.offTintColor.withAlphaComponent(0.5)) 76 | // } 77 | title = "Thumb Image Switch" 78 | 79 | super.viewDidLoad() 80 | 81 | // Do any additional setup after loading the view. 82 | } 83 | 84 | 85 | } 86 | -------------------------------------------------------------------------------- /SwitchesDemo/Controllers/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // testi 4 | // 5 | // Created by Jawad Ali on 11/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Switches 11 | class ViewController: UIViewController { 12 | @IBOutlet weak var collectionView: UICollectionView! 13 | 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | title = "JD Switches" 17 | } 18 | } 19 | 20 | //MARK:- CollectionView Delegate 21 | 22 | extension UIViewController : UICollectionViewDelegate , UICollectionViewDataSource { 23 | public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 24 | return SwitchType.allCases.count 25 | } 26 | 27 | public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 28 | 29 | 30 | if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as? CollectionViewCell { 31 | 32 | let getCase = SwitchType.allCases[indexPath.row] 33 | cell.config(type: getCase) 34 | 35 | cell.performSgue = {[unowned self] identifier in 36 | self.performSegue(withIdentifier: identifier, sender: nil) 37 | } 38 | return cell 39 | } 40 | 41 | return UICollectionViewCell() 42 | } 43 | } 44 | 45 | //MARK:- Flow layout delegate 46 | extension UIViewController: UICollectionViewDelegateFlowLayout { 47 | public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 48 | let width = (view.bounds.width) / 2 49 | let height = width * 1.15 50 | return CGSize(width: width, height: height) 51 | } 52 | public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { 53 | return 0 54 | } 55 | 56 | public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { 57 | return 0 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /SwitchesDemo/Model/SwitchType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwitchType.swift 3 | // testi 4 | // 5 | // Created by Jawad Ali on 29/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Switches 11 | enum SwitchType:String, CaseIterable { 12 | 13 | case transition = "Transition Switches" 14 | case loading = "Loading Switches" 15 | case jelly = "Jelly Switches" 16 | case animated = "Animated Switches" 17 | case modeA = "Mode Switches" 18 | case modeB = "Cool Switches" 19 | case gradient = "Gradient Switches" 20 | case thumbImage = "Cover Switches" 21 | case smiley = "Smiley Switches" 22 | case ranndom = "Random Switches" 23 | } 24 | extension SwitchType { 25 | 26 | var coverSwitch: BaseControl { 27 | switch self { 28 | case .transition: 29 | let display = Switcher() 30 | display.superview?.layer.shadowOffset = .zero 31 | display.superview?.layer.shadowOpacity = 0.3 32 | display.superview?.layer.shadowRadius = 5 33 | return display 34 | case .loading: 35 | let display = SDSwitch() 36 | display.isLoadingEnabled = true 37 | display.thumbRadiusPadding = 5 38 | return display 39 | case .jelly: 40 | return YapLiquidSwitch() 41 | case .animated: 42 | let animatedStyleA = YapAnimatedSwitch() 43 | animatedStyleA.thumbRadiusPadding = 6 44 | animatedStyleA.offThumbTintColor = #colorLiteral(red: 0.9540559649, green: 0.8972979188, blue: 0.8275271654, alpha: 1) 45 | animatedStyleA.onThumbTintColor = #colorLiteral(red: 0.4935741425, green: 0.5258994699, blue: 0.9827957749, alpha: 1) 46 | animatedStyleA.onTintColor = .white 47 | animatedStyleA.offTintColor = .white 48 | animatedStyleA.tintColor = .white 49 | return animatedStyleA 50 | case .modeA: 51 | let modeSwitchStyleA = YapDarkAndLightModeSwitch() 52 | modeSwitchStyleA.thumbRadiusPadding = 8 53 | modeSwitchStyleA.thumbTintColor = #colorLiteral(red: 0.9683158994, green: 0.501380682, blue: 0.4961987734, alpha: 1) 54 | modeSwitchStyleA.offTintColor = .white 55 | modeSwitchStyleA.onTintColor = #colorLiteral(red: 0.6057969332, green: 0.6360527277, blue: 0.9815277457, alpha: 1) 56 | return modeSwitchStyleA 57 | case .modeB: 58 | let modeSwitchStyleA = YapModeSwitch() 59 | modeSwitchStyleA.thumbRadiusPadding = 8 60 | modeSwitchStyleA.thumbTintColor = #colorLiteral(red: 0.9683158994, green: 0.501380682, blue: 0.4961987734, alpha: 1) 61 | modeSwitchStyleA.offTintColor = .white 62 | modeSwitchStyleA.onTintColor = #colorLiteral(red: 0.6057969332, green: 0.6360527277, blue: 0.9815277457, alpha: 1) 63 | return modeSwitchStyleA 64 | case .gradient: 65 | let gradientSwitchVertical = YapGradientSwitch() 66 | gradientSwitchVertical.borderWidth = 2 67 | gradientSwitchVertical.gradientPosition = .vertical 68 | gradientSwitchVertical.onColors = [#colorLiteral(red: 0.722638309, green: 0.1586903036, blue: 0.2223222256, alpha: 1),#colorLiteral(red: 0.1921568662, green: 0.007843137719, blue: 0.09019608051, alpha: 1)] 69 | gradientSwitchVertical.offColors = [#colorLiteral(red: 0.1042157337, green: 0.4639918208, blue: 0.2866171598, alpha: 1),#colorLiteral(red: 0.1294117719, green: 0.1414276541, blue: 0.06666667014, alpha: 1)] 70 | gradientSwitchVertical.onText = "START" 71 | gradientSwitchVertical.offText = "STOP" 72 | return gradientSwitchVertical 73 | 74 | case .smiley: 75 | return YapSmileSwitch() 76 | case .thumbImage: 77 | let thumbImageSwitchC = YapSwitch() 78 | thumbImageSwitchC.onThumbImage = #imageLiteral(resourceName: "right") 79 | thumbImageSwitchC.offThumbImage = #imageLiteral(resourceName: "left") 80 | thumbImageSwitchC.onTintColor = #colorLiteral(red: 0.6210919023, green: 0.3142792583, blue: 0.6986252666, alpha: 1) 81 | thumbImageSwitchC.onText = "OFF" 82 | thumbImageSwitchC.offText = "ON" 83 | thumbImageSwitchC.offTextColor = #colorLiteral(red: 0.6210919023, green: 0.3142792583, blue: 0.6986252666, alpha: 1) 84 | thumbImageSwitchC.offBorderColor = #colorLiteral(red: 0.8901180029, green: 0.8902462125, blue: 0.8900898695, alpha: 1) 85 | thumbImageSwitchC.onBorderColor = #colorLiteral(red: 0.6210919023, green: 0.3142792583, blue: 0.6986252666, alpha: 1) 86 | thumbImageSwitchC.thumbRadiusPadding = 8 87 | thumbImageSwitchC.borderWidth = 3 88 | thumbImageSwitchC.offTintColor = #colorLiteral(red: 0.9568627451, green: 0.9587000478, blue: 0.9568627451, alpha: 1) 89 | return thumbImageSwitchC 90 | case .ranndom: 91 | let randomB = YapSwitchSlim() 92 | 93 | randomB.onThumbTintColor = #colorLiteral(red: 0.0864540413, green: 0.5291488171, blue: 0.5380262733, alpha: 1) 94 | randomB.onTintColor = #colorLiteral(red: 0.4112847149, green: 0.7173692584, blue: 0.7089804411, alpha: 1) 95 | randomB.offTintColor = #colorLiteral(red: 0.5372059941, green: 0.5372863412, blue: 0.5371884108, alpha: 1) 96 | randomB.slimTrack = 10 97 | randomB.thumbRadiusPadding = 0 98 | return randomB 99 | 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Icon-App-20x20@2x.png", 5 | "idiom" : "iphone", 6 | "scale" : "2x", 7 | "size" : "20x20" 8 | }, 9 | { 10 | "filename" : "Icon-App-20x20@3x.png", 11 | "idiom" : "iphone", 12 | "scale" : "3x", 13 | "size" : "20x20" 14 | }, 15 | { 16 | "filename" : "Icon-App-29x29@1x.png", 17 | "idiom" : "iphone", 18 | "scale" : "1x", 19 | "size" : "29x29" 20 | }, 21 | { 22 | "filename" : "Icon-App-29x29@2x.png", 23 | "idiom" : "iphone", 24 | "scale" : "2x", 25 | "size" : "29x29" 26 | }, 27 | { 28 | "filename" : "Icon-App-29x29@3x.png", 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "29x29" 32 | }, 33 | { 34 | "filename" : "Icon-App-40x40@2x.png", 35 | "idiom" : "iphone", 36 | "scale" : "2x", 37 | "size" : "40x40" 38 | }, 39 | { 40 | "filename" : "Icon-App-40x40@3x.png", 41 | "idiom" : "iphone", 42 | "scale" : "3x", 43 | "size" : "40x40" 44 | }, 45 | { 46 | "filename" : "Icon-App-60x60@2x.png", 47 | "idiom" : "iphone", 48 | "scale" : "2x", 49 | "size" : "60x60" 50 | }, 51 | { 52 | "filename" : "Icon-App-60x60@3x.png", 53 | "idiom" : "iphone", 54 | "scale" : "3x", 55 | "size" : "60x60" 56 | }, 57 | { 58 | "filename" : "Icon-App-20x20@1x.png", 59 | "idiom" : "ipad", 60 | "scale" : "1x", 61 | "size" : "20x20" 62 | }, 63 | { 64 | "filename" : "Icon-App-20x20@2x.png", 65 | "idiom" : "ipad", 66 | "scale" : "2x", 67 | "size" : "20x20" 68 | }, 69 | { 70 | "filename" : "Icon-App-29x29@1x.png", 71 | "idiom" : "ipad", 72 | "scale" : "1x", 73 | "size" : "29x29" 74 | }, 75 | { 76 | "filename" : "Icon-App-29x29@2x.png", 77 | "idiom" : "ipad", 78 | "scale" : "2x", 79 | "size" : "29x29" 80 | }, 81 | { 82 | "filename" : "Icon-App-40x40@1x.png", 83 | "idiom" : "ipad", 84 | "scale" : "1x", 85 | "size" : "40x40" 86 | }, 87 | { 88 | "filename" : "Icon-App-40x40@2x.png", 89 | "idiom" : "ipad", 90 | "scale" : "2x", 91 | "size" : "40x40" 92 | }, 93 | { 94 | "filename" : "Icon-App-76x76@1x.png", 95 | "idiom" : "ipad", 96 | "scale" : "1x", 97 | "size" : "76x76" 98 | }, 99 | { 100 | "filename" : "Icon-App-76x76@2x.png", 101 | "idiom" : "ipad", 102 | "scale" : "2x", 103 | "size" : "76x76" 104 | }, 105 | { 106 | "filename" : "Icon-App-83.5x83.5@2x.png", 107 | "idiom" : "ipad", 108 | "scale" : "2x", 109 | "size" : "83.5x83.5" 110 | }, 111 | { 112 | "filename" : "ItunesArtwork@2x.png", 113 | "idiom" : "ios-marketing", 114 | "scale" : "1x", 115 | "size" : "1024x1024" 116 | } 117 | ], 118 | "info" : { 119 | "author" : "xcode", 120 | "version" : 1 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/check.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "check@3x.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/check.imageset/check@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/check.imageset/check@3x.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/check1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "check1.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/check1.imageset/check1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/check1.imageset/check1.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/check9.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "check9.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/check9.imageset/check9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/check9.imageset/check9.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/close.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "close.pdf", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/close.imageset/close.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/close.imageset/close.pdf -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/cross1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "cross1.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/cross1.imageset/cross1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/cross1.imageset/cross1.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/cross24.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "cross24.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/cross24.imageset/cross24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/cross24.imageset/cross24.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/cross7.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "cross7.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/cross7.imageset/cross7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/cross7.imageset/cross7.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/go1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "go1.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/go1.imageset/go1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/go1.imageset/go1.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/go2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "go2.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/go2.imageset/go2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/go2.imageset/go2.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/icon_check_primary_dark.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_check_primary_dark.pdf", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/icon_check_primary_dark.imageset/icon_check_primary_dark.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/icon_check_primary_dark.imageset/icon_check_primary_dark.pdf -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/icon_close_dark_purple.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_close_dark_purple.pdf", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/icon_close_dark_purple.imageset/icon_close_dark_purple.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/icon_close_dark_purple.imageset/icon_close_dark_purple.pdf -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/left.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "left.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/left.imageset/left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/left.imageset/left.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/off1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "off1.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | }, 21 | "properties" : { 22 | "template-rendering-intent" : "template" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/off1.imageset/off1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/off1.imageset/off1.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/off2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "off2.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/off2.imageset/off2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/off2.imageset/off2.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/onTick.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "onTick.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/onTick.imageset/onTick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/onTick.imageset/onTick.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/q1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "q1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/q1.imageset/q1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/q1.imageset/q1.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/right.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "right.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/right.imageset/right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/right.imageset/right.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/switch-animation-post.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "switch-animation-post.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/switch-animation-post.imageset/switch-animation-post.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/switch-animation-post.imageset/switch-animation-post.jpg -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/thumb.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "filename" : "thumb.png", 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/thumb.imageset/thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/thumb.imageset/thumb.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/thumb1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "thumb1.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/thumb1.imageset/thumb1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/thumb1.imageset/thumb1.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/thumb12.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "thumb12.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/thumb12.imageset/thumb12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/thumb12.imageset/thumb12.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/thumb4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "thumb4.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/thumb4.imageset/thumb4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/thumb4.imageset/thumb4.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/thumb7.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "thumb7.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/thumb7.imageset/thumb7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/thumb7.imageset/thumb7.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/w1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "w1.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Assets.xcassets/w1.imageset/w1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/SwitchesDemo/Resources/Assets.xcassets/w1.imageset/w1.png -------------------------------------------------------------------------------- /SwitchesDemo/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | Switches 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIMainStoryboardFile 45 | Main 46 | UIRequiredDeviceCapabilities 47 | 48 | armv7 49 | 50 | UISupportedInterfaceOrientations 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | UISupportedInterfaceOrientations~ipad 57 | 58 | UIInterfaceOrientationPortrait 59 | UIInterfaceOrientationPortraitUpsideDown 60 | UIInterfaceOrientationLandscapeLeft 61 | UIInterfaceOrientationLandscapeRight 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /SwitchesDemo/Storyboard/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /SwitchesDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /SwitchesDemoTests/SwitchesDemoTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwitchesDemoTests.swift 3 | // SwitchesDemoTests 4 | // 5 | // Created by Jawad Ali on 30/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwitchesDemo 11 | 12 | class SwitchesDemoTests: XCTestCase { 13 | 14 | override func setUpWithError() throws { 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDownWithError() throws { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | } 21 | 22 | func testExample() throws { 23 | // This is an example of a functional test case. 24 | // Use XCTAssert and related functions to verify your tests produce the correct results. 25 | } 26 | 27 | func testPerformanceExample() throws { 28 | // This is an example of a performance test case. 29 | self.measure { 30 | // Put the code you want to measure the time of here. 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /SwitchesDemoUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /SwitchesDemoUITests/SwitchesDemoUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwitchesDemoUITests.swift 3 | // SwitchesDemoUITests 4 | // 5 | // Created by Jawad Ali on 30/08/2020. 6 | // Copyright © 2020 Jawad Ali. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class SwitchesDemoUITests: XCTestCase { 12 | 13 | override func setUpWithError() throws { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | 16 | // In UI tests it is usually best to stop immediately when a failure occurs. 17 | continueAfterFailure = false 18 | 19 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 20 | } 21 | 22 | override func tearDownWithError() throws { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | } 25 | 26 | func testExample() throws { 27 | // UI tests must launch the application that they test. 28 | let app = XCUIApplication() 29 | app.launch() 30 | 31 | // Use recording to get started writing UI tests. 32 | // Use XCTAssert and related functions to verify your tests produce the correct results. 33 | } 34 | 35 | func testLaunchPerformance() throws { 36 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { 37 | // This measures how long it takes to launch your application. 38 | measure(metrics: [XCTOSSignpostMetric.applicationLaunch]) { 39 | XCUIApplication().launch() 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /images/Screen Recording 2020-08-31 at 3.39.54 PM.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/images/Screen Recording 2020-08-31 at 3.39.54 PM.gif -------------------------------------------------------------------------------- /images/abcd: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /images/ezgif.com-video-to-gif-23.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/images/ezgif.com-video-to-gif-23.gif -------------------------------------------------------------------------------- /images/ezgif.com-video-to-gif-24.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/images/ezgif.com-video-to-gif-24.gif -------------------------------------------------------------------------------- /images/ezgif.com-video-to-gif-25.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/images/ezgif.com-video-to-gif-25.gif -------------------------------------------------------------------------------- /images/ezgif.com-video-to-gif-26.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwd-ali/Switch-Animations/71626e8b569d7bfd28ad6d748121d315e4d98bf7/images/ezgif.com-video-to-gif-26.gif --------------------------------------------------------------------------------