├── .gitignore ├── Gif └── SegmentedControlExample.gif ├── LICENSE.md ├── README.md ├── SegmentedControl.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata ├── xcshareddata │ └── xcschemes │ │ └── SegmentedControl.xcscheme └── xcuserdata │ └── hongxin.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── SegmentedControl.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── SegmentedControl ├── Constants.swift ├── Protocols.swift ├── SCScrollView.swift ├── SegmentedControl.swift ├── Style.swift └── Supporting Files │ └── Info.plist ├── SegmentedControlExample.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── hongxin.xcuserdatad │ └── xcschemes │ ├── SegmentedControlExample.xcscheme │ └── xcschememanagement.plist ├── SegmentedControlExample ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── chat-selected.imageset │ │ ├── Contents.json │ │ └── chat-selected.pdf │ ├── chat.imageset │ │ ├── Contents.json │ │ └── chat.pdf │ ├── me-selected.imageset │ │ ├── Contents.json │ │ └── me-selected.pdf │ ├── me.imageset │ │ ├── Contents.json │ │ └── me.pdf │ ├── notification-selected.imageset │ │ ├── Contents.json │ │ └── notification-selected.pdf │ ├── notification.imageset │ │ ├── Contents.json │ │ └── notification.pdf │ ├── project-selected.imageset │ │ ├── Contents.json │ │ └── project-selected.pdf │ ├── project.imageset │ │ ├── Contents.json │ │ └── project.pdf │ ├── taskSegmentAdditionIcon.imageset │ │ ├── Contents.json │ │ └── taskSegmentAdditionIcon.pdf │ └── taskSegmentAdditionIconSelected.imageset │ │ ├── Contents.json │ │ └── taskSegmentAdditionIconSelected.pdf ├── Base.lproj │ └── Main.storyboard ├── ExampleViewController.swift └── Info.plist └── TBSegmentedControl.podspec /.gitignore: -------------------------------------------------------------------------------- 1 | ## Various settings 2 | *.pbxuser 3 | !default.pbxuser 4 | *.mode1v3 5 | !default.mode1v3 6 | *.mode2v3 7 | !default.mode2v3 8 | *.perspectivev3 9 | !default.perspectivev3 10 | xcuserdata 11 | xcuserdata/ 12 | 13 | # Carthage 14 | # 15 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 16 | 17 | Carthage/Checkouts 18 | Carthage/Build 19 | -------------------------------------------------------------------------------- /Gif/SegmentedControlExample.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teambition/SegmentedControl/58a6f9270e60accf4513176e472e82146458e601/Gif/SegmentedControlExample.gif -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Teambition 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SegmentedControl 2 | SegmentedControl is a highly customizable segmented control for iOS applications. 3 | 4 | ![Example](Gif/SegmentedControlExample.gif "SegmentedControlExample") 5 | 6 | ## How To Get Started 7 | ### Carthage 8 | Specify "SegmentedControl" in your ```Cartfile```: 9 | ```ogdl 10 | github "teambition/SegmentedControl" 11 | ``` 12 | 13 | ### Usage 14 | #### Text 15 | ```swift 16 | let titles: [NSAttributedString] = ... 17 | let selectedTitles: [NSAttributedString] = ... 18 | 19 | // for storyboard 20 | segmentedControl.setTitles(titles, selectedTitles: selectedTitles) 21 | // programmatically 22 | let segmentedControl = SegmentedControl.initWithTitles(titles, selectedTitles: selectedTitles) 23 | 24 | // assign delegate 25 | segmentedControl.delegate = self 26 | 27 | // configure selection box if needed, the default style is 'none' 28 | segmentedControl.selectionBoxStyle = .default 29 | segmentedControl.selectionBoxColor = UIColor(white: 0.62, alpha: 1) 30 | segmentedControl.selectionBoxCornerRadius = 15 // default is 0 31 | segmentedControl.selectionBoxEdgeInsets = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20) // default is UIEdgeInsets.zero 32 | 33 | // configure selection indicator if needed, the default style is 'none' 34 | segmentedControl.selectionIndicatorStyle = .top 35 | segmentedControl.selectionIndicatorColor = UIColor(white: 0.3, alpha: 1) 36 | segmentedControl.selectionIndicatorHeight = 3 // default is 5 37 | segmentedControl.selectionIndicatorEdgeInsets = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20) // default is UIEdgeInsets.zero 38 | ``` 39 | 40 | #### Image 41 | ```swift 42 | let images = ... 43 | let selectedImages = ... 44 | 45 | // for storyboard 46 | segmentedControl.setImages(images, selectedImages: selectedImages) 47 | // programmatically 48 | let segmentedControl = SegmentedControl.initWithImages(images, selectedImages: selectedImages) 49 | 50 | // assign delegate 51 | segmentedControl.delegate = self 52 | 53 | // configure selection box if needed, the default style is 'none' 54 | segmentedControl.selectionBoxStyle = .default 55 | segmentedControl.selectionBoxColor = UIColor.lightGray 56 | segmentedControl.selectionBoxCornerRadius = 15 // default is 0 57 | segmentedControl.selectionBoxEdgeInsets = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20) // default is UIEdgeInsets.zero 58 | 59 | // configure selection indicator if needed, the default style is 'none' 60 | segmentedControl.selectionIndicatorStyle = .bottom 61 | segmentedControl.selectionIndicatorColor = UIColor.darkGray 62 | segmentedControl.selectionIndicatorHeight = 3 // default is 5 63 | segmentedControl.selectionIndicatorEdgeInsets = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20) // default is UIEdgeInsets.zero 64 | ``` 65 | 66 | #### Implement delegate 67 | ```swift 68 | func segmentedControl(_ segmentedControl: SegmentedControl, didSelectIndex selectedIndex: Int) { 69 | // do something 70 | } 71 | 72 | func segmentedControl(_ segmentedControl: SegmentedControl, didLongPressIndex longPressIndex: Int) { 73 | // do something 74 | } 75 | ``` 76 | 77 | ## Minimum Requirement 78 | iOS 8.0 79 | 80 | ## Release Notes 81 | * [Release Notes](https://github.com/teambition/SegmentedControl/releases) 82 | 83 | ## License 84 | SegmentedControl is released under the MIT license. See [LICENSE](https://github.com/teambition/SegmentedControl/blob/master/LICENSE.md) for details. 85 | 86 | ## More Info 87 | Have a question? Please [open an issue](https://github.com/teambition/SegmentedControl/issues/new)! 88 | -------------------------------------------------------------------------------- /SegmentedControl.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D324D58C1DC981D0005BBBF5 /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = D324D58B1DC981D0005BBBF5 /* Protocols.swift */; }; 11 | D3E3B10B1C3288E5003EAA6E /* SegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3E3B10A1C3288E5003EAA6E /* SegmentedControl.swift */; }; 12 | D3E3B10D1C328A80003EAA6E /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3E3B10C1C328A80003EAA6E /* Constants.swift */; }; 13 | D3E3B10F1C3378DC003EAA6E /* SCScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3E3B10E1C3378DC003EAA6E /* SCScrollView.swift */; }; 14 | D3E3B1111C337ECB003EAA6E /* Style.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3E3B1101C337ECB003EAA6E /* Style.swift */; }; 15 | /* End PBXBuildFile section */ 16 | 17 | /* Begin PBXFileReference section */ 18 | D324D58B1DC981D0005BBBF5 /* Protocols.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Protocols.swift; sourceTree = ""; }; 19 | D3E3B0CB1C3282BB003EAA6E /* SegmentedControl.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SegmentedControl.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 20 | D3E3B0F51C3283E8003EAA6E /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 21 | D3E3B10A1C3288E5003EAA6E /* SegmentedControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SegmentedControl.swift; sourceTree = ""; }; 22 | D3E3B10C1C328A80003EAA6E /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; 23 | D3E3B10E1C3378DC003EAA6E /* SCScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SCScrollView.swift; sourceTree = ""; }; 24 | D3E3B1101C337ECB003EAA6E /* Style.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Style.swift; sourceTree = ""; }; 25 | /* End PBXFileReference section */ 26 | 27 | /* Begin PBXFrameworksBuildPhase section */ 28 | D3E3B0C71C3282BB003EAA6E /* Frameworks */ = { 29 | isa = PBXFrameworksBuildPhase; 30 | buildActionMask = 2147483647; 31 | files = ( 32 | ); 33 | runOnlyForDeploymentPostprocessing = 0; 34 | }; 35 | /* End PBXFrameworksBuildPhase section */ 36 | 37 | /* Begin PBXGroup section */ 38 | D3E3B0C11C3282BB003EAA6E = { 39 | isa = PBXGroup; 40 | children = ( 41 | D3E3B0CD1C3282BB003EAA6E /* SegmentedControl */, 42 | D3E3B0CC1C3282BB003EAA6E /* Products */, 43 | ); 44 | sourceTree = ""; 45 | }; 46 | D3E3B0CC1C3282BB003EAA6E /* Products */ = { 47 | isa = PBXGroup; 48 | children = ( 49 | D3E3B0CB1C3282BB003EAA6E /* SegmentedControl.framework */, 50 | ); 51 | name = Products; 52 | sourceTree = ""; 53 | }; 54 | D3E3B0CD1C3282BB003EAA6E /* SegmentedControl */ = { 55 | isa = PBXGroup; 56 | children = ( 57 | D3E3B1101C337ECB003EAA6E /* Style.swift */, 58 | D3E3B10C1C328A80003EAA6E /* Constants.swift */, 59 | D324D58B1DC981D0005BBBF5 /* Protocols.swift */, 60 | D3E3B10A1C3288E5003EAA6E /* SegmentedControl.swift */, 61 | D3E3B10E1C3378DC003EAA6E /* SCScrollView.swift */, 62 | D3E3B0F41C3283B2003EAA6E /* Supporting Files */, 63 | ); 64 | path = SegmentedControl; 65 | sourceTree = ""; 66 | }; 67 | D3E3B0F41C3283B2003EAA6E /* Supporting Files */ = { 68 | isa = PBXGroup; 69 | children = ( 70 | D3E3B0F51C3283E8003EAA6E /* Info.plist */, 71 | ); 72 | path = "Supporting Files"; 73 | sourceTree = ""; 74 | }; 75 | /* End PBXGroup section */ 76 | 77 | /* Begin PBXHeadersBuildPhase section */ 78 | D3E3B0C81C3282BB003EAA6E /* Headers */ = { 79 | isa = PBXHeadersBuildPhase; 80 | buildActionMask = 2147483647; 81 | files = ( 82 | ); 83 | runOnlyForDeploymentPostprocessing = 0; 84 | }; 85 | /* End PBXHeadersBuildPhase section */ 86 | 87 | /* Begin PBXNativeTarget section */ 88 | D3E3B0CA1C3282BB003EAA6E /* SegmentedControl */ = { 89 | isa = PBXNativeTarget; 90 | buildConfigurationList = D3E3B0D31C3282BB003EAA6E /* Build configuration list for PBXNativeTarget "SegmentedControl" */; 91 | buildPhases = ( 92 | D3E3B0C61C3282BB003EAA6E /* Sources */, 93 | D3E3B0C71C3282BB003EAA6E /* Frameworks */, 94 | D3E3B0C81C3282BB003EAA6E /* Headers */, 95 | D3E3B0C91C3282BB003EAA6E /* Resources */, 96 | ); 97 | buildRules = ( 98 | ); 99 | dependencies = ( 100 | ); 101 | name = SegmentedControl; 102 | productName = SegmentedControl; 103 | productReference = D3E3B0CB1C3282BB003EAA6E /* SegmentedControl.framework */; 104 | productType = "com.apple.product-type.framework"; 105 | }; 106 | /* End PBXNativeTarget section */ 107 | 108 | /* Begin PBXProject section */ 109 | D3E3B0C21C3282BB003EAA6E /* Project object */ = { 110 | isa = PBXProject; 111 | attributes = { 112 | LastUpgradeCheck = 1020; 113 | ORGANIZATIONNAME = Teambition; 114 | TargetAttributes = { 115 | D3E3B0CA1C3282BB003EAA6E = { 116 | CreatedOnToolsVersion = 7.2; 117 | LastSwiftMigration = 1020; 118 | }; 119 | }; 120 | }; 121 | buildConfigurationList = D3E3B0C51C3282BB003EAA6E /* Build configuration list for PBXProject "SegmentedControl" */; 122 | compatibilityVersion = "Xcode 3.2"; 123 | developmentRegion = en; 124 | hasScannedForEncodings = 0; 125 | knownRegions = ( 126 | en, 127 | Base, 128 | ); 129 | mainGroup = D3E3B0C11C3282BB003EAA6E; 130 | productRefGroup = D3E3B0CC1C3282BB003EAA6E /* Products */; 131 | projectDirPath = ""; 132 | projectRoot = ""; 133 | targets = ( 134 | D3E3B0CA1C3282BB003EAA6E /* SegmentedControl */, 135 | ); 136 | }; 137 | /* End PBXProject section */ 138 | 139 | /* Begin PBXResourcesBuildPhase section */ 140 | D3E3B0C91C3282BB003EAA6E /* Resources */ = { 141 | isa = PBXResourcesBuildPhase; 142 | buildActionMask = 2147483647; 143 | files = ( 144 | ); 145 | runOnlyForDeploymentPostprocessing = 0; 146 | }; 147 | /* End PBXResourcesBuildPhase section */ 148 | 149 | /* Begin PBXSourcesBuildPhase section */ 150 | D3E3B0C61C3282BB003EAA6E /* Sources */ = { 151 | isa = PBXSourcesBuildPhase; 152 | buildActionMask = 2147483647; 153 | files = ( 154 | D324D58C1DC981D0005BBBF5 /* Protocols.swift in Sources */, 155 | D3E3B10D1C328A80003EAA6E /* Constants.swift in Sources */, 156 | D3E3B10B1C3288E5003EAA6E /* SegmentedControl.swift in Sources */, 157 | D3E3B10F1C3378DC003EAA6E /* SCScrollView.swift in Sources */, 158 | D3E3B1111C337ECB003EAA6E /* Style.swift in Sources */, 159 | ); 160 | runOnlyForDeploymentPostprocessing = 0; 161 | }; 162 | /* End PBXSourcesBuildPhase section */ 163 | 164 | /* Begin XCBuildConfiguration section */ 165 | D3E3B0D11C3282BB003EAA6E /* Debug */ = { 166 | isa = XCBuildConfiguration; 167 | buildSettings = { 168 | ALWAYS_SEARCH_USER_PATHS = NO; 169 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 170 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 171 | CLANG_CXX_LIBRARY = "libc++"; 172 | CLANG_ENABLE_MODULES = YES; 173 | CLANG_ENABLE_OBJC_ARC = YES; 174 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 175 | CLANG_WARN_BOOL_CONVERSION = YES; 176 | CLANG_WARN_COMMA = YES; 177 | CLANG_WARN_CONSTANT_CONVERSION = YES; 178 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 179 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 180 | CLANG_WARN_EMPTY_BODY = YES; 181 | CLANG_WARN_ENUM_CONVERSION = YES; 182 | CLANG_WARN_INFINITE_RECURSION = YES; 183 | CLANG_WARN_INT_CONVERSION = YES; 184 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 185 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 186 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 187 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 188 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 189 | CLANG_WARN_STRICT_PROTOTYPES = YES; 190 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 191 | CLANG_WARN_UNREACHABLE_CODE = YES; 192 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 193 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 194 | COPY_PHASE_STRIP = NO; 195 | CURRENT_PROJECT_VERSION = 1; 196 | DEBUG_INFORMATION_FORMAT = dwarf; 197 | ENABLE_STRICT_OBJC_MSGSEND = YES; 198 | ENABLE_TESTABILITY = YES; 199 | GCC_C_LANGUAGE_STANDARD = gnu99; 200 | GCC_DYNAMIC_NO_PIC = NO; 201 | GCC_NO_COMMON_BLOCKS = YES; 202 | GCC_OPTIMIZATION_LEVEL = 0; 203 | GCC_PREPROCESSOR_DEFINITIONS = ( 204 | "DEBUG=1", 205 | "$(inherited)", 206 | ); 207 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 208 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 209 | GCC_WARN_UNDECLARED_SELECTOR = YES; 210 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 211 | GCC_WARN_UNUSED_FUNCTION = YES; 212 | GCC_WARN_UNUSED_VARIABLE = YES; 213 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 214 | MTL_ENABLE_DEBUG_INFO = YES; 215 | ONLY_ACTIVE_ARCH = YES; 216 | SDKROOT = iphoneos; 217 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 218 | TARGETED_DEVICE_FAMILY = "1,2"; 219 | VERSIONING_SYSTEM = "apple-generic"; 220 | VERSION_INFO_PREFIX = ""; 221 | }; 222 | name = Debug; 223 | }; 224 | D3E3B0D21C3282BB003EAA6E /* Release */ = { 225 | isa = XCBuildConfiguration; 226 | buildSettings = { 227 | ALWAYS_SEARCH_USER_PATHS = NO; 228 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 229 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 230 | CLANG_CXX_LIBRARY = "libc++"; 231 | CLANG_ENABLE_MODULES = YES; 232 | CLANG_ENABLE_OBJC_ARC = YES; 233 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 234 | CLANG_WARN_BOOL_CONVERSION = YES; 235 | CLANG_WARN_COMMA = YES; 236 | CLANG_WARN_CONSTANT_CONVERSION = YES; 237 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 238 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 239 | CLANG_WARN_EMPTY_BODY = YES; 240 | CLANG_WARN_ENUM_CONVERSION = YES; 241 | CLANG_WARN_INFINITE_RECURSION = YES; 242 | CLANG_WARN_INT_CONVERSION = YES; 243 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 244 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 245 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 246 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 247 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 248 | CLANG_WARN_STRICT_PROTOTYPES = YES; 249 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 250 | CLANG_WARN_UNREACHABLE_CODE = YES; 251 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 252 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 253 | COPY_PHASE_STRIP = NO; 254 | CURRENT_PROJECT_VERSION = 1; 255 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 256 | ENABLE_NS_ASSERTIONS = NO; 257 | ENABLE_STRICT_OBJC_MSGSEND = YES; 258 | GCC_C_LANGUAGE_STANDARD = gnu99; 259 | GCC_NO_COMMON_BLOCKS = YES; 260 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 261 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 262 | GCC_WARN_UNDECLARED_SELECTOR = YES; 263 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 264 | GCC_WARN_UNUSED_FUNCTION = YES; 265 | GCC_WARN_UNUSED_VARIABLE = YES; 266 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 267 | MTL_ENABLE_DEBUG_INFO = NO; 268 | SDKROOT = iphoneos; 269 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 270 | TARGETED_DEVICE_FAMILY = "1,2"; 271 | VALIDATE_PRODUCT = YES; 272 | VERSIONING_SYSTEM = "apple-generic"; 273 | VERSION_INFO_PREFIX = ""; 274 | }; 275 | name = Release; 276 | }; 277 | D3E3B0D41C3282BB003EAA6E /* Debug */ = { 278 | isa = XCBuildConfiguration; 279 | buildSettings = { 280 | CLANG_ENABLE_MODULES = YES; 281 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 282 | DEFINES_MODULE = YES; 283 | DYLIB_COMPATIBILITY_VERSION = 1; 284 | DYLIB_CURRENT_VERSION = 1; 285 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 286 | INFOPLIST_FILE = "$(SRCROOT)/SegmentedControl/Supporting Files/Info.plist"; 287 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 288 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 289 | PRODUCT_BUNDLE_IDENTIFIER = Teambition.SegmentedControl; 290 | PRODUCT_NAME = "$(TARGET_NAME)"; 291 | SKIP_INSTALL = YES; 292 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 293 | SWIFT_VERSION = 5.0; 294 | }; 295 | name = Debug; 296 | }; 297 | D3E3B0D51C3282BB003EAA6E /* Release */ = { 298 | isa = XCBuildConfiguration; 299 | buildSettings = { 300 | CLANG_ENABLE_MODULES = YES; 301 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 302 | DEFINES_MODULE = YES; 303 | DYLIB_COMPATIBILITY_VERSION = 1; 304 | DYLIB_CURRENT_VERSION = 1; 305 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 306 | INFOPLIST_FILE = "$(SRCROOT)/SegmentedControl/Supporting Files/Info.plist"; 307 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 308 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 309 | PRODUCT_BUNDLE_IDENTIFIER = Teambition.SegmentedControl; 310 | PRODUCT_NAME = "$(TARGET_NAME)"; 311 | SKIP_INSTALL = YES; 312 | SWIFT_VERSION = 5.0; 313 | }; 314 | name = Release; 315 | }; 316 | /* End XCBuildConfiguration section */ 317 | 318 | /* Begin XCConfigurationList section */ 319 | D3E3B0C51C3282BB003EAA6E /* Build configuration list for PBXProject "SegmentedControl" */ = { 320 | isa = XCConfigurationList; 321 | buildConfigurations = ( 322 | D3E3B0D11C3282BB003EAA6E /* Debug */, 323 | D3E3B0D21C3282BB003EAA6E /* Release */, 324 | ); 325 | defaultConfigurationIsVisible = 0; 326 | defaultConfigurationName = Release; 327 | }; 328 | D3E3B0D31C3282BB003EAA6E /* Build configuration list for PBXNativeTarget "SegmentedControl" */ = { 329 | isa = XCConfigurationList; 330 | buildConfigurations = ( 331 | D3E3B0D41C3282BB003EAA6E /* Debug */, 332 | D3E3B0D51C3282BB003EAA6E /* Release */, 333 | ); 334 | defaultConfigurationIsVisible = 0; 335 | defaultConfigurationName = Release; 336 | }; 337 | /* End XCConfigurationList section */ 338 | }; 339 | rootObject = D3E3B0C21C3282BB003EAA6E /* Project object */; 340 | } 341 | -------------------------------------------------------------------------------- /SegmentedControl.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SegmentedControl.xcodeproj/xcshareddata/xcschemes/SegmentedControl.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /SegmentedControl.xcodeproj/xcuserdata/hongxin.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SegmentedControl.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | D3E3B0CA1C3282BB003EAA6E 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /SegmentedControl.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /SegmentedControl.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SegmentedControl/Constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // SegmentedControl 4 | // 5 | // Created by Xin Hong on 15/12/29. 6 | // Copyright © 2015年 Teambition. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | internal struct SelectionIndicator { 12 | static let defaultHeight: CGFloat = 5 13 | } 14 | 15 | internal struct Constant { 16 | static let defaultSelectionHorizontalPadding: CGFloat = 15 17 | } 18 | -------------------------------------------------------------------------------- /SegmentedControl/Protocols.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Protocols.swift 3 | // SegmentedControl 4 | // 5 | // Created by Xin Hong on 2016/11/2. 6 | // Copyright © 2016年 Teambition. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public protocol SegmentedControlDelegate: class { 12 | func segmentedControl(_ segmentedControl: SegmentedControl, didSelectIndex selectedIndex: Int) 13 | func segmentedControl(_ segmentedControl: SegmentedControl, didLongPressIndex longPressIndex: Int) 14 | } 15 | 16 | public extension SegmentedControlDelegate { 17 | func segmentedControl(_ segmentedControl: SegmentedControl, didSelectIndex selectedIndex: Int) { 18 | 19 | } 20 | 21 | func segmentedControl(_ segmentedControl: SegmentedControl, didLongPressIndex longPressIndex: Int) { 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SegmentedControl/SCScrollView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SCScrollView.swift 3 | // SegmentedControl 4 | // 5 | // Created by Xin Hong on 15/12/30. 6 | // Copyright © 2015年 Teambition. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | internal class SCScrollView: UIScrollView { 12 | override func touchesBegan(_ touches: Set, with event: UIEvent?) { 13 | if !isDragging { 14 | next?.touchesBegan(touches, with: event) 15 | } else { 16 | super.touchesBegan(touches, with: event) 17 | } 18 | } 19 | 20 | override func touchesMoved(_ touches: Set, with event: UIEvent?) { 21 | if !isDragging { 22 | next?.touchesMoved(touches, with: event) 23 | } else { 24 | super.touchesMoved(touches, with: event) 25 | } 26 | } 27 | 28 | override func touchesEnded(_ touches: Set, with event: UIEvent?) { 29 | if !isDragging { 30 | next?.touchesEnded(touches, with: event) 31 | } else { 32 | super.touchesEnded(touches, with: event) 33 | } 34 | } 35 | } 36 | 37 | internal extension SCScrollView { 38 | var parentViewController: UIViewController? { 39 | var parentResponder: UIResponder? = self 40 | while parentResponder != nil { 41 | parentResponder = parentResponder!.next 42 | if let viewController = parentResponder as? UIViewController { 43 | return viewController 44 | } 45 | } 46 | return nil 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /SegmentedControl/SegmentedControl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SegmentedControl.swift 3 | // SegmentedControl 4 | // 5 | // Created by Xin Hong on 15/12/29. 6 | // Copyright © 2015年 Teambition. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class SegmentedControl: UIControl { 12 | open weak var delegate: SegmentedControlDelegate? 13 | 14 | open var layoutPolicy: SegmentedControlLayoutPolicy = .fixed 15 | 16 | open fileprivate(set) var selectedIndex = 0 { 17 | didSet { 18 | if selectedIndex != oldValue { 19 | updateTitleSizes() 20 | } 21 | setNeedsDisplay() 22 | } 23 | } 24 | 25 | /// Only available in dynamic layout policy 26 | open var segmentSpacing: CGFloat = 0 27 | /// Only available in dynamic layout policy 28 | open var selectionBoxHeight: CGFloat = 0 29 | /// Only available in dynamic layout policy 30 | open var selectionHorizontalPadding: CGFloat = Constant.defaultSelectionHorizontalPadding 31 | /// Only available in dynamic layout policy, only the left and right inset works, the top and bottom inset will be ignored. 32 | open var contentInset = UIEdgeInsets.zero 33 | 34 | /// Only available in fixed layout policy 35 | open var segmentWidth: CGFloat? 36 | /// Only available in fixed layout policy 37 | open var minimumSegmentWidth: CGFloat? 38 | /// Only available in fixed layout policy 39 | open var maximumSegmentWidth: CGFloat? 40 | 41 | open var isAnimationEnabled = true 42 | open var isUserDragEnabled = true 43 | open fileprivate(set) var style: SegmentedControlStyle = .text 44 | 45 | open var selectionBoxStyle: SegmentedControlSelectionBoxStyle = .none 46 | open var selectionBoxColor = UIColor.blue 47 | open var selectionBoxCornerRadius: CGFloat = 0 48 | /// Only available in fixed layout policy 49 | open var selectionBoxEdgeInsets = UIEdgeInsets.zero 50 | 51 | /// Only available in selectionBoxStyle == .default 52 | open var shouldShowAllBox: Bool = false 53 | open var unselectedBoxColor = UIColor(red: 38.0/255.0, green: 38.0/255.0, blue: 38.0/255.0, alpha: 0.04) 54 | private var allBoxLayers: [CALayer] = [] 55 | 56 | open var selectionIndicatorStyle: SegmentedControlSelectionIndicatorStyle = .none 57 | open var selectionIndicatorColor = UIColor.black 58 | open var selectionIndicatorHeight = SelectionIndicator.defaultHeight 59 | open var selectionIndicatorEdgeInsets = UIEdgeInsets.zero 60 | open var selectionIndicatorCornerRadius: CGFloat = 0 61 | open var titleAttachedIconPositionOffset: (x: CGFloat, y: CGFloat) = (0, 0) 62 | 63 | 64 | /// Use center alignment when not fill the width 65 | open var horizontallyCenterIfPossible: Bool = true 66 | 67 | open fileprivate(set) var titles = [NSAttributedString]() { 68 | didSet { 69 | updateTitleSizes() 70 | } 71 | } 72 | 73 | open fileprivate(set) var selectedTitles: [NSAttributedString]? 74 | open fileprivate(set) var images = [UIImage]() 75 | open fileprivate(set) var selectedImages: [UIImage]? 76 | open fileprivate(set) var titleAttachedIcons: [UIImage]? 77 | open fileprivate(set) var selectedTitleAttachedIcons: [UIImage]? 78 | 79 | fileprivate var titleSizes = [CGSize]() 80 | 81 | private func updateTitleSizes() { 82 | titleSizes = titles.enumerated().map({ (offset, title) in 83 | if offset == selectedIndex, 84 | let selected = selectedTitles, 85 | 0..= 0.5, "MinimumPressDuration of LongPressGestureRecognizer must be no less than 0.5") 111 | if let longPressGesture = longPressGesture { 112 | longPressGesture.minimumPressDuration = longPressMinimumPressDuration 113 | } 114 | } 115 | } 116 | open fileprivate(set) var isLongPressActivated = false 117 | open var scrollContentInset: UIEdgeInsets { 118 | return scrollView.contentInset 119 | } 120 | open var scrollContentSize: CGSize { 121 | return scrollView.contentSize 122 | } 123 | open var scrollContentOffset: CGPoint { 124 | return scrollView.contentOffset 125 | } 126 | 127 | fileprivate lazy var scrollView: SCScrollView = { 128 | let scrollView = SCScrollView() 129 | scrollView.scrollsToTop = false 130 | scrollView.showsHorizontalScrollIndicator = false 131 | scrollView.showsVerticalScrollIndicator = false 132 | return scrollView 133 | }() 134 | fileprivate lazy var selectionBoxLayer = CALayer() 135 | fileprivate lazy var selectionIndicatorLayer = CALayer() 136 | fileprivate var longPressGesture: UILongPressGestureRecognizer? 137 | 138 | // MARK: - Public functions 139 | open class func initWithTitles(_ titles: [NSAttributedString], selectedTitles: [NSAttributedString]?) -> SegmentedControl { 140 | let segmentedControl = SegmentedControl(frame: CGRect.zero) 141 | segmentedControl.style = .text 142 | segmentedControl.titles = titles 143 | segmentedControl.selectedTitles = selectedTitles 144 | return segmentedControl 145 | } 146 | 147 | open class func initWithImages(_ images: [UIImage], selectedImages: [UIImage]?) -> SegmentedControl { 148 | let segmentedControl = SegmentedControl(frame: CGRect.zero) 149 | segmentedControl.style = .image 150 | segmentedControl.images = images 151 | segmentedControl.selectedImages = selectedImages 152 | return segmentedControl 153 | } 154 | 155 | open func setTitles(_ titles: [NSAttributedString], selectedTitles: [NSAttributedString]?) { 156 | style = .text 157 | self.titles = titles 158 | self.selectedTitles = selectedTitles 159 | } 160 | 161 | open func setImages(_ images: [UIImage], selectedImages: [UIImage]?) { 162 | style = .image 163 | self.images = images 164 | self.selectedImages = selectedImages 165 | } 166 | 167 | open func setTitleAttachedIcons(_ titleAttachedIcons: [UIImage]?, selectedTitleAttachedIcons: [UIImage]?) { 168 | self.titleAttachedIcons = titleAttachedIcons 169 | self.selectedTitleAttachedIcons = selectedTitleAttachedIcons 170 | } 171 | 172 | open func setSelected(at index: Int, animated: Bool) { 173 | if !(0.. CGRect? { 203 | guard 0.., with event: UIEvent?) { 301 | guard !isLongPressActivated else { 302 | return 303 | } 304 | guard let touch = touches.first else { 305 | return 306 | } 307 | 308 | let touchLocation = touch.location(in: self) 309 | guard let touchIndex = evaluateTouchIndex(fromTouchLocation: touchLocation) else { 310 | return 311 | } 312 | 313 | if 0.. Int? { 369 | func startX(_ index: Int) -> CGFloat { 370 | return contentInset.left + totalSegmentsWidths(before: index).reduce(0, +) + segmentSpacing * CGFloat(index) 371 | } 372 | func endX(_ index: Int) -> CGFloat { 373 | return startX(index) + singleSegmentWidth(at: index) 374 | } 375 | 376 | guard bounds.contains(touchLocation) else { 377 | return nil 378 | } 379 | 380 | switch layoutPolicy { 381 | case .fixed: 382 | if singleSegmentWidth() == 0 { 383 | return nil 384 | } 385 | let touchIndex = Int((touchLocation.x + scrollView.contentOffset.x) / singleSegmentWidth()) 386 | return touchIndex 387 | case .dynamic: 388 | let touchX = touchLocation.x + scrollView.contentOffset.x 389 | var maxIndex = segmentsCount() - 1 390 | var minIndex = 0 391 | 392 | if startX(minIndex)...endX(minIndex) ~= touchX { 393 | return minIndex 394 | } else if startX(maxIndex)...endX(maxIndex) ~= touchX { 395 | return maxIndex 396 | } else { 397 | while (maxIndex - minIndex) / 2 > 0 { 398 | let midIndex = minIndex + (maxIndex - minIndex) / 2 399 | 400 | if startX(minIndex)...endX(minIndex) ~= touchX { 401 | return minIndex 402 | } else if startX(maxIndex)...endX(maxIndex) ~= touchX { 403 | return maxIndex 404 | } else if startX(midIndex)...endX(midIndex) ~= touchX { 405 | return midIndex 406 | } else if touchX < startX(midIndex) { 407 | maxIndex = midIndex 408 | } else { 409 | minIndex = midIndex 410 | } 411 | } 412 | } 413 | return nil 414 | } 415 | } 416 | } 417 | 418 | extension SegmentedControl: UIGestureRecognizerDelegate { 419 | // MARK: - UIGestureRecognizerDelegate 420 | open override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { 421 | if gestureRecognizer == longPressGesture { 422 | if let longPressIndex = locationIndex(for: gestureRecognizer) { 423 | return isUnselectedSegmentsLongPressEnabled ? true : longPressIndex == selectedIndex 424 | } 425 | } 426 | return false 427 | } 428 | 429 | @objc func segmentedControlLongPressed(_ gesture: UIGestureRecognizer) { 430 | switch gesture.state { 431 | case .possible: 432 | print("LongPressGesture Possible!") 433 | break 434 | case .began: 435 | print("LongPressGesture Began!") 436 | isLongPressActivated = true 437 | longPressDidBegin(gesture) 438 | break 439 | case .changed: 440 | print("LongPressGesture Changed!") 441 | break 442 | case .ended: 443 | print("LongPressGesture Ended!") 444 | isLongPressActivated = false 445 | break 446 | case .cancelled: 447 | print("LongPressGesture Cancelled!") 448 | isLongPressActivated = false 449 | break 450 | case .failed: 451 | print("LongPressGesture Failed!") 452 | isLongPressActivated = false 453 | break 454 | @unknown default: 455 | print("LongPressGesture Unknown Default!") 456 | isLongPressActivated = false 457 | break 458 | } 459 | } 460 | 461 | fileprivate func locationIndex(for gesture: UIGestureRecognizer) -> Int? { 462 | let longPressLocation = gesture.location(in: self) 463 | return evaluateTouchIndex(fromTouchLocation: longPressLocation) 464 | } 465 | 466 | fileprivate func longPressDidBegin(_ gesture: UIGestureRecognizer) { 467 | guard let longPressIndex = locationIndex(for: gesture) else { 468 | return 469 | } 470 | 471 | if longPressIndex != selectedIndex && !isUnselectedSegmentsLongPressEnabled { 472 | return 473 | } 474 | if 0.. CGSize { 644 | let size = attributedString.size() 645 | return CGRect(origin: CGPoint.zero, size: size).integral.size 646 | } 647 | 648 | fileprivate func selectedImage(at index: Int) -> UIImage? { 649 | if let selectedImages = selectedImages, 0.. NSAttributedString? { 656 | if let selectedTitles = selectedTitles, 0.. UIImage? { 663 | if let titleAttachedIcons = titleAttachedIcons, 0.. UIImage? { 670 | if let selectedTitleAttachedIcons = selectedTitleAttachedIcons, 0.. Int { 677 | switch style { 678 | case .text: 679 | return titles.count 680 | case .image: 681 | return images.count 682 | } 683 | } 684 | 685 | fileprivate func frameForSelectionBox(with index: Int) -> CGRect { 686 | if selectionBoxStyle == .none { 687 | return CGRect.zero 688 | } 689 | 690 | let xPosition: CGFloat = { 691 | switch layoutPolicy { 692 | case .fixed: 693 | return singleSegmentWidth() * CGFloat(index) 694 | case .dynamic: 695 | let frontWidths = totalSegmentsWidths(before: index) 696 | return contentInset.left + frontWidths.reduce(0, +) + segmentSpacing * CGFloat(frontWidths.count) 697 | } 698 | }() 699 | 700 | let boxRect: CGRect = { 701 | switch layoutPolicy { 702 | case .fixed: 703 | let fullRect = CGRect(x: xPosition, y: 0, width: singleSegmentWidth(), height: frame.height) 704 | return CGRect(x: fullRect.origin.x + selectionBoxEdgeInsets.left, 705 | y: fullRect.origin.y + selectionBoxEdgeInsets.top, 706 | width: fullRect.width - (selectionBoxEdgeInsets.left + selectionBoxEdgeInsets.right), 707 | height: fullRect.height - (selectionBoxEdgeInsets.top + selectionBoxEdgeInsets.bottom)) 708 | case .dynamic: 709 | return CGRect(x: xPosition, 710 | y: (frame.height - selectionBoxHeight) / 2, 711 | width: singleSegmentWidth(at: index), 712 | height: selectionBoxHeight) 713 | } 714 | }() 715 | 716 | return boxRect 717 | } 718 | 719 | fileprivate func frameForSelectionIndicator() -> CGRect { 720 | if selectionIndicatorStyle == .none { 721 | return CGRect.zero 722 | } 723 | 724 | let xPosition: CGFloat = { 725 | switch layoutPolicy { 726 | case .fixed: 727 | return singleSegmentWidth() * CGFloat(selectedIndex) 728 | case .dynamic: 729 | let frontWidths = totalSegmentsWidths(before: selectedIndex) 730 | return contentInset.left + frontWidths.reduce(0, +) + segmentSpacing * CGFloat(frontWidths.count) 731 | } 732 | }() 733 | 734 | let yPosition: CGFloat = { 735 | switch selectionIndicatorStyle { 736 | case .bottom: 737 | return frame.height - selectionIndicatorHeight 738 | case .top: 739 | return 0 740 | default: 741 | return 0 742 | } 743 | }() 744 | 745 | let indicatorRect: CGRect = { 746 | switch layoutPolicy { 747 | case .fixed: 748 | let fullRect = CGRect(x: xPosition, y: yPosition, width: singleSegmentWidth(), height: selectionIndicatorHeight) 749 | return CGRect(x: fullRect.origin.x + selectionIndicatorEdgeInsets.left, 750 | y: fullRect.origin.y + selectionIndicatorEdgeInsets.top, 751 | width: fullRect.width - (selectionIndicatorEdgeInsets.left + selectionIndicatorEdgeInsets.right), 752 | height: fullRect.height - (selectionIndicatorEdgeInsets.top + selectionIndicatorEdgeInsets.bottom)) 753 | case .dynamic: 754 | return CGRect( 755 | x: xPosition, 756 | y: yPosition, 757 | width: singleSegmentWidth(at: selectedIndex), 758 | height: selectionIndicatorHeight 759 | ).inset(by: selectionIndicatorEdgeInsets) 760 | } 761 | }() 762 | return indicatorRect 763 | } 764 | 765 | fileprivate func rectForSelectedIndex() -> CGRect { 766 | switch layoutPolicy { 767 | case .fixed: 768 | return CGRect(x: singleSegmentWidth() * CGFloat(selectedIndex), y: 0, width: singleSegmentWidth(), height: frame.height) 769 | case .dynamic: 770 | let frontWidths = totalSegmentsWidths(before: selectedIndex) 771 | let xPosition = contentInset.left + frontWidths.reduce(0, +) + segmentSpacing * CGFloat(frontWidths.count) 772 | return CGRect(x: xPosition, y: 0, width: singleSegmentWidth(at: selectedIndex), height: frame.height) 773 | } 774 | } 775 | 776 | fileprivate func singleSegmentWidth(at index: Int? = nil) -> CGFloat { 777 | func defaultSegmentWidth() -> CGFloat { 778 | if segmentsCount() == 0 { 779 | return 0 780 | } 781 | var segmentWidth = frame.width / CGFloat(segmentsCount()) 782 | if let minimumSegmentWidth = minimumSegmentWidth { 783 | if segmentWidth < minimumSegmentWidth { 784 | segmentWidth = minimumSegmentWidth 785 | } 786 | } 787 | if let maximumSegmentWidth = maximumSegmentWidth { 788 | if segmentWidth > maximumSegmentWidth { 789 | segmentWidth = maximumSegmentWidth 790 | } 791 | } 792 | return segmentWidth 793 | } 794 | 795 | switch layoutPolicy { 796 | case .fixed: 797 | if let segmentWidth = segmentWidth { 798 | return segmentWidth 799 | } 800 | return defaultSegmentWidth() 801 | case .dynamic: 802 | guard let index = index, 0.. CGFloat { 814 | switch layoutPolicy { 815 | case .fixed: 816 | return CGFloat(segmentsCount()) * singleSegmentWidth() 817 | case .dynamic: 818 | let segmentsWidths = titleSizes.enumerated().map { singleSegmentWidth(at: $0.offset) } 819 | return segmentsWidths.reduce(0, +) + segmentSpacing * CGFloat(segmentsWidths.count - 1) 820 | } 821 | } 822 | 823 | fileprivate func totalSegmentsWidths(before index: Int) -> [CGFloat] { 824 | switch layoutPolicy { 825 | case .fixed: 826 | return Array(repeating: singleSegmentWidth(), count: index) 827 | case .dynamic: 828 | return titleSizes[0.. 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 | 0.1.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /SegmentedControlExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4AA16E482463B4D400547F70 /* SegmentedControl.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4AA16E472463B4D400547F70 /* SegmentedControl.framework */; }; 11 | 4AA16E492463B4D500547F70 /* SegmentedControl.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4AA16E472463B4D400547F70 /* SegmentedControl.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 12 | D3E3B0E31C328316003EAA6E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3E3B0E21C328316003EAA6E /* AppDelegate.swift */; }; 13 | D3E3B0E51C328316003EAA6E /* ExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3E3B0E41C328316003EAA6E /* ExampleViewController.swift */; }; 14 | D3E3B0E81C328316003EAA6E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D3E3B0E61C328316003EAA6E /* Main.storyboard */; }; 15 | D3E3B0EA1C328316003EAA6E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D3E3B0E91C328316003EAA6E /* Assets.xcassets */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXCopyFilesBuildPhase section */ 19 | 4AA16E4A2463B4D500547F70 /* Embed Frameworks */ = { 20 | isa = PBXCopyFilesBuildPhase; 21 | buildActionMask = 2147483647; 22 | dstPath = ""; 23 | dstSubfolderSpec = 10; 24 | files = ( 25 | 4AA16E492463B4D500547F70 /* SegmentedControl.framework in Embed Frameworks */, 26 | ); 27 | name = "Embed Frameworks"; 28 | runOnlyForDeploymentPostprocessing = 0; 29 | }; 30 | /* End PBXCopyFilesBuildPhase section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 4AA16E432463B4C100547F70 /* SegmentedControl.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SegmentedControl.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | 4AA16E472463B4D400547F70 /* SegmentedControl.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SegmentedControl.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | D3E3B0DF1C328316003EAA6E /* SegmentedControlExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SegmentedControlExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | D3E3B0E21C328316003EAA6E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 37 | D3E3B0E41C328316003EAA6E /* ExampleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleViewController.swift; sourceTree = ""; }; 38 | D3E3B0E71C328316003EAA6E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 39 | D3E3B0E91C328316003EAA6E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 40 | D3E3B0EE1C328316003EAA6E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 41 | /* End PBXFileReference section */ 42 | 43 | /* Begin PBXFrameworksBuildPhase section */ 44 | D3E3B0DC1C328316003EAA6E /* Frameworks */ = { 45 | isa = PBXFrameworksBuildPhase; 46 | buildActionMask = 2147483647; 47 | files = ( 48 | 4AA16E482463B4D400547F70 /* SegmentedControl.framework in Frameworks */, 49 | ); 50 | runOnlyForDeploymentPostprocessing = 0; 51 | }; 52 | /* End PBXFrameworksBuildPhase section */ 53 | 54 | /* Begin PBXGroup section */ 55 | 4AA16E422463B4C100547F70 /* Frameworks */ = { 56 | isa = PBXGroup; 57 | children = ( 58 | 4AA16E472463B4D400547F70 /* SegmentedControl.framework */, 59 | 4AA16E432463B4C100547F70 /* SegmentedControl.framework */, 60 | ); 61 | name = Frameworks; 62 | sourceTree = ""; 63 | }; 64 | D3E3B0D61C328316003EAA6E = { 65 | isa = PBXGroup; 66 | children = ( 67 | D3E3B0E11C328316003EAA6E /* SegmentedControlExample */, 68 | D3E3B0E01C328316003EAA6E /* Products */, 69 | 4AA16E422463B4C100547F70 /* Frameworks */, 70 | ); 71 | sourceTree = ""; 72 | }; 73 | D3E3B0E01C328316003EAA6E /* Products */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | D3E3B0DF1C328316003EAA6E /* SegmentedControlExample.app */, 77 | ); 78 | name = Products; 79 | sourceTree = ""; 80 | }; 81 | D3E3B0E11C328316003EAA6E /* SegmentedControlExample */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | D3E3B0E21C328316003EAA6E /* AppDelegate.swift */, 85 | D3E3B0E41C328316003EAA6E /* ExampleViewController.swift */, 86 | D3E3B0E61C328316003EAA6E /* Main.storyboard */, 87 | D3E3B0E91C328316003EAA6E /* Assets.xcassets */, 88 | D3E3B0EE1C328316003EAA6E /* Info.plist */, 89 | ); 90 | path = SegmentedControlExample; 91 | sourceTree = ""; 92 | }; 93 | /* End PBXGroup section */ 94 | 95 | /* Begin PBXNativeTarget section */ 96 | D3E3B0DE1C328316003EAA6E /* SegmentedControlExample */ = { 97 | isa = PBXNativeTarget; 98 | buildConfigurationList = D3E3B0F11C328316003EAA6E /* Build configuration list for PBXNativeTarget "SegmentedControlExample" */; 99 | buildPhases = ( 100 | D3E3B0DB1C328316003EAA6E /* Sources */, 101 | D3E3B0DC1C328316003EAA6E /* Frameworks */, 102 | D3E3B0DD1C328316003EAA6E /* Resources */, 103 | 4AA16E4A2463B4D500547F70 /* Embed Frameworks */, 104 | ); 105 | buildRules = ( 106 | ); 107 | dependencies = ( 108 | ); 109 | name = SegmentedControlExample; 110 | productName = SegmentedControlExample; 111 | productReference = D3E3B0DF1C328316003EAA6E /* SegmentedControlExample.app */; 112 | productType = "com.apple.product-type.application"; 113 | }; 114 | /* End PBXNativeTarget section */ 115 | 116 | /* Begin PBXProject section */ 117 | D3E3B0D71C328316003EAA6E /* Project object */ = { 118 | isa = PBXProject; 119 | attributes = { 120 | LastSwiftUpdateCheck = 0720; 121 | LastUpgradeCheck = 1020; 122 | ORGANIZATIONNAME = Teambition; 123 | TargetAttributes = { 124 | D3E3B0DE1C328316003EAA6E = { 125 | CreatedOnToolsVersion = 7.2; 126 | LastSwiftMigration = 1020; 127 | }; 128 | }; 129 | }; 130 | buildConfigurationList = D3E3B0DA1C328316003EAA6E /* Build configuration list for PBXProject "SegmentedControlExample" */; 131 | compatibilityVersion = "Xcode 3.2"; 132 | developmentRegion = en; 133 | hasScannedForEncodings = 0; 134 | knownRegions = ( 135 | en, 136 | Base, 137 | ); 138 | mainGroup = D3E3B0D61C328316003EAA6E; 139 | productRefGroup = D3E3B0E01C328316003EAA6E /* Products */; 140 | projectDirPath = ""; 141 | projectRoot = ""; 142 | targets = ( 143 | D3E3B0DE1C328316003EAA6E /* SegmentedControlExample */, 144 | ); 145 | }; 146 | /* End PBXProject section */ 147 | 148 | /* Begin PBXResourcesBuildPhase section */ 149 | D3E3B0DD1C328316003EAA6E /* Resources */ = { 150 | isa = PBXResourcesBuildPhase; 151 | buildActionMask = 2147483647; 152 | files = ( 153 | D3E3B0EA1C328316003EAA6E /* Assets.xcassets in Resources */, 154 | D3E3B0E81C328316003EAA6E /* Main.storyboard in Resources */, 155 | ); 156 | runOnlyForDeploymentPostprocessing = 0; 157 | }; 158 | /* End PBXResourcesBuildPhase section */ 159 | 160 | /* Begin PBXSourcesBuildPhase section */ 161 | D3E3B0DB1C328316003EAA6E /* Sources */ = { 162 | isa = PBXSourcesBuildPhase; 163 | buildActionMask = 2147483647; 164 | files = ( 165 | D3E3B0E51C328316003EAA6E /* ExampleViewController.swift in Sources */, 166 | D3E3B0E31C328316003EAA6E /* AppDelegate.swift in Sources */, 167 | ); 168 | runOnlyForDeploymentPostprocessing = 0; 169 | }; 170 | /* End PBXSourcesBuildPhase section */ 171 | 172 | /* Begin PBXVariantGroup section */ 173 | D3E3B0E61C328316003EAA6E /* Main.storyboard */ = { 174 | isa = PBXVariantGroup; 175 | children = ( 176 | D3E3B0E71C328316003EAA6E /* Base */, 177 | ); 178 | name = Main.storyboard; 179 | sourceTree = ""; 180 | }; 181 | /* End PBXVariantGroup section */ 182 | 183 | /* Begin XCBuildConfiguration section */ 184 | D3E3B0EF1C328316003EAA6E /* Debug */ = { 185 | isa = XCBuildConfiguration; 186 | buildSettings = { 187 | ALWAYS_SEARCH_USER_PATHS = NO; 188 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 189 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 190 | CLANG_CXX_LIBRARY = "libc++"; 191 | CLANG_ENABLE_MODULES = YES; 192 | CLANG_ENABLE_OBJC_ARC = YES; 193 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 194 | CLANG_WARN_BOOL_CONVERSION = YES; 195 | CLANG_WARN_COMMA = YES; 196 | CLANG_WARN_CONSTANT_CONVERSION = YES; 197 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 198 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 199 | CLANG_WARN_EMPTY_BODY = YES; 200 | CLANG_WARN_ENUM_CONVERSION = YES; 201 | CLANG_WARN_INFINITE_RECURSION = YES; 202 | CLANG_WARN_INT_CONVERSION = YES; 203 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 204 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 205 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 206 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 207 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 208 | CLANG_WARN_STRICT_PROTOTYPES = YES; 209 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 210 | CLANG_WARN_UNREACHABLE_CODE = YES; 211 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 212 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 213 | COPY_PHASE_STRIP = NO; 214 | DEBUG_INFORMATION_FORMAT = dwarf; 215 | ENABLE_STRICT_OBJC_MSGSEND = YES; 216 | ENABLE_TESTABILITY = YES; 217 | GCC_C_LANGUAGE_STANDARD = gnu99; 218 | GCC_DYNAMIC_NO_PIC = NO; 219 | GCC_NO_COMMON_BLOCKS = YES; 220 | GCC_OPTIMIZATION_LEVEL = 0; 221 | GCC_PREPROCESSOR_DEFINITIONS = ( 222 | "DEBUG=1", 223 | "$(inherited)", 224 | ); 225 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 226 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 227 | GCC_WARN_UNDECLARED_SELECTOR = YES; 228 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 229 | GCC_WARN_UNUSED_FUNCTION = YES; 230 | GCC_WARN_UNUSED_VARIABLE = YES; 231 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 232 | MTL_ENABLE_DEBUG_INFO = YES; 233 | ONLY_ACTIVE_ARCH = YES; 234 | SDKROOT = iphoneos; 235 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 236 | TARGETED_DEVICE_FAMILY = "1,2"; 237 | }; 238 | name = Debug; 239 | }; 240 | D3E3B0F01C328316003EAA6E /* Release */ = { 241 | isa = XCBuildConfiguration; 242 | buildSettings = { 243 | ALWAYS_SEARCH_USER_PATHS = NO; 244 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 245 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 246 | CLANG_CXX_LIBRARY = "libc++"; 247 | CLANG_ENABLE_MODULES = YES; 248 | CLANG_ENABLE_OBJC_ARC = YES; 249 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 250 | CLANG_WARN_BOOL_CONVERSION = YES; 251 | CLANG_WARN_COMMA = YES; 252 | CLANG_WARN_CONSTANT_CONVERSION = YES; 253 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 254 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 255 | CLANG_WARN_EMPTY_BODY = YES; 256 | CLANG_WARN_ENUM_CONVERSION = YES; 257 | CLANG_WARN_INFINITE_RECURSION = YES; 258 | CLANG_WARN_INT_CONVERSION = YES; 259 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 260 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 261 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 262 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 263 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 264 | CLANG_WARN_STRICT_PROTOTYPES = YES; 265 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 266 | CLANG_WARN_UNREACHABLE_CODE = YES; 267 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 268 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 269 | COPY_PHASE_STRIP = NO; 270 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 271 | ENABLE_NS_ASSERTIONS = NO; 272 | ENABLE_STRICT_OBJC_MSGSEND = YES; 273 | GCC_C_LANGUAGE_STANDARD = gnu99; 274 | GCC_NO_COMMON_BLOCKS = YES; 275 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 276 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 277 | GCC_WARN_UNDECLARED_SELECTOR = YES; 278 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 279 | GCC_WARN_UNUSED_FUNCTION = YES; 280 | GCC_WARN_UNUSED_VARIABLE = YES; 281 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 282 | MTL_ENABLE_DEBUG_INFO = NO; 283 | SDKROOT = iphoneos; 284 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 285 | TARGETED_DEVICE_FAMILY = "1,2"; 286 | VALIDATE_PRODUCT = YES; 287 | }; 288 | name = Release; 289 | }; 290 | D3E3B0F21C328316003EAA6E /* Debug */ = { 291 | isa = XCBuildConfiguration; 292 | buildSettings = { 293 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 294 | INFOPLIST_FILE = SegmentedControlExample/Info.plist; 295 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 296 | PRODUCT_BUNDLE_IDENTIFIER = Teambition.SegmentedControlExample; 297 | PRODUCT_NAME = "$(TARGET_NAME)"; 298 | SWIFT_VERSION = 5.0; 299 | }; 300 | name = Debug; 301 | }; 302 | D3E3B0F31C328316003EAA6E /* Release */ = { 303 | isa = XCBuildConfiguration; 304 | buildSettings = { 305 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 306 | INFOPLIST_FILE = SegmentedControlExample/Info.plist; 307 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 308 | PRODUCT_BUNDLE_IDENTIFIER = Teambition.SegmentedControlExample; 309 | PRODUCT_NAME = "$(TARGET_NAME)"; 310 | SWIFT_VERSION = 5.0; 311 | }; 312 | name = Release; 313 | }; 314 | /* End XCBuildConfiguration section */ 315 | 316 | /* Begin XCConfigurationList section */ 317 | D3E3B0DA1C328316003EAA6E /* Build configuration list for PBXProject "SegmentedControlExample" */ = { 318 | isa = XCConfigurationList; 319 | buildConfigurations = ( 320 | D3E3B0EF1C328316003EAA6E /* Debug */, 321 | D3E3B0F01C328316003EAA6E /* Release */, 322 | ); 323 | defaultConfigurationIsVisible = 0; 324 | defaultConfigurationName = Release; 325 | }; 326 | D3E3B0F11C328316003EAA6E /* Build configuration list for PBXNativeTarget "SegmentedControlExample" */ = { 327 | isa = XCConfigurationList; 328 | buildConfigurations = ( 329 | D3E3B0F21C328316003EAA6E /* Debug */, 330 | D3E3B0F31C328316003EAA6E /* Release */, 331 | ); 332 | defaultConfigurationIsVisible = 0; 333 | defaultConfigurationName = Release; 334 | }; 335 | /* End XCConfigurationList section */ 336 | }; 337 | rootObject = D3E3B0D71C328316003EAA6E /* Project object */; 338 | } 339 | -------------------------------------------------------------------------------- /SegmentedControlExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SegmentedControlExample.xcodeproj/xcuserdata/hongxin.xcuserdatad/xcschemes/SegmentedControlExample.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 | -------------------------------------------------------------------------------- /SegmentedControlExample.xcodeproj/xcuserdata/hongxin.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SegmentedControlExample.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | D3E3B0DE1C328316003EAA6E 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /SegmentedControlExample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SegmentedControlExample 4 | // 5 | // Created by 洪鑫 on 15/12/29. 6 | // Copyright © 2015年 Teambition. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | var window: UIWindow? 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { 16 | return true 17 | } 18 | 19 | func applicationWillResignActive(_ application: UIApplication) { 20 | 21 | } 22 | 23 | func applicationDidEnterBackground(_ application: UIApplication) { 24 | 25 | } 26 | 27 | func applicationWillEnterForeground(_ application: UIApplication) { 28 | 29 | } 30 | 31 | func applicationDidBecomeActive(_ application: UIApplication) { 32 | 33 | } 34 | 35 | func applicationWillTerminate(_ application: UIApplication) { 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /SegmentedControlExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /SegmentedControlExample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SegmentedControlExample/Assets.xcassets/chat-selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "chat-selected.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /SegmentedControlExample/Assets.xcassets/chat-selected.imageset/chat-selected.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teambition/SegmentedControl/58a6f9270e60accf4513176e472e82146458e601/SegmentedControlExample/Assets.xcassets/chat-selected.imageset/chat-selected.pdf -------------------------------------------------------------------------------- /SegmentedControlExample/Assets.xcassets/chat.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "chat.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /SegmentedControlExample/Assets.xcassets/chat.imageset/chat.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teambition/SegmentedControl/58a6f9270e60accf4513176e472e82146458e601/SegmentedControlExample/Assets.xcassets/chat.imageset/chat.pdf -------------------------------------------------------------------------------- /SegmentedControlExample/Assets.xcassets/me-selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "me-selected.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /SegmentedControlExample/Assets.xcassets/me-selected.imageset/me-selected.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teambition/SegmentedControl/58a6f9270e60accf4513176e472e82146458e601/SegmentedControlExample/Assets.xcassets/me-selected.imageset/me-selected.pdf -------------------------------------------------------------------------------- /SegmentedControlExample/Assets.xcassets/me.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "me.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /SegmentedControlExample/Assets.xcassets/me.imageset/me.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teambition/SegmentedControl/58a6f9270e60accf4513176e472e82146458e601/SegmentedControlExample/Assets.xcassets/me.imageset/me.pdf -------------------------------------------------------------------------------- /SegmentedControlExample/Assets.xcassets/notification-selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "notification-selected.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /SegmentedControlExample/Assets.xcassets/notification-selected.imageset/notification-selected.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teambition/SegmentedControl/58a6f9270e60accf4513176e472e82146458e601/SegmentedControlExample/Assets.xcassets/notification-selected.imageset/notification-selected.pdf -------------------------------------------------------------------------------- /SegmentedControlExample/Assets.xcassets/notification.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "notification.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /SegmentedControlExample/Assets.xcassets/notification.imageset/notification.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teambition/SegmentedControl/58a6f9270e60accf4513176e472e82146458e601/SegmentedControlExample/Assets.xcassets/notification.imageset/notification.pdf -------------------------------------------------------------------------------- /SegmentedControlExample/Assets.xcassets/project-selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "project-selected.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /SegmentedControlExample/Assets.xcassets/project-selected.imageset/project-selected.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teambition/SegmentedControl/58a6f9270e60accf4513176e472e82146458e601/SegmentedControlExample/Assets.xcassets/project-selected.imageset/project-selected.pdf -------------------------------------------------------------------------------- /SegmentedControlExample/Assets.xcassets/project.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "project.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /SegmentedControlExample/Assets.xcassets/project.imageset/project.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teambition/SegmentedControl/58a6f9270e60accf4513176e472e82146458e601/SegmentedControlExample/Assets.xcassets/project.imageset/project.pdf -------------------------------------------------------------------------------- /SegmentedControlExample/Assets.xcassets/taskSegmentAdditionIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "taskSegmentAdditionIcon.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /SegmentedControlExample/Assets.xcassets/taskSegmentAdditionIcon.imageset/taskSegmentAdditionIcon.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teambition/SegmentedControl/58a6f9270e60accf4513176e472e82146458e601/SegmentedControlExample/Assets.xcassets/taskSegmentAdditionIcon.imageset/taskSegmentAdditionIcon.pdf -------------------------------------------------------------------------------- /SegmentedControlExample/Assets.xcassets/taskSegmentAdditionIconSelected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "taskSegmentAdditionIconSelected.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /SegmentedControlExample/Assets.xcassets/taskSegmentAdditionIconSelected.imageset/taskSegmentAdditionIconSelected.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teambition/SegmentedControl/58a6f9270e60accf4513176e472e82146458e601/SegmentedControlExample/Assets.xcassets/taskSegmentAdditionIconSelected.imageset/taskSegmentAdditionIconSelected.pdf -------------------------------------------------------------------------------- /SegmentedControlExample/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /SegmentedControlExample/ExampleViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExampleViewController.swift 3 | // SegmentedControlExample 4 | // 5 | // Created by 洪鑫 on 15/12/29. 6 | // Copyright © 2015年 Teambition. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SegmentedControl 11 | 12 | private let kLivelyBlueColor = UIColor(red: 3 / 255, green: 169 / 255, blue: 244 / 255, alpha: 1) 13 | 14 | class ExampleViewController: UIViewController { 15 | @IBOutlet weak var segmentedControl1: SegmentedControl! 16 | @IBOutlet weak var segmentedControl2: SegmentedControl! 17 | @IBOutlet weak var segmentedControl3: SegmentedControl! 18 | 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | setupUI() 22 | } 23 | 24 | fileprivate func setupUI() { 25 | configureNavigationTitleSegmentedControl() 26 | configureNavigationBelowSegmentedControl() 27 | configureSegmentedControl1() 28 | configureSegmentedControl2() 29 | configureSegmentedControl3() 30 | } 31 | 32 | fileprivate func configureNavigationTitleSegmentedControl() { 33 | let titleStrings = ["任务", "分享", "文件", "日程"] 34 | let titles: [NSAttributedString] = { 35 | let attributes: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 17), .foregroundColor: UIColor.darkGray] 36 | var titles = [NSAttributedString]() 37 | for titleString in titleStrings { 38 | let title = NSAttributedString(string: titleString, attributes: attributes) 39 | titles.append(title) 40 | } 41 | return titles 42 | }() 43 | let selectedTitles: [NSAttributedString] = { 44 | let attributes: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 17), .foregroundColor: UIColor.white] 45 | var selectedTitles = [NSAttributedString]() 46 | for titleString in titleStrings { 47 | let selectedTitle = NSAttributedString(string: titleString, attributes: attributes) 48 | selectedTitles.append(selectedTitle) 49 | } 50 | return selectedTitles 51 | }() 52 | let segmentedControl = SegmentedControl.initWithTitles(titles, selectedTitles: selectedTitles) 53 | segmentedControl.delegate = self 54 | segmentedControl.backgroundColor = .clear 55 | segmentedControl.selectionBoxColor = kLivelyBlueColor 56 | segmentedControl.selectionBoxStyle = .default 57 | segmentedControl.selectionBoxCornerRadius = 15 58 | segmentedControl.frame.size = CGSize(width: 70 * titles.count, height: 30) 59 | segmentedControl.isLongPressEnabled = true 60 | segmentedControl.isUnselectedSegmentsLongPressEnabled = true 61 | segmentedControl.longPressMinimumPressDuration = 1 62 | segmentedControl.setTitleAttachedIcons([#imageLiteral(resourceName: "taskSegmentAdditionIcon")], selectedTitleAttachedIcons: [#imageLiteral(resourceName: "taskSegmentAdditionIconSelected")]) 63 | navigationItem.titleView = segmentedControl 64 | } 65 | 66 | fileprivate func configureNavigationBelowSegmentedControl() { 67 | let titleStrings = ["任务", "分享", "文件", "日程", "账目", "标签", "通知", "聊天", "收件箱", "联系人"] 68 | let titles: [NSAttributedString] = { 69 | let attributes: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 16), .foregroundColor: UIColor.black] 70 | var titles = [NSAttributedString]() 71 | for titleString in titleStrings { 72 | let title = NSAttributedString(string: titleString, attributes: attributes) 73 | titles.append(title) 74 | } 75 | return titles 76 | }() 77 | let selectedTitles: [NSAttributedString] = { 78 | let attributes: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 16), .foregroundColor: kLivelyBlueColor] 79 | var selectedTitles = [NSAttributedString]() 80 | for titleString in titleStrings { 81 | let selectedTitle = NSAttributedString(string: titleString, attributes: attributes) 82 | selectedTitles.append(selectedTitle) 83 | } 84 | return selectedTitles 85 | }() 86 | let segmentedControl = SegmentedControl.initWithTitles(titles, selectedTitles: selectedTitles) 87 | segmentedControl.delegate = self 88 | segmentedControl.backgroundColor = .white 89 | segmentedControl.autoresizingMask = [.flexibleRightMargin, .flexibleWidth] 90 | segmentedControl.selectionIndicatorStyle = .bottom 91 | segmentedControl.selectionIndicatorColor = kLivelyBlueColor 92 | segmentedControl.selectionIndicatorHeight = 3 93 | segmentedControl.segmentWidth = 65 94 | segmentedControl.frame.origin.y = UIApplication.shared.statusBarFrame.height + 44 95 | segmentedControl.frame.size = CGSize(width: UIScreen.main.bounds.width, height: 40) 96 | view.insertSubview(segmentedControl, belowSubview: navigationController!.navigationBar) 97 | } 98 | 99 | fileprivate func configureSegmentedControl1() { 100 | let titleStrings = ["任务", "分享", "文件", "日程", "聊天"] 101 | let titles: [NSAttributedString] = { 102 | let attributes: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 17), .foregroundColor: UIColor.white] 103 | var titles = [NSAttributedString]() 104 | for titleString in titleStrings { 105 | let title = NSAttributedString(string: titleString, attributes: attributes) 106 | titles.append(title) 107 | } 108 | return titles 109 | }() 110 | let selectedTitles: [NSAttributedString] = { 111 | let attributes: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 17), .foregroundColor: UIColor(white: 0.1, alpha: 1)] 112 | var selectedTitles = [NSAttributedString]() 113 | for titleString in titleStrings { 114 | let selectedTitle = NSAttributedString(string: titleString, attributes: attributes) 115 | selectedTitles.append(selectedTitle) 116 | } 117 | return selectedTitles 118 | }() 119 | segmentedControl1.setTitles(titles, selectedTitles: selectedTitles) 120 | segmentedControl1.delegate = self 121 | segmentedControl1.selectionBoxStyle = .default 122 | segmentedControl1.minimumSegmentWidth = 375.0 / 4.0 123 | segmentedControl1.selectionBoxColor = UIColor(white: 0.62, alpha: 1) 124 | segmentedControl1.selectionIndicatorStyle = .top 125 | segmentedControl1.selectionIndicatorColor = UIColor(white: 0.3, alpha: 1) 126 | } 127 | 128 | fileprivate func configureSegmentedControl2() { 129 | let images = [#imageLiteral(resourceName: "project"), #imageLiteral(resourceName: "me"), #imageLiteral(resourceName: "notification"), #imageLiteral(resourceName: "chat")] 130 | let selectedImages = [#imageLiteral(resourceName: "project-selected"), #imageLiteral(resourceName: "me-selected"), #imageLiteral(resourceName: "notification-selected"), #imageLiteral(resourceName: "chat-selected")] 131 | segmentedControl2.setImages(images, selectedImages: selectedImages) 132 | segmentedControl2.delegate = self 133 | segmentedControl2.selectionIndicatorStyle = .bottom 134 | segmentedControl2.selectionIndicatorColor = kLivelyBlueColor 135 | segmentedControl2.selectionIndicatorHeight = 3 136 | segmentedControl2.selectionIndicatorEdgeInsets = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20) 137 | } 138 | 139 | fileprivate func configureSegmentedControl3() { 140 | let titleStrings = ["Tasks", "Posts", "Files", "Meetings", "Favourites", "Chats"] 141 | let titles: [NSAttributedString] = { 142 | let attributes: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 17), .foregroundColor: UIColor.darkGray] 143 | var titles = [NSAttributedString]() 144 | for titleString in titleStrings { 145 | let title = NSAttributedString(string: titleString, attributes: attributes) 146 | titles.append(title) 147 | } 148 | return titles 149 | }() 150 | let selectedTitles: [NSAttributedString] = { 151 | let attributes: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 17), .foregroundColor: UIColor.white] 152 | var selectedTitles = [NSAttributedString]() 153 | for titleString in titleStrings { 154 | let selectedTitle = NSAttributedString(string: titleString, attributes: attributes) 155 | selectedTitles.append(selectedTitle) 156 | } 157 | return selectedTitles 158 | }() 159 | segmentedControl3.setTitles(titles, selectedTitles: selectedTitles) 160 | segmentedControl3.delegate = self 161 | segmentedControl3.layoutPolicy = .dynamic 162 | segmentedControl3.segmentSpacing = 20 163 | segmentedControl3.selectionBoxHeight = 30 164 | segmentedControl3.selectionHorizontalPadding = 15 165 | segmentedControl3.selectionBoxStyle = .default 166 | segmentedControl3.selectionBoxCornerRadius = 15 167 | segmentedControl3.shouldShowAllBox = true 168 | 169 | segmentedControl3.selectionBoxColor = kLivelyBlueColor 170 | segmentedControl3.contentInset = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15) 171 | segmentedControl3.setTitleAttachedIcons([#imageLiteral(resourceName: "taskSegmentAdditionIcon")], selectedTitleAttachedIcons: [#imageLiteral(resourceName: "taskSegmentAdditionIconSelected")]) 172 | segmentedControl3.titleAttachedIconPositionOffset = (5, 1) 173 | segmentedControl3.isLongPressEnabled = true 174 | segmentedControl3.isUnselectedSegmentsLongPressEnabled = true 175 | segmentedControl3.longPressMinimumPressDuration = 0.8 176 | } 177 | } 178 | 179 | extension ExampleViewController: SegmentedControlDelegate { 180 | func segmentedControl(_ segmentedControl: SegmentedControl, didSelectIndex selectedIndex: Int) { 181 | print("Did select index \(selectedIndex)") 182 | switch segmentedControl.style { 183 | case .text: 184 | print("The title is “\(segmentedControl.titles[selectedIndex].string)”\n") 185 | case .image: 186 | print("The image is “\(segmentedControl.images[selectedIndex])”\n") 187 | } 188 | } 189 | 190 | func segmentedControl(_ segmentedControl: SegmentedControl, didLongPressIndex longPressIndex: Int) { 191 | print("Did long press index \(longPressIndex)") 192 | if UIDevice.current.userInterfaceIdiom == .pad { 193 | let viewController = UIViewController() 194 | viewController.modalPresentationStyle = .popover 195 | viewController.preferredContentSize = CGSize(width: 200, height: 300) 196 | if let popoverController = viewController.popoverPresentationController { 197 | popoverController.sourceView = view 198 | let yOffset: CGFloat = 10 199 | popoverController.sourceRect = view.convert(CGRect(origin: CGPoint(x: 70 * CGFloat(longPressIndex), y: yOffset), size: CGSize(width: 70, height: 30)), from: navigationItem.titleView) 200 | popoverController.permittedArrowDirections = .any 201 | present(viewController, animated: true, completion: nil) 202 | } 203 | } else { 204 | let message = segmentedControl.style == .text ? "Long press title “\(segmentedControl.titles[longPressIndex].string)”" : "Long press image “\(segmentedControl.images[longPressIndex])”" 205 | let alert = UIAlertController(title: nil, message: message, preferredStyle: .actionSheet) 206 | let cancelAction = UIAlertAction(title: "OK", style: .cancel, handler: nil) 207 | alert.addAction(cancelAction) 208 | present(alert, animated: true, completion: nil) 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /SegmentedControlExample/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 | 0.1.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 11 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /TBSegmentedControl.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Created by teambition-ios on 2020/7/27. 3 | # Copyright © 2020 teambition. All rights reserved. 4 | # 5 | 6 | Pod::Spec.new do |s| 7 | s.name = 'TBSegmentedControl' 8 | s.version = '0.3.1' 9 | s.summary = 'SegmentedControl is a highly customizable segmented control for iOS applications.' 10 | s.description = <<-DESC 11 | SegmentedControl is a highly customizable segmented control for iOS applications. 12 | DESC 13 | 14 | s.homepage = 'https://github.com/teambition/SegmentedControl' 15 | s.license = { :type => 'MIT', :file => 'LICENSE.md' } 16 | s.author = { 'teambition mobile' => 'teambition-mobile@alibaba-inc.com' } 17 | s.source = { :git => 'https://github.com/teambition/SegmentedControl.git', :tag => s.version.to_s } 18 | 19 | s.swift_version = '5.0' 20 | s.ios.deployment_target = '8.0' 21 | 22 | s.source_files = 'SegmentedControl/*.swift' 23 | 24 | end 25 | --------------------------------------------------------------------------------