├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── TGPControlDemo.gif ├── TGPControls.podspec ├── TGPControls.xcodeproj ├── project.pbxproj └── xcshareddata │ └── xcschemes │ └── TGPControls.xcscheme ├── TGPControls ├── Info.plist ├── TGPCamelLabels.swift ├── TGPControls.h ├── TGPControlsTicksProtocol.swift └── TGPDiscreteSlider.swift └── TGPControlsDemo ├── Cartfile ├── Podfile ├── TGPControlsDemo.xcodeproj └── project.pbxproj └── TGPControlsDemo ├── AppDelegate.swift ├── Assets.xcassets ├── AppIcon.appiconset │ ├── Contents.json │ ├── icon1024.png │ ├── icon120.png │ ├── icon152.png │ ├── icon167.png │ └── icon76.png ├── Contents.json ├── launch.imageset │ ├── Contents.json │ └── launch1024.png ├── thumb.imageset │ ├── Contents.json │ └── wthumb.png ├── tick.imageset │ ├── Contents.json │ └── wtick.png └── track.imageset │ ├── Contents.json │ └── wtrack.png ├── Base.lproj ├── InfoPlist.strings ├── LaunchScreen.storyboard ├── Localizable.strings └── Main.storyboard ├── TGPControlsDemo-Cart-Info.plist ├── TGPControlsDemo-Pods-Info.plist ├── ViewController.swift └── ar.lproj ├── InfoPlist.strings ├── Localizable.strings └── Main.strings /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.xcworkspace 7 | !default.xcworkspace 8 | xcuserdata 9 | profile 10 | DerivedData 11 | 12 | # Pods - for those of you who use CocoaPods 13 | Pods 14 | Podfile.lock 15 | 16 | # Carthage 17 | Cartfile.resolved 18 | Carthage/ 19 | 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode9.3 3 | 4 | script: xcodebuild clean && xcodebuild build -project TGPControls.xcodeproj -scheme TGPControls CODE_SIGNING_REQUIRED=NO 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Xavier Schott 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TGP Controls 2 | 3 | [![Build Status](https://travis-ci.org/SwiftArchitect/TGPControls.svg?branch=master)](https://travis-ci.org/SwiftArchitect/TGPControls) 4 | [![CocoaPods](http://img.shields.io/cocoapods/v/TGPControls.svg)](https://cocoapods.org/pods/TGPControls) 5 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg)](https://github.com/Carthage/Carthage) 6 | ![MIT License](https://img.shields.io/cocoapods/l/TGPControls.svg) 7 | 8 | ![Demo Project](TGPControlDemo.gif) 9 | 10 | # Slider with _ticks_ & animated Label (Swift) 11 | TGPControls are drop-in replacement of `UISlider` & `UILabel`, with visual preview in **Interface Builder**, single liner instrumentation, smooth animations, simple API, powerful customization. 12 | 13 | ## What's DiscreteSlider? 14 | ![ticksdemo](https://cloud.githubusercontent.com/assets/4073988/5912371/144aaf24-a588-11e4-9a22-42832eb2c235.gif) 15 | 16 | `TGPDiscreteSlider`: A slider with discrete steps (i.e. ticks), discrete range, with customizable track, thumb and ticks. 17 | Ideal to select individual steps, star rating, integers, rainbow colors, slices and in general any value that is not a continuum. Just set the number of stops, TGPDiscreteSlider handles the selection and the animations for you. 18 | 19 | ## What's CamelLabels? 20 | ![camellabels2](https://cloud.githubusercontent.com/assets/4073988/5912454/15774398-a589-11e4-8f08-18c9c7b59871.gif) 21 | 22 | `TGPCamelLabels`: A set of animated labels (dock effect) representing a selection. Can be used alone or in conjunction with a UIControl. 23 | Ideal to represent steps. *The discrete slider and the camel labels can work in unison.* 24 | 25 | ## Compatibility 26 | 1. Written in **Swift 4**, can be integrated with **Swift** or **Obj-C** 27 | 2. `TGPControls` are **AutoLayout**, `IB Designable` and `IB Inspectable` ready 28 | 3. Version **5.0.1** and better comes with a **Swift 4** demo application for **iOS 8** and above. 29 | _**iOS 7** supported in versions 2.1.0 and earlier_ 30 | 4. Automatic support for both left-to-right and right-to-left languages 31 | 32 | ![imagessliderdemo](https://cloud.githubusercontent.com/assets/4073988/6628373/183c7452-c8c2-11e4-9a63-107805bc0cc4.gif) 33 | 34 | | Copyright disclaimer | 35 | | :--- | 36 | |The above slider is inspired by [National Parks by National Geographic](https://itunes.apple.com/us/app/national-parks-by-national/id518426085?mt=8) iPhone App.| 37 | |National Parks iPhone App is developed by [Rally Interactive LLC](http://rallyinteractive.com).| 38 | |The above image, styling, appearance and look & feel all [Copyright © 2015 National Geographic Society](http://www.nationalgeographic.com).| 39 | |TGPControls is *not* associated with National Geographic Society, Rally Interactive LLC or any of it's subsidiaries.| 40 | 41 | ## Fully Customizable 42 | 43 | ![alphabetslider](https://cloud.githubusercontent.com/assets/4073988/5912297/c3f21bb2-a586-11e4-8eb1-a1f930ccbdd5.gif) 44 | 45 | Control everything about the slider or its labels, starting with colors, images and fonts, including track and ticks shape, and thumb shadows. 46 | All computations regarding range and sizing and handled automatically. 47 | Use the two classes in tandem to create stunning new controls, which can be resized dynamically, to intergrate beautifully into your application. 48 | 49 | ![onoff](https://cloud.githubusercontent.com/assets/4073988/5912516/36af8006-a58a-11e4-91bf-03ef24476645.gif) 50 | 51 | Most customization can be done in **Interface Builder** and require **zero coding**. 52 | 53 | --- 54 | 55 | ## How to integrate (iOS 8 and higher) 56 | 57 | #### Using [CocoaPods](http://cocoapods.org/?q=TGPControls) 58 | 59 | ``` 60 | platform :ios, '8.0' 61 | use_frameworks! 62 | target 'TGPControlsDemo' do 63 | pod 'TGPControls' 64 | end 65 | ``` 66 | 67 | #### Using [Carthage](https://github.com/Carthage/Carthage) 68 | 69 | ``` 70 | github "SwiftArchitect/TGPControls" 71 | ``` 72 | 73 | --- 74 | 75 | Besides customization, which you can do entirely under Interface Builder in iOS 8 and later, both `TGPDiscreteSlider` and `TGPCamelLabels` require surprisingly little code to integrate. 76 | 77 | ### DiscreteSlider 78 | 79 | For simplicity, TGPDiscreteSlider does *not* descend from UISlider but from **UIControl**. 80 | It uses a `minimumValue`, a `tickCount` and an `incrementValue` (instead of *minimumValue* and *maximumValue*). 81 | All graphic aspects, such as physical spacing of the ticks or physical width of the track are controlled internally. This makes TGPDiscreteSlider predictable. it is guaranteed to always fit snuggly inside its bounds. 82 | 83 | **Single step**: To use DiscreteSlider + CamelLabels in tandem, you need exactly **1 line of code**: 84 | ``` 85 | discreteSlider.ticksListener = camelLabels 86 | ``` 87 | 88 | ![complete](https://cloud.githubusercontent.com/assets/4073988/5912616/26cf1b0a-a58b-11e4-92f7-f9dbcd53c413.gif) 89 | 90 | That's all! 91 | Through the `TGPControlsTicksProtocol`, the camelLabels listen to _value_ and _size_ changes. You may want to adopt this protocol to handle changes, or use the traditional `UIControl` notifications: 92 | 93 | **Advanced Steps (1a)**: *register* to notifications (*just like any UIControl*) 94 | ``` 95 | discreteSlider.addTarget(self, 96 | action: #selector(valueChanged(_:event:)), 97 | for: .valueChanged) 98 | ``` 99 | **Advanced Steps (1b)**: *respond* to notification 100 | ``` 101 | func valueChanged(_ sender: TGPDiscreteSlider, event:UIEvent) { 102 | self.stepper.value = Double(sender.value) 103 | } 104 | ``` 105 | 106 | 107 | **tickStyle, trackStyle, thumbStyle Properties:** 108 | 109 | | Constant | Value | | 110 | |:--------------|:------------:| ----- | 111 | | `.iOS` | 0 | Gives to any component the iOS appearance: Ticks are invisible, track is blue and gray, thumb is round with a shadow | 112 | | `.rectangular` | 1 | Boxy look with hard edges | 113 | | `.rounded` | 2 | From rounded rectangles to perfects circles, depending on vertical to horizontal ratio | 114 | | `.invisible` | 3 | Surprisingly useful to individually hide ticks, track, or even thumb | 115 | 116 | **Other Properties:** 117 | 118 | | Listener | | 119 | |:-----| ----- | 120 | | `ticksListener` | a `TGPControlsTicksProtocol` listener, such as `TGPCamelLabels`, which receives spacing changes notifications. | 121 | | `allTargets` | Inherited from `UIControl`, receives actions for control event | 122 | 123 | | Tick | | 124 | |:-----| ----- | 125 | | `tickStyle` | See style property | 126 | | `tickSize` | absolute dimension | 127 | | `tickCount` | discrete steps, must be 2 or greater | 128 | | `minimumTickTintColor` | lower side ticks ; takes precedence over `minimumTrackTintColor` (†1) | 129 | | `maximumTickTintColor` | higher side ticks ; takes precedence over `maximumTrackTintColor` (†1) | 130 | | `tickImage` | a `UIImage`, fits in `tickSize` (†) | 131 | | `ticksDistance` | horizontal spacing _(calculated)_ | 132 | 133 | | Track | | 134 | |:------| ----- | 135 | | `trackStyle` | See style property | 136 | | `trackThickness` | height | 137 | | `trackImage` | a `UIImage`, ignores `trackThickness` (†2) | 138 | | `minimumTrackTintColor` | track lower side | 139 | | `maximumTrackTintColor` | track higher side | 140 | 141 | | Thumb | | 142 | |:------| ----- | 143 | | `thumbStyle` | See style property | 144 | | `thumbSize` | absolute size | 145 | | `thumbTintColor` | background | 146 | | `thumbImage` | a`UIImage`, dictates `thumbSize` (†) | 147 | | `thumbShadowRadius` | breaking the _flat design concept_ | 148 | | `thumbShadowOffset` | applied to `thumbShadowRadius`, may affect control bounds | 149 | 150 | (†1) Introduced in 5.1.0. 151 | (†2) Introduced in 4.0.0. Former versions used a resource name as a `string`. 152 | 153 | ![image](https://cloud.githubusercontent.com/assets/4073988/5910789/e102af28-a572-11e4-9169-b18555e20eab.png) 154 | 155 | ### CamelLabels 156 | 157 | Besides font customization, `TGPCamelLabels` only requires a set of labels (supplied as *strings*), and an active index selection. 158 | 159 | **Step 1**: *tell* the TGPCamelLabels what to select 160 | _(Refer to **Single step** section above to use DiscreteSlider + CamelLabels in tandem)_ 161 | ``` 162 | camelLabels.value = sender.value 163 | ``` 164 | 165 | *There is no step 2.* 166 | Most of the customization can be done inside **Interface Builder**. 167 | 168 | **Properties:** 169 | 170 | | Property | | 171 | |:---------| ----- | 172 | | `tickCount` | **design only** (_preferrably not to be used_): the number of ticks is driven by the number of elements in the `names` array | 173 | | `names` | supplies a new set of labels ; supersedes the `tickCount` property, which will return the number of labels. A typical use may be `camelLabels.names = ["OFF", "ON"]` | 174 | | `ticksListener` | ties a discrete slider to its camel labels. This is your most robust method to not only ensure that the layout of both controls match exactly, but also adjust this spacing when orientation changes. A typical use may be `discreteSlider.ticksListener = camelLabels` | 175 | | `ticksDistance` | _override_ the labels spacing entirely ; **prefer** the `ticksListener` mechanism if it is available to you. A typical use may be `camelLabels.ticksDistance = 15` | 176 | | `value` | which label is emphasized (_selected_) | 177 | | `backgroundColor` | labels become *tap-through* (**click-through**) when set to `UIColor.clear` ; use TGPCamelLabels *on top of* other UI elements, **even native iOS objects**!(*) | 178 | 179 | (*) _You can tap on the `UISwitch` laid out either under or below the camel labels:_ 180 | ![uiswitch](https://cloud.githubusercontent.com/assets/4073988/11609813/a3b63526-9b45-11e5-9562-34fc2c9b134d.gif) 181 | 182 | | Edges & Animation | | 183 | |:------------------| ----- | 184 | | `numberOfLiness` | Support for multiple lines labels | 185 | | `offCenter` | **leftmost and righmost labels only**: relative inset expressed as a proportion of individual label width: 0: none, +0.5: nudge in by a half width (fully fit) or -0.5: draw completely outside | 186 | | `insets` | **leftmost and righmost labels only**: absolute inset expressed in pixels | 187 | | `emphasisLayout` | emphasized (_selected_) labels vertical alignment ; `.top`, `.centerY` or `.bottom`. Default is `.top` (‡) | 188 | | `regularLayout` | regular labels vertical alignment ; `.top`, `.centerY` or `.bottom`. Default is `.bottom` (‡) | 189 | 190 | (‡) No camel animation will occur when `emphasisLayout` = `regularLayout`, i.e. `.centerY`. 191 | 192 | | Emphasized labels | | 193 | |:------------------| ----- | 194 | | `upFontName` | font | 195 | | `upFontSize` | size | 196 | | `upFontColor` | color | 197 | 198 | | Regular labels | | 199 | |:---------------| ----- | 200 | | `dnFontName` | font | 201 | | `dnFontSize` | size | 202 | | `dnFontColor` | color | 203 | 204 | --- 205 | 206 | ### Code example (Swift) 207 | 208 | See **TGPControlsDemo** project: `TGPControlsDemo` (Modern Swift syntax + IBInspectable) 209 | 210 | **Running the demo using Carthage** 211 | 212 | 1. `cd TGPControlsDemo` 213 | 2. `carthage update` 214 | 3. open TGPControlsDemo.xcodeproj 215 | 4. Select scheme **TGPControlsDemo Cart** 216 | 5. Build & run 217 | 218 | **Running the demo using Cocoapods** 219 | 220 | 1. `cd TGPControlsDemo` 221 | 2. `pod install` 222 | 3. open TGPControlsDemo.xcworkspace 223 | 4. Select scheme **TGPControlsDemo Pods** 224 | 5. Build & run 225 | 226 | ``` 227 | import UIKit 228 | import TGPControls 229 | 230 | class ViewController: UIViewController { 231 | @IBOutlet weak var oneTo10Labels: TGPCamelLabels! 232 | @IBOutlet weak var oneTo10Slider: TGPDiscreteSlider! 233 | @IBOutlet weak var alphabetLabels: TGPCamelLabels! 234 | @IBOutlet weak var alphabetSlider: TGPDiscreteSlider! 235 | @IBOutlet weak var switch1Camel: TGPCamelLabels! 236 | 237 | override func viewDidLoad() { 238 | super.viewDidLoad() 239 | 240 | alphabetLabels.names = [ 241 | "A","B","C","D","E","F","G","H","I","J","K","L","M", 242 | "N","O","P","Q","R","S","T","U","V","W","X","Y","Z"] 243 | switch1Camel.names = ["OFF", "ON"] 244 | 245 | // Automatically track tick spacing and value changes 246 | alphabetSlider.ticksListener = alphabetLabels 247 | oneTo10Slider.ticksListener = oneTo10Labels 248 | } 249 | 250 | // MARK: - UISwitch 251 | @IBAction func switch1ValueChanged(_ sender: UISwitch) { 252 | switch1Camel.value = (sender.isOn) ? 1 : 0 253 | } 254 | } 255 | ``` 256 | ### Customization examples 257 | 258 | ![image](https://cloud.githubusercontent.com/assets/4073988/5909892/7fdc091e-a569-11e4-906b-da0f185a1b91.png) 259 | 260 | ![custom](https://cloud.githubusercontent.com/assets/4073988/5912951/19788d6a-a590-11e4-9e0c-57a79cb5d020.gif) 261 | -------------------------------------------------------------------------------- /TGPControlDemo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftArchitect/TGPControls/cec613690eaf159271967c1a9853638062d9c6c0/TGPControlDemo.gif -------------------------------------------------------------------------------- /TGPControls.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | 3 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 4 | spec.name = "TGPControls" 5 | spec.version = "5.1.0" 6 | spec.summary = "Custom animated iOS controls: Animated discrete slider, animated labels" 7 | 8 | spec.description = <<-DESC 9 | Provide an iOS looking UISlider with discrete, controlable steps 10 | Provide dynamic, animated labels for discrete slider 11 | Entirely compatible with UISlider 12 | DESC 13 | spec.homepage = "https://github.com/SwiftArchitect/TGPControls" 14 | spec.screenshots = "https://cloud.githubusercontent.com/assets/4073988/5912371/144aaf24-a588-11e4-9a22-42832eb2c235.gif", "https://cloud.githubusercontent.com/assets/4073988/5912454/15774398-a589-11e4-8f08-18c9c7b59871.gif", "https://cloud.githubusercontent.com/assets/4073988/6628373/183c7452-c8c2-11e4-9a63-107805bc0cc4.gif", "https://cloud.githubusercontent.com/assets/4073988/5912297/c3f21bb2-a586-11e4-8eb1-a1f930ccbdd5.gif" 15 | 16 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 17 | spec.license = { :type => "MIT", :file => "LICENSE" } 18 | 19 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 20 | spec.author = { "Xavier Schott" => "https://www.swiftarchitect.com/swiftarchitect/" } 21 | 22 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 23 | spec.platform = :ios 24 | spec.ios.deployment_target = '8.0' 25 | spec.requires_arc = true 26 | spec.swift_version = '4.0' 27 | 28 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 29 | spec.source = { :git => "https://github.com/SwiftArchitect/TGPControls.git", :tag => "v5.1.0" } 30 | spec.source_files = "TGPControls/**/*.{swift}" 31 | spec.exclude_files = "TGPControlsDemo/*" 32 | 33 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 34 | spec.framework = "UIKit" 35 | 36 | end 37 | -------------------------------------------------------------------------------- /TGPControls.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | DC56BE021E46EA2000AAD0D9 /* TGPControls.h in Headers */ = {isa = PBXBuildFile; fileRef = DC56BE001E46EA2000AAD0D9 /* TGPControls.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | DC88CC4E1E46EAD10076AC65 /* TGPCamelLabels.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC88CC4B1E46EAD10076AC65 /* TGPCamelLabels.swift */; }; 12 | DC88CC4F1E46EAD10076AC65 /* TGPControlsTicksProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC88CC4C1E46EAD10076AC65 /* TGPControlsTicksProtocol.swift */; }; 13 | DC88CC501E46EAD10076AC65 /* TGPDiscreteSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC88CC4D1E46EAD10076AC65 /* TGPDiscreteSlider.swift */; }; 14 | /* End PBXBuildFile section */ 15 | 16 | /* Begin PBXFileReference section */ 17 | DC56BDFD1E46EA2000AAD0D9 /* TGPControls.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TGPControls.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 18 | DC56BE001E46EA2000AAD0D9 /* TGPControls.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TGPControls.h; sourceTree = ""; }; 19 | DC56BE011E46EA2000AAD0D9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 20 | DC88CC4B1E46EAD10076AC65 /* TGPCamelLabels.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TGPCamelLabels.swift; sourceTree = ""; }; 21 | DC88CC4C1E46EAD10076AC65 /* TGPControlsTicksProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TGPControlsTicksProtocol.swift; sourceTree = ""; }; 22 | DC88CC4D1E46EAD10076AC65 /* TGPDiscreteSlider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TGPDiscreteSlider.swift; sourceTree = ""; }; 23 | /* End PBXFileReference section */ 24 | 25 | /* Begin PBXFrameworksBuildPhase section */ 26 | DC56BDF91E46EA2000AAD0D9 /* Frameworks */ = { 27 | isa = PBXFrameworksBuildPhase; 28 | buildActionMask = 2147483647; 29 | files = ( 30 | ); 31 | runOnlyForDeploymentPostprocessing = 0; 32 | }; 33 | /* End PBXFrameworksBuildPhase section */ 34 | 35 | /* Begin PBXGroup section */ 36 | DC56BDF31E46EA2000AAD0D9 = { 37 | isa = PBXGroup; 38 | children = ( 39 | DC56BDFF1E46EA2000AAD0D9 /* TGPControls */, 40 | DC56BDFE1E46EA2000AAD0D9 /* Products */, 41 | ); 42 | sourceTree = ""; 43 | }; 44 | DC56BDFE1E46EA2000AAD0D9 /* Products */ = { 45 | isa = PBXGroup; 46 | children = ( 47 | DC56BDFD1E46EA2000AAD0D9 /* TGPControls.framework */, 48 | ); 49 | name = Products; 50 | sourceTree = ""; 51 | }; 52 | DC56BDFF1E46EA2000AAD0D9 /* TGPControls */ = { 53 | isa = PBXGroup; 54 | children = ( 55 | DC56BE001E46EA2000AAD0D9 /* TGPControls.h */, 56 | DC88CC4B1E46EAD10076AC65 /* TGPCamelLabels.swift */, 57 | DC88CC4C1E46EAD10076AC65 /* TGPControlsTicksProtocol.swift */, 58 | DC88CC4D1E46EAD10076AC65 /* TGPDiscreteSlider.swift */, 59 | DC56BE011E46EA2000AAD0D9 /* Info.plist */, 60 | ); 61 | path = TGPControls; 62 | sourceTree = ""; 63 | }; 64 | /* End PBXGroup section */ 65 | 66 | /* Begin PBXHeadersBuildPhase section */ 67 | DC56BDFA1E46EA2000AAD0D9 /* Headers */ = { 68 | isa = PBXHeadersBuildPhase; 69 | buildActionMask = 2147483647; 70 | files = ( 71 | DC56BE021E46EA2000AAD0D9 /* TGPControls.h in Headers */, 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | /* End PBXHeadersBuildPhase section */ 76 | 77 | /* Begin PBXNativeTarget section */ 78 | DC56BDFC1E46EA2000AAD0D9 /* TGPControls */ = { 79 | isa = PBXNativeTarget; 80 | buildConfigurationList = DC56BE051E46EA2000AAD0D9 /* Build configuration list for PBXNativeTarget "TGPControls" */; 81 | buildPhases = ( 82 | DC56BDF81E46EA2000AAD0D9 /* Sources */, 83 | DC56BDF91E46EA2000AAD0D9 /* Frameworks */, 84 | DC56BDFA1E46EA2000AAD0D9 /* Headers */, 85 | DC56BDFB1E46EA2000AAD0D9 /* Resources */, 86 | ); 87 | buildRules = ( 88 | ); 89 | dependencies = ( 90 | ); 91 | name = TGPControls; 92 | productName = TGPControls; 93 | productReference = DC56BDFD1E46EA2000AAD0D9 /* TGPControls.framework */; 94 | productType = "com.apple.product-type.framework"; 95 | }; 96 | /* End PBXNativeTarget section */ 97 | 98 | /* Begin PBXProject section */ 99 | DC56BDF41E46EA2000AAD0D9 /* Project object */ = { 100 | isa = PBXProject; 101 | attributes = { 102 | LastUpgradeCheck = 0940; 103 | ORGANIZATIONNAME = SwiftArchitect; 104 | TargetAttributes = { 105 | DC56BDFC1E46EA2000AAD0D9 = { 106 | CreatedOnToolsVersion = 8.2.1; 107 | LastSwiftMigration = 0940; 108 | ProvisioningStyle = Automatic; 109 | }; 110 | }; 111 | }; 112 | buildConfigurationList = DC56BDF71E46EA2000AAD0D9 /* Build configuration list for PBXProject "TGPControls" */; 113 | compatibilityVersion = "Xcode 9.3"; 114 | developmentRegion = English; 115 | hasScannedForEncodings = 0; 116 | knownRegions = ( 117 | en, 118 | ); 119 | mainGroup = DC56BDF31E46EA2000AAD0D9; 120 | productRefGroup = DC56BDFE1E46EA2000AAD0D9 /* Products */; 121 | projectDirPath = ""; 122 | projectRoot = ""; 123 | targets = ( 124 | DC56BDFC1E46EA2000AAD0D9 /* TGPControls */, 125 | ); 126 | }; 127 | /* End PBXProject section */ 128 | 129 | /* Begin PBXResourcesBuildPhase section */ 130 | DC56BDFB1E46EA2000AAD0D9 /* Resources */ = { 131 | isa = PBXResourcesBuildPhase; 132 | buildActionMask = 2147483647; 133 | files = ( 134 | ); 135 | runOnlyForDeploymentPostprocessing = 0; 136 | }; 137 | /* End PBXResourcesBuildPhase section */ 138 | 139 | /* Begin PBXSourcesBuildPhase section */ 140 | DC56BDF81E46EA2000AAD0D9 /* Sources */ = { 141 | isa = PBXSourcesBuildPhase; 142 | buildActionMask = 2147483647; 143 | files = ( 144 | DC88CC501E46EAD10076AC65 /* TGPDiscreteSlider.swift in Sources */, 145 | DC88CC4F1E46EAD10076AC65 /* TGPControlsTicksProtocol.swift in Sources */, 146 | DC88CC4E1E46EAD10076AC65 /* TGPCamelLabels.swift in Sources */, 147 | ); 148 | runOnlyForDeploymentPostprocessing = 0; 149 | }; 150 | /* End PBXSourcesBuildPhase section */ 151 | 152 | /* Begin XCBuildConfiguration section */ 153 | DC56BE031E46EA2000AAD0D9 /* Debug */ = { 154 | isa = XCBuildConfiguration; 155 | buildSettings = { 156 | ALWAYS_SEARCH_USER_PATHS = NO; 157 | CLANG_ANALYZER_NONNULL = YES; 158 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 159 | CLANG_CXX_LIBRARY = "libc++"; 160 | CLANG_ENABLE_MODULES = YES; 161 | CLANG_ENABLE_OBJC_ARC = YES; 162 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 163 | CLANG_WARN_BOOL_CONVERSION = YES; 164 | CLANG_WARN_COMMA = YES; 165 | CLANG_WARN_CONSTANT_CONVERSION = YES; 166 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 167 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 168 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 169 | CLANG_WARN_EMPTY_BODY = YES; 170 | CLANG_WARN_ENUM_CONVERSION = YES; 171 | CLANG_WARN_INFINITE_RECURSION = YES; 172 | CLANG_WARN_INT_CONVERSION = YES; 173 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 174 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 175 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 176 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 177 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 178 | CLANG_WARN_STRICT_PROTOTYPES = YES; 179 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 180 | CLANG_WARN_UNREACHABLE_CODE = YES; 181 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 182 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 183 | COPY_PHASE_STRIP = NO; 184 | CURRENT_PROJECT_VERSION = 1; 185 | DEBUG_INFORMATION_FORMAT = dwarf; 186 | ENABLE_STRICT_OBJC_MSGSEND = YES; 187 | ENABLE_TESTABILITY = YES; 188 | GCC_C_LANGUAGE_STANDARD = gnu99; 189 | GCC_DYNAMIC_NO_PIC = NO; 190 | GCC_NO_COMMON_BLOCKS = YES; 191 | GCC_OPTIMIZATION_LEVEL = 0; 192 | GCC_PREPROCESSOR_DEFINITIONS = ( 193 | "DEBUG=1", 194 | "$(inherited)", 195 | ); 196 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 197 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 198 | GCC_WARN_UNDECLARED_SELECTOR = YES; 199 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 200 | GCC_WARN_UNUSED_FUNCTION = YES; 201 | GCC_WARN_UNUSED_VARIABLE = YES; 202 | MTL_ENABLE_DEBUG_INFO = YES; 203 | ONLY_ACTIVE_ARCH = YES; 204 | SDKROOT = iphoneos; 205 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 206 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 207 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 208 | TARGETED_DEVICE_FAMILY = "1,2"; 209 | VERSIONING_SYSTEM = "apple-generic"; 210 | VERSION_INFO_PREFIX = ""; 211 | }; 212 | name = Debug; 213 | }; 214 | DC56BE041E46EA2000AAD0D9 /* Release */ = { 215 | isa = XCBuildConfiguration; 216 | buildSettings = { 217 | ALWAYS_SEARCH_USER_PATHS = NO; 218 | CLANG_ANALYZER_NONNULL = YES; 219 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 220 | CLANG_CXX_LIBRARY = "libc++"; 221 | CLANG_ENABLE_MODULES = YES; 222 | CLANG_ENABLE_OBJC_ARC = YES; 223 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 224 | CLANG_WARN_BOOL_CONVERSION = YES; 225 | CLANG_WARN_COMMA = YES; 226 | CLANG_WARN_CONSTANT_CONVERSION = YES; 227 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 228 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 229 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 230 | CLANG_WARN_EMPTY_BODY = YES; 231 | CLANG_WARN_ENUM_CONVERSION = YES; 232 | CLANG_WARN_INFINITE_RECURSION = YES; 233 | CLANG_WARN_INT_CONVERSION = YES; 234 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 235 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 236 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 237 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 238 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 239 | CLANG_WARN_STRICT_PROTOTYPES = YES; 240 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 241 | CLANG_WARN_UNREACHABLE_CODE = YES; 242 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 243 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 244 | COPY_PHASE_STRIP = NO; 245 | CURRENT_PROJECT_VERSION = 1; 246 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 247 | ENABLE_NS_ASSERTIONS = NO; 248 | ENABLE_STRICT_OBJC_MSGSEND = YES; 249 | GCC_C_LANGUAGE_STANDARD = gnu99; 250 | GCC_NO_COMMON_BLOCKS = YES; 251 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 252 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 253 | GCC_WARN_UNDECLARED_SELECTOR = YES; 254 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 255 | GCC_WARN_UNUSED_FUNCTION = YES; 256 | GCC_WARN_UNUSED_VARIABLE = YES; 257 | MTL_ENABLE_DEBUG_INFO = NO; 258 | SDKROOT = iphoneos; 259 | SWIFT_COMPILATION_MODE = wholemodule; 260 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 261 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 262 | TARGETED_DEVICE_FAMILY = "1,2"; 263 | VALIDATE_PRODUCT = YES; 264 | VERSIONING_SYSTEM = "apple-generic"; 265 | VERSION_INFO_PREFIX = ""; 266 | }; 267 | name = Release; 268 | }; 269 | DC56BE061E46EA2000AAD0D9 /* Debug */ = { 270 | isa = XCBuildConfiguration; 271 | buildSettings = { 272 | CLANG_ENABLE_MODULES = YES; 273 | CODE_SIGN_IDENTITY = ""; 274 | DEFINES_MODULE = YES; 275 | DEVELOPMENT_TEAM = ""; 276 | DYLIB_COMPATIBILITY_VERSION = 1; 277 | DYLIB_CURRENT_VERSION = 1; 278 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 279 | INFOPLIST_FILE = TGPControls/Info.plist; 280 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 281 | LD_RUNPATH_SEARCH_PATHS = ( 282 | "$(inherited)", 283 | "@executable_path/Frameworks", 284 | "@loader_path/Frameworks", 285 | ); 286 | PRODUCT_BUNDLE_IDENTIFIER = com.swiftarchitect.TGPControls; 287 | PRODUCT_NAME = "$(TARGET_NAME)"; 288 | SKIP_INSTALL = YES; 289 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 290 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 291 | SWIFT_VERSION = 4.0; 292 | }; 293 | name = Debug; 294 | }; 295 | DC56BE071E46EA2000AAD0D9 /* Release */ = { 296 | isa = XCBuildConfiguration; 297 | buildSettings = { 298 | CLANG_ENABLE_MODULES = YES; 299 | CODE_SIGN_IDENTITY = ""; 300 | DEFINES_MODULE = YES; 301 | DEVELOPMENT_TEAM = ""; 302 | DYLIB_COMPATIBILITY_VERSION = 1; 303 | DYLIB_CURRENT_VERSION = 1; 304 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 305 | INFOPLIST_FILE = TGPControls/Info.plist; 306 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 307 | LD_RUNPATH_SEARCH_PATHS = ( 308 | "$(inherited)", 309 | "@executable_path/Frameworks", 310 | "@loader_path/Frameworks", 311 | ); 312 | PRODUCT_BUNDLE_IDENTIFIER = com.swiftarchitect.TGPControls; 313 | PRODUCT_NAME = "$(TARGET_NAME)"; 314 | SKIP_INSTALL = YES; 315 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 316 | SWIFT_VERSION = 4.0; 317 | }; 318 | name = Release; 319 | }; 320 | /* End XCBuildConfiguration section */ 321 | 322 | /* Begin XCConfigurationList section */ 323 | DC56BDF71E46EA2000AAD0D9 /* Build configuration list for PBXProject "TGPControls" */ = { 324 | isa = XCConfigurationList; 325 | buildConfigurations = ( 326 | DC56BE031E46EA2000AAD0D9 /* Debug */, 327 | DC56BE041E46EA2000AAD0D9 /* Release */, 328 | ); 329 | defaultConfigurationIsVisible = 0; 330 | defaultConfigurationName = Release; 331 | }; 332 | DC56BE051E46EA2000AAD0D9 /* Build configuration list for PBXNativeTarget "TGPControls" */ = { 333 | isa = XCConfigurationList; 334 | buildConfigurations = ( 335 | DC56BE061E46EA2000AAD0D9 /* Debug */, 336 | DC56BE071E46EA2000AAD0D9 /* Release */, 337 | ); 338 | defaultConfigurationIsVisible = 0; 339 | defaultConfigurationName = Release; 340 | }; 341 | /* End XCConfigurationList section */ 342 | }; 343 | rootObject = DC56BDF41E46EA2000AAD0D9 /* Project object */; 344 | } 345 | -------------------------------------------------------------------------------- /TGPControls.xcodeproj/xcshareddata/xcschemes/TGPControls.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /TGPControls/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 5.0.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /TGPControls/TGPCamelLabels.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | // Interface builder hides the IBInspectable for UIControl 4 | #if TARGET_INTERFACE_BUILDER 5 | public class TGPCamelLabels_INTERFACE_BUILDER:UIView { 6 | } 7 | #else // !TARGET_INTERFACE_BUILDER 8 | public class TGPCamelLabels_INTERFACE_BUILDER:UIControl { 9 | } 10 | #endif // TARGET_INTERFACE_BUILDER 11 | 12 | @IBDesignable 13 | public class TGPCamelLabels: TGPCamelLabels_INTERFACE_BUILDER { 14 | 15 | let validAttributes = [NSLayoutAttribute.top.rawValue, // 3 16 | NSLayoutAttribute.centerY.rawValue, // 10 17 | NSLayoutAttribute.bottom.rawValue] // 4 18 | 19 | // Only used if labels.count < 1 20 | @IBInspectable public var tickCount:Int { 21 | get { 22 | return names.count 23 | } 24 | set { 25 | // Put some order to tickCount: 1 >= count >= 128 26 | let count = max(1, min(newValue, 128)) 27 | debugNames(count: count) 28 | layoutTrack() 29 | } 30 | } 31 | 32 | @IBInspectable public var ticksDistance:CGFloat = 44.0 { 33 | didSet { 34 | ticksDistance = max(0, ticksDistance) 35 | layoutTrack() 36 | } 37 | } 38 | 39 | @IBInspectable public var value:UInt = 0 { 40 | didSet { 41 | dockEffect(duration: animationDuration) 42 | } 43 | } 44 | 45 | @IBInspectable public var upFontName:String? = nil { 46 | didSet { 47 | layoutTrack() 48 | } 49 | } 50 | 51 | @IBInspectable public var upFontSize:CGFloat = 12 { 52 | didSet { 53 | layoutTrack() 54 | } 55 | } 56 | 57 | @IBInspectable public var upFontColor:UIColor? = nil { 58 | didSet { 59 | layoutTrack() 60 | } 61 | } 62 | 63 | @IBInspectable public var downFontName:String? = nil { 64 | didSet { 65 | layoutTrack() 66 | } 67 | } 68 | 69 | @IBInspectable public var downFontSize:CGFloat = 12 { 70 | didSet { 71 | layoutTrack() 72 | } 73 | } 74 | 75 | @IBInspectable public var downFontColor:UIColor? = nil { 76 | didSet { 77 | layoutTrack() 78 | } 79 | } 80 | 81 | @IBInspectable public var numberOfLinesInLabel:Int = 1 { 82 | didSet { 83 | layoutTrack() 84 | } 85 | } 86 | 87 | // Label off-center to the left and right of the slider 88 | // expressed in label width. 0: none, -1/2: half outside, 1/2; half inside 89 | @IBInspectable public var offCenter:CGFloat = 0 { 90 | didSet { 91 | layoutTrack() 92 | } 93 | } 94 | 95 | // Label margins to the left and right of the slider 96 | @IBInspectable public var insets:NSInteger = 0 { 97 | didSet { 98 | layoutTrack() 99 | } 100 | } 101 | 102 | // Where should emphasized labels be drawn (10: centerY, 3: top, 4: bottom) 103 | // By default, emphasized labels are animated towards the top of the frame. 104 | // This creates the dock effect (camel). They can also be centered vertically, or move down (reverse effect). 105 | @IBInspectable public var emphasisLayout:Int = NSLayoutAttribute.top.rawValue { 106 | didSet { 107 | if !validAttributes.contains(emphasisLayout) { 108 | emphasisLayout = NSLayoutAttribute.top.rawValue 109 | } 110 | layoutTrack() 111 | } 112 | } 113 | 114 | // Where should regular labels be drawn (10: centerY, 3: top, 4: bottom) 115 | // By default, emphasized labels are animated towards the bottom of the frame. 116 | // This creates the dock effect (camel). They can also be centered vertically, or move up (reverse effect). 117 | @IBInspectable public var regularLayout:Int = NSLayoutAttribute.bottom.rawValue { 118 | didSet { 119 | if !validAttributes.contains(regularLayout) { 120 | regularLayout = NSLayoutAttribute.bottom.rawValue 121 | } 122 | layoutTrack() 123 | } 124 | } 125 | 126 | // MARK: @IBInspectable adapters 127 | 128 | public var emphasisLayoutAttribute:NSLayoutAttribute { 129 | get { 130 | return NSLayoutAttribute(rawValue: emphasisLayout) ?? .top 131 | } 132 | set { 133 | emphasisLayout = newValue.rawValue 134 | } 135 | } 136 | 137 | public var regularLayoutAttribute:NSLayoutAttribute { 138 | get { 139 | return NSLayoutAttribute(rawValue: regularLayout) ?? .bottom 140 | } 141 | set { 142 | regularLayout = newValue.rawValue 143 | } 144 | } 145 | 146 | // MARK: Properties 147 | 148 | public var names:[String] = [] { // Will dictate the number of ticks 149 | didSet { 150 | assert(names.count > 0) 151 | layoutTrack() 152 | } 153 | } 154 | 155 | // When bounds change, recalculate layout 156 | override public var bounds: CGRect { 157 | didSet { 158 | layoutTrack() 159 | setNeedsDisplay() 160 | } 161 | } 162 | 163 | public var animationDuration:TimeInterval = 0.15 164 | 165 | // Private 166 | var lastValue = NSNotFound 167 | var emphasizedLabels:[UILabel] = [] 168 | var regularLabels:[UILabel] = [] 169 | var localeCharacterDirection = CFLocaleLanguageDirection.leftToRight 170 | 171 | // MARK: UIView 172 | 173 | required public init?(coder aDecoder: NSCoder) { 174 | super.init(coder: aDecoder) 175 | initProperties() 176 | } 177 | 178 | override init(frame: CGRect) { 179 | super.init(frame: frame) 180 | initProperties() 181 | } 182 | 183 | // clickthrough 184 | public override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { 185 | for view in subviews { 186 | if !view.isHidden && 187 | view.alpha > 0.0 && 188 | view.isUserInteractionEnabled && 189 | view.point(inside: convert(point, to: view), with: event) { 190 | return true 191 | } 192 | } 193 | return false 194 | } 195 | 196 | // MARK: TGPCamelLabels 197 | 198 | func initProperties() { 199 | if let systemLocale = CFLocaleCopyCurrent(), 200 | let localeIdentifier = CFLocaleGetIdentifier(systemLocale) { 201 | localeCharacterDirection = CFLocaleGetLanguageCharacterDirection(localeIdentifier.rawValue) 202 | } 203 | 204 | debugNames(count: 10) 205 | layoutTrack() 206 | } 207 | 208 | func debugNames(count:Int) { 209 | // Dynamic property, will create an array with labels, generally for debugging purposes 210 | var array:[String] = [] 211 | for iterate in 1...count { 212 | array.append("\(iterate)") 213 | } 214 | names = array 215 | } 216 | 217 | func layoutTrack() { 218 | 219 | func insetLabel(_ label:UILabel?, withInset inset:NSInteger, andMultiplier multiplier:CGFloat) { 220 | assert(label != nil) 221 | if let label = label { 222 | label.frame = { 223 | var frame = label.frame 224 | frame.origin.x += frame.size.width * multiplier 225 | frame.origin.x += CGFloat(inset) 226 | return frame 227 | }() 228 | } 229 | } 230 | 231 | for label in emphasizedLabels { 232 | label.removeFromSuperview() 233 | } 234 | emphasizedLabels = [] 235 | for label in regularLabels { 236 | label.removeFromSuperview() 237 | } 238 | regularLabels = [] 239 | 240 | let count = names.count 241 | if count > 0 { 242 | var centerX = (bounds.width - (CGFloat(count - 1) * ticksDistance))/2.0 243 | if .rightToLeft == localeCharacterDirection { 244 | centerX = bounds.width - centerX 245 | } 246 | let tickSpacing = (.rightToLeft == localeCharacterDirection) 247 | ? -ticksDistance 248 | : ticksDistance 249 | let centerY = bounds.height / 2.0 250 | for name in names { 251 | let upLabel = UILabel.init() 252 | upLabel.numberOfLines = numberOfLinesInLabel 253 | emphasizedLabels.append(upLabel) 254 | upLabel.text = name 255 | if let upFontName = upFontName { 256 | upLabel.font = UIFont.init(name: upFontName, size: upFontSize) 257 | } else { 258 | upLabel.font = UIFont.boldSystemFont(ofSize: upFontSize) 259 | } 260 | if let textColor = upFontColor ?? tintColor { 261 | upLabel.textColor = textColor 262 | } 263 | upLabel.sizeToFit() 264 | upLabel.center = CGPoint(x: centerX, y: centerY) 265 | 266 | upLabel.frame = { 267 | var frame = upLabel.frame 268 | frame.origin.y = bounds.height - frame.height 269 | return frame 270 | }() 271 | 272 | upLabel.alpha = 0.0 273 | addSubview(upLabel) 274 | 275 | let dnLabel = UILabel.init() 276 | dnLabel.numberOfLines = numberOfLinesInLabel 277 | regularLabels.append(dnLabel) 278 | dnLabel.text = name 279 | if let downFontName = downFontName { 280 | dnLabel.font = UIFont.init(name:downFontName, size:downFontSize) 281 | } else { 282 | dnLabel.font = UIFont.boldSystemFont(ofSize: downFontSize) 283 | } 284 | dnLabel.textColor = downFontColor ?? UIColor.gray 285 | dnLabel.sizeToFit() 286 | dnLabel.center = CGPoint(x:centerX, y:centerY) 287 | dnLabel.frame = { 288 | var frame = dnLabel.frame 289 | frame.origin.y = bounds.height - frame.height 290 | return frame 291 | }() 292 | addSubview(dnLabel) 293 | 294 | centerX += tickSpacing 295 | } 296 | 297 | // Fix left and right label, if there are at least 2 labels 298 | if names.count > 1 { 299 | let localeInsets = (.rightToLeft == localeCharacterDirection) 300 | ? -insets 301 | : insets 302 | let localeOffCenter = (.rightToLeft == localeCharacterDirection) 303 | ? -offCenter 304 | : offCenter 305 | insetLabel(emphasizedLabels.first, withInset: localeInsets, andMultiplier: localeOffCenter) 306 | insetLabel(emphasizedLabels.last, withInset: -localeInsets, andMultiplier: -localeOffCenter) 307 | insetLabel(regularLabels.first, withInset: localeInsets, andMultiplier: localeOffCenter) 308 | insetLabel(regularLabels.last, withInset: -localeInsets, andMultiplier: -localeOffCenter) 309 | } 310 | 311 | dockEffect(duration:0.0) 312 | } 313 | } 314 | 315 | 316 | func dockEffect(duration:TimeInterval) 317 | { 318 | let emphasized = Int(value) 319 | 320 | // Unlike the National Parks from which it is inspired, this Dock Effect 321 | // does not abruptly change from BOLD to plain. Instead, we have 2 sets of 322 | // labels, which are faded back and forth, in unisson. 323 | // - BOLD to plain 324 | // - Black to gray 325 | // - high to low 326 | // Each animation picks up where the previous left off 327 | let moveBlock:() -> Void = { 328 | // De-emphasize almost all 329 | for (idx, label) in self.emphasizedLabels.enumerated() { 330 | if emphasized != idx { 331 | self.verticalAlign(aView: label, 332 | alpha: 0, 333 | attribute: self.regularLayoutAttribute) 334 | } 335 | } 336 | for (idx, label) in self.regularLabels.enumerated() { 337 | if emphasized != idx { 338 | self.verticalAlign(aView: label, 339 | alpha: 1, 340 | attribute: self.regularLayoutAttribute) 341 | } 342 | } 343 | 344 | // Emphasize the selection 345 | if emphasized < self.emphasizedLabels.count { 346 | self.verticalAlign(aView: self.emphasizedLabels[emphasized], 347 | alpha:1, 348 | attribute: self.emphasisLayoutAttribute) 349 | } 350 | if emphasized < self.regularLabels.count { 351 | self.verticalAlign(aView: self.regularLabels[emphasized], 352 | alpha:0, 353 | attribute: self.emphasisLayoutAttribute) 354 | } 355 | } 356 | 357 | if duration > 0 { 358 | UIView.animate(withDuration: duration, 359 | delay: 0, 360 | options: [.beginFromCurrentState, .curveLinear], 361 | animations: moveBlock, 362 | completion: nil) 363 | } else { 364 | moveBlock() 365 | } 366 | } 367 | 368 | func verticalAlign(aView:UIView, alpha:CGFloat, attribute layout:NSLayoutAttribute) { 369 | switch layout { 370 | case .top: 371 | aView.frame = { 372 | var frame = aView.frame 373 | frame.origin.y = 0 374 | return frame 375 | }() 376 | 377 | case .bottom: 378 | aView.frame = { 379 | var frame = aView.frame 380 | frame.origin.y = bounds.height - frame.height 381 | return frame 382 | }() 383 | 384 | default: // .center 385 | aView.frame = { 386 | var frame = aView.frame 387 | frame.origin.y = (bounds.height - frame.height) / 2 388 | return frame 389 | }() 390 | } 391 | aView.alpha = alpha 392 | } 393 | } 394 | 395 | extension TGPCamelLabels : TGPControlsTicksProtocol { 396 | public func tgpTicksDistanceChanged(ticksDistance: CGFloat, sender: AnyObject) { 397 | self.ticksDistance = ticksDistance 398 | } 399 | 400 | public func tgpValueChanged(value: UInt) { 401 | self.value = value 402 | } 403 | } 404 | -------------------------------------------------------------------------------- /TGPControls/TGPControls.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | //! Project version number for TGPControls. 4 | FOUNDATION_EXPORT double TGPControlsVersionNumber; 5 | 6 | //! Project version string for TGPControls. 7 | FOUNDATION_EXPORT const unsigned char TGPControlsVersionString[]; 8 | 9 | // In this header, you should import all the public headers of your framework using statements like #import 10 | -------------------------------------------------------------------------------- /TGPControls/TGPControlsTicksProtocol.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objc public protocol TGPControlsTicksProtocol 4 | { 5 | func tgpTicksDistanceChanged(ticksDistance:CGFloat, sender:AnyObject) 6 | func tgpValueChanged(value:UInt) 7 | } 8 | -------------------------------------------------------------------------------- /TGPControls/TGPDiscreteSlider.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public enum ComponentStyle:Int { 4 | case iOS = 0 5 | case rectangular 6 | case rounded 7 | case invisible 8 | case image 9 | } 10 | 11 | // Interface builder hides the IBInspectable for UIControl 12 | #if TARGET_INTERFACE_BUILDER 13 | public class TGPSlider_INTERFACE_BUILDER:UIView { 14 | } 15 | #else // !TARGET_INTERFACE_BUILDER 16 | public class TGPSlider_INTERFACE_BUILDER:UIControl { 17 | } 18 | #endif // TARGET_INTERFACE_BUILDER 19 | 20 | @IBDesignable 21 | public class TGPDiscreteSlider:TGPSlider_INTERFACE_BUILDER { 22 | 23 | @IBInspectable public var tickStyle:Int = ComponentStyle.rectangular.rawValue { 24 | didSet { 25 | layoutTrack() 26 | } 27 | } 28 | 29 | @IBInspectable public var tickSize:CGSize = CGSize(width:1, height:4) { 30 | didSet { 31 | tickSize.width = max(0, tickSize.width) 32 | tickSize.height = max(0, tickSize.height) 33 | layoutTrack() 34 | } 35 | } 36 | 37 | @IBInspectable public var tickCount:Int = 11 { 38 | didSet { 39 | tickCount = max(2, tickCount) 40 | layoutTrack() 41 | } 42 | } 43 | 44 | @IBInspectable public var minimumTickTintColor:UIColor? = nil { 45 | didSet { 46 | layoutTrack() 47 | } 48 | } 49 | 50 | @IBInspectable public var maximumTickTintColor:UIColor? = nil { 51 | didSet { 52 | layoutTrack() 53 | } 54 | } 55 | 56 | @IBInspectable public var tickImage:UIImage? = nil { 57 | didSet { 58 | layoutTrack() 59 | } 60 | } 61 | 62 | @IBInspectable public var trackStyle:Int = ComponentStyle.iOS.rawValue { 63 | didSet { 64 | layoutTrack() 65 | } 66 | } 67 | 68 | @IBInspectable public var trackThickness:CGFloat = 2 { 69 | didSet { 70 | trackThickness = max(0, trackThickness) 71 | layoutTrack() 72 | } 73 | } 74 | 75 | @IBInspectable public var trackImage:UIImage? = nil { 76 | didSet { 77 | layoutTrack() 78 | } 79 | } 80 | 81 | @IBInspectable public var minimumTrackTintColor:UIColor? = nil { 82 | didSet { 83 | layoutTrack() 84 | } 85 | } 86 | 87 | @IBInspectable public var maximumTrackTintColor = UIColor(white: 0.71, alpha: 1) { 88 | didSet { 89 | layoutTrack() 90 | } 91 | } 92 | 93 | @IBInspectable public var thumbStyle:Int = ComponentStyle.iOS.rawValue { 94 | didSet { 95 | layoutTrack() 96 | } 97 | } 98 | 99 | @IBInspectable public var thumbSize:CGSize = CGSize(width:10, height:10) { 100 | didSet { 101 | thumbSize.width = max(1, thumbSize.width) 102 | thumbSize.height = max(1, thumbSize.height) 103 | layoutTrack() 104 | } 105 | } 106 | 107 | @IBInspectable public var thumbTintColor:UIColor? = nil { 108 | didSet { 109 | layoutTrack() 110 | } 111 | } 112 | 113 | @IBInspectable public var thumbImage:UIImage? = nil { 114 | didSet { 115 | if let thumbImage = thumbImage { 116 | thumbLayer.contents = thumbImage.cgImage 117 | } else { 118 | thumbLayer.contents = nil 119 | } 120 | layoutTrack() 121 | } 122 | } 123 | 124 | @IBInspectable public var thumbShadowRadius:CGFloat = 0 { 125 | didSet { 126 | layoutTrack() 127 | } 128 | } 129 | 130 | @IBInspectable public var thumbShadowOffset:CGSize = CGSize.zero { 131 | didSet { 132 | layoutTrack() 133 | } 134 | } 135 | 136 | @IBInspectable public var incrementValue:Int = 1 { 137 | didSet { 138 | if(0 == incrementValue) { 139 | incrementValue = 1; // nonZeroIncrement 140 | } 141 | layoutTrack() 142 | } 143 | } 144 | 145 | // MARK: UISlider substitution 146 | // AKA: UISlider value (as CGFloat for compatibility with UISlider API, but expected to contain integers) 147 | 148 | @IBInspectable public var minimumValue:CGFloat { 149 | get { 150 | return CGFloat(intMinimumValue) 151 | } 152 | set { 153 | intMinimumValue = Int(newValue) 154 | layoutTrack() 155 | } 156 | } 157 | 158 | @IBInspectable public var value:CGFloat { 159 | get { 160 | return CGFloat(intValue) 161 | } 162 | set { 163 | intValue = Int(newValue) 164 | layoutTrack() 165 | } 166 | } 167 | 168 | // MARK: @IBInspectable adapters 169 | 170 | public var tickComponentStyle:ComponentStyle { 171 | get { 172 | return ComponentStyle(rawValue: tickStyle) ?? .rectangular 173 | } 174 | set { 175 | tickStyle = newValue.rawValue 176 | } 177 | } 178 | 179 | public var trackComponentStyle:ComponentStyle { 180 | get { 181 | return ComponentStyle(rawValue: trackStyle) ?? .iOS 182 | } 183 | set { 184 | trackStyle = newValue.rawValue 185 | } 186 | } 187 | 188 | public var thumbComponentStyle:ComponentStyle { 189 | get { 190 | return ComponentStyle(rawValue: thumbStyle) ?? .iOS 191 | } 192 | set { 193 | thumbStyle = newValue.rawValue 194 | } 195 | } 196 | 197 | // MARK: Properties 198 | 199 | public override var tintColor: UIColor! { 200 | didSet { 201 | layoutTrack() 202 | } 203 | } 204 | 205 | public override var bounds: CGRect { 206 | didSet { 207 | layoutTrack() 208 | } 209 | } 210 | 211 | public var ticksDistance:CGFloat { 212 | get { 213 | assert(tickCount > 1, "2 ticks minimum \(tickCount)") 214 | let segments = CGFloat(max(1, tickCount - 1)) 215 | return trackRectangle.width / segments 216 | } 217 | } 218 | 219 | @objc public var ticksListener:TGPControlsTicksProtocol? = nil { 220 | didSet { 221 | ticksListener?.tgpTicksDistanceChanged(ticksDistance: ticksDistance, 222 | sender: self) 223 | } 224 | } 225 | 226 | var intValue:Int = 0 227 | var intMinimumValue = -5 228 | 229 | var ticksAbscissae:[CGPoint] = [] 230 | var thumbAbscissa:CGFloat = 0 231 | var thumbLayer = CALayer() 232 | 233 | var trackLayer = CALayer() 234 | var leftTrackLayer = CALayer() 235 | var rightTrackLayer = CALayer() 236 | var leadingTrackLayer: CALayer! 237 | var trailingTrackLayer: CALayer! 238 | 239 | var ticksLayer = CALayer() 240 | var leftTicksLayer = CALayer() 241 | var rightTicksLayer = CALayer() 242 | var leadingTicksLayer: CALayer! 243 | var trailingTicksLayer: CALayer! 244 | 245 | var trackRectangle = CGRect.zero 246 | var touchedInside = false 247 | var localeCharacterDirection = CFLocaleLanguageDirection.leftToRight 248 | 249 | let iOSThumbShadowRadius:CGFloat = 4 250 | let iOSThumbShadowOffset = CGSize(width:0, height:3) 251 | 252 | // MARK: UIControl 253 | 254 | required public init?(coder aDecoder: NSCoder) { 255 | super.init(coder: aDecoder) 256 | initProperties() 257 | } 258 | 259 | override init(frame: CGRect) { 260 | super.init(frame: frame) 261 | initProperties() 262 | } 263 | 264 | public override func draw(_ rect: CGRect) { 265 | drawTrack() 266 | drawTicks() 267 | drawThumb() 268 | } 269 | 270 | func sendActionsForControlEvents() { 271 | // Automatic UIControlEventValueChanged notification 272 | if let ticksListener = ticksListener { 273 | ticksListener.tgpValueChanged(value: UInt(value-minimumValue)) 274 | } 275 | } 276 | 277 | // MARK: TGPDiscreteSlider 278 | 279 | func initProperties() { 280 | if let systemLocale = CFLocaleCopyCurrent(), 281 | let localeIdentifier = CFLocaleGetIdentifier(systemLocale) { 282 | localeCharacterDirection = CFLocaleGetLanguageCharacterDirection(localeIdentifier.rawValue) 283 | } 284 | 285 | leadingTrackLayer = (.rightToLeft == localeCharacterDirection) 286 | ? rightTrackLayer 287 | : leftTrackLayer 288 | trailingTrackLayer = (.rightToLeft == localeCharacterDirection) 289 | ? leftTrackLayer 290 | : rightTrackLayer 291 | leadingTicksLayer = (.rightToLeft == localeCharacterDirection) 292 | ? rightTicksLayer 293 | : leftTicksLayer 294 | trailingTicksLayer = (.rightToLeft == localeCharacterDirection) 295 | ? leftTicksLayer 296 | : rightTicksLayer 297 | 298 | // Track and ticks are in a clear clipping layer, and left + right sublayers, 299 | // which brings in free animation 300 | trackLayer.masksToBounds = true 301 | trackLayer.backgroundColor = UIColor.clear.cgColor 302 | layer.addSublayer(trackLayer) 303 | trackLayer.addSublayer(leftTrackLayer) 304 | trackLayer.addSublayer(rightTrackLayer) 305 | 306 | // Ticks in between track and thumb 307 | ticksLayer.masksToBounds = true 308 | ticksLayer.backgroundColor = UIColor.clear.cgColor 309 | layer.addSublayer(ticksLayer) 310 | ticksLayer.addSublayer(rightTicksLayer) // reverse order, left covers right 311 | ticksLayer.addSublayer(leftTicksLayer) 312 | 313 | // The thumb is its own CALayer, which brings in free animation 314 | layer.addSublayer(thumbLayer) 315 | 316 | isMultipleTouchEnabled = false 317 | layoutTrack() 318 | } 319 | 320 | func drawTicks() { 321 | ticksLayer.frame = bounds 322 | let path = UIBezierPath() 323 | 324 | switch tickComponentStyle { 325 | case .rounded: 326 | fallthrough 327 | 328 | case .rectangular: 329 | fallthrough 330 | 331 | case .image: 332 | for originPoint in ticksAbscissae { 333 | let rectangle = CGRect(x: originPoint.x-(tickSize.width/2), 334 | y: originPoint.y-(tickSize.height/2), 335 | width: tickSize.width, 336 | height: tickSize.height) 337 | switch tickComponentStyle { 338 | case .rounded: 339 | path.append(UIBezierPath(roundedRect: rectangle, 340 | cornerRadius: rectangle.height/2)) 341 | 342 | case .rectangular: 343 | path.append(UIBezierPath(rect: rectangle)) 344 | 345 | case .image: 346 | // Draw image if exists 347 | if let image = tickImage, 348 | let cgImage = image.cgImage, 349 | let ctx = UIGraphicsGetCurrentContext() { 350 | let centered = CGRect(x: rectangle.origin.x + (rectangle.width/2) - (image.size.width/2), 351 | y: rectangle.origin.y + (rectangle.height/2) - (image.size.height/2), 352 | width: image.size.width, 353 | height: image.size.height) 354 | ctx.draw(cgImage, in: centered) 355 | } 356 | 357 | case .invisible: 358 | fallthrough 359 | 360 | case .iOS: 361 | fallthrough 362 | 363 | default: 364 | assert(false) 365 | break 366 | } 367 | } 368 | 369 | case .invisible: 370 | fallthrough 371 | 372 | case .iOS: 373 | fallthrough 374 | 375 | default: 376 | // Nothing to draw 377 | break 378 | } 379 | 380 | leftTicksLayer.frame = { 381 | var frame = ticksLayer.bounds 382 | let tickWidth = (.rightToLeft == localeCharacterDirection) 383 | ? -tickSize.width/2 384 | : tickSize.width/2 385 | frame.size.width = tickWidth + thumbAbscissa 386 | 387 | return frame 388 | }() 389 | 390 | leftTicksLayer.mask = { 391 | let maskLayer = CAShapeLayer() 392 | maskLayer.frame = ticksLayer.bounds 393 | maskLayer.path = path.cgPath 394 | return maskLayer 395 | }() 396 | 397 | rightTicksLayer.frame = ticksLayer.bounds 398 | 399 | rightTicksLayer.mask = { 400 | let maskLayer = CAShapeLayer() 401 | maskLayer.path = path.cgPath 402 | return maskLayer 403 | }() 404 | 405 | if let backgroundColor = minimumTickTintColor ?? (minimumTrackTintColor ?? tintColor) { 406 | leadingTicksLayer.backgroundColor = backgroundColor.cgColor 407 | } 408 | trailingTicksLayer.backgroundColor = maximumTickTintColor?.cgColor ?? maximumTrackTintColor.cgColor 409 | } 410 | 411 | func drawTrack() { 412 | switch(trackComponentStyle) { 413 | case .rectangular: 414 | trackLayer.frame = trackRectangle 415 | trackLayer.cornerRadius = 0.0 416 | 417 | case .invisible: 418 | trackLayer.frame = CGRect.zero 419 | 420 | case .image: 421 | trackLayer.frame = CGRect.zero 422 | 423 | // Draw image if exists 424 | if let image = trackImage, 425 | let cgImage = image.cgImage, 426 | let ctx = UIGraphicsGetCurrentContext() { 427 | let centered = CGRect(x: (frame.width/2) - (image.size.width/2), 428 | y: (frame.height/2) - (image.size.height/2), 429 | width: image.size.width, 430 | height: image.size.height) 431 | ctx.draw(cgImage, in: centered) 432 | } 433 | 434 | case .rounded: 435 | fallthrough 436 | 437 | case .iOS: 438 | fallthrough 439 | 440 | default: 441 | trackLayer.frame = trackRectangle 442 | trackLayer.cornerRadius = trackRectangle.height/2 443 | break 444 | } 445 | 446 | leftTrackLayer.frame = { 447 | var frame = trackLayer.bounds 448 | frame.size.width = thumbAbscissa - trackRectangle.minX 449 | return frame 450 | }() 451 | 452 | rightTrackLayer.frame = { 453 | var frame = trackLayer.bounds 454 | frame.size.width = trackRectangle.width - leftTrackLayer.frame.width 455 | frame.origin.x = leftTrackLayer.frame.maxX 456 | return frame 457 | }() 458 | 459 | if let backgroundColor = minimumTrackTintColor ?? tintColor { 460 | leadingTrackLayer.backgroundColor = backgroundColor.cgColor 461 | } 462 | trailingTrackLayer.backgroundColor = maximumTrackTintColor.cgColor 463 | } 464 | 465 | func drawThumb() { 466 | if( value >= minimumValue) { // Feature: hide the thumb when below range 467 | 468 | let thumbSizeForStyle = thumbSizeIncludingShadow() 469 | let thumbWidth = thumbSizeForStyle.width 470 | let thumbHeight = thumbSizeForStyle.height 471 | let rectangle = CGRect(x:thumbAbscissa - (thumbWidth / 2), 472 | y: (frame.height - thumbHeight)/2, 473 | width: thumbWidth, 474 | height: thumbHeight) 475 | 476 | let shadowRadius = (thumbComponentStyle == .iOS) ? iOSThumbShadowRadius : thumbShadowRadius 477 | let shadowOffset = (thumbComponentStyle == .iOS) ? iOSThumbShadowOffset : thumbShadowOffset 478 | 479 | thumbLayer.frame = ((shadowRadius != 0.0) // Ignore offset if there is no shadow 480 | ? rectangle.insetBy(dx: shadowRadius + shadowOffset.width, 481 | dy: shadowRadius + shadowOffset.height) 482 | : rectangle.insetBy(dx: shadowRadius, 483 | dy: shadowRadius)) 484 | 485 | switch thumbComponentStyle { 486 | case .rounded: // A rounded thumb is circular 487 | thumbLayer.backgroundColor = (thumbTintColor ?? UIColor.lightGray).cgColor 488 | thumbLayer.borderColor = UIColor.clear.cgColor 489 | thumbLayer.borderWidth = 0.0 490 | thumbLayer.cornerRadius = thumbLayer.frame.width/2 491 | thumbLayer.allowsEdgeAntialiasing = true 492 | 493 | case .image: 494 | // image is set using layer.contents 495 | thumbLayer.backgroundColor = UIColor.clear.cgColor 496 | thumbLayer.borderColor = UIColor.clear.cgColor 497 | thumbLayer.borderWidth = 0.0 498 | thumbLayer.cornerRadius = 0.0 499 | thumbLayer.allowsEdgeAntialiasing = false 500 | 501 | case .rectangular: 502 | thumbLayer.backgroundColor = (thumbTintColor ?? UIColor.lightGray).cgColor 503 | thumbLayer.borderColor = UIColor.clear.cgColor 504 | thumbLayer.borderWidth = 0.0 505 | thumbLayer.cornerRadius = 0.0 506 | thumbLayer.allowsEdgeAntialiasing = false 507 | 508 | case .invisible: 509 | thumbLayer.backgroundColor = UIColor.clear.cgColor 510 | thumbLayer.cornerRadius = 0.0 511 | 512 | case .iOS: 513 | fallthrough 514 | 515 | default: 516 | thumbLayer.backgroundColor = (thumbTintColor ?? UIColor.white).cgColor 517 | 518 | // Only default iOS thumb has a border 519 | if nil == thumbTintColor { 520 | let borderColor = UIColor(white:0.5, alpha: 1) 521 | thumbLayer.borderColor = borderColor.cgColor 522 | thumbLayer.borderWidth = 0.25 523 | } else { 524 | thumbLayer.borderWidth = 0 525 | } 526 | thumbLayer.cornerRadius = thumbLayer.frame.width/2 527 | thumbLayer.allowsEdgeAntialiasing = true 528 | break 529 | } 530 | 531 | // Shadow 532 | if(shadowRadius != 0.0) { 533 | #if TARGET_INTERFACE_BUILDER 534 | thumbLayer.shadowOffset = CGSize(width: shadowOffset.width, 535 | height: -shadowOffset.height) 536 | #else // !TARGET_INTERFACE_BUILDER 537 | thumbLayer.shadowOffset = shadowOffset 538 | #endif // TARGET_INTERFACE_BUILDER 539 | 540 | thumbLayer.shadowRadius = shadowRadius 541 | thumbLayer.shadowColor = UIColor.black.cgColor 542 | thumbLayer.shadowOpacity = 0.15 543 | } else { 544 | thumbLayer.shadowRadius = 0.0 545 | thumbLayer.shadowOffset = CGSize.zero 546 | thumbLayer.shadowColor = UIColor.clear.cgColor 547 | thumbLayer.shadowOpacity = 0.0 548 | } 549 | } 550 | } 551 | 552 | func layoutTrack() { 553 | assert(tickCount > 1, "2 ticks minimum \(tickCount)") 554 | let segments = max(1, tickCount - 1) 555 | let thumbWidth = thumbSizeIncludingShadow().width 556 | 557 | // Calculate the track ticks positions 558 | let trackHeight = (.iOS == trackComponentStyle) ? 2 : trackThickness 559 | var trackSize = CGSize(width: frame.width - thumbWidth, 560 | height: trackHeight) 561 | if(.image == trackComponentStyle) { 562 | if let image = trackImage { 563 | trackSize.width = image.size.width - thumbWidth 564 | } 565 | } 566 | 567 | trackRectangle = CGRect(x: (frame.width - trackSize.width)/2, 568 | y: (frame.height - trackSize.height)/2, 569 | width: trackSize.width, 570 | height: trackSize.height) 571 | let trackY = frame.height / 2 572 | ticksAbscissae = [] 573 | for iterate in 0 ... segments { 574 | let ratio = Double(iterate) / Double(segments) 575 | let originX = trackRectangle.origin.x + (CGFloat)(trackSize.width * CGFloat(ratio)) 576 | ticksAbscissae.append(CGPoint(x: originX, y: trackY)) 577 | } 578 | layoutThumb() 579 | 580 | // If we have a TGPDiscreteSliderTicksListener (such as TGPCamelLabels), broadcast new spacing 581 | ticksListener?.tgpTicksDistanceChanged(ticksDistance:ticksDistance, sender:self) 582 | setNeedsDisplay() 583 | } 584 | 585 | func layoutThumb() { 586 | assert(tickCount > 1, "2 ticks minimum \(tickCount)") 587 | let segments = max(1, tickCount - 1) 588 | 589 | // Calculate the thumb position 590 | let nonZeroIncrement = ((0 == incrementValue) ? 1 : incrementValue) 591 | var thumbRatio = Double(value - minimumValue) / Double(segments * nonZeroIncrement) 592 | thumbRatio = max(0.0, min(thumbRatio, 1.0)) // Normalized 593 | thumbRatio = (.rightToLeft == localeCharacterDirection) 594 | ? 1.0 - thumbRatio 595 | : thumbRatio 596 | thumbAbscissa = trackRectangle.origin.x + (CGFloat)(trackRectangle.width * CGFloat(thumbRatio)) 597 | } 598 | 599 | func thumbSizeIncludingShadow() -> CGSize { 600 | switch thumbComponentStyle { 601 | case .invisible: 602 | fallthrough 603 | 604 | case .rectangular: 605 | fallthrough 606 | 607 | case .rounded: 608 | return ((thumbShadowRadius != 0.0) 609 | ? CGSize(width:thumbSize.width 610 | + (thumbShadowRadius * 2) 611 | + (thumbShadowOffset.width * 2), 612 | height: thumbSize.height 613 | + (thumbShadowRadius * 2) 614 | + (thumbShadowOffset.height * 2)) 615 | : thumbSize) 616 | 617 | case .iOS: 618 | return CGSize(width: 28.0 619 | + (iOSThumbShadowRadius * 2) 620 | + (iOSThumbShadowOffset.width * 2), 621 | height: 28.0 622 | + (iOSThumbShadowRadius * 2) 623 | + (iOSThumbShadowOffset.height * 2)) 624 | 625 | case .image: 626 | if let thumbImage = thumbImage { 627 | return thumbImage.size 628 | } 629 | fallthrough 630 | 631 | default: 632 | return CGSize(width: 33, height: 33) 633 | } 634 | } 635 | 636 | // MARK: UIResponder 637 | public override func touchesBegan(_ touches: Set, with event: UIEvent?) { 638 | touchedInside = true 639 | 640 | touchDown(touches, animationDuration: 0.1) 641 | sendActionForControlEvent(controlEvent: .valueChanged, with: event) 642 | sendActionForControlEvent(controlEvent: .touchDown, with:event) 643 | 644 | if let touch = touches.first { 645 | if touch.tapCount > 1 { 646 | sendActionForControlEvent(controlEvent: .touchDownRepeat, with: event) 647 | } 648 | } 649 | } 650 | 651 | public override func touchesMoved(_ touches: Set, with event: UIEvent?) { 652 | touchDown(touches, animationDuration:0) 653 | 654 | let inside = touchesAreInside(touches) 655 | sendActionForControlEvent(controlEvent: .valueChanged, with: event) 656 | 657 | if inside != touchedInside { // Crossing boundary 658 | sendActionForControlEvent(controlEvent: (inside) ? .touchDragEnter : .touchDragExit, 659 | with: event) 660 | touchedInside = inside 661 | } 662 | // Drag 663 | sendActionForControlEvent(controlEvent: (inside) ? .touchDragInside : .touchDragOutside, 664 | with: event) 665 | } 666 | 667 | public override func touchesEnded(_ touches: Set, with event: UIEvent?) { 668 | touchUp(touches) 669 | 670 | sendActionForControlEvent(controlEvent: .valueChanged, with: event) 671 | sendActionForControlEvent(controlEvent: (touchesAreInside(touches)) ? .touchUpInside : .touchUpOutside, 672 | with: event) 673 | } 674 | 675 | public override func touchesCancelled(_ touches: Set, with event: UIEvent?) { 676 | touchUp(touches) 677 | 678 | sendActionForControlEvent(controlEvent: .valueChanged, with:event) 679 | sendActionForControlEvent(controlEvent: .touchCancel, with:event) 680 | } 681 | 682 | 683 | // MARK: Touches 684 | 685 | func touchDown(_ touches: Set, animationDuration duration:TimeInterval) { 686 | if let touch = touches.first { 687 | let location = touch.location(in: touch.view) 688 | moveThumbTo(abscissa: location.x, animationDuration: duration) 689 | } 690 | } 691 | 692 | func touchUp(_ touches: Set) { 693 | if let touch = touches.first { 694 | let location = touch.location(in: touch.view) 695 | let tick = pickTickFromSliderPosition(abscissa: location.x) 696 | moveThumbToTick(tick: tick) 697 | } 698 | } 699 | 700 | func touchesAreInside(_ touches: Set) -> Bool { 701 | var inside = false 702 | if let touch = touches.first { 703 | let location = touch.location(in: touch.view) 704 | if let bounds = touch.view?.bounds { 705 | inside = bounds.contains(location) 706 | } 707 | } 708 | return inside 709 | } 710 | 711 | // MARK: Notifications 712 | 713 | func moveThumbToTick(tick: UInt) { 714 | let nonZeroIncrement = ((0 == incrementValue) ? 1 : incrementValue) 715 | let intValue = Int(minimumValue) + (Int(tick) * nonZeroIncrement) 716 | if intValue != self.intValue { 717 | self.intValue = intValue 718 | sendActionsForControlEvents() 719 | } 720 | 721 | layoutThumb() 722 | setNeedsDisplay() 723 | } 724 | 725 | func moveThumbTo(abscissa:CGFloat, animationDuration duration:TimeInterval) { 726 | let leftMost = trackRectangle.minX 727 | let rightMost = trackRectangle.maxX 728 | 729 | thumbAbscissa = max(leftMost, min(abscissa, rightMost)) 730 | CATransaction.setAnimationDuration(duration) 731 | 732 | let tick = pickTickFromSliderPosition(abscissa: thumbAbscissa) 733 | let nonZeroIncrement = ((0 == incrementValue) ? 1 : incrementValue) 734 | let intValue = Int(minimumValue) + (Int(tick) * nonZeroIncrement) 735 | if intValue != self.intValue { 736 | self.intValue = intValue 737 | sendActionsForControlEvents() 738 | } 739 | 740 | setNeedsDisplay() 741 | } 742 | 743 | func pickTickFromSliderPosition(abscissa: CGFloat) -> UInt { 744 | let leftMost = trackRectangle.minX 745 | let rightMost = trackRectangle.maxX 746 | let clampedAbscissa = max(leftMost, min(abscissa, rightMost)) 747 | var ratio = Double(clampedAbscissa - leftMost) / Double(rightMost - leftMost) 748 | ratio = (.rightToLeft == localeCharacterDirection) 749 | ? 1.0 - ratio 750 | : ratio 751 | let segments = max(1, tickCount - 1) 752 | return UInt(round( Double(segments) * ratio)) 753 | } 754 | 755 | func sendActionForControlEvent(controlEvent:UIControlEvents, with event:UIEvent?) { 756 | for target in allTargets { 757 | if let caActions = actions(forTarget: target, forControlEvent: controlEvent) { 758 | for actionName in caActions { 759 | sendAction(NSSelectorFromString(actionName), to: target, for: event) 760 | } 761 | } 762 | } 763 | } 764 | 765 | #if TARGET_INTERFACE_BUILDER 766 | // MARK: TARGET_INTERFACE_BUILDER stub 767 | // Interface builder hides the IBInspectable for UIControl 768 | 769 | let allTargets: Set = Set() 770 | func addTarget(_ target: Any?, action: Selector, for controlEvents: UIControlEvents) {} 771 | func actions(forTarget target: Any?, forControlEvent controlEvent: UIControlEvents) -> [String]? { return nil } 772 | func sendAction(_ action: Selector, to target: Any?, for event: UIEvent?) {} 773 | #endif // TARGET_INTERFACE_BUILDER 774 | } 775 | -------------------------------------------------------------------------------- /TGPControlsDemo/Cartfile: -------------------------------------------------------------------------------- 1 | github "SwiftArchitect/TGPControls" 2 | 3 | -------------------------------------------------------------------------------- /TGPControlsDemo/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '8.0' 2 | 3 | use_frameworks! 4 | 5 | target 'TGPControlsDemo-Pods' do 6 | #pod 'TGPControls', :path => '../../TGPControls' 7 | pod 'TGPControls' 8 | end 9 | 10 | # Failed to render and update auto layout status for UIViewController : dlopen(Cosmos.framework, 1): no suitable image found 11 | # https://github.com/CocoaPods/CocoaPods/issues/7606#issuecomment-381279098 12 | # https://stackoverflow.com/a/49936237/218152 13 | post_install do |installer| 14 | installer.pods_project.targets.each do |target| 15 | target.build_configurations.each do |config| 16 | config.build_settings.delete('CODE_SIGNING_ALLOWED') 17 | config.build_settings.delete('CODE_SIGNING_REQUIRED') 18 | end 19 | end 20 | installer.pods_project.build_configurations.each do |config| 21 | config.build_settings.delete('CODE_SIGNING_ALLOWED') 22 | config.build_settings.delete('CODE_SIGNING_REQUIRED') 23 | end 24 | end 25 | 26 | -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | AC26D296425BA1D414B87647 /* Pods_TGPControlsDemo_Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 96ADF77D1CF3E3C9B5F25B38 /* Pods_TGPControlsDemo_Pods.framework */; }; 11 | DC4FF6431EA2E08C00BBF8E4 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC56BDC81E46DEB900AAD0D9 /* ViewController.swift */; }; 12 | DC4FF6441EA2E08C00BBF8E4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC56BDC61E46DEB900AAD0D9 /* AppDelegate.swift */; }; 13 | DC4FF6481EA2E08C00BBF8E4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DC56BDCF1E46DEB900AAD0D9 /* LaunchScreen.storyboard */; }; 14 | DC4FF6491EA2E08C00BBF8E4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DC56BDCD1E46DEB900AAD0D9 /* Assets.xcassets */; }; 15 | DC4FF64A1EA2E08C00BBF8E4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DC56BDCA1E46DEB900AAD0D9 /* Main.storyboard */; }; 16 | DC4FF6531EA2E2E700BBF8E4 /* TGPControls.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC4FF6521EA2E2E700BBF8E4 /* TGPControls.framework */; }; 17 | DC4FF6541EA2E2E700BBF8E4 /* TGPControls.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DC4FF6521EA2E2E700BBF8E4 /* TGPControls.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 18 | DC56BDC71E46DEB900AAD0D9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC56BDC61E46DEB900AAD0D9 /* AppDelegate.swift */; }; 19 | DC56BDC91E46DEB900AAD0D9 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC56BDC81E46DEB900AAD0D9 /* ViewController.swift */; }; 20 | DC56BDCC1E46DEB900AAD0D9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DC56BDCA1E46DEB900AAD0D9 /* Main.storyboard */; }; 21 | DC56BDCE1E46DEB900AAD0D9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DC56BDCD1E46DEB900AAD0D9 /* Assets.xcassets */; }; 22 | DC56BDD11E46DEB900AAD0D9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DC56BDCF1E46DEB900AAD0D9 /* LaunchScreen.storyboard */; }; 23 | DC7677B820C5C5BA006155F3 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DC7677B620C5C5BA006155F3 /* Localizable.strings */; }; 24 | DC7677B920C5C5BA006155F3 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DC7677B620C5C5BA006155F3 /* Localizable.strings */; }; 25 | DC7677BD20C5C712006155F3 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DC7677BB20C5C712006155F3 /* InfoPlist.strings */; }; 26 | DC7677BE20C5C712006155F3 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DC7677BB20C5C712006155F3 /* InfoPlist.strings */; }; 27 | /* End PBXBuildFile section */ 28 | 29 | /* Begin PBXCopyFilesBuildPhase section */ 30 | DC4FF6551EA2E2E700BBF8E4 /* Embed Frameworks */ = { 31 | isa = PBXCopyFilesBuildPhase; 32 | buildActionMask = 2147483647; 33 | dstPath = ""; 34 | dstSubfolderSpec = 10; 35 | files = ( 36 | DC4FF6541EA2E2E700BBF8E4 /* TGPControls.framework in Embed Frameworks */, 37 | ); 38 | name = "Embed Frameworks"; 39 | runOnlyForDeploymentPostprocessing = 0; 40 | }; 41 | /* End PBXCopyFilesBuildPhase section */ 42 | 43 | /* Begin PBXFileReference section */ 44 | 860AEC28CE2BF295D5323689 /* Pods-TGPControlsDemo-Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TGPControlsDemo-Pods.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TGPControlsDemo-Pods/Pods-TGPControlsDemo-Pods.debug.xcconfig"; sourceTree = ""; }; 45 | 96ADF77D1CF3E3C9B5F25B38 /* Pods_TGPControlsDemo_Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TGPControlsDemo_Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | D4E7ADCD9BF439A69C180A4A /* Pods-TGPControlsDemo-Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TGPControlsDemo-Pods.release.xcconfig"; path = "Pods/Target Support Files/Pods-TGPControlsDemo-Pods/Pods-TGPControlsDemo-Pods.release.xcconfig"; sourceTree = ""; }; 47 | DC4FF6501EA2E08C00BBF8E4 /* TGPControlsDemo-Cart.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TGPControlsDemo-Cart.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 48 | DC4FF6511EA2E08C00BBF8E4 /* TGPControlsDemo-Cart-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TGPControlsDemo-Cart-Info.plist"; sourceTree = ""; }; 49 | DC4FF6521EA2E2E700BBF8E4 /* TGPControls.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = TGPControls.framework; path = Carthage/Build/iOS/TGPControls.framework; sourceTree = ""; }; 50 | DC56BDC31E46DEB900AAD0D9 /* TGPControlsDemo-Pods.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TGPControlsDemo-Pods.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | DC56BDC61E46DEB900AAD0D9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 52 | DC56BDC81E46DEB900AAD0D9 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 53 | DC56BDCB1E46DEB900AAD0D9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 54 | DC56BDCD1E46DEB900AAD0D9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 55 | DC56BDD01E46DEB900AAD0D9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 56 | DC56BDD21E46DEB900AAD0D9 /* TGPControlsDemo-Pods-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TGPControlsDemo-Pods-Info.plist"; sourceTree = ""; }; 57 | DC7677B720C5C5BA006155F3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = ""; }; 58 | DC7677BA20C5C5E5006155F3 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = ""; }; 59 | DC7677BC20C5C712006155F3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = ""; }; 60 | DC7677BF20C5C769006155F3 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/InfoPlist.strings; sourceTree = ""; }; 61 | DCEE05AD20C20D650081CD34 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Main.strings; sourceTree = ""; }; 62 | /* End PBXFileReference section */ 63 | 64 | /* Begin PBXFrameworksBuildPhase section */ 65 | DC4FF6451EA2E08C00BBF8E4 /* Frameworks */ = { 66 | isa = PBXFrameworksBuildPhase; 67 | buildActionMask = 2147483647; 68 | files = ( 69 | DC4FF6531EA2E2E700BBF8E4 /* TGPControls.framework in Frameworks */, 70 | ); 71 | runOnlyForDeploymentPostprocessing = 0; 72 | }; 73 | DC56BDC01E46DEB900AAD0D9 /* Frameworks */ = { 74 | isa = PBXFrameworksBuildPhase; 75 | buildActionMask = 2147483647; 76 | files = ( 77 | AC26D296425BA1D414B87647 /* Pods_TGPControlsDemo_Pods.framework in Frameworks */, 78 | ); 79 | runOnlyForDeploymentPostprocessing = 0; 80 | }; 81 | /* End PBXFrameworksBuildPhase section */ 82 | 83 | /* Begin PBXGroup section */ 84 | 10EC92099730DF4A939701BE /* Frameworks */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | DC4FF6521EA2E2E700BBF8E4 /* TGPControls.framework */, 88 | 96ADF77D1CF3E3C9B5F25B38 /* Pods_TGPControlsDemo_Pods.framework */, 89 | ); 90 | name = Frameworks; 91 | sourceTree = ""; 92 | }; 93 | 90671EEB912570A269B400F1 /* Pods */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 860AEC28CE2BF295D5323689 /* Pods-TGPControlsDemo-Pods.debug.xcconfig */, 97 | D4E7ADCD9BF439A69C180A4A /* Pods-TGPControlsDemo-Pods.release.xcconfig */, 98 | ); 99 | name = Pods; 100 | sourceTree = ""; 101 | }; 102 | DC56BDBA1E46DEB900AAD0D9 = { 103 | isa = PBXGroup; 104 | children = ( 105 | DC56BDC51E46DEB900AAD0D9 /* TGPControlsDemo */, 106 | DC56BDC41E46DEB900AAD0D9 /* Products */, 107 | 10EC92099730DF4A939701BE /* Frameworks */, 108 | 90671EEB912570A269B400F1 /* Pods */, 109 | ); 110 | sourceTree = ""; 111 | }; 112 | DC56BDC41E46DEB900AAD0D9 /* Products */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | DC56BDC31E46DEB900AAD0D9 /* TGPControlsDemo-Pods.app */, 116 | DC4FF6501EA2E08C00BBF8E4 /* TGPControlsDemo-Cart.app */, 117 | ); 118 | name = Products; 119 | sourceTree = ""; 120 | }; 121 | DC56BDC51E46DEB900AAD0D9 /* TGPControlsDemo */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | DC56BDC61E46DEB900AAD0D9 /* AppDelegate.swift */, 125 | DC56BDC81E46DEB900AAD0D9 /* ViewController.swift */, 126 | DC56BDCA1E46DEB900AAD0D9 /* Main.storyboard */, 127 | DC56BDCD1E46DEB900AAD0D9 /* Assets.xcassets */, 128 | DC56BDCF1E46DEB900AAD0D9 /* LaunchScreen.storyboard */, 129 | DC56BDD21E46DEB900AAD0D9 /* TGPControlsDemo-Pods-Info.plist */, 130 | DC4FF6511EA2E08C00BBF8E4 /* TGPControlsDemo-Cart-Info.plist */, 131 | DC7677B620C5C5BA006155F3 /* Localizable.strings */, 132 | DC7677BB20C5C712006155F3 /* InfoPlist.strings */, 133 | ); 134 | path = TGPControlsDemo; 135 | sourceTree = ""; 136 | }; 137 | /* End PBXGroup section */ 138 | 139 | /* Begin PBXNativeTarget section */ 140 | DC4FF6401EA2E08C00BBF8E4 /* TGPControlsDemo-Cart */ = { 141 | isa = PBXNativeTarget; 142 | buildConfigurationList = DC4FF64D1EA2E08C00BBF8E4 /* Build configuration list for PBXNativeTarget "TGPControlsDemo-Cart" */; 143 | buildPhases = ( 144 | DC4FF6421EA2E08C00BBF8E4 /* Sources */, 145 | DC4FF6451EA2E08C00BBF8E4 /* Frameworks */, 146 | DC4FF6471EA2E08C00BBF8E4 /* Resources */, 147 | DC4FF6551EA2E2E700BBF8E4 /* Embed Frameworks */, 148 | ); 149 | buildRules = ( 150 | ); 151 | dependencies = ( 152 | ); 153 | name = "TGPControlsDemo-Cart"; 154 | productName = TGPControlsDemo; 155 | productReference = DC4FF6501EA2E08C00BBF8E4 /* TGPControlsDemo-Cart.app */; 156 | productType = "com.apple.product-type.application"; 157 | }; 158 | DC56BDC21E46DEB900AAD0D9 /* TGPControlsDemo-Pods */ = { 159 | isa = PBXNativeTarget; 160 | buildConfigurationList = DC56BDD51E46DEB900AAD0D9 /* Build configuration list for PBXNativeTarget "TGPControlsDemo-Pods" */; 161 | buildPhases = ( 162 | 1934555AC11CDE41167D1F71 /* [CP] Check Pods Manifest.lock */, 163 | DC56BDBF1E46DEB900AAD0D9 /* Sources */, 164 | DC56BDC01E46DEB900AAD0D9 /* Frameworks */, 165 | DC56BDC11E46DEB900AAD0D9 /* Resources */, 166 | A772C50843047EE677ADAF15 /* [CP] Embed Pods Frameworks */, 167 | ); 168 | buildRules = ( 169 | ); 170 | dependencies = ( 171 | ); 172 | name = "TGPControlsDemo-Pods"; 173 | productName = TGPControlsDemo; 174 | productReference = DC56BDC31E46DEB900AAD0D9 /* TGPControlsDemo-Pods.app */; 175 | productType = "com.apple.product-type.application"; 176 | }; 177 | /* End PBXNativeTarget section */ 178 | 179 | /* Begin PBXProject section */ 180 | DC56BDBB1E46DEB900AAD0D9 /* Project object */ = { 181 | isa = PBXProject; 182 | attributes = { 183 | LastSwiftUpdateCheck = 0820; 184 | LastUpgradeCheck = 0940; 185 | ORGANIZATIONNAME = SwiftArchitect; 186 | TargetAttributes = { 187 | DC56BDC21E46DEB900AAD0D9 = { 188 | CreatedOnToolsVersion = 8.2.1; 189 | LastSwiftMigration = 0940; 190 | ProvisioningStyle = Automatic; 191 | }; 192 | }; 193 | }; 194 | buildConfigurationList = DC56BDBE1E46DEB900AAD0D9 /* Build configuration list for PBXProject "TGPControlsDemo" */; 195 | compatibilityVersion = "Xcode 9.3"; 196 | developmentRegion = English; 197 | hasScannedForEncodings = 0; 198 | knownRegions = ( 199 | en, 200 | Base, 201 | ar, 202 | ); 203 | mainGroup = DC56BDBA1E46DEB900AAD0D9; 204 | productRefGroup = DC56BDC41E46DEB900AAD0D9 /* Products */; 205 | projectDirPath = ""; 206 | projectRoot = ""; 207 | targets = ( 208 | DC56BDC21E46DEB900AAD0D9 /* TGPControlsDemo-Pods */, 209 | DC4FF6401EA2E08C00BBF8E4 /* TGPControlsDemo-Cart */, 210 | ); 211 | }; 212 | /* End PBXProject section */ 213 | 214 | /* Begin PBXResourcesBuildPhase section */ 215 | DC4FF6471EA2E08C00BBF8E4 /* Resources */ = { 216 | isa = PBXResourcesBuildPhase; 217 | buildActionMask = 2147483647; 218 | files = ( 219 | DC4FF6481EA2E08C00BBF8E4 /* LaunchScreen.storyboard in Resources */, 220 | DC7677B920C5C5BA006155F3 /* Localizable.strings in Resources */, 221 | DC4FF6491EA2E08C00BBF8E4 /* Assets.xcassets in Resources */, 222 | DC4FF64A1EA2E08C00BBF8E4 /* Main.storyboard in Resources */, 223 | DC7677BE20C5C712006155F3 /* InfoPlist.strings in Resources */, 224 | ); 225 | runOnlyForDeploymentPostprocessing = 0; 226 | }; 227 | DC56BDC11E46DEB900AAD0D9 /* Resources */ = { 228 | isa = PBXResourcesBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | DC56BDD11E46DEB900AAD0D9 /* LaunchScreen.storyboard in Resources */, 232 | DC7677B820C5C5BA006155F3 /* Localizable.strings in Resources */, 233 | DC56BDCE1E46DEB900AAD0D9 /* Assets.xcassets in Resources */, 234 | DC56BDCC1E46DEB900AAD0D9 /* Main.storyboard in Resources */, 235 | DC7677BD20C5C712006155F3 /* InfoPlist.strings in Resources */, 236 | ); 237 | runOnlyForDeploymentPostprocessing = 0; 238 | }; 239 | /* End PBXResourcesBuildPhase section */ 240 | 241 | /* Begin PBXShellScriptBuildPhase section */ 242 | 1934555AC11CDE41167D1F71 /* [CP] Check Pods Manifest.lock */ = { 243 | isa = PBXShellScriptBuildPhase; 244 | buildActionMask = 2147483647; 245 | files = ( 246 | ); 247 | inputPaths = ( 248 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 249 | "${PODS_ROOT}/Manifest.lock", 250 | ); 251 | name = "[CP] Check Pods Manifest.lock"; 252 | outputPaths = ( 253 | "$(DERIVED_FILE_DIR)/Pods-TGPControlsDemo-Pods-checkManifestLockResult.txt", 254 | ); 255 | runOnlyForDeploymentPostprocessing = 0; 256 | shellPath = /bin/sh; 257 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 258 | showEnvVarsInLog = 0; 259 | }; 260 | A772C50843047EE677ADAF15 /* [CP] Embed Pods Frameworks */ = { 261 | isa = PBXShellScriptBuildPhase; 262 | buildActionMask = 2147483647; 263 | files = ( 264 | ); 265 | inputPaths = ( 266 | "${SRCROOT}/Pods/Target Support Files/Pods-TGPControlsDemo-Pods/Pods-TGPControlsDemo-Pods-frameworks.sh", 267 | "${BUILT_PRODUCTS_DIR}/TGPControls/TGPControls.framework", 268 | ); 269 | name = "[CP] Embed Pods Frameworks"; 270 | outputPaths = ( 271 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/TGPControls.framework", 272 | ); 273 | runOnlyForDeploymentPostprocessing = 0; 274 | shellPath = /bin/sh; 275 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-TGPControlsDemo-Pods/Pods-TGPControlsDemo-Pods-frameworks.sh\"\n"; 276 | showEnvVarsInLog = 0; 277 | }; 278 | /* End PBXShellScriptBuildPhase section */ 279 | 280 | /* Begin PBXSourcesBuildPhase section */ 281 | DC4FF6421EA2E08C00BBF8E4 /* Sources */ = { 282 | isa = PBXSourcesBuildPhase; 283 | buildActionMask = 2147483647; 284 | files = ( 285 | DC4FF6431EA2E08C00BBF8E4 /* ViewController.swift in Sources */, 286 | DC4FF6441EA2E08C00BBF8E4 /* AppDelegate.swift in Sources */, 287 | ); 288 | runOnlyForDeploymentPostprocessing = 0; 289 | }; 290 | DC56BDBF1E46DEB900AAD0D9 /* Sources */ = { 291 | isa = PBXSourcesBuildPhase; 292 | buildActionMask = 2147483647; 293 | files = ( 294 | DC56BDC91E46DEB900AAD0D9 /* ViewController.swift in Sources */, 295 | DC56BDC71E46DEB900AAD0D9 /* AppDelegate.swift in Sources */, 296 | ); 297 | runOnlyForDeploymentPostprocessing = 0; 298 | }; 299 | /* End PBXSourcesBuildPhase section */ 300 | 301 | /* Begin PBXVariantGroup section */ 302 | DC56BDCA1E46DEB900AAD0D9 /* Main.storyboard */ = { 303 | isa = PBXVariantGroup; 304 | children = ( 305 | DC56BDCB1E46DEB900AAD0D9 /* Base */, 306 | DCEE05AD20C20D650081CD34 /* ar */, 307 | ); 308 | name = Main.storyboard; 309 | sourceTree = ""; 310 | }; 311 | DC56BDCF1E46DEB900AAD0D9 /* LaunchScreen.storyboard */ = { 312 | isa = PBXVariantGroup; 313 | children = ( 314 | DC56BDD01E46DEB900AAD0D9 /* Base */, 315 | ); 316 | name = LaunchScreen.storyboard; 317 | sourceTree = ""; 318 | }; 319 | DC7677B620C5C5BA006155F3 /* Localizable.strings */ = { 320 | isa = PBXVariantGroup; 321 | children = ( 322 | DC7677B720C5C5BA006155F3 /* Base */, 323 | DC7677BA20C5C5E5006155F3 /* ar */, 324 | ); 325 | name = Localizable.strings; 326 | sourceTree = ""; 327 | }; 328 | DC7677BB20C5C712006155F3 /* InfoPlist.strings */ = { 329 | isa = PBXVariantGroup; 330 | children = ( 331 | DC7677BC20C5C712006155F3 /* Base */, 332 | DC7677BF20C5C769006155F3 /* ar */, 333 | ); 334 | name = InfoPlist.strings; 335 | sourceTree = ""; 336 | }; 337 | /* End PBXVariantGroup section */ 338 | 339 | /* Begin XCBuildConfiguration section */ 340 | DC4FF64E1EA2E08C00BBF8E4 /* Debug */ = { 341 | isa = XCBuildConfiguration; 342 | buildSettings = { 343 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; 344 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 345 | DEVELOPMENT_TEAM = ""; 346 | FRAMEWORK_SEARCH_PATHS = ( 347 | "$(inherited)", 348 | "$(PROJECT_DIR)/Carthage/Build/iOS", 349 | ); 350 | INFOPLIST_FILE = "$(SRCROOT)/TGPControlsDemo/TGPControlsDemo-Cart-Info.plist"; 351 | LD_RUNPATH_SEARCH_PATHS = ( 352 | "$(inherited)", 353 | "@executable_path/Frameworks", 354 | ); 355 | PRODUCT_BUNDLE_IDENTIFIER = com.swiftarchitect.TGPControlsDemo; 356 | PRODUCT_NAME = "$(TARGET_NAME)"; 357 | SWIFT_VERSION = 4.0; 358 | }; 359 | name = Debug; 360 | }; 361 | DC4FF64F1EA2E08C00BBF8E4 /* Release */ = { 362 | isa = XCBuildConfiguration; 363 | buildSettings = { 364 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; 365 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 366 | DEVELOPMENT_TEAM = ""; 367 | FRAMEWORK_SEARCH_PATHS = ( 368 | "$(inherited)", 369 | "$(PROJECT_DIR)/Carthage/Build/iOS", 370 | ); 371 | INFOPLIST_FILE = "$(SRCROOT)/TGPControlsDemo/TGPControlsDemo-Cart-Info.plist"; 372 | LD_RUNPATH_SEARCH_PATHS = ( 373 | "$(inherited)", 374 | "@executable_path/Frameworks", 375 | ); 376 | PRODUCT_BUNDLE_IDENTIFIER = com.swiftarchitect.TGPControlsDemo; 377 | PRODUCT_NAME = "$(TARGET_NAME)"; 378 | SWIFT_VERSION = 4.0; 379 | }; 380 | name = Release; 381 | }; 382 | DC56BDD31E46DEB900AAD0D9 /* Debug */ = { 383 | isa = XCBuildConfiguration; 384 | buildSettings = { 385 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; 386 | ALWAYS_SEARCH_USER_PATHS = NO; 387 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 388 | CLANG_ANALYZER_NONNULL = YES; 389 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 390 | CLANG_CXX_LIBRARY = "libc++"; 391 | CLANG_ENABLE_MODULES = YES; 392 | CLANG_ENABLE_OBJC_ARC = YES; 393 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 394 | CLANG_WARN_BOOL_CONVERSION = YES; 395 | CLANG_WARN_COMMA = YES; 396 | CLANG_WARN_CONSTANT_CONVERSION = YES; 397 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 398 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 399 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 400 | CLANG_WARN_EMPTY_BODY = YES; 401 | CLANG_WARN_ENUM_CONVERSION = YES; 402 | CLANG_WARN_INFINITE_RECURSION = YES; 403 | CLANG_WARN_INT_CONVERSION = YES; 404 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 405 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 406 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 407 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 408 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 409 | CLANG_WARN_STRICT_PROTOTYPES = YES; 410 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 411 | CLANG_WARN_UNREACHABLE_CODE = YES; 412 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 413 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 414 | COPY_PHASE_STRIP = NO; 415 | DEBUG_INFORMATION_FORMAT = dwarf; 416 | ENABLE_STRICT_OBJC_MSGSEND = YES; 417 | ENABLE_TESTABILITY = YES; 418 | GCC_C_LANGUAGE_STANDARD = gnu99; 419 | GCC_DYNAMIC_NO_PIC = NO; 420 | GCC_NO_COMMON_BLOCKS = YES; 421 | GCC_OPTIMIZATION_LEVEL = 0; 422 | GCC_PREPROCESSOR_DEFINITIONS = ( 423 | "DEBUG=1", 424 | "$(inherited)", 425 | ); 426 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 427 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 428 | GCC_WARN_UNDECLARED_SELECTOR = YES; 429 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 430 | GCC_WARN_UNUSED_FUNCTION = YES; 431 | GCC_WARN_UNUSED_VARIABLE = YES; 432 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 433 | MTL_ENABLE_DEBUG_INFO = YES; 434 | ONLY_ACTIVE_ARCH = YES; 435 | SDKROOT = iphoneos; 436 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 437 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 438 | SWIFT_VERSION = 4.0; 439 | TARGETED_DEVICE_FAMILY = "1,2"; 440 | }; 441 | name = Debug; 442 | }; 443 | DC56BDD41E46DEB900AAD0D9 /* Release */ = { 444 | isa = XCBuildConfiguration; 445 | buildSettings = { 446 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; 447 | ALWAYS_SEARCH_USER_PATHS = NO; 448 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 449 | CLANG_ANALYZER_NONNULL = YES; 450 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 451 | CLANG_CXX_LIBRARY = "libc++"; 452 | CLANG_ENABLE_MODULES = YES; 453 | CLANG_ENABLE_OBJC_ARC = YES; 454 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 455 | CLANG_WARN_BOOL_CONVERSION = YES; 456 | CLANG_WARN_COMMA = YES; 457 | CLANG_WARN_CONSTANT_CONVERSION = YES; 458 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 459 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 460 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 461 | CLANG_WARN_EMPTY_BODY = YES; 462 | CLANG_WARN_ENUM_CONVERSION = YES; 463 | CLANG_WARN_INFINITE_RECURSION = YES; 464 | CLANG_WARN_INT_CONVERSION = YES; 465 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 466 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 467 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 468 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 469 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 470 | CLANG_WARN_STRICT_PROTOTYPES = YES; 471 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 472 | CLANG_WARN_UNREACHABLE_CODE = YES; 473 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 474 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 475 | COPY_PHASE_STRIP = NO; 476 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 477 | ENABLE_NS_ASSERTIONS = NO; 478 | ENABLE_STRICT_OBJC_MSGSEND = YES; 479 | GCC_C_LANGUAGE_STANDARD = gnu99; 480 | GCC_NO_COMMON_BLOCKS = YES; 481 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 482 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 483 | GCC_WARN_UNDECLARED_SELECTOR = YES; 484 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 485 | GCC_WARN_UNUSED_FUNCTION = YES; 486 | GCC_WARN_UNUSED_VARIABLE = YES; 487 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 488 | MTL_ENABLE_DEBUG_INFO = NO; 489 | SDKROOT = iphoneos; 490 | SWIFT_COMPILATION_MODE = wholemodule; 491 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 492 | SWIFT_VERSION = 4.0; 493 | TARGETED_DEVICE_FAMILY = "1,2"; 494 | VALIDATE_PRODUCT = YES; 495 | }; 496 | name = Release; 497 | }; 498 | DC56BDD61E46DEB900AAD0D9 /* Debug */ = { 499 | isa = XCBuildConfiguration; 500 | baseConfigurationReference = 860AEC28CE2BF295D5323689 /* Pods-TGPControlsDemo-Pods.debug.xcconfig */; 501 | buildSettings = { 502 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; 503 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 504 | DEVELOPMENT_TEAM = ""; 505 | INFOPLIST_FILE = "$(SRCROOT)/TGPControlsDemo/TGPControlsDemo-Pods-Info.plist"; 506 | LD_RUNPATH_SEARCH_PATHS = ( 507 | "$(inherited)", 508 | "@executable_path/Frameworks", 509 | ); 510 | PRODUCT_BUNDLE_IDENTIFIER = com.swiftarchitect.TGPControlsDemo; 511 | PRODUCT_NAME = "$(TARGET_NAME)"; 512 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 513 | SWIFT_VERSION = 4.0; 514 | }; 515 | name = Debug; 516 | }; 517 | DC56BDD71E46DEB900AAD0D9 /* Release */ = { 518 | isa = XCBuildConfiguration; 519 | baseConfigurationReference = D4E7ADCD9BF439A69C180A4A /* Pods-TGPControlsDemo-Pods.release.xcconfig */; 520 | buildSettings = { 521 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; 522 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 523 | DEVELOPMENT_TEAM = ""; 524 | INFOPLIST_FILE = "$(SRCROOT)/TGPControlsDemo/TGPControlsDemo-Pods-Info.plist"; 525 | LD_RUNPATH_SEARCH_PATHS = ( 526 | "$(inherited)", 527 | "@executable_path/Frameworks", 528 | ); 529 | PRODUCT_BUNDLE_IDENTIFIER = com.swiftarchitect.TGPControlsDemo; 530 | PRODUCT_NAME = "$(TARGET_NAME)"; 531 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 532 | SWIFT_VERSION = 4.0; 533 | }; 534 | name = Release; 535 | }; 536 | /* End XCBuildConfiguration section */ 537 | 538 | /* Begin XCConfigurationList section */ 539 | DC4FF64D1EA2E08C00BBF8E4 /* Build configuration list for PBXNativeTarget "TGPControlsDemo-Cart" */ = { 540 | isa = XCConfigurationList; 541 | buildConfigurations = ( 542 | DC4FF64E1EA2E08C00BBF8E4 /* Debug */, 543 | DC4FF64F1EA2E08C00BBF8E4 /* Release */, 544 | ); 545 | defaultConfigurationIsVisible = 0; 546 | defaultConfigurationName = Release; 547 | }; 548 | DC56BDBE1E46DEB900AAD0D9 /* Build configuration list for PBXProject "TGPControlsDemo" */ = { 549 | isa = XCConfigurationList; 550 | buildConfigurations = ( 551 | DC56BDD31E46DEB900AAD0D9 /* Debug */, 552 | DC56BDD41E46DEB900AAD0D9 /* Release */, 553 | ); 554 | defaultConfigurationIsVisible = 0; 555 | defaultConfigurationName = Release; 556 | }; 557 | DC56BDD51E46DEB900AAD0D9 /* Build configuration list for PBXNativeTarget "TGPControlsDemo-Pods" */ = { 558 | isa = XCConfigurationList; 559 | buildConfigurations = ( 560 | DC56BDD61E46DEB900AAD0D9 /* Debug */, 561 | DC56BDD71E46DEB900AAD0D9 /* Release */, 562 | ); 563 | defaultConfigurationIsVisible = 0; 564 | defaultConfigurationName = Release; 565 | }; 566 | /* End XCConfigurationList section */ 567 | }; 568 | rootObject = DC56BDBB1E46DEB900AAD0D9 /* Project object */; 569 | } 570 | -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | @UIApplicationMain 4 | class AppDelegate: UIResponder, UIApplicationDelegate { 5 | 6 | var window: UIWindow? 7 | 8 | 9 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 10 | // Override point for customization after application launch. 11 | return true 12 | } 13 | 14 | func applicationWillResignActive(_ application: UIApplication) { 15 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 16 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 17 | } 18 | 19 | func applicationDidEnterBackground(_ application: UIApplication) { 20 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 21 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 22 | } 23 | 24 | func applicationWillEnterForeground(_ application: UIApplication) { 25 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 26 | } 27 | 28 | func applicationDidBecomeActive(_ application: UIApplication) { 29 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 30 | } 31 | 32 | func applicationWillTerminate(_ application: UIApplication) { 33 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "60x60", 35 | "idiom" : "iphone", 36 | "filename" : "icon120.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "idiom" : "iphone", 41 | "size" : "60x60", 42 | "scale" : "3x" 43 | }, 44 | { 45 | "idiom" : "ipad", 46 | "size" : "20x20", 47 | "scale" : "1x" 48 | }, 49 | { 50 | "idiom" : "ipad", 51 | "size" : "20x20", 52 | "scale" : "2x" 53 | }, 54 | { 55 | "idiom" : "ipad", 56 | "size" : "29x29", 57 | "scale" : "1x" 58 | }, 59 | { 60 | "idiom" : "ipad", 61 | "size" : "29x29", 62 | "scale" : "2x" 63 | }, 64 | { 65 | "idiom" : "ipad", 66 | "size" : "40x40", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "idiom" : "ipad", 71 | "size" : "40x40", 72 | "scale" : "2x" 73 | }, 74 | { 75 | "size" : "76x76", 76 | "idiom" : "ipad", 77 | "filename" : "icon76.png", 78 | "scale" : "1x" 79 | }, 80 | { 81 | "size" : "76x76", 82 | "idiom" : "ipad", 83 | "filename" : "icon152.png", 84 | "scale" : "2x" 85 | }, 86 | { 87 | "size" : "83.5x83.5", 88 | "idiom" : "ipad", 89 | "filename" : "icon167.png", 90 | "scale" : "2x" 91 | }, 92 | { 93 | "size" : "1024x1024", 94 | "idiom" : "ios-marketing", 95 | "filename" : "icon1024.png", 96 | "scale" : "1x" 97 | } 98 | ], 99 | "info" : { 100 | "version" : 1, 101 | "author" : "xcode" 102 | } 103 | } -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/Assets.xcassets/AppIcon.appiconset/icon1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftArchitect/TGPControls/cec613690eaf159271967c1a9853638062d9c6c0/TGPControlsDemo/TGPControlsDemo/Assets.xcassets/AppIcon.appiconset/icon1024.png -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/Assets.xcassets/AppIcon.appiconset/icon120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftArchitect/TGPControls/cec613690eaf159271967c1a9853638062d9c6c0/TGPControlsDemo/TGPControlsDemo/Assets.xcassets/AppIcon.appiconset/icon120.png -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/Assets.xcassets/AppIcon.appiconset/icon152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftArchitect/TGPControls/cec613690eaf159271967c1a9853638062d9c6c0/TGPControlsDemo/TGPControlsDemo/Assets.xcassets/AppIcon.appiconset/icon152.png -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/Assets.xcassets/AppIcon.appiconset/icon167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftArchitect/TGPControls/cec613690eaf159271967c1a9853638062d9c6c0/TGPControlsDemo/TGPControlsDemo/Assets.xcassets/AppIcon.appiconset/icon167.png -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/Assets.xcassets/AppIcon.appiconset/icon76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftArchitect/TGPControls/cec613690eaf159271967c1a9853638062d9c6c0/TGPControlsDemo/TGPControlsDemo/Assets.xcassets/AppIcon.appiconset/icon76.png -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/Assets.xcassets/launch.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "launch1024.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/Assets.xcassets/launch.imageset/launch1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftArchitect/TGPControls/cec613690eaf159271967c1a9853638062d9c6c0/TGPControlsDemo/TGPControlsDemo/Assets.xcassets/launch.imageset/launch1024.png -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/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 | "filename" : "wthumb.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/Assets.xcassets/thumb.imageset/wthumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftArchitect/TGPControls/cec613690eaf159271967c1a9853638062d9c6c0/TGPControlsDemo/TGPControlsDemo/Assets.xcassets/thumb.imageset/wthumb.png -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/Assets.xcassets/tick.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "wtick.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/Assets.xcassets/tick.imageset/wtick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftArchitect/TGPControls/cec613690eaf159271967c1a9853638062d9c6c0/TGPControlsDemo/TGPControlsDemo/Assets.xcassets/tick.imageset/wtick.png -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/Assets.xcassets/track.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "wtrack.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/Assets.xcassets/track.imageset/wtrack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftArchitect/TGPControls/cec613690eaf159271967c1a9853638062d9c6c0/TGPControlsDemo/TGPControlsDemo/Assets.xcassets/track.imageset/wtrack.png -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/Base.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | "CFBundleDisplayName" = "TGPControls"; 2 | "NSHumanReadableCopyright" = "2016 Xavier Schott"; 3 | -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/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 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/Base.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "oneTo10Labels.numbers" = "1 2 3 4 5 6 7 8 9 10"; 2 | "alphabetLabels.letters" = "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z"; 3 | 4 | "pictureLabels.east" = "orient"; 5 | "pictureLabels.west" = "occident"; 6 | "pictureLabels.up" = "zénith"; 7 | "pictureLabels.down" = "nadir"; 8 | "pictureLabels.north" = "septentrion"; 9 | "pictureLabels.south" = "midi"; 10 | 11 | "switch1Camel.off" = "OFF"; 12 | "switch1Camel.on" = "ON"; 13 | 14 | "switch2Camel.off" = "O"; 15 | "switch2Camel.on" = "l"; 16 | -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/TGPControlsDemo-Cart-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | TGPControls 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 5.0.3 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/TGPControlsDemo-Pods-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | TGPControls 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 5.0.3 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/ViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import TGPControls 3 | 4 | class ViewController: UIViewController { 5 | @IBOutlet weak var oneTo10Labels: TGPCamelLabels! 6 | @IBOutlet weak var oneTo10Slider: TGPDiscreteSlider! 7 | 8 | @IBOutlet weak var alphabetLabels: TGPCamelLabels! 9 | @IBOutlet weak var alphabetSlider: TGPDiscreteSlider! 10 | 11 | @IBOutlet var pictureLabels: TGPCamelLabels! 12 | @IBOutlet var pictureSlider: TGPDiscreteSlider! 13 | 14 | @IBOutlet weak var switch1Camel: TGPCamelLabels! 15 | @IBOutlet weak var switch2Camel: TGPCamelLabels! 16 | 17 | @IBOutlet weak var controlEventsLabel: UILabel! 18 | @IBOutlet weak var dualColorSlider: TGPDiscreteSlider! 19 | @IBOutlet weak var stepper: UIStepper! 20 | 21 | private func localizedStrings(_ key: String) -> [String] { 22 | return NSLocalizedString(key, comment: "") 23 | .split(separator: " ") 24 | .map({ (substring) -> String in 25 | return "\(substring)" 26 | }) 27 | } 28 | 29 | override func viewDidLoad() { 30 | super.viewDidLoad() 31 | 32 | oneTo10Labels.names = localizedStrings("oneTo10Labels.numbers") 33 | 34 | alphabetLabels.names = localizedStrings("alphabetLabels.letters") 35 | alphabetSlider.tickCount = alphabetLabels.names.count // Number of letters in the given alphabet 36 | 37 | pictureLabels.names = [NSLocalizedString("pictureLabels.east", comment: ""), 38 | NSLocalizedString("pictureLabels.west", comment: ""), 39 | NSLocalizedString("pictureLabels.up", comment: ""), 40 | NSLocalizedString("pictureLabels.down", comment: ""), 41 | NSLocalizedString("pictureLabels.north", comment: ""), 42 | NSLocalizedString("pictureLabels.south", comment: "")] 43 | 44 | switch1Camel.names = [NSLocalizedString("switch1Camel.off", comment: ""), 45 | NSLocalizedString("switch1Camel.on", comment: "")] 46 | 47 | switch2Camel.names = [NSLocalizedString("switch2Camel.off", comment: ""), 48 | NSLocalizedString("switch2Camel.on", comment: "")] 49 | 50 | 51 | // Automatically track tick spacing changes and UIControlEventValueChanged 52 | alphabetSlider.ticksListener = alphabetLabels 53 | oneTo10Slider.ticksListener = oneTo10Labels 54 | pictureSlider.ticksListener = pictureLabels 55 | 56 | // UIControlEvents 57 | dualColorSlider.addTarget(self, action: #selector(ViewController.touchDown(_:event:)), for: .touchDown) 58 | dualColorSlider.addTarget(self, action: #selector(ViewController.touchDownRepeat(_:event:)), for: .touchDownRepeat) 59 | dualColorSlider.addTarget(self, action: #selector(ViewController.touchDragInside(_:event:)), for: .touchDragInside) 60 | dualColorSlider.addTarget(self, action: #selector(ViewController.touchDragOutside(_:event:)), for: .touchDragOutside) 61 | dualColorSlider.addTarget(self, action: #selector(ViewController.touchDragEnter(_:event:)), for: .touchDragEnter) 62 | dualColorSlider.addTarget(self, action: #selector(ViewController.touchDragExit(_:event:)), for: .touchDragExit) 63 | dualColorSlider.addTarget(self, action: #selector(ViewController.touchUpInside(_:event:)), for: .touchUpInside) 64 | dualColorSlider.addTarget(self, action: #selector(ViewController.touchUpOutside(_:event:)), for: .touchUpOutside) 65 | dualColorSlider.addTarget(self, action: #selector(ViewController.touchCancel(_:event:)), for: .touchCancel) 66 | dualColorSlider.addTarget(self, action: #selector(ViewController.valueChanged(_:event:)), for: .valueChanged) 67 | } 68 | 69 | // MARK: - UISwitch 70 | 71 | @IBAction func switch1ValueChanged(_ sender: UISwitch) { 72 | switch1Camel.value = (sender.isOn) ? 1 : 0 73 | } 74 | 75 | @IBAction func switch2TouchUpInside(_ sender: UISwitch) { 76 | switch2Camel.value = (sender.isOn) ? 1 : 0 77 | } 78 | 79 | // MARK: - UIControlEvents 80 | 81 | @objc func touchDown(_ sender: UIControl, event:UIEvent) { 82 | controlEventsLabel.text = "touchDown" 83 | } 84 | @objc func touchDownRepeat(_ sender: UIControl, event:UIEvent) { 85 | controlEventsLabel.text = "touchDownRepeat" 86 | } 87 | @objc func touchDragInside(_ sender: UIControl, event:UIEvent) { 88 | controlEventsLabel.text = "touchDragInside" 89 | } 90 | @objc func touchDragOutside(_ sender: UIControl, event:UIEvent) { 91 | controlEventsLabel.text = "touchDragOutside" 92 | } 93 | @objc func touchDragEnter(_ sender: UIControl, event:UIEvent) { 94 | controlEventsLabel.text = "touchDragEnter" 95 | } 96 | @objc func touchDragExit(_ sender: UIControl, event:UIEvent) { 97 | controlEventsLabel.text = "touchDragExit" 98 | } 99 | @objc func touchUpInside(_ sender: UIControl, event:UIEvent) { 100 | controlEventsLabel.text = "touchUpInside" 101 | } 102 | @objc func touchUpOutside(_ sender: UIControl, event:UIEvent) { 103 | controlEventsLabel.text = "touchUpOutside" 104 | } 105 | @objc func touchCancel(_ sender: UIControl, event:UIEvent) { 106 | controlEventsLabel.text = "touchCancel" 107 | } 108 | @objc func valueChanged(_ sender: TGPDiscreteSlider, event:UIEvent) { 109 | controlEventsLabel.text = "valueChanged" 110 | stepper.value = Double(sender.value) 111 | } 112 | 113 | // MARK: - UIStepper 114 | 115 | @IBAction func stepperValueChanged(_ sender: UIStepper) { 116 | dualColorSlider.value = CGFloat(sender.value) 117 | } 118 | } 119 | 120 | -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/ar.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | "CFBundleDisplayName" = "مراقبةTGP"; 2 | "NSHumanReadableCopyright" = "2016 Xavier Schott"; 3 | -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/ar.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "oneTo10Labels.numbers" = "١ ٢ ٣ ٤ ٥ ٦ ٧ ٨ ٩ ١٠"; 2 | "alphabetLabels.letters" = "ا ب ج د ه و ز ح ط ي ك ل م ن س ع ف ص ق ر ش ت ث خ ذ ض ظ غ"; 3 | 4 | "pictureLabels.east" = "الشرق"; 5 | "pictureLabels.west" = "غرب"; 6 | "pictureLabels.up" = "إلى فوق"; 7 | "pictureLabels.down" = "نزولا"; 8 | "pictureLabels.north" = "شمال"; 9 | "pictureLabels.south" = "جنوب"; 10 | 11 | "switch1Camel.off" = "اطفاء"; 12 | "switch1Camel.on" = "تشغيل"; 13 | 14 | "switch2Camel.off" = "٠"; 15 | "switch2Camel.on" = "١"; 16 | -------------------------------------------------------------------------------- /TGPControlsDemo/TGPControlsDemo/ar.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "UILabel"; text = "Layers and transparency"; ObjectID = "1wG-kf-nie"; */ 3 | "1wG-kf-nie.text" = "Layers and transparency"; 4 | 5 | /* Class = "UILabel"; text = "CamelLabels + Switch"; ObjectID = "A6t-OF-SSy"; */ 6 | "A6t-OF-SSy.text" = "CamelLabels + Switch"; 7 | 8 | /* Class = "UILabel"; text = "Variations"; ObjectID = "XPd-Af-CYy"; */ 9 | "XPd-Af-CYy.text" = "Variations"; 10 | 11 | /* Class = "UILabel"; text = "UIControlActions"; ObjectID = "gID-5d-KDe"; */ 12 | "gID-5d-KDe.text" = "UIControlActions"; 13 | 14 | /* Class = "UILabel"; text = "DiscreteSlider + CamelLabels"; ObjectID = "hED-uQ-kcm"; */ 15 | "hED-uQ-kcm.text" = "DiscreteSlider + CamelLabels"; 16 | --------------------------------------------------------------------------------