├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── stale.yml ├── .gitignore ├── .travis.yml ├── Example ├── AppDelegate.swift ├── Assets.xcassets │ ├── 1-selected.imageset │ │ ├── 1-selected.png │ │ ├── 1-selected@2x.png │ │ └── Contents.json │ ├── 1.imageset │ │ ├── 1.png │ │ ├── 1@2x.png │ │ └── Contents.json │ ├── 2-selected.imageset │ │ ├── 2-selected.png │ │ ├── 2-selected@2x.png │ │ └── Contents.json │ ├── 2.imageset │ │ ├── 2.png │ │ ├── 2@2x.png │ │ └── Contents.json │ ├── 3-selected.imageset │ │ ├── 3-selected.png │ │ ├── 3-selected@2x.png │ │ └── Contents.json │ ├── 3.imageset │ │ ├── 3.png │ │ ├── 3@2x.png │ │ └── Contents.json │ ├── 4-selected.imageset │ │ ├── 4-selected.png │ │ ├── 4-selected@2x.png │ │ └── Contents.json │ ├── 4.imageset │ │ ├── 4.png │ │ ├── 4@2x.png │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── earthquakes.imageset │ │ ├── Contents.json │ │ ├── eathquakes.png │ │ ├── eathquakes@2x.png │ │ └── eathquakes@3x.png │ ├── eruption.imageset │ │ ├── Contents.json │ │ ├── eruption.png │ │ ├── eruption@2x.png │ │ └── eruption@3x.png │ ├── floods.imageset │ │ ├── Contents.json │ │ ├── floods.png │ │ ├── floods@2x.png │ │ └── floods@3x.png │ ├── heat.imageset │ │ ├── Contents.json │ │ ├── heat.png │ │ ├── heat@2x.png │ │ └── heat@3x.png │ ├── tornado.imageset │ │ ├── Contents.json │ │ ├── tornado.png │ │ ├── tornado@2x.png │ │ └── tornado@3x.png │ └── wildfires.imageset │ │ ├── Contents.json │ │ ├── wildfires.png │ │ ├── wildfires@2x.png │ │ └── wildfires@3x.png ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── CustomViewController.swift ├── HMSegmentedControlViewController.swift ├── Info.plist ├── SegmentioViewController.swift └── TopBarViewController.swift ├── LICENSE ├── MXSegmentedControl.podspec ├── MXSegmentedControl.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── MXSegmentedControl.xcscheme ├── MXSegmentedControl ├── Info.plist ├── MXIndicator.swift ├── MXSegment.swift ├── MXSegmentedControl.h └── MXSegmentedControl.swift ├── Package.swift ├── README.md └── screencast.gif /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | NOTE: ISSUES ARE NOT FOR CODE HELP - Ask for Help at https://stackoverflow.com 9 | 10 | Your issue may already be reported! 11 | Please search on the [issue tracker](../) before creating one. 12 | 13 | #### Issue Description 14 | * When Issue Happens 15 | * Steps To Reproduce 16 | 17 | #### Environment Information 18 | * Lib Version 19 | * OS Version 20 | 21 | #### Your Code 22 | 23 | ``` 24 | If relevant, paste all of your challenge code in here 25 | ``` 26 | 27 | #### Screenshot -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Description 4 | 5 | 6 | ## Motivation and Context 7 | 8 | 9 | 10 | ## How Has This Been Tested? 11 | 12 | 13 | 14 | 15 | ## Screenshots (if appropriate): 16 | 17 | ## Types of changes 18 | 19 | - [ ] Bug fix (non-breaking change which fixes an issue) 20 | - [ ] New feature (non-breaking change which adds functionality) 21 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 22 | 23 | ## Checklist: 24 | 25 | 26 | - [ ] My code follows the code style of this project. 27 | - [ ] My change requires a change to the documentation. 28 | - [ ] I have updated the documentation accordingly. -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-stale - https://github.com/probot/stale 2 | 3 | # Number of days of inactivity before an Issue or Pull Request becomes stale 4 | daysUntilStale: 60 5 | 6 | # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. 7 | # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. 8 | daysUntilClose: 7 9 | 10 | # Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) 11 | onlyLabels: [] 12 | 13 | # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable 14 | exemptLabels: 15 | - bug 16 | - help wanted 17 | - enhancement 18 | - need review 19 | 20 | # Label to use when marking as stale 21 | staleLabel: stale 22 | 23 | # Comment to post when marking as stale. Set to `false` to disable 24 | markComment: > 25 | This issue has been automatically marked as stale because it has not had 26 | recent activity. It will be closed if no further activity occurs. Thank you 27 | for your contributions. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | xcuserdata 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode11.2 3 | env: 4 | global: 5 | - FRAMEWORK_NAME=MXSegmentedControl 6 | before_install: 7 | - brew update 8 | - brew outdated carthage || brew upgrade carthage 9 | install: 10 | - gem install cocoapods 11 | - gem install xcpretty --no-document --quiet 12 | script: 13 | - set -o pipefail && xcodebuild -project MXSegmentedControl.xcodeproj -scheme $FRAMEWORK_NAME 14 | -sdk iphonesimulator13.2 -configuration Release | xcpretty -c 15 | - pod lib lint --quick 16 | before_deploy: 17 | - carthage build --no-skip-current 18 | - carthage archive $FRAMEWORK_NAME 19 | deploy: 20 | provider: releases 21 | api_key: 22 | secure: GCbSn67brClZCNGbnlqGkwnWjFLSgYwWkjAOhkqBGIKzh/pARW227QXdDZP9vPz3oLxO/ux/tM1aojweakJCzIoYXENijTcB5rt7teUDsFiH/UInjfTCxNRLNzREUrvwOywAwEq37Ysdw3QH222SnJIVPl14ucwCyXXDMMQUmeyHLJg46AfNhZBheuWdzH5skAel9K68FOMeW9rbDEAUiXi+UHFRfmpEy0g0ukfVsVmnXB7YYQLN7ATz3fTwe6rMwPBneh5eibG7mNA81ROfsyLV7JUibjSzDIaXYPjsS+PCD8c7hWgfaTUdEeqjgOAAhwlbJz0yq5Z/ONtpRoCVFzniMV9gghr32rrt8+RvyrL9ELiqoWg9IvTQg9gNZ0wtMyT0pPTRuFxX4iD4kd0Q4YQjiPRsekxN3sOInE/njBYer69m3+geX25PcAxLCSZiuj2tXEcm4GPrApX+M822uU1PFq/8yOw6z8zfdTTgKu6l0HJaDTtqQ9BLU7m5lf0B2xQ7aloRucBPoJK+G2xOC/z6uZ06vJPoHi4eNRuiCSbThWg9NCBnWcAqYfcK/0kbQTc7E2olI13Wo5hXjXLQTpWIjkRt42Y5d11Hy4tUDF6qCx696W3rWWZnkkEWqtA7n0YYxhws8MiK7X1sedx7XrF8UfZU33TybPXt7pv726s= 23 | file: "$FRAMEWORK_NAME.framework.zip" 24 | skip_cleanup: true 25 | on: 26 | repo: maxep/MXSegmentedControl 27 | tags: true 28 | -------------------------------------------------------------------------------- /Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // AppDelegate.swift 2 | // 3 | // Copyright (c) 2019 Maxime Epain 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | import UIKit 24 | 25 | @UIApplicationMain 26 | class AppDelegate: UIResponder, UIApplicationDelegate { 27 | 28 | var window: UIWindow? 29 | 30 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 31 | // Override point for customization after application launch. 32 | return true 33 | } 34 | 35 | func applicationWillResignActive(_ application: UIApplication) { 36 | // 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. 37 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 38 | } 39 | 40 | func applicationDidEnterBackground(_ application: UIApplication) { 41 | // 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. 42 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 43 | } 44 | 45 | func applicationWillEnterForeground(_ application: UIApplication) { 46 | // 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. 47 | } 48 | 49 | func applicationDidBecomeActive(_ application: UIApplication) { 50 | // 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. 51 | } 52 | 53 | func applicationWillTerminate(_ application: UIApplication) { 54 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 55 | } 56 | 57 | 58 | } 59 | 60 | -------------------------------------------------------------------------------- /Example/Assets.xcassets/1-selected.imageset/1-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/1-selected.imageset/1-selected.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/1-selected.imageset/1-selected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/1-selected.imageset/1-selected@2x.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/1-selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "1-selected.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "1-selected@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/1.imageset/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/1.imageset/1.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/1.imageset/1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/1.imageset/1@2x.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "1.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "1@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/2-selected.imageset/2-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/2-selected.imageset/2-selected.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/2-selected.imageset/2-selected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/2-selected.imageset/2-selected@2x.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/2-selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "2-selected.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "2-selected@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/2.imageset/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/2.imageset/2.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/2.imageset/2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/2.imageset/2@2x.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "2.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "2@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/3-selected.imageset/3-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/3-selected.imageset/3-selected.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/3-selected.imageset/3-selected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/3-selected.imageset/3-selected@2x.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/3-selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "3-selected.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "3-selected@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/3.imageset/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/3.imageset/3.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/3.imageset/3@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/3.imageset/3@2x.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "3.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "3@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/4-selected.imageset/4-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/4-selected.imageset/4-selected.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/4-selected.imageset/4-selected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/4-selected.imageset/4-selected@2x.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/4-selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "4-selected.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "4-selected@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/4.imageset/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/4.imageset/4.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/4.imageset/4@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/4.imageset/4@2x.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "4.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "4@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Example/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" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/earthquakes.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "eathquakes.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "eathquakes@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "eathquakes@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/earthquakes.imageset/eathquakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/earthquakes.imageset/eathquakes.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/earthquakes.imageset/eathquakes@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/earthquakes.imageset/eathquakes@2x.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/earthquakes.imageset/eathquakes@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/earthquakes.imageset/eathquakes@3x.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/eruption.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "eruption.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "eruption@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "eruption@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/eruption.imageset/eruption.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/eruption.imageset/eruption.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/eruption.imageset/eruption@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/eruption.imageset/eruption@2x.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/eruption.imageset/eruption@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/eruption.imageset/eruption@3x.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/floods.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "floods.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "floods@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "floods@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/floods.imageset/floods.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/floods.imageset/floods.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/floods.imageset/floods@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/floods.imageset/floods@2x.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/floods.imageset/floods@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/floods.imageset/floods@3x.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/heat.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "heat.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "heat@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "heat@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/heat.imageset/heat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/heat.imageset/heat.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/heat.imageset/heat@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/heat.imageset/heat@2x.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/heat.imageset/heat@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/heat.imageset/heat@3x.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/tornado.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "tornado.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "tornado@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "tornado@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/tornado.imageset/tornado.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/tornado.imageset/tornado.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/tornado.imageset/tornado@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/tornado.imageset/tornado@2x.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/tornado.imageset/tornado@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/tornado.imageset/tornado@3x.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/wildfires.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "wildfires.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "wildfires@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "wildfires@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/wildfires.imageset/wildfires.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/wildfires.imageset/wildfires.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/wildfires.imageset/wildfires@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/wildfires.imageset/wildfires@2x.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/wildfires.imageset/wildfires@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/Example/Assets.xcassets/wildfires.imageset/wildfires@3x.png -------------------------------------------------------------------------------- /Example/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 | -------------------------------------------------------------------------------- /Example/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 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 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 | 254 | 255 | 256 | 257 | 258 | 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 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | -------------------------------------------------------------------------------- /Example/CustomViewController.swift: -------------------------------------------------------------------------------- 1 | // CustomViewController.swift 2 | // 3 | // Copyright (c) 2019 Maxime Epain 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | import UIKit 24 | import MXSegmentedControl 25 | 26 | class CustomViewController: UIViewController { 27 | 28 | @IBOutlet weak var segmentedControl1: MXSegmentedControl! 29 | @IBOutlet weak var segmentedControl2: MXSegmentedControl! 30 | @IBOutlet weak var segmentedControl3: MXSegmentedControl! 31 | @IBOutlet weak var scrollView: UIScrollView! 32 | 33 | override func viewDidLoad() { 34 | super.viewDidLoad() 35 | 36 | segmentedControl1.append(title: "First") 37 | segmentedControl1.append(title: "Second") 38 | segmentedControl1.append(title: "Third") 39 | segmentedControl1.append(title: "Fourth") 40 | segmentedControl1.append(title: "Fifth") 41 | segmentedControl1.append(title: "Sixth") 42 | segmentedControl1.append(title: "Seven") 43 | segmentedControl1.append(title: "Eight") 44 | segmentedControl1.append(title: "Nine") 45 | segmentedControl1.animation.duration = 0 46 | 47 | segmentedControl2.append(title: "First") 48 | segmentedControl2.append(title: "Second") 49 | 50 | segmentedControl3.append(title: "First") 51 | .set(titleColor: #colorLiteral(red: 0.9981629252, green: 0.6096215844, blue: 0.6670924425, alpha: 1), for: .selected) 52 | segmentedControl3.append(title: "Second") 53 | .set(titleColor: #colorLiteral(red: 1, green: 0.7996580601, blue: 0.4006214142, alpha: 1), for: .selected) 54 | segmentedControl3.append(title: "Third") 55 | .set(titleColor: #colorLiteral(red: 0.9995033145, green: 0.9875727296, blue: 0.4039593935, alpha: 1), for: .selected) 56 | segmentedControl3.append(title: "Fourth") 57 | .set(titleColor: #colorLiteral(red: 0.8013994098, green: 0.9842005372, blue: 0.4007700682, alpha: 1), for: .selected) 58 | segmentedControl3.append(title: "Fifth") 59 | .set(titleColor: #colorLiteral(red: 0.4016934037, green: 0.9801344275, blue: 0.3977853954, alpha: 1), for: .selected) 60 | 61 | segmentedControl3.indicator.boxView.alpha = 0.1 62 | 63 | segmentedControl3.addTarget(self, action: #selector(changeIndex(segmentedControl:)), for: .valueChanged) 64 | } 65 | 66 | @objc func changeIndex(segmentedControl: MXSegmentedControl) { 67 | 68 | if let segment = segmentedControl.segment(at: segmentedControl.selectedIndex) { 69 | segmentedControl.indicator.boxView.backgroundColor = segment.titleColor(for: .selected) 70 | segmentedControl.indicator.lineView.backgroundColor = segment.titleColor(for: .selected) 71 | } 72 | } 73 | 74 | } 75 | 76 | -------------------------------------------------------------------------------- /Example/HMSegmentedControlViewController.swift: -------------------------------------------------------------------------------- 1 | // HMSegmentedControlViewController.swift 2 | // 3 | // Copyright (c) 2019 Maxime Epain 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | import UIKit 24 | import MXSegmentedControl 25 | 26 | class HMSegmentedControlViewController: UIViewController { 27 | 28 | @IBOutlet weak var segmentControl1: MXSegmentedControl! 29 | @IBOutlet weak var segmentControl2: MXSegmentedControl! 30 | @IBOutlet weak var segmentControl3: MXSegmentedControl! 31 | @IBOutlet weak var segmentControl4: MXSegmentedControl! 32 | @IBOutlet weak var segmentControl5: MXSegmentedControl! 33 | 34 | override func viewDidLoad() { 35 | super.viewDidLoad() 36 | 37 | segmentControl1.append(title: "Trending") 38 | segmentControl1.append(title: "News") 39 | segmentControl1.append(title: "Library") 40 | segmentControl1.indicator.linePosition = .top 41 | 42 | segmentControl2.append(title: "One") 43 | segmentControl2.append(title: "Two") 44 | segmentControl2.append(title: "Three") 45 | segmentControl2.append(title: "Four") 46 | segmentControl2.append(title: "Five") 47 | segmentControl2.append(title: "Six") 48 | segmentControl2.append(title: "Seven") 49 | segmentControl2.append(title: "Eight") 50 | segmentControl2.segmentEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 10, right: 10) 51 | 52 | segmentControl3.append(image: #imageLiteral(resourceName: "1")) 53 | .set(image: #imageLiteral(resourceName: "1-selected"), for: .selected) 54 | .set(image: .left) 55 | .imageView?.contentMode = .scaleAspectFit 56 | segmentControl3.append(image: #imageLiteral(resourceName: "2")) 57 | .set(image: #imageLiteral(resourceName: "2-selected"), for: .selected) 58 | .set(image: .left) 59 | .imageView?.contentMode = .scaleAspectFit 60 | segmentControl3.append(image: #imageLiteral(resourceName: "3")) 61 | .set(image: #imageLiteral(resourceName: "3-selected"), for: .selected) 62 | .set(image: .left) 63 | .imageView?.contentMode = .scaleAspectFit 64 | segmentControl3.append(image: #imageLiteral(resourceName: "4")) 65 | .set(image: #imageLiteral(resourceName: "4-selected"), for: .selected) 66 | .set(image: .left) 67 | .imageView?.contentMode = .scaleAspectFit 68 | 69 | segmentControl4.append(title: "One") 70 | segmentControl4.append(title: "Two") 71 | segmentControl4.append(title: "Three") 72 | segmentControl4.append(title: "4") 73 | segmentControl4.append(title: "Five") 74 | 75 | segmentControl5.append(title: "Worldwide") 76 | segmentControl5.append(title: "Local") 77 | segmentControl5.append(title: "Headlines") 78 | segmentControl5.indicator.linePosition = .top 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /Example/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 | -------------------------------------------------------------------------------- /Example/SegmentioViewController.swift: -------------------------------------------------------------------------------- 1 | // SegmentioViewController.swift 2 | // 3 | // Copyright (c) 2019 Maxime Epain 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | import UIKit 24 | import MXSegmentedControl 25 | 26 | class SegmentioViewController: UIViewController { 27 | 28 | @IBOutlet weak var segmentControl1: MXSegmentedControl! 29 | @IBOutlet weak var segmentControl2: MXSegmentedControl! 30 | @IBOutlet weak var segmentControl3: MXSegmentedControl! 31 | @IBOutlet weak var segmentControl4: MXSegmentedControl! 32 | @IBOutlet weak var segmentControl5: MXSegmentedControl! 33 | @IBOutlet weak var segmentControl6: MXSegmentedControl! 34 | 35 | override func viewDidLoad() { 36 | super.viewDidLoad() 37 | 38 | segmentControl1.append(title: "Tornado") 39 | segmentControl1.append(title: "Earthquakes") 40 | segmentControl1.append(title: "Extreme heat") 41 | segmentControl1.append(title: "Eruption") 42 | segmentControl1.append(title: "Floods") 43 | segmentControl1.append(title: "Wildfires") 44 | 45 | segmentControl2.append(image: #imageLiteral(resourceName: "tornado")) 46 | segmentControl2.append(image: #imageLiteral(resourceName: "earthquakes")) 47 | segmentControl2.append(image: #imageLiteral(resourceName: "heat")) 48 | segmentControl2.append(image: #imageLiteral(resourceName: "eruption")) 49 | segmentControl2.append(image: #imageLiteral(resourceName: "floods")) 50 | segmentControl2.append(image: #imageLiteral(resourceName: "wildfires")) 51 | segmentControl2.segmentWidth = 128 52 | 53 | segmentControl3.append(title: "Tornado") 54 | .set(image: #imageLiteral(resourceName: "tornado")) 55 | .set(image: .top) 56 | .set(padding: 16) 57 | segmentControl3.append(title: "Earthquakes") 58 | .set(image: #imageLiteral(resourceName: "earthquakes")) 59 | .set(image: .top) 60 | .set(padding: 16) 61 | segmentControl3.append(title: "Extreme heat") 62 | .set(image: #imageLiteral(resourceName: "heat")) 63 | .set(image: .top) 64 | .set(padding: 16) 65 | segmentControl3.append(title: "Eruption") 66 | .set(image: #imageLiteral(resourceName: "eruption")) 67 | .set(image: .top) 68 | .set(padding: 16) 69 | segmentControl3.append(title: "Floods") 70 | .set(image: #imageLiteral(resourceName: "floods")) 71 | .set(image: .top) 72 | .set(padding: 16) 73 | segmentControl3.append(title: "Wildfires") 74 | .set(image: #imageLiteral(resourceName: "wildfires")) 75 | .set(image: .top) 76 | .set(padding: 16) 77 | 78 | segmentControl4.append(title: "Tornado") 79 | .set(image: #imageLiteral(resourceName: "tornado")) 80 | .set(image: .bottom) 81 | .set(padding: 16) 82 | segmentControl4.append(title: "Earthquakes") 83 | .set(image: #imageLiteral(resourceName: "earthquakes")) 84 | .set(image: .top) 85 | .set(padding: 16) 86 | segmentControl4.append(title: "Extreme heat") 87 | .set(image: #imageLiteral(resourceName: "heat")) 88 | .set(image: .right) 89 | segmentControl4.append(title: "Eruption") 90 | .set(image: #imageLiteral(resourceName: "eruption")) 91 | .set(image: .left) 92 | segmentControl4.append(title: "Floods") 93 | .set(image: #imageLiteral(resourceName: "floods")) 94 | .set(image: .bottom) 95 | .set(padding: 16) 96 | segmentControl4.append(title: "Wildfires") 97 | .set(image: #imageLiteral(resourceName: "wildfires")) 98 | .set(image: .top) 99 | .set(padding: 16) 100 | } 101 | 102 | } 103 | 104 | extension UIFont { 105 | 106 | class func exampleAvenirMedium(ofSize size: CGFloat) -> UIFont { 107 | return UIFont(name: "Avenir-Book", size: size) ?? UIFont.systemFont(ofSize: size) 108 | } 109 | 110 | class func exampleAvenirLight(ofSize size: CGFloat) -> UIFont { 111 | return UIFont(name: "Avenir-Light", size: size) ?? UIFont.systemFont(ofSize: size) 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /Example/TopBarViewController.swift: -------------------------------------------------------------------------------- 1 | // ViewController.swift 2 | // 3 | // Copyright (c) 2019 Maxime Epain 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | import UIKit 24 | import MXSegmentedControl 25 | 26 | class TopBarViewController: UIViewController { 27 | 28 | @IBOutlet weak var segmentedControl: MXSegmentedControl! 29 | 30 | override func viewDidLoad() { 31 | super.viewDidLoad() 32 | 33 | segmentedControl.append(title: "Segmentio") 34 | .set(titleColor: #colorLiteral(red: 0.08598647267, green: 0.093843095, blue: 0.1104642078, alpha: 1), for: .selected) 35 | segmentedControl.append(title: "HMSegmentedControl") 36 | .set(titleColor: #colorLiteral(red: 0.08598647267, green: 0.093843095, blue: 0.1104642078, alpha: 1), for: .selected) 37 | segmentedControl.append(title: "Other") 38 | .set(titleColor: #colorLiteral(red: 0.9098039269, green: 0.4784313738, blue: 0.6431372762, alpha: 1), for: .selected) 39 | 40 | segmentedControl.indicator.lineView.backgroundColor = #colorLiteral(red: 0.9568627451, green: 0.4392156863, blue: 0.3803921569, alpha: 1) 41 | segmentedControl.addTarget(self, action: #selector(changeIndex(segmentedControl:)), for: .valueChanged) 42 | 43 | navigationItem.titleView = segmentedControl 44 | } 45 | 46 | @objc func changeIndex(segmentedControl: MXSegmentedControl) { 47 | 48 | switch segmentedControl.selectedIndex { 49 | case 0: 50 | segmentedControl.indicator.lineView.backgroundColor = #colorLiteral(red: 0.9568627451, green: 0.4392156863, blue: 0.3803921569, alpha: 1) 51 | case 1: 52 | segmentedControl.indicator.lineView.backgroundColor = #colorLiteral(red: 0.2044631541, green: 0.7111002803, blue: 0.898917675, alpha: 1) 53 | case 2: 54 | segmentedControl.indicator.lineView.backgroundColor = #colorLiteral(red: 0.9098039269, green: 0.4784313738, blue: 0.6431372762, alpha: 1) 55 | default: 56 | break 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Maxime Epain 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /MXSegmentedControl.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "MXSegmentedControl" 3 | s.version = "1.1.0" 4 | s.summary = "A very customizable segmented control for iOS." 5 | s.description = <<-DESC 6 | MXSegmentedControl is a segmented control framework for iOS: 7 | - Very customizable UI. 8 | - Supports storyboard design. 9 | - Can be linked with a scroll view. 10 | DESC 11 | 12 | s.homepage = "https://github.com/maxep/MXSegmentedControl" 13 | s.license = 'MIT' 14 | s.author = { "Maxime Epain" => "maxime.epain@gmail.com" } 15 | s.source = { :git => "https://github.com/maxep/MXSegmentedControl.git", :tag => s.version.to_s } 16 | s.social_media_url = 'https://twitter.com/MaximeEpain' 17 | 18 | s.platform = :ios, '8.0' 19 | s.requires_arc = true 20 | s.swift_version = '4.2' 21 | 22 | s.source_files = 'MXSegmentedControl/*.swift' 23 | 24 | end 25 | -------------------------------------------------------------------------------- /MXSegmentedControl.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 9C801BC31E72BD9200E75652 /* MXSegmentedControl.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C801BC11E72BD9200E75652 /* MXSegmentedControl.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 9C801BCA1E72BDDC00E75652 /* MXSegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C801BC91E72BDDC00E75652 /* MXSegmentedControl.swift */; }; 12 | 9C801BD41E72F96800E75652 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C801BD31E72F96800E75652 /* AppDelegate.swift */; }; 13 | 9C801BD61E72F96800E75652 /* CustomViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C801BD51E72F96800E75652 /* CustomViewController.swift */; }; 14 | 9C801BD91E72F96800E75652 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9C801BD71E72F96800E75652 /* Main.storyboard */; }; 15 | 9C801BDB1E72F96800E75652 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9C801BDA1E72F96800E75652 /* Assets.xcassets */; }; 16 | 9C801BDE1E72F96800E75652 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9C801BDC1E72F96800E75652 /* LaunchScreen.storyboard */; }; 17 | 9C801BE31E72F9AA00E75652 /* MXSegmentedControl.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9C801BBE1E72BD9200E75652 /* MXSegmentedControl.framework */; }; 18 | 9C801BE41E72F9AA00E75652 /* MXSegmentedControl.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9C801BBE1E72BD9200E75652 /* MXSegmentedControl.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 19 | 9C8C7DA21F32124000560FF8 /* TopBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C8C7DA11F32124000560FF8 /* TopBarViewController.swift */; }; 20 | 9C8C7DA41F33762E00560FF8 /* SegmentioViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C8C7DA31F33762E00560FF8 /* SegmentioViewController.swift */; }; 21 | 9CCDC53D1FB35C010073B7A9 /* HMSegmentedControlViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CCDC53C1FB35C010073B7A9 /* HMSegmentedControlViewController.swift */; }; 22 | 9CF63C2D1E7C01EE00C56E1B /* MXSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CF63C2C1E7C01EE00C56E1B /* MXSegment.swift */; }; 23 | 9CF63C301E7C023200C56E1B /* MXIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CF63C2F1E7C023200C56E1B /* MXIndicator.swift */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXContainerItemProxy section */ 27 | 9C801BE51E72F9AA00E75652 /* PBXContainerItemProxy */ = { 28 | isa = PBXContainerItemProxy; 29 | containerPortal = 9C801BB51E72BD9200E75652 /* Project object */; 30 | proxyType = 1; 31 | remoteGlobalIDString = 9C801BBD1E72BD9200E75652; 32 | remoteInfo = MXSegmentedControl; 33 | }; 34 | /* End PBXContainerItemProxy section */ 35 | 36 | /* Begin PBXCopyFilesBuildPhase section */ 37 | 9C801BE71E72F9AA00E75652 /* Embed Frameworks */ = { 38 | isa = PBXCopyFilesBuildPhase; 39 | buildActionMask = 2147483647; 40 | dstPath = ""; 41 | dstSubfolderSpec = 10; 42 | files = ( 43 | 9C801BE41E72F9AA00E75652 /* MXSegmentedControl.framework in Embed Frameworks */, 44 | ); 45 | name = "Embed Frameworks"; 46 | runOnlyForDeploymentPostprocessing = 0; 47 | }; 48 | /* End PBXCopyFilesBuildPhase section */ 49 | 50 | /* Begin PBXFileReference section */ 51 | 9C801BBE1E72BD9200E75652 /* MXSegmentedControl.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MXSegmentedControl.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | 9C801BC11E72BD9200E75652 /* MXSegmentedControl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXSegmentedControl.h; sourceTree = ""; }; 53 | 9C801BC21E72BD9200E75652 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 54 | 9C801BC91E72BDDC00E75652 /* MXSegmentedControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXSegmentedControl.swift; sourceTree = ""; }; 55 | 9C801BD11E72F96800E75652 /* MXSegmentedControl Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "MXSegmentedControl Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | 9C801BD31E72F96800E75652 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 57 | 9C801BD51E72F96800E75652 /* CustomViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomViewController.swift; sourceTree = ""; }; 58 | 9C801BD81E72F96800E75652 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 59 | 9C801BDA1E72F96800E75652 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 60 | 9C801BDD1E72F96800E75652 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 61 | 9C801BDF1E72F96800E75652 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 62 | 9C857EA61FB4B0C100D44719 /* screencast.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = screencast.gif; sourceTree = ""; }; 63 | 9C8C7DA11F32124000560FF8 /* TopBarViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TopBarViewController.swift; sourceTree = ""; }; 64 | 9C8C7DA31F33762E00560FF8 /* SegmentioViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SegmentioViewController.swift; sourceTree = ""; }; 65 | 9C9BD41A1E787F0800485B8E /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 66 | 9C9BD41B1E787F0800485B8E /* MXSegmentedControl.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = MXSegmentedControl.podspec; sourceTree = ""; }; 67 | 9C9BD41C1E787F0800485B8E /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 68 | 9C9BD41D1E787FD800485B8E /* .travis.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = .travis.yml; sourceTree = ""; }; 69 | 9CCDC53C1FB35C010073B7A9 /* HMSegmentedControlViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HMSegmentedControlViewController.swift; sourceTree = ""; }; 70 | 9CF63C2C1E7C01EE00C56E1B /* MXSegment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXSegment.swift; sourceTree = ""; }; 71 | 9CF63C2F1E7C023200C56E1B /* MXIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXIndicator.swift; sourceTree = ""; }; 72 | /* End PBXFileReference section */ 73 | 74 | /* Begin PBXFrameworksBuildPhase section */ 75 | 9C801BBA1E72BD9200E75652 /* Frameworks */ = { 76 | isa = PBXFrameworksBuildPhase; 77 | buildActionMask = 2147483647; 78 | files = ( 79 | ); 80 | runOnlyForDeploymentPostprocessing = 0; 81 | }; 82 | 9C801BCE1E72F96800E75652 /* Frameworks */ = { 83 | isa = PBXFrameworksBuildPhase; 84 | buildActionMask = 2147483647; 85 | files = ( 86 | 9C801BE31E72F9AA00E75652 /* MXSegmentedControl.framework in Frameworks */, 87 | ); 88 | runOnlyForDeploymentPostprocessing = 0; 89 | }; 90 | /* End PBXFrameworksBuildPhase section */ 91 | 92 | /* Begin PBXGroup section */ 93 | 9C801BB41E72BD9200E75652 = { 94 | isa = PBXGroup; 95 | children = ( 96 | 9C9BD4191E787EF200485B8E /* Metadata */, 97 | 9C801BC01E72BD9200E75652 /* MXSegmentedControl */, 98 | 9C801BD21E72F96800E75652 /* Example */, 99 | 9C801BBF1E72BD9200E75652 /* Products */, 100 | ); 101 | sourceTree = ""; 102 | }; 103 | 9C801BBF1E72BD9200E75652 /* Products */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 9C801BBE1E72BD9200E75652 /* MXSegmentedControl.framework */, 107 | 9C801BD11E72F96800E75652 /* MXSegmentedControl Example.app */, 108 | ); 109 | name = Products; 110 | sourceTree = ""; 111 | }; 112 | 9C801BC01E72BD9200E75652 /* MXSegmentedControl */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | 9C801BC11E72BD9200E75652 /* MXSegmentedControl.h */, 116 | 9C801BC21E72BD9200E75652 /* Info.plist */, 117 | 9C801BC91E72BDDC00E75652 /* MXSegmentedControl.swift */, 118 | 9CF63C2C1E7C01EE00C56E1B /* MXSegment.swift */, 119 | 9CF63C2F1E7C023200C56E1B /* MXIndicator.swift */, 120 | ); 121 | path = MXSegmentedControl; 122 | sourceTree = ""; 123 | }; 124 | 9C801BD21E72F96800E75652 /* Example */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 9C801BD31E72F96800E75652 /* AppDelegate.swift */, 128 | 9C8C7DA11F32124000560FF8 /* TopBarViewController.swift */, 129 | 9C8C7DA31F33762E00560FF8 /* SegmentioViewController.swift */, 130 | 9CCDC53C1FB35C010073B7A9 /* HMSegmentedControlViewController.swift */, 131 | 9C801BD51E72F96800E75652 /* CustomViewController.swift */, 132 | 9C801BD71E72F96800E75652 /* Main.storyboard */, 133 | 9C801BDA1E72F96800E75652 /* Assets.xcassets */, 134 | 9C801BDC1E72F96800E75652 /* LaunchScreen.storyboard */, 135 | 9C801BDF1E72F96800E75652 /* Info.plist */, 136 | ); 137 | path = Example; 138 | sourceTree = ""; 139 | }; 140 | 9C9BD4191E787EF200485B8E /* Metadata */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | 9C9BD41C1E787F0800485B8E /* README.md */, 144 | 9C9BD41B1E787F0800485B8E /* MXSegmentedControl.podspec */, 145 | 9C9BD41D1E787FD800485B8E /* .travis.yml */, 146 | 9C9BD41A1E787F0800485B8E /* LICENSE */, 147 | 9C857EA61FB4B0C100D44719 /* screencast.gif */, 148 | ); 149 | name = Metadata; 150 | sourceTree = ""; 151 | }; 152 | /* End PBXGroup section */ 153 | 154 | /* Begin PBXHeadersBuildPhase section */ 155 | 9C801BBB1E72BD9200E75652 /* Headers */ = { 156 | isa = PBXHeadersBuildPhase; 157 | buildActionMask = 2147483647; 158 | files = ( 159 | 9C801BC31E72BD9200E75652 /* MXSegmentedControl.h in Headers */, 160 | ); 161 | runOnlyForDeploymentPostprocessing = 0; 162 | }; 163 | /* End PBXHeadersBuildPhase section */ 164 | 165 | /* Begin PBXNativeTarget section */ 166 | 9C801BBD1E72BD9200E75652 /* MXSegmentedControl */ = { 167 | isa = PBXNativeTarget; 168 | buildConfigurationList = 9C801BC61E72BD9200E75652 /* Build configuration list for PBXNativeTarget "MXSegmentedControl" */; 169 | buildPhases = ( 170 | 9C801BB91E72BD9200E75652 /* Sources */, 171 | 9C801BBA1E72BD9200E75652 /* Frameworks */, 172 | 9C801BBB1E72BD9200E75652 /* Headers */, 173 | 9C801BBC1E72BD9200E75652 /* Resources */, 174 | ); 175 | buildRules = ( 176 | ); 177 | dependencies = ( 178 | ); 179 | name = MXSegmentedControl; 180 | productName = MXSegmentedControl; 181 | productReference = 9C801BBE1E72BD9200E75652 /* MXSegmentedControl.framework */; 182 | productType = "com.apple.product-type.framework"; 183 | }; 184 | 9C801BD01E72F96800E75652 /* Example */ = { 185 | isa = PBXNativeTarget; 186 | buildConfigurationList = 9C801BE01E72F96800E75652 /* Build configuration list for PBXNativeTarget "Example" */; 187 | buildPhases = ( 188 | 9C801BCD1E72F96800E75652 /* Sources */, 189 | 9C801BCE1E72F96800E75652 /* Frameworks */, 190 | 9C801BCF1E72F96800E75652 /* Resources */, 191 | 9C801BE71E72F9AA00E75652 /* Embed Frameworks */, 192 | ); 193 | buildRules = ( 194 | ); 195 | dependencies = ( 196 | 9C801BE61E72F9AA00E75652 /* PBXTargetDependency */, 197 | ); 198 | name = Example; 199 | productName = Example; 200 | productReference = 9C801BD11E72F96800E75652 /* MXSegmentedControl Example.app */; 201 | productType = "com.apple.product-type.application"; 202 | }; 203 | /* End PBXNativeTarget section */ 204 | 205 | /* Begin PBXProject section */ 206 | 9C801BB51E72BD9200E75652 /* Project object */ = { 207 | isa = PBXProject; 208 | attributes = { 209 | LastSwiftUpdateCheck = 0820; 210 | LastUpgradeCheck = 1010; 211 | ORGANIZATIONNAME = "Maxime Epain"; 212 | TargetAttributes = { 213 | 9C801BBD1E72BD9200E75652 = { 214 | CreatedOnToolsVersion = 8.2; 215 | DevelopmentTeam = C75GEDTP26; 216 | LastSwiftMigration = 0900; 217 | ProvisioningStyle = Automatic; 218 | }; 219 | 9C801BD01E72F96800E75652 = { 220 | CreatedOnToolsVersion = 8.2; 221 | DevelopmentTeam = C75GEDTP26; 222 | LastSwiftMigration = 0900; 223 | ProvisioningStyle = Automatic; 224 | }; 225 | }; 226 | }; 227 | buildConfigurationList = 9C801BB81E72BD9200E75652 /* Build configuration list for PBXProject "MXSegmentedControl" */; 228 | compatibilityVersion = "Xcode 3.2"; 229 | developmentRegion = English; 230 | hasScannedForEncodings = 0; 231 | knownRegions = ( 232 | English, 233 | en, 234 | Base, 235 | ); 236 | mainGroup = 9C801BB41E72BD9200E75652; 237 | productRefGroup = 9C801BBF1E72BD9200E75652 /* Products */; 238 | projectDirPath = ""; 239 | projectRoot = ""; 240 | targets = ( 241 | 9C801BBD1E72BD9200E75652 /* MXSegmentedControl */, 242 | 9C801BD01E72F96800E75652 /* Example */, 243 | ); 244 | }; 245 | /* End PBXProject section */ 246 | 247 | /* Begin PBXResourcesBuildPhase section */ 248 | 9C801BBC1E72BD9200E75652 /* Resources */ = { 249 | isa = PBXResourcesBuildPhase; 250 | buildActionMask = 2147483647; 251 | files = ( 252 | ); 253 | runOnlyForDeploymentPostprocessing = 0; 254 | }; 255 | 9C801BCF1E72F96800E75652 /* Resources */ = { 256 | isa = PBXResourcesBuildPhase; 257 | buildActionMask = 2147483647; 258 | files = ( 259 | 9C801BDE1E72F96800E75652 /* LaunchScreen.storyboard in Resources */, 260 | 9C801BDB1E72F96800E75652 /* Assets.xcassets in Resources */, 261 | 9C801BD91E72F96800E75652 /* Main.storyboard in Resources */, 262 | ); 263 | runOnlyForDeploymentPostprocessing = 0; 264 | }; 265 | /* End PBXResourcesBuildPhase section */ 266 | 267 | /* Begin PBXSourcesBuildPhase section */ 268 | 9C801BB91E72BD9200E75652 /* Sources */ = { 269 | isa = PBXSourcesBuildPhase; 270 | buildActionMask = 2147483647; 271 | files = ( 272 | 9CF63C2D1E7C01EE00C56E1B /* MXSegment.swift in Sources */, 273 | 9CF63C301E7C023200C56E1B /* MXIndicator.swift in Sources */, 274 | 9C801BCA1E72BDDC00E75652 /* MXSegmentedControl.swift in Sources */, 275 | ); 276 | runOnlyForDeploymentPostprocessing = 0; 277 | }; 278 | 9C801BCD1E72F96800E75652 /* Sources */ = { 279 | isa = PBXSourcesBuildPhase; 280 | buildActionMask = 2147483647; 281 | files = ( 282 | 9C801BD61E72F96800E75652 /* CustomViewController.swift in Sources */, 283 | 9C801BD41E72F96800E75652 /* AppDelegate.swift in Sources */, 284 | 9CCDC53D1FB35C010073B7A9 /* HMSegmentedControlViewController.swift in Sources */, 285 | 9C8C7DA41F33762E00560FF8 /* SegmentioViewController.swift in Sources */, 286 | 9C8C7DA21F32124000560FF8 /* TopBarViewController.swift in Sources */, 287 | ); 288 | runOnlyForDeploymentPostprocessing = 0; 289 | }; 290 | /* End PBXSourcesBuildPhase section */ 291 | 292 | /* Begin PBXTargetDependency section */ 293 | 9C801BE61E72F9AA00E75652 /* PBXTargetDependency */ = { 294 | isa = PBXTargetDependency; 295 | target = 9C801BBD1E72BD9200E75652 /* MXSegmentedControl */; 296 | targetProxy = 9C801BE51E72F9AA00E75652 /* PBXContainerItemProxy */; 297 | }; 298 | /* End PBXTargetDependency section */ 299 | 300 | /* Begin PBXVariantGroup section */ 301 | 9C801BD71E72F96800E75652 /* Main.storyboard */ = { 302 | isa = PBXVariantGroup; 303 | children = ( 304 | 9C801BD81E72F96800E75652 /* Base */, 305 | ); 306 | name = Main.storyboard; 307 | sourceTree = ""; 308 | }; 309 | 9C801BDC1E72F96800E75652 /* LaunchScreen.storyboard */ = { 310 | isa = PBXVariantGroup; 311 | children = ( 312 | 9C801BDD1E72F96800E75652 /* Base */, 313 | ); 314 | name = LaunchScreen.storyboard; 315 | sourceTree = ""; 316 | }; 317 | /* End PBXVariantGroup section */ 318 | 319 | /* Begin XCBuildConfiguration section */ 320 | 9C801BC41E72BD9200E75652 /* Debug */ = { 321 | isa = XCBuildConfiguration; 322 | buildSettings = { 323 | ALWAYS_SEARCH_USER_PATHS = NO; 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_UNREACHABLE_CODE = YES; 348 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 349 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 350 | COPY_PHASE_STRIP = NO; 351 | CURRENT_PROJECT_VERSION = 1; 352 | DEBUG_INFORMATION_FORMAT = dwarf; 353 | ENABLE_STRICT_OBJC_MSGSEND = YES; 354 | ENABLE_TESTABILITY = YES; 355 | GCC_C_LANGUAGE_STANDARD = gnu99; 356 | GCC_DYNAMIC_NO_PIC = NO; 357 | GCC_NO_COMMON_BLOCKS = YES; 358 | GCC_OPTIMIZATION_LEVEL = 0; 359 | GCC_PREPROCESSOR_DEFINITIONS = ( 360 | "DEBUG=1", 361 | "$(inherited)", 362 | ); 363 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 364 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 365 | GCC_WARN_UNDECLARED_SELECTOR = YES; 366 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 367 | GCC_WARN_UNUSED_FUNCTION = YES; 368 | GCC_WARN_UNUSED_VARIABLE = YES; 369 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 370 | MTL_ENABLE_DEBUG_INFO = YES; 371 | ONLY_ACTIVE_ARCH = YES; 372 | SDKROOT = iphoneos; 373 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 374 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 375 | TARGETED_DEVICE_FAMILY = "1,2"; 376 | VERSIONING_SYSTEM = "apple-generic"; 377 | VERSION_INFO_PREFIX = ""; 378 | }; 379 | name = Debug; 380 | }; 381 | 9C801BC51E72BD9200E75652 /* Release */ = { 382 | isa = XCBuildConfiguration; 383 | buildSettings = { 384 | ALWAYS_SEARCH_USER_PATHS = NO; 385 | CLANG_ANALYZER_NONNULL = YES; 386 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 387 | CLANG_CXX_LIBRARY = "libc++"; 388 | CLANG_ENABLE_MODULES = YES; 389 | CLANG_ENABLE_OBJC_ARC = YES; 390 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 391 | CLANG_WARN_BOOL_CONVERSION = YES; 392 | CLANG_WARN_COMMA = YES; 393 | CLANG_WARN_CONSTANT_CONVERSION = YES; 394 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 395 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 396 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 397 | CLANG_WARN_EMPTY_BODY = YES; 398 | CLANG_WARN_ENUM_CONVERSION = YES; 399 | CLANG_WARN_INFINITE_RECURSION = YES; 400 | CLANG_WARN_INT_CONVERSION = YES; 401 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 402 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 403 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 404 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 405 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 406 | CLANG_WARN_STRICT_PROTOTYPES = YES; 407 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 408 | CLANG_WARN_UNREACHABLE_CODE = YES; 409 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 410 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 411 | COPY_PHASE_STRIP = NO; 412 | CURRENT_PROJECT_VERSION = 1; 413 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 414 | ENABLE_NS_ASSERTIONS = NO; 415 | ENABLE_STRICT_OBJC_MSGSEND = YES; 416 | GCC_C_LANGUAGE_STANDARD = gnu99; 417 | GCC_NO_COMMON_BLOCKS = YES; 418 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 419 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 420 | GCC_WARN_UNDECLARED_SELECTOR = YES; 421 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 422 | GCC_WARN_UNUSED_FUNCTION = YES; 423 | GCC_WARN_UNUSED_VARIABLE = YES; 424 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 425 | MTL_ENABLE_DEBUG_INFO = NO; 426 | SDKROOT = iphoneos; 427 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 428 | TARGETED_DEVICE_FAMILY = "1,2"; 429 | VALIDATE_PRODUCT = YES; 430 | VERSIONING_SYSTEM = "apple-generic"; 431 | VERSION_INFO_PREFIX = ""; 432 | }; 433 | name = Release; 434 | }; 435 | 9C801BC71E72BD9200E75652 /* Debug */ = { 436 | isa = XCBuildConfiguration; 437 | buildSettings = { 438 | CLANG_ENABLE_MODULES = YES; 439 | CODE_SIGN_IDENTITY = ""; 440 | DEFINES_MODULE = YES; 441 | DEVELOPMENT_TEAM = C75GEDTP26; 442 | DYLIB_COMPATIBILITY_VERSION = 1; 443 | DYLIB_CURRENT_VERSION = 1; 444 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 445 | INFOPLIST_FILE = MXSegmentedControl/Info.plist; 446 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 447 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 448 | PRODUCT_BUNDLE_IDENTIFIER = fr.maxep.MXSegmentedControl; 449 | PRODUCT_NAME = "$(TARGET_NAME)"; 450 | SKIP_INSTALL = YES; 451 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 452 | SWIFT_VERSION = 4.2; 453 | }; 454 | name = Debug; 455 | }; 456 | 9C801BC81E72BD9200E75652 /* Release */ = { 457 | isa = XCBuildConfiguration; 458 | buildSettings = { 459 | CLANG_ENABLE_MODULES = YES; 460 | CODE_SIGN_IDENTITY = ""; 461 | DEFINES_MODULE = YES; 462 | DEVELOPMENT_TEAM = C75GEDTP26; 463 | DYLIB_COMPATIBILITY_VERSION = 1; 464 | DYLIB_CURRENT_VERSION = 1; 465 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 466 | INFOPLIST_FILE = MXSegmentedControl/Info.plist; 467 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 468 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 469 | PRODUCT_BUNDLE_IDENTIFIER = fr.maxep.MXSegmentedControl; 470 | PRODUCT_NAME = "$(TARGET_NAME)"; 471 | SKIP_INSTALL = YES; 472 | SWIFT_VERSION = 4.2; 473 | }; 474 | name = Release; 475 | }; 476 | 9C801BE11E72F96800E75652 /* Debug */ = { 477 | isa = XCBuildConfiguration; 478 | buildSettings = { 479 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 480 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 481 | DEVELOPMENT_TEAM = C75GEDTP26; 482 | INFOPLIST_FILE = Example/Info.plist; 483 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 484 | PRODUCT_BUNDLE_IDENTIFIER = fr.maxep.MXSegmentedControl.Example; 485 | PRODUCT_NAME = "MXSegmentedControl Example"; 486 | SWIFT_VERSION = 4.0; 487 | }; 488 | name = Debug; 489 | }; 490 | 9C801BE21E72F96800E75652 /* Release */ = { 491 | isa = XCBuildConfiguration; 492 | buildSettings = { 493 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 494 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 495 | DEVELOPMENT_TEAM = C75GEDTP26; 496 | INFOPLIST_FILE = Example/Info.plist; 497 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 498 | PRODUCT_BUNDLE_IDENTIFIER = fr.maxep.MXSegmentedControl.Example; 499 | PRODUCT_NAME = "MXSegmentedControl Example"; 500 | SWIFT_VERSION = 4.0; 501 | }; 502 | name = Release; 503 | }; 504 | /* End XCBuildConfiguration section */ 505 | 506 | /* Begin XCConfigurationList section */ 507 | 9C801BB81E72BD9200E75652 /* Build configuration list for PBXProject "MXSegmentedControl" */ = { 508 | isa = XCConfigurationList; 509 | buildConfigurations = ( 510 | 9C801BC41E72BD9200E75652 /* Debug */, 511 | 9C801BC51E72BD9200E75652 /* Release */, 512 | ); 513 | defaultConfigurationIsVisible = 0; 514 | defaultConfigurationName = Release; 515 | }; 516 | 9C801BC61E72BD9200E75652 /* Build configuration list for PBXNativeTarget "MXSegmentedControl" */ = { 517 | isa = XCConfigurationList; 518 | buildConfigurations = ( 519 | 9C801BC71E72BD9200E75652 /* Debug */, 520 | 9C801BC81E72BD9200E75652 /* Release */, 521 | ); 522 | defaultConfigurationIsVisible = 0; 523 | defaultConfigurationName = Release; 524 | }; 525 | 9C801BE01E72F96800E75652 /* Build configuration list for PBXNativeTarget "Example" */ = { 526 | isa = XCConfigurationList; 527 | buildConfigurations = ( 528 | 9C801BE11E72F96800E75652 /* Debug */, 529 | 9C801BE21E72F96800E75652 /* Release */, 530 | ); 531 | defaultConfigurationIsVisible = 0; 532 | defaultConfigurationName = Release; 533 | }; 534 | /* End XCConfigurationList section */ 535 | }; 536 | rootObject = 9C801BB51E72BD9200E75652 /* Project object */; 537 | } 538 | -------------------------------------------------------------------------------- /MXSegmentedControl.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MXSegmentedControl.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /MXSegmentedControl.xcodeproj/xcshareddata/xcschemes/MXSegmentedControl.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 | -------------------------------------------------------------------------------- /MXSegmentedControl/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.1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /MXSegmentedControl/MXIndicator.swift: -------------------------------------------------------------------------------- 1 | // MXIndicator.swift 2 | // 3 | // Copyright (c) 2019 Maxime Epain 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | import UIKit 24 | 25 | @objcMembers 26 | public class MXIndicator: UIView { 27 | 28 | /// The indicator line position 29 | /// 30 | /// - top: The line will be drawn at the top of the segemented control 31 | /// - bottom: The line will be drawn at the bottom of the segemented control 32 | public enum Position { 33 | case top 34 | case bottom 35 | } 36 | 37 | /// The indicator insets 38 | public var contentEdgeInsets = UIEdgeInsets.zero 39 | 40 | /// The indicator line view. 41 | public let lineView = UIView() 42 | 43 | public var linePosition = Position.bottom 44 | 45 | /// The indicator box view. 46 | public let boxView = UIView() 47 | 48 | /// The line height value. 49 | public var lineHeight: CGFloat = 1 50 | 51 | /// :nodoc: 52 | required public init?(coder aDecoder: NSCoder) { 53 | super.init(coder: aDecoder) 54 | initialize() 55 | } 56 | 57 | /// :nodoc: 58 | override public init(frame: CGRect) { 59 | super.init(frame: frame) 60 | initialize() 61 | } 62 | 63 | private func initialize() { 64 | 65 | lineView.backgroundColor = tintColor 66 | boxView.backgroundColor = tintColor 67 | boxView.alpha = 0 68 | 69 | addSubview(boxView) 70 | addSubview(lineView) 71 | } 72 | 73 | /// :nodoc: 74 | override public func layoutSubviews() { 75 | super.layoutSubviews() 76 | 77 | var frame = self.bounds //container.inset(by: margin) 78 | //UIEdgeInsetsInsetRect(bounds, contentEdgeInsets) 79 | 80 | if linePosition == .top { 81 | 82 | lineView.frame = CGRect(x: 0, y: 0, width: frame.width, height: lineHeight) 83 | 84 | frame.origin.y = lineHeight 85 | frame.size.height -= lineHeight 86 | boxView.frame = frame 87 | 88 | } else { 89 | frame.size.height -= lineHeight 90 | boxView.frame = frame 91 | 92 | frame.origin.y = frame.size.height 93 | frame.size.height = lineHeight 94 | lineView.frame = frame 95 | } 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /MXSegmentedControl/MXSegment.swift: -------------------------------------------------------------------------------- 1 | // MXSegment.swift 2 | // 3 | // Copyright (c) 2019 Maxime Epain 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | import UIKit 24 | 25 | /// A segment button embed in MXSegmentedControl 26 | @objcMembers 27 | public class MXSegment: UIButton { 28 | 29 | /// The Image position in segment related to title. 30 | /// 31 | /// - top: Image will be placed on top of the title. 32 | /// - left: Image will be placed at the left of the title. 33 | /// - bottom: Image will be placed below the title. 34 | /// - right: Image will be placed at the right of the title. 35 | public enum ImagePosition { 36 | case top 37 | case left 38 | case bottom 39 | case right 40 | } 41 | 42 | /// The image position related to the title. 43 | public var imagePosition = ImagePosition.top 44 | 45 | /// Padding between segment title and image 46 | public var padding: CGFloat = 8 47 | 48 | /// The segment width 49 | public var width: CGFloat { 50 | get { 51 | guard _width == UIView.noIntrinsicMetric else { return _width } 52 | return intrinsicContentSize.width 53 | } 54 | set { _width = newValue } 55 | } 56 | 57 | private var _width: CGFloat = UIView.noIntrinsicMetric 58 | 59 | @discardableResult public func set(width: CGFloat) -> MXSegment { 60 | self.width = width 61 | return self 62 | } 63 | 64 | @discardableResult public func set(image position: ImagePosition) -> MXSegment { 65 | self.imagePosition = position 66 | return self 67 | } 68 | 69 | @discardableResult public func set(padding: CGFloat) -> MXSegment { 70 | self.padding = padding 71 | return self 72 | } 73 | 74 | @discardableResult public func set(title: String?, for state: UIControl.State = .normal) -> MXSegment { 75 | super.setTitle(title, for: state) 76 | return self 77 | } 78 | 79 | @discardableResult public func set(titleColor: UIColor?, for state: UIControl.State = .normal) -> MXSegment { 80 | super.setTitleColor(titleColor, for: state) 81 | return self 82 | } 83 | 84 | @discardableResult public func set(image: UIImage?, for state: UIControl.State = .normal) -> MXSegment { 85 | super.setImage(image, for: state) 86 | return self 87 | } 88 | 89 | @discardableResult public func set(background image: UIImage?, for state: UIControl.State = .normal) -> MXSegment { 90 | super.setBackgroundImage(image, for: state) 91 | return self 92 | } 93 | 94 | @discardableResult public func set(attributedTitle: NSAttributedString?, for state: UIControl.State = .normal) -> MXSegment { 95 | super.setAttributedTitle(attributedTitle, for: state) 96 | return self 97 | } 98 | 99 | } 100 | 101 | // MARK: - Layouts 102 | 103 | extension MXSegment { 104 | 105 | /// :nodoc: 106 | public override func layoutSubviews() { 107 | super.layoutSubviews() 108 | 109 | guard let titleLabel = titleLabel, let imageView = imageView else { 110 | return 111 | } 112 | 113 | guard !titleLabel.frame.isEmpty, !imageView.frame.isEmpty else { 114 | return 115 | } 116 | 117 | switch imagePosition { 118 | case .top: 119 | layoutTop(imageView, titleLabel) 120 | case .left: 121 | layoutLeft(imageView, titleLabel) 122 | case .bottom: 123 | layoutBottom(imageView, titleLabel) 124 | case .right: 125 | layoutRight(imageView, titleLabel) 126 | } 127 | } 128 | 129 | /// :nodoc: 130 | public override var intrinsicContentSize: CGSize { 131 | let titleSize = titleLabel?.intrinsicContentSize 132 | let imageSize = imageView?.intrinsicContentSize 133 | 134 | var size = CGSize.zero 135 | 136 | switch imagePosition { 137 | case .top, .bottom: 138 | 139 | if let titleSize = titleSize { 140 | size.height += titleSize.height 141 | size.height += titleEdgeInsets.top 142 | size.height += titleEdgeInsets.bottom 143 | 144 | var width = titleSize.width 145 | width += titleEdgeInsets.left 146 | width += titleEdgeInsets.right 147 | 148 | size.width = width 149 | } 150 | 151 | if let imageSize = imageSize { 152 | size.height += imageSize.height 153 | size.height += imageEdgeInsets.top 154 | size.height += imageEdgeInsets.bottom 155 | 156 | var width = imageSize.width 157 | width += imageEdgeInsets.left 158 | width += imageEdgeInsets.right 159 | 160 | size.width = max(size.width, width) 161 | } 162 | 163 | if titleLabel != nil || imageView != nil { 164 | size.height += padding 165 | } 166 | 167 | case .left, .right: 168 | 169 | if let titleSize = titleSize { 170 | size.width += titleSize.width 171 | size.width += titleEdgeInsets.left 172 | size.width += titleEdgeInsets.right 173 | 174 | var height = titleSize.height 175 | height += titleEdgeInsets.top 176 | height += titleEdgeInsets.bottom 177 | 178 | size.height = height 179 | } 180 | 181 | if let imageSize = imageSize { 182 | size.width += imageSize.height 183 | size.width += imageEdgeInsets.left 184 | size.width += imageEdgeInsets.right 185 | 186 | var height = imageSize.height 187 | height += imageEdgeInsets.top 188 | height += imageEdgeInsets.bottom 189 | 190 | size.height = max(size.height, height) 191 | } 192 | 193 | if titleLabel != nil || imageView != nil { 194 | size.width += padding 195 | } 196 | } 197 | 198 | if titleLabel != nil || imageView != nil { 199 | size.width += contentEdgeInsets.left 200 | size.width += contentEdgeInsets.right 201 | 202 | size.height += contentEdgeInsets.top 203 | size.height += contentEdgeInsets.bottom 204 | } 205 | return size 206 | } 207 | 208 | private func layoutTop(_ imageView: UIImageView, _ titleLabel: UILabel) { 209 | let contentRect = self.contentRect(forBounds: bounds) 210 | let imageSize = imageView.intrinsicContentSize 211 | let titleSize = titleLabel.intrinsicContentSize 212 | 213 | // Compute Image View Frame 214 | var width = contentRect.width 215 | width -= imageEdgeInsets.left 216 | width -= imageEdgeInsets.right 217 | width = min(width, imageSize.width) 218 | 219 | var height = contentRect.height 220 | height -= imageEdgeInsets.top 221 | height -= imageEdgeInsets.bottom 222 | height -= titleEdgeInsets.top 223 | height -= titleEdgeInsets.bottom 224 | height -= titleSize.height 225 | height -= padding 226 | height = min(height, imageSize.height) 227 | 228 | var x = max(contentRect.minX + imageEdgeInsets.left, 229 | contentRect.midX - width / 2) 230 | var y = max(contentRect.minY + imageEdgeInsets.top, 231 | contentRect.midY - (height + titleSize.height + padding) / 2) 232 | 233 | imageView.frame = CGRect(x: x, y: y, width: width, height: height) 234 | // 235 | 236 | // Compute Title Label Frame 237 | x = max(contentRect.minX + titleEdgeInsets.left, 238 | contentRect.midX - titleSize.width / 2) 239 | y = imageView.frame.maxY 240 | y += imageEdgeInsets.bottom 241 | y += padding 242 | y += titleEdgeInsets.top 243 | 244 | width = contentRect.width 245 | width -= titleEdgeInsets.left 246 | width -= titleEdgeInsets.right 247 | width = min(width, titleSize.width) 248 | 249 | titleLabel.frame = CGRect(x: x, y: y, width: width, height: titleSize.height) 250 | // 251 | } 252 | 253 | private func layoutBottom(_ imageView: UIImageView, _ titleLabel: UILabel) { 254 | let contentRect = self.contentRect(forBounds: bounds) 255 | let imageSize = imageView.intrinsicContentSize 256 | let titleSize = titleLabel.intrinsicContentSize 257 | 258 | // Compute Title Label Frame 259 | var width = contentRect.width 260 | width -= titleEdgeInsets.left 261 | width -= titleEdgeInsets.right 262 | width = min(width, titleSize.width) 263 | 264 | var height = contentRect.height 265 | height -= imageEdgeInsets.top 266 | height -= imageEdgeInsets.bottom 267 | height -= titleEdgeInsets.top 268 | height -= titleEdgeInsets.bottom 269 | height -= titleSize.height 270 | height -= padding 271 | height = min(height, imageSize.height) 272 | 273 | var x = max(contentRect.minX + titleEdgeInsets.left, 274 | contentRect.midX - titleSize.width / 2) 275 | var y = max(contentRect.minY + titleEdgeInsets.top, 276 | contentRect.midY - (height + titleSize.height + padding) / 2) 277 | 278 | titleLabel.frame = CGRect(x: x, y: y, width: width, height: titleSize.height) 279 | // 280 | 281 | // Compute Image View Frame 282 | width = contentRect.width 283 | width -= imageEdgeInsets.left 284 | width -= imageEdgeInsets.right 285 | width = min(width, imageSize.width) 286 | 287 | x = max(contentRect.minX + imageEdgeInsets.left, 288 | contentRect.midX - width / 2) 289 | 290 | y = titleLabel.frame.maxY 291 | y += titleEdgeInsets.bottom 292 | y += padding 293 | y += imageEdgeInsets.top 294 | 295 | imageView.frame = CGRect(x: x, y: y, width: width, height: height) 296 | // 297 | } 298 | 299 | private func layoutLeft(_ imageView: UIImageView, _ titleLabel: UILabel) { 300 | let contentRect = self.contentRect(forBounds: bounds) 301 | let imageSize = imageView.intrinsicContentSize 302 | let titleSize = titleLabel.intrinsicContentSize 303 | 304 | // Compute Image View Frame 305 | var width = contentRect.width 306 | width -= imageEdgeInsets.left 307 | width -= imageEdgeInsets.right 308 | width -= titleEdgeInsets.left 309 | width -= titleEdgeInsets.right 310 | width -= padding 311 | width = min(width, imageSize.width) 312 | 313 | var height = contentRect.height 314 | height -= imageEdgeInsets.top 315 | height -= imageEdgeInsets.bottom 316 | height = min(height, imageSize.height) 317 | 318 | var x = max(contentRect.minX + imageEdgeInsets.left, 319 | contentRect.midX - (width + titleSize.width + padding) / 2) 320 | var y = max(contentRect.minY + imageEdgeInsets.top, 321 | contentRect.midY - height / 2) 322 | 323 | imageView.frame = CGRect(x: x, y: y, width: width, height: height) 324 | // 325 | 326 | // Compute Title Label Frame 327 | y = max(contentRect.minY + titleEdgeInsets.top, 328 | contentRect.midY - titleSize.height / 2) 329 | x = imageView.frame.maxX 330 | x += imageEdgeInsets.right 331 | x += padding 332 | x += titleEdgeInsets.left 333 | 334 | height = contentRect.height 335 | height -= titleEdgeInsets.top 336 | height -= titleEdgeInsets.bottom 337 | height = min(height, titleSize.height) 338 | 339 | width = contentRect.maxX 340 | width -= titleEdgeInsets.right 341 | width -= x 342 | 343 | titleLabel.frame = CGRect(x: x, y: y, width: width, height: height) 344 | // 345 | } 346 | 347 | private func layoutRight(_ imageView: UIImageView, _ titleLabel: UILabel) { 348 | let contentRect = self.contentRect(forBounds: bounds) 349 | let imageSize = imageView.intrinsicContentSize 350 | let titleSize = titleLabel.intrinsicContentSize 351 | 352 | // Compute Title Label Frame 353 | var width = contentRect.width 354 | width -= imageEdgeInsets.left 355 | width -= imageEdgeInsets.right 356 | width -= titleEdgeInsets.left 357 | width -= titleEdgeInsets.right 358 | width -= padding 359 | width -= imageSize.width 360 | 361 | var height = contentRect.height 362 | height -= titleEdgeInsets.top 363 | height -= titleEdgeInsets.bottom 364 | height = min(height, titleSize.height) 365 | 366 | var x = max(contentRect.minX + titleEdgeInsets.left, 367 | contentRect.midX - (imageSize.width + width + padding) / 2) 368 | var y = max(contentRect.minY + titleEdgeInsets.top, 369 | contentRect.midY - height / 2) 370 | 371 | titleLabel.frame = CGRect(x: x, y: y, width: width, height: height) 372 | // 373 | 374 | // Compute Image View Frame 375 | x += width 376 | x += titleEdgeInsets.right 377 | x += padding 378 | x += imageEdgeInsets.left 379 | 380 | y = max(contentRect.minY + imageEdgeInsets.top, 381 | contentRect.midY - imageSize.height / 2) 382 | 383 | width = contentRect.maxX 384 | width -= imageEdgeInsets.left 385 | width -= imageEdgeInsets.right 386 | width = min(width, imageSize.width) 387 | 388 | height = contentRect.height 389 | height -= imageEdgeInsets.top 390 | height -= imageEdgeInsets.bottom 391 | height = min(height, imageSize.height) 392 | 393 | imageView.frame = CGRect(x: x, y: y, width: width, height: height) 394 | // 395 | } 396 | 397 | } 398 | -------------------------------------------------------------------------------- /MXSegmentedControl/MXSegmentedControl.h: -------------------------------------------------------------------------------- 1 | // MXSegmentedControl.h 2 | // 3 | // Copyright (c) 2019 Maxime Epain 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | //! Project version number for MXSegmentedControl. 26 | FOUNDATION_EXPORT double MXSegmentedControlVersionNumber; 27 | 28 | //! Project version string for MXSegmentedControl. 29 | FOUNDATION_EXPORT const unsigned char MXSegmentedControlVersionString[]; 30 | 31 | // In this header, you should import all the public headers of your framework using statements like #import 32 | 33 | 34 | -------------------------------------------------------------------------------- /MXSegmentedControl/MXSegmentedControl.swift: -------------------------------------------------------------------------------- 1 | // MXSegmentedControl.swift 2 | // 3 | // Copyright (c) 2019 Maxime Epain 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 13 | // all 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 21 | // THE SOFTWARE. 22 | 23 | import UIKit 24 | 25 | /// A MXSegmentedControl object is a horizontal control made of multiple segments, each segment functioning as a button. An indicator shows the current position of the selected segment. 26 | /// A segmented control can display a title (an NSString object), an attributed title (NSAttributedString object) and/or an image (UIImage object). 27 | /// The MXSegmentedControl object automatically resizes segments to fit proportionally within their superview unless they have a specific width set. 28 | /// 29 | /// You can connect a UIScrollView using the `scrollView` property to make the indicator move with the scroll view's contentOffset. 30 | /// 31 | /// You register the target-action methods for a segmented control using the value​Changed constant as shown below. 32 | /// 33 | /// segmentedControl.addTarget(self, action: "action:", forControlEvents: .ValueChanged) 34 | @IBDesignable 35 | @objcMembers 36 | open class MXSegmentedControl: UIControl { 37 | 38 | // MARK: Customizing Appearance 39 | 40 | /// A Boolean value that controls whether the segmented control bounces past the edge of content and back again. 41 | /// If the value of this property is true, the segmented control bounces when it encounters a boundary of the content. Bouncing visually indicates that control has reached an edge of the content. If the value is false, scrolling stops immediately at the content boundary without bouncing. The default value is true. 42 | @IBInspectable public dynamic var bounces: Bool { 43 | get { return _scrollView.bounces } 44 | set { _scrollView.bounces = newValue } 45 | } 46 | 47 | /// The font of the segments. 48 | /// If you are using styled text in iOS 6 or later, assigning a new value to this property causes the font to be applied to the entirety of the string in the attributedText property. If you want to apply the font to only a portion of the text, create a new attributed string with the desired style information and associate it with the segment. If you are not using styled text, this property applies to the entire segments string in the title property. 49 | /// The default value for this property is the system font at a size of 17 points (using the systemFont(ofSize:) class method of UIFont). 50 | @IBInspectable public dynamic var font = UIFont.systemFont(ofSize: 17) { 51 | didSet { 52 | for segment in contentView.segments { 53 | segment.titleLabel?.font = font 54 | } 55 | setNeedsLayout() 56 | } 57 | } 58 | 59 | /// Sets the color of the segments title to use for the normal state. 60 | @IBInspectable public dynamic var textColor: UIColor = .lightGray { 61 | didSet { 62 | for segment in contentView.segments { 63 | segment.setTitleColor(textColor, for: .normal) 64 | } 65 | } 66 | } 67 | 68 | /// Sets the color of the segments title to use for the selected state. 69 | @IBInspectable public dynamic var selectedTextColor: UIColor? { 70 | didSet { 71 | for segment in contentView.segments { 72 | segment.setTitleColor(selectedTextColor, for: .selected) 73 | } 74 | } 75 | } 76 | 77 | /// Sets the width of the segments. 78 | @IBInspectable public var segmentWidth: CGFloat = 0 { 79 | didSet { 80 | for segment in contentView.segments { 81 | segment.width = segmentWidth 82 | } 83 | setNeedsLayout() 84 | } 85 | } 86 | 87 | /// The segmented control content insets 88 | @IBInspectable public dynamic var contentEdgeInsets = UIEdgeInsets.zero 89 | 90 | /// The inset or outset margins for the rectangle surrounding all of the segments' content. 91 | /// Use this property to resize and reposition the effective drawing rectangle for the segments content. The content comprises the segment image and segment title. You can specify a different value for each of the four insets (top, left, bottom, right). A positive value shrinks, or insets, that edge—moving it closer to the center of the segment. A negative value expands, or outsets, that edge. Use the UIEdgeInsets(top:left:bottom:right:) function to construct a value for this property. The default value is UIEdgeInsetsZero. 92 | /// The segments uses this property to determine intrinsicContentSize and sizeThatFits(_:). 93 | @IBInspectable public dynamic var segmentEdgeInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8) { 94 | didSet { 95 | for segment in contentView.segments { 96 | segment.contentEdgeInsets = segmentEdgeInsets 97 | } 98 | setNeedsLayout() 99 | } 100 | } 101 | 102 | /// The segmented control's indicator. 103 | public let indicator = MXIndicator() 104 | 105 | /// The segmented control's separators. 106 | public internal(set) var separators: Separators { 107 | get { return contentView.separators } 108 | set { contentView.separators = newValue } 109 | } 110 | 111 | /// The indicator animation description. 112 | public var animation = Animation(duration: 0.25, delay: 0, dampingRatio: 1, velocity: 0, options: [.beginFromCurrentState, .allowUserInteraction]) 113 | 114 | /// The indicator progress. 115 | public var progress: CGFloat = 0 { 116 | didSet { layoutIndicator() } 117 | } 118 | 119 | /// The currently selected segment index. 120 | public private(set) var selectedIndex: Int = 0 { 121 | willSet { contentView.segments[selectedIndex].isSelected = false } 122 | didSet { 123 | sendActions(for: .valueChanged) 124 | contentView.segments[selectedIndex].isSelected = true 125 | } 126 | } 127 | 128 | // MARK: Initializers 129 | 130 | let _scrollView = UIScrollView() 131 | let contentView = ContentView() 132 | 133 | /// :nodoc: 134 | required public init?(coder aDecoder: NSCoder) { 135 | super.init(coder: aDecoder) 136 | initialize() 137 | } 138 | 139 | /// :nodoc: 140 | override public init(frame: CGRect) { 141 | super.init(frame: frame) 142 | initialize() 143 | } 144 | 145 | /// Initializes and returns a newly allocated segmented control object with the specified titles. 146 | /// 147 | /// - Parameter titles: The segments titles. 148 | public convenience init(withTitles titles: [String]) { 149 | self.init(frame: CGRect.zero) 150 | for title in titles { 151 | append(title: title) 152 | } 153 | } 154 | 155 | /// Initializes and returns a newly allocated segmented control object with the specified titles. 156 | /// 157 | /// - Parameter titles: The segments titles. 158 | public convenience init(withAttributedTitles titles: [NSAttributedString]) { 159 | self.init(frame: CGRect.zero) 160 | for title in titles { 161 | append(attributedTitle: title) 162 | } 163 | } 164 | 165 | /// Initializes and returns a newly allocated segmented control object with the specified images. 166 | /// 167 | /// - Parameter images: The segments images. 168 | public convenience init(withImages images: [UIImage]) { 169 | self.init(frame: CGRect.zero) 170 | for image in images { 171 | append(image: image) 172 | } 173 | } 174 | 175 | private func initialize() { 176 | _scrollView.canCancelContentTouches = true 177 | _scrollView.showsVerticalScrollIndicator = false 178 | _scrollView.showsHorizontalScrollIndicator = false 179 | 180 | selectedTextColor = tintColor 181 | 182 | _scrollView.addSubview(indicator) 183 | _scrollView.addSubview(contentView) 184 | addSubview(_scrollView) 185 | } 186 | 187 | deinit { 188 | guard let scrollView = scrollView else { return } 189 | scrollView.removeObserver(self, forKeyPath: keyPath, context: &context) 190 | } 191 | 192 | // MARK: Layout 193 | 194 | /// :nodoc: 195 | open override func layoutSubviews() { 196 | super.layoutSubviews() 197 | 198 | var frame = self.bounds.inset(by: contentEdgeInsets) 199 | _scrollView.frame = frame 200 | 201 | let size = contentView.intrinsicContentSize 202 | if size.width > frame.size.width { 203 | frame.size.width = size.width 204 | } 205 | 206 | contentView.setNeedsLayout() 207 | contentView.frame = frame 208 | contentView.layoutIfNeeded() 209 | 210 | _scrollView.contentSize = frame.size 211 | _scrollView.contentInset = UIEdgeInsets.zero 212 | 213 | layoutIndicator() 214 | } 215 | 216 | private func layoutIndicator() { 217 | guard contentView.segments.count > 0 else { 218 | return 219 | } 220 | 221 | let index = min(Int(progress), contentView.segments.count - 1) 222 | let current = segment(at: index)! 223 | 224 | var frame = CGRect.zero 225 | frame.size.height = bounds.height 226 | 227 | // Compute indicator's position 228 | var x = progress < 0 ? 0 : current.frame.size.width * (progress - CGFloat(index)) 229 | 230 | for i in 0.. MXSegment? { 377 | guard index < contentView.segments.count else { 378 | return nil 379 | } 380 | return contentView.segments[index] 381 | } 382 | 383 | /// Counts of segment. 384 | public var count: Int { 385 | return contentView.segments.count 386 | } 387 | 388 | public func newSegment() -> MXSegment { 389 | 390 | let segment = MXSegment() 391 | 392 | segment.setTitleColor(textColor, for: .normal) 393 | segment.setTitleColor(selectedTextColor, for: .selected) 394 | segment.contentEdgeInsets = segmentEdgeInsets 395 | segment.titleLabel?.font = font 396 | segment.isSelected = (contentView.segments.count == selectedIndex) 397 | segment.addTarget(self, action: #selector(MXSegmentedControl.select(segment:)), for: .touchUpInside) 398 | 399 | contentView.append(segment) 400 | return segment 401 | } 402 | 403 | @objc private func select(segment: MXSegment) { 404 | if let index = contentView.segments.index(of: segment) { 405 | select(index: index, animated: true) 406 | } 407 | } 408 | 409 | /// Removes the segment the given index. 410 | /// 411 | /// - Parameter index: The segment index to remove. 412 | public func remove(segmentAt index: Int) { 413 | guard index < contentView.segments.count else { 414 | return 415 | } 416 | contentView.remove( segment(at: index)! ) 417 | } 418 | 419 | /// Removes all segments. 420 | public func removeAll() { 421 | for segment in contentView.segments { 422 | contentView.remove(segment) 423 | } 424 | layoutIfNeeded() 425 | } 426 | 427 | /// Creates and append a new segment with the given title. 428 | /// 429 | /// - Parameter title: The new segment title. 430 | @discardableResult public func append(title: String) -> MXSegment { 431 | let segment = newSegment() 432 | segment.setTitle(title, for: .normal) 433 | return segment 434 | } 435 | 436 | /// Creates and append a new segment with the given title. 437 | /// 438 | /// - Parameter title: The new segment title. 439 | @discardableResult public func append(attributedTitle: NSAttributedString) -> MXSegment { 440 | let segment = newSegment() 441 | segment.setAttributedTitle(attributedTitle, for: .normal) 442 | return segment 443 | } 444 | 445 | /// Creates and append a new segment with the given image. 446 | /// 447 | /// - Parameter title: The new segment image. 448 | @discardableResult public func append(image: UIImage?) -> MXSegment { 449 | let segment = newSegment() 450 | segment.setImage(image, for: .normal) 451 | return segment 452 | } 453 | 454 | /// Selects the segment at the given index. 455 | /// 456 | /// - Parameters: 457 | /// - index: The segment index to be selected. 458 | /// - animated: true if the selection should be animated, false if it should be immediate. 459 | public func select(index: Int, animated: Bool) { 460 | selectedIndex = index 461 | 462 | UIView.animate(withDuration: animated ? animation.duration : 0, 463 | delay: animation.delay, 464 | usingSpringWithDamping: animation.dampingRatio, 465 | initialSpringVelocity: animation.velocity, 466 | options: animation.options, 467 | animations: { self.progress = CGFloat(index) }) 468 | 469 | if let scrollView = scrollView { 470 | var contentOffset = scrollView.contentOffset 471 | contentOffset.x = CGFloat(index) * scrollView.frame.size.width 472 | scrollView.setContentOffset(contentOffset, animated: animated) 473 | } 474 | } 475 | 476 | } 477 | 478 | // MARK: - Content View 479 | 480 | extension MXSegmentedControl { 481 | 482 | class ContentView: UIView { 483 | 484 | var segments = [MXSegment]() 485 | 486 | var separators = Separators() 487 | 488 | func append(_ segment: MXSegment) { 489 | guard !segments.contains(segment) else { 490 | return 491 | } 492 | 493 | if segments.count > 0 { 494 | let separator = CALayer() 495 | separator.backgroundColor = separators.color.cgColor 496 | layer.addSublayer(separator) 497 | separators.layers.append(separator) 498 | } 499 | 500 | addSubview(segment) 501 | segments.append(segment) 502 | 503 | invalidateIntrinsicContentSize() 504 | layoutIfNeeded() 505 | } 506 | 507 | func remove(_ segment: MXSegment) { 508 | guard var index = segments.index(of: segment) else { 509 | return 510 | } 511 | 512 | segment.removeFromSuperview() 513 | segments.remove(at: index) 514 | 515 | if segments.count > 0 { 516 | index = segments.count - 1 517 | let separator = separators.layers[index] 518 | separator.removeFromSuperlayer() 519 | separators.layers.remove(at: index) 520 | } 521 | 522 | invalidateIntrinsicContentSize() 523 | layoutIfNeeded() 524 | } 525 | 526 | override var intrinsicContentSize: CGSize { 527 | get { 528 | var size = CGSize(width: CGFloat(separators.layers.count) * separators.inset.width, height: UIView.noIntrinsicMetric) 529 | for segment in segments { 530 | size.width += segment.width 531 | size.height = max(segment.intrinsicContentSize.height, size.height) 532 | } 533 | return size 534 | } 535 | } 536 | 537 | override func layoutSubviews() { 538 | super.layoutSubviews() 539 | 540 | let segments = self.segments.sorted(by: { $0.width > $1.width }) 541 | 542 | let height = frame.size.height 543 | var width = frame.size.width - CGFloat(separators.layers.count) * separators.inset.width 544 | 545 | for (index, segment) in segments.enumerated() { 546 | 547 | var frame = CGRect.zero 548 | frame.size.width = max(segment.width, width / CGFloat(self.segments.count - index)) 549 | frame.size.height = height 550 | segment.layer.frame = frame 551 | 552 | width -= frame.size.width 553 | } 554 | 555 | var separatorFrame = CGRect(x: 0, y: separators.inset.top, width: separators.inset.width, height: height - separators.inset.top - separators.inset.bottom) 556 | for (index, segment) in self.segments.enumerated() { 557 | 558 | if index > 0 { 559 | separators.layers[index - 1].frame = separatorFrame 560 | separatorFrame.origin.x += separators.inset.width 561 | } 562 | 563 | var frame = segment.frame 564 | frame.origin.x = separatorFrame.origin.x 565 | segment.frame = frame 566 | separatorFrame.origin.x += frame.size.width 567 | } 568 | } 569 | 570 | } 571 | 572 | } 573 | 574 | // MARK: - Animation 575 | 576 | extension MXSegmentedControl { 577 | 578 | /// The indicator animation properties. 579 | public struct Animation { 580 | 581 | /// The total duration of the animations, measured in seconds. If you specify a negative value or 0, the changes are made without animating them. 582 | public var duration: TimeInterval 583 | 584 | /// The amount of time (measured in seconds) to wait before beginning the animations. Specify a value of 0 to begin the animations immediately. 585 | public var delay: TimeInterval 586 | 587 | /// The damping ratio for the spring animation as it approaches its quiescent state. 588 | /// To smoothly decelerate the animation without oscillation, use a value of 1. Employ a damping ratio closer to zero to increase oscillation. 589 | public var dampingRatio: CGFloat 590 | 591 | /// The initial spring velocity. For smooth start to the animation, match this value to the view’s velocity as it was prior to attachment. 592 | /// A value of 1 corresponds to the total animation distance traversed in one second. For example, if the total animation distance is 200 points and you want the start of the animation to match a view velocity of 100 pt/s, use a value of 0.5. 593 | public var velocity: CGFloat 594 | 595 | /// A mask of options indicating how you want to perform the animations. For a list of valid constants, see UIViewAnimationOptions. 596 | public var options: UIView.AnimationOptions 597 | } 598 | 599 | } 600 | 601 | // MARK: - Separators 602 | 603 | extension MXSegmentedControl { 604 | 605 | /// Segmented control seperators representation. 606 | /// This structure lets you customize the seperator inset as well as the color. You can also customize the seperators layers directly. 607 | public struct Separators { 608 | 609 | /// The seperator inset representation. 610 | public struct Inset { 611 | 612 | /// The seperator inset top value. 613 | public var top: CGFloat 614 | 615 | /// The seperator inset width value. 616 | public var width: CGFloat 617 | 618 | /// The seperator inset bottom value. 619 | public var bottom: CGFloat 620 | 621 | /// Initializes a zero value seperator inset. 622 | public static var zero = Inset(top: 0, width: 0, bottom: 0) 623 | 624 | public init(top: CGFloat, width: CGFloat, bottom: CGFloat) { 625 | self.top = top 626 | self.width = width 627 | self.bottom = bottom 628 | } 629 | } 630 | 631 | /// The separators inset. 632 | public var inset = Inset.zero 633 | 634 | /// The seperators color. 635 | public var color = UIColor.lightGray 636 | 637 | /// The separators layers. 638 | public internal(set) var layers = [CALayer]() 639 | } 640 | 641 | } 642 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "MXSegmentedControl", 6 | platforms: [.iOS(.v8)], 7 | products: [ 8 | .library(name: "MXSegmentedControl", 9 | targets: ["MXSegmentedControl"]) 10 | ], 11 | targets: [ 12 | .target( 13 | name: "MXSegmentedControl", 14 | path: "MXSegmentedControl" 15 | ) 16 | ], 17 | swiftLanguageVersions: [.v5,.v4_2] 18 | ) 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MXSegmentedControl 2 | 3 | [![CI Status](http://img.shields.io/travis/maxep/MXSegmentedControl.svg?style=flat)](https://travis-ci.org/maxep/MXSegmentedControl) 4 | [![Version](https://img.shields.io/cocoapods/v/MXSegmentedControl.svg?style=flat)](http://cocoadocs.org/docsets/MXSegmentedControl) 5 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 6 | [![License](https://img.shields.io/cocoapods/l/MXSegmentedControl.svg?style=flat)](http://cocoadocs.org/docsets/MXSegmentedControl) 7 | [![Platform](https://img.shields.io/cocoapods/p/MXSegmentedControl.svg?style=flat)](http://cocoadocs.org/docsets/MXSegmentedControl) 8 | 9 | Yet another segmented control for iOS inspired by [Segmentio](https://github.com/Yalantis/Segmentio) & [HMSegmentedPager](https://github.com/HeshamMegid/HMSegmentedControl). 10 | MXSegmentedControl is easy to integrate and very customizable. It dynamically computes the segments width to fit or fill its content, it can also track a UIScrollView content offset to change segment. 11 | 12 |

13 | 14 |

15 | 16 | ## Installation 17 | 18 | MXSegmentedControl is available through [CocoaPods](https://cocoapods.org/pods/MXSegmentedControl). To install 19 | it, simply add the following line to your Podfile: 20 | 21 | ``` 22 | pod 'MXSegmentedControl' 23 | ``` 24 | 25 | ## Usage 26 | 27 | + You can configure a simple segmented control like following: 28 | 29 | ```swift 30 | segmentedControl.append(title: "First") 31 | segmentedControl.append(title: "Second") 32 | segmentedControl.append(title: "Third") 33 | ``` 34 | 35 | ## Author 36 | 37 | [Maxime Epain](http://maxep.me) 38 | 39 | ## License 40 | 41 | MXSegmentedControl is available under the MIT license. See the [LICENSE](LICENSE) file for more info. 42 | -------------------------------------------------------------------------------- /screencast.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxep/MXSegmentedControl/eab7a95c36cbbddc15ef7fb2ce7be28ef56fc218/screencast.gif --------------------------------------------------------------------------------