├── .gitignore ├── .swift-version ├── .travis.yml ├── Demo ├── ScrollableSegmentedControlDemo │ ├── ScrollableSegmentedControlDemo.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── ScrollableSegmentedControlDemo.xcscheme │ └── ScrollableSegmentedControlDemo │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── segment-1.imageset │ │ │ ├── 704-compose-toolbar.png │ │ │ ├── 704-compose-toolbar@2x.png │ │ │ ├── 704-compose-toolbar@3x.png │ │ │ └── Contents.json │ │ ├── segment-2.imageset │ │ │ ├── 707-albums-toolbar.png │ │ │ ├── 707-albums-toolbar@2x.png │ │ │ ├── 707-albums-toolbar@3x.png │ │ │ └── Contents.json │ │ ├── segment-3.imageset │ │ │ ├── 714-camera-toolbar.png │ │ │ ├── 714-camera-toolbar@2x.png │ │ │ ├── 714-camera-toolbar@3x.png │ │ │ └── Contents.json │ │ ├── segment-4.imageset │ │ │ ├── 724-info-toolbar.png │ │ │ ├── 724-info-toolbar@2x.png │ │ │ ├── 724-info-toolbar@3x.png │ │ │ └── Contents.json │ │ ├── segment-5.imageset │ │ │ ├── 729-top-list-toolbar.png │ │ │ ├── 729-top-list-toolbar@2x.png │ │ │ ├── 729-top-list-toolbar@3x.png │ │ │ └── Contents.json │ │ └── segment-6.imageset │ │ │ ├── 730-envelope-toolbar.png │ │ │ ├── 730-envelope-toolbar@2x.png │ │ │ ├── 730-envelope-toolbar@3x.png │ │ │ └── Contents.json │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ ├── ObjCTestViewController.h │ │ ├── ObjCTestViewController.m │ │ ├── ScrollableSegmentedControlDemo-Bridging-Header.h │ │ └── TableViewController.swift ├── image_on_left.png ├── image_on_top.png ├── image_only.png └── text_only.png ├── LICENSE ├── README.md ├── ScrollableSegmentedControl.podspec ├── ScrollableSegmentedControl.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── ScrollableSegmentedControl.xcscheme ├── ScrollableSegmentedControl ├── Info.plist ├── ScrollableSegmentedControl.h └── ScrollableSegmentedControl.swift └── ScrollableSegmentedControlTests ├── Info.plist └── ScrollableSegmentedControlTests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | ScrollableSegmentedControl.xcodeproj/project.xcworkspace/xcuserdata 2 | ScrollableSegmentedControl.xcodeproj/xcuserdata 3 | Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo.xcodeproj/project.xcworkspace/xcuserdata 4 | Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo.xcodeproj/xcuserdata 5 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 5.0 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: swift 2 | osx_image: xcode10.2 3 | xcode_project: ScrollableSegmentedControl.xcodeproj 4 | xcode_scheme: ScrollableSegmentedControl 5 | xcode_destination: platform=iOS Simulator,OS=12.2,name=iPhone X 6 | -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | CD8F9C721F8A74AF00BD826C /* ObjCTestViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = CD8F9C701F8A74AE00BD826C /* ObjCTestViewController.m */; }; 11 | CD9A240B1DD5A64F00B580D0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD9A240A1DD5A64F00B580D0 /* AppDelegate.swift */; }; 12 | CD9A24101DD5A64F00B580D0 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CD9A240E1DD5A64F00B580D0 /* Main.storyboard */; }; 13 | CD9A24121DD5A64F00B580D0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CD9A24111DD5A64F00B580D0 /* Assets.xcassets */; }; 14 | CD9A24151DD5A64F00B580D0 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CD9A24131DD5A64F00B580D0 /* LaunchScreen.storyboard */; }; 15 | CD9A24301DD5A76700B580D0 /* ScrollableSegmentedControl.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD9A242D1DD5A75300B580D0 /* ScrollableSegmentedControl.framework */; }; 16 | CD9A24311DD5A76700B580D0 /* ScrollableSegmentedControl.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CD9A242D1DD5A75300B580D0 /* ScrollableSegmentedControl.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 17 | CDDF9BD21DD5A7D4000B09AF /* TableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDDF9BD11DD5A7D4000B09AF /* TableViewController.swift */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXContainerItemProxy section */ 21 | CD9A242C1DD5A75300B580D0 /* PBXContainerItemProxy */ = { 22 | isa = PBXContainerItemProxy; 23 | containerPortal = CD9A24271DD5A75300B580D0 /* ScrollableSegmentedControl.xcodeproj */; 24 | proxyType = 2; 25 | remoteGlobalIDString = CD078D9D1DD4B5B30083F53F; 26 | remoteInfo = ScrollableSegmentedControl; 27 | }; 28 | CD9A242E1DD5A75300B580D0 /* PBXContainerItemProxy */ = { 29 | isa = PBXContainerItemProxy; 30 | containerPortal = CD9A24271DD5A75300B580D0 /* ScrollableSegmentedControl.xcodeproj */; 31 | proxyType = 2; 32 | remoteGlobalIDString = CD078DA61DD4B5B30083F53F; 33 | remoteInfo = ScrollableSegmentedControlTests; 34 | }; 35 | CD9A24321DD5A76700B580D0 /* PBXContainerItemProxy */ = { 36 | isa = PBXContainerItemProxy; 37 | containerPortal = CD9A24271DD5A75300B580D0 /* ScrollableSegmentedControl.xcodeproj */; 38 | proxyType = 1; 39 | remoteGlobalIDString = CD078D9C1DD4B5B30083F53F; 40 | remoteInfo = ScrollableSegmentedControl; 41 | }; 42 | /* End PBXContainerItemProxy section */ 43 | 44 | /* Begin PBXCopyFilesBuildPhase section */ 45 | CD9A24341DD5A76700B580D0 /* Embed Frameworks */ = { 46 | isa = PBXCopyFilesBuildPhase; 47 | buildActionMask = 2147483647; 48 | dstPath = ""; 49 | dstSubfolderSpec = 10; 50 | files = ( 51 | CD9A24311DD5A76700B580D0 /* ScrollableSegmentedControl.framework in Embed Frameworks */, 52 | ); 53 | name = "Embed Frameworks"; 54 | runOnlyForDeploymentPostprocessing = 0; 55 | }; 56 | /* End PBXCopyFilesBuildPhase section */ 57 | 58 | /* Begin PBXFileReference section */ 59 | CD8F9C6B1F8A748C00BD826C /* ScrollableSegmentedControlDemo-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ScrollableSegmentedControlDemo-Bridging-Header.h"; sourceTree = ""; }; 60 | CD8F9C701F8A74AE00BD826C /* ObjCTestViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ObjCTestViewController.m; sourceTree = ""; }; 61 | CD8F9C711F8A74AF00BD826C /* ObjCTestViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjCTestViewController.h; sourceTree = ""; }; 62 | CD9A24071DD5A64F00B580D0 /* ScrollableSegmentedControlDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ScrollableSegmentedControlDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | CD9A240A1DD5A64F00B580D0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 64 | CD9A240F1DD5A64F00B580D0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 65 | CD9A24111DD5A64F00B580D0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 66 | CD9A24141DD5A64F00B580D0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 67 | CD9A24161DD5A64F00B580D0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 68 | CD9A24271DD5A75300B580D0 /* ScrollableSegmentedControl.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ScrollableSegmentedControl.xcodeproj; path = ../../ScrollableSegmentedControl.xcodeproj; sourceTree = ""; }; 69 | CDDF9BD11DD5A7D4000B09AF /* TableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = ""; }; 70 | /* End PBXFileReference section */ 71 | 72 | /* Begin PBXFrameworksBuildPhase section */ 73 | CD9A24041DD5A64F00B580D0 /* Frameworks */ = { 74 | isa = PBXFrameworksBuildPhase; 75 | buildActionMask = 2147483647; 76 | files = ( 77 | CD9A24301DD5A76700B580D0 /* ScrollableSegmentedControl.framework in Frameworks */, 78 | ); 79 | runOnlyForDeploymentPostprocessing = 0; 80 | }; 81 | /* End PBXFrameworksBuildPhase section */ 82 | 83 | /* Begin PBXGroup section */ 84 | CD9A23FE1DD5A64F00B580D0 = { 85 | isa = PBXGroup; 86 | children = ( 87 | CD9A24271DD5A75300B580D0 /* ScrollableSegmentedControl.xcodeproj */, 88 | CD9A24091DD5A64F00B580D0 /* ScrollableSegmentedControlDemo */, 89 | CD9A24081DD5A64F00B580D0 /* Products */, 90 | ); 91 | sourceTree = ""; 92 | }; 93 | CD9A24081DD5A64F00B580D0 /* Products */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | CD9A24071DD5A64F00B580D0 /* ScrollableSegmentedControlDemo.app */, 97 | ); 98 | name = Products; 99 | sourceTree = ""; 100 | }; 101 | CD9A24091DD5A64F00B580D0 /* ScrollableSegmentedControlDemo */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | CD9A240A1DD5A64F00B580D0 /* AppDelegate.swift */, 105 | CDDF9BD11DD5A7D4000B09AF /* TableViewController.swift */, 106 | CD9A240E1DD5A64F00B580D0 /* Main.storyboard */, 107 | CD9A24111DD5A64F00B580D0 /* Assets.xcassets */, 108 | CD9A24131DD5A64F00B580D0 /* LaunchScreen.storyboard */, 109 | CD9A24161DD5A64F00B580D0 /* Info.plist */, 110 | CD8F9C6B1F8A748C00BD826C /* ScrollableSegmentedControlDemo-Bridging-Header.h */, 111 | CD8F9C711F8A74AF00BD826C /* ObjCTestViewController.h */, 112 | CD8F9C701F8A74AE00BD826C /* ObjCTestViewController.m */, 113 | ); 114 | path = ScrollableSegmentedControlDemo; 115 | sourceTree = ""; 116 | }; 117 | CD9A24281DD5A75300B580D0 /* Products */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | CD9A242D1DD5A75300B580D0 /* ScrollableSegmentedControl.framework */, 121 | CD9A242F1DD5A75300B580D0 /* ScrollableSegmentedControlTests.xctest */, 122 | ); 123 | name = Products; 124 | sourceTree = ""; 125 | }; 126 | /* End PBXGroup section */ 127 | 128 | /* Begin PBXNativeTarget section */ 129 | CD9A24061DD5A64F00B580D0 /* ScrollableSegmentedControlDemo */ = { 130 | isa = PBXNativeTarget; 131 | buildConfigurationList = CD9A24191DD5A64F00B580D0 /* Build configuration list for PBXNativeTarget "ScrollableSegmentedControlDemo" */; 132 | buildPhases = ( 133 | CD9A24031DD5A64F00B580D0 /* Sources */, 134 | CD9A24041DD5A64F00B580D0 /* Frameworks */, 135 | CD9A24051DD5A64F00B580D0 /* Resources */, 136 | CD9A24341DD5A76700B580D0 /* Embed Frameworks */, 137 | ); 138 | buildRules = ( 139 | ); 140 | dependencies = ( 141 | CD9A24331DD5A76700B580D0 /* PBXTargetDependency */, 142 | ); 143 | name = ScrollableSegmentedControlDemo; 144 | productName = ScrollableSegmentedControlDemo; 145 | productReference = CD9A24071DD5A64F00B580D0 /* ScrollableSegmentedControlDemo.app */; 146 | productType = "com.apple.product-type.application"; 147 | }; 148 | /* End PBXNativeTarget section */ 149 | 150 | /* Begin PBXProject section */ 151 | CD9A23FF1DD5A64F00B580D0 /* Project object */ = { 152 | isa = PBXProject; 153 | attributes = { 154 | LastSwiftUpdateCheck = 0800; 155 | LastUpgradeCheck = 1020; 156 | ORGANIZATIONNAME = Pomarium; 157 | TargetAttributes = { 158 | CD9A24061DD5A64F00B580D0 = { 159 | CreatedOnToolsVersion = 8.0; 160 | LastSwiftMigration = 1020; 161 | ProvisioningStyle = Automatic; 162 | }; 163 | }; 164 | }; 165 | buildConfigurationList = CD9A24021DD5A64F00B580D0 /* Build configuration list for PBXProject "ScrollableSegmentedControlDemo" */; 166 | compatibilityVersion = "Xcode 3.2"; 167 | developmentRegion = en; 168 | hasScannedForEncodings = 0; 169 | knownRegions = ( 170 | en, 171 | Base, 172 | ); 173 | mainGroup = CD9A23FE1DD5A64F00B580D0; 174 | productRefGroup = CD9A24081DD5A64F00B580D0 /* Products */; 175 | projectDirPath = ""; 176 | projectReferences = ( 177 | { 178 | ProductGroup = CD9A24281DD5A75300B580D0 /* Products */; 179 | ProjectRef = CD9A24271DD5A75300B580D0 /* ScrollableSegmentedControl.xcodeproj */; 180 | }, 181 | ); 182 | projectRoot = ""; 183 | targets = ( 184 | CD9A24061DD5A64F00B580D0 /* ScrollableSegmentedControlDemo */, 185 | ); 186 | }; 187 | /* End PBXProject section */ 188 | 189 | /* Begin PBXReferenceProxy section */ 190 | CD9A242D1DD5A75300B580D0 /* ScrollableSegmentedControl.framework */ = { 191 | isa = PBXReferenceProxy; 192 | fileType = wrapper.framework; 193 | path = ScrollableSegmentedControl.framework; 194 | remoteRef = CD9A242C1DD5A75300B580D0 /* PBXContainerItemProxy */; 195 | sourceTree = BUILT_PRODUCTS_DIR; 196 | }; 197 | CD9A242F1DD5A75300B580D0 /* ScrollableSegmentedControlTests.xctest */ = { 198 | isa = PBXReferenceProxy; 199 | fileType = wrapper.cfbundle; 200 | path = ScrollableSegmentedControlTests.xctest; 201 | remoteRef = CD9A242E1DD5A75300B580D0 /* PBXContainerItemProxy */; 202 | sourceTree = BUILT_PRODUCTS_DIR; 203 | }; 204 | /* End PBXReferenceProxy section */ 205 | 206 | /* Begin PBXResourcesBuildPhase section */ 207 | CD9A24051DD5A64F00B580D0 /* Resources */ = { 208 | isa = PBXResourcesBuildPhase; 209 | buildActionMask = 2147483647; 210 | files = ( 211 | CD9A24151DD5A64F00B580D0 /* LaunchScreen.storyboard in Resources */, 212 | CD9A24121DD5A64F00B580D0 /* Assets.xcassets in Resources */, 213 | CD9A24101DD5A64F00B580D0 /* Main.storyboard in Resources */, 214 | ); 215 | runOnlyForDeploymentPostprocessing = 0; 216 | }; 217 | /* End PBXResourcesBuildPhase section */ 218 | 219 | /* Begin PBXSourcesBuildPhase section */ 220 | CD9A24031DD5A64F00B580D0 /* Sources */ = { 221 | isa = PBXSourcesBuildPhase; 222 | buildActionMask = 2147483647; 223 | files = ( 224 | CD8F9C721F8A74AF00BD826C /* ObjCTestViewController.m in Sources */, 225 | CDDF9BD21DD5A7D4000B09AF /* TableViewController.swift in Sources */, 226 | CD9A240B1DD5A64F00B580D0 /* AppDelegate.swift in Sources */, 227 | ); 228 | runOnlyForDeploymentPostprocessing = 0; 229 | }; 230 | /* End PBXSourcesBuildPhase section */ 231 | 232 | /* Begin PBXTargetDependency section */ 233 | CD9A24331DD5A76700B580D0 /* PBXTargetDependency */ = { 234 | isa = PBXTargetDependency; 235 | name = ScrollableSegmentedControl; 236 | targetProxy = CD9A24321DD5A76700B580D0 /* PBXContainerItemProxy */; 237 | }; 238 | /* End PBXTargetDependency section */ 239 | 240 | /* Begin PBXVariantGroup section */ 241 | CD9A240E1DD5A64F00B580D0 /* Main.storyboard */ = { 242 | isa = PBXVariantGroup; 243 | children = ( 244 | CD9A240F1DD5A64F00B580D0 /* Base */, 245 | ); 246 | name = Main.storyboard; 247 | sourceTree = ""; 248 | }; 249 | CD9A24131DD5A64F00B580D0 /* LaunchScreen.storyboard */ = { 250 | isa = PBXVariantGroup; 251 | children = ( 252 | CD9A24141DD5A64F00B580D0 /* Base */, 253 | ); 254 | name = LaunchScreen.storyboard; 255 | sourceTree = ""; 256 | }; 257 | /* End PBXVariantGroup section */ 258 | 259 | /* Begin XCBuildConfiguration section */ 260 | CD9A24171DD5A64F00B580D0 /* Debug */ = { 261 | isa = XCBuildConfiguration; 262 | buildSettings = { 263 | ALWAYS_SEARCH_USER_PATHS = NO; 264 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 265 | CLANG_ANALYZER_NONNULL = YES; 266 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 267 | CLANG_CXX_LIBRARY = "libc++"; 268 | CLANG_ENABLE_MODULES = YES; 269 | CLANG_ENABLE_OBJC_ARC = YES; 270 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 271 | CLANG_WARN_BOOL_CONVERSION = YES; 272 | CLANG_WARN_COMMA = YES; 273 | CLANG_WARN_CONSTANT_CONVERSION = YES; 274 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 275 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 276 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 277 | CLANG_WARN_EMPTY_BODY = YES; 278 | CLANG_WARN_ENUM_CONVERSION = YES; 279 | CLANG_WARN_INFINITE_RECURSION = YES; 280 | CLANG_WARN_INT_CONVERSION = YES; 281 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 282 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 283 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 284 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 285 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 286 | CLANG_WARN_STRICT_PROTOTYPES = YES; 287 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 288 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 289 | CLANG_WARN_UNREACHABLE_CODE = YES; 290 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 291 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 292 | COPY_PHASE_STRIP = NO; 293 | DEBUG_INFORMATION_FORMAT = dwarf; 294 | ENABLE_STRICT_OBJC_MSGSEND = YES; 295 | ENABLE_TESTABILITY = YES; 296 | GCC_C_LANGUAGE_STANDARD = gnu99; 297 | GCC_DYNAMIC_NO_PIC = NO; 298 | GCC_NO_COMMON_BLOCKS = YES; 299 | GCC_OPTIMIZATION_LEVEL = 0; 300 | GCC_PREPROCESSOR_DEFINITIONS = ( 301 | "DEBUG=1", 302 | "$(inherited)", 303 | ); 304 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 305 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 306 | GCC_WARN_UNDECLARED_SELECTOR = YES; 307 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 308 | GCC_WARN_UNUSED_FUNCTION = YES; 309 | GCC_WARN_UNUSED_VARIABLE = YES; 310 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 311 | MTL_ENABLE_DEBUG_INFO = YES; 312 | ONLY_ACTIVE_ARCH = YES; 313 | SDKROOT = iphoneos; 314 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 315 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 316 | }; 317 | name = Debug; 318 | }; 319 | CD9A24181DD5A64F00B580D0 /* Release */ = { 320 | isa = XCBuildConfiguration; 321 | buildSettings = { 322 | ALWAYS_SEARCH_USER_PATHS = NO; 323 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 324 | CLANG_ANALYZER_NONNULL = YES; 325 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 326 | CLANG_CXX_LIBRARY = "libc++"; 327 | CLANG_ENABLE_MODULES = YES; 328 | CLANG_ENABLE_OBJC_ARC = YES; 329 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 330 | CLANG_WARN_BOOL_CONVERSION = YES; 331 | CLANG_WARN_COMMA = YES; 332 | CLANG_WARN_CONSTANT_CONVERSION = YES; 333 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 334 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 335 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 336 | CLANG_WARN_EMPTY_BODY = YES; 337 | CLANG_WARN_ENUM_CONVERSION = YES; 338 | CLANG_WARN_INFINITE_RECURSION = YES; 339 | CLANG_WARN_INT_CONVERSION = YES; 340 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 341 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 342 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 343 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 344 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 345 | CLANG_WARN_STRICT_PROTOTYPES = YES; 346 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 347 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 348 | CLANG_WARN_UNREACHABLE_CODE = YES; 349 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 350 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 351 | COPY_PHASE_STRIP = NO; 352 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 353 | ENABLE_NS_ASSERTIONS = NO; 354 | ENABLE_STRICT_OBJC_MSGSEND = YES; 355 | GCC_C_LANGUAGE_STANDARD = gnu99; 356 | GCC_NO_COMMON_BLOCKS = YES; 357 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 358 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 359 | GCC_WARN_UNDECLARED_SELECTOR = YES; 360 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 361 | GCC_WARN_UNUSED_FUNCTION = YES; 362 | GCC_WARN_UNUSED_VARIABLE = YES; 363 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 364 | MTL_ENABLE_DEBUG_INFO = NO; 365 | SDKROOT = iphoneos; 366 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 367 | VALIDATE_PRODUCT = YES; 368 | }; 369 | name = Release; 370 | }; 371 | CD9A241A1DD5A64F00B580D0 /* Debug */ = { 372 | isa = XCBuildConfiguration; 373 | buildSettings = { 374 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 375 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 376 | CLANG_ENABLE_MODULES = YES; 377 | INFOPLIST_FILE = ScrollableSegmentedControlDemo/Info.plist; 378 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 379 | PRODUCT_BUNDLE_IDENTIFIER = au.com.pomarium.ScrollableSegmentedControlDemo; 380 | PRODUCT_NAME = "$(TARGET_NAME)"; 381 | SWIFT_OBJC_BRIDGING_HEADER = "ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo-Bridging-Header.h"; 382 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 383 | SWIFT_VERSION = 5.0; 384 | }; 385 | name = Debug; 386 | }; 387 | CD9A241B1DD5A64F00B580D0 /* Release */ = { 388 | isa = XCBuildConfiguration; 389 | buildSettings = { 390 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 391 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 392 | CLANG_ENABLE_MODULES = YES; 393 | INFOPLIST_FILE = ScrollableSegmentedControlDemo/Info.plist; 394 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 395 | PRODUCT_BUNDLE_IDENTIFIER = au.com.pomarium.ScrollableSegmentedControlDemo; 396 | PRODUCT_NAME = "$(TARGET_NAME)"; 397 | SWIFT_OBJC_BRIDGING_HEADER = "ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo-Bridging-Header.h"; 398 | SWIFT_VERSION = 5.0; 399 | }; 400 | name = Release; 401 | }; 402 | /* End XCBuildConfiguration section */ 403 | 404 | /* Begin XCConfigurationList section */ 405 | CD9A24021DD5A64F00B580D0 /* Build configuration list for PBXProject "ScrollableSegmentedControlDemo" */ = { 406 | isa = XCConfigurationList; 407 | buildConfigurations = ( 408 | CD9A24171DD5A64F00B580D0 /* Debug */, 409 | CD9A24181DD5A64F00B580D0 /* Release */, 410 | ); 411 | defaultConfigurationIsVisible = 0; 412 | defaultConfigurationName = Release; 413 | }; 414 | CD9A24191DD5A64F00B580D0 /* Build configuration list for PBXNativeTarget "ScrollableSegmentedControlDemo" */ = { 415 | isa = XCConfigurationList; 416 | buildConfigurations = ( 417 | CD9A241A1DD5A64F00B580D0 /* Debug */, 418 | CD9A241B1DD5A64F00B580D0 /* Release */, 419 | ); 420 | defaultConfigurationIsVisible = 0; 421 | defaultConfigurationName = Release; 422 | }; 423 | /* End XCConfigurationList section */ 424 | }; 425 | rootObject = CD9A23FF1DD5A64F00B580D0 /* Project object */; 426 | } 427 | -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo.xcodeproj/xcshareddata/xcschemes/ScrollableSegmentedControlDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ScrollableSegmentedControlDemo 4 | // 5 | // Created by Goce Petrovski on 11/11/16. 6 | // Copyright © 2016 Pomarium. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ScrollableSegmentedControl 11 | 12 | @UIApplicationMain 13 | class AppDelegate: UIResponder, UIApplicationDelegate { 14 | 15 | var window: UIWindow? 16 | 17 | 18 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 19 | // Override point for customization after application launch. 20 | let segmentedControlAppearance = ScrollableSegmentedControl.appearance() 21 | segmentedControlAppearance.segmentContentColor = UIColor.white 22 | segmentedControlAppearance.selectedSegmentContentColor = UIColor.yellow 23 | segmentedControlAppearance.backgroundColor = UIColor.black 24 | 25 | return true 26 | } 27 | 28 | func applicationWillResignActive(_ application: UIApplication) { 29 | // 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. 30 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 31 | } 32 | 33 | func applicationDidEnterBackground(_ application: UIApplication) { 34 | // 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. 35 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 36 | } 37 | 38 | func applicationWillEnterForeground(_ application: UIApplication) { 39 | // 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. 40 | } 41 | 42 | func applicationDidBecomeActive(_ application: UIApplication) { 43 | // 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. 44 | } 45 | 46 | func applicationWillTerminate(_ application: UIApplication) { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-1.imageset/704-compose-toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GocePetrovski/ScrollableSegmentedControl/16c890302bd92ba9f7064603cea32633f7ba73bc/Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-1.imageset/704-compose-toolbar.png -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-1.imageset/704-compose-toolbar@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GocePetrovski/ScrollableSegmentedControl/16c890302bd92ba9f7064603cea32633f7ba73bc/Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-1.imageset/704-compose-toolbar@2x.png -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-1.imageset/704-compose-toolbar@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GocePetrovski/ScrollableSegmentedControl/16c890302bd92ba9f7064603cea32633f7ba73bc/Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-1.imageset/704-compose-toolbar@3x.png -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "704-compose-toolbar.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "704-compose-toolbar@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "704-compose-toolbar@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-2.imageset/707-albums-toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GocePetrovski/ScrollableSegmentedControl/16c890302bd92ba9f7064603cea32633f7ba73bc/Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-2.imageset/707-albums-toolbar.png -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-2.imageset/707-albums-toolbar@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GocePetrovski/ScrollableSegmentedControl/16c890302bd92ba9f7064603cea32633f7ba73bc/Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-2.imageset/707-albums-toolbar@2x.png -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-2.imageset/707-albums-toolbar@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GocePetrovski/ScrollableSegmentedControl/16c890302bd92ba9f7064603cea32633f7ba73bc/Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-2.imageset/707-albums-toolbar@3x.png -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "707-albums-toolbar.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "707-albums-toolbar@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "707-albums-toolbar@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-3.imageset/714-camera-toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GocePetrovski/ScrollableSegmentedControl/16c890302bd92ba9f7064603cea32633f7ba73bc/Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-3.imageset/714-camera-toolbar.png -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-3.imageset/714-camera-toolbar@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GocePetrovski/ScrollableSegmentedControl/16c890302bd92ba9f7064603cea32633f7ba73bc/Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-3.imageset/714-camera-toolbar@2x.png -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-3.imageset/714-camera-toolbar@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GocePetrovski/ScrollableSegmentedControl/16c890302bd92ba9f7064603cea32633f7ba73bc/Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-3.imageset/714-camera-toolbar@3x.png -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "714-camera-toolbar.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "714-camera-toolbar@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "714-camera-toolbar@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-4.imageset/724-info-toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GocePetrovski/ScrollableSegmentedControl/16c890302bd92ba9f7064603cea32633f7ba73bc/Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-4.imageset/724-info-toolbar.png -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-4.imageset/724-info-toolbar@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GocePetrovski/ScrollableSegmentedControl/16c890302bd92ba9f7064603cea32633f7ba73bc/Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-4.imageset/724-info-toolbar@2x.png -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-4.imageset/724-info-toolbar@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GocePetrovski/ScrollableSegmentedControl/16c890302bd92ba9f7064603cea32633f7ba73bc/Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-4.imageset/724-info-toolbar@3x.png -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "724-info-toolbar.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "724-info-toolbar@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "724-info-toolbar@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-5.imageset/729-top-list-toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GocePetrovski/ScrollableSegmentedControl/16c890302bd92ba9f7064603cea32633f7ba73bc/Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-5.imageset/729-top-list-toolbar.png -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-5.imageset/729-top-list-toolbar@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GocePetrovski/ScrollableSegmentedControl/16c890302bd92ba9f7064603cea32633f7ba73bc/Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-5.imageset/729-top-list-toolbar@2x.png -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-5.imageset/729-top-list-toolbar@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GocePetrovski/ScrollableSegmentedControl/16c890302bd92ba9f7064603cea32633f7ba73bc/Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-5.imageset/729-top-list-toolbar@3x.png -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-5.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "729-top-list-toolbar.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "729-top-list-toolbar@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "729-top-list-toolbar@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-6.imageset/730-envelope-toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GocePetrovski/ScrollableSegmentedControl/16c890302bd92ba9f7064603cea32633f7ba73bc/Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-6.imageset/730-envelope-toolbar.png -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-6.imageset/730-envelope-toolbar@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GocePetrovski/ScrollableSegmentedControl/16c890302bd92ba9f7064603cea32633f7ba73bc/Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-6.imageset/730-envelope-toolbar@2x.png -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-6.imageset/730-envelope-toolbar@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GocePetrovski/ScrollableSegmentedControl/16c890302bd92ba9f7064603cea32633f7ba73bc/Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-6.imageset/730-envelope-toolbar@3x.png -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Assets.xcassets/segment-6.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "730-envelope-toolbar.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "730-envelope-toolbar@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "730-envelope-toolbar@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/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 | -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 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 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 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 | 259 | 260 | 261 | 262 | 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 | -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/ObjCTestViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ObjCTestViewController.h 3 | // ScrollableSegmentedControlDemo 4 | // 5 | // Created by Goce Petrovski on 8/10/17. 6 | // Copyright © 2017 Pomarium. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ObjCTestViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/ObjCTestViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ObjCTestViewController.m 3 | // ScrollableSegmentedControlDemo 4 | // 5 | // Created by Goce Petrovski on 8/10/17. 6 | // Copyright © 2017 Pomarium. All rights reserved. 7 | // 8 | 9 | #import "ObjCTestViewController.h" 10 | @import ScrollableSegmentedControl; 11 | 12 | @interface ObjCTestViewController () 13 | 14 | @end 15 | 16 | @implementation ObjCTestViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | 21 | ScrollableSegmentedControl *segmentedControl = [[ScrollableSegmentedControl alloc] init]; 22 | segmentedControl.selectedSegmentIndex = 0; 23 | segmentedControl.segmentStyle = ScrollableSegmentedControlSegmentStyleTextOnly; 24 | } 25 | 26 | - (void)didReceiveMemoryWarning { 27 | [super didReceiveMemoryWarning]; 28 | // Dispose of any resources that can be recreated. 29 | } 30 | 31 | /* 32 | #pragma mark - Navigation 33 | 34 | // In a storyboard-based application, you will often want to do a little preparation before navigation 35 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 36 | // Get the new view controller using [segue destinationViewController]. 37 | // Pass the selected object to the new view controller. 38 | } 39 | */ 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /Demo/ScrollableSegmentedControlDemo/ScrollableSegmentedControlDemo/TableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewController.swift 3 | // ScrollableSegmentedControlDemo 4 | // 5 | // Created by Goce Petrovski on 11/11/16. 6 | // Copyright © 2016 Pomarium. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ScrollableSegmentedControl 11 | 12 | class TableViewController: UITableViewController { 13 | 14 | @IBOutlet weak var segmentedControl: ScrollableSegmentedControl! 15 | @IBOutlet weak var removeSegmentButton: UIBarButtonItem! 16 | @IBOutlet weak var fixedWidthSwitch: UISwitch! 17 | 18 | var selectedIndexPath = IndexPath(row: 0, section: 0) 19 | var selectedAttributesIndexPath = IndexPath(row: 0, section: 1) 20 | 21 | let largerRedTextAttributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16), 22 | NSAttributedString.Key.foregroundColor: UIColor.red] 23 | let largerRedTextHighlightAttributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16), 24 | NSAttributedString.Key.foregroundColor: UIColor.blue] 25 | let largerRedTextSelectAttributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16), 26 | NSAttributedString.Key.foregroundColor: UIColor.orange] 27 | 28 | override func viewDidLoad() { 29 | super.viewDidLoad() 30 | 31 | segmentedControl.segmentStyle = .textOnly 32 | segmentedControl.insertSegment(withTitle: "Segment 1", image: #imageLiteral(resourceName: "segment-1"), at: 0) 33 | segmentedControl.insertSegment(withTitle: "S 2", image: #imageLiteral(resourceName: "segment-2"), at: 1) 34 | segmentedControl.insertSegment(withTitle: "Segment 3.0001", image: #imageLiteral(resourceName: "segment-3"), at: 2) 35 | segmentedControl.insertSegment(withTitle: "Seg 4", image: #imageLiteral(resourceName: "segment-4"), at: 3) 36 | segmentedControl.insertSegment(withTitle: "Segment 5", image: #imageLiteral(resourceName: "segment-5"), at: 4) 37 | segmentedControl.insertSegment(withTitle: "Segment 6", image: #imageLiteral(resourceName: "segment-6"), at: 5) 38 | segmentedControl.underlineHeight = 3.0 39 | 40 | segmentedControl.underlineSelected = true 41 | segmentedControl.selectedSegmentIndex = 0 42 | //fixedWidthSwitch.isOn = false 43 | segmentedControl.fixedSegmentWidth = fixedWidthSwitch.isOn 44 | 45 | segmentedControl.addTarget(self, action: #selector(TableViewController.segmentSelected(sender:)), for: .valueChanged) 46 | } 47 | 48 | @objc func segmentSelected(sender:ScrollableSegmentedControl) { 49 | print("Segment at index \(sender.selectedSegmentIndex) selected") 50 | } 51 | 52 | @IBAction func addSegment(_ sender: Any) { 53 | let index = segmentedControl.numberOfSegments 54 | segmentedControl.insertSegment(withTitle: "Segment \(index + 1)", image: #imageLiteral(resourceName: "segment-6"), at: index) 55 | } 56 | 57 | @IBAction func removeSegment(_ sender: Any) { 58 | //segmentedControl.removeSegment(at: 0) 59 | if segmentedControl.numberOfSegments > 1 { 60 | segmentedControl.removeSegment(at: segmentedControl.numberOfSegments - 1) 61 | } 62 | } 63 | 64 | @IBAction func underlineHeightChanged(_ sender: UISlider) { 65 | segmentedControl.underlineHeight = CGFloat(sender.value) 66 | } 67 | @IBAction func toggleFixedWidth(_ sender: UISwitch) { 68 | segmentedControl.fixedSegmentWidth = sender.isOn 69 | segmentedControl.setNeedsLayout() 70 | } 71 | 72 | override func didReceiveMemoryWarning() { 73 | super.didReceiveMemoryWarning() 74 | // Dispose of any resources that can be recreated. 75 | } 76 | 77 | override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { 78 | if indexPath.section == 1 && indexPath.row == 1 { 79 | cell.textLabel!.attributedText = NSAttributedString(string: cell.textLabel!.text!, attributes: largerRedTextAttributes) 80 | } 81 | } 82 | 83 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 84 | if indexPath.section == 0 { 85 | if let cell = tableView.cellForRow(at: selectedIndexPath) { 86 | cell.accessoryType = .none 87 | } 88 | selectedIndexPath = indexPath 89 | if let cell = tableView.cellForRow(at: selectedIndexPath) { 90 | cell.accessoryType = .checkmark 91 | } 92 | 93 | var height = 44 94 | switch indexPath.row { 95 | case 0: 96 | segmentedControl.segmentStyle = .textOnly 97 | case 1: 98 | segmentedControl.segmentStyle = .imageOnly 99 | height = 52 100 | case 2: 101 | segmentedControl.segmentStyle = .imageOnTop 102 | height = 60 103 | case 3: 104 | segmentedControl.segmentStyle = .imageOnLeft 105 | default: break 106 | 107 | } 108 | 109 | let headerView = tableView.tableHeaderView! 110 | tableView.tableHeaderView = nil 111 | var headerFrame = headerView.frame 112 | 113 | headerFrame.size.height = CGFloat(height) 114 | headerView.frame = headerFrame 115 | tableView.tableHeaderView = headerView 116 | } else if indexPath.section == 1 { 117 | if let cell = tableView.cellForRow(at: selectedAttributesIndexPath) { 118 | cell.accessoryType = .none 119 | } 120 | selectedAttributesIndexPath = indexPath 121 | if let cell = tableView.cellForRow(at: selectedAttributesIndexPath) { 122 | cell.accessoryType = .checkmark 123 | } 124 | 125 | switch indexPath.row { 126 | case 0: 127 | segmentedControl.setTitleTextAttributes(nil, for: .normal) 128 | segmentedControl.setTitleTextAttributes(nil, for: .highlighted) 129 | segmentedControl.setTitleTextAttributes(nil, for: .selected) 130 | case 1: 131 | segmentedControl.setTitleTextAttributes(largerRedTextAttributes, for: .normal) 132 | segmentedControl.setTitleTextAttributes(largerRedTextHighlightAttributes, for: .highlighted) 133 | segmentedControl.setTitleTextAttributes(largerRedTextSelectAttributes, for: .selected) 134 | default: break 135 | 136 | } 137 | } 138 | 139 | tableView.deselectRow(at: indexPath, animated: true) 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /Demo/image_on_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GocePetrovski/ScrollableSegmentedControl/16c890302bd92ba9f7064603cea32633f7ba73bc/Demo/image_on_left.png -------------------------------------------------------------------------------- /Demo/image_on_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GocePetrovski/ScrollableSegmentedControl/16c890302bd92ba9f7064603cea32633f7ba73bc/Demo/image_on_top.png -------------------------------------------------------------------------------- /Demo/image_only.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GocePetrovski/ScrollableSegmentedControl/16c890302bd92ba9f7064603cea32633f7ba73bc/Demo/image_only.png -------------------------------------------------------------------------------- /Demo/text_only.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GocePetrovski/ScrollableSegmentedControl/16c890302bd92ba9f7064603cea32633f7ba73bc/Demo/text_only.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Goce Petrovski @GocePetrovski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ScrollableSegmentedControl 2 | 3 | [![CI Status](https://travis-ci.org/GocePetrovski/ScrollableSegmentedControl.svg?branch=master)](https://travis-ci.org/GocePetrovski/ScrollableSegmentedControl) 4 | [![Version](https://img.shields.io/cocoapods/v/ScrollableSegmentedControl.svg?style=flat)](http://cocoadocs.org/docsets/ScrollableSegmentedControl) 5 | [![License](https://img.shields.io/cocoapods/l/ScrollableSegmentedControl.svg?style=flat)](http://cocoadocs.org/docsets/ScrollableSegmentedControl) 6 | [![Platform](https://img.shields.io/cocoapods/p/ScrollableSegmentedControl.svg?style=flat)](http://cocoadocs.org/docsets/ScrollableSegmentedControl) 7 | 8 | ScrollableSegmentedControl is replacement for UISegmentedControl that becomes scrollable when the total width of all segments exceeds the width of the control itself. 9 | 10 | ScrollableSegmentedControl supports 4 styles of segments: text only, image only, text with image on top and text with image on left. 11 | 12 | | Text Only | Image Only | 13 | |-------------------------------|---------------------------------| 14 | |![Demo](Demo/text_only.png) |![Demo](Demo/image_only.png) | 15 | 16 | 17 | | Image On Top | Image On Left | 18 | |-------------------------------|---------------------------------| 19 | |![Demo](Demo/image_on_top.png) |![Demo](Demo/image_on_left.png) | 20 | 21 | 22 | 23 | ### Support 24 | 25 | - Xcode 10.2+ 26 | - Swift 5.0+ 27 | - iOS 9.0+ 28 | 29 | 30 | # Usage 31 | 32 | ScrollableSegmentedControl is designed to be used exactly same as UISegmentedControl. methods and properties are deliberately named same or very similar to ones found in UISegmentedControl. 33 | 34 | ```swift 35 | @IBOutlet weak var segmentedControl: ScrollableSegmentedControl! 36 | 37 | override func viewDidLoad() { 38 | super.viewDidLoad() 39 | 40 | segmentedControl.segmentStyle = .textOnly 41 | segmentedControl.insertSegment(withTitle: "Segment 1", image: #imageLiteral(resourceName: "segment-1"), at: 0) 42 | segmentedControl.insertSegment(withTitle: "Segment 2", image: #imageLiteral(resourceName: "segment-2"), at: 1) 43 | segmentedControl.insertSegment(withTitle: "Segment 3", image: #imageLiteral(resourceName: "segment-3"), at: 2) 44 | segmentedControl.insertSegment(withTitle: "Segment 4", image: #imageLiteral(resourceName: "segment-4"), at: 3) 45 | segmentedControl.insertSegment(withTitle: "Segment 5", image: #imageLiteral(resourceName: "segment-5"), at: 4) 46 | segmentedControl.insertSegment(withTitle: "Segment 6", image: #imageLiteral(resourceName: "segment-6"), at: 5) 47 | 48 | segmentedControl.underlineSelected = true 49 | 50 | segmentedControl.addTarget(self, action: #selector(TableViewController.segmentSelected(sender:)), for: .valueChanged) 51 | 52 | // change some colors 53 | segmentedControl.segmentContentColor = UIColor.white 54 | segmentedControl.selectedSegmentContentColor = UIColor.yellow 55 | segmentedControl.backgroundColor = UIColor.black 56 | 57 | // Turn off all segments been fixed/equal width. 58 | // The width of each segment would be based on the text length and font size. 59 | segmentedControl.fixedSegmentWidth = false 60 | } 61 | 62 | func segmentSelected(sender:ScrollableSegmentedControl) { 63 | print("Segment at index \(sender.selectedSegmentIndex) selected") 64 | } 65 | ``` 66 | 67 | Using appearance proxy to change some colors 68 | 69 | ```swift 70 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 71 | let segmentedControlAppearance = ScrollableSegmentedControl.appearance() 72 | segmentedControlAppearance.segmentContentColor = UIColor.white 73 | segmentedControlAppearance.selectedSegmentContentColor = UIColor.yellow 74 | segmentedControlAppearance.backgroundColor = UIColor.black 75 | 76 | return true 77 | } 78 | ``` 79 | 80 | Configuring custom title attributes via setTitleTextAttributes method 81 | 82 | ```swift 83 | let largerRedTextAttributes = [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 16), NSAttributedStringKey.foregroundColor: UIColor.red] 84 | let largerRedTextHighlightAttributes = [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 16), NSAttributedStringKey.foregroundColor: UIColor.blue] 85 | let largerRedTextSelectAttributes = [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 16), NSAttributedStringKey.foregroundColor: UIColor.orange] 86 | 87 | segmentedControl.setTitleTextAttributes(largerRedTextAttributes, for: .normal) 88 | segmentedControl.setTitleTextAttributes(largerRedTextHighlightAttributes, for: .highlighted) 89 | segmentedControl.setTitleTextAttributes(largerRedTextSelectAttributes, for: .selected) 90 | ``` 91 | 92 | See the Demo project for example on how to use it. 93 | 94 | 95 | # Installation 96 | 97 | ### CocoaPods 98 | The easiest way of installing ScrollableSegmentedControl is via [CocoaPods](http://cocoapods.org/). 99 | 100 | ``` 101 | pod 'ScrollableSegmentedControl', '~> 1.5.0' 102 | ``` 103 | 104 | ### Old-fashioned way 105 | Just add `ScrollableSegmentedControl.swift` to your project. 106 | -------------------------------------------------------------------------------- /ScrollableSegmentedControl.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'ScrollableSegmentedControl' 3 | s.version = '1.5.0' 4 | s.license = 'MIT' 5 | s.summary = 'Scrollable Segmented Control for when UISegmentedControl is not sufficient' 6 | s.homepage = 'https://github.com/GocePetrovski/ScrollableSegmentedControl' 7 | s.social_media_url = 'http://twitter.com/GocePetrovski' 8 | s.authors = { 'Goce Petrovski' => 'goce.petrovski@gmail.com' } 9 | s.source = { :git => 'https://github.com/GocePetrovski/ScrollableSegmentedControl.git', :tag => s.version } 10 | 11 | s.platform = :ios, '9.0' 12 | s.swift_version = '5.0' 13 | 14 | s.source_files = 'ScrollableSegmentedControl/*.swift' 15 | end 16 | -------------------------------------------------------------------------------- /ScrollableSegmentedControl.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | CD078DA71DD4B5B30083F53F /* ScrollableSegmentedControl.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD078D9D1DD4B5B30083F53F /* ScrollableSegmentedControl.framework */; }; 11 | CD078DAC1DD4B5B30083F53F /* ScrollableSegmentedControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD078DAB1DD4B5B30083F53F /* ScrollableSegmentedControlTests.swift */; }; 12 | CD078DAE1DD4B5B30083F53F /* ScrollableSegmentedControl.h in Headers */ = {isa = PBXBuildFile; fileRef = CD078DA01DD4B5B30083F53F /* ScrollableSegmentedControl.h */; settings = {ATTRIBUTES = (Public, ); }; }; 13 | CD078DB81DD4B69F0083F53F /* ScrollableSegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD078DB71DD4B69F0083F53F /* ScrollableSegmentedControl.swift */; }; 14 | /* End PBXBuildFile section */ 15 | 16 | /* Begin PBXContainerItemProxy section */ 17 | CD078DA81DD4B5B30083F53F /* PBXContainerItemProxy */ = { 18 | isa = PBXContainerItemProxy; 19 | containerPortal = CD078D941DD4B5B30083F53F /* Project object */; 20 | proxyType = 1; 21 | remoteGlobalIDString = CD078D9C1DD4B5B30083F53F; 22 | remoteInfo = ScrollableSegmentedControl; 23 | }; 24 | /* End PBXContainerItemProxy section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | CD078D9D1DD4B5B30083F53F /* ScrollableSegmentedControl.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ScrollableSegmentedControl.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | CD078DA01DD4B5B30083F53F /* ScrollableSegmentedControl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScrollableSegmentedControl.h; sourceTree = ""; }; 29 | CD078DA11DD4B5B30083F53F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 30 | CD078DA61DD4B5B30083F53F /* ScrollableSegmentedControlTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ScrollableSegmentedControlTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 31 | CD078DAB1DD4B5B30083F53F /* ScrollableSegmentedControlTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollableSegmentedControlTests.swift; sourceTree = ""; }; 32 | CD078DAD1DD4B5B30083F53F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33 | CD078DB71DD4B69F0083F53F /* ScrollableSegmentedControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrollableSegmentedControl.swift; sourceTree = ""; }; 34 | /* End PBXFileReference section */ 35 | 36 | /* Begin PBXFrameworksBuildPhase section */ 37 | CD078D991DD4B5B30083F53F /* Frameworks */ = { 38 | isa = PBXFrameworksBuildPhase; 39 | buildActionMask = 2147483647; 40 | files = ( 41 | ); 42 | runOnlyForDeploymentPostprocessing = 0; 43 | }; 44 | CD078DA31DD4B5B30083F53F /* Frameworks */ = { 45 | isa = PBXFrameworksBuildPhase; 46 | buildActionMask = 2147483647; 47 | files = ( 48 | CD078DA71DD4B5B30083F53F /* ScrollableSegmentedControl.framework in Frameworks */, 49 | ); 50 | runOnlyForDeploymentPostprocessing = 0; 51 | }; 52 | /* End PBXFrameworksBuildPhase section */ 53 | 54 | /* Begin PBXGroup section */ 55 | CD078D931DD4B5B30083F53F = { 56 | isa = PBXGroup; 57 | children = ( 58 | CD078D9F1DD4B5B30083F53F /* ScrollableSegmentedControl */, 59 | CD078DAA1DD4B5B30083F53F /* ScrollableSegmentedControlTests */, 60 | CD078D9E1DD4B5B30083F53F /* Products */, 61 | ); 62 | sourceTree = ""; 63 | }; 64 | CD078D9E1DD4B5B30083F53F /* Products */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | CD078D9D1DD4B5B30083F53F /* ScrollableSegmentedControl.framework */, 68 | CD078DA61DD4B5B30083F53F /* ScrollableSegmentedControlTests.xctest */, 69 | ); 70 | name = Products; 71 | sourceTree = ""; 72 | }; 73 | CD078D9F1DD4B5B30083F53F /* ScrollableSegmentedControl */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | CD078DB71DD4B69F0083F53F /* ScrollableSegmentedControl.swift */, 77 | CD078DA01DD4B5B30083F53F /* ScrollableSegmentedControl.h */, 78 | CD078DA11DD4B5B30083F53F /* Info.plist */, 79 | ); 80 | path = ScrollableSegmentedControl; 81 | sourceTree = ""; 82 | }; 83 | CD078DAA1DD4B5B30083F53F /* ScrollableSegmentedControlTests */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | CD078DAB1DD4B5B30083F53F /* ScrollableSegmentedControlTests.swift */, 87 | CD078DAD1DD4B5B30083F53F /* Info.plist */, 88 | ); 89 | path = ScrollableSegmentedControlTests; 90 | sourceTree = ""; 91 | }; 92 | /* End PBXGroup section */ 93 | 94 | /* Begin PBXHeadersBuildPhase section */ 95 | CD078D9A1DD4B5B30083F53F /* Headers */ = { 96 | isa = PBXHeadersBuildPhase; 97 | buildActionMask = 2147483647; 98 | files = ( 99 | CD078DAE1DD4B5B30083F53F /* ScrollableSegmentedControl.h in Headers */, 100 | ); 101 | runOnlyForDeploymentPostprocessing = 0; 102 | }; 103 | /* End PBXHeadersBuildPhase section */ 104 | 105 | /* Begin PBXNativeTarget section */ 106 | CD078D9C1DD4B5B30083F53F /* ScrollableSegmentedControl */ = { 107 | isa = PBXNativeTarget; 108 | buildConfigurationList = CD078DB11DD4B5B30083F53F /* Build configuration list for PBXNativeTarget "ScrollableSegmentedControl" */; 109 | buildPhases = ( 110 | CD078D981DD4B5B30083F53F /* Sources */, 111 | CD078D991DD4B5B30083F53F /* Frameworks */, 112 | CD078D9A1DD4B5B30083F53F /* Headers */, 113 | CD078D9B1DD4B5B30083F53F /* Resources */, 114 | ); 115 | buildRules = ( 116 | ); 117 | dependencies = ( 118 | ); 119 | name = ScrollableSegmentedControl; 120 | productName = ScrollableSegmentedControl; 121 | productReference = CD078D9D1DD4B5B30083F53F /* ScrollableSegmentedControl.framework */; 122 | productType = "com.apple.product-type.framework"; 123 | }; 124 | CD078DA51DD4B5B30083F53F /* ScrollableSegmentedControlTests */ = { 125 | isa = PBXNativeTarget; 126 | buildConfigurationList = CD078DB41DD4B5B30083F53F /* Build configuration list for PBXNativeTarget "ScrollableSegmentedControlTests" */; 127 | buildPhases = ( 128 | CD078DA21DD4B5B30083F53F /* Sources */, 129 | CD078DA31DD4B5B30083F53F /* Frameworks */, 130 | CD078DA41DD4B5B30083F53F /* Resources */, 131 | ); 132 | buildRules = ( 133 | ); 134 | dependencies = ( 135 | CD078DA91DD4B5B30083F53F /* PBXTargetDependency */, 136 | ); 137 | name = ScrollableSegmentedControlTests; 138 | productName = ScrollableSegmentedControlTests; 139 | productReference = CD078DA61DD4B5B30083F53F /* ScrollableSegmentedControlTests.xctest */; 140 | productType = "com.apple.product-type.bundle.unit-test"; 141 | }; 142 | /* End PBXNativeTarget section */ 143 | 144 | /* Begin PBXProject section */ 145 | CD078D941DD4B5B30083F53F /* Project object */ = { 146 | isa = PBXProject; 147 | attributes = { 148 | LastSwiftUpdateCheck = 0800; 149 | LastUpgradeCheck = 1020; 150 | ORGANIZATIONNAME = Pomarium; 151 | TargetAttributes = { 152 | CD078D9C1DD4B5B30083F53F = { 153 | CreatedOnToolsVersion = 8.0; 154 | LastSwiftMigration = 1020; 155 | ProvisioningStyle = Automatic; 156 | }; 157 | CD078DA51DD4B5B30083F53F = { 158 | CreatedOnToolsVersion = 8.0; 159 | LastSwiftMigration = 1020; 160 | ProvisioningStyle = Automatic; 161 | }; 162 | }; 163 | }; 164 | buildConfigurationList = CD078D971DD4B5B30083F53F /* Build configuration list for PBXProject "ScrollableSegmentedControl" */; 165 | compatibilityVersion = "Xcode 3.2"; 166 | developmentRegion = en; 167 | hasScannedForEncodings = 0; 168 | knownRegions = ( 169 | en, 170 | Base, 171 | ); 172 | mainGroup = CD078D931DD4B5B30083F53F; 173 | productRefGroup = CD078D9E1DD4B5B30083F53F /* Products */; 174 | projectDirPath = ""; 175 | projectRoot = ""; 176 | targets = ( 177 | CD078D9C1DD4B5B30083F53F /* ScrollableSegmentedControl */, 178 | CD078DA51DD4B5B30083F53F /* ScrollableSegmentedControlTests */, 179 | ); 180 | }; 181 | /* End PBXProject section */ 182 | 183 | /* Begin PBXResourcesBuildPhase section */ 184 | CD078D9B1DD4B5B30083F53F /* Resources */ = { 185 | isa = PBXResourcesBuildPhase; 186 | buildActionMask = 2147483647; 187 | files = ( 188 | ); 189 | runOnlyForDeploymentPostprocessing = 0; 190 | }; 191 | CD078DA41DD4B5B30083F53F /* Resources */ = { 192 | isa = PBXResourcesBuildPhase; 193 | buildActionMask = 2147483647; 194 | files = ( 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | }; 198 | /* End PBXResourcesBuildPhase section */ 199 | 200 | /* Begin PBXSourcesBuildPhase section */ 201 | CD078D981DD4B5B30083F53F /* Sources */ = { 202 | isa = PBXSourcesBuildPhase; 203 | buildActionMask = 2147483647; 204 | files = ( 205 | CD078DB81DD4B69F0083F53F /* ScrollableSegmentedControl.swift in Sources */, 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | CD078DA21DD4B5B30083F53F /* Sources */ = { 210 | isa = PBXSourcesBuildPhase; 211 | buildActionMask = 2147483647; 212 | files = ( 213 | CD078DAC1DD4B5B30083F53F /* ScrollableSegmentedControlTests.swift in Sources */, 214 | ); 215 | runOnlyForDeploymentPostprocessing = 0; 216 | }; 217 | /* End PBXSourcesBuildPhase section */ 218 | 219 | /* Begin PBXTargetDependency section */ 220 | CD078DA91DD4B5B30083F53F /* PBXTargetDependency */ = { 221 | isa = PBXTargetDependency; 222 | target = CD078D9C1DD4B5B30083F53F /* ScrollableSegmentedControl */; 223 | targetProxy = CD078DA81DD4B5B30083F53F /* PBXContainerItemProxy */; 224 | }; 225 | /* End PBXTargetDependency section */ 226 | 227 | /* Begin XCBuildConfiguration section */ 228 | CD078DAF1DD4B5B30083F53F /* Debug */ = { 229 | isa = XCBuildConfiguration; 230 | buildSettings = { 231 | ALWAYS_SEARCH_USER_PATHS = NO; 232 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 233 | CLANG_ANALYZER_NONNULL = YES; 234 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 235 | CLANG_CXX_LIBRARY = "libc++"; 236 | CLANG_ENABLE_MODULES = YES; 237 | CLANG_ENABLE_OBJC_ARC = YES; 238 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 239 | CLANG_WARN_BOOL_CONVERSION = YES; 240 | CLANG_WARN_COMMA = YES; 241 | CLANG_WARN_CONSTANT_CONVERSION = YES; 242 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 243 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 244 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 245 | CLANG_WARN_EMPTY_BODY = YES; 246 | CLANG_WARN_ENUM_CONVERSION = YES; 247 | CLANG_WARN_INFINITE_RECURSION = YES; 248 | CLANG_WARN_INT_CONVERSION = YES; 249 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 250 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 251 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 252 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 253 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 254 | CLANG_WARN_STRICT_PROTOTYPES = YES; 255 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 256 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 257 | CLANG_WARN_UNREACHABLE_CODE = YES; 258 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 259 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 260 | COPY_PHASE_STRIP = NO; 261 | CURRENT_PROJECT_VERSION = 1; 262 | DEBUG_INFORMATION_FORMAT = dwarf; 263 | ENABLE_STRICT_OBJC_MSGSEND = YES; 264 | ENABLE_TESTABILITY = YES; 265 | GCC_C_LANGUAGE_STANDARD = gnu99; 266 | GCC_DYNAMIC_NO_PIC = NO; 267 | GCC_NO_COMMON_BLOCKS = YES; 268 | GCC_OPTIMIZATION_LEVEL = 0; 269 | GCC_PREPROCESSOR_DEFINITIONS = ( 270 | "DEBUG=1", 271 | "$(inherited)", 272 | ); 273 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 274 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 275 | GCC_WARN_UNDECLARED_SELECTOR = YES; 276 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 277 | GCC_WARN_UNUSED_FUNCTION = YES; 278 | GCC_WARN_UNUSED_VARIABLE = YES; 279 | MTL_ENABLE_DEBUG_INFO = YES; 280 | ONLY_ACTIVE_ARCH = YES; 281 | SDKROOT = iphoneos; 282 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 283 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 284 | SWIFT_VERSION = 5.0; 285 | TARGETED_DEVICE_FAMILY = "1,2"; 286 | VERSIONING_SYSTEM = "apple-generic"; 287 | VERSION_INFO_PREFIX = ""; 288 | }; 289 | name = Debug; 290 | }; 291 | CD078DB01DD4B5B30083F53F /* Release */ = { 292 | isa = XCBuildConfiguration; 293 | buildSettings = { 294 | ALWAYS_SEARCH_USER_PATHS = NO; 295 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 296 | CLANG_ANALYZER_NONNULL = YES; 297 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 298 | CLANG_CXX_LIBRARY = "libc++"; 299 | CLANG_ENABLE_MODULES = YES; 300 | CLANG_ENABLE_OBJC_ARC = YES; 301 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 302 | CLANG_WARN_BOOL_CONVERSION = YES; 303 | CLANG_WARN_COMMA = YES; 304 | CLANG_WARN_CONSTANT_CONVERSION = YES; 305 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 306 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 307 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 308 | CLANG_WARN_EMPTY_BODY = YES; 309 | CLANG_WARN_ENUM_CONVERSION = YES; 310 | CLANG_WARN_INFINITE_RECURSION = YES; 311 | CLANG_WARN_INT_CONVERSION = YES; 312 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 313 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 314 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 315 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 316 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 317 | CLANG_WARN_STRICT_PROTOTYPES = YES; 318 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 319 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 320 | CLANG_WARN_UNREACHABLE_CODE = YES; 321 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 322 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 323 | COPY_PHASE_STRIP = NO; 324 | CURRENT_PROJECT_VERSION = 1; 325 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 326 | ENABLE_NS_ASSERTIONS = NO; 327 | ENABLE_STRICT_OBJC_MSGSEND = YES; 328 | GCC_C_LANGUAGE_STANDARD = gnu99; 329 | GCC_NO_COMMON_BLOCKS = YES; 330 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 331 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 332 | GCC_WARN_UNDECLARED_SELECTOR = YES; 333 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 334 | GCC_WARN_UNUSED_FUNCTION = YES; 335 | GCC_WARN_UNUSED_VARIABLE = YES; 336 | MTL_ENABLE_DEBUG_INFO = NO; 337 | SDKROOT = iphoneos; 338 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 339 | SWIFT_VERSION = 5.0; 340 | TARGETED_DEVICE_FAMILY = "1,2"; 341 | VALIDATE_PRODUCT = YES; 342 | VERSIONING_SYSTEM = "apple-generic"; 343 | VERSION_INFO_PREFIX = ""; 344 | }; 345 | name = Release; 346 | }; 347 | CD078DB21DD4B5B30083F53F /* Debug */ = { 348 | isa = XCBuildConfiguration; 349 | buildSettings = { 350 | CLANG_ENABLE_MODULES = YES; 351 | CODE_SIGN_IDENTITY = ""; 352 | DEFINES_MODULE = YES; 353 | DYLIB_COMPATIBILITY_VERSION = 1; 354 | DYLIB_CURRENT_VERSION = 1; 355 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 356 | INFOPLIST_FILE = ScrollableSegmentedControl/Info.plist; 357 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 358 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 359 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 360 | PRODUCT_BUNDLE_IDENTIFIER = au.com.pomarium.ScrollableSegmentedControl; 361 | PRODUCT_NAME = "$(TARGET_NAME)"; 362 | SKIP_INSTALL = YES; 363 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 364 | SWIFT_VERSION = 5.0; 365 | }; 366 | name = Debug; 367 | }; 368 | CD078DB31DD4B5B30083F53F /* Release */ = { 369 | isa = XCBuildConfiguration; 370 | buildSettings = { 371 | CLANG_ENABLE_MODULES = YES; 372 | CODE_SIGN_IDENTITY = ""; 373 | DEFINES_MODULE = YES; 374 | DYLIB_COMPATIBILITY_VERSION = 1; 375 | DYLIB_CURRENT_VERSION = 1; 376 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 377 | INFOPLIST_FILE = ScrollableSegmentedControl/Info.plist; 378 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 379 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 380 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 381 | LLVM_LTO = YES; 382 | PRODUCT_BUNDLE_IDENTIFIER = au.com.pomarium.ScrollableSegmentedControl; 383 | PRODUCT_NAME = "$(TARGET_NAME)"; 384 | SKIP_INSTALL = YES; 385 | SWIFT_VERSION = 5.0; 386 | }; 387 | name = Release; 388 | }; 389 | CD078DB51DD4B5B30083F53F /* Debug */ = { 390 | isa = XCBuildConfiguration; 391 | buildSettings = { 392 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 393 | INFOPLIST_FILE = ScrollableSegmentedControlTests/Info.plist; 394 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 395 | PRODUCT_BUNDLE_IDENTIFIER = au.com.pomarium.ScrollableSegmentedControlTests; 396 | PRODUCT_NAME = "$(TARGET_NAME)"; 397 | SWIFT_VERSION = 5.0; 398 | }; 399 | name = Debug; 400 | }; 401 | CD078DB61DD4B5B30083F53F /* Release */ = { 402 | isa = XCBuildConfiguration; 403 | buildSettings = { 404 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 405 | INFOPLIST_FILE = ScrollableSegmentedControlTests/Info.plist; 406 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 407 | PRODUCT_BUNDLE_IDENTIFIER = au.com.pomarium.ScrollableSegmentedControlTests; 408 | PRODUCT_NAME = "$(TARGET_NAME)"; 409 | SWIFT_VERSION = 5.0; 410 | }; 411 | name = Release; 412 | }; 413 | /* End XCBuildConfiguration section */ 414 | 415 | /* Begin XCConfigurationList section */ 416 | CD078D971DD4B5B30083F53F /* Build configuration list for PBXProject "ScrollableSegmentedControl" */ = { 417 | isa = XCConfigurationList; 418 | buildConfigurations = ( 419 | CD078DAF1DD4B5B30083F53F /* Debug */, 420 | CD078DB01DD4B5B30083F53F /* Release */, 421 | ); 422 | defaultConfigurationIsVisible = 0; 423 | defaultConfigurationName = Release; 424 | }; 425 | CD078DB11DD4B5B30083F53F /* Build configuration list for PBXNativeTarget "ScrollableSegmentedControl" */ = { 426 | isa = XCConfigurationList; 427 | buildConfigurations = ( 428 | CD078DB21DD4B5B30083F53F /* Debug */, 429 | CD078DB31DD4B5B30083F53F /* Release */, 430 | ); 431 | defaultConfigurationIsVisible = 0; 432 | defaultConfigurationName = Release; 433 | }; 434 | CD078DB41DD4B5B30083F53F /* Build configuration list for PBXNativeTarget "ScrollableSegmentedControlTests" */ = { 435 | isa = XCConfigurationList; 436 | buildConfigurations = ( 437 | CD078DB51DD4B5B30083F53F /* Debug */, 438 | CD078DB61DD4B5B30083F53F /* Release */, 439 | ); 440 | defaultConfigurationIsVisible = 0; 441 | defaultConfigurationName = Release; 442 | }; 443 | /* End XCConfigurationList section */ 444 | }; 445 | rootObject = CD078D941DD4B5B30083F53F /* Project object */; 446 | } 447 | -------------------------------------------------------------------------------- /ScrollableSegmentedControl.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ScrollableSegmentedControl.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ScrollableSegmentedControl.xcodeproj/xcshareddata/xcschemes/ScrollableSegmentedControl.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /ScrollableSegmentedControl/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 | 1.4.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /ScrollableSegmentedControl/ScrollableSegmentedControl.h: -------------------------------------------------------------------------------- 1 | // 2 | // ScrollableSegmentedControl.h 3 | // ScrollableSegmentedControl 4 | // 5 | // Created by Goce Petrovski on 10/11/16. 6 | // Copyright © 2016 Pomarium. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for ScrollableSegmentedControl. 12 | FOUNDATION_EXPORT double ScrollableSegmentedControlVersionNumber; 13 | 14 | //! Project version string for ScrollableSegmentedControl. 15 | FOUNDATION_EXPORT const unsigned char ScrollableSegmentedControlVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /ScrollableSegmentedControl/ScrollableSegmentedControl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScrollableSegmentedControl.swift 3 | // ScrollableSegmentedControl 4 | // 5 | // Created by Goce Petrovski on 10/11/16. 6 | // Copyright © 2017 Pomarium. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @objc 12 | public enum ScrollableSegmentedControlSegmentStyle: Int { 13 | case textOnly, imageOnly, imageOnTop, imageOnLeft 14 | } 15 | 16 | /** 17 | A ScrollableSegmentedControl object is horizontaly scrollable control made of multiple segments, each segment functioning as discrete button. 18 | */ 19 | @IBDesignable 20 | @objc public class ScrollableSegmentedControl: UIControl { 21 | fileprivate let flowLayout = UICollectionViewFlowLayout() 22 | fileprivate var collectionView:UICollectionView? 23 | private var collectionViewController:CollectionViewController? 24 | private var segmentsData = [SegmentData]() 25 | private var longestTextWidth:CGFloat = 10 26 | 27 | /** 28 | A Boolean value that determines if the width of all segments is going to be fixed or not. 29 | 30 | When this value is set to true all segments have the same width which equivalent of the width required to display the text that requires the longest width to be drawn. 31 | The default value is true. 32 | */ 33 | public var fixedSegmentWidth: Bool = true { 34 | didSet { 35 | if oldValue != fixedSegmentWidth { 36 | setNeedsLayout() 37 | } 38 | } 39 | } 40 | 41 | 42 | @objc public var segmentStyle:ScrollableSegmentedControlSegmentStyle = .textOnly { 43 | didSet { 44 | if oldValue != segmentStyle { 45 | if let collectionView_ = collectionView { 46 | let nilCellClass:AnyClass? = nil 47 | // unregister the old cell 48 | switch oldValue { 49 | case .textOnly: 50 | collectionView_.register(nilCellClass, forCellWithReuseIdentifier: CollectionViewController.textOnlyCellIdentifier) 51 | case .imageOnly: 52 | collectionView_.register(nilCellClass, forCellWithReuseIdentifier: CollectionViewController.imageOnlyCellIdentifier) 53 | case .imageOnTop: 54 | collectionView_.register(nilCellClass, forCellWithReuseIdentifier: CollectionViewController.imageOnTopCellIdentifier) 55 | case .imageOnLeft: 56 | collectionView_.register(nilCellClass, forCellWithReuseIdentifier: CollectionViewController.imageOnLeftCellIdentifier) 57 | } 58 | 59 | // register the new cell 60 | switch segmentStyle { 61 | case .textOnly: 62 | collectionView_.register(TextOnlySegmentCollectionViewCell.self, forCellWithReuseIdentifier: CollectionViewController.textOnlyCellIdentifier) 63 | case .imageOnly: 64 | collectionView_.register(ImageOnlySegmentCollectionViewCell.self, forCellWithReuseIdentifier: CollectionViewController.imageOnlyCellIdentifier) 65 | case .imageOnTop: 66 | collectionView_.register(ImageOnTopSegmentCollectionViewCell.self, forCellWithReuseIdentifier: CollectionViewController.imageOnTopCellIdentifier) 67 | case .imageOnLeft: 68 | collectionView_.register(ImageOnLeftSegmentCollectionViewCell.self, forCellWithReuseIdentifier: CollectionViewController.imageOnLeftCellIdentifier) 69 | } 70 | 71 | let indexPath = collectionView?.indexPathsForSelectedItems?.last 72 | 73 | setNeedsLayout() 74 | 75 | if indexPath != nil { 76 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.05, execute: { 77 | self.collectionView?.selectItem(at: indexPath, animated: true, scrollPosition: .left) 78 | }) 79 | } 80 | } 81 | } 82 | } 83 | } 84 | 85 | override public var tintColor: UIColor! { 86 | didSet { 87 | collectionView?.tintColor = tintColor 88 | reloadSegments() 89 | } 90 | } 91 | 92 | fileprivate var _segmentContentColor:UIColor? 93 | @objc public dynamic var segmentContentColor:UIColor? { 94 | get { return _segmentContentColor } 95 | set { 96 | _segmentContentColor = newValue 97 | reloadSegments() 98 | } 99 | } 100 | 101 | fileprivate var _underlineHeight: CGFloat = 4.0 102 | @objc public dynamic var underlineHeight: CGFloat { 103 | get { return _underlineHeight } 104 | set { 105 | if newValue != _underlineHeight { 106 | _underlineHeight = newValue 107 | reloadSegments() 108 | } 109 | } 110 | } 111 | 112 | fileprivate var _selectedSegmentContentColor:UIColor? 113 | @objc public dynamic var selectedSegmentContentColor:UIColor? { 114 | get { return _selectedSegmentContentColor } 115 | set { 116 | _selectedSegmentContentColor = newValue 117 | reloadSegments() 118 | } 119 | } 120 | 121 | 122 | override public init(frame: CGRect) { 123 | super.init(frame: frame) 124 | configure() 125 | } 126 | 127 | required public init?(coder aDecoder: NSCoder) { 128 | super.init(coder: aDecoder) 129 | configure() 130 | } 131 | 132 | fileprivate var normalAttributes:[NSAttributedString.Key : Any]? 133 | fileprivate var highlightedAttributes:[NSAttributedString.Key : Any]? 134 | fileprivate var selectedAttributes:[NSAttributedString.Key : Any]? 135 | fileprivate var _titleAttributes:[UInt: [NSAttributedString.Key : Any]] = [UInt: [NSAttributedString.Key : Any]]() 136 | @objc public func setTitleTextAttributes(_ attributes: [NSAttributedString.Key : Any]?, for state: UIControl.State) { 137 | _titleAttributes[state.rawValue] = attributes 138 | 139 | normalAttributes = _titleAttributes[UIControl.State.normal.rawValue] 140 | highlightedAttributes = _titleAttributes[UIControl.State.highlighted.rawValue] 141 | selectedAttributes = _titleAttributes[UIControl.State.selected.rawValue] 142 | 143 | for segment in segmentsData { 144 | configureAttributedTitlesForSegment(segment) 145 | 146 | if let title = segment.title { 147 | calculateLongestTextWidth(text: title) 148 | } 149 | } 150 | 151 | flowLayout.invalidateLayout() 152 | reloadSegments() 153 | } 154 | 155 | private func configureAttributedTitlesForSegment(_ segment:SegmentData) { 156 | segment.normalAttributedTitle = nil 157 | segment.highlightedAttributedTitle = nil 158 | segment.selectedAttributedTitle = nil 159 | 160 | if let title = segment.title { 161 | if normalAttributes != nil { 162 | segment.normalAttributedTitle = NSAttributedString(string: title, attributes: normalAttributes!) 163 | } 164 | 165 | if highlightedAttributes != nil { 166 | segment.highlightedAttributedTitle = NSAttributedString(string: title, attributes: highlightedAttributes!) 167 | } else { 168 | if selectedAttributes != nil { 169 | segment.highlightedAttributedTitle = NSAttributedString(string: title, attributes: selectedAttributes!) 170 | } else { 171 | if normalAttributes != nil { 172 | segment.highlightedAttributedTitle = NSAttributedString(string: title, attributes: normalAttributes!) 173 | } 174 | } 175 | } 176 | 177 | if selectedAttributes != nil { 178 | segment.selectedAttributedTitle = NSAttributedString(string: title, attributes: selectedAttributes!) 179 | } else { 180 | if highlightedAttributes != nil { 181 | segment.selectedAttributedTitle = NSAttributedString(string: title, attributes: highlightedAttributes!) 182 | } else { 183 | if normalAttributes != nil { 184 | segment.selectedAttributedTitle = NSAttributedString(string: title, attributes: normalAttributes!) 185 | } 186 | } 187 | } 188 | } 189 | } 190 | 191 | @objc public func titleTextAttributes(for state: UIControl.State) -> [NSAttributedString.Key : Any]? { 192 | return _titleAttributes[state.rawValue] 193 | } 194 | 195 | // MARK: - Managing Segments 196 | 197 | /** 198 | Inserts a segment at a specific position in the receiver and gives it a title as content. 199 | */ 200 | @objc public func insertSegment(withTitle title: String, at index: Int) { 201 | let segment = SegmentData() 202 | segment.title = title 203 | configureAttributedTitlesForSegment(segment) 204 | segmentsData.insert(segment, at: index) 205 | calculateLongestTextWidth(text: title) 206 | reloadSegments() 207 | } 208 | 209 | /** 210 | Inserts a segment at a specified position in the receiver and gives it an image as content. 211 | */ 212 | @objc public func insertSegment(with image: UIImage, at index: Int) { 213 | let segment = SegmentData() 214 | segment.image = image.withRenderingMode(.alwaysTemplate) 215 | segmentsData.insert(segment, at: index) 216 | reloadSegments() 217 | } 218 | 219 | 220 | /** 221 | Inserts a segment at a specific position in the receiver and gives it a title as content and/or image as content. 222 | */ 223 | @objc public func insertSegment(withTitle title: String?, image: UIImage?, at index: Int) { 224 | let segment = SegmentData() 225 | segment.title = title 226 | segment.image = image?.withRenderingMode(.alwaysTemplate) 227 | segmentsData.insert(segment, at: index) 228 | 229 | if let str = title { 230 | calculateLongestTextWidth(text: str) 231 | } 232 | reloadSegments() 233 | } 234 | 235 | /** 236 | Removes segment at a specific position from the receiver. 237 | */ 238 | @objc public func removeSegment(at index: Int){ 239 | segmentsData.remove(at: index) 240 | if(selectedSegmentIndex == index) { 241 | selectedSegmentIndex = selectedSegmentIndex - 1 242 | } else if(selectedSegmentIndex > segmentsData.count) { 243 | selectedSegmentIndex = -1 244 | } 245 | reloadSegments() 246 | } 247 | 248 | /** 249 | Returns the number of segments the receiver has. 250 | */ 251 | @objc public var numberOfSegments: Int { return segmentsData.count } 252 | 253 | /** 254 | Returns the title of the specified segment. 255 | */ 256 | @objc public func titleForSegment(at segment: Int) -> String? { 257 | if segmentsData.count == 0 { 258 | return nil 259 | } 260 | 261 | return safeSegmentData(forIndex: segment).title 262 | } 263 | 264 | 265 | /** 266 | The index number identifying the selected segment (that is, the last segment touched). 267 | 268 | Set this property to -1 to turn off the current selection. 269 | */ 270 | @objc public var selectedSegmentIndex: Int = -1 { 271 | didSet{ 272 | if selectedSegmentIndex < -1 { 273 | selectedSegmentIndex = -1 274 | } else if selectedSegmentIndex > segmentsData.count - 1 { 275 | selectedSegmentIndex = segmentsData.count - 1 276 | } 277 | 278 | if selectedSegmentIndex >= 0 { 279 | var scrollPossition:UICollectionView.ScrollPosition = .bottom 280 | let indexPath = IndexPath(item: selectedSegmentIndex, section: 0) 281 | if let atribs = collectionView?.layoutAttributesForItem(at: indexPath) { 282 | let frame = atribs.frame 283 | if frame.origin.x < collectionView!.contentOffset.x { 284 | scrollPossition = .left 285 | } else if frame.origin.x + frame.size.width > (collectionView!.frame.size.width + collectionView!.contentOffset.x) { 286 | scrollPossition = .right 287 | } 288 | } 289 | 290 | collectionView?.selectItem(at: indexPath, animated: true, scrollPosition: scrollPossition) 291 | } else { 292 | if let indexPath = collectionView?.indexPathsForSelectedItems?.first { 293 | collectionView?.deselectItem(at: indexPath, animated: true) 294 | } 295 | } 296 | 297 | if oldValue != selectedSegmentIndex { 298 | self.sendActions(for: .valueChanged) 299 | } 300 | } 301 | } 302 | 303 | /** 304 | Configure if the selected segment should have underline. Default value is false. 305 | */ 306 | @IBInspectable 307 | @objc public var underlineSelected:Bool = false 308 | 309 | // MARK: - Layout management 310 | 311 | override public func layoutSubviews() { 312 | super.layoutSubviews() 313 | 314 | guard let collectionView_ = collectionView else { 315 | return 316 | } 317 | 318 | collectionView_.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height) 319 | collectionView_.contentOffset = CGPoint(x: 0, y: 0) 320 | collectionView_.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) 321 | 322 | flowLayout.invalidateLayout() 323 | configureSegmentSize() 324 | reloadSegments() 325 | } 326 | 327 | // MARK: - Private 328 | 329 | fileprivate func configure() { 330 | clipsToBounds = true 331 | 332 | flowLayout.scrollDirection = .horizontal 333 | flowLayout.minimumInteritemSpacing = 0 334 | flowLayout.minimumLineSpacing = 0 335 | 336 | collectionView = UICollectionView(frame: frame, collectionViewLayout: flowLayout) 337 | collectionView!.tag = 1 338 | collectionView!.tintColor = tintColor 339 | collectionView!.register(TextOnlySegmentCollectionViewCell.self, forCellWithReuseIdentifier: CollectionViewController.textOnlyCellIdentifier) 340 | collectionViewController = CollectionViewController(segmentedControl: self) 341 | collectionView!.dataSource = collectionViewController 342 | collectionView!.delegate = collectionViewController 343 | collectionView!.backgroundColor = UIColor.clear 344 | collectionView!.showsHorizontalScrollIndicator = false 345 | addSubview(collectionView!) 346 | } 347 | 348 | fileprivate func configureSegmentSize() { 349 | let width:CGFloat 350 | 351 | if fixedSegmentWidth == true { 352 | switch segmentStyle { 353 | case .imageOnLeft: 354 | width = longestTextWidth + BaseSegmentCollectionViewCell.imageSize + BaseSegmentCollectionViewCell.imageToTextMargin * 2 355 | default: 356 | if collectionView!.frame.size.width > longestTextWidth * CGFloat(segmentsData.count) { 357 | width = collectionView!.frame.size.width / CGFloat(segmentsData.count) 358 | } else { 359 | width = longestTextWidth 360 | } 361 | } 362 | 363 | flowLayout.estimatedItemSize = CGSize() 364 | flowLayout.itemSize = CGSize(width: width, height: frame.size.height) 365 | } else { 366 | width = 1.0 367 | flowLayout.itemSize = CGSize(width: width, height: frame.size.height) 368 | flowLayout.estimatedItemSize = CGSize(width: width, height: frame.size.height) 369 | } 370 | } 371 | 372 | fileprivate func calculateLongestTextWidth(text:String) { 373 | let fontAttributes:[NSAttributedString.Key:Any] 374 | if normalAttributes != nil { 375 | fontAttributes = normalAttributes! 376 | } else if highlightedAttributes != nil { 377 | fontAttributes = highlightedAttributes! 378 | } else if selectedAttributes != nil { 379 | fontAttributes = selectedAttributes! 380 | } else { 381 | fontAttributes = [NSAttributedString.Key.font: BaseSegmentCollectionViewCell.defaultFont] 382 | } 383 | 384 | let size = (text as NSString).size(withAttributes: fontAttributes) 385 | let newLongestTextWidth = 2.0 + size.width + BaseSegmentCollectionViewCell.textPadding * 2 386 | if newLongestTextWidth > longestTextWidth { 387 | longestTextWidth = newLongestTextWidth 388 | configureSegmentSize() 389 | } 390 | } 391 | 392 | private func safeSegmentData(forIndex index:Int) -> SegmentData { 393 | let segmentData:SegmentData 394 | 395 | if index <= 0 { 396 | segmentData = segmentsData[0] 397 | } else if index >= segmentsData.count { 398 | segmentData = segmentsData[segmentsData.count - 1] 399 | } else { 400 | segmentData = segmentsData[index] 401 | } 402 | 403 | return segmentData 404 | } 405 | 406 | fileprivate func reloadSegments() { 407 | if let collectionView_ = collectionView { 408 | collectionView_.reloadData() 409 | if selectedSegmentIndex >= 0 { 410 | let indexPath = IndexPath(item: selectedSegmentIndex, section: 0) 411 | collectionView_.selectItem(at: indexPath, animated: true, scrollPosition: .bottom) 412 | } 413 | } 414 | } 415 | 416 | /* 417 | Private internal classes to be used only by this class. 418 | */ 419 | 420 | // MARK: - SegmentData 421 | 422 | final private class SegmentData { 423 | var title:String? 424 | var normalAttributedTitle:NSAttributedString? 425 | var highlightedAttributedTitle:NSAttributedString? 426 | var selectedAttributedTitle:NSAttributedString? 427 | var image:UIImage? 428 | } 429 | 430 | // MARK : - CollectionViewController 431 | 432 | /** 433 | A CollectionViewController is private inner class with main purpose to hide UICollectionView protocol conformances. 434 | */ 435 | final private class CollectionViewController : NSObject, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout{ 436 | static let textOnlyCellIdentifier = "textOnlyCellIdentifier" 437 | static let imageOnlyCellIdentifier = "imageOnlyCellIdentifier" 438 | static let imageOnTopCellIdentifier = "imageOnTopCellIdentifier" 439 | static let imageOnLeftCellIdentifier = "imageOnLeftCellIdentifier" 440 | 441 | private weak var segmentedControl: ScrollableSegmentedControl! 442 | 443 | init(segmentedControl:ScrollableSegmentedControl) { 444 | self.segmentedControl = segmentedControl 445 | } 446 | 447 | // UICollectionViewDataSource 448 | 449 | fileprivate func numberOfSections(in collectionView: UICollectionView) -> Int { 450 | return 1 451 | } 452 | 453 | 454 | fileprivate func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 455 | return segmentedControl.numberOfSegments 456 | } 457 | 458 | fileprivate func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 459 | let segmentCell:BaseSegmentCollectionViewCell 460 | let data = segmentedControl.segmentsData[indexPath.item] 461 | 462 | switch segmentedControl.segmentStyle { 463 | case .textOnly: 464 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CollectionViewController.textOnlyCellIdentifier, for: indexPath) as! TextOnlySegmentCollectionViewCell 465 | cell.titleLabel.text = data.title 466 | segmentCell = cell 467 | case .imageOnly: 468 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CollectionViewController.imageOnlyCellIdentifier, for: indexPath) as! ImageOnlySegmentCollectionViewCell 469 | cell.imageView.image = data.image 470 | segmentCell = cell 471 | case .imageOnTop: 472 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CollectionViewController.imageOnTopCellIdentifier, for: indexPath) as! ImageOnTopSegmentCollectionViewCell 473 | cell.titleLabel.text = data.title 474 | cell.imageView.image = data.image 475 | 476 | segmentCell = cell 477 | case .imageOnLeft: 478 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CollectionViewController.imageOnLeftCellIdentifier, for: indexPath) as! ImageOnLeftSegmentCollectionViewCell 479 | cell.titleLabel.text = data.title 480 | cell.imageView.image = data.image 481 | 482 | segmentCell = cell 483 | } 484 | segmentCell.underlineHeight = segmentedControl.underlineHeight 485 | segmentCell.showUnderline = segmentedControl.underlineSelected 486 | if segmentedControl.underlineSelected { 487 | segmentCell.tintColor = segmentedControl.tintColor 488 | } 489 | 490 | 491 | segmentCell.contentColor = segmentedControl.segmentContentColor 492 | segmentCell.selectedContentColor = segmentedControl.selectedSegmentContentColor 493 | 494 | segmentCell.normalAttributedTitle = data.normalAttributedTitle 495 | segmentCell.highlightedAttributedTitle = data.highlightedAttributedTitle 496 | segmentCell.selectedAttributedTitle = data.selectedAttributedTitle 497 | 498 | return segmentCell 499 | } 500 | 501 | // MARK UICollectionViewDelegate 502 | 503 | fileprivate func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 504 | segmentedControl.selectedSegmentIndex = indexPath.item 505 | } 506 | 507 | fileprivate func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { 508 | var label:UILabel? 509 | if let _cell = cell as? TextOnlySegmentCollectionViewCell { 510 | label = _cell.titleLabel 511 | } else if let _cell = cell as? ImageOnTopSegmentCollectionViewCell { 512 | label = _cell.titleLabel 513 | } else if let _cell = cell as? ImageOnLeftSegmentCollectionViewCell { 514 | label = _cell.titleLabel 515 | } else { 516 | label = nil 517 | } 518 | 519 | if let titleLabel = label { 520 | let data = segmentedControl.segmentsData[indexPath.item] 521 | 522 | if cell.isHighlighted && data.highlightedAttributedTitle != nil { 523 | titleLabel.attributedText = data.highlightedAttributedTitle! 524 | } else if cell.isSelected && data.selectedAttributedTitle != nil { 525 | titleLabel.attributedText = data.selectedAttributedTitle! 526 | } else { 527 | if data.normalAttributedTitle != nil { 528 | titleLabel.attributedText = data.normalAttributedTitle! 529 | } 530 | } 531 | } 532 | } 533 | } 534 | 535 | 536 | // MARK: - SegmentCollectionViewCell 537 | 538 | private class BaseSegmentCollectionViewCell: UICollectionViewCell { 539 | static let textPadding:CGFloat = 8.0 540 | static let imageToTextMargin:CGFloat = 14.0 541 | static let imageSize:CGFloat = 14.0 542 | static let defaultFont = UIFont.systemFont(ofSize: 14) 543 | static let defaultTextColor = UIColor.darkGray 544 | 545 | var underlineView:UIView? 546 | var underlineHeight: CGFloat = 4.0 { 547 | didSet { 548 | if oldValue != underlineHeight { 549 | setNeedsUpdateConstraints() 550 | } 551 | } 552 | } 553 | public var contentColor:UIColor? 554 | public var selectedContentColor:UIColor? 555 | 556 | var normalAttributedTitle:NSAttributedString? 557 | var highlightedAttributedTitle:NSAttributedString? 558 | var selectedAttributedTitle:NSAttributedString? 559 | var variableConstraints = [NSLayoutConstraint]() 560 | 561 | var showUnderline:Bool = false { 562 | didSet { 563 | if oldValue != showUnderline { 564 | if oldValue == false && underlineView != nil { 565 | underlineView?.removeFromSuperview() 566 | } else { 567 | underlineView = UIView() 568 | underlineView!.tag = 999 569 | underlineView!.backgroundColor = tintColor 570 | underlineView!.isHidden = !isSelected 571 | contentView.insertSubview(underlineView!, at: contentView.subviews.count) 572 | } 573 | 574 | configureConstraints() 575 | } 576 | } 577 | } 578 | 579 | override var tintColor: UIColor!{ 580 | didSet{ 581 | underlineView?.backgroundColor = tintColor 582 | } 583 | } 584 | 585 | override init(frame: CGRect) { 586 | super.init(frame: frame) 587 | configure() 588 | } 589 | 590 | required init?(coder aDecoder: NSCoder) { 591 | super.init(coder: aDecoder) 592 | configure() 593 | } 594 | 595 | func configure() { 596 | configureConstraints() 597 | } 598 | 599 | private func configureConstraints() { 600 | if let underline = underlineView { 601 | underline.translatesAutoresizingMaskIntoConstraints = false 602 | underline.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true 603 | underline.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true 604 | underline.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true 605 | } 606 | } 607 | 608 | override func setNeedsUpdateConstraints() { 609 | super.setNeedsUpdateConstraints() 610 | NSLayoutConstraint.deactivate(variableConstraints) 611 | variableConstraints.removeAll() 612 | } 613 | 614 | override var isHighlighted: Bool { 615 | didSet { 616 | underlineView?.isHidden = !isHighlighted && !isSelected 617 | } 618 | } 619 | 620 | override var isSelected: Bool { 621 | didSet { 622 | underlineView?.isHidden = !isSelected 623 | } 624 | } 625 | } 626 | 627 | private class TextOnlySegmentCollectionViewCell: BaseSegmentCollectionViewCell { 628 | let titleLabel = UILabel() 629 | 630 | override var contentColor:UIColor? { 631 | didSet { 632 | titleLabel.textColor = (contentColor == nil) ? BaseSegmentCollectionViewCell.defaultTextColor : contentColor! 633 | } 634 | } 635 | 636 | override var selectedContentColor:UIColor? { 637 | didSet { 638 | titleLabel.highlightedTextColor = (selectedContentColor == nil) ? UIColor.black : selectedContentColor! 639 | } 640 | } 641 | 642 | override var isHighlighted: Bool { 643 | didSet { 644 | if let title = (isHighlighted) ? super.highlightedAttributedTitle : super.normalAttributedTitle { 645 | titleLabel.attributedText = title 646 | } else { 647 | titleLabel.isHighlighted = isHighlighted 648 | } 649 | } 650 | } 651 | 652 | override var isSelected: Bool { 653 | didSet { 654 | if isSelected { 655 | if let title = super.selectedAttributedTitle { 656 | titleLabel.attributedText = title 657 | } else { 658 | titleLabel.textColor = (selectedContentColor == nil) ? UIColor.black : selectedContentColor! 659 | } 660 | } else { 661 | if let title = super.normalAttributedTitle { 662 | titleLabel.attributedText = title 663 | } else { 664 | titleLabel.textColor = (contentColor == nil) ? BaseSegmentCollectionViewCell.defaultTextColor : contentColor! 665 | } 666 | } 667 | } 668 | } 669 | 670 | override func configure(){ 671 | super.configure() 672 | contentView.addSubview(titleLabel) 673 | titleLabel.translatesAutoresizingMaskIntoConstraints = false 674 | titleLabel.textColor = BaseSegmentCollectionViewCell.defaultTextColor 675 | titleLabel.font = BaseSegmentCollectionViewCell.defaultFont 676 | titleLabel.textAlignment = .center 677 | } 678 | 679 | override func updateConstraints() { 680 | super.updateConstraints() 681 | NSLayoutConstraint.deactivate(variableConstraints) 682 | variableConstraints.removeAll() 683 | 684 | variableConstraints.append(titleLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor)) 685 | variableConstraints.append(titleLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor)) 686 | variableConstraints.append(titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: BaseSegmentCollectionViewCell.textPadding)) 687 | variableConstraints.append(titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -BaseSegmentCollectionViewCell.textPadding)) 688 | 689 | if let underline = underlineView { 690 | variableConstraints.append(underline.heightAnchor.constraint(equalToConstant: underlineHeight)) 691 | } 692 | NSLayoutConstraint.activate(variableConstraints) 693 | } 694 | } 695 | 696 | private class ImageOnlySegmentCollectionViewCell: BaseSegmentCollectionViewCell { 697 | let imageView = UIImageView() 698 | 699 | override var contentColor:UIColor? { 700 | didSet { 701 | imageView.tintColor = (contentColor == nil) ? BaseSegmentCollectionViewCell.defaultTextColor : contentColor! 702 | } 703 | } 704 | 705 | override var isHighlighted: Bool { 706 | didSet { 707 | if isHighlighted { 708 | imageView.tintColor = (selectedContentColor == nil) ? BaseSegmentCollectionViewCell.defaultTextColor : selectedContentColor! 709 | } else { 710 | imageView.tintColor = (contentColor == nil) ? BaseSegmentCollectionViewCell.defaultTextColor : contentColor! 711 | } 712 | } 713 | } 714 | 715 | override var isSelected: Bool { 716 | didSet { 717 | if isSelected { 718 | imageView.tintColor = (selectedContentColor == nil) ? BaseSegmentCollectionViewCell.defaultTextColor : selectedContentColor! 719 | } else { 720 | imageView.tintColor = (contentColor == nil) ? BaseSegmentCollectionViewCell.defaultTextColor : contentColor! 721 | } 722 | } 723 | } 724 | 725 | override func configure(){ 726 | super.configure() 727 | 728 | contentView.addSubview(imageView) 729 | imageView.translatesAutoresizingMaskIntoConstraints = false 730 | imageView.tintColor = BaseSegmentCollectionViewCell.defaultTextColor 731 | } 732 | 733 | override func updateConstraints() { 734 | super.updateConstraints() 735 | NSLayoutConstraint.deactivate(variableConstraints) 736 | variableConstraints.removeAll() 737 | 738 | variableConstraints.append(imageView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor)) 739 | variableConstraints.append(imageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor)) 740 | variableConstraints.append(imageView.leadingAnchor.constraint(greaterThanOrEqualTo: contentView.leadingAnchor, constant: BaseSegmentCollectionViewCell.textPadding)) 741 | variableConstraints.append(contentView.trailingAnchor.constraint(greaterThanOrEqualTo: imageView.trailingAnchor, constant: BaseSegmentCollectionViewCell.textPadding)) 742 | 743 | if let underline = underlineView { 744 | variableConstraints.append(underline.heightAnchor.constraint(equalToConstant: underlineHeight)) 745 | } 746 | 747 | NSLayoutConstraint.activate(variableConstraints) 748 | } 749 | } 750 | 751 | private class BaseImageSegmentCollectionViewCell: BaseSegmentCollectionViewCell { 752 | let titleLabel = UILabel() 753 | let imageView = UIImageView() 754 | internal let stackView = UIStackView() 755 | 756 | override var contentColor:UIColor? { 757 | didSet { 758 | titleLabel.textColor = (contentColor == nil) ? BaseSegmentCollectionViewCell.defaultTextColor : contentColor! 759 | imageView.tintColor = (contentColor == nil) ? BaseSegmentCollectionViewCell.defaultTextColor : contentColor! 760 | } 761 | } 762 | 763 | override var selectedContentColor:UIColor? { 764 | didSet { 765 | titleLabel.highlightedTextColor = (selectedContentColor == nil) ? UIColor.black : selectedContentColor! 766 | } 767 | } 768 | 769 | override var isHighlighted: Bool { 770 | didSet { 771 | if let title = (isHighlighted) ? super.highlightedAttributedTitle : super.normalAttributedTitle { 772 | titleLabel.attributedText = title 773 | } else { 774 | titleLabel.isHighlighted = isHighlighted 775 | } 776 | 777 | if isHighlighted { 778 | imageView.tintColor = (selectedContentColor == nil) ? BaseSegmentCollectionViewCell.defaultTextColor : selectedContentColor! 779 | } else { 780 | imageView.tintColor = (contentColor == nil) ? BaseSegmentCollectionViewCell.defaultTextColor : contentColor! 781 | } 782 | } 783 | } 784 | 785 | override var isSelected: Bool { 786 | didSet { 787 | if isSelected { 788 | if let title = super.selectedAttributedTitle { 789 | titleLabel.attributedText = title 790 | } else { 791 | titleLabel.textColor = (selectedContentColor == nil) ? UIColor.black : selectedContentColor! 792 | } 793 | imageView.tintColor = (selectedContentColor == nil) ? BaseSegmentCollectionViewCell.defaultTextColor : selectedContentColor! 794 | } else { 795 | if let title = super.normalAttributedTitle { 796 | titleLabel.attributedText = title 797 | } else { 798 | titleLabel.textColor = (contentColor == nil) ? BaseSegmentCollectionViewCell.defaultTextColor : contentColor! 799 | } 800 | imageView.tintColor = (contentColor == nil) ? BaseSegmentCollectionViewCell.defaultTextColor : contentColor! 801 | } 802 | } 803 | } 804 | 805 | override func configure(){ 806 | super.configure() 807 | titleLabel.font = BaseSegmentCollectionViewCell.defaultFont 808 | imageView.contentMode = .scaleAspectFit 809 | imageView.clipsToBounds = true 810 | 811 | titleLabel.translatesAutoresizingMaskIntoConstraints = false 812 | imageView.translatesAutoresizingMaskIntoConstraints = false 813 | 814 | stackView.distribution = .fill 815 | stackView.spacing = BaseSegmentCollectionViewCell.textPadding 816 | stackView.addArrangedSubview(imageView) 817 | stackView.addArrangedSubview(titleLabel) 818 | 819 | stackView.translatesAutoresizingMaskIntoConstraints = false 820 | contentView.addSubview(stackView) 821 | } 822 | 823 | override func updateConstraints() { 824 | super.updateConstraints() 825 | NSLayoutConstraint.deactivate(variableConstraints) 826 | variableConstraints.removeAll() 827 | 828 | variableConstraints.append(stackView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor)) 829 | variableConstraints.append(stackView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor)) 830 | variableConstraints.append(stackView.leadingAnchor.constraint(greaterThanOrEqualTo: contentView.leadingAnchor, constant: BaseSegmentCollectionViewCell.textPadding)) 831 | variableConstraints.append(contentView.trailingAnchor.constraint(greaterThanOrEqualTo: stackView.trailingAnchor, constant: BaseSegmentCollectionViewCell.textPadding)) 832 | 833 | if let underline = underlineView { 834 | variableConstraints.append(underline.heightAnchor.constraint(equalToConstant: underlineHeight)) 835 | } 836 | 837 | NSLayoutConstraint.activate(variableConstraints) 838 | } 839 | } 840 | 841 | private class ImageOnTopSegmentCollectionViewCell: BaseImageSegmentCollectionViewCell { 842 | override func configure(){ 843 | super.configure() 844 | stackView.axis = .vertical 845 | } 846 | } 847 | 848 | private class ImageOnLeftSegmentCollectionViewCell: BaseImageSegmentCollectionViewCell { 849 | override func configure(){ 850 | super.configure() 851 | var imgFrame = imageView.frame 852 | imgFrame.size = CGSize(width: BaseSegmentCollectionViewCell.imageSize, height: BaseSegmentCollectionViewCell.imageSize) 853 | imageView.frame = imgFrame 854 | imageView.heightAnchor.constraint(equalToConstant: BaseSegmentCollectionViewCell.imageSize).isActive = true 855 | imageView.widthAnchor.constraint(equalToConstant: BaseSegmentCollectionViewCell.imageSize).isActive = true 856 | 857 | stackView.axis = .horizontal 858 | } 859 | } 860 | } 861 | -------------------------------------------------------------------------------- /ScrollableSegmentedControlTests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /ScrollableSegmentedControlTests/ScrollableSegmentedControlTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScrollableSegmentedControlTests.swift 3 | // ScrollableSegmentedControlTests 4 | // 5 | // Created by Goce Petrovski on 10/11/16. 6 | // Copyright © 2016 Pomarium. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ScrollableSegmentedControl 11 | 12 | class ScrollableSegmentedControlTests: XCTestCase { 13 | let segmentedControl = ScrollableSegmentedControl() 14 | 15 | override func setUp() { 16 | super.setUp() 17 | segmentedControl.frame = CGRect(x: 0, y: 0, width: 320, height: 100) 18 | segmentedControl.layoutSubviews() 19 | } 20 | 21 | override func tearDown() { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | super.tearDown() 24 | } 25 | 26 | func testNumberOfSegements() { 27 | segmentedControl.insertSegment(withTitle: "segment 1", image: nil, at: 0) 28 | segmentedControl.insertSegment(withTitle: "segment 2", image: nil, at: 0) 29 | 30 | XCTAssertTrue(segmentedControl.numberOfSegments == 2, "Number of segments should be 2") 31 | 32 | segmentedControl.removeSegment(at: 0) 33 | XCTAssertTrue(segmentedControl.numberOfSegments == 1, "Number of segments should be 1") 34 | } 35 | 36 | func testSegementTitleContent() { 37 | segmentedControl.insertSegment(withTitle: "segment 1", image: nil, at: 0) 38 | segmentedControl.insertSegment(withTitle: "segment 3", image: nil, at: 1) 39 | segmentedControl.insertSegment(withTitle: "segment 4", image: nil, at: 2) 40 | segmentedControl.insertSegment(withTitle: "segment 2", image: nil, at: 1) 41 | 42 | 43 | XCTAssertNotNil(segmentedControl.titleForSegment(at: 2)) 44 | XCTAssert(segmentedControl.titleForSegment(at:2)?.compare("segment 3") == ComparisonResult.orderedSame, "Title should be segment 3") 45 | XCTAssertNotNil(segmentedControl.titleForSegment(at:-1)) 46 | XCTAssert(segmentedControl.titleForSegment(at:-1)?.compare("segment 1") == ComparisonResult.orderedSame, "Title should be segment 1") 47 | XCTAssertNotNil(segmentedControl.titleForSegment(at:99)) 48 | XCTAssert(segmentedControl.titleForSegment(at:99)?.compare("segment 4") == ComparisonResult.orderedSame, "Title should be segment 4") 49 | 50 | segmentedControl.removeSegment(at: 3) 51 | XCTAssert(segmentedControl.titleForSegment(at:99)?.compare("segment 3") == ComparisonResult.orderedSame, "Title should be segment 3") 52 | } 53 | 54 | func testInternalNumberOfSegements(){ 55 | segmentedControl.insertSegment(withTitle: "segment 1", image: nil, at: 0) 56 | segmentedControl.insertSegment(withTitle: "segment 2", image: nil, at: 1) 57 | segmentedControl.insertSegment(withTitle: "segment 3", image: nil, at: 2) 58 | 59 | let collectionView = segmentedControl.subviews[0] as? UICollectionView 60 | XCTAssertNotNil(collectionView) 61 | 62 | XCTAssert(collectionView?.numberOfSections == 1) 63 | XCTAssert(collectionView?.numberOfItems(inSection: 0) == 3) 64 | } 65 | 66 | func testSelectedIndex(){ 67 | segmentedControl.insertSegment(withTitle: "segment 1", image: nil, at: 0) 68 | segmentedControl.insertSegment(withTitle: "segment 2", image: nil, at: 1) 69 | segmentedControl.insertSegment(withTitle: "segment 3", image: nil, at: 2) 70 | 71 | segmentedControl.underlineSelected = true 72 | segmentedControl.selectedSegmentIndex = 1 73 | 74 | XCTAssert(segmentedControl.selectedSegmentIndex == 1) 75 | 76 | let collectionView = segmentedControl.viewWithTag(1) as? UICollectionView 77 | XCTAssertNotNil(collectionView) 78 | let indexPath = collectionView!.indexPathsForSelectedItems?.last 79 | XCTAssert(indexPath?.item == 1) 80 | } 81 | 82 | func testUnderlineIsPresent(){ 83 | segmentedControl.insertSegment(withTitle: "segment 1", image: nil, at: 0) 84 | segmentedControl.insertSegment(withTitle: "segment 2", image: nil, at: 1) 85 | segmentedControl.insertSegment(withTitle: "segment 3", image: nil, at: 2) 86 | 87 | let collectionView = segmentedControl.viewWithTag(1) as? UICollectionView 88 | XCTAssertNotNil(collectionView) 89 | 90 | segmentedControl.underlineSelected = true 91 | segmentedControl.selectedSegmentIndex = 1 92 | 93 | 94 | let indexPath = collectionView!.indexPathsForSelectedItems?.last 95 | XCTAssertNotNil(indexPath) 96 | let cell = collectionView?.dataSource?.collectionView(collectionView!, cellForItemAt: indexPath!) 97 | XCTAssertNotNil(cell) 98 | 99 | let underlineView = cell?.contentView.viewWithTag(999) 100 | XCTAssertNotNil(underlineView) 101 | } 102 | 103 | func testTintColor(){ 104 | segmentedControl.insertSegment(withTitle: "segment 1", image: nil, at: 0) 105 | segmentedControl.insertSegment(withTitle: "segment 2", image: nil, at: 1) 106 | segmentedControl.insertSegment(withTitle: "segment 3", image: nil, at: 2) 107 | 108 | let collectionView = segmentedControl.viewWithTag(1) as? UICollectionView 109 | segmentedControl.underlineSelected = true 110 | segmentedControl.selectedSegmentIndex = 1 111 | 112 | let indexPath = collectionView!.indexPathsForSelectedItems?.last 113 | var cell = collectionView?.dataSource?.collectionView(collectionView!, cellForItemAt: indexPath!) 114 | var underlineView = cell?.contentView.viewWithTag(999) 115 | 116 | let color = UIColor.purple 117 | XCTAssertFalse(underlineView?.backgroundColor == color) 118 | 119 | segmentedControl.tintColor = color 120 | cell = collectionView?.dataSource?.collectionView(collectionView!, cellForItemAt: indexPath!) 121 | underlineView = cell?.contentView.viewWithTag(999) 122 | XCTAssertTrue(underlineView?.backgroundColor == color) 123 | } 124 | 125 | func testUnderlineHeight() { 126 | segmentedControl.insertSegment(withTitle: "segment 1", image: nil, at: 0) 127 | segmentedControl.underlineSelected = true 128 | segmentedControl.segmentStyle = .textOnly 129 | 130 | self.segmentedControl.underlineHeight = 3 131 | segmentedControl.selectedSegmentIndex = 0 132 | 133 | var collectionView = self.segmentedControl.viewWithTag(1) as? UICollectionView 134 | var indexPath = collectionView!.indexPathsForSelectedItems?.last 135 | var cell = collectionView?.dataSource?.collectionView(collectionView!, cellForItemAt: indexPath!) 136 | cell?.updateConstraints() 137 | var underlineView = cell?.contentView.viewWithTag(999) 138 | // The underline has only one constraint at its level, the height 139 | XCTAssertTrue(underlineView?.constraints.first?.constant == 3) 140 | 141 | self.segmentedControl.underlineHeight = 10 142 | 143 | collectionView = self.segmentedControl.viewWithTag(1) as? UICollectionView 144 | indexPath = collectionView!.indexPathsForSelectedItems?.last 145 | cell = collectionView?.dataSource?.collectionView(collectionView!, cellForItemAt: indexPath!) 146 | cell?.updateConstraints() 147 | underlineView = cell?.contentView.viewWithTag(999) 148 | // The underline has only one constraint at its level, the height 149 | XCTAssertTrue(underlineView?.constraints.first?.constant == 10) 150 | 151 | } 152 | 153 | // func testPerformanceExample() { 154 | // // This is an example of a performance test case. 155 | // self.measure { 156 | // // Put the code you want to measure the time of here. 157 | // } 158 | // } 159 | 160 | } 161 | --------------------------------------------------------------------------------