├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .swiftlint.yml ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cartfile.private ├── Example.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── Example ├── Example │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── Simpsons │ │ │ ├── Apu_Nahasapeemapetilon.imageset │ │ │ │ ├── Apu_Nahasapeemapetilon.png │ │ │ │ └── Contents.json │ │ │ ├── Bart_Simpsons.imageset │ │ │ │ ├── Bart_Simpsons.png │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ ├── Homer_Simpsons.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── Homer_Simpsons.png │ │ │ ├── Lisa_Simpsons.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── Lisa_Simpsons.png │ │ │ ├── Maggie_Simpsons.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── Maggie_Simpsons.png │ │ │ ├── Marge_Simpsons.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── Marge_Simpsons.png │ │ │ ├── Montgomery_Burns.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── Montgomery_Burns.png │ │ │ ├── Ned_Flanders.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── Ned_Flanders.png │ │ │ ├── Otto_Mann.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── Otto_Mann.png │ │ │ └── default-avatar.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── default-avatar@2x.png │ │ ├── TabBar │ │ │ ├── Contents.json │ │ │ ├── icon_bar.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── a_bar.png │ │ │ │ └── a_bar@2x.png │ │ │ ├── icon_button.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── a_button.png │ │ │ │ └── a_button@2x.png │ │ │ ├── icon_segmented.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── a_segmented.png │ │ │ │ └── a_segmented@2x.png │ │ │ └── icon_twitter.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── a_twitter.png │ │ │ │ └── a_twitter@2x.png │ │ └── Youtube │ │ │ ├── Contents.json │ │ │ ├── home.imageset │ │ │ ├── Contents.json │ │ │ └── home@2x.png │ │ │ ├── more_options.imageset │ │ │ ├── Contents.json │ │ │ ├── more_options@2x.png │ │ │ └── more_options@3x.png │ │ │ ├── profile.imageset │ │ │ ├── Contents.json │ │ │ └── profile@2x.png │ │ │ ├── search.imageset │ │ │ ├── Contents.json │ │ │ ├── search@2x-1.png │ │ │ └── search@2x.png │ │ │ └── trending.imageset │ │ │ ├── Contents.json │ │ │ └── trending@2x.png │ ├── BarExampleViewController.swift │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Storyboard.storyboard │ ├── ButtonBarExampleViewController.swift │ ├── ChildControllers │ │ ├── ChildExampleViewController.swift │ │ └── TableChildExampleViewController.swift │ ├── Helpers │ │ ├── DataProvider.swift │ │ ├── PostCell.swift │ │ └── PostCell.xib │ ├── Info.plist │ ├── Instagram │ │ └── InstagramExampleViewController.swift │ ├── NavButtonBarExampleViewController.swift │ ├── ReloadExampleViewController.swift │ ├── SegmentedExampleViewController.swift │ ├── Spotify │ │ └── SpotifyExampleViewController.swift │ ├── TwitterExampleViewController.swift │ ├── Youtube │ │ ├── YoutubeExampleViewController.swift │ │ ├── YoutubeIconCell.swift │ │ └── YoutubeIconCell.xib │ └── YoutubeWithLabel │ │ ├── YoutubeIconWithLabelCell.swift │ │ ├── YoutubeIconWithLabelCell.xib │ │ └── YoutubeWithLabelExampleViewController.swift ├── ExampleUITests │ ├── ExampleUITests.swift │ └── Info.plist ├── bar.gif ├── barButton.gif ├── instagram.gif ├── navBarButton.gif ├── pagerTabStripTypes.gif ├── segmented.gif ├── spotify.gif ├── twitter.gif └── youtube.gif ├── LICENSE ├── Migration.md ├── ObjC ├── FXPageControl.h └── FXPageControl.m ├── Package.resolved ├── Package.swift ├── Playground.playground ├── Contents.swift └── contents.xcplayground ├── README.md ├── Sources └── XLPagerTabStrip │ ├── BarPagerTabStripViewController.swift │ ├── BarView.swift │ ├── BaseButtonBarPagerTabStripViewController.swift │ ├── ButtonBarPagerTabStripViewController.swift │ ├── ButtonBarView.swift │ ├── ButtonBarViewCell.swift │ ├── IndicatorInfo.swift │ ├── PagerTabStripBehaviour.swift │ ├── PagerTabStripError.swift │ ├── PagerTabStripViewController.swift │ ├── Resources │ └── ButtonCell.xib │ ├── SegmentedPagerTabStripViewController.swift │ ├── SwipeDirection.swift │ ├── TwitterPagerTabStripViewController.swift │ └── UIWindow+Pager.swift ├── Tests ├── Info.plist └── XLPagerTabStripTests.swift ├── XLPagerTabStrip.podspec ├── XLPagerTabStrip.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── XLPagerTabStrip.xcscheme ├── XLPagerTabStrip.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── XLPagerTabStrip ├── Info.plist └── XLPagerTabStrip.h └── XLPagerTabTrip.png /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: xmartlabs # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Before submitting issues ... 2 | 3 | - Make sure you are using XLPagerTabStrip [latest release](https://github.com/xmartlabs/XLPagerTabStrip/releases) or master branch version. 4 | - Make sure your Xcode version is the latest stable one. 5 | - Check if the issue was [already reported or fixed](https://github.com/xmartlabs/XLPagerTabStrip/issues?utf8=%E2%9C%93&q=is%3Aissue). We add labels to each issue in order to easily find related issues. If you found a match add a brief comment "I have the same problem" or "+1". 6 | - Please do not use the issue tracker for personal support requests. Stack Overflow is a better place for that where a wider community can help you! 7 | 8 | When submitting issues, please provide the following information to help maintainers to fix the problem faster: 9 | 10 | - Environment: XLPagerTabStrip, Xcode and iOS version you are using. 11 | - In case of reporting errors, provide Xcode console output of stack trace or code compilation error. 12 | - Any other additional detail such as example code that you think it would be useful to understand, reproduce and solve the problem. 13 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/.github/PULL_REQUEST_TEMPLATE.md -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## OS X Finder 2 | .DS_Store 3 | 4 | ## Build generated 5 | build/ 6 | DerivedData 7 | 8 | ## Various settings 9 | *.pbxuser 10 | !default.pbxuser 11 | *.mode1v3 12 | !default.mode1v3 13 | *.mode2v3 14 | !default.mode2v3 15 | *.perspectivev3 16 | !default.perspectivev3 17 | xcuserdata 18 | 19 | ## Other 20 | *.xccheckout 21 | *.moved-aside 22 | *.xcuserstate 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | 29 | ## Playgrounds 30 | timeline.xctimeline 31 | playground.xcworkspace 32 | 33 | # Swift Package Manager 34 | # 35 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 36 | # Packages/ 37 | .build/ 38 | 39 | # CocoaPods 40 | # 41 | # We recommend against adding the Pods directory to your .gitignore. However 42 | # you should judge for yourself, the pros and cons are mentioned at: 43 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 44 | # 45 | # Pods/ 46 | 47 | # Carthage 48 | # 49 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 50 | # Carthage/Checkouts 51 | 52 | Carthage/Build 53 | 54 | .swift-version 55 | .swiftpm/xcode 56 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: # rule identifiers to exclude from running 2 | # - colon 3 | # - comma 4 | # - control_statement 5 | - line_length 6 | - function_body_length 7 | - identifier_name 8 | - type_name 9 | - large_tuple 10 | opt_in_rules: # some rules are only opt-in 11 | - empty_count 12 | # Find all the available rules by running: 13 | # swiftlint rules 14 | excluded: # paths to ignore during linting. Takes precedence over `included`. 15 | - Carthage 16 | - Pods 17 | - Source/ExcludedFolder 18 | - Source/ExcludedFile.swift 19 | 20 | # configurable rules can be customized from this configuration file 21 | # binary rules can set their severity level 22 | force_cast: warning # implicitly 23 | force_try: 24 | severity: warning # explicitly 25 | # rules that have both warning and error levels, can set just the warning level 26 | # implicitly 27 | line_length: 200 28 | # they can set both implicitly with an array 29 | type_body_length: 30 | - 300 # warning 31 | - 400 # error 32 | # or they can set both explicitly 33 | file_length: 34 | warning: 500 35 | error: 1200 36 | # naming rules can set warnings/errors for min_length and max_length 37 | # additionally they can set excluded names 38 | type_name: 39 | min_length: 4 # only warning 40 | max_length: # warning and error 41 | warning: 40 42 | error: 50 43 | excluded: iPhone # excluded via string 44 | identifier_name: 45 | min_length: # only min_length 46 | error: 4 # only error 47 | excluded: # excluded via string array 48 | - id 49 | - URL 50 | - GlobalAPIKey 51 | reporter: "xcode" # reporter type (xcode, json, csv, checkstyle, junit, html, emoji) -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode10.2 3 | branches: 4 | only: 5 | - master 6 | env: 7 | - DESTINATION="OS=12.1,name=iPhone XS" SCHEME="XLPagerTabStrip" SDK=iphonesimulator 8 | before_install: 9 | - brew update 10 | - brew outdated carthage || brew upgrade carthage 11 | script: 12 | - set -o pipefail 13 | - xcodebuild -version 14 | - xcodebuild clean build -project XLPagerTabStrip.xcodeproj -scheme "$SCHEME" -sdk "$SDK" 15 | #- xcodebuild test -project XLPagerTabStrip.xcodeproj -scheme "$SCHEME" -sdk "$SDK" 16 | - xcodebuild -project XLPagerTabStrip.xcodeproj -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO test | xcpretty 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to XLPagerTabStrip will be documented in this file. 3 | 4 | ### [9.1.0](https://github.com/xmartlabs/XLPagerTabStrip/releases/tag/9.1.0) 5 | 6 | * SPM support. 7 | 8 | ### [9.0.0](https://github.com/xmartlabs/XLPagerTabStrip/releases/tag/9.0.0) 9 | 10 | * Xcode 10.2 and Swift 5.0 support. 11 | * Minor updates and fixes. 12 | 13 | ### [8.1.0](https://github.com/xmartlabs/XLPagerTabStrip/releases/tag/8.1.0) 14 | 15 | * Xcode 10 and Swift 4.2 support. 16 | * Minor updates and fixes. 17 | 18 | ### [8.0.1](https://github.com/xmartlabs/XLPagerTabStrip/releases/tag/8.0.1) 19 | 20 | * Bug fixes and stability improvements. 21 | * Support for iPhone X. 22 | 23 | ### [8.0.0](https://github.com/xmartlabs/XLPagerTabStrip/releases/tag/8.0.0) 24 | 25 | * Xcode 9 support. (Swift 4) 26 | * Bug fixes and stability improvements. 27 | 28 | ### [7.0.0](https://github.com/xmartlabs/XLPagerTabStrip/releases/tag/7.0.0) 29 | 30 | * Bug fixes and stability improvements. 31 | 32 | ### [6.0.0](https://github.com/xmartlabs/XLPagerTabStrip/releases/tag/6.0.0) 33 | 34 | * Swift 3 support 35 | * **Breaking change**: Swiftified names of functions (you can see more details about it [here](https://github.com/xmartlabs/XLPagerTabStrip/Migration.md)) 36 | 37 | ### [5.1.0](https://github.com/xmartlabs/XLPagerTabStrip/releases/tag/5.0.0) 38 | 39 | * Xcode 8 support. (Swift 2.3) 40 | 41 | ### [5.0.0](https://github.com/xmartlabs/XLPagerTabStrip/releases/tag/5.0.0) 42 | 43 | * Xcode 7.3 support. 44 | * Bug fixes and stability improvements. 45 | 46 | ### [4.0.2](https://github.com/xmartlabs/XLPagerTabStrip/releases/tag/4.0.2) 47 | 48 | * Bug fixes 49 | 50 | ### [4.0.1](https://github.com/xmartlabs/XLPagerTabStrip/releases/tag/4.0.1) 51 | 52 | * Bug fixes and stability improvements 53 | 54 | ### [4.0.0](https://github.com/xmartlabs/XLPagerTabStrip/releases/tag/4.0.0) 55 | 56 | 57 | * Base code migration from obj-c to swift. 58 | * Removed XL prefix from all types. 59 | 60 | ### [3.0.0](https://github.com/xmartlabs/XLPagerTabStrip/releases/tag/3.0.0) 61 | 62 | * `selectedBarAlignment` added to `XLButtonBarView`. 63 | * `shouldCellsFillAvailableWidth` added to `XLButtonBarView`. 64 | * Bug fixes and Stability improvements. 65 | 66 | ### [2.0.0](https://github.com/xmartlabs/XLPagerTabStrip/releases/tag/v2.0.0) 67 | 68 | * Added ability to change look and feel of selected tab. 69 | * `changeCurrentIndexProgressiveBlock` added to `XLButtonBarPagerTabStripViewController`. 70 | * `changeCurrentIndexBlock` added to `XLButtonBarPagerTabStripViewController`. 71 | * indxWasChanged parameter was added to `-(void)pagerTabStripViewController:(XLPagerTabStripViewController *)pagerTabStripViewController updateIndicatorFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex withProgressPercentage:(CGFloat)progressPercentage indexWasChanged:(BOOL)indexWasChanged;` 72 | * Bug Fix Issue #45: When the current tab is tapped by the user, and later swiping to another tab, the indicator now changes as expected. 73 | * Bug Fix: When scrolling between tabs with progressive indicator, the indicator now scrolls swiftly. It used to jump for an instant. 74 | * Bug Fix Issue #54: Twitter PagerTabStrip wasn't loading the navigation title correctly. 75 | * Bug Fix Issue #32: Demo for Nav Button Bar Example fix. 76 | * Bug Fix Issue #32: Twitter Pager white dots that mark which tab is currently selected is non selectable now. 77 | * Bug Fix Issue #22: moveToViewControllerAtIndex: in viewDidLoad or viewWillAppear is not reflected in buttonBarView. 78 | 79 | ### [1.1.1](https://github.com/xmartlabs/XLPagerTabStrip/releases/tag/v1.1.1) 80 | 81 | * Nav Button example added 82 | * Support for iOS 7.0 and above 83 | 84 | ### [1.1.0](https://github.com/xmartlabs/XLPagerTabStrip/releases/tag/v1.1.0) 85 | 86 | * Twitter pager added 87 | * Bug fixes and stability improvements 88 | 89 | ### [1.0.0](https://github.com/xmartlabs/XLPagerTabStrip/releases/tag/v1.0.0) 90 | 91 | * Initial release 92 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing Guidelines 2 | -------------------------------------------------- 3 | 4 | This document provides general guidelines about how to contribute to the project. Keep in mind these important things before you start contributing. 5 | 6 | ### Asking Questions 7 | 8 | We do not use github issues for general library support. We think this questions should be posted on stack overflow using [XlPagerTabStrip](http://http://stackoverflow.com/questions/tagged/xlpagertabstrip) tag. 9 | 10 | ### Reporting issues 11 | 12 | * Use [github issues](https://github.com/xmartlabs/XLPagerTabStrip/issues) to report a bug. 13 | * Before creating a new issue: 14 | * Make sure you are using the [latest release](https://github.com/xmartlabs/XLPagerTabStrip/releases). 15 | * Check if the issue was [already reported or fixed](https://github.com/xmartlabs/XLPagerTabStrip/issues?utf8=%E2%9C%93&q=is%3Aissue). Notice that it may not be released yet. 16 | * If you found a match add a brief comment "I have the same problem" or "+1". This helps prioritize the issues addressing the most common and critical first. If possible add additional information to help us reproduce and fix the issue. Please use your best judgement. 17 | * Reporting issues: 18 | * Please include the following information to help maintainers to fix the problem faster: 19 | * Xcode version you are using. 20 | * iOS version you are targeting. 21 | * Full Xcode console output of stack trace or code compilation error. 22 | * Any other additional detail you think it would be useful to understand and solve the problem. 23 | 24 | 25 | ### Pull requests 26 | 27 | The easiest way to start contributing is searching open issues by `help wanted` tag. We also add a `difficulty` tag (difficulty: easy, difficulty: moderate, difficulty: hard) in order to give an idea of how complex it can be to implement the feature according maintainers project experience. 28 | 29 | * Add test coverage to the feature or fix. We only accept new feature pull requests that have related test coverage. This allows us to keep the library stable as we move forward. 30 | * Remember to document the new feature. We do not accept new feature pull requests without its associated documentation. 31 | * In case of a new feature please update the example project showing the feature. 32 | * Please only one fix or feature per pull request. This will increase the chances your feature will be merged. 33 | 34 | 35 | ###### Suggested git workflow to contribute 36 | 37 | 1. Fork the XLPagerTabStrip repository. 38 | 2. Clone your forked project into your developer machine: `git clone git@github.com:/XLPagerTabStrip.git` 39 | 3. Add the original project repo as upstream repository in your forked project: `git remote add upstream git@github.com:xmartlabs/XLPagerTabStrip.git` 40 | 4. Before starting a new feature make sure your forked master branch is synchronized upstream master branch. Considering you do not mere your pull request into master you can run: `git checkout master` and then `git pull upstream master`. Optionally `git push origin master`. 41 | 5. Create a new branch. Note that the starting point is the upstream master branch HEAD. `git checkout -b my-feature-name` 42 | 6. Stage all your changes `git add .` and commit them `git commit -m "Your commit message"` 43 | 7. Make sure your branch is up to date with upstream master, `git pull --rebase upstream master`, resolve conflicts if necessary. This will move your commit to the top of git stack. 44 | 8. Squash your commits into one commit. `git rebase -i HEAD~6` considering you did 6 commits. 45 | 9. Push your branch into your forked remote repository. 46 | 10. Create a new pull request adding any useful comment. 47 | 48 | 49 | ###### Code style and conventions 50 | 51 | We try to follow our [swift style guide](https://github.com/xmartlabs/Swift-Style-Guide). Following it is not strictly necessary to contribute and to have a pull request accepted but project maintainers try to follow it. We would love to hear your ideas to improve our code style and conventions. Feel free to contribute. 52 | 53 | 54 | ### Feature proposal 55 | 56 | We would love to hear your ideas and make a discussions about it. 57 | 58 | * Use github issues to make feature proposals. 59 | * We use `type: feature request` label to mark all [feature request issues](https://github.com/xmartlabs/XLPagerTabStrip/labels/type%3A%20feature%20request). 60 | * Before submitting your proposal make sure there is no similar feature request. If you found a match feel free to join the discussion or just add a brief "+1" if you think the feature is worth implementing. 61 | * Be as specific as possible providing a precise explanation of feature request so anyone can understand the problem and the benefits of solving it. 62 | -------------------------------------------------------------------------------- /Cartfile.private: -------------------------------------------------------------------------------- 1 | github "Quick/Quick" 2 | github "Quick/Nimble" 3 | -------------------------------------------------------------------------------- /Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Example 4 | // 5 | // Copyright © 2016 Xmartlabs SRL. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | 10 | @UIApplicationMain 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | var window: UIWindow? 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | 18 | UITabBar.appearance().tintColor = UIColor.init(red: 0.027, green: 0.725, blue: 0.608, alpha: 1) 19 | 20 | _ = YoutubeExampleViewController(nibName: nil, bundle: nil) 21 | 22 | return true 23 | } 24 | 25 | func applicationWillResignActive(_ application: UIApplication) { 26 | // 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. 27 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 28 | } 29 | 30 | func applicationDidEnterBackground(_ application: UIApplication) { 31 | // 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. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | func applicationWillEnterForeground(_ application: UIApplication) { 36 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 37 | } 38 | 39 | func applicationDidBecomeActive(_ application: UIApplication) { 40 | // 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. 41 | } 42 | 43 | func applicationWillTerminate(_ application: UIApplication) { 44 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /Example/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 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Simpsons/Apu_Nahasapeemapetilon.imageset/Apu_Nahasapeemapetilon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/Example/Assets.xcassets/Simpsons/Apu_Nahasapeemapetilon.imageset/Apu_Nahasapeemapetilon.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Simpsons/Apu_Nahasapeemapetilon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Apu_Nahasapeemapetilon.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Simpsons/Bart_Simpsons.imageset/Bart_Simpsons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/Example/Assets.xcassets/Simpsons/Bart_Simpsons.imageset/Bart_Simpsons.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Simpsons/Bart_Simpsons.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Bart_Simpsons.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Simpsons/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Simpsons/Homer_Simpsons.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Homer_Simpsons.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Simpsons/Homer_Simpsons.imageset/Homer_Simpsons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/Example/Assets.xcassets/Simpsons/Homer_Simpsons.imageset/Homer_Simpsons.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Simpsons/Lisa_Simpsons.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Lisa_Simpsons.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Simpsons/Lisa_Simpsons.imageset/Lisa_Simpsons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/Example/Assets.xcassets/Simpsons/Lisa_Simpsons.imageset/Lisa_Simpsons.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Simpsons/Maggie_Simpsons.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Maggie_Simpsons.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Simpsons/Maggie_Simpsons.imageset/Maggie_Simpsons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/Example/Assets.xcassets/Simpsons/Maggie_Simpsons.imageset/Maggie_Simpsons.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Simpsons/Marge_Simpsons.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Marge_Simpsons.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Simpsons/Marge_Simpsons.imageset/Marge_Simpsons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/Example/Assets.xcassets/Simpsons/Marge_Simpsons.imageset/Marge_Simpsons.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Simpsons/Montgomery_Burns.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Montgomery_Burns.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Simpsons/Montgomery_Burns.imageset/Montgomery_Burns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/Example/Assets.xcassets/Simpsons/Montgomery_Burns.imageset/Montgomery_Burns.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Simpsons/Ned_Flanders.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Ned_Flanders.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Simpsons/Ned_Flanders.imageset/Ned_Flanders.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/Example/Assets.xcassets/Simpsons/Ned_Flanders.imageset/Ned_Flanders.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Simpsons/Otto_Mann.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Otto_Mann.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Simpsons/Otto_Mann.imageset/Otto_Mann.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/Example/Assets.xcassets/Simpsons/Otto_Mann.imageset/Otto_Mann.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Simpsons/default-avatar.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "default-avatar@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Simpsons/default-avatar.imageset/default-avatar@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/Example/Assets.xcassets/Simpsons/default-avatar.imageset/default-avatar@2x.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/TabBar/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/TabBar/icon_bar.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "a_bar.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "a_bar@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/Example/Assets.xcassets/TabBar/icon_bar.imageset/a_bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/Example/Assets.xcassets/TabBar/icon_bar.imageset/a_bar.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/TabBar/icon_bar.imageset/a_bar@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/Example/Assets.xcassets/TabBar/icon_bar.imageset/a_bar@2x.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/TabBar/icon_button.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "a_button.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "a_button@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/Example/Assets.xcassets/TabBar/icon_button.imageset/a_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/Example/Assets.xcassets/TabBar/icon_button.imageset/a_button.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/TabBar/icon_button.imageset/a_button@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/Example/Assets.xcassets/TabBar/icon_button.imageset/a_button@2x.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/TabBar/icon_segmented.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "a_segmented.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "a_segmented@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/Example/Assets.xcassets/TabBar/icon_segmented.imageset/a_segmented.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/Example/Assets.xcassets/TabBar/icon_segmented.imageset/a_segmented.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/TabBar/icon_segmented.imageset/a_segmented@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/Example/Assets.xcassets/TabBar/icon_segmented.imageset/a_segmented@2x.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/TabBar/icon_twitter.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "a_twitter.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "a_twitter@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/Example/Assets.xcassets/TabBar/icon_twitter.imageset/a_twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/Example/Assets.xcassets/TabBar/icon_twitter.imageset/a_twitter.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/TabBar/icon_twitter.imageset/a_twitter@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/Example/Assets.xcassets/TabBar/icon_twitter.imageset/a_twitter@2x.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Youtube/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Youtube/home.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "home@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Youtube/home.imageset/home@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/Example/Assets.xcassets/Youtube/home.imageset/home@2x.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Youtube/more_options.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "more_options@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "more_options@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Youtube/more_options.imageset/more_options@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/Example/Assets.xcassets/Youtube/more_options.imageset/more_options@2x.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Youtube/more_options.imageset/more_options@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/Example/Assets.xcassets/Youtube/more_options.imageset/more_options@3x.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Youtube/profile.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "profile@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Youtube/profile.imageset/profile@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/Example/Assets.xcassets/Youtube/profile.imageset/profile@2x.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Youtube/search.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "search@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "search@2x-1.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Youtube/search.imageset/search@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/Example/Assets.xcassets/Youtube/search.imageset/search@2x-1.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Youtube/search.imageset/search@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/Example/Assets.xcassets/Youtube/search.imageset/search@2x.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Youtube/trending.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "trending@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Youtube/trending.imageset/trending@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/Example/Assets.xcassets/Youtube/trending.imageset/trending@2x.png -------------------------------------------------------------------------------- /Example/Example/BarExampleViewController.swift: -------------------------------------------------------------------------------- 1 | // BarExampleViewController.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Foundation 26 | import XLPagerTabStrip 27 | 28 | class BarExampleViewController: BarPagerTabStripViewController { 29 | var isReload = false 30 | 31 | required init?(coder aDecoder: NSCoder) { 32 | super.init(coder: aDecoder) 33 | } 34 | 35 | override func viewDidLoad() { 36 | 37 | // set up style before super view did load is executed 38 | settings.style.selectedBarBackgroundColor = .orange 39 | // - 40 | 41 | super.viewDidLoad() 42 | } 43 | 44 | // MARK: - PagerTabStripDataSource 45 | 46 | override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] { 47 | 48 | let child_1 = TableChildExampleViewController(style: .plain, itemInfo: "Table View") 49 | let child_2 = ChildExampleViewController(itemInfo: "View") 50 | let child_3 = TableChildExampleViewController(style: .grouped, itemInfo: "Table View 2") 51 | let child_4 = ChildExampleViewController(itemInfo: "View 2") 52 | 53 | guard isReload else { 54 | return [child_1, child_2, child_3, child_4] 55 | } 56 | 57 | var childViewControllers = [child_1, child_2, child_3, child_4] 58 | for index in childViewControllers.indices { 59 | let nElements = childViewControllers.count - index 60 | let n = (Int(arc4random()) % nElements) + index 61 | if n != index { 62 | childViewControllers.swapAt(index, n) 63 | } 64 | } 65 | let nItems = 1 + (arc4random() % 4) 66 | return Array(childViewControllers.prefix(Int(nItems))) 67 | } 68 | 69 | override func reloadPagerTabStripView() { 70 | isReload = true 71 | if arc4random() % 2 == 0 { 72 | pagerBehaviour = .progressive(skipIntermediateViewControllers: arc4random() % 2 == 0, elasticIndicatorLimit: arc4random() % 2 == 0 ) 73 | } else { 74 | pagerBehaviour = .common(skipIntermediateViewControllers: arc4random() % 2 == 0) 75 | } 76 | super.reloadPagerTabStripView() 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Example/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 | 29 | -------------------------------------------------------------------------------- /Example/Example/ButtonBarExampleViewController.swift: -------------------------------------------------------------------------------- 1 | // ButtonBarExampleViewController.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Foundation 26 | import XLPagerTabStrip 27 | 28 | class ButtonBarExampleViewController: ButtonBarPagerTabStripViewController { 29 | 30 | var isReload = false 31 | 32 | override func viewDidLoad() { 33 | super.viewDidLoad() 34 | 35 | buttonBarView.selectedBar.backgroundColor = .orange 36 | buttonBarView.backgroundColor = UIColor(red: 7/255, green: 185/255, blue: 155/255, alpha: 1) 37 | } 38 | 39 | // MARK: - PagerTabStripDataSource 40 | 41 | override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] { 42 | let child_1 = TableChildExampleViewController(style: .plain, itemInfo: "Table View") 43 | let child_2 = ChildExampleViewController(itemInfo: "View") 44 | let child_3 = TableChildExampleViewController(style: .grouped, itemInfo: "Table View 2") 45 | let child_4 = ChildExampleViewController(itemInfo: "View 2") 46 | let child_5 = TableChildExampleViewController(style: .plain, itemInfo: "Table View 3") 47 | let child_6 = ChildExampleViewController(itemInfo: "View 3") 48 | let child_7 = TableChildExampleViewController(style: .grouped, itemInfo: "Table View 4") 49 | let child_8 = ChildExampleViewController(itemInfo: "View 4") 50 | 51 | guard isReload else { 52 | return [child_1, child_2, child_3, child_4, child_5, child_6, child_7, child_8] 53 | } 54 | 55 | var childViewControllers = [child_1, child_2, child_3, child_4, child_5, child_6, child_7, child_8] 56 | 57 | for index in childViewControllers.indices { 58 | let nElements = childViewControllers.count - index 59 | let n = (Int(arc4random()) % nElements) + index 60 | if n != index { 61 | childViewControllers.swapAt(index, n) 62 | } 63 | } 64 | let nItems = 1 + (arc4random() % 8) 65 | return Array(childViewControllers.prefix(Int(nItems))) 66 | } 67 | 68 | override func reloadPagerTabStripView() { 69 | isReload = true 70 | if arc4random() % 2 == 0 { 71 | pagerBehaviour = .progressive(skipIntermediateViewControllers: arc4random() % 2 == 0, elasticIndicatorLimit: arc4random() % 2 == 0 ) 72 | } else { 73 | pagerBehaviour = .common(skipIntermediateViewControllers: arc4random() % 2 == 0) 74 | } 75 | super.reloadPagerTabStripView() 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Example/Example/ChildControllers/ChildExampleViewController.swift: -------------------------------------------------------------------------------- 1 | // ChildExampleViewController.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Foundation 26 | import XLPagerTabStrip 27 | 28 | class ChildExampleViewController: UIViewController, IndicatorInfoProvider { 29 | 30 | var itemInfo: IndicatorInfo = "View" 31 | 32 | init(itemInfo: IndicatorInfo) { 33 | self.itemInfo = itemInfo 34 | super.init(nibName: nil, bundle: nil) 35 | } 36 | 37 | required init?(coder aDecoder: NSCoder) { 38 | fatalError("init(coder:) has not been implemented") 39 | } 40 | 41 | override func viewDidLoad() { 42 | super.viewDidLoad() 43 | 44 | let label = UILabel() 45 | label.translatesAutoresizingMaskIntoConstraints = false 46 | label.text = "XLPagerTabStrip" 47 | 48 | view.addSubview(label) 49 | view.backgroundColor = .white 50 | 51 | view.addConstraint(NSLayoutConstraint(item: label, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1, constant: 0)) 52 | view.addConstraint(NSLayoutConstraint(item: label, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1, constant: -50)) 53 | } 54 | 55 | // MARK: - IndicatorInfoProvider 56 | 57 | func indicatorInfo(for pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo { 58 | return itemInfo 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Example/Example/ChildControllers/TableChildExampleViewController.swift: -------------------------------------------------------------------------------- 1 | // TableChildExampleViewController.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Foundation 26 | import XLPagerTabStrip 27 | 28 | class TableChildExampleViewController: UITableViewController, IndicatorInfoProvider { 29 | 30 | let cellIdentifier = "postCell" 31 | var blackTheme = false 32 | var itemInfo = IndicatorInfo(title: "View") 33 | 34 | init(style: UITableView.Style, itemInfo: IndicatorInfo) { 35 | self.itemInfo = itemInfo 36 | super.init(style: style) 37 | } 38 | 39 | required init?(coder aDecoder: NSCoder) { 40 | fatalError("init(coder:) has not been implemented") 41 | } 42 | 43 | override func viewDidLoad() { 44 | super.viewDidLoad() 45 | tableView.register(UINib(nibName: "PostCell", bundle: Bundle.main), forCellReuseIdentifier: cellIdentifier) 46 | tableView.estimatedRowHeight = 600.0 47 | tableView.rowHeight = UITableView.automaticDimension 48 | tableView.allowsSelection = false 49 | if blackTheme { 50 | tableView.backgroundColor = UIColor(red: 15/255.0, green: 16/255.0, blue: 16/255.0, alpha: 1.0) 51 | } 52 | } 53 | 54 | override func viewWillAppear(_ animated: Bool) { 55 | super.viewWillAppear(animated) 56 | tableView.reloadData() 57 | } 58 | 59 | // MARK: - UITableViewDataSource 60 | 61 | override func numberOfSections(in tableView: UITableView) -> Int { 62 | return 1 63 | } 64 | 65 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 66 | return DataProvider.sharedInstance.postsData.count 67 | } 68 | 69 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 70 | guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? PostCell, 71 | let data = DataProvider.sharedInstance.postsData.object(at: indexPath.row) as? NSDictionary else { return PostCell() } 72 | 73 | cell.configureWithData(data) 74 | if blackTheme { 75 | cell.changeStylToBlack() 76 | } 77 | return cell 78 | } 79 | 80 | // MARK: - IndicatorInfoProvider 81 | 82 | func indicatorInfo(for pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo { 83 | return itemInfo 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /Example/Example/Helpers/DataProvider.swift: -------------------------------------------------------------------------------- 1 | // PostCell.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Foundation 26 | import UIKit 27 | 28 | class DataProvider { 29 | static let sharedInstance = DataProvider() 30 | 31 | lazy var postsData: NSArray = { 32 | let jsonStr = "[{\"post\":{\"id\":113,\"text\":\"We're getting fifty percent of the t-shirt sales\",\"created_at\":\"2014-04-17T00:45:40.556Z\",\"user\":{\"id\":9,\"name\":\"Lisa Simpsons\",\"imageURL\":\"http://obscure-refuge-3149.herokuapp.com/images/Lisa_Simpsons.png\"}}},{\"post\":{\"id\":66,\"text\":\"Eep!\",\"created_at\":\"2014-04-09T21:29:59.982Z\",\"user\":{\"id\":7,\"name\":\"Bart Simpsons\",\"imageURL\":\"http://obscure-refuge-3149.herokuapp.com/images/Bart_Simpsons.png\"}}},{\"post\":{\"id\":42,\"text\":\"I'm not thinking straight, why did I have that wine cooler last month?\",\"created_at\":\"2014-04-09T17:58:41.704Z\",\"user\":{\"id\":5,\"name\":\"Ned Flanders\",\"imageURL\":\"http://obscure-refuge-3149.herokuapp.com/images/Ned_Flanders.png\"}}},{\"post\":{\"id\":84,\"text\":\"Son, when you participate in sporting events, it's not whether you win or lose: it's how drunk you get.\",\"created_at\":\"2014-04-03T20:21:32.119Z\",\"user\":{\"id\":8,\"name\":\"Homer Simpsons\",\"imageURL\":\"http://obscure-refuge-3149.herokuapp.com/images/Homer_Simpsons.png\"}}},{\"post\":{\"id\":75,\"text\":\"I'm normally not a praying man, but if you're up there, please save me Superman.\",\"created_at\":\"2014-04-03T02:04:43.053Z\",\"user\":{\"id\":8,\"name\":\"Homer Simpsons\",\"imageURL\":\"http://obscure-refuge-3149.herokuapp.com/images/Homer_Simpsons.png\"}}},{\"post\":{\"id\":26,\"text\":\"Homer, please get rid of that pig.\",\"created_at\":\"2014-04-02T03:48:56.381Z\",\"user\":{\"id\":3,\"name\":\"Marge Simpsons\",\"imageURL\":\"http://obscure-refuge-3149.herokuapp.com/images/Marge_Simpsons.png\"}}},{\"post\":{\"id\":40,\"text\":\"You sold weapon-grade plutoneum to the Iraqies without a mark up.\",\"created_at\":\"2014-03-28T05:23:24.657Z\",\"user\":{\"id\":4,\"name\":\"Montgomery Burns\",\"imageURL\":\"http://obscure-refuge-3149.herokuapp.com/images/Montgomery_Burns.png\"}}},{\"post\":{\"id\":28,\"text\":\"Homer, don't say that. The way I see it, if you raised three children who can knock out and hog tie a perfect stranger you must be doing something right.\",\"created_at\":\"2014-03-22T14:24:22.408Z\",\"user\":{\"id\":3,\"name\":\"Marge Simpsons\",\"imageURL\":\"http://obscure-refuge-3149.herokuapp.com/images/Marge_Simpsons.png\"}}},{\"post\":{\"id\":48,\"text\":\"Hi-dilly-ho, neighborinos!\",\"created_at\":\"2014-03-21T08:39:20.764Z\",\"user\":{\"id\":5,\"name\":\"Ned Flanders\",\"imageURL\":\"http://obscure-refuge-3149.herokuapp.com/images/Ned_Flanders.png\"}}},{\"post\":{\"id\":78,\"text\":\"Maybe, just once, someone will call me 'Sir' without adding, 'You're making a scene.'\",\"created_at\":\"2014-03-20T02:44:28.075Z\",\"user\":{\"id\":8,\"name\":\"Homer Simpsons\",\"imageURL\":\"http://obscure-refuge-3149.herokuapp.com/images/Homer_Simpsons.png\"}}},{\"post\":{\"id\":33,\"text\":\"This is the type of trickery I pay you for.\",\"created_at\":\"2014-03-18T08:25:14.507Z\",\"user\":{\"id\":4,\"name\":\"Montgomery Burns\",\"imageURL\":\"http://obscure-refuge-3149.herokuapp.com/images/Montgomery_Burns.png\"}}},{\"post\":{\"id\":72,\"text\":\"Oh, so they have internet on computers now!\",\"created_at\":\"2014-03-03T19:02:56.032Z\",\"user\":{\"id\":8,\"name\":\"Homer Simpsons\",\"imageURL\":\"http://obscure-refuge-3149.herokuapp.com/images/Homer_Simpsons.png\"}}},{\"post\":{\"id\":1,\"text\":\"You know, I do! I mean, there comes a time in a man's life when he asks himself, 'who will float my corpse down the Ganges?'\",\"created_at\":\"2014-02-24T14:09:00.912Z\",\"user\":{\"id\":1,\"name\":\"Apu Nahasapeemapetilon\",\"imageURL\":\"http://obscure-refuge-3149.herokuapp.com/images/Apu_Nahasapeemapetilon.png\"}}},{\"post\":{\"id\":62,\"text\":\"Ay Caramba!\",\"created_at\":\"2014-02-18T16:38:37.958Z\",\"user\":{\"id\":7,\"name\":\"Bart Simpsons\",\"imageURL\":\"http://obscure-refuge-3149.herokuapp.com/images/Bart_Simpsons.png\"}}},{\"post\":{\"id\":19,\"text\":\"Throughout the ages, the finger painter, the Play-Doh sculptor, the Lincoln Logger, stood alone against the daycare teacher of her time. She did not live to earn aproval stickers, she lived for herself, that she might achieve things that are the glory of all humanity. These are my terms. I do not care to play by any others. And now, if the jury will allow me, it's naptime.\",\"created_at\":\"2014-02-16T22:11:33.236Z\",\"user\":{\"id\":2,\"name\":\"Maggie Simpsons\",\"imageURL\":\"http://obscure-refuge-3149.herokuapp.com/images/Maggie_Simpsons.png\"}}},{\"post\":{\"id\":76,\"text\":\"Son, if you really want something in this life, you have to work for it. Now quiet! They're about to announce the lottery numbers.\",\"created_at\":\"2014-02-16T19:09:55.062Z\",\"user\":{\"id\":8,\"name\":\"Homer Simpsons\",\"imageURL\":\"http://obscure-refuge-3149.herokuapp.com/images/Homer_Simpsons.png\"}}},{\"post\":{\"id\":22,\"text\":\"Somebody throw the goddamn bomb!\",\"created_at\":\"2014-02-16T13:50:25.313Z\",\"user\":{\"id\":3,\"name\":\"Marge Simpsons\",\"imageURL\":\"http://obscure-refuge-3149.herokuapp.com/images/Marge_Simpsons.png\"}}},{\"post\":{\"id\":36,\"text\":\"Oh, so mother nature needs a favor? Well, maybe she should have thought of that when she was besetting us with droughts and floods and poison monkeys.\",\"created_at\":\"2014-02-13T06:51:57.549Z\",\"user\":{\"id\":4,\"name\":\"Montgomery Burns\",\"imageURL\":\"http://obscure-refuge-3149.herokuapp.com/images/Montgomery_Burns.png\"}}}]" 33 | let jsonData = jsonStr.data(using: String.Encoding.utf8) 34 | let res = try! JSONSerialization.jsonObject(with: jsonData!, options: []) // swiftlint:disable:this force_try 35 | return res as! NSArray // swiftlint:disable:this force_cast 36 | }() 37 | } 38 | 39 | class NavController: UINavigationController { 40 | 41 | override var preferredStatusBarStyle: UIStatusBarStyle { 42 | return .lightContent 43 | } 44 | 45 | } 46 | 47 | class TabBarController: UITabBarController { 48 | 49 | override var preferredStatusBarStyle: UIStatusBarStyle { 50 | return .lightContent 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /Example/Example/Helpers/PostCell.swift: -------------------------------------------------------------------------------- 1 | // PostCell.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import UIKit 26 | 27 | class PostCell: UITableViewCell { 28 | 29 | @IBOutlet weak var userImage: UIImageView! 30 | @IBOutlet weak var postName: UILabel! 31 | @IBOutlet weak var postText: UILabel! 32 | 33 | override func awakeFromNib() { 34 | super.awakeFromNib() 35 | userImage.layer.cornerRadius = 10.0 36 | } 37 | 38 | func configureWithData(_ data: NSDictionary) { 39 | if let post = data["post"] as? NSDictionary, let user = post["user"] as? NSDictionary { 40 | postName.text = user["name"] as? String 41 | postText.text = post["text"] as? String 42 | userImage.image = UIImage(named: postName.text!.replacingOccurrences(of: " ", with: "_")) 43 | } 44 | } 45 | 46 | func changeStylToBlack() { 47 | userImage?.layer.cornerRadius = 30.0 48 | postText.text = nil 49 | postName.font = UIFont(name: "HelveticaNeue-Light", size:18) ?? .systemFont(ofSize: 18) 50 | postName.textColor = .white 51 | backgroundColor = UIColor(red: 15/255.0, green: 16/255.0, blue: 16/255.0, alpha: 1.0) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Example/Example/Helpers/PostCell.xib: -------------------------------------------------------------------------------- 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 | 34 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /Example/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 | 9.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Storyboard 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UIViewControllerBasedStatusBarAppearance 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Example/Example/Instagram/InstagramExampleViewController.swift: -------------------------------------------------------------------------------- 1 | // SegmentedExampleViewController.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Foundation 26 | import XLPagerTabStrip 27 | 28 | class InstagramExampleViewController: ButtonBarPagerTabStripViewController { 29 | 30 | @IBOutlet weak var shadowView: UIView! 31 | let blueInstagramColor = UIColor(red: 37/255.0, green: 111/255.0, blue: 206/255.0, alpha: 1.0) 32 | 33 | override func viewDidLoad() { 34 | // change selected bar color 35 | settings.style.buttonBarBackgroundColor = .white 36 | settings.style.buttonBarItemBackgroundColor = .white 37 | settings.style.selectedBarBackgroundColor = blueInstagramColor 38 | settings.style.buttonBarItemFont = .boldSystemFont(ofSize: 14) 39 | settings.style.selectedBarHeight = 2.0 40 | settings.style.buttonBarMinimumLineSpacing = 0 41 | settings.style.buttonBarItemTitleColor = .black 42 | settings.style.buttonBarItemsShouldFillAvailableWidth = true 43 | settings.style.buttonBarLeftContentInset = 0 44 | settings.style.buttonBarRightContentInset = 0 45 | 46 | changeCurrentIndexProgressive = { [weak self] (oldCell: ButtonBarViewCell?, newCell: ButtonBarViewCell?, progressPercentage: CGFloat, changeCurrentIndex: Bool, animated: Bool) -> Void in 47 | guard changeCurrentIndex == true else { return } 48 | oldCell?.label.textColor = .black 49 | newCell?.label.textColor = self?.blueInstagramColor 50 | } 51 | super.viewDidLoad() 52 | } 53 | 54 | // MARK: - PagerTabStripDataSource 55 | 56 | override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] { 57 | let child_1 = TableChildExampleViewController(style: .plain, itemInfo: "FOLLOWING") 58 | let child_2 = ChildExampleViewController(itemInfo: "YOU") 59 | return [child_1, child_2] 60 | } 61 | 62 | // MARK: - Custom Action 63 | 64 | @IBAction func closeAction(_ sender: UIButton) { 65 | dismiss(animated: true, completion: nil) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Example/Example/NavButtonBarExampleViewController.swift: -------------------------------------------------------------------------------- 1 | // NavButtonBarExampleViewController.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Foundation 26 | import XLPagerTabStrip 27 | 28 | class NavButtonBarExampleViewController: ButtonBarPagerTabStripViewController { 29 | var isReload = false 30 | 31 | override func viewDidLoad() { 32 | // set up style before super view did load is executed 33 | settings.style.buttonBarBackgroundColor = .clear 34 | settings.style.selectedBarBackgroundColor = .orange 35 | //- 36 | super.viewDidLoad() 37 | 38 | buttonBarView.removeFromSuperview() 39 | navigationController?.navigationBar.addSubview(buttonBarView) 40 | 41 | changeCurrentIndexProgressive = { (oldCell: ButtonBarViewCell?, newCell: ButtonBarViewCell?, progressPercentage: CGFloat, changeCurrentIndex: Bool, animated: Bool) -> Void in 42 | guard changeCurrentIndex == true else { return } 43 | 44 | oldCell?.label.textColor = UIColor(white: 1, alpha: 0.6) 45 | newCell?.label.textColor = .white 46 | 47 | if animated { 48 | UIView.animate(withDuration: 0.1, animations: { () -> Void in 49 | newCell?.transform = CGAffineTransform(scaleX: 1.0, y: 1.0) 50 | oldCell?.transform = CGAffineTransform(scaleX: 0.8, y: 0.8) 51 | }) 52 | } else { 53 | newCell?.transform = CGAffineTransform(scaleX: 1.0, y: 1.0) 54 | oldCell?.transform = CGAffineTransform(scaleX: 0.8, y: 0.8) 55 | } 56 | } 57 | } 58 | 59 | // MARK: - PagerTabStripDataSource 60 | 61 | override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] { 62 | let child_1 = TableChildExampleViewController(style: .plain, itemInfo: "Table View") 63 | let child_2 = ChildExampleViewController(itemInfo: "View") 64 | let child_3 = TableChildExampleViewController(style: .grouped, itemInfo: "Table View 2") 65 | let child_4 = ChildExampleViewController(itemInfo: "View 1") 66 | let child_5 = TableChildExampleViewController(style: .plain, itemInfo: "Table View 3") 67 | let child_6 = ChildExampleViewController(itemInfo: "View 2") 68 | let child_7 = TableChildExampleViewController(style: .grouped, itemInfo: "Table View 4") 69 | let child_8 = ChildExampleViewController(itemInfo: "View 3") 70 | 71 | guard isReload else { 72 | return [child_1, child_2, child_3, child_4, child_5, child_6, child_7, child_8] 73 | } 74 | 75 | var childViewControllers = [child_1, child_2, child_3, child_4, child_5, child_6, child_7, child_8] 76 | 77 | for index in childViewControllers.indices { 78 | let nElements = childViewControllers.count - index 79 | let n = (Int(arc4random()) % nElements) + index 80 | if n != index { 81 | childViewControllers.swapAt(index, n) 82 | } 83 | } 84 | let nItems = 1 + (arc4random() % 8) 85 | return Array(childViewControllers.prefix(Int(nItems))) 86 | } 87 | 88 | override func reloadPagerTabStripView() { 89 | isReload = true 90 | if arc4random() % 2 == 0 { 91 | pagerBehaviour = .progressive(skipIntermediateViewControllers: arc4random() % 2 == 0, elasticIndicatorLimit: arc4random() % 2 == 0 ) 92 | } else { 93 | pagerBehaviour = .common(skipIntermediateViewControllers: arc4random() % 2 == 0) 94 | } 95 | super.reloadPagerTabStripView() 96 | } 97 | 98 | override func configureCell(_ cell: ButtonBarViewCell, indicatorInfo: IndicatorInfo) { 99 | super.configureCell(cell, indicatorInfo: indicatorInfo) 100 | cell.backgroundColor = .clear 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Example/Example/ReloadExampleViewController.swift: -------------------------------------------------------------------------------- 1 | // ReloadExampleViewController.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import UIKit 26 | import XLPagerTabStrip 27 | 28 | class ReloadExampleViewController: UIViewController { 29 | 30 | @IBOutlet lazy var titleLabel: UILabel! = { 31 | let label = UILabel() 32 | return label 33 | }() 34 | 35 | lazy var bigLabel: UILabel = { 36 | let bigLabel = UILabel() 37 | bigLabel.backgroundColor = .clear 38 | bigLabel.textColor = .white 39 | bigLabel.font = UIFont.boldSystemFont(ofSize: 20) 40 | bigLabel.adjustsFontSizeToFitWidth = true 41 | return bigLabel 42 | }() 43 | 44 | override func viewDidLoad() { 45 | super.viewDidLoad() 46 | 47 | if navigationController != nil { 48 | navigationItem.titleView = bigLabel 49 | bigLabel.sizeToFit() 50 | } 51 | 52 | if let pagerViewController = children.first as? PagerTabStripViewController { 53 | updateTitle(of: pagerViewController) 54 | } 55 | } 56 | 57 | @IBAction func reloadTapped(_ sender: UIBarButtonItem) { 58 | for childViewController in children { 59 | guard let child = childViewController as? PagerTabStripViewController else { 60 | continue 61 | } 62 | child.reloadPagerTabStripView() 63 | updateTitle(of: child) 64 | break 65 | } 66 | } 67 | 68 | @IBAction func closeTapped(_ sender: UIButton) { 69 | dismiss(animated: true, completion: nil) 70 | } 71 | 72 | func updateTitle(of pagerTabStripViewController: PagerTabStripViewController) { 73 | func stringFromBool(_ bool: Bool) -> String { 74 | return bool ? "YES" : "NO" 75 | } 76 | 77 | titleLabel.text = "Progressive = \(stringFromBool(pagerTabStripViewController.pagerBehaviour.isProgressiveIndicator)) ElasticLimit = \(stringFromBool(pagerTabStripViewController.pagerBehaviour.isElasticIndicatorLimit))" 78 | 79 | (navigationItem.titleView as? UILabel)?.text = titleLabel.text 80 | navigationItem.titleView?.sizeToFit() 81 | } 82 | 83 | override var preferredStatusBarStyle: UIStatusBarStyle { 84 | return .lightContent 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Example/Example/SegmentedExampleViewController.swift: -------------------------------------------------------------------------------- 1 | // SegmentedExampleViewController.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Foundation 26 | import XLPagerTabStrip 27 | 28 | class SegmentedExampleViewController: SegmentedPagerTabStripViewController { 29 | 30 | var isReload = false 31 | 32 | required init?(coder aDecoder: NSCoder) { 33 | super.init(coder: aDecoder) 34 | // change segmented style 35 | settings.style.segmentedControlColor = .white 36 | } 37 | 38 | // MARK: - PagerTabStripDataSource 39 | 40 | override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] { 41 | let child_1 = TableChildExampleViewController(style: .plain, itemInfo: "Table View") 42 | let child_2 = ChildExampleViewController(itemInfo: "View") 43 | let child_3 = TableChildExampleViewController(style: .grouped, itemInfo: "Table View 2") 44 | let child_4 = ChildExampleViewController(itemInfo: "View 2") 45 | 46 | guard isReload else { 47 | return [child_1, child_2, child_3, child_4] 48 | } 49 | 50 | var childViewControllers = [child_1, child_2, child_3, child_4] 51 | let count = childViewControllers.count 52 | 53 | for index in childViewControllers.indices { 54 | let nElements = count - index 55 | let n = (Int(arc4random()) % nElements) + index 56 | if n != index { 57 | childViewControllers.swapAt(index, n) 58 | } 59 | } 60 | let nItems = 1 + (arc4random() % 4) 61 | return Array(childViewControllers.prefix(Int(nItems))) 62 | } 63 | 64 | @IBAction func reloadTapped(_ sender: UIBarButtonItem) { 65 | isReload = true 66 | pagerBehaviour = .common(skipIntermediateViewControllers: arc4random() % 2 == 0) 67 | reloadPagerTabStripView() 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Example/Example/Spotify/SpotifyExampleViewController.swift: -------------------------------------------------------------------------------- 1 | // SpotifyExampleViewController.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Foundation 26 | import XLPagerTabStrip 27 | 28 | class SpotifyExampleViewController: ButtonBarPagerTabStripViewController { 29 | 30 | @IBOutlet weak var shadowView: UIView! 31 | 32 | let graySpotifyColor = UIColor(red: 21/255.0, green: 21/255.0, blue: 24/255.0, alpha: 1.0) 33 | let darkGraySpotifyColor = UIColor(red: 19/255.0, green: 20/255.0, blue: 20/255.0, alpha: 1.0) 34 | 35 | override func viewDidLoad() { 36 | // change selected bar color 37 | settings.style.buttonBarBackgroundColor = graySpotifyColor 38 | settings.style.buttonBarItemBackgroundColor = graySpotifyColor 39 | settings.style.selectedBarBackgroundColor = UIColor(red: 33/255.0, green: 174/255.0, blue: 67/255.0, alpha: 1.0) 40 | settings.style.buttonBarItemFont = UIFont(name: "HelveticaNeue-Light", size:14) ?? UIFont.systemFont(ofSize: 14) 41 | settings.style.selectedBarHeight = 3.0 42 | settings.style.buttonBarMinimumLineSpacing = 0 43 | settings.style.buttonBarItemTitleColor = .black 44 | settings.style.buttonBarItemsShouldFillAvailableWidth = true 45 | 46 | settings.style.buttonBarLeftContentInset = 20 47 | settings.style.buttonBarRightContentInset = 20 48 | 49 | changeCurrentIndexProgressive = { (oldCell: ButtonBarViewCell?, newCell: ButtonBarViewCell?, progressPercentage: CGFloat, changeCurrentIndex: Bool, animated: Bool) -> Void in 50 | guard changeCurrentIndex == true else { return } 51 | oldCell?.label.textColor = UIColor(red: 138/255.0, green: 138/255.0, blue: 144/255.0, alpha: 1.0) 52 | newCell?.label.textColor = .white 53 | } 54 | super.viewDidLoad() 55 | } 56 | 57 | // MARK: - PagerTabStripDataSource 58 | 59 | override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] { 60 | let child_1 = TableChildExampleViewController(style: .plain, itemInfo: IndicatorInfo(title: "FRIENDS")) 61 | child_1.blackTheme = true 62 | let child_2 = TableChildExampleViewController(style: .plain, itemInfo: IndicatorInfo(title: "FEATURED")) 63 | child_2.blackTheme = true 64 | return [child_1, child_2] 65 | } 66 | 67 | // MARK: - Actions 68 | 69 | @IBAction func closeAction(_ sender: UIButton) { 70 | dismiss(animated: true, completion: nil) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Example/Example/TwitterExampleViewController.swift: -------------------------------------------------------------------------------- 1 | // TwitterExampleViewController.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Foundation 26 | import XLPagerTabStrip 27 | 28 | class TwitterExampleViewController: TwitterPagerTabStripViewController { 29 | var isReload = false 30 | 31 | override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] { 32 | 33 | let child_1 = TableChildExampleViewController(style: .plain, itemInfo: "TableView") 34 | let child_2 = ChildExampleViewController(itemInfo: "View") 35 | let child_3 = TableChildExampleViewController(style: .grouped, itemInfo: "TableView 2") 36 | let child_4 = ChildExampleViewController(itemInfo: "View 2") 37 | let child_5 = TableChildExampleViewController(style: .plain, itemInfo: "TableView 3") 38 | let child_6 = ChildExampleViewController(itemInfo: "View 3") 39 | let child_7 = TableChildExampleViewController(style: .grouped, itemInfo: "TableView 4") 40 | let child_8 = ChildExampleViewController(itemInfo: "View 4") 41 | 42 | guard isReload else { 43 | return [child_1, child_2, child_3, child_4, child_5, child_6, child_7, child_8] 44 | } 45 | 46 | var childViewControllers = [child_1, child_2, child_3, child_4, child_5, child_6, child_7, child_8] 47 | 48 | for index in childViewControllers.indices { 49 | let nElements = childViewControllers.count - index 50 | let n = (Int(arc4random()) % nElements) + index 51 | if n != index { 52 | childViewControllers.swapAt(index, n) 53 | } 54 | } 55 | let nItems = 1 + (arc4random() % 8) 56 | return Array(childViewControllers.prefix(Int(nItems))) 57 | } 58 | 59 | @IBAction func reloadTapped(_ sender: AnyObject) { 60 | isReload = true 61 | reloadPagerTabStripView() 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Example/Example/Youtube/YoutubeExampleViewController.swift: -------------------------------------------------------------------------------- 1 | // YoutubeExampleViewController.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Foundation 26 | import XLPagerTabStrip 27 | 28 | class YoutubeExampleViewController: BaseButtonBarPagerTabStripViewController { 29 | 30 | let redColor = UIColor(red: 221/255.0, green: 0/255.0, blue: 19/255.0, alpha: 1.0) 31 | let unselectedIconColor = UIColor(red: 73/255.0, green: 8/255.0, blue: 10/255.0, alpha: 1.0) 32 | 33 | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { 34 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 35 | } 36 | 37 | required init?(coder aDecoder: NSCoder) { 38 | super.init(coder: aDecoder) 39 | 40 | buttonBarItemSpec = ButtonBarItemSpec.nibFile(nibName: "YoutubeIconCell", bundle: Bundle(for: YoutubeIconCell.self), width: { _ in 41 | return 55.0 42 | }) 43 | } 44 | 45 | override func viewDidLoad() { 46 | // change selected bar color 47 | settings.style.buttonBarBackgroundColor = redColor 48 | settings.style.buttonBarItemBackgroundColor = .clear 49 | settings.style.selectedBarBackgroundColor = UIColor(red: 234/255.0, green: 234/255.0, blue: 234/255.0, alpha: 1.0) 50 | settings.style.selectedBarHeight = 4.0 51 | settings.style.buttonBarMinimumLineSpacing = 0 52 | settings.style.buttonBarItemTitleColor = .black 53 | settings.style.buttonBarItemsShouldFillAvailableWidth = true 54 | settings.style.buttonBarLeftContentInset = 0 55 | settings.style.buttonBarRightContentInset = 0 56 | 57 | changeCurrentIndexProgressive = { [weak self] (oldCell: YoutubeIconCell?, newCell: YoutubeIconCell?, progressPercentage: CGFloat, changeCurrentIndex: Bool, animated: Bool) -> Void in 58 | guard changeCurrentIndex == true else { return } 59 | oldCell?.iconImage.tintColor = self?.unselectedIconColor 60 | newCell?.iconImage.tintColor = .white 61 | } 62 | super.viewDidLoad() 63 | navigationController?.navigationBar.shadowImage = UIImage() 64 | navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default) 65 | } 66 | 67 | // MARK: - PagerTabStripDataSource 68 | 69 | override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] { 70 | let child_1 = TableChildExampleViewController(style: .plain, itemInfo: IndicatorInfo(title: " HOME", image: UIImage(named: "home"))) 71 | let child_2 = TableChildExampleViewController(style: .plain, itemInfo: IndicatorInfo(title: " TRENDING", image: UIImage(named: "trending"))) 72 | let child_3 = ChildExampleViewController(itemInfo: IndicatorInfo(title: " ACCOUNT", image: UIImage(named: "profile"))) 73 | return [child_1, child_2, child_3] 74 | } 75 | 76 | override func configure(cell: YoutubeIconCell, for indicatorInfo: IndicatorInfo) { 77 | cell.iconImage.image = indicatorInfo.image?.withRenderingMode(.alwaysTemplate) 78 | } 79 | 80 | override func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int, withProgressPercentage progressPercentage: CGFloat, indexWasChanged: Bool) { 81 | super.updateIndicator(for: viewController, fromIndex: fromIndex, toIndex: toIndex, withProgressPercentage: progressPercentage, indexWasChanged: indexWasChanged) 82 | if indexWasChanged && toIndex > -1 && toIndex < viewControllers.count { 83 | let child = viewControllers[toIndex] as! IndicatorInfoProvider // swiftlint:disable:this force_cast 84 | UIView.performWithoutAnimation({ [weak self] () -> Void in 85 | guard let me = self else { return } 86 | me.navigationItem.leftBarButtonItem?.title = child.indicatorInfo(for: me).title 87 | }) 88 | } 89 | } 90 | 91 | // MARK: - Actions 92 | 93 | @IBAction func closeAction(_ sender: UIButton) { 94 | dismiss(animated: true, completion: nil) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Example/Example/Youtube/YoutubeIconCell.swift: -------------------------------------------------------------------------------- 1 | // YoutubeIconCell.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Foundation 26 | import UIKit 27 | 28 | class YoutubeIconCell: UICollectionViewCell { 29 | 30 | @IBOutlet weak var iconImage: UIImageView! 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Example/Example/Youtube/YoutubeIconCell.xib: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Example/Example/YoutubeWithLabel/YoutubeIconWithLabelCell.swift: -------------------------------------------------------------------------------- 1 | // YoutubeIconWithLabelCell.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Foundation 26 | import UIKit 27 | 28 | class YoutubeIconWithLabelCell: UICollectionViewCell { 29 | 30 | @IBOutlet weak var iconImage: UIImageView! 31 | @IBOutlet weak var iconLabel: UILabel! 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Example/Example/YoutubeWithLabel/YoutubeIconWithLabelCell.xib: -------------------------------------------------------------------------------- 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 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Example/Example/YoutubeWithLabel/YoutubeWithLabelExampleViewController.swift: -------------------------------------------------------------------------------- 1 | // YoutubeWithLabelExampleViewController.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Foundation 26 | import XLPagerTabStrip 27 | 28 | class YoutubeWithLabelExampleViewController: BaseButtonBarPagerTabStripViewController { 29 | 30 | let redColor = UIColor(red: 221/255.0, green: 0/255.0, blue: 19/255.0, alpha: 1.0) 31 | let unselectedIconColor = UIColor(red: 73/255.0, green: 8/255.0, blue: 10/255.0, alpha: 1.0) 32 | 33 | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { 34 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 35 | } 36 | 37 | required init?(coder aDecoder: NSCoder) { 38 | super.init(coder: aDecoder) 39 | 40 | buttonBarItemSpec = ButtonBarItemSpec.nibFile(nibName: "YoutubeIconWithLabelCell", bundle: Bundle(for: YoutubeIconWithLabelCell.self), width: { _ in 41 | return 70.0 42 | }) 43 | } 44 | 45 | override func viewDidLoad() { 46 | // change selected bar color 47 | settings.style.buttonBarBackgroundColor = redColor 48 | settings.style.buttonBarItemBackgroundColor = .clear 49 | settings.style.selectedBarBackgroundColor = UIColor(red: 234/255.0, green: 234/255.0, blue: 234/255.0, alpha: 1.0) 50 | settings.style.selectedBarHeight = 4.0 51 | settings.style.buttonBarMinimumLineSpacing = 0 52 | settings.style.buttonBarItemTitleColor = .black 53 | settings.style.buttonBarItemsShouldFillAvailableWidth = true 54 | settings.style.buttonBarLeftContentInset = 0 55 | settings.style.buttonBarRightContentInset = 0 56 | 57 | changeCurrentIndexProgressive = { [weak self] (oldCell: YoutubeIconWithLabelCell?, newCell: YoutubeIconWithLabelCell?, progressPercentage: CGFloat, changeCurrentIndex: Bool, animated: Bool) -> Void in 58 | guard changeCurrentIndex == true else { return } 59 | oldCell?.iconImage.tintColor = self?.unselectedIconColor 60 | oldCell?.iconLabel.textColor = self?.unselectedIconColor 61 | newCell?.iconImage.tintColor = .white 62 | newCell?.iconLabel.textColor = .white 63 | } 64 | super.viewDidLoad() 65 | navigationController?.navigationBar.shadowImage = UIImage() 66 | navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default) 67 | } 68 | 69 | // MARK: - PagerTabStripDataSource 70 | 71 | override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] { 72 | let child_1 = TableChildExampleViewController(style: .plain, itemInfo: IndicatorInfo(title: " HOME", image: UIImage(named: "home"))) 73 | let child_2 = TableChildExampleViewController(style: .plain, itemInfo: IndicatorInfo(title: " TRENDING", image: UIImage(named: "trending"))) 74 | let child_3 = ChildExampleViewController(itemInfo: IndicatorInfo(title: " ACCOUNT", image: UIImage(named: "profile"))) 75 | return [child_1, child_2, child_3] 76 | } 77 | 78 | override func configure(cell: YoutubeIconWithLabelCell, for indicatorInfo: IndicatorInfo) { 79 | cell.iconImage.image = indicatorInfo.image?.withRenderingMode(.alwaysTemplate) 80 | cell.iconLabel.text = indicatorInfo.title?.trimmingCharacters(in: .whitespacesAndNewlines) 81 | } 82 | 83 | override func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int, withProgressPercentage progressPercentage: CGFloat, indexWasChanged: Bool) { 84 | super.updateIndicator(for: viewController, fromIndex: fromIndex, toIndex: toIndex, withProgressPercentage: progressPercentage, indexWasChanged: indexWasChanged) 85 | if indexWasChanged && toIndex > -1 && toIndex < viewControllers.count { 86 | let child = viewControllers[toIndex] as! IndicatorInfoProvider // swiftlint:disable:this force_cast 87 | UIView.performWithoutAnimation({ [weak self] () -> Void in 88 | guard let me = self else { return } 89 | me.navigationItem.leftBarButtonItem?.title = child.indicatorInfo(for: me).title 90 | }) 91 | } 92 | } 93 | 94 | // MARK: - Actions 95 | 96 | @IBAction func closeAction(_ sender: UIButton) { 97 | dismiss(animated: true, completion: nil) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Example/ExampleUITests/ExampleUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExampleUITests.swift 3 | // ExampleUITests 4 | // 5 | // Copyright © 2016 Xmartlabs SRL. All rights reserved. 6 | // 7 | 8 | import XCTest 9 | 10 | class ExampleUITests: XCTestCase { 11 | 12 | override func setUp() { 13 | super.setUp() 14 | 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | 17 | // In UI tests it is usually best to stop immediately when a failure occurs. 18 | continueAfterFailure = false 19 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 20 | XCUIApplication().launch() 21 | 22 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 23 | } 24 | 25 | override func tearDown() { 26 | // Put teardown code here. This method is called after the invocation of each test method in the class. 27 | super.tearDown() 28 | } 29 | 30 | func testExample() { 31 | // Use recording to get started writing UI tests. 32 | // Use XCTAssert and related functions to verify your tests produce the correct results. 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /Example/ExampleUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/bar.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/bar.gif -------------------------------------------------------------------------------- /Example/barButton.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/barButton.gif -------------------------------------------------------------------------------- /Example/instagram.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/instagram.gif -------------------------------------------------------------------------------- /Example/navBarButton.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/navBarButton.gif -------------------------------------------------------------------------------- /Example/pagerTabStripTypes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/pagerTabStripTypes.gif -------------------------------------------------------------------------------- /Example/segmented.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/segmented.gif -------------------------------------------------------------------------------- /Example/spotify.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/spotify.gif -------------------------------------------------------------------------------- /Example/twitter.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/twitter.gif -------------------------------------------------------------------------------- /Example/youtube.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/Example/youtube.gif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Xmartlabs SRL 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Migration.md: -------------------------------------------------------------------------------- 1 | ## How to migrate from Swift 2 to Swift 3 2 | 3 | To migrate from Swift 2 to Swift 3 you have to change the naming of some of the functions you call or override. These are the name changes for version 6.0+ in `PagerTabStripViewController`: 4 | 5 | | Swift 2 function name | Swift 3 function name | 6 | | --------------------- | --------------------- | 7 | | `func viewControllersForPagerTabStrip(_ pagerTabStripController: PagerTabStripViewController) -> [UIViewController]` | `func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController]` | 8 | | `func indicatorInfoForPagerTabStrip(_ pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo` | `func indicatorInfo(for pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo` | 9 | | `func pagerTabStripViewController(_ pagerTabStripViewController: PagerTabStripViewController, updateIndicatorFromIndex fromIndex: Int, toIndex: Int)` | `func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int)` | 10 | | `func pagerTabStripViewController(_ pagerTabStripViewController: PagerTabStripViewController, updateIndicatorFromIndex fromIndex: Int, toIndex: Int, withProgressPercentage progressPercentage: CGFloat, indexWasChanged: Bool)` | `func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int, withProgressPercentage progressPercentage: CGFloat, indexWasChanged: Bool)` | 11 | | `func moveToViewControllerAtIndex(_ index: Int, animated: Bool = true)` | `func moveToViewController(at index: Int, animated: Bool = true)` | 12 | | `func moveToViewController(_ viewController: UIViewController, animated: Bool = true)` | `func moveTo(viewController: UIViewController, animated: Bool = true)` | 13 | | `func canMoveToIndex(index: Int) -> Bool` | `func canMoveTo(index: Int) -> Bool` | 14 | | ` func pageOffsetForChildIndex(index: Int) -> CGFloat` | `func pageOffsetForChild(at index: Int) -> CGFloat` | 15 | | `func offsetForChildIndex(_ index: Int) -> CGFloat` | `func offsetForChild(at index: Int) -> CGFloat` | 16 | | `func offsetForChildViewController(_ viewController: UIViewController) throws -> CGFloat` | `func offsetForChild(viewController: UIViewController) throws -> CGFloat` | 17 | | `func pageForContentOffset(_ contentOffset: CGFloat) -> Int` | `func pageFor(contentOffset: CGFloat) -> Int` | 18 | | `func virtualPageForContentOffset(_ contentOffset: CGFloat) -> Int` | `func virtualPageFor(contentOffset: CGFloat) -> Int` | 19 | | `func pageForVirtualPage(_ virtualPage: Int) -> Int` | `func pageFor(virtualPage: Int) -> Int` | 20 | 21 | You can check all the changes in [this pull request](https://github.com/xmartlabs/XLPagerTabStrip/pull/226) -------------------------------------------------------------------------------- /ObjC/FXPageControl.h: -------------------------------------------------------------------------------- 1 | // 2 | // FXPageControl.h 3 | // 4 | // Version 1.6 5 | // 6 | // Created by Nick Lockwood on 07/01/2010. 7 | // Copyright 2010 Charcoal Design 8 | // 9 | // Distributed under the permissive zlib License 10 | // Get the latest version of FXPageControl from here: 11 | // 12 | // https://github.com/nicklockwood/FXPageControl 13 | // 14 | // This software is provided 'as-is', without any express or implied 15 | // warranty. In no event will the authors be held liable for any damages 16 | // arising from the use of this software. 17 | // 18 | // Permission is granted to anyone to use this software for any purpose, 19 | // including commercial applications, and to alter it and redistribute it 20 | // freely, subject to the following restrictions: 21 | // 22 | // 1. The origin of this software must not be misrepresented; you must not 23 | // claim that you wrote the original software. If you use this software 24 | // in a product, an acknowledgment in the product documentation would be 25 | // appreciated but is not required. 26 | // 27 | // 2. Altered source versions must be plainly marked as such, and must not be 28 | // misrepresented as being the original software. 29 | // 30 | // 3. This notice may not be removed or altered from any source distribution. 31 | // 32 | 33 | 34 | #pragma clang diagnostic push 35 | #pragma clang diagnostic ignored "-Wobjc-missing-property-synthesis" 36 | #import 37 | 38 | 39 | NS_ASSUME_NONNULL_BEGIN 40 | 41 | 42 | extern const CGPathRef FXPageControlDotShapeCircle; 43 | extern const CGPathRef FXPageControlDotShapeSquare; 44 | extern const CGPathRef FXPageControlDotShapeTriangle; 45 | 46 | 47 | @protocol FXPageControlDelegate; 48 | 49 | 50 | IB_DESIGNABLE @interface FXPageControl : UIControl 51 | 52 | - (void)setUp; 53 | - (CGSize)sizeForNumberOfPages:(NSInteger)pageCount; 54 | - (void)updateCurrentPageDisplay; 55 | 56 | @property (nonatomic, weak) IBOutlet id delegate; 57 | 58 | @property (nonatomic, assign) IBInspectable NSInteger currentPage; 59 | @property (nonatomic, assign) IBInspectable NSInteger numberOfPages; 60 | @property (nonatomic, assign) IBInspectable BOOL defersCurrentPageDisplay; 61 | @property (nonatomic, assign) IBInspectable BOOL hidesForSinglePage; 62 | @property (nonatomic, assign, getter = isWrapEnabled) IBInspectable BOOL wrapEnabled; 63 | @property (nonatomic, assign, getter = isVertical) IBInspectable BOOL vertical; 64 | 65 | @property (nonatomic, assign) IBInspectable CGPathRef dotShape; 66 | @property (nonatomic, assign) IBInspectable CGFloat dotSize; 67 | @property (nonatomic, assign) IBInspectable CGFloat dotShadowBlur; 68 | @property (nonatomic, assign) IBInspectable CGSize dotShadowOffset; 69 | @property (nonatomic, assign) IBInspectable CGFloat dotBorderWidth; 70 | @property (nonatomic, strong, nullable) IBInspectable UIImage *dotImage; 71 | @property (nonatomic, strong, nullable) IBInspectable UIColor *dotColor; 72 | @property (nonatomic, strong, nullable) IBInspectable UIColor *dotShadowColor; 73 | @property (nonatomic, strong, nullable) IBInspectable UIColor *dotBorderColor; 74 | 75 | @property (nonatomic, assign) IBInspectable CGPathRef selectedDotShape; 76 | @property (nonatomic, assign) IBInspectable CGFloat selectedDotSize; 77 | @property (nonatomic, assign) IBInspectable CGFloat selectedDotShadowBlur; 78 | @property (nonatomic, assign) IBInspectable CGSize selectedDotShadowOffset; 79 | @property (nonatomic, assign) IBInspectable CGFloat selectedDotBorderWidth; 80 | @property (nonatomic, strong, nullable) IBInspectable UIImage *selectedDotImage; 81 | @property (nonatomic, strong, nullable) IBInspectable UIColor *selectedDotColor; 82 | @property (nonatomic, strong, nullable) IBInspectable UIColor *selectedDotShadowColor; 83 | @property (nonatomic, strong, nullable) IBInspectable UIColor *selectedDotBorderColor; 84 | 85 | @property (nonatomic, assign) IBInspectable CGFloat dotSpacing; 86 | 87 | @end 88 | 89 | 90 | @protocol FXPageControlDelegate 91 | @optional 92 | 93 | - (nullable UIImage *)pageControl:(FXPageControl *)pageControl imageForDotAtIndex:(NSInteger)index; 94 | - (CGPathRef)pageControl:(FXPageControl *)pageControl shapeForDotAtIndex:(NSInteger)index; 95 | - (UIColor *)pageControl:(FXPageControl *)pageControl colorForDotAtIndex:(NSInteger)index; 96 | 97 | - (nullable UIImage *)pageControl:(FXPageControl *)pageControl selectedImageForDotAtIndex:(NSInteger)index; 98 | - (CGPathRef)pageControl:(FXPageControl *)pageControl selectedShapeForDotAtIndex:(NSInteger)index; 99 | - (UIColor *)pageControl:(FXPageControl *)pageControl selectedColorForDotAtIndex:(NSInteger)index; 100 | 101 | @end 102 | 103 | 104 | NS_ASSUME_NONNULL_END 105 | 106 | 107 | #pragma clang diagnostic pop 108 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "FXPageControl", 6 | "repositoryURL": "https://github.com/nicklockwood/FXPageControl.git", 7 | "state": { 8 | "branch": null, 9 | "revision": "a94633402ba98c52f86c2a70e61ff086dec9de78", 10 | "version": "1.6.0" 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.4 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "XLPagerTabStrip", 8 | platforms: [ 9 | .iOS(.v11) 10 | ], 11 | products: [ 12 | // Products define the executables and libraries a package produces, and make them visible to other packages. 13 | .library( 14 | name: "XLPagerTabStrip", 15 | targets: ["XLPagerTabStrip"]), 16 | ], 17 | dependencies: [ 18 | .package( 19 | url: "https://github.com/nicklockwood/FXPageControl.git", 20 | .upToNextMajor(from: "1.6.0") 21 | ) 22 | ], 23 | targets: [ 24 | .target( 25 | name: "XLPagerTabStrip", 26 | dependencies: ["FXPageControl"]), 27 | ], 28 | swiftLanguageVersions: [ 29 | .v5 30 | ] 31 | ) 32 | -------------------------------------------------------------------------------- /Playground.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Playground.playground 3 | // XLPagerTabStrip 4 | // 5 | // Copyright © 2016 Xmartlabs SRL. All rights reserved. 6 | // 7 | 8 | //: Playground - noun: a place where people can play 9 | 10 | import UIKit 11 | import XLPagerTabStrip 12 | 13 | var helloWorld = "Hello, playground" 14 | -------------------------------------------------------------------------------- /Playground.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![XLPagerTabStripView](https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/master/XLPagerTabTrip.png) 2 | 3 |

4 | Build status 5 | Platform iOS 6 | Swift 5 compatible 7 | Carthage compatible 8 | CocoaPods compatible 9 | License: MIT 10 | 11 | 12 | 13 |

14 | 15 | Made with ❤️ by [XMARTLABS](http://xmartlabs.com). 16 | 17 | Android [PagerTabStrip](http://developer.android.com/reference/android/support/v4/view/PagerTabStrip.html) for iOS! 18 | 19 | 👉 Looking for a SwiftUI version? Check out [PagerTabStripView](https://github.com/xmartlabs/PagerTabStripView), it's fully written in pure SwiftUI. 👈 20 | 21 | **XLPagerTabStrip** is a *Container View Controller* that allows us to switch easily among a collection of view controllers. Pan gesture can be used to move on to next or previous view controller. It shows a interactive indicator of the current, previous, next child view controllers. 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | 32 | ## Getting involved 33 | 34 | * If you **want to contribute** please feel free to **submit pull requests**. 35 | * If you **have a feature request** please **open an issue**. 36 | * If you **found a bug** or **need help** please **check older issues, [FAQ](#faq) and threads on [StackOverflow](http://stackoverflow.com/questions/tagged/XLPagerTabStrip) (Tag 'XLPagerTabStrip') before submitting an issue**. 37 | 38 | **Before contribute check the [CONTRIBUTING](CONTRIBUTING.md) file for more info.** 39 | 40 | If you use **XLPagerTabStrip** in your app we would love to hear about it! Drop us a line on [twitter](https://twitter.com/xmartlabs). 41 | 42 | ## Pager Types 43 | 44 | The library provides 4 different ways to show the view controllers. 45 | 46 | ### Button Bar 47 | 48 | This is likely the most common pager type. It's used by many well-known apps such as instagram, youtube, skype, and many others. 49 | 50 | 51 | 52 | ### Bar 53 | 54 | This mode doesn't show a title neither an image. It only shows a bar that indicates the current view controller. 55 | 56 | 57 | 58 | ### Twitter 59 | 60 | A long time ago, the twitter app made use of this type of pager in the app main screen. 61 | 62 | 63 | 64 | ### Segmented 65 | 66 | This mode uses a `UISegmentedControl` to indicate which view controller is being displayed. 67 | 68 | 69 | 70 | ## Usage 71 | 72 | Basically, we just need to provide the list of child view controllers to show, and these view controllers should provide the information (title or image) that will be shown in the associated indicator. 73 | 74 | Let's see the steps to do this: 75 | 76 | ##### Choose which type of pager we want to create 77 | 78 | First, we must choose the type of pager we want to create. Depending on our choice, we will have to create a view controller that extends from one of the following controllers: `TwitterPagerTabStripViewController`, `ButtonBarPagerTabStripViewController`, `SegmentedPagerTabStripViewController`, `BarPagerTabStripViewController`. 79 | 80 | > All these built-in pager controllers extend from the base class `PagerTabStripViewController`. 81 | > You can also make your custom pager controller by extending directly from `PagerTabStripViewController` in the event that no pager menu type fits your needs. 82 | 83 | ```swift 84 | import XLPagerTabStrip 85 | 86 | class MyPagerTabStripName: ButtonBarPagerTabStripViewController { 87 | .. 88 | } 89 | ``` 90 | 91 | ##### Connect outlets and add layout constraints 92 | 93 | We strongly recommend using IB to set up our page controller views. 94 | 95 | Drag a `UIViewController` into the storyboard and set up its class with your pager controller (`MyPagerTabStripName`). 96 | Drag a `UIScrollView` into your view controller view and connect `PagerTabStripViewController` `containerView` outlet with the scroll view. 97 | 98 | Depending on which type of paging view controller you are working with you may have to connect more outlets. 99 | 100 | For `BarPagerTabStripViewController`, we should connect `barView` outlet. barView type is UIView. `ButtonBarPagerTabStripViewController` requires us to connect `buttonBarView` outlet. `buttonBarView` type is `ButtonBarView` which extends from `UICollectionView`. `SegmentedPagerTabStripViewController` has a `segmentedControl` outlet; if the outlet is not connected the library try to set up the navigationItem `titleView` property using a `UISegmentedControl`. `TwitterPagerTabStripViewController` doesn't require us to connect any additional outlet. 101 | 102 | > The example project contains a example for each pager controller type and we can look into it to see how views were added and how outlets were connected. 103 | 104 | ##### Provide the view controllers that will appear embedded into the PagerTabStrip view controller 105 | 106 | You can provide the view controllers by overriding `func viewControllers(for: pagerTabStripController: PagerTabStripViewController) -> [UIViewController]` method. 107 | 108 | ```swift 109 | override public func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] { 110 | return [MyEmbeddedViewController(), MySecondEmbeddedViewController()] 111 | } 112 | ``` 113 | 114 | > The method above is the only method declared in `PagerTabStripDataSource` protocol. We don't need to explicitly conform to it since base pager class already does it. 115 | 116 | 117 | ##### Provide information to show in each indicator 118 | 119 | Every UIViewController that will appear within the PagerTabStrip needs to provide either a title or an image. 120 | In order to do so they should conform to `IndicatorInfoProvider` by implementing `func indicatorInfo(for pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo` 121 | which provides the information required to show the PagerTabStrip menu (indicator) associated with the view controller. 122 | 123 | ```swift 124 | class MyEmbeddedViewController: UITableViewController, IndicatorInfoProvider { 125 | 126 | func indicatorInfo(for pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo { 127 | return IndicatorInfo(title: "My Child title") 128 | } 129 | } 130 | ``` 131 | 132 | **For a detailed step-by-step guide about how to use the library, please check out this community [blog post](https://medium.com/michaeladeyeri/how-to-implement-android-like-tab-layouts-in-ios-using-swift-3-578516c3aa9).** 133 | 134 | That's it! We're done! 🍻🍻 135 | 136 | 137 | ## Customization 138 | 139 | ##### Pager Behaviour 140 | 141 | The pager indicator can be updated progressive as we swipe or at once in the middle of the transition between the view controllers. 142 | By setting up `pagerBehaviour` property we can choose how the indicator should be updated. 143 | 144 | ```swift 145 | public var pagerBehaviour: PagerTabStripBehaviour 146 | ``` 147 | 148 | ```swift 149 | public enum PagerTabStripBehaviour { 150 | case common(skipIntermediteViewControllers: Bool) 151 | case progressive(skipIntermediteViewControllers: Bool, elasticIndicatorLimit: Bool) 152 | } 153 | ``` 154 | 155 | Default Values: 156 | ```swift 157 | // Twitter Type 158 | PagerTabStripBehaviour.common(skipIntermediateViewControllers: true) 159 | // Segmented Type 160 | PagerTabStripBehaviour.common(skipIntermediateViewControllers: true) 161 | // Bar Type 162 | PagerTabStripBehaviour.progressive(skipIntermediateViewControllers: true, elasticIndicatorLimit: true) 163 | // ButtonBar Type 164 | PagerTabStripBehaviour.progressive(skipIntermediateViewControllers: true, elasticIndicatorLimit: true) 165 | ``` 166 | 167 | As you might have noticed, `common` and `progressive` enumeration cases have `skipIntermediateViewControllers` and `elasticIndicatorLimit` associated values. 168 | 169 | `skipIntermediateViewControllers` allows us to skip intermediate view controllers when a tab indicator is tapped. 170 | 171 | `elasticIndicatorLimit` allows us to tension the indicator when we reach a limit, I mean when we try to move forward from last indicator or move back from first indicator. 172 | 173 | ##### PagerTabStripDelegate & PagerTabStripIsProgressiveDelegate 174 | 175 | Normally we don't need to implement these protocols because each pager type already conforms to it in order to properly update its indicator. However, there may be some scenarios when overriding a method may come in handy. 176 | 177 | ```swift 178 | public protocol PagerTabStripDelegate: class { 179 | 180 | func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int) 181 | } 182 | 183 | public protocol PagerTabStripIsProgressiveDelegate : PagerTabStripDelegate { 184 | 185 | func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int, withProgressPercentage progressPercentage: CGFloat, indexWasChanged: Bool) 186 | } 187 | ``` 188 | 189 | > Again, the method invoked by the library depends on the `pagerBehaviour` value. 190 | 191 | 192 | 193 | 194 | ### ButtonBar Customization 195 | 196 | ```swift 197 | 198 | settings.style.buttonBarBackgroundColor: UIColor? 199 | // buttonBar minimumInteritemSpacing value, note that button bar extends from UICollectionView 200 | settings.style.buttonBarMinimumInteritemSpacing: CGFloat? 201 | // buttonBar minimumLineSpacing value 202 | settings.style.buttonBarMinimumLineSpacing: CGFloat? 203 | // buttonBar flow layout left content inset value 204 | settings.style.buttonBarLeftContentInset: CGFloat? 205 | // buttonBar flow layout right content inset value 206 | settings.style.buttonBarRightContentInset: CGFloat? 207 | 208 | // selected bar view is created programmatically so it's important to set up the following 2 properties properly 209 | settings.style.selectedBarBackgroundColor = UIColor.black 210 | settings.style.selectedBarHeight: CGFloat = 5 211 | 212 | // each buttonBar item is a UICollectionView cell of type ButtonBarViewCell 213 | settings.style.buttonBarItemBackgroundColor: UIColor? 214 | settings.style.buttonBarItemFont = UIFont.systemFont(ofSize: 18) 215 | // helps to determine the cell width, it represent the space before and after the title label 216 | settings.style.buttonBarItemLeftRightMargin: CGFloat = 8 217 | settings.style.buttonBarItemTitleColor: UIColor? 218 | // in case the barView items do not fill the screen width this property stretch the cells to fill the screen 219 | settings.style.buttonBarItemsShouldFillAvailableWidth = true 220 | // only used if button bar is created programmatically and not using storyboards or nib files as recommended. 221 | public var buttonBarHeight: CGFloat? 222 | ``` 223 | 224 | **Important:** Settings should be called before `viewDidLoad` is called. 225 | ```swift 226 | override func viewDidLoad() { 227 | self.settings.style.selectedBarHeight = 2 228 | self.settings.style.selectedBarBackgroundColor = UIColor.white 229 | 230 | super.viewDidLoad() 231 | } 232 | ``` 233 | 234 | ##### Update cells when selected indicator changes 235 | 236 | We may need to update the indicator cell when the displayed view controller changes. The following function properties help to accomplish that. Depending on our pager `pagerBehaviour` value we will have to set up `changeCurrentIndex` or `changeCurrentIndexProgressive`. 237 | 238 | ```swift 239 | public var changeCurrentIndex: ((oldCell: ButtonBarViewCell?, newCell: ButtonBarViewCell?, animated: Bool) -> Void)? 240 | public var changeCurrentIndexProgressive: ((oldCell: ButtonBarViewCell?, newCell: ButtonBarViewCell?, progressPercentage: CGFloat, changeCurrentIndex: Bool, animated: Bool) -> Void)? 241 | ``` 242 | 243 | Let's see an example: 244 | 245 | ```swift 246 | changeCurrentIndexProgressive = { (oldCell: ButtonBarViewCell?, newCell: ButtonBarViewCell?, progressPercentage: CGFloat, changeCurrentIndex: Bool, animated: Bool) -> Void in 247 | guard changeCurrentIndex == true else { return } 248 | 249 | oldCell?.label.textColor = UIColor(white: 1, alpha: 0.6) 250 | newCell?.label.textColor = UIColor.white 251 | 252 | if animated { 253 | UIView.animate(withDuration: 0.1, animations: { () -> Void in 254 | newCell?.transform = CGAffineTransform(scaleX: 1.0, y: 1.0) 255 | oldCell?.transform = CGAffineTransform(scaleX: 0.8, y: 0.8) 256 | }) 257 | } 258 | else { 259 | newCell?.transform = CGAffineTransform(scaleX: 1.0, y: 1.0) 260 | oldCell?.transform = CGAffineTransform(scaleX: 0.8, y: 0.8) 261 | } 262 | } 263 | ``` 264 | 265 | ### Bar Type Customization 266 | 267 | ```swift 268 | settings.style.barBackgroundColor: UIColor? 269 | settings.style.selectedBarBackgroundColor: UIColor? 270 | // barHeight is only set up when the bar is created programmatically and not using storyboards or xib files as recommended. 271 | settings.style.barHeight: CGFloat = 5 272 | ``` 273 | 274 | ### Twitter Type Customization 275 | 276 | ```swift 277 | settings.style.dotColor = UIColor(white: 1, alpha: 0.4) 278 | settings.style.selectedDotColor = UIColor.white 279 | settings.style.portraitTitleFont = UIFont.systemFont(ofSize: 18) 280 | settings.style.landscapeTitleFont = UIFont.systemFont(ofSize: 15) 281 | settings.style.titleColor = UIColor.white 282 | ``` 283 | 284 | ### Segmented Type Customization 285 | 286 | ```swift 287 | settings.style.segmentedControlColor: UIColor? 288 | ``` 289 | 290 | 291 | 292 | ## Requirements 293 | 294 | * iOS 9.3+ 295 | * Xcode 10.2+ 296 | 297 | ## Examples 298 | 299 | Follow these 3 steps to run Example project: Clone XLPagerTabStrip repository, open XLPagerTabStrip workspace and run the *Example* project. 300 | 301 | ## Installation 302 | 303 | ### CocoaPods 304 | 305 | [CocoaPods](https://cocoapods.org/) is a dependency manager for Cocoa projects. 306 | 307 | To install XLPagerTabStrip, simply add the following line to your Podfile: 308 | 309 | ```ruby 310 | pod 'XLPagerTabStrip', '~> 9.0' 311 | ``` 312 | 313 | ### Carthage 314 | 315 | [Carthage](https://github.com/Carthage/Carthage) is a simple, decentralized dependency manager for Cocoa. 316 | 317 | To install XLPagerTabStrip, simply add the following line to your Cartfile: 318 | 319 | ```ogdl 320 | github "xmartlabs/XLPagerTabStrip" ~> 9.0 321 | ``` 322 | 323 | ### SPM 324 | 325 | - File > Swift Packages > Add Package Dependency 326 | - Add `https://github.com/xmartlabs/XLPagerTabStrip.git` 327 | - Select "Up to Next Major" with "9.0.0" 328 | 329 | ## FAQ 330 | 331 | #### How to change the visible child view controller programmatically 332 | 333 | `PagerTabStripViewController` provides the following methods to programmatically change the visible child view controller: 334 | 335 | ```swift 336 | func moveToViewController(at index: Int) 337 | func moveToViewController(at index: Int, animated: Bool) 338 | func moveTo(viewController: UIViewController) 339 | func moveTo(viewController: UIViewController, animated: Bool) 340 | ``` 341 | 342 | 343 | #### How to migrate from Swift 2 to Swift 3 344 | 345 | Check out [our migration guide](https://github.com/xmartlabs/XLPagerTabStrip/blob/master/Migration.md) 346 | 347 | ## Author 348 | 349 | * [Martin Barreto](https://github.com/mtnBarreto) ([@mtnBarreto](https://twitter.com/mtnBarreto)) 350 | 351 | ## Change Log 352 | 353 | This can be found in the [CHANGELOG.md](CHANGELOG.md) file. 354 | -------------------------------------------------------------------------------- /Sources/XLPagerTabStrip/BarPagerTabStripViewController.swift: -------------------------------------------------------------------------------- 1 | // BarPagerTabStripViewController.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Foundation 26 | import UIKit 27 | 28 | public struct BarPagerTabStripSettings { 29 | 30 | public struct Style { 31 | public var barBackgroundColor: UIColor? 32 | public var selectedBarBackgroundColor: UIColor? 33 | public var barHeight: CGFloat = 5 // barHeight is ony set up when the bar is created programatically and not using storyboards or xib files. 34 | } 35 | 36 | public var style = Style() 37 | } 38 | 39 | open class BarPagerTabStripViewController: PagerTabStripViewController, PagerTabStripDataSource, PagerTabStripIsProgressiveDelegate { 40 | 41 | public var settings = BarPagerTabStripSettings() 42 | 43 | @IBOutlet weak public var barView: BarView! 44 | 45 | required public init?(coder aDecoder: NSCoder) { 46 | super.init(coder: aDecoder) 47 | delegate = self 48 | datasource = self 49 | } 50 | 51 | public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { 52 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 53 | delegate = self 54 | datasource = self 55 | } 56 | 57 | open override func viewDidLoad() { 58 | super.viewDidLoad() 59 | barView = barView ?? { 60 | let barView = BarView(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: settings.style.barHeight)) 61 | barView.autoresizingMask = .flexibleWidth 62 | barView.backgroundColor = .black 63 | barView.selectedBar.backgroundColor = .white 64 | return barView 65 | }() 66 | 67 | barView.backgroundColor = settings.style.barBackgroundColor ?? barView.backgroundColor 68 | barView.selectedBar.backgroundColor = settings.style.selectedBarBackgroundColor ?? barView.selectedBar.backgroundColor 69 | } 70 | 71 | open override func viewWillAppear(_ animated: Bool) { 72 | super.viewWillAppear(animated) 73 | if barView.superview == nil { 74 | view.addSubview(barView) 75 | } 76 | barView.optionsCount = viewControllers.count 77 | barView.moveTo(index: currentIndex, animated: false) 78 | } 79 | 80 | open override func reloadPagerTabStripView() { 81 | super.reloadPagerTabStripView() 82 | barView.optionsCount = viewControllers.count 83 | if isViewLoaded { 84 | barView.moveTo(index: currentIndex, animated: false) 85 | } 86 | } 87 | 88 | // MARK: - PagerTabStripDelegate 89 | 90 | open func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int, withProgressPercentage progressPercentage: CGFloat, indexWasChanged: Bool) { 91 | 92 | barView.move(fromIndex: fromIndex, toIndex: toIndex, progressPercentage: progressPercentage) 93 | } 94 | 95 | open func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int) { 96 | barView.moveTo(index: toIndex, animated: true) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Sources/XLPagerTabStrip/BarView.swift: -------------------------------------------------------------------------------- 1 | // BarView.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Foundation 26 | import UIKit 27 | 28 | open class BarView: UIView { 29 | 30 | open lazy var selectedBar: UIView = { [unowned self] in 31 | let selectedBar = UIView(frame: CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height)) 32 | return selectedBar 33 | }() 34 | 35 | var optionsCount = 1 { 36 | willSet(newOptionsCount) { 37 | if newOptionsCount <= selectedIndex { 38 | selectedIndex = optionsCount - 1 39 | } 40 | } 41 | } 42 | var selectedIndex = 0 43 | 44 | required public init?(coder aDecoder: NSCoder) { 45 | super.init(coder: aDecoder) 46 | addSubview(selectedBar) 47 | } 48 | 49 | override init(frame: CGRect) { 50 | super.init(frame: frame) 51 | addSubview(selectedBar) 52 | } 53 | 54 | // MARK: - Helpers 55 | 56 | private func updateSelectedBarPosition(with animation: Bool) { 57 | var frame = selectedBar.frame 58 | frame.size.width = self.frame.size.width / CGFloat(optionsCount) 59 | frame.origin.x = frame.size.width * CGFloat(selectedIndex) 60 | if animation { 61 | UIView.animate(withDuration: 0.3, animations: { [weak self] in 62 | self?.selectedBar.frame = frame 63 | }) 64 | } else { 65 | selectedBar.frame = frame 66 | } 67 | } 68 | 69 | open func moveTo(index: Int, animated: Bool) { 70 | selectedIndex = index 71 | updateSelectedBarPosition(with: animated) 72 | } 73 | 74 | open func move(fromIndex: Int, toIndex: Int, progressPercentage: CGFloat) { 75 | selectedIndex = (progressPercentage > 0.5) ? toIndex : fromIndex 76 | 77 | var newFrame = selectedBar.frame 78 | newFrame.size.width = frame.size.width / CGFloat(optionsCount) 79 | var fromFrame = newFrame 80 | fromFrame.origin.x = newFrame.size.width * CGFloat(fromIndex) 81 | var toFrame = newFrame 82 | toFrame.origin.x = toFrame.size.width * CGFloat(toIndex) 83 | var targetFrame = fromFrame 84 | targetFrame.origin.x += (toFrame.origin.x - targetFrame.origin.x) * CGFloat(progressPercentage) 85 | selectedBar.frame = targetFrame 86 | } 87 | 88 | open override func layoutSubviews() { 89 | super.layoutSubviews() 90 | updateSelectedBarPosition(with: false) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Sources/XLPagerTabStrip/ButtonBarView.swift: -------------------------------------------------------------------------------- 1 | // ButtonBarView.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import UIKit 26 | 27 | public enum PagerScroll { 28 | case no 29 | case yes 30 | case scrollOnlyIfOutOfScreen 31 | } 32 | 33 | public enum SelectedBarAlignment { 34 | case left 35 | case center 36 | case right 37 | case progressive 38 | } 39 | 40 | public enum SelectedBarVerticalAlignment { 41 | case top 42 | case middle 43 | case bottom 44 | } 45 | 46 | open class ButtonBarView: UICollectionView { 47 | 48 | open lazy var selectedBar: UIView = { [unowned self] in 49 | let bar = UIView(frame: CGRect(x: 0, y: self.frame.size.height - CGFloat(self.selectedBarHeight), width: 0, height: CGFloat(self.selectedBarHeight))) 50 | bar.layer.zPosition = 9999 51 | return bar 52 | }() 53 | 54 | internal var selectedBarHeight: CGFloat = 4 { 55 | didSet { 56 | updateSelectedBarYPosition() 57 | } 58 | } 59 | var selectedBarVerticalAlignment: SelectedBarVerticalAlignment = .bottom 60 | var selectedBarAlignment: SelectedBarAlignment = .center 61 | var selectedIndex = 0 62 | 63 | required public init?(coder aDecoder: NSCoder) { 64 | super.init(coder: aDecoder) 65 | addSubview(selectedBar) 66 | } 67 | 68 | public override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) { 69 | super.init(frame: frame, collectionViewLayout: layout) 70 | addSubview(selectedBar) 71 | } 72 | 73 | open func moveTo(index: Int, animated: Bool, swipeDirection: SwipeDirection, pagerScroll: PagerScroll) { 74 | selectedIndex = index 75 | updateSelectedBarPosition(animated, swipeDirection: swipeDirection, pagerScroll: pagerScroll) 76 | } 77 | 78 | open func move(fromIndex: Int, toIndex: Int, progressPercentage: CGFloat, pagerScroll: PagerScroll) { 79 | selectedIndex = progressPercentage > 0.5 ? toIndex : fromIndex 80 | 81 | let fromFrame = layoutAttributesForItem(at: IndexPath(item: fromIndex, section: 0))!.frame 82 | let numberOfItems = dataSource!.collectionView(self, numberOfItemsInSection: 0) 83 | 84 | var toFrame: CGRect 85 | 86 | if toIndex < 0 || toIndex > numberOfItems - 1 { 87 | if toIndex < 0 { 88 | let cellAtts = layoutAttributesForItem(at: IndexPath(item: 0, section: 0)) 89 | toFrame = cellAtts!.frame.offsetBy(dx: -cellAtts!.frame.size.width, dy: 0) 90 | } else { 91 | let cellAtts = layoutAttributesForItem(at: IndexPath(item: (numberOfItems - 1), section: 0)) 92 | toFrame = cellAtts!.frame.offsetBy(dx: cellAtts!.frame.size.width, dy: 0) 93 | } 94 | } else { 95 | toFrame = layoutAttributesForItem(at: IndexPath(item: toIndex, section: 0))!.frame 96 | } 97 | 98 | var targetFrame = fromFrame 99 | targetFrame.size.height = selectedBar.frame.size.height 100 | targetFrame.size.width += (toFrame.size.width - fromFrame.size.width) * progressPercentage 101 | targetFrame.origin.x += (toFrame.origin.x - fromFrame.origin.x) * progressPercentage 102 | 103 | selectedBar.frame = CGRect(x: targetFrame.origin.x, y: selectedBar.frame.origin.y, width: targetFrame.size.width, height: selectedBar.frame.size.height) 104 | 105 | var targetContentOffset: CGFloat = 0.0 106 | if contentSize.width > frame.size.width { 107 | let toContentOffset = contentOffsetForCell(withFrame: toFrame, andIndex: toIndex) 108 | let fromContentOffset = contentOffsetForCell(withFrame: fromFrame, andIndex: fromIndex) 109 | 110 | targetContentOffset = fromContentOffset + ((toContentOffset - fromContentOffset) * progressPercentage) 111 | } 112 | 113 | setContentOffset(CGPoint(x: targetContentOffset, y: 0), animated: false) 114 | } 115 | 116 | open func updateSelectedBarPosition(_ animated: Bool, swipeDirection: SwipeDirection, pagerScroll: PagerScroll) { 117 | var selectedBarFrame = selectedBar.frame 118 | 119 | let selectedCellIndexPath = IndexPath(item: selectedIndex, section: 0) 120 | let attributes = layoutAttributesForItem(at: selectedCellIndexPath) 121 | let selectedCellFrame = attributes!.frame 122 | 123 | updateContentOffset(animated: animated, pagerScroll: pagerScroll, toFrame: selectedCellFrame, toIndex: (selectedCellIndexPath as NSIndexPath).row) 124 | 125 | selectedBarFrame.size.width = selectedCellFrame.size.width 126 | selectedBarFrame.origin.x = selectedCellFrame.origin.x 127 | 128 | if animated { 129 | UIView.animate(withDuration: 0.3, animations: { [weak self] in 130 | self?.selectedBar.frame = selectedBarFrame 131 | }) 132 | } else { 133 | selectedBar.frame = selectedBarFrame 134 | } 135 | } 136 | 137 | // MARK: - Helpers 138 | 139 | private func updateContentOffset(animated: Bool, pagerScroll: PagerScroll, toFrame: CGRect, toIndex: Int) { 140 | guard pagerScroll != .no || (pagerScroll != .scrollOnlyIfOutOfScreen && (toFrame.origin.x < contentOffset.x || toFrame.origin.x >= (contentOffset.x + frame.size.width - contentInset.left))) else { return } 141 | let targetContentOffset = contentSize.width > frame.size.width ? contentOffsetForCell(withFrame: toFrame, andIndex: toIndex) : 0 142 | setContentOffset(CGPoint(x: targetContentOffset, y: 0), animated: animated) 143 | } 144 | 145 | private func contentOffsetForCell(withFrame cellFrame: CGRect, andIndex index: Int) -> CGFloat { 146 | let sectionInset = (collectionViewLayout as! UICollectionViewFlowLayout).sectionInset // swiftlint:disable:this force_cast 147 | var alignmentOffset: CGFloat = 0.0 148 | 149 | switch selectedBarAlignment { 150 | case .left: 151 | alignmentOffset = sectionInset.left 152 | case .right: 153 | alignmentOffset = frame.size.width - sectionInset.right - cellFrame.size.width 154 | case .center: 155 | alignmentOffset = (frame.size.width - cellFrame.size.width) * 0.5 156 | case .progressive: 157 | let cellHalfWidth = cellFrame.size.width * 0.5 158 | let leftAlignmentOffset = sectionInset.left + cellHalfWidth 159 | let rightAlignmentOffset = frame.size.width - sectionInset.right - cellHalfWidth 160 | let numberOfItems = dataSource!.collectionView(self, numberOfItemsInSection: 0) 161 | let progress = index / (numberOfItems - 1) 162 | alignmentOffset = leftAlignmentOffset + (rightAlignmentOffset - leftAlignmentOffset) * CGFloat(progress) - cellHalfWidth 163 | } 164 | 165 | var contentOffset = cellFrame.origin.x - alignmentOffset 166 | contentOffset = max(0, contentOffset) 167 | contentOffset = min(contentSize.width - frame.size.width, contentOffset) 168 | return contentOffset 169 | } 170 | 171 | private func updateSelectedBarYPosition() { 172 | var selectedBarFrame = selectedBar.frame 173 | 174 | switch selectedBarVerticalAlignment { 175 | case .top: 176 | selectedBarFrame.origin.y = 0 177 | case .middle: 178 | selectedBarFrame.origin.y = (frame.size.height - selectedBarHeight) / 2 179 | case .bottom: 180 | selectedBarFrame.origin.y = frame.size.height - selectedBarHeight 181 | } 182 | 183 | selectedBarFrame.size.height = selectedBarHeight 184 | selectedBar.frame = selectedBarFrame 185 | } 186 | 187 | override open func layoutSubviews() { 188 | super.layoutSubviews() 189 | updateSelectedBarYPosition() 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /Sources/XLPagerTabStrip/ButtonBarViewCell.swift: -------------------------------------------------------------------------------- 1 | // ButtonBarViewCell.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import UIKit 26 | import Foundation 27 | 28 | open class ButtonBarViewCell: UICollectionViewCell { 29 | 30 | @IBOutlet open var imageView: UIImageView! 31 | @IBOutlet open var label: UILabel! 32 | 33 | public required init?(coder aDecoder: NSCoder) { 34 | super.init(coder: aDecoder) 35 | 36 | isAccessibilityElement = true 37 | accessibilityTraits.insert([.button, .header]) 38 | } 39 | 40 | open override var isSelected: Bool { 41 | get { 42 | return super.isSelected 43 | } 44 | set { 45 | super.isSelected = newValue 46 | if (newValue) { 47 | accessibilityTraits.insert(.selected) 48 | } else { 49 | accessibilityTraits.remove(.selected) 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/XLPagerTabStrip/IndicatorInfo.swift: -------------------------------------------------------------------------------- 1 | // IndicatorInfo.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Foundation 26 | import UIKit 27 | 28 | public struct IndicatorInfo { 29 | 30 | public var title: String? 31 | public var image: UIImage? 32 | public var highlightedImage: UIImage? 33 | public var accessibilityLabel: String? 34 | public var userInfo: Any? 35 | 36 | public init(title: String?) { 37 | self.title = title 38 | self.accessibilityLabel = title 39 | } 40 | 41 | public init(image: UIImage?, highlightedImage: UIImage? = nil, userInfo: Any? = nil) { 42 | self.image = image 43 | self.highlightedImage = highlightedImage 44 | self.userInfo = userInfo 45 | } 46 | 47 | public init(title: String?, image: UIImage?, highlightedImage: UIImage? = nil, userInfo: Any? = nil) { 48 | self.title = title 49 | self.accessibilityLabel = title 50 | self.image = image 51 | self.highlightedImage = highlightedImage 52 | self.userInfo = userInfo 53 | } 54 | 55 | public init(title: String?, accessibilityLabel:String?, image: UIImage?, highlightedImage: UIImage? = nil, userInfo: Any? = nil) { 56 | self.title = title 57 | self.accessibilityLabel = accessibilityLabel 58 | self.image = image 59 | self.highlightedImage = highlightedImage 60 | self.userInfo = userInfo 61 | } 62 | 63 | } 64 | 65 | extension IndicatorInfo : ExpressibleByStringLiteral { 66 | 67 | public init(stringLiteral value: String) { 68 | title = value 69 | accessibilityLabel = value 70 | } 71 | 72 | public init(extendedGraphemeClusterLiteral value: String) { 73 | title = value 74 | accessibilityLabel = value 75 | } 76 | 77 | public init(unicodeScalarLiteral value: String) { 78 | title = value 79 | accessibilityLabel = value 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Sources/XLPagerTabStrip/PagerTabStripBehaviour.swift: -------------------------------------------------------------------------------- 1 | // PagerTabStripOptions.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Foundation 26 | 27 | public enum PagerTabStripBehaviour { 28 | 29 | case common(skipIntermediateViewControllers: Bool) 30 | case progressive(skipIntermediateViewControllers: Bool, elasticIndicatorLimit: Bool) 31 | 32 | public var skipIntermediateViewControllers: Bool { 33 | switch self { 34 | case .common(let skipIntermediateViewControllers): 35 | return skipIntermediateViewControllers 36 | case .progressive(let skipIntermediateViewControllers, _): 37 | return skipIntermediateViewControllers 38 | } 39 | } 40 | 41 | public var isProgressiveIndicator: Bool { 42 | switch self { 43 | case .common: 44 | return false 45 | case .progressive: 46 | return true 47 | } 48 | } 49 | 50 | public var isElasticIndicatorLimit: Bool { 51 | switch self { 52 | case .common: 53 | return false 54 | case .progressive(_, let elasticIndicatorLimit): 55 | return elasticIndicatorLimit 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Sources/XLPagerTabStrip/PagerTabStripError.swift: -------------------------------------------------------------------------------- 1 | // PagerTabStripError.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Foundation 26 | 27 | public enum PagerTabStripError: Error { 28 | 29 | case viewControllerOutOfBounds 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Sources/XLPagerTabStrip/PagerTabStripViewController.swift: -------------------------------------------------------------------------------- 1 | // PagerTabStripViewController.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Foundation 26 | import UIKit 27 | 28 | // MARK: Protocols 29 | 30 | public protocol IndicatorInfoProvider { 31 | 32 | func indicatorInfo(for pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo 33 | 34 | } 35 | 36 | public protocol PagerTabStripDelegate: AnyObject { 37 | 38 | func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int) 39 | } 40 | 41 | public protocol PagerTabStripIsProgressiveDelegate: PagerTabStripDelegate { 42 | 43 | func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int, withProgressPercentage progressPercentage: CGFloat, indexWasChanged: Bool) 44 | } 45 | 46 | public protocol PagerTabStripDataSource: AnyObject { 47 | 48 | func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] 49 | } 50 | 51 | // MARK: PagerTabStripViewController 52 | 53 | open class PagerTabStripViewController: UIViewController, UIScrollViewDelegate { 54 | 55 | @IBOutlet weak public var containerView: UIScrollView! 56 | 57 | open weak var delegate: PagerTabStripDelegate? 58 | open weak var datasource: PagerTabStripDataSource? 59 | 60 | open var pagerBehaviour = PagerTabStripBehaviour.progressive(skipIntermediateViewControllers: true, elasticIndicatorLimit: true) 61 | 62 | open private(set) var viewControllers = [UIViewController]() 63 | open private(set) var currentIndex = 0 64 | open private(set) var preCurrentIndex = 0 // used *only* to store the index to which move when the pager becomes visible 65 | 66 | open var pageWidth: CGFloat { 67 | return containerView.bounds.width 68 | } 69 | 70 | open var scrollPercentage: CGFloat { 71 | if swipeDirection != .right { 72 | let module = fmod(containerView.contentOffset.x, pageWidth) 73 | return module == 0.0 ? 1.0 : module / pageWidth 74 | } 75 | return 1 - fmod(containerView.contentOffset.x >= 0 ? containerView.contentOffset.x : pageWidth + containerView.contentOffset.x, pageWidth) / pageWidth 76 | } 77 | 78 | open var swipeDirection: SwipeDirection { 79 | if containerView.contentOffset.x > lastContentOffset { 80 | return .left 81 | } else if containerView.contentOffset.x < lastContentOffset { 82 | return .right 83 | } 84 | return .none 85 | } 86 | 87 | override open func viewDidLoad() { 88 | super.viewDidLoad() 89 | let conteinerViewAux = containerView ?? { 90 | let containerView = UIScrollView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: view.bounds.height)) 91 | containerView.autoresizingMask = [.flexibleWidth, .flexibleHeight] 92 | return containerView 93 | }() 94 | containerView = conteinerViewAux 95 | if containerView.superview == nil { 96 | view.addSubview(containerView) 97 | } 98 | containerView.bounces = true 99 | containerView.alwaysBounceHorizontal = true 100 | containerView.alwaysBounceVertical = false 101 | containerView.scrollsToTop = false 102 | containerView.delegate = self 103 | containerView.showsVerticalScrollIndicator = false 104 | containerView.showsHorizontalScrollIndicator = false 105 | containerView.isPagingEnabled = true 106 | reloadViewControllers() 107 | 108 | let childController = viewControllers[currentIndex] 109 | addChild(childController) 110 | childController.view.autoresizingMask = [.flexibleHeight, .flexibleWidth] 111 | containerView.addSubview(childController.view) 112 | childController.didMove(toParent: self) 113 | } 114 | 115 | open override func viewWillAppear(_ animated: Bool) { 116 | super.viewWillAppear(animated) 117 | isViewAppearing = true 118 | children.forEach { $0.beginAppearanceTransition(true, animated: animated) } 119 | } 120 | 121 | override open func viewDidAppear(_ animated: Bool) { 122 | super.viewDidAppear(animated) 123 | lastSize = containerView.bounds.size 124 | updateIfNeeded() 125 | let needToUpdateCurrentChild = preCurrentIndex != currentIndex 126 | if needToUpdateCurrentChild { 127 | moveToViewController(at: preCurrentIndex) 128 | } 129 | isViewAppearing = false 130 | children.forEach { $0.endAppearanceTransition() } 131 | } 132 | 133 | open override func viewWillDisappear(_ animated: Bool) { 134 | super.viewWillDisappear(animated) 135 | children.forEach { $0.beginAppearanceTransition(false, animated: animated) } 136 | } 137 | 138 | open override func viewDidDisappear(_ animated: Bool) { 139 | super.viewDidDisappear(animated) 140 | children.forEach { $0.endAppearanceTransition() } 141 | } 142 | 143 | override open func viewDidLayoutSubviews() { 144 | super.viewDidLayoutSubviews() 145 | updateIfNeeded() 146 | } 147 | 148 | open override var shouldAutomaticallyForwardAppearanceMethods: Bool { 149 | return false 150 | } 151 | 152 | open func moveToViewController(at index: Int, animated: Bool = true) { 153 | guard isViewLoaded && view.window != nil && currentIndex != index else { 154 | preCurrentIndex = index 155 | return 156 | } 157 | 158 | if animated && pagerBehaviour.skipIntermediateViewControllers && abs(currentIndex - index) > 1 { 159 | var tmpViewControllers = viewControllers 160 | let currentChildVC = viewControllers[currentIndex] 161 | let fromIndex = currentIndex < index ? index - 1 : index + 1 162 | let fromChildVC = viewControllers[fromIndex] 163 | tmpViewControllers[currentIndex] = fromChildVC 164 | tmpViewControllers[fromIndex] = currentChildVC 165 | pagerTabStripChildViewControllersForScrolling = tmpViewControllers 166 | containerView.setContentOffset(CGPoint(x: pageOffsetForChild(at: fromIndex), y: 0), animated: false) 167 | (navigationController?.view ?? view).isUserInteractionEnabled = !animated 168 | containerView.setContentOffset(CGPoint(x: pageOffsetForChild(at: index), y: 0), animated: true) 169 | } else { 170 | (navigationController?.view ?? view).isUserInteractionEnabled = !animated 171 | containerView.setContentOffset(CGPoint(x: pageOffsetForChild(at: index), y: 0), animated: animated) 172 | } 173 | } 174 | 175 | open func moveTo(viewController: UIViewController, animated: Bool = true) { 176 | moveToViewController(at: viewControllers.firstIndex(of: viewController)!, animated: animated) 177 | } 178 | 179 | // MARK: - PagerTabStripDataSource 180 | 181 | open func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] { 182 | assertionFailure("Sub-class must implement the PagerTabStripDataSource viewControllers(for:) method") 183 | return [] 184 | } 185 | 186 | // MARK: - Helpers 187 | 188 | open func updateIfNeeded() { 189 | if isViewLoaded && !lastSize.equalTo(containerView.bounds.size) { 190 | updateContent() 191 | } 192 | } 193 | 194 | open func canMoveTo(index: Int) -> Bool { 195 | return currentIndex != index && viewControllers.count > index 196 | } 197 | 198 | open func pageOffsetForChild(at index: Int) -> CGFloat { 199 | return CGFloat(index) * containerView.bounds.width 200 | } 201 | 202 | open func offsetForChild(at index: Int) -> CGFloat { 203 | return (CGFloat(index) * containerView.bounds.width) + ((containerView.bounds.width - view.bounds.width) * 0.5) 204 | } 205 | 206 | open func offsetForChild(viewController: UIViewController) throws -> CGFloat { 207 | guard let index = viewControllers.firstIndex(of: viewController) else { 208 | throw PagerTabStripError.viewControllerOutOfBounds 209 | } 210 | return offsetForChild(at: index) 211 | } 212 | 213 | open func pageFor(contentOffset: CGFloat) -> Int { 214 | let result = virtualPageFor(contentOffset: contentOffset) 215 | return pageFor(virtualPage: result) 216 | } 217 | 218 | open func virtualPageFor(contentOffset: CGFloat) -> Int { 219 | return Int((contentOffset + 1.5 * pageWidth) / pageWidth) - 1 220 | } 221 | 222 | open func pageFor(virtualPage: Int) -> Int { 223 | if virtualPage < 0 { 224 | return 0 225 | } 226 | if virtualPage > viewControllers.count - 1 { 227 | return viewControllers.count - 1 228 | } 229 | return virtualPage 230 | } 231 | 232 | open func updateContent() { 233 | if lastSize.width != containerView.bounds.size.width { 234 | lastSize = containerView.bounds.size 235 | containerView.contentOffset = CGPoint(x: pageOffsetForChild(at: currentIndex), y: 0) 236 | } 237 | lastSize = containerView.bounds.size 238 | 239 | let pagerViewControllers = pagerTabStripChildViewControllersForScrolling ?? viewControllers 240 | containerView.contentSize = CGSize(width: containerView.bounds.width * CGFloat(pagerViewControllers.count), height: containerView.contentSize.height) 241 | 242 | for (index, childController) in pagerViewControllers.enumerated() { 243 | let pageOffsetForChild = self.pageOffsetForChild(at: index) 244 | if abs(containerView.contentOffset.x - pageOffsetForChild) < containerView.bounds.width { 245 | if childController.parent != nil { 246 | childController.view.frame = CGRect(x: offsetForChild(at: index), y: 0, width: view.bounds.width, height: containerView.bounds.height) 247 | childController.view.autoresizingMask = [.flexibleHeight, .flexibleWidth] 248 | } else { 249 | childController.beginAppearanceTransition(true, animated: false) 250 | addChild(childController) 251 | childController.view.frame = CGRect(x: offsetForChild(at: index), y: 0, width: view.bounds.width, height: containerView.bounds.height) 252 | childController.view.autoresizingMask = [.flexibleHeight, .flexibleWidth] 253 | containerView.addSubview(childController.view) 254 | childController.didMove(toParent: self) 255 | childController.endAppearanceTransition() 256 | } 257 | } else { 258 | if childController.parent != nil { 259 | childController.beginAppearanceTransition(false, animated: false) 260 | childController.willMove(toParent: nil) 261 | childController.view.removeFromSuperview() 262 | childController.removeFromParent() 263 | childController.endAppearanceTransition() 264 | } 265 | } 266 | } 267 | 268 | let oldCurrentIndex = currentIndex 269 | let virtualPage = virtualPageFor(contentOffset: containerView.contentOffset.x) 270 | let newCurrentIndex = pageFor(virtualPage: virtualPage) 271 | currentIndex = newCurrentIndex 272 | preCurrentIndex = currentIndex 273 | let changeCurrentIndex = newCurrentIndex != oldCurrentIndex 274 | 275 | if let progressiveDelegate = self as? PagerTabStripIsProgressiveDelegate, pagerBehaviour.isProgressiveIndicator { 276 | 277 | let (fromIndex, toIndex, scrollPercentage) = progressiveIndicatorData(virtualPage) 278 | progressiveDelegate.updateIndicator(for: self, fromIndex: fromIndex, toIndex: toIndex, withProgressPercentage: scrollPercentage, indexWasChanged: changeCurrentIndex) 279 | } else { 280 | delegate?.updateIndicator(for: self, fromIndex: min(oldCurrentIndex, pagerViewControllers.count - 1), toIndex: newCurrentIndex) 281 | } 282 | } 283 | 284 | open func reloadPagerTabStripView() { 285 | guard isViewLoaded else { return } 286 | for childController in viewControllers where childController.parent != nil { 287 | childController.beginAppearanceTransition(false, animated: false) 288 | childController.willMove(toParent: nil) 289 | childController.view.removeFromSuperview() 290 | childController.removeFromParent() 291 | childController.endAppearanceTransition() 292 | } 293 | reloadViewControllers() 294 | containerView.contentSize = CGSize(width: containerView.bounds.width * CGFloat(viewControllers.count), height: containerView.contentSize.height) 295 | if currentIndex >= viewControllers.count { 296 | currentIndex = viewControllers.count - 1 297 | } 298 | preCurrentIndex = currentIndex 299 | containerView.contentOffset = CGPoint(x: pageOffsetForChild(at: currentIndex), y: 0) 300 | updateContent() 301 | } 302 | 303 | // MARK: - UIScrollViewDelegate 304 | 305 | open func scrollViewDidScroll(_ scrollView: UIScrollView) { 306 | if containerView == scrollView { 307 | updateContent() 308 | lastContentOffset = scrollView.contentOffset.x 309 | } 310 | } 311 | 312 | open func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { 313 | if containerView == scrollView { 314 | lastPageNumber = pageFor(contentOffset: scrollView.contentOffset.x) 315 | } 316 | } 317 | 318 | open func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { 319 | if containerView == scrollView { 320 | pagerTabStripChildViewControllersForScrolling = nil 321 | (navigationController?.view ?? view).isUserInteractionEnabled = true 322 | updateContent() 323 | } 324 | } 325 | 326 | // MARK: - Orientation 327 | 328 | open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { 329 | super.viewWillTransition(to: size, with: coordinator) 330 | isViewRotating = true 331 | pageBeforeRotate = currentIndex 332 | coordinator.animate(alongsideTransition: nil) { [weak self] _ in 333 | guard let me = self else { return } 334 | me.isViewRotating = false 335 | me.currentIndex = me.pageBeforeRotate 336 | me.preCurrentIndex = me.currentIndex 337 | me.updateIfNeeded() 338 | } 339 | } 340 | 341 | // MARK: Private 342 | 343 | private func progressiveIndicatorData(_ virtualPage: Int) -> (Int, Int, CGFloat) { 344 | let count = viewControllers.count 345 | var fromIndex = currentIndex 346 | var toIndex = currentIndex 347 | let direction = swipeDirection 348 | 349 | if direction == .left { 350 | if virtualPage > count - 1 { 351 | fromIndex = count - 1 352 | toIndex = count 353 | } else { 354 | if self.scrollPercentage >= 0.5 { 355 | fromIndex = max(toIndex - 1, 0) 356 | } else { 357 | toIndex = fromIndex + 1 358 | } 359 | } 360 | } else if direction == .right { 361 | if virtualPage < 0 { 362 | fromIndex = 0 363 | toIndex = -1 364 | } else { 365 | if self.scrollPercentage > 0.5 { 366 | fromIndex = min(toIndex + 1, count - 1) 367 | } else { 368 | toIndex = fromIndex - 1 369 | } 370 | } 371 | } 372 | let scrollPercentage = pagerBehaviour.isElasticIndicatorLimit ? self.scrollPercentage : ((toIndex < 0 || toIndex >= count) ? 0.0 : self.scrollPercentage) 373 | return (fromIndex, toIndex, scrollPercentage) 374 | } 375 | 376 | private func reloadViewControllers() { 377 | guard let dataSource = datasource else { 378 | fatalError("dataSource must not be nil") 379 | } 380 | viewControllers = dataSource.viewControllers(for: self) 381 | // viewControllers 382 | guard !viewControllers.isEmpty else { 383 | fatalError("viewControllers(for:) should provide at least one child view controller") 384 | } 385 | viewControllers.forEach { if !($0 is IndicatorInfoProvider) { fatalError("Every view controller provided by PagerTabStripDataSource's viewControllers(for:) method must conform to IndicatorInfoProvider") }} 386 | 387 | } 388 | 389 | private var pagerTabStripChildViewControllersForScrolling: [UIViewController]? 390 | private var lastPageNumber = 0 391 | private var lastContentOffset: CGFloat = 0.0 392 | private var pageBeforeRotate = 0 393 | private var lastSize = CGSize(width: 0, height: 0) 394 | internal var isViewRotating = false 395 | internal var isViewAppearing = false 396 | 397 | } 398 | -------------------------------------------------------------------------------- /Sources/XLPagerTabStrip/Resources/ButtonCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 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 | -------------------------------------------------------------------------------- /Sources/XLPagerTabStrip/SegmentedPagerTabStripViewController.swift: -------------------------------------------------------------------------------- 1 | // SegmentedPagerTabStripViewController.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Foundation 26 | import UIKit 27 | 28 | public struct SegmentedPagerTabStripSettings { 29 | 30 | public struct Style { 31 | public var segmentedControlColor: UIColor? 32 | } 33 | 34 | public var style = Style() 35 | } 36 | 37 | open class SegmentedPagerTabStripViewController: PagerTabStripViewController, PagerTabStripDataSource, PagerTabStripDelegate { 38 | 39 | @IBOutlet weak public var segmentedControl: UISegmentedControl! 40 | 41 | open var settings = SegmentedPagerTabStripSettings() 42 | 43 | public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { 44 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 45 | pagerBehaviour = PagerTabStripBehaviour.common(skipIntermediateViewControllers: true) 46 | delegate = self 47 | datasource = self 48 | } 49 | 50 | required public init?(coder aDecoder: NSCoder) { 51 | super.init(coder: aDecoder) 52 | pagerBehaviour = PagerTabStripBehaviour.common(skipIntermediateViewControllers: true) 53 | delegate = self 54 | datasource = self 55 | } 56 | 57 | private(set) var shouldUpdateSegmentedControl = true 58 | 59 | open override func viewDidLoad() { 60 | super.viewDidLoad() 61 | let auxSegmentedControl = segmentedControl ?? UISegmentedControl() 62 | segmentedControl = auxSegmentedControl 63 | if segmentedControl.superview == nil { 64 | navigationItem.titleView = segmentedControl 65 | } 66 | segmentedControl.tintColor = settings.style.segmentedControlColor ?? segmentedControl.tintColor 67 | segmentedControl.addTarget(self, action: #selector(SegmentedPagerTabStripViewController.segmentedControlChanged(_:)), for: .valueChanged) 68 | reloadSegmentedControl() 69 | } 70 | 71 | open override func reloadPagerTabStripView() { 72 | super.reloadPagerTabStripView() 73 | if isViewLoaded { 74 | reloadSegmentedControl() 75 | } 76 | } 77 | 78 | func reloadSegmentedControl() { 79 | segmentedControl.removeAllSegments() 80 | for (index, item) in viewControllers.enumerated() { 81 | let child = item as! IndicatorInfoProvider // swiftlint:disable:this force_cast 82 | if let image = child.indicatorInfo(for: self).image { 83 | segmentedControl.insertSegment(with: image, at: index, animated: false) 84 | } else { 85 | segmentedControl.insertSegment(withTitle: child.indicatorInfo(for: self).title, at: index, animated: false) 86 | } 87 | } 88 | segmentedControl.selectedSegmentIndex = currentIndex 89 | } 90 | 91 | @objc func segmentedControlChanged(_ sender: UISegmentedControl) { 92 | let index = sender.selectedSegmentIndex 93 | updateIndicator(for: self, fromIndex: currentIndex, toIndex: index) 94 | shouldUpdateSegmentedControl = false 95 | moveToViewController(at: index) 96 | } 97 | 98 | // MARK: - PagerTabStripDelegate 99 | 100 | open func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int) { 101 | if shouldUpdateSegmentedControl { 102 | segmentedControl.selectedSegmentIndex = toIndex 103 | } 104 | } 105 | 106 | // MARK: - UIScrollViewDelegate 107 | 108 | open override func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { 109 | super.scrollViewDidEndScrollingAnimation(scrollView) 110 | shouldUpdateSegmentedControl = true 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Sources/XLPagerTabStrip/SwipeDirection.swift: -------------------------------------------------------------------------------- 1 | // SwipeDirection.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Foundation 26 | 27 | public enum SwipeDirection { 28 | case left 29 | case right 30 | case none 31 | } 32 | -------------------------------------------------------------------------------- /Sources/XLPagerTabStrip/TwitterPagerTabStripViewController.swift: -------------------------------------------------------------------------------- 1 | // TwitterPagerTabStripViewController.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Foundation 26 | import UIKit 27 | #if SWIFT_PACKAGE 28 | import FXPageControl 29 | #endif 30 | 31 | public struct TwitterPagerTabStripSettings { 32 | 33 | public struct Style { 34 | public var dotColor = UIColor(white: 1, alpha: 0.4) 35 | public var selectedDotColor = UIColor.white 36 | public var portraitTitleFont = UIFont.systemFont(ofSize: 18) 37 | public var landscapeTitleFont = UIFont.systemFont(ofSize: 15) 38 | public var titleColor = UIColor.white 39 | } 40 | 41 | public var style = Style() 42 | } 43 | 44 | open class TwitterPagerTabStripViewController: PagerTabStripViewController, PagerTabStripDataSource, PagerTabStripIsProgressiveDelegate { 45 | 46 | open var settings = TwitterPagerTabStripSettings() 47 | 48 | public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { 49 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 50 | pagerBehaviour = .progressive(skipIntermediateViewControllers: true, elasticIndicatorLimit: true) 51 | delegate = self 52 | datasource = self 53 | } 54 | 55 | required public init?(coder aDecoder: NSCoder) { 56 | super.init(coder: aDecoder) 57 | pagerBehaviour = .progressive(skipIntermediateViewControllers: true, elasticIndicatorLimit: true) 58 | delegate = self 59 | datasource = self 60 | } 61 | 62 | open override func viewDidLoad() { 63 | super.viewDidLoad() 64 | 65 | if titleView.superview == nil { 66 | navigationItem.titleView = titleView 67 | } 68 | 69 | // keep watching the frame of titleView 70 | titleView.addObserver(self, forKeyPath: "frame", options: [.new, .old], context: nil) 71 | 72 | guard let navigationController = navigationController else { 73 | fatalError("TwitterPagerTabStripViewController should be embedded in a UINavigationController") 74 | } 75 | titleView.frame = CGRect(x: 0, y: 0, width: navigationController.navigationBar.frame.width, height: navigationController.navigationBar.frame.height) 76 | titleView.addSubview(titleScrollView) 77 | titleView.addSubview(pageControl) 78 | reloadNavigationViewItems() 79 | } 80 | 81 | open override func reloadPagerTabStripView() { 82 | super.reloadPagerTabStripView() 83 | guard isViewLoaded else { return } 84 | 85 | reloadNavigationViewItems() 86 | setNavigationViewItemsPosition(updateAlpha: true) 87 | } 88 | 89 | // MARK: - PagerTabStripDelegate 90 | 91 | open func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int, withProgressPercentage progressPercentage: CGFloat, indexWasChanged: Bool) { 92 | 93 | // move indicator scroll view 94 | guard let distance = distanceValue else { return } 95 | var xOffset: CGFloat = 0 96 | if fromIndex < toIndex { 97 | xOffset = distance * CGFloat(fromIndex) + distance * progressPercentage 98 | } else if fromIndex > toIndex { 99 | xOffset = distance * CGFloat(fromIndex) - distance * progressPercentage 100 | } else { 101 | xOffset = distance * CGFloat(fromIndex) 102 | } 103 | 104 | titleScrollView.contentOffset = CGPoint(x: xOffset, y: 0) 105 | 106 | // update alpha of titles 107 | setAlphaWith(offset: xOffset, andDistance: distance) 108 | 109 | // update page control page 110 | pageControl.currentPage = currentIndex 111 | } 112 | 113 | open func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int) { 114 | fatalError() 115 | } 116 | 117 | open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 118 | guard object as AnyObject === titleView && keyPath == "frame" && change?[NSKeyValueChangeKey.kindKey] as? UInt == NSKeyValueChange.setting.rawValue else { return } 119 | 120 | let oldRect = (change![NSKeyValueChangeKey.oldKey]! as AnyObject).cgRectValue 121 | let newRect = (change![NSKeyValueChangeKey.oldKey]! as AnyObject).cgRectValue 122 | if (oldRect?.equalTo(newRect!))! { 123 | titleScrollView.frame = CGRect(x: 0, y: 0, width: titleView.frame.width, height: titleScrollView.frame.height) 124 | setNavigationViewItemsPosition(updateAlpha: true) 125 | } 126 | } 127 | 128 | deinit { 129 | if isViewLoaded { 130 | titleView.removeObserver(self, forKeyPath: "frame") 131 | } 132 | } 133 | 134 | open override func viewDidLayoutSubviews() { 135 | super.viewDidLayoutSubviews() 136 | setNavigationViewItemsPosition(updateAlpha: false) 137 | } 138 | 139 | // MARK: - Helpers 140 | 141 | private lazy var titleView: UIView = { 142 | let navigationView = UIView() 143 | navigationView.autoresizingMask = [.flexibleWidth, .flexibleHeight] 144 | return navigationView 145 | }() 146 | 147 | private lazy var titleScrollView: UIScrollView = { [unowned self] in 148 | let titleScrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: self.view.bounds.width, height: 44)) 149 | titleScrollView.autoresizingMask = [.flexibleWidth, .flexibleHeight] 150 | titleScrollView.bounces = true 151 | titleScrollView.scrollsToTop = false 152 | titleScrollView.delegate = self 153 | titleScrollView.showsVerticalScrollIndicator = false 154 | titleScrollView.showsHorizontalScrollIndicator = false 155 | titleScrollView.isPagingEnabled = true 156 | titleScrollView.isUserInteractionEnabled = false 157 | titleScrollView.alwaysBounceHorizontal = true 158 | titleScrollView.alwaysBounceVertical = false 159 | return titleScrollView 160 | }() 161 | 162 | private lazy var pageControl: FXPageControl = { [unowned self] in 163 | let pageControl = FXPageControl() 164 | pageControl.backgroundColor = .clear 165 | pageControl.dotSize = 3.8 166 | pageControl.dotSpacing = 4.0 167 | pageControl.dotColor = self.settings.style.dotColor 168 | pageControl.selectedDotColor = self.settings.style.selectedDotColor 169 | pageControl.isUserInteractionEnabled = false 170 | return pageControl 171 | }() 172 | 173 | private var childTitleLabels = [UILabel]() 174 | 175 | private func reloadNavigationViewItems() { 176 | // remove all child view controller header labels 177 | childTitleLabels.forEach { $0.removeFromSuperview() } 178 | childTitleLabels.removeAll() 179 | for (index, item) in viewControllers.enumerated() { 180 | let child = item as! IndicatorInfoProvider // swiftlint:disable:this force_cast 181 | let indicatorInfo = child.indicatorInfo(for: self) 182 | let navTitleLabel: UILabel = { 183 | let label = UILabel() 184 | label.text = indicatorInfo.title 185 | label.font = UIWindow.isPortrait ? settings.style.portraitTitleFont : settings.style.landscapeTitleFont 186 | label.textColor = settings.style.titleColor 187 | label.alpha = 0 188 | return label 189 | }() 190 | navTitleLabel.alpha = currentIndex == index ? 1 : 0 191 | navTitleLabel.textColor = settings.style.titleColor 192 | titleScrollView.addSubview(navTitleLabel) 193 | childTitleLabels.append(navTitleLabel) 194 | } 195 | } 196 | 197 | private func setNavigationViewItemsPosition(updateAlpha: Bool) { 198 | guard let distance = distanceValue else { return } 199 | let isPortrait = UIWindow.isPortrait 200 | let navBarHeight: CGFloat = navigationController!.navigationBar.frame.size.height 201 | for (index, label) in childTitleLabels.enumerated() { 202 | if updateAlpha { 203 | label.alpha = currentIndex == index ? 1 : 0 204 | } 205 | label.font = isPortrait ? settings.style.portraitTitleFont : settings.style.landscapeTitleFont 206 | let viewSize = label.intrinsicContentSize 207 | let originX = distance - viewSize.width/2 + CGFloat(index) * distance 208 | let originY = (CGFloat(navBarHeight) - viewSize.height) / 2 209 | label.frame = CGRect(x: originX, y: originY - 2, width: viewSize.width, height: viewSize.height) 210 | label.tag = index 211 | } 212 | 213 | let xOffset = distance * CGFloat(currentIndex) 214 | titleScrollView.contentOffset = CGPoint(x: xOffset, y: 0) 215 | 216 | pageControl.numberOfPages = childTitleLabels.count 217 | pageControl.currentPage = currentIndex 218 | let viewSize = pageControl.sizeForNumber(ofPages: childTitleLabels.count) 219 | let originX = distance - viewSize.width / 2 220 | pageControl.frame = CGRect(x: originX, y: navBarHeight - 10, width: viewSize.width, height: viewSize.height) 221 | } 222 | 223 | private func setAlphaWith(offset: CGFloat, andDistance distance: CGFloat) { 224 | for (index, label) in childTitleLabels.enumerated() { 225 | label.alpha = { 226 | if offset < distance * CGFloat(index) { 227 | return (offset - distance * CGFloat(index - 1)) / distance 228 | } else { 229 | return 1 - ((offset - distance * CGFloat(index)) / distance) 230 | } 231 | }() 232 | } 233 | } 234 | 235 | private var distanceValue: CGFloat? { 236 | return navigationController.map { $0.navigationBar.convert($0.navigationBar.center, to: titleView) }?.x 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /Sources/XLPagerTabStrip/UIWindow+Pager.swift: -------------------------------------------------------------------------------- 1 | // TwitterPagerTabStripViewController.swift 2 | // XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip ) 3 | // 4 | // Copyright (c) 2017 Xmartlabs ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Foundation 26 | import UIKit 27 | 28 | extension UIWindow { 29 | static var isLandscape: Bool { 30 | if #available(iOS 13.0, *) { 31 | return UIApplication.shared.windows 32 | .first? 33 | .windowScene? 34 | .interfaceOrientation 35 | .isLandscape ?? false 36 | } else { 37 | return UIApplication.shared.statusBarOrientation.isLandscape 38 | } 39 | } 40 | static var isPortrait: Bool { 41 | if #available(iOS 13.0, *) { 42 | return UIApplication.shared.windows 43 | .first? 44 | .windowScene? 45 | .interfaceOrientation 46 | .isPortrait ?? false 47 | } else { 48 | return UIApplication.shared.statusBarOrientation.isPortrait 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Tests/XLPagerTabStripTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XLPagerTabStripTests.swift 3 | // XLPagerTabStripTests 4 | // 5 | // Copyright © 2016 Xmartlabs SRL. All rights reserved. 6 | // 7 | 8 | import XCTest 9 | @testable import XLPagerTabStrip 10 | 11 | class XLPagerTabStripTests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | super.tearDown() 21 | } 22 | 23 | func testExample() { 24 | // This is an example of a functional test case. 25 | // Use XCTAssert and related functions to verify your tests produce the correct results. 26 | } 27 | 28 | func testPerformanceExample() { 29 | // This is an example of a performance test case. 30 | self.measure { 31 | // Put the code you want to measure the time of here. 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /XLPagerTabStrip.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "XLPagerTabStrip" 3 | s.version = "9.1.0" 4 | s.summary = "Android PagerTabStrip for iOS and much more." 5 | s.homepage = "https://github.com/xmartlabs/XLPagerTabStrip" 6 | s.license = { type: 'MIT', file: 'LICENSE' } 7 | s.author = { "Martin Barreto" => "martin@xmartlabs.com" } 8 | s.source = { git: "https://github.com/xmartlabs/XLPagerTabStrip.git", tag: s.version.to_s } 9 | s.social_media_url = 'https://twitter.com/xmartlabs' 10 | s.ios.deployment_target = '11.0' 11 | s.requires_arc = true 12 | s.ios.source_files = 'Sources/**/*.{swift}', 'ObjC/*.{h,m}' 13 | s.ios.frameworks = 'UIKit', 'Foundation' 14 | s.resource_bundles = { 'XLPagerTabStrip' => ['Sources/**/ButtonCell.xib'] } 15 | s.swift_version = "5.0" 16 | end 17 | -------------------------------------------------------------------------------- /XLPagerTabStrip.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /XLPagerTabStrip.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /XLPagerTabStrip.xcodeproj/xcshareddata/xcschemes/XLPagerTabStrip.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 42 | 48 | 49 | 50 | 51 | 52 | 62 | 63 | 69 | 70 | 71 | 72 | 78 | 79 | 85 | 86 | 87 | 88 | 90 | 91 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /XLPagerTabStrip.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /XLPagerTabStrip.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /XLPagerTabStrip/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 | 9.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /XLPagerTabStrip/XLPagerTabStrip.h: -------------------------------------------------------------------------------- 1 | // 2 | // XLPagerTabStrip.h 3 | // XLPagerTabStrip 4 | // 5 | // Copyright © 2016 Xmartlabs SRL. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | //! Project version number for XLPagerTabStrip. 11 | FOUNDATION_EXPORT double XLPagerTabStripVersionNumber; 12 | 13 | //! Project version string for XLPagerTabStrip. 14 | FOUNDATION_EXPORT const unsigned char XLPagerTabStripVersionString[]; 15 | 16 | // In this header, you should import all the public headers of your framework using statements like #import 17 | 18 | #import "FXPageControl.h" 19 | 20 | -------------------------------------------------------------------------------- /XLPagerTabTrip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/XLPagerTabStrip/211ed62aa376722cf93c429802a8b6ff66a8bd52/XLPagerTabTrip.png --------------------------------------------------------------------------------