├── .gitignore ├── LICENSE ├── ParallaxHeader.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── tringo.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── tringo.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── ParallaxHeader.xcworkspace ├── contents.xcworkspacedata └── xcuserdata │ └── tringo.xcuserdatad │ ├── UserInterfaceState.xcuserstate │ └── xcdebugger │ └── Breakpoints_v2.xcbkptlist ├── ParallaxHeader ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Cell │ ├── NumberCell.swift │ └── NumberCell.xib ├── HeaderView.swift ├── Info.plist ├── ProfileViewController.swift ├── PublishedViewController.swift ├── SavedViewController.swift └── TaggedViewController.swift ├── Podfile ├── Podfile.lock ├── Pods ├── Manifest.lock ├── Pods.xcodeproj │ ├── project.pbxproj │ └── xcuserdata │ │ └── tringo.xcuserdatad │ │ └── xcschemes │ │ ├── Pods-ParallaxHeader.xcscheme │ │ ├── XLPagerTabStrip-XLPagerTabStrip.xcscheme │ │ ├── XLPagerTabStrip.xcscheme │ │ └── xcschememanagement.plist ├── Target Support Files │ ├── Pods-ParallaxHeader │ │ ├── Info.plist │ │ ├── Pods-ParallaxHeader-acknowledgements.markdown │ │ ├── Pods-ParallaxHeader-acknowledgements.plist │ │ ├── Pods-ParallaxHeader-dummy.m │ │ ├── Pods-ParallaxHeader-frameworks.sh │ │ ├── Pods-ParallaxHeader-resources.sh │ │ ├── Pods-ParallaxHeader-umbrella.h │ │ ├── Pods-ParallaxHeader.debug.xcconfig │ │ ├── Pods-ParallaxHeader.modulemap │ │ └── Pods-ParallaxHeader.release.xcconfig │ └── XLPagerTabStrip │ │ ├── Info.plist │ │ ├── ResourceBundle-XLPagerTabStrip-Info.plist │ │ ├── XLPagerTabStrip-dummy.m │ │ ├── XLPagerTabStrip-prefix.pch │ │ ├── XLPagerTabStrip-umbrella.h │ │ ├── XLPagerTabStrip.modulemap │ │ └── XLPagerTabStrip.xcconfig └── XLPagerTabStrip │ ├── LICENSE │ ├── README.md │ └── Sources │ ├── BarPagerTabStripViewController.swift │ ├── BarView.swift │ ├── BaseButtonBarPagerTabStripViewController.swift │ ├── ButtonBarPagerTabStripViewController.swift │ ├── ButtonBarView.swift │ ├── ButtonBarViewCell.swift │ ├── ButtonCell.xib │ ├── FXPageControl.h │ ├── FXPageControl.m │ ├── IndicatorInfo.swift │ ├── PagerTabStripBehaviour.swift │ ├── PagerTabStripError.swift │ ├── PagerTabStripViewController.swift │ ├── SegmentedPagerTabStripViewController.swift │ ├── SwipeDirection.swift │ └── TwitterPagerTabStripViewController.swift ├── README.md └── walkthrough.gif /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | .build/ 41 | 42 | # CocoaPods 43 | # 44 | # We recommend against adding the Pods directory to your .gitignore. However 45 | # you should judge for yourself, the pros and cons are mentioned at: 46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 47 | # 48 | # Pods/ 49 | 50 | # Carthage 51 | # 52 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 53 | # Carthage/Checkouts 54 | 55 | Carthage/Build 56 | 57 | # fastlane 58 | # 59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 60 | # screenshots whenever they are needed. 61 | # For more information about the recommended setup visit: 62 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 63 | 64 | fastlane/report.xml 65 | fastlane/Preview.html 66 | fastlane/screenshots 67 | fastlane/test_output 68 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /ParallaxHeader.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ParallaxHeader.xcodeproj/project.xcworkspace/xcuserdata/tringo.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngominhtrint/ParallaxHeader/ad2d06ce4c25488110eccf79304515ee3a00435e/ParallaxHeader.xcodeproj/project.xcworkspace/xcuserdata/tringo.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ParallaxHeader.xcodeproj/xcuserdata/tringo.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ParallaxHeader.xcscheme 8 | 9 | orderHint 10 | 3 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ParallaxHeader.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ParallaxHeader.xcworkspace/xcuserdata/tringo.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngominhtrint/ParallaxHeader/ad2d06ce4c25488110eccf79304515ee3a00435e/ParallaxHeader.xcworkspace/xcuserdata/tringo.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ParallaxHeader.xcworkspace/xcuserdata/tringo.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /ParallaxHeader/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ParallaxHeader 4 | // 5 | // Created by TriNgo on 3/26/18. 6 | // Copyright © 2018 TriNgo. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // 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. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // 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. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // 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. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /ParallaxHeader/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /ParallaxHeader/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 | -------------------------------------------------------------------------------- /ParallaxHeader/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /ParallaxHeader/Cell/NumberCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NumberCell.swift 3 | // ParallaxHeader 4 | // 5 | // Created by TriNgo on 3/26/18. 6 | // Copyright © 2018 TriNgo. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class NumberCell: UITableViewCell { 12 | 13 | @IBOutlet weak var lbNumber: UILabel! 14 | 15 | override func awakeFromNib() { 16 | super.awakeFromNib() 17 | // Initialization code 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ParallaxHeader/Cell/NumberCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /ParallaxHeader/HeaderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HeaderView.swift 3 | // ParallaxHeader 4 | // 5 | // Created by TriNgo on 3/28/18. 6 | // Copyright © 2018 TriNgo. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class HeaderView: UIView { 12 | 13 | var heightLayoutConstraint = NSLayoutConstraint() 14 | var bottomLayoutConstraint = NSLayoutConstraint() 15 | 16 | var containerView = UIView() 17 | var containerLayoutConstraint = NSLayoutConstraint() 18 | 19 | override init(frame: CGRect) { 20 | super.init(frame: frame) 21 | self.backgroundColor = UIColor.white 22 | 23 | // The container view is needed to extend the visible area for the image view 24 | // to include that below the navigation bar. If this container view isn't present 25 | // the image view would be clipped at the navigation bar's bottom and the parallax 26 | // effect would not work correctly 27 | 28 | containerView.translatesAutoresizingMaskIntoConstraints = false 29 | containerView.backgroundColor = UIColor.red 30 | self.addSubview(containerView) 31 | self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[containerView]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["containerView" : containerView])) 32 | self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[containerView]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["containerView" : containerView])) 33 | containerLayoutConstraint = NSLayoutConstraint(item: containerView, attribute: .height, relatedBy: .equal, toItem: self, attribute: .height, multiplier: 1.0, constant: 0.0) 34 | self.addConstraint(containerLayoutConstraint) 35 | 36 | let imageView: UIImageView = UIImageView.init() 37 | imageView.translatesAutoresizingMaskIntoConstraints = false 38 | imageView.backgroundColor = UIColor.white 39 | imageView.clipsToBounds = true 40 | imageView.contentMode = .scaleAspectFill 41 | imageView.image = UIImage(named: "Apple-Touch-ID-Promo") 42 | containerView.addSubview(imageView) 43 | containerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[imageView]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["imageView" : imageView])) 44 | bottomLayoutConstraint = NSLayoutConstraint(item: imageView, attribute: .bottom, relatedBy: .equal, toItem: containerView, attribute: .bottom, multiplier: 1.0, constant: 0.0) 45 | containerView.addConstraint(bottomLayoutConstraint) 46 | heightLayoutConstraint = NSLayoutConstraint(item: imageView, attribute: .height, relatedBy: .equal, toItem: containerView, attribute: .height, multiplier: 1.0, constant: 0.0) 47 | containerView.addConstraint(heightLayoutConstraint) 48 | } 49 | 50 | required init?(coder aDecoder: NSCoder) { 51 | super.init(coder: aDecoder) 52 | } 53 | 54 | func scrollViewDidScroll(scrollView: UIScrollView) { 55 | containerLayoutConstraint.constant = scrollView.contentInset.top; 56 | let offsetY = -(scrollView.contentOffset.y + scrollView.contentInset.top); 57 | containerView.clipsToBounds = offsetY <= 0 58 | bottomLayoutConstraint.constant = offsetY >= 0 ? 0 : -offsetY / 2 59 | heightLayoutConstraint.constant = max(offsetY + scrollView.contentInset.top, scrollView.contentInset.top) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /ParallaxHeader/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ParallaxHeader/ProfileViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProfileViewController.swift 3 | // ParallaxHeader 4 | // 5 | // Created by TriNgo on 3/26/18. 6 | // Copyright © 2018 TriNgo. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import XLPagerTabStrip 11 | 12 | class ProfileViewController: ButtonBarPagerTabStripViewController { 13 | 14 | @IBOutlet weak var containerHeightConstraint: NSLayoutConstraint! 15 | @IBOutlet weak var scrollView: UIScrollView! 16 | @IBOutlet weak var contentView: UIView! 17 | @IBOutlet weak var headerView: HeaderView! 18 | @IBOutlet weak var barView: ButtonBarView! 19 | 20 | let blue: UIColor = UIColor(red: 35/255, green: 133/255, blue: 192/255, alpha: 1) 21 | 22 | var goingUp: Bool? 23 | var childScrollingDownDueToParent = false 24 | 25 | override func viewDidLoad() { 26 | setupView() 27 | super.viewDidLoad() 28 | } 29 | 30 | private func setupView() { 31 | containerHeightConstraint.constant = UIScreen.main.bounds.height - 3 * barView.bounds.height 32 | 33 | settings.style.buttonBarBackgroundColor = .clear 34 | settings.style.buttonBarItemBackgroundColor = .clear 35 | settings.style.selectedBarBackgroundColor = blue 36 | settings.style.buttonBarItemFont = .boldSystemFont(ofSize: 14) 37 | settings.style.selectedBarHeight = 2.0 38 | settings.style.buttonBarMinimumLineSpacing = 1.0 39 | settings.style.buttonBarItemTitleColor = .black 40 | settings.style.buttonBarItemsShouldFillAvailableWidth = true 41 | settings.style.buttonBarLeftContentInset = 0 42 | settings.style.buttonBarRightContentInset = 0 43 | 44 | changeCurrentIndexProgressive = { (oldCell: ButtonBarViewCell?, newCell: ButtonBarViewCell?, progressPercentage: CGFloat, changeCurrentIndex: Bool, animated: Bool) in 45 | guard changeCurrentIndex == true else { return } 46 | oldCell?.label.textColor = .lightGray 47 | newCell?.label.textColor = self.blue 48 | } 49 | } 50 | 51 | override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] { 52 | let storyboard = UIStoryboard(name: "Main", bundle: nil) 53 | 54 | let publishedViewController = storyboard.instantiateViewController(withIdentifier: "PublishedViewController") as! PublishedViewController 55 | publishedViewController.itemInfo = IndicatorInfo(title: "PUBLISHED") 56 | publishedViewController.delegate = self 57 | 58 | let taggedViewController = storyboard.instantiateViewController(withIdentifier: "TaggedViewController") as! TaggedViewController 59 | taggedViewController.itemInfo = IndicatorInfo(title: "TAGGED") 60 | 61 | let savedViewController = storyboard.instantiateViewController(withIdentifier: "SavedViewController") as! SavedViewController 62 | savedViewController.itemInfo = IndicatorInfo(title: "SAVED") 63 | savedViewController.delegate = self 64 | 65 | return [publishedViewController, taggedViewController, savedViewController] 66 | } 67 | } 68 | 69 | extension ProfileViewController: PublishedViewControllerDelegate, SavedViewControllerDelegate { 70 | func scrollViewDidScroll(scrollView: UIScrollView, tableView: UITableView) { 71 | goingUp = scrollView.panGestureRecognizer.translation(in: scrollView).y < 0 72 | 73 | let parentViewMaxContentYOffset = self.scrollView.contentSize.height - self.scrollView.frame.height 74 | 75 | if goingUp! { 76 | if scrollView == tableView { 77 | if self.scrollView.contentOffset.y < parentViewMaxContentYOffset && !childScrollingDownDueToParent { 78 | self.scrollView.contentOffset.y = max(min(self.scrollView.contentOffset.y + tableView.contentOffset.y, parentViewMaxContentYOffset), 0) 79 | tableView.contentOffset.y = 0 80 | } 81 | } 82 | } else { 83 | if scrollView == tableView { 84 | if tableView.contentOffset.y < 0 && self.scrollView.contentOffset.y > 0 { 85 | self.scrollView.contentOffset.y = max(self.scrollView.contentOffset.y - abs(tableView.contentOffset.y), 0) 86 | } 87 | } 88 | if scrollView == self.scrollView { 89 | if tableView.contentOffset.y > 0 && self.scrollView.contentOffset.y < parentViewMaxContentYOffset { 90 | childScrollingDownDueToParent = true 91 | tableView.contentOffset.y = max(tableView.contentOffset.y - (parentViewMaxContentYOffset - self.scrollView.contentOffset.y), 0) 92 | self.scrollView.contentOffset.y = parentViewMaxContentYOffset 93 | childScrollingDownDueToParent = false 94 | } 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /ParallaxHeader/PublishedViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PublishedViewController.swift 3 | // ParallaxHeader 4 | // 5 | // Created by TriNgo on 3/26/18. 6 | // Copyright © 2018 TriNgo. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import XLPagerTabStrip 11 | 12 | protocol PublishedViewControllerDelegate: class { 13 | func scrollViewDidScroll(scrollView: UIScrollView, tableView: UITableView) 14 | } 15 | 16 | class PublishedViewController: UIViewController { 17 | 18 | @IBOutlet weak var tableView: UITableView! 19 | var itemInfo = IndicatorInfo(title: "Published") 20 | weak var delegate: PublishedViewControllerDelegate? 21 | 22 | override func viewDidLoad() { 23 | super.viewDidLoad() 24 | 25 | tableView.dataSource = self 26 | tableView.delegate = self 27 | tableView.tableFooterView = UIView() 28 | tableView.register(UINib(nibName: "NumberCell", bundle: nil), forCellReuseIdentifier: "NumberCellReuseIdentifier") 29 | } 30 | } 31 | 32 | extension PublishedViewController: UITableViewDataSource { 33 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 34 | return 30 35 | } 36 | 37 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 38 | if let cell = tableView.dequeueReusableCell(withIdentifier: "NumberCellReuseIdentifier", for: indexPath) as? NumberCell { 39 | cell.lbNumber.text = "\(indexPath.row)" 40 | return cell 41 | } 42 | return UITableViewCell() 43 | } 44 | } 45 | 46 | extension PublishedViewController: UITableViewDelegate { 47 | func scrollViewDidScroll(_ scrollView: UIScrollView) { 48 | delegate?.scrollViewDidScroll(scrollView: scrollView, tableView: tableView) 49 | } 50 | } 51 | 52 | extension PublishedViewController: IndicatorInfoProvider { 53 | func indicatorInfo(for pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo { 54 | return itemInfo 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ParallaxHeader/SavedViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SavedViewController.swift 3 | // ParallaxHeader 4 | // 5 | // Created by TriNgo on 3/26/18. 6 | // Copyright © 2018 TriNgo. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import XLPagerTabStrip 11 | 12 | protocol SavedViewControllerDelegate: class { 13 | func scrollViewDidScroll(scrollView: UIScrollView, tableView: UITableView) 14 | } 15 | 16 | class SavedViewController: UIViewController { 17 | 18 | var itemInfo = IndicatorInfo(title: "Saved") 19 | weak var delegate: SavedViewControllerDelegate? 20 | 21 | @IBOutlet weak var tableView: UITableView! 22 | override func viewDidLoad() { 23 | super.viewDidLoad() 24 | 25 | tableView.dataSource = self 26 | tableView.delegate = self 27 | tableView.tableFooterView = UIView() 28 | tableView.register(UINib(nibName: "NumberCell", bundle: nil), forCellReuseIdentifier: "NumberCellReuseIdentifier") 29 | } 30 | } 31 | 32 | extension SavedViewController: UITableViewDataSource { 33 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 34 | return 3 35 | } 36 | 37 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 38 | if let cell = tableView.dequeueReusableCell(withIdentifier: "NumberCellReuseIdentifier", for: indexPath) as? NumberCell { 39 | cell.lbNumber.text = "\(indexPath.row)" 40 | return cell 41 | } 42 | return UITableViewCell() 43 | } 44 | } 45 | 46 | extension SavedViewController: UITableViewDelegate { 47 | func scrollViewDidScroll(_ scrollView: UIScrollView) { 48 | delegate?.scrollViewDidScroll(scrollView: scrollView, tableView: tableView) 49 | } 50 | } 51 | 52 | extension SavedViewController: IndicatorInfoProvider { 53 | func indicatorInfo(for pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo { 54 | return itemInfo 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ParallaxHeader/TaggedViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TaggedViewController.swift 3 | // ParallaxHeader 4 | // 5 | // Created by TriNgo on 3/26/18. 6 | // Copyright © 2018 TriNgo. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import XLPagerTabStrip 11 | 12 | class TaggedViewController: UIViewController { 13 | 14 | var itemInfo = IndicatorInfo(title: "Tagged") 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | 19 | } 20 | } 21 | 22 | extension TaggedViewController: IndicatorInfoProvider { 23 | func indicatorInfo(for pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo { 24 | return itemInfo 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'ParallaxHeader' do 5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | # Pods for ParallaxHeader 9 | pod 'XLPagerTabStrip', '~> 8.0' 10 | 11 | end 12 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - XLPagerTabStrip (8.0.0) 3 | 4 | DEPENDENCIES: 5 | - XLPagerTabStrip (~> 8.0) 6 | 7 | SPEC CHECKSUMS: 8 | XLPagerTabStrip: d1ab378af0923320ec0b5eab11d7b2f803d31261 9 | 10 | PODFILE CHECKSUM: 311889b2d08b979ea83e84c323aa3d539b3fb022 11 | 12 | COCOAPODS: 1.4.0 13 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - XLPagerTabStrip (8.0.0) 3 | 4 | DEPENDENCIES: 5 | - XLPagerTabStrip (~> 8.0) 6 | 7 | SPEC CHECKSUMS: 8 | XLPagerTabStrip: d1ab378af0923320ec0b5eab11d7b2f803d31261 9 | 10 | PODFILE CHECKSUM: 311889b2d08b979ea83e84c323aa3d539b3fb022 11 | 12 | COCOAPODS: 1.4.0 13 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/tringo.xcuserdatad/xcschemes/Pods-ParallaxHeader.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 65 | 66 | 68 | 69 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/tringo.xcuserdatad/xcschemes/XLPagerTabStrip-XLPagerTabStrip.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 65 | 66 | 68 | 69 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/tringo.xcuserdatad/xcschemes/XLPagerTabStrip.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/tringo.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Pods-ParallaxHeader.xcscheme 8 | 9 | isShown 10 | 11 | orderHint 12 | 0 13 | 14 | XLPagerTabStrip-XLPagerTabStrip.xcscheme 15 | 16 | isShown 17 | 18 | orderHint 19 | 2 20 | 21 | XLPagerTabStrip.xcscheme 22 | 23 | isShown 24 | 25 | orderHint 26 | 1 27 | 28 | 29 | SuppressBuildableAutocreation 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ParallaxHeader/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ParallaxHeader/Pods-ParallaxHeader-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## XLPagerTabStrip 5 | 6 | The MIT License (MIT) 7 | 8 | Copyright (c) 2017 Xmartlabs SRL 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | Generated by CocoaPods - https://cocoapods.org 29 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ParallaxHeader/Pods-ParallaxHeader-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | The MIT License (MIT) 18 | 19 | Copyright (c) 2017 Xmartlabs SRL 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | 39 | License 40 | MIT 41 | Title 42 | XLPagerTabStrip 43 | Type 44 | PSGroupSpecifier 45 | 46 | 47 | FooterText 48 | Generated by CocoaPods - https://cocoapods.org 49 | Title 50 | 51 | Type 52 | PSGroupSpecifier 53 | 54 | 55 | StringsTable 56 | Acknowledgements 57 | Title 58 | Acknowledgements 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ParallaxHeader/Pods-ParallaxHeader-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_ParallaxHeader : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_ParallaxHeader 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ParallaxHeader/Pods-ParallaxHeader-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | # Used as a return value for each invocation of `strip_invalid_archs` function. 10 | STRIP_BINARY_RETVAL=0 11 | 12 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 13 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 14 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 15 | 16 | # Copies and strips a vendored framework 17 | install_framework() 18 | { 19 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 20 | local source="${BUILT_PRODUCTS_DIR}/$1" 21 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 22 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 23 | elif [ -r "$1" ]; then 24 | local source="$1" 25 | fi 26 | 27 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 28 | 29 | if [ -L "${source}" ]; then 30 | echo "Symlinked..." 31 | source="$(readlink "${source}")" 32 | fi 33 | 34 | # Use filter instead of exclude so missing patterns don't throw errors. 35 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 36 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 37 | 38 | local basename 39 | basename="$(basename -s .framework "$1")" 40 | binary="${destination}/${basename}.framework/${basename}" 41 | if ! [ -r "$binary" ]; then 42 | binary="${destination}/${basename}" 43 | fi 44 | 45 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 46 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 47 | strip_invalid_archs "$binary" 48 | fi 49 | 50 | # Resign the code if required by the build settings to avoid unstable apps 51 | code_sign_if_enabled "${destination}/$(basename "$1")" 52 | 53 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 54 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 55 | local swift_runtime_libs 56 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 57 | for lib in $swift_runtime_libs; do 58 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 59 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 60 | code_sign_if_enabled "${destination}/${lib}" 61 | done 62 | fi 63 | } 64 | 65 | # Copies and strips a vendored dSYM 66 | install_dsym() { 67 | local source="$1" 68 | if [ -r "$source" ]; then 69 | # Copy the dSYM into a the targets temp dir. 70 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 71 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 72 | 73 | local basename 74 | basename="$(basename -s .framework.dSYM "$source")" 75 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 76 | 77 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 78 | if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then 79 | strip_invalid_archs "$binary" 80 | fi 81 | 82 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 83 | # Move the stripped file into its final destination. 84 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 85 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 86 | else 87 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 88 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 89 | fi 90 | fi 91 | } 92 | 93 | # Signs a framework with the provided identity 94 | code_sign_if_enabled() { 95 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 96 | # Use the current code_sign_identitiy 97 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 98 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" 99 | 100 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 101 | code_sign_cmd="$code_sign_cmd &" 102 | fi 103 | echo "$code_sign_cmd" 104 | eval "$code_sign_cmd" 105 | fi 106 | } 107 | 108 | # Strip invalid architectures 109 | strip_invalid_archs() { 110 | binary="$1" 111 | # Get architectures for current target binary 112 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 113 | # Intersect them with the architectures we are building for 114 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 115 | # If there are no archs supported by this binary then warn the user 116 | if [[ -z "$intersected_archs" ]]; then 117 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 118 | STRIP_BINARY_RETVAL=0 119 | return 120 | fi 121 | stripped="" 122 | for arch in $binary_archs; do 123 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 124 | # Strip non-valid architectures in-place 125 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 126 | stripped="$stripped $arch" 127 | fi 128 | done 129 | if [[ "$stripped" ]]; then 130 | echo "Stripped $binary of architectures:$stripped" 131 | fi 132 | STRIP_BINARY_RETVAL=1 133 | } 134 | 135 | 136 | if [[ "$CONFIGURATION" == "Debug" ]]; then 137 | install_framework "${BUILT_PRODUCTS_DIR}/XLPagerTabStrip/XLPagerTabStrip.framework" 138 | fi 139 | if [[ "$CONFIGURATION" == "Release" ]]; then 140 | install_framework "${BUILT_PRODUCTS_DIR}/XLPagerTabStrip/XLPagerTabStrip.framework" 141 | fi 142 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 143 | wait 144 | fi 145 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ParallaxHeader/Pods-ParallaxHeader-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 12 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 13 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 14 | 15 | case "${TARGETED_DEVICE_FAMILY}" in 16 | 1,2) 17 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 18 | ;; 19 | 1) 20 | TARGET_DEVICE_ARGS="--target-device iphone" 21 | ;; 22 | 2) 23 | TARGET_DEVICE_ARGS="--target-device ipad" 24 | ;; 25 | 3) 26 | TARGET_DEVICE_ARGS="--target-device tv" 27 | ;; 28 | 4) 29 | TARGET_DEVICE_ARGS="--target-device watch" 30 | ;; 31 | *) 32 | TARGET_DEVICE_ARGS="--target-device mac" 33 | ;; 34 | esac 35 | 36 | install_resource() 37 | { 38 | if [[ "$1" = /* ]] ; then 39 | RESOURCE_PATH="$1" 40 | else 41 | RESOURCE_PATH="${PODS_ROOT}/$1" 42 | fi 43 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 44 | cat << EOM 45 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 46 | EOM 47 | exit 1 48 | fi 49 | case $RESOURCE_PATH in 50 | *.storyboard) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 53 | ;; 54 | *.xib) 55 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 56 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 57 | ;; 58 | *.framework) 59 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 60 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 61 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 62 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 63 | ;; 64 | *.xcdatamodel) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 67 | ;; 68 | *.xcdatamodeld) 69 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 70 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 71 | ;; 72 | *.xcmappingmodel) 73 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 74 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 75 | ;; 76 | *.xcassets) 77 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 78 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 79 | ;; 80 | *) 81 | echo "$RESOURCE_PATH" || true 82 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 83 | ;; 84 | esac 85 | } 86 | 87 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 89 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 90 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 91 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 92 | fi 93 | rm -f "$RESOURCES_TO_COPY" 94 | 95 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 96 | then 97 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 98 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 99 | while read line; do 100 | if [[ $line != "${PODS_ROOT}*" ]]; then 101 | XCASSET_FILES+=("$line") 102 | fi 103 | done <<<"$OTHER_XCASSETS" 104 | 105 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 106 | fi 107 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ParallaxHeader/Pods-ParallaxHeader-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_ParallaxHeaderVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_ParallaxHeaderVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ParallaxHeader/Pods-ParallaxHeader.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/XLPagerTabStrip" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/XLPagerTabStrip/XLPagerTabStrip.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "XLPagerTabStrip" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ParallaxHeader/Pods-ParallaxHeader.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_ParallaxHeader { 2 | umbrella header "Pods-ParallaxHeader-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ParallaxHeader/Pods-ParallaxHeader.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/XLPagerTabStrip" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/XLPagerTabStrip/XLPagerTabStrip.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "XLPagerTabStrip" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Pods/Target Support Files/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 | 8.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/XLPagerTabStrip/ResourceBundle-XLPagerTabStrip-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleIdentifier 8 | ${PRODUCT_BUNDLE_IDENTIFIER} 9 | CFBundleInfoDictionaryVersion 10 | 6.0 11 | CFBundleName 12 | ${PRODUCT_NAME} 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 8.0.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Pods/Target Support Files/XLPagerTabStrip/XLPagerTabStrip-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_XLPagerTabStrip : NSObject 3 | @end 4 | @implementation PodsDummy_XLPagerTabStrip 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/XLPagerTabStrip/XLPagerTabStrip-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/XLPagerTabStrip/XLPagerTabStrip-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "FXPageControl.h" 14 | 15 | FOUNDATION_EXPORT double XLPagerTabStripVersionNumber; 16 | FOUNDATION_EXPORT const unsigned char XLPagerTabStripVersionString[]; 17 | 18 | -------------------------------------------------------------------------------- /Pods/Target Support Files/XLPagerTabStrip/XLPagerTabStrip.modulemap: -------------------------------------------------------------------------------- 1 | framework module XLPagerTabStrip { 2 | umbrella header "XLPagerTabStrip-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/XLPagerTabStrip/XLPagerTabStrip.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/XLPagerTabStrip 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_LDFLAGS = -framework "Foundation" -framework "UIKit" 5 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT} 9 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/XLPagerTabStrip 10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | SKIP_INSTALL = YES 12 | -------------------------------------------------------------------------------- /Pods/XLPagerTabStrip/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 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 | -------------------------------------------------------------------------------- /Pods/XLPagerTabStrip/README.md: -------------------------------------------------------------------------------- 1 | # XLPagerTabStrip 2 | 3 |

4 | Build status 5 | Platform iOS 6 | Swift 3 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 | **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. 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | ## Getting involved 31 | 32 | * If you **want to contribute** please feel free to **submit pull requests**. 33 | * If you **have a feature request** please **open an issue**. 34 | * 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**. 35 | 36 | **Before contribute check the [CONTRIBUTING](CONTRIBUTING.md) file for more info.** 37 | 38 | If you use **XLPagerTabStrip** in your app We would love to hear about it! Drop us a line on [twitter](https://twitter.com/xmartlabs). 39 | 40 | ## Pager Types 41 | 42 | The library provides 4 different ways to show the view controllers. 43 | 44 | ### Button Bar 45 | 46 | This is likely to be the most common pager type. It's used by many well known apps such as instagram, youtube, skype and many others. 47 | 48 | 49 | 50 | ### Bar 51 | 52 | This mode doesn't show a title neither an image. It only shows a bar that indicates the current view controller. 53 | 54 | 55 | 56 | ### Twitter 57 | 58 | Long time ago twitter app made use of this type of pager in the app main screen. 59 | 60 | 61 | 62 | ### Segmented 63 | 64 | This mode uses a `UISegmentedControl` to indicates which is the view controller being displayed. 65 | 66 | 67 | 68 | ## Usage 69 | 70 | 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. 71 | 72 | Let's see the steps to do this: 73 | 74 | ##### Choose which type of pager we want to create 75 | 76 | First we should 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`. 77 | 78 | > All these build-in pager controllers extend from the base class `PagerTabStripViewController`. 79 | > You can also make your custom pager controller by extending directly from `PagerTabStripViewController` in case no pager menu type fits your needs. 80 | 81 | ```swift 82 | import XLPagerTabStrip 83 | 84 | class MyPagerTabStripName: ButtonBarPagerTabStripViewController { 85 | .. 86 | } 87 | ``` 88 | 89 | ##### Connect outlets and add layout constraints 90 | 91 | We strongly recommend to use IB to set up our page controller views. 92 | 93 | Drag into the storyboard a `UIViewController` and set up its class with your pager controller (`MyPagerTabStripName`). 94 | Drag a `UIScrollView` into your view controller view and connect `PagerTabStripViewController` `containerView` outlet with the scroll view. 95 | 96 | Depending on which type of paging view controller you are working with you may have to connect more outlets. 97 | 98 | 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. 99 | 100 | > 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. 101 | 102 | ##### Provide the view controllers that will appear embedded into the PagerTabStrip view controller 103 | 104 | You can provide the view controllers by overriding `func viewControllers(for: pagerTabStripController: PagerTabStripViewController) -> [UIViewController]` method. 105 | 106 | ```swift 107 | override public func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] { 108 | return [MyEmbeddedViewController(), MySecondEmbeddedViewController()] 109 | } 110 | ``` 111 | 112 | > 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. 113 | 114 | 115 | ##### Provide information to show in each indicator 116 | 117 | Every UIViewController that will appear within the PagerTabStrip needs to provide either a title or an image. 118 | In order to do so they should conform to `IndicatorInfoProvider` by implementing `func indicatorInfo(for pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo` 119 | which provides the information required to show the PagerTabStrip menu (indicator) associated with the view controller. 120 | 121 | ```swift 122 | class MyEmbeddedViewController: UITableViewController, IndicatorInfoProvider { 123 | 124 | func indicatorInfo(for pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo { 125 | return IndicatorInfo(title: "My Child title") 126 | } 127 | } 128 | ``` 129 | 130 | **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).** 131 | 132 | That's it! We're done! 🍻🍻 133 | 134 | 135 | ## Customization 136 | 137 | ##### Pager Behaviour 138 | 139 | The pager indicator can be updated progressive as we swipe or at once in the middle of the transition between the view controllers. 140 | By setting up `pagerBehaviour` property we can choose how the indicator should be updated. 141 | 142 | ```swift 143 | public var pagerBehaviour: PagerTabStripBehaviour 144 | ``` 145 | 146 | ```swift 147 | public enum PagerTabStripBehaviour { 148 | case Common(skipIntermediteViewControllers: Bool) 149 | case Progressive(skipIntermediteViewControllers: Bool, elasticIndicatorLimit: Bool) 150 | } 151 | ``` 152 | 153 | Default Values: 154 | ```swift 155 | // Twitter Type 156 | PagerTabStripBehaviour.Common(skipIntermediteViewControllers: true) 157 | // Segmented Type 158 | PagerTabStripBehaviour.Common(skipIntermediteViewControllers: true) 159 | // Bar Type 160 | PagerTabStripBehaviour.Progressive(skipIntermediteViewControllers: true, elasticIndicatorLimit: true) 161 | // ButtonBar Type 162 | PagerTabStripBehaviour.Progressive(skipIntermediteViewControllers: true, elasticIndicatorLimit: true)` 163 | ``` 164 | 165 | As you might have noticed `Common` and `Progressive` enumeration cases has `skipIntermediteViewControllers` and `elasticIndicatorLimit` associated values. 166 | 167 | `skipIntermediteViewControllers` allows us to skip intermediate view controllers when a tab indicator is tapped. 168 | 169 | `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. 170 | 171 | ##### PagerTabStripDelegate & PagerTabStripIsProgressiveDelegate 172 | 173 | Normally we don't need to implement these protocols because each pager type already conforms to it in order to properly update its indicator. Anyway there may be some scenarios when overriding a method come come in handy. 174 | 175 | ```swift 176 | public protocol PagerTabStripDelegate: class { 177 | 178 | func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int) 179 | } 180 | 181 | public protocol PagerTabStripIsProgressiveDelegate : PagerTabStripDelegate { 182 | 183 | func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int, withProgressPercentage progressPercentage: CGFloat, indexWasChanged: Bool) 184 | } 185 | ``` 186 | 187 | > Again, The method invoked by the library depends on the `pagerBehaviour` value. 188 | 189 | 190 | 191 | 192 | ### ButtonBar Customization 193 | 194 | ```swift 195 | 196 | settings.style.buttonBarBackgroundColor: UIColor? 197 | // buttonBar minimumInteritemSpacing value, note that button bar extends from UICollectionView 198 | settings.style.buttonBarMinimumInteritemSpacing: CGFloat? 199 | // buttonBar minimumLineSpacing value 200 | settings.style.buttonBarMinimumLineSpacing: CGFloat? 201 | // buttonBar flow layout left content inset value 202 | settings.style.buttonBarLeftContentInset: CGFloat? 203 | // buttonBar flow layout right content inset value 204 | settings.style.buttonBarRightContentInset: CGFloat? 205 | 206 | // selected bar view is created programmatically so it's important to set up the following 2 properties properly 207 | settings.style.selectedBarBackgroundColor = UIColor.blackColor() 208 | settings.style.selectedBarHeight: CGFloat = 5 209 | 210 | // each buttonBar item is a UICollectionView cell of type ButtonBarViewCell 211 | settings.style.buttonBarItemBackgroundColor: UIColor? 212 | settings.style.buttonBarItemFont = UIFont.systemFontOfSize(18) 213 | // helps to determine the cell width, it represent the space before and after the title label 214 | settings.style.buttonBarItemLeftRightMargin: CGFloat = 8 215 | settings.style.buttonBarItemTitleColor: UIColor? 216 | // in case the barView items do not fill the screen width this property stretch the cells to fill the screen 217 | settings.style.buttonBarItemsShouldFillAvailiableWidth = true 218 | // only used if button bar is created programmatically and not using storyboards or nib files as recommended. 219 | public var buttonBarHeight: CGFloat? 220 | ``` 221 | 222 | **Important:** Settings should be called before `viewDidLoad` is called. 223 | ```swift 224 | override func viewDidLoad() { 225 | self.settings.style.selectedBarHeight = 2 226 | self.settings.style.selectedBarBackgroundColor = UIColor.whiteColor() 227 | 228 | super.viewDidLoad() 229 | } 230 | ``` 231 | 232 | ##### Update cells when selected indicator changes 233 | 234 | 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`. 235 | 236 | ```swift 237 | public var changeCurrentIndex: ((oldCell: ButtonBarViewCell?, newCell: ButtonBarViewCell?, animated: Bool) -> Void)? 238 | public var changeCurrentIndexProgressive: ((oldCell: ButtonBarViewCell?, newCell: ButtonBarViewCell?, progressPercentage: CGFloat, changeCurrentIndex: Bool, animated: Bool) -> Void)? 239 | ``` 240 | 241 | Let's see an example: 242 | 243 | ```swift 244 | changeCurrentIndexProgressive = { (oldCell: ButtonBarViewCell?, newCell: ButtonBarViewCell?, progressPercentage: CGFloat, changeCurrentIndex: Bool, animated: Bool) -> Void in 245 | guard changeCurrentIndex == true else { return } 246 | 247 | oldCell?.label.textColor = UIColor(white: 1, alpha: 0.6) 248 | newCell?.label.textColor = .whiteColor() 249 | 250 | if animated { 251 | UIView.animateWithDuration(0.1, animations: { () -> Void in 252 | newCell?.transform = CGAffineTransformMakeScale(1.0, 1.0) 253 | oldCell?.transform = CGAffineTransformMakeScale(0.8, 0.8) 254 | }) 255 | } 256 | else { 257 | newCell?.transform = CGAffineTransformMakeScale(1.0, 1.0) 258 | oldCell?.transform = CGAffineTransformMakeScale(0.8, 0.8) 259 | } 260 | } 261 | ``` 262 | 263 | ### Bar Type Customization 264 | 265 | ```swift 266 | settings.style.barBackgroundColor: UIColor? 267 | settings.style.selectedBarBackgroundColor: UIColor? 268 | // barHeight is only set up when the bar is created programmatically and not using storyboards or xib files as recommended. 269 | settings.style.barHeight: CGFloat = 5 270 | ``` 271 | 272 | ### Twitter Type Customization 273 | 274 | ```swift 275 | settings.style.dotColor = UIColor(white: 1, alpha: 0.4) 276 | settings.style.selectedDotColor = UIColor.whiteColor() 277 | settings.style.portraitTitleFont = UIFont.systemFontOfSize(18) 278 | settings.style.landscapeTitleFont = UIFont.systemFontOfSize(15) 279 | settings.style.titleColor = UIColor.whiteColor() 280 | ``` 281 | 282 | ### Segmented Type Customization 283 | 284 | ```swift 285 | settings.style.segmentedControlColor: UIColor? 286 | ``` 287 | 288 | 289 | 290 | ## Requirements 291 | 292 | * iOS 8.0+ 293 | * Xcode 8.0+ 294 | 295 | ## Examples 296 | 297 | Follow these 3 steps to run Example project: Clone XLPagerTabStrip repository, open XLPagerTabStrip workspace and run the *Example* project. 298 | 299 | ## Installation 300 | 301 | ### CocoaPods 302 | 303 | [CocoaPods](https://cocoapods.org/) is a dependency manager for Cocoa projects. 304 | 305 | To install XLPagerTabStrip, simply add the following line to your Podfile: 306 | 307 | ```ruby 308 | pod 'XLPagerTabStrip', '~> 8.0' 309 | ``` 310 | 311 | ### Carthage 312 | 313 | [Carthage](https://github.com/Carthage/Carthage) is a simple, decentralized dependency manager for Cocoa. 314 | 315 | To install XLPagerTabStrip, simply add the following line to your Cartfile: 316 | 317 | ```ogdl 318 | github "xmartlabs/XLPagerTabStrip" ~> 8.0 319 | ``` 320 | 321 | ## FAQ 322 | 323 | #### How to change the visible child view controller programmatically 324 | 325 | `PagerTabStripViewController` provides the following methods to programmatically change the visible child view controller: 326 | 327 | ```swift 328 | func moveToViewController(at index: Int) 329 | func moveToViewController(at index: Int, animated: Bool) 330 | func moveTo(viewController: UIViewController) 331 | func moveTo(viewController: UIViewController, animated: Bool) 332 | ``` 333 | 334 | 335 | #### How to migrate from Swift 2 to Swift 3 336 | 337 | Check out [our migration guide](https://github.com/xmartlabs/XLPagerTabStrip/blob/master/Migration.md) 338 | 339 | ## Author 340 | 341 | * [Martin Barreto](https://github.com/mtnBarreto) ([@mtnBarreto](https://twitter.com/mtnBarreto)) 342 | 343 | ## Change Log 344 | 345 | This can be found in the [CHANGELOG.md](CHANGELOG.md) file. 346 | -------------------------------------------------------------------------------- /Pods/XLPagerTabStrip/Sources/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 | -------------------------------------------------------------------------------- /Pods/XLPagerTabStrip/Sources/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 | 27 | open class BarView: UIView { 28 | 29 | open lazy var selectedBar: UIView = { [unowned self] in 30 | let selectedBar = UIView(frame: CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height)) 31 | return selectedBar 32 | }() 33 | 34 | var optionsCount = 1 { 35 | willSet(newOptionsCount) { 36 | if newOptionsCount <= selectedIndex { 37 | selectedIndex = optionsCount - 1 38 | } 39 | } 40 | } 41 | var selectedIndex = 0 42 | 43 | required public init?(coder aDecoder: NSCoder) { 44 | super.init(coder: aDecoder) 45 | addSubview(selectedBar) 46 | } 47 | 48 | override init(frame: CGRect) { 49 | super.init(frame: frame) 50 | addSubview(selectedBar) 51 | } 52 | 53 | // MARK: - Helpers 54 | 55 | private func updateSelectedBarPosition(with animation: Bool) { 56 | var frame = selectedBar.frame 57 | frame.size.width = self.frame.size.width / CGFloat(optionsCount) 58 | frame.origin.x = frame.size.width * CGFloat(selectedIndex) 59 | if animation { 60 | UIView.animate(withDuration: 0.3, animations: { [weak self] in 61 | self?.selectedBar.frame = frame 62 | }) 63 | } else { 64 | selectedBar.frame = frame 65 | } 66 | } 67 | 68 | open func moveTo(index: Int, animated: Bool) { 69 | selectedIndex = index 70 | updateSelectedBarPosition(with: animated) 71 | } 72 | 73 | open func move(fromIndex: Int, toIndex: Int, progressPercentage: CGFloat) { 74 | selectedIndex = (progressPercentage > 0.5) ? toIndex : fromIndex 75 | 76 | var newFrame = selectedBar.frame 77 | newFrame.size.width = frame.size.width / CGFloat(optionsCount) 78 | var fromFrame = newFrame 79 | fromFrame.origin.x = newFrame.size.width * CGFloat(fromIndex) 80 | var toFrame = newFrame 81 | toFrame.origin.x = toFrame.size.width * CGFloat(toIndex) 82 | var targetFrame = fromFrame 83 | targetFrame.origin.x += (toFrame.origin.x - targetFrame.origin.x) * CGFloat(progressPercentage) 84 | selectedBar.frame = targetFrame 85 | } 86 | 87 | open override func layoutSubviews() { 88 | super.layoutSubviews() 89 | updateSelectedBarPosition(with: false) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Pods/XLPagerTabStrip/Sources/BaseButtonBarPagerTabStripViewController.swift: -------------------------------------------------------------------------------- 1 | // BaseButtonBarPagerTabStripViewController.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 | open class BaseButtonBarPagerTabStripViewController: PagerTabStripViewController, PagerTabStripDataSource, PagerTabStripIsProgressiveDelegate, UICollectionViewDelegate, UICollectionViewDataSource { 28 | 29 | public var settings = ButtonBarPagerTabStripSettings() 30 | public var buttonBarItemSpec: ButtonBarItemSpec! 31 | public var changeCurrentIndex: ((_ oldCell: ButtonBarCellType?, _ newCell: ButtonBarCellType?, _ animated: Bool) -> Void)? 32 | public var changeCurrentIndexProgressive: ((_ oldCell: ButtonBarCellType?, _ newCell: ButtonBarCellType?, _ progressPercentage: CGFloat, _ changeCurrentIndex: Bool, _ animated: Bool) -> Void)? 33 | 34 | @IBOutlet public weak var buttonBarView: ButtonBarView! 35 | 36 | lazy private var cachedCellWidths: [CGFloat]? = { [unowned self] in 37 | return self.calculateWidths() 38 | }() 39 | 40 | public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { 41 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 42 | delegate = self 43 | datasource = self 44 | } 45 | 46 | required public init?(coder aDecoder: NSCoder) { 47 | super.init(coder: aDecoder) 48 | delegate = self 49 | datasource = self 50 | } 51 | 52 | open override func viewDidLoad() { 53 | super.viewDidLoad() 54 | let buttonBarViewAux = buttonBarView ?? { 55 | let flowLayout = UICollectionViewFlowLayout() 56 | flowLayout.scrollDirection = .horizontal 57 | flowLayout.sectionInset = UIEdgeInsets(top: 0, left: settings.style.buttonBarLeftContentInset ?? 35, bottom: 0, right: settings.style.buttonBarRightContentInset ?? 35) 58 | let buttonBarHeight = settings.style.buttonBarHeight ?? 44 59 | let buttonBar = ButtonBarView(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: buttonBarHeight), collectionViewLayout: flowLayout) 60 | buttonBar.backgroundColor = .orange 61 | buttonBar.selectedBar.backgroundColor = .black 62 | buttonBar.autoresizingMask = .flexibleWidth 63 | var newContainerViewFrame = containerView.frame 64 | newContainerViewFrame.origin.y = buttonBarHeight 65 | newContainerViewFrame.size.height = containerView.frame.size.height - (buttonBarHeight - containerView.frame.origin.y) 66 | containerView.frame = newContainerViewFrame 67 | return buttonBar 68 | }() 69 | buttonBarView = buttonBarViewAux 70 | 71 | if buttonBarView.superview == nil { 72 | view.addSubview(buttonBarView) 73 | } 74 | if buttonBarView.delegate == nil { 75 | buttonBarView.delegate = self 76 | } 77 | if buttonBarView.dataSource == nil { 78 | buttonBarView.dataSource = self 79 | } 80 | buttonBarView.scrollsToTop = false 81 | let flowLayout = buttonBarView.collectionViewLayout as! UICollectionViewFlowLayout // swiftlint:disable:this force_cast 82 | flowLayout.scrollDirection = .horizontal 83 | flowLayout.minimumInteritemSpacing = settings.style.buttonBarMinimumInteritemSpacing ?? flowLayout.minimumInteritemSpacing 84 | flowLayout.minimumLineSpacing = settings.style.buttonBarMinimumLineSpacing ?? flowLayout.minimumLineSpacing 85 | let sectionInset = flowLayout.sectionInset 86 | flowLayout.sectionInset = UIEdgeInsets(top: sectionInset.top, left: settings.style.buttonBarLeftContentInset ?? sectionInset.left, bottom: sectionInset.bottom, right: settings.style.buttonBarRightContentInset ?? sectionInset.right) 87 | buttonBarView.showsHorizontalScrollIndicator = false 88 | buttonBarView.backgroundColor = settings.style.buttonBarBackgroundColor ?? buttonBarView.backgroundColor 89 | buttonBarView.selectedBar.backgroundColor = settings.style.selectedBarBackgroundColor 90 | 91 | buttonBarView.selectedBarHeight = settings.style.selectedBarHeight 92 | // register button bar item cell 93 | switch buttonBarItemSpec! { 94 | case .nibFile(let nibName, let bundle, _): 95 | buttonBarView.register(UINib(nibName: nibName, bundle: bundle), forCellWithReuseIdentifier:"Cell") 96 | case .cellClass: 97 | buttonBarView.register(ButtonBarCellType.self, forCellWithReuseIdentifier:"Cell") 98 | } 99 | //- 100 | } 101 | 102 | open override func viewWillAppear(_ animated: Bool) { 103 | super.viewWillAppear(animated) 104 | buttonBarView.layoutIfNeeded() 105 | isViewAppearing = true 106 | } 107 | 108 | open override func viewDidAppear(_ animated: Bool) { 109 | super.viewDidAppear(animated) 110 | isViewAppearing = false 111 | } 112 | 113 | open override func viewDidLayoutSubviews() { 114 | super.viewDidLayoutSubviews() 115 | 116 | guard isViewAppearing || isViewRotating else { return } 117 | 118 | // Force the UICollectionViewFlowLayout to get laid out again with the new size if 119 | // a) The view is appearing. This ensures that 120 | // collectionView:layout:sizeForItemAtIndexPath: is called for a second time 121 | // when the view is shown and when the view *frame(s)* are actually set 122 | // (we need the view frame's to have been set to work out the size's and on the 123 | // first call to collectionView:layout:sizeForItemAtIndexPath: the view frame(s) 124 | // aren't set correctly) 125 | // b) The view is rotating. This ensures that 126 | // collectionView:layout:sizeForItemAtIndexPath: is called again and can use the views 127 | // *new* frame so that the buttonBarView cell's actually get resized correctly 128 | cachedCellWidths = calculateWidths() 129 | buttonBarView.collectionViewLayout.invalidateLayout() 130 | // When the view first appears or is rotated we also need to ensure that the barButtonView's 131 | // selectedBar is resized and its contentOffset/scroll is set correctly (the selected 132 | // tab/cell may end up either skewed or off screen after a rotation otherwise) 133 | buttonBarView.moveTo(index: currentIndex, animated: false, swipeDirection: .none, pagerScroll: .scrollOnlyIfOutOfScreen) 134 | } 135 | 136 | // MARK: - View Rotation 137 | 138 | open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { 139 | super.viewWillTransition(to: size, with: coordinator) 140 | } 141 | 142 | // MARK: - Public Methods 143 | 144 | open override func reloadPagerTabStripView() { 145 | super.reloadPagerTabStripView() 146 | guard isViewLoaded else { return } 147 | buttonBarView.reloadData() 148 | cachedCellWidths = calculateWidths() 149 | buttonBarView.moveTo(index: currentIndex, animated: false, swipeDirection: .none, pagerScroll: .yes) 150 | } 151 | 152 | open func calculateStretchedCellWidths(_ minimumCellWidths: [CGFloat], suggestedStretchedCellWidth: CGFloat, previousNumberOfLargeCells: Int) -> CGFloat { 153 | var numberOfLargeCells = 0 154 | var totalWidthOfLargeCells: CGFloat = 0 155 | 156 | for minimumCellWidthValue in minimumCellWidths where minimumCellWidthValue > suggestedStretchedCellWidth { 157 | totalWidthOfLargeCells += minimumCellWidthValue 158 | numberOfLargeCells += 1 159 | } 160 | 161 | guard numberOfLargeCells > previousNumberOfLargeCells else { return suggestedStretchedCellWidth } 162 | 163 | let flowLayout = buttonBarView.collectionViewLayout as! UICollectionViewFlowLayout // swiftlint:disable:this force_cast 164 | let collectionViewAvailiableWidth = buttonBarView.frame.size.width - flowLayout.sectionInset.left - flowLayout.sectionInset.right 165 | let numberOfCells = minimumCellWidths.count 166 | let cellSpacingTotal = CGFloat(numberOfCells - 1) * flowLayout.minimumLineSpacing 167 | 168 | let numberOfSmallCells = numberOfCells - numberOfLargeCells 169 | let newSuggestedStretchedCellWidth = (collectionViewAvailiableWidth - totalWidthOfLargeCells - cellSpacingTotal) / CGFloat(numberOfSmallCells) 170 | 171 | return calculateStretchedCellWidths(minimumCellWidths, suggestedStretchedCellWidth: newSuggestedStretchedCellWidth, previousNumberOfLargeCells: numberOfLargeCells) 172 | } 173 | 174 | open func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int) { 175 | guard shouldUpdateButtonBarView else { return } 176 | buttonBarView.moveTo(index: toIndex, animated: true, swipeDirection: toIndex < fromIndex ? .right : .left, pagerScroll: .yes) 177 | 178 | if let changeCurrentIndex = changeCurrentIndex { 179 | let oldCell = buttonBarView.cellForItem(at: IndexPath(item: currentIndex != fromIndex ? fromIndex : toIndex, section: 0)) as? ButtonBarCellType 180 | let newCell = buttonBarView.cellForItem(at: IndexPath(item: currentIndex, section: 0)) as? ButtonBarCellType 181 | changeCurrentIndex(oldCell, newCell, true) 182 | } 183 | } 184 | 185 | open func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int, withProgressPercentage progressPercentage: CGFloat, indexWasChanged: Bool) { 186 | guard shouldUpdateButtonBarView else { return } 187 | buttonBarView.move(fromIndex: fromIndex, toIndex: toIndex, progressPercentage: progressPercentage, pagerScroll: .yes) 188 | if let changeCurrentIndexProgressive = changeCurrentIndexProgressive { 189 | let oldCell = buttonBarView.cellForItem(at: IndexPath(item: currentIndex != fromIndex ? fromIndex : toIndex, section: 0)) as? ButtonBarCellType 190 | let newCell = buttonBarView.cellForItem(at: IndexPath(item: currentIndex, section: 0)) as? ButtonBarCellType 191 | changeCurrentIndexProgressive(oldCell, newCell, progressPercentage, indexWasChanged, true) 192 | } 193 | } 194 | 195 | // MARK: - UICollectionViewDelegateFlowLayut 196 | 197 | @objc open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize { 198 | guard let cellWidthValue = cachedCellWidths?[indexPath.row] else { 199 | fatalError("cachedCellWidths for \(indexPath.row) must not be nil") 200 | } 201 | return CGSize(width: cellWidthValue, height: collectionView.frame.size.height) 202 | } 203 | 204 | open func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 205 | guard indexPath.item != currentIndex else { return } 206 | 207 | buttonBarView.moveTo(index: indexPath.item, animated: true, swipeDirection: .none, pagerScroll: .yes) 208 | shouldUpdateButtonBarView = false 209 | 210 | let oldCell = buttonBarView.cellForItem(at: IndexPath(item: currentIndex, section: 0)) as? ButtonBarCellType 211 | let newCell = buttonBarView.cellForItem(at: IndexPath(item: indexPath.item, section: 0)) as? ButtonBarCellType 212 | if pagerBehaviour.isProgressiveIndicator { 213 | if let changeCurrentIndexProgressive = changeCurrentIndexProgressive { 214 | changeCurrentIndexProgressive(oldCell, newCell, 1, true, true) 215 | } 216 | } else { 217 | if let changeCurrentIndex = changeCurrentIndex { 218 | changeCurrentIndex(oldCell, newCell, true) 219 | } 220 | } 221 | moveToViewController(at: indexPath.item) 222 | } 223 | 224 | // MARK: - UICollectionViewDataSource 225 | 226 | open func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 227 | return viewControllers.count 228 | } 229 | 230 | open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 231 | guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as? ButtonBarCellType else { 232 | fatalError("UICollectionViewCell should be or extend from ButtonBarViewCell") 233 | } 234 | let childController = viewControllers[indexPath.item] as! IndicatorInfoProvider // swiftlint:disable:this force_cast 235 | let indicatorInfo = childController.indicatorInfo(for: self) 236 | 237 | configure(cell: cell, for: indicatorInfo) 238 | 239 | if pagerBehaviour.isProgressiveIndicator { 240 | if let changeCurrentIndexProgressive = changeCurrentIndexProgressive { 241 | changeCurrentIndexProgressive(currentIndex == indexPath.item ? nil : cell, currentIndex == indexPath.item ? cell : nil, 1, true, false) 242 | } 243 | } else { 244 | if let changeCurrentIndex = changeCurrentIndex { 245 | changeCurrentIndex(currentIndex == indexPath.item ? nil : cell, currentIndex == indexPath.item ? cell : nil, false) 246 | } 247 | } 248 | 249 | return cell 250 | } 251 | 252 | // MARK: - UIScrollViewDelegate 253 | 254 | open override func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { 255 | super.scrollViewDidEndScrollingAnimation(scrollView) 256 | 257 | guard scrollView == containerView else { return } 258 | shouldUpdateButtonBarView = true 259 | } 260 | 261 | open func configure(cell: ButtonBarCellType, for indicatorInfo: IndicatorInfo) { 262 | fatalError("You must override this method to set up ButtonBarView cell accordingly") 263 | } 264 | 265 | private func calculateWidths() -> [CGFloat] { 266 | let flowLayout = buttonBarView.collectionViewLayout as! UICollectionViewFlowLayout // swiftlint:disable:this force_cast 267 | let numberOfCells = viewControllers.count 268 | 269 | var minimumCellWidths = [CGFloat]() 270 | var collectionViewContentWidth: CGFloat = 0 271 | 272 | for viewController in viewControllers { 273 | let childController = viewController as! IndicatorInfoProvider // swiftlint:disable:this force_cast 274 | let indicatorInfo = childController.indicatorInfo(for: self) 275 | switch buttonBarItemSpec! { 276 | case .cellClass(let widthCallback): 277 | let width = widthCallback(indicatorInfo) 278 | minimumCellWidths.append(width) 279 | collectionViewContentWidth += width 280 | case .nibFile(_, _, let widthCallback): 281 | let width = widthCallback(indicatorInfo) 282 | minimumCellWidths.append(width) 283 | collectionViewContentWidth += width 284 | } 285 | } 286 | 287 | let cellSpacingTotal = CGFloat(numberOfCells - 1) * flowLayout.minimumLineSpacing 288 | collectionViewContentWidth += cellSpacingTotal 289 | 290 | let collectionViewAvailableVisibleWidth = buttonBarView.frame.size.width - flowLayout.sectionInset.left - flowLayout.sectionInset.right 291 | 292 | if !settings.style.buttonBarItemsShouldFillAvailableWidth || collectionViewAvailableVisibleWidth < collectionViewContentWidth { 293 | return minimumCellWidths 294 | } else { 295 | let stretchedCellWidthIfAllEqual = (collectionViewAvailableVisibleWidth - cellSpacingTotal) / CGFloat(numberOfCells) 296 | let generalMinimumCellWidth = calculateStretchedCellWidths(minimumCellWidths, suggestedStretchedCellWidth: stretchedCellWidthIfAllEqual, previousNumberOfLargeCells: 0) 297 | var stretchedCellWidths = [CGFloat]() 298 | 299 | for minimumCellWidthValue in minimumCellWidths { 300 | let cellWidth = (minimumCellWidthValue > generalMinimumCellWidth) ? minimumCellWidthValue : generalMinimumCellWidth 301 | stretchedCellWidths.append(cellWidth) 302 | } 303 | 304 | return stretchedCellWidths 305 | } 306 | } 307 | 308 | private var shouldUpdateButtonBarView = true 309 | } 310 | 311 | open class ExampleBaseButtonBarPagerTabStripViewController: BaseButtonBarPagerTabStripViewController { 312 | 313 | public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { 314 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 315 | initialize() 316 | } 317 | 318 | public required init?(coder aDecoder: NSCoder) { 319 | super.init(coder: aDecoder) 320 | initialize() 321 | } 322 | 323 | open func initialize() { 324 | buttonBarItemSpec = .nibFile(nibName: "ButtonCell", bundle: Bundle(for: ButtonBarViewCell.self), width: { [weak self] (childItemInfo) -> CGFloat in 325 | let label = UILabel() 326 | label.translatesAutoresizingMaskIntoConstraints = false 327 | label.font = self?.settings.style.buttonBarItemFont ?? label.font 328 | label.text = childItemInfo.title 329 | let labelSize = label.intrinsicContentSize 330 | return labelSize.width + CGFloat(self?.settings.style.buttonBarItemLeftRightMargin ?? 8 * 2) 331 | }) 332 | } 333 | 334 | open override func configure(cell: ButtonBarViewCell, for indicatorInfo: IndicatorInfo) { 335 | cell.label.text = indicatorInfo.title 336 | if let image = indicatorInfo.image { 337 | cell.imageView.image = image 338 | } 339 | if let highlightedImage = indicatorInfo.highlightedImage { 340 | cell.imageView.highlightedImage = highlightedImage 341 | } 342 | } 343 | } 344 | -------------------------------------------------------------------------------- /Pods/XLPagerTabStrip/Sources/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 | -------------------------------------------------------------------------------- /Pods/XLPagerTabStrip/Sources/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 Foundation 26 | 27 | open class ButtonBarViewCell: UICollectionViewCell { 28 | 29 | @IBOutlet open var imageView: UIImageView! 30 | @IBOutlet open var label: UILabel! 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Pods/XLPagerTabStrip/Sources/ButtonCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Pods/XLPagerTabStrip/Sources/FXPageControl.h: -------------------------------------------------------------------------------- 1 | // 2 | // FXPageControl.h 3 | // 4 | // Version 1.4 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 GCC diagnostic push 35 | #pragma GCC diagnostic ignored "-Wobjc-missing-property-synthesis" 36 | #import 37 | 38 | 39 | #import 40 | #undef weak_delegate 41 | #if __has_feature(objc_arc_weak) 42 | #define weak_delegate weak 43 | #else 44 | #define weak_delegate unsafe_unretained 45 | #endif 46 | 47 | 48 | extern const CGPathRef FXPageControlDotShapeCircle; 49 | extern const CGPathRef FXPageControlDotShapeSquare; 50 | extern const CGPathRef FXPageControlDotShapeTriangle; 51 | 52 | 53 | @protocol FXPageControlDelegate; 54 | 55 | 56 | IB_DESIGNABLE @interface FXPageControl : UIControl 57 | 58 | - (void)setUp; 59 | - (CGSize)sizeForNumberOfPages:(NSInteger)pageCount; 60 | - (void)updateCurrentPageDisplay; 61 | 62 | @property (nonatomic, weak_delegate) IBOutlet id delegate; 63 | 64 | @property (nonatomic, assign) IBInspectable NSInteger currentPage; 65 | @property (nonatomic, assign) IBInspectable NSInteger numberOfPages; 66 | @property (nonatomic, assign) IBInspectable BOOL defersCurrentPageDisplay; 67 | @property (nonatomic, assign) IBInspectable BOOL hidesForSinglePage; 68 | @property (nonatomic, assign, getter = isWrapEnabled) IBInspectable BOOL wrapEnabled; 69 | @property (nonatomic, assign, getter = isVertical) IBInspectable BOOL vertical; 70 | 71 | @property (nonatomic, strong) IBInspectable UIImage *dotImage; 72 | @property (nonatomic, assign) IBInspectable CGPathRef dotShape; 73 | @property (nonatomic, assign) IBInspectable CGFloat dotSize; 74 | @property (nonatomic, strong) IBInspectable UIColor *dotColor; 75 | @property (nonatomic, strong) IBInspectable UIColor *dotShadowColor; 76 | @property (nonatomic, assign) IBInspectable CGFloat dotShadowBlur; 77 | @property (nonatomic, assign) IBInspectable CGSize dotShadowOffset; 78 | 79 | @property (nonatomic, strong) IBInspectable UIImage *selectedDotImage; 80 | @property (nonatomic, assign) IBInspectable CGPathRef selectedDotShape; 81 | @property (nonatomic, assign) IBInspectable CGFloat selectedDotSize; 82 | @property (nonatomic, strong) IBInspectable UIColor *selectedDotColor; 83 | @property (nonatomic, strong) IBInspectable UIColor *selectedDotShadowColor; 84 | @property (nonatomic, assign) IBInspectable CGFloat selectedDotShadowBlur; 85 | @property (nonatomic, assign) IBInspectable CGSize selectedDotShadowOffset; 86 | 87 | @property (nonatomic, assign) IBInspectable CGFloat dotSpacing; 88 | 89 | @end 90 | 91 | 92 | @protocol FXPageControlDelegate 93 | @optional 94 | 95 | - (UIImage *)pageControl:(FXPageControl *)pageControl imageForDotAtIndex:(NSInteger)index; 96 | - (CGPathRef)pageControl:(FXPageControl *)pageControl shapeForDotAtIndex:(NSInteger)index; 97 | - (UIColor *)pageControl:(FXPageControl *)pageControl colorForDotAtIndex:(NSInteger)index; 98 | 99 | - (UIImage *)pageControl:(FXPageControl *)pageControl selectedImageForDotAtIndex:(NSInteger)index; 100 | - (CGPathRef)pageControl:(FXPageControl *)pageControl selectedShapeForDotAtIndex:(NSInteger)index; 101 | - (UIColor *)pageControl:(FXPageControl *)pageControl selectedColorForDotAtIndex:(NSInteger)index; 102 | 103 | @end 104 | 105 | 106 | #pragma GCC diagnostic pop 107 | -------------------------------------------------------------------------------- /Pods/XLPagerTabStrip/Sources/FXPageControl.m: -------------------------------------------------------------------------------- 1 | // 2 | // FXPageControl.m 3 | // 4 | // Version 1.4 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 | #import "FXPageControl.h" 34 | 35 | 36 | #pragma GCC diagnostic ignored "-Wgnu" 37 | #pragma GCC diagnostic ignored "-Warc-repeated-use-of-weak" 38 | #pragma GCC diagnostic ignored "-Wdirect-ivar-access" 39 | 40 | 41 | #import 42 | #if !__has_feature(objc_arc) 43 | #error This class requires automatic reference counting 44 | #endif 45 | 46 | 47 | const CGPathRef FXPageControlDotShapeCircle = (const CGPathRef)1; 48 | const CGPathRef FXPageControlDotShapeSquare = (const CGPathRef)2; 49 | const CGPathRef FXPageControlDotShapeTriangle = (const CGPathRef)3; 50 | #define LAST_SHAPE FXPageControlDotShapeTriangle 51 | 52 | 53 | @implementation NSObject (FXPageControl) 54 | 55 | - (UIImage *)pageControl:(__unused FXPageControl *)pageControl imageForDotAtIndex:(__unused NSInteger)index { return nil; } 56 | - (CGPathRef)pageControl:(__unused FXPageControl *)pageControl shapeForDotAtIndex:(__unused NSInteger)index { return NULL; } 57 | - (UIColor *)pageControl:(__unused FXPageControl *)pageControl colorForDotAtIndex:(__unused NSInteger)index { return nil; } 58 | 59 | - (UIImage *)pageControl:(__unused FXPageControl *)pageControl selectedImageForDotAtIndex:(__unused NSInteger)index { return nil; } 60 | - (CGPathRef)pageControl:(__unused FXPageControl *)pageControl selectedShapeForDotAtIndex:(__unused NSInteger)index { return NULL; } 61 | - (UIColor *)pageControl:(__unused FXPageControl *)pageControl selectedColorForDotAtIndex:(__unused NSInteger)index { return nil; } 62 | 63 | @end 64 | 65 | 66 | @implementation FXPageControl 67 | 68 | - (void)setUp 69 | { 70 | //needs redrawing if bounds change 71 | self.contentMode = UIViewContentModeRedraw; 72 | 73 | //set defaults 74 | _dotSpacing = 10.0f; 75 | _dotSize = 6.0f; 76 | _dotShadowOffset = CGSizeMake(0, 1); 77 | _selectedDotShadowOffset = CGSizeMake(0, 1); 78 | } 79 | 80 | - (id)initWithFrame:(CGRect)frame 81 | { 82 | if ((self = [super initWithFrame:frame])) 83 | { 84 | [self setUp]; 85 | } 86 | return self; 87 | } 88 | 89 | - (id)initWithCoder:(NSCoder *)aDecoder 90 | { 91 | if ((self = [super initWithCoder:aDecoder])) 92 | { 93 | [self setUp]; 94 | } 95 | return self; 96 | } 97 | 98 | - (void)dealloc 99 | { 100 | if (_dotShape > LAST_SHAPE) CGPathRelease(_dotShape); 101 | if (_selectedDotShape > LAST_SHAPE) CGPathRelease(_selectedDotShape); 102 | } 103 | 104 | - (CGSize)sizeForNumberOfPages:(__unused NSInteger)pageCount 105 | { 106 | CGFloat width = _dotSize + (_dotSize + _dotSpacing) * (_numberOfPages - 1); 107 | return _vertical? CGSizeMake(_dotSize, width): CGSizeMake(width, _dotSize); 108 | } 109 | 110 | - (void)updateCurrentPageDisplay 111 | { 112 | [self setNeedsDisplay]; 113 | } 114 | 115 | - (void)drawRect:(__unused CGRect)rect 116 | { 117 | if (_numberOfPages > 1 || !_hidesForSinglePage) 118 | { 119 | CGContextRef context = UIGraphicsGetCurrentContext(); 120 | CGSize size = [self sizeForNumberOfPages:_numberOfPages]; 121 | if (_vertical) 122 | { 123 | CGContextTranslateCTM(context, self.frame.size.width / 2, (self.frame.size.height - size.height) / 2); 124 | } 125 | else 126 | { 127 | CGContextTranslateCTM(context, (self.frame.size.width - size.width) / 2, self.frame.size.height / 2); 128 | } 129 | 130 | for (int i = 0; i < _numberOfPages; i++) 131 | { 132 | UIImage *dotImage = nil; 133 | UIColor *dotColor = nil; 134 | CGPathRef dotShape = NULL; 135 | CGFloat dotSize = 0; 136 | UIColor *dotShadowColor = nil; 137 | CGSize dotShadowOffset = CGSizeZero; 138 | CGFloat dotShadowBlur = 0; 139 | 140 | if (i == _currentPage) 141 | { 142 | [_selectedDotColor setFill]; 143 | dotImage = [_delegate pageControl:self selectedImageForDotAtIndex:i] ?: _selectedDotImage; 144 | dotShape = [_delegate pageControl:self selectedShapeForDotAtIndex:i] ?: _selectedDotShape ?: _dotShape; 145 | dotColor = [_delegate pageControl:self selectedColorForDotAtIndex:i] ?: _selectedDotColor ?: [UIColor blackColor]; 146 | dotShadowBlur = _selectedDotShadowBlur; 147 | dotShadowColor = _selectedDotShadowColor; 148 | dotShadowOffset = _selectedDotShadowOffset; 149 | dotSize = _selectedDotSize ?: _dotSize; 150 | } 151 | else 152 | { 153 | [_dotColor setFill]; 154 | dotImage = [_delegate pageControl:self imageForDotAtIndex:i] ?: _dotImage; 155 | dotShape = [_delegate pageControl:self shapeForDotAtIndex:i] ?: _dotShape; 156 | dotColor = [_delegate pageControl:self colorForDotAtIndex:i] ?: _dotColor; 157 | if (!dotColor) 158 | { 159 | //fall back to selected dot color with reduced alpha 160 | dotColor = [_delegate pageControl:self selectedColorForDotAtIndex:i] ?: _selectedDotColor ?: [UIColor blackColor]; 161 | dotColor = [dotColor colorWithAlphaComponent:0.25f]; 162 | } 163 | dotShadowBlur = _dotShadowBlur; 164 | dotShadowColor = _dotShadowColor; 165 | dotShadowOffset = _dotShadowOffset; 166 | dotSize = _dotSize; 167 | } 168 | 169 | CGContextSaveGState(context); 170 | CGFloat offset = (_dotSize + _dotSpacing) * i + _dotSize / 2; 171 | CGContextTranslateCTM(context, _vertical? 0: offset, _vertical? offset: 0); 172 | 173 | if (dotShadowColor && ![dotShadowColor isEqual:[UIColor clearColor]]) 174 | { 175 | CGContextSetShadowWithColor(context, dotShadowOffset, dotShadowBlur, dotShadowColor.CGColor); 176 | } 177 | if (dotImage) 178 | { 179 | [dotImage drawInRect:CGRectMake(-dotImage.size.width / 2, -dotImage.size.height / 2, dotImage.size.width, dotImage.size.height)]; 180 | } 181 | else 182 | { 183 | [dotColor setFill]; 184 | if (!dotShape || dotShape == FXPageControlDotShapeCircle) 185 | { 186 | CGContextFillEllipseInRect(context, CGRectMake(-dotSize / 2, -dotSize / 2, dotSize, dotSize)); 187 | } 188 | else if (dotShape == FXPageControlDotShapeSquare) 189 | { 190 | CGContextFillRect(context, CGRectMake(-dotSize / 2, -dotSize / 2, dotSize, dotSize)); 191 | } 192 | else if (dotShape == FXPageControlDotShapeTriangle) 193 | { 194 | CGContextBeginPath(context); 195 | CGContextMoveToPoint(context, 0, -dotSize / 2); 196 | CGContextAddLineToPoint(context, dotSize / 2, dotSize / 2); 197 | CGContextAddLineToPoint(context, -dotSize / 2, dotSize / 2); 198 | CGContextAddLineToPoint(context, 0, -dotSize / 2); 199 | CGContextFillPath(context); 200 | } 201 | else 202 | { 203 | CGContextBeginPath(context); 204 | CGContextAddPath(context, dotShape); 205 | CGContextFillPath(context); 206 | } 207 | } 208 | CGContextRestoreGState(context); 209 | } 210 | } 211 | } 212 | 213 | - (NSInteger)clampedPageValue:(NSInteger)page 214 | { 215 | if (_wrapEnabled) 216 | { 217 | return _numberOfPages? (page + _numberOfPages) % _numberOfPages: 0; 218 | } 219 | else 220 | { 221 | return MIN(MAX(0, page), _numberOfPages - 1); 222 | } 223 | } 224 | 225 | - (void)setDotImage:(UIImage *)dotImage 226 | { 227 | if (_dotImage != dotImage) 228 | { 229 | _dotImage = dotImage; 230 | [self setNeedsDisplay]; 231 | } 232 | } 233 | 234 | - (void)setDotShape:(CGPathRef)dotShape 235 | { 236 | if (_dotShape != dotShape) 237 | { 238 | if (_dotShape > LAST_SHAPE) CGPathRelease(_dotShape); 239 | _dotShape = dotShape; 240 | if (_dotShape > LAST_SHAPE) CGPathRetain(_dotShape); 241 | [self setNeedsDisplay]; 242 | } 243 | } 244 | 245 | - (void)setDotSize:(CGFloat)dotSize 246 | { 247 | if (ABS(_dotSize - dotSize) > 0.001) 248 | { 249 | _dotSize = dotSize; 250 | [self setNeedsDisplay]; 251 | } 252 | } 253 | 254 | - (void)setDotColor:(UIColor *)dotColor 255 | { 256 | if (_dotColor != dotColor) 257 | { 258 | _dotColor = dotColor; 259 | [self setNeedsDisplay]; 260 | } 261 | } 262 | 263 | - (void)setDotShadowColor:(UIColor *)dotColor 264 | { 265 | if (_dotShadowColor != dotColor) 266 | { 267 | _dotShadowColor = dotColor; 268 | [self setNeedsDisplay]; 269 | } 270 | } 271 | 272 | - (void)setDotShadowBlur:(CGFloat)dotShadowBlur 273 | { 274 | if (ABS(_dotShadowBlur - dotShadowBlur) > 0.001) 275 | { 276 | _dotShadowBlur = dotShadowBlur; 277 | [self setNeedsDisplay]; 278 | } 279 | } 280 | 281 | - (void)setDotShadowOffset:(CGSize)dotShadowOffset 282 | { 283 | if (!CGSizeEqualToSize(_dotShadowOffset, dotShadowOffset)) 284 | { 285 | _dotShadowOffset = dotShadowOffset; 286 | [self setNeedsDisplay]; 287 | } 288 | } 289 | 290 | - (void)setSelectedDotImage:(UIImage *)dotImage 291 | { 292 | if (_selectedDotImage != dotImage) 293 | { 294 | _selectedDotImage = dotImage; 295 | [self setNeedsDisplay]; 296 | } 297 | } 298 | 299 | - (void)setSelectedDotColor:(UIColor *)dotColor 300 | { 301 | if (_selectedDotColor != dotColor) 302 | { 303 | _selectedDotColor = dotColor; 304 | [self setNeedsDisplay]; 305 | } 306 | } 307 | 308 | - (void)setSelectedDotShape:(CGPathRef)dotShape 309 | { 310 | if (_selectedDotShape != dotShape) 311 | { 312 | if (_selectedDotShape > LAST_SHAPE) CGPathRelease(_selectedDotShape); 313 | _selectedDotShape = dotShape; 314 | if (_selectedDotShape > LAST_SHAPE) CGPathRetain(_selectedDotShape); 315 | [self setNeedsDisplay]; 316 | } 317 | } 318 | 319 | - (void)setSelectedDotSize:(CGFloat)dotSize 320 | { 321 | if (ABS(_selectedDotSize - dotSize) > 0.001) 322 | { 323 | _selectedDotSize = dotSize; 324 | [self setNeedsDisplay]; 325 | } 326 | } 327 | 328 | - (void)setSelectedDotShadowColor:(UIColor *)dotColor 329 | { 330 | if (_selectedDotShadowColor != dotColor) 331 | { 332 | _selectedDotShadowColor = dotColor; 333 | [self setNeedsDisplay]; 334 | } 335 | } 336 | 337 | - (void)setSelectedDotShadowBlur:(CGFloat)dotShadowBlur 338 | { 339 | if (ABS(_selectedDotShadowBlur - dotShadowBlur) > 0.001) 340 | { 341 | _selectedDotShadowBlur = dotShadowBlur; 342 | [self setNeedsDisplay]; 343 | } 344 | } 345 | 346 | - (void)setSelectedDotShadowOffset:(CGSize)dotShadowOffset 347 | { 348 | if (!CGSizeEqualToSize(_selectedDotShadowOffset, dotShadowOffset)) 349 | { 350 | _selectedDotShadowOffset = dotShadowOffset; 351 | [self setNeedsDisplay]; 352 | } 353 | } 354 | 355 | - (void)setDotSpacing:(CGFloat)dotSpacing 356 | { 357 | if (ABS(_dotSpacing - dotSpacing) > 0.001) 358 | { 359 | _dotSpacing = dotSpacing; 360 | [self setNeedsDisplay]; 361 | } 362 | } 363 | 364 | - (void)setDelegate:(id)delegate 365 | { 366 | if (_delegate != delegate) 367 | { 368 | _delegate = delegate; 369 | [self setNeedsDisplay]; 370 | } 371 | } 372 | 373 | - (void)setCurrentPage:(NSInteger)page 374 | { 375 | _currentPage = [self clampedPageValue:page]; 376 | [self setNeedsDisplay]; 377 | } 378 | 379 | - (void)setNumberOfPages:(NSInteger)pages 380 | { 381 | if (_numberOfPages != pages) 382 | { 383 | _numberOfPages = pages; 384 | if (_currentPage >= pages) 385 | { 386 | _currentPage = pages - 1; 387 | } 388 | [self setNeedsDisplay]; 389 | } 390 | } 391 | 392 | - (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event 393 | { 394 | CGPoint point = [touch locationInView:self]; 395 | BOOL forward = _vertical? (point.y > self.frame.size.height / 2): (point.x > self.frame.size.width / 2); 396 | _currentPage = [self clampedPageValue:_currentPage + (forward? 1: -1)]; 397 | if (!_defersCurrentPageDisplay) 398 | { 399 | [self setNeedsDisplay]; 400 | } 401 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 402 | [super endTrackingWithTouch:touch withEvent:event]; 403 | } 404 | 405 | - (CGSize)sizeThatFits:(__unused CGSize)size 406 | { 407 | CGSize dotSize = [self sizeForNumberOfPages:_numberOfPages]; 408 | if (_selectedDotSize) 409 | { 410 | CGFloat width = (_selectedDotSize - _dotSize); 411 | CGFloat height = MAX(36, MAX(_dotSize, _selectedDotSize)); 412 | dotSize.width = _vertical? height: dotSize.width + width; 413 | dotSize.height = _vertical? dotSize.height + width: height; 414 | 415 | } 416 | if ((_dotShadowColor && ![_dotShadowColor isEqual:[UIColor clearColor]]) || 417 | (_selectedDotShadowColor && ![_selectedDotShadowColor isEqual:[UIColor clearColor]])) 418 | { 419 | dotSize.width += MAX(_dotShadowOffset.width, _selectedDotShadowOffset.width) * 2; 420 | dotSize.height += MAX(_dotShadowOffset.height, _selectedDotShadowOffset.height) * 2; 421 | dotSize.width += MAX(_dotShadowBlur, _selectedDotShadowBlur) * 2; 422 | dotSize.height += MAX(_dotShadowBlur, _selectedDotShadowBlur) * 2; 423 | } 424 | return dotSize; 425 | } 426 | 427 | - (CGSize)intrinsicContentSize 428 | { 429 | return [self sizeThatFits:self.bounds.size]; 430 | } 431 | 432 | @end 433 | -------------------------------------------------------------------------------- /Pods/XLPagerTabStrip/Sources/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 | 27 | public struct IndicatorInfo { 28 | 29 | public var title: String? 30 | public var image: UIImage? 31 | public var highlightedImage: UIImage? 32 | 33 | public init(title: String?) { 34 | self.title = title 35 | } 36 | 37 | public init(image: UIImage?, highlightedImage: UIImage? = nil) { 38 | self.image = image 39 | self.highlightedImage = highlightedImage 40 | } 41 | 42 | public init(title: String?, image: UIImage?, highlightedImage: UIImage? = nil) { 43 | self.title = title 44 | self.image = image 45 | self.highlightedImage = highlightedImage 46 | } 47 | 48 | } 49 | 50 | extension IndicatorInfo : ExpressibleByStringLiteral { 51 | 52 | public init(stringLiteral value: String) { 53 | title = value 54 | } 55 | 56 | public init(extendedGraphemeClusterLiteral value: String) { 57 | title = value 58 | } 59 | 60 | public init(unicodeScalarLiteral value: String) { 61 | title = value 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Pods/XLPagerTabStrip/Sources/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 | -------------------------------------------------------------------------------- /Pods/XLPagerTabStrip/Sources/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 | -------------------------------------------------------------------------------- /Pods/XLPagerTabStrip/Sources/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 | 27 | // MARK: Protocols 28 | 29 | public protocol IndicatorInfoProvider { 30 | 31 | func indicatorInfo(for pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo 32 | 33 | } 34 | 35 | public protocol PagerTabStripDelegate: class { 36 | 37 | func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int) 38 | } 39 | 40 | public protocol PagerTabStripIsProgressiveDelegate: PagerTabStripDelegate { 41 | 42 | func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int, withProgressPercentage progressPercentage: CGFloat, indexWasChanged: Bool) 43 | } 44 | 45 | public protocol PagerTabStripDataSource: class { 46 | 47 | func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] 48 | } 49 | 50 | // MARK: PagerTabStripViewController 51 | 52 | open class PagerTabStripViewController: UIViewController, UIScrollViewDelegate { 53 | 54 | @IBOutlet weak public var containerView: UIScrollView! 55 | 56 | open weak var delegate: PagerTabStripDelegate? 57 | open weak var datasource: PagerTabStripDataSource? 58 | 59 | open var pagerBehaviour = PagerTabStripBehaviour.progressive(skipIntermediateViewControllers: true, elasticIndicatorLimit: true) 60 | 61 | open private(set) var viewControllers = [UIViewController]() 62 | open private(set) var currentIndex = 0 63 | open private(set) var preCurrentIndex = 0 // used *only* to store the index to which move when the pager becomes visible 64 | 65 | open var pageWidth: CGFloat { 66 | return containerView.bounds.width 67 | } 68 | 69 | open var scrollPercentage: CGFloat { 70 | if swipeDirection != .right { 71 | let module = fmod(containerView.contentOffset.x, pageWidth) 72 | return module == 0.0 ? 1.0 : module / pageWidth 73 | } 74 | return 1 - fmod(containerView.contentOffset.x >= 0 ? containerView.contentOffset.x : pageWidth + containerView.contentOffset.x, pageWidth) / pageWidth 75 | } 76 | 77 | open var swipeDirection: SwipeDirection { 78 | if containerView.contentOffset.x > lastContentOffset { 79 | return .left 80 | } else if containerView.contentOffset.x < lastContentOffset { 81 | return .right 82 | } 83 | return .none 84 | } 85 | 86 | override open func viewDidLoad() { 87 | super.viewDidLoad() 88 | let conteinerViewAux = containerView ?? { 89 | let containerView = UIScrollView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: view.bounds.height)) 90 | containerView.autoresizingMask = [.flexibleWidth, .flexibleHeight] 91 | return containerView 92 | }() 93 | containerView = conteinerViewAux 94 | if containerView.superview == nil { 95 | view.addSubview(containerView) 96 | } 97 | containerView.bounces = true 98 | containerView.alwaysBounceHorizontal = true 99 | containerView.alwaysBounceVertical = false 100 | containerView.scrollsToTop = false 101 | containerView.delegate = self 102 | containerView.showsVerticalScrollIndicator = false 103 | containerView.showsHorizontalScrollIndicator = false 104 | containerView.isPagingEnabled = true 105 | reloadViewControllers() 106 | 107 | let childController = viewControllers[currentIndex] 108 | addChildViewController(childController) 109 | childController.view.autoresizingMask = [.flexibleHeight, .flexibleWidth] 110 | containerView.addSubview(childController.view) 111 | childController.didMove(toParentViewController: self) 112 | } 113 | 114 | open override func viewWillAppear(_ animated: Bool) { 115 | super.viewWillAppear(animated) 116 | isViewAppearing = true 117 | childViewControllers.forEach { $0.beginAppearanceTransition(true, animated: animated) } 118 | } 119 | 120 | override open func viewDidAppear(_ animated: Bool) { 121 | super.viewDidAppear(animated) 122 | lastSize = containerView.bounds.size 123 | updateIfNeeded() 124 | let needToUpdateCurrentChild = preCurrentIndex != currentIndex 125 | if needToUpdateCurrentChild { 126 | moveToViewController(at: preCurrentIndex) 127 | } 128 | isViewAppearing = false 129 | childViewControllers.forEach { $0.endAppearanceTransition() } 130 | } 131 | 132 | open override func viewWillDisappear(_ animated: Bool) { 133 | super.viewWillDisappear(animated) 134 | childViewControllers.forEach { $0.beginAppearanceTransition(false, animated: animated) } 135 | } 136 | 137 | open override func viewDidDisappear(_ animated: Bool) { 138 | super.viewDidDisappear(animated) 139 | childViewControllers.forEach { $0.endAppearanceTransition() } 140 | } 141 | 142 | override open func viewDidLayoutSubviews() { 143 | super.viewDidLayoutSubviews() 144 | updateIfNeeded() 145 | } 146 | 147 | open override var shouldAutomaticallyForwardAppearanceMethods: Bool { 148 | return false 149 | } 150 | 151 | open func moveToViewController(at index: Int, animated: Bool = true) { 152 | guard isViewLoaded && view.window != nil && currentIndex != index else { 153 | preCurrentIndex = index 154 | return 155 | } 156 | 157 | if animated && pagerBehaviour.skipIntermediateViewControllers && abs(currentIndex - index) > 1 { 158 | var tmpViewControllers = viewControllers 159 | let currentChildVC = viewControllers[currentIndex] 160 | let fromIndex = currentIndex < index ? index - 1 : index + 1 161 | let fromChildVC = viewControllers[fromIndex] 162 | tmpViewControllers[currentIndex] = fromChildVC 163 | tmpViewControllers[fromIndex] = currentChildVC 164 | pagerTabStripChildViewControllersForScrolling = tmpViewControllers 165 | containerView.setContentOffset(CGPoint(x: pageOffsetForChild(at: fromIndex), y: 0), animated: false) 166 | (navigationController?.view ?? view).isUserInteractionEnabled = !animated 167 | containerView.setContentOffset(CGPoint(x: pageOffsetForChild(at: index), y: 0), animated: true) 168 | } else { 169 | (navigationController?.view ?? view).isUserInteractionEnabled = !animated 170 | containerView.setContentOffset(CGPoint(x: pageOffsetForChild(at: index), y: 0), animated: animated) 171 | } 172 | } 173 | 174 | open func moveTo(viewController: UIViewController, animated: Bool = true) { 175 | moveToViewController(at: viewControllers.index(of: viewController)!, animated: animated) 176 | } 177 | 178 | // MARK: - PagerTabStripDataSource 179 | 180 | open func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] { 181 | assertionFailure("Sub-class must implement the PagerTabStripDataSource viewControllers(for:) method") 182 | return [] 183 | } 184 | 185 | // MARK: - Helpers 186 | 187 | open func updateIfNeeded() { 188 | if isViewLoaded && !lastSize.equalTo(containerView.bounds.size) { 189 | updateContent() 190 | } 191 | } 192 | 193 | open func canMoveTo(index: Int) -> Bool { 194 | return currentIndex != index && viewControllers.count > index 195 | } 196 | 197 | open func pageOffsetForChild(at index: Int) -> CGFloat { 198 | return CGFloat(index) * containerView.bounds.width 199 | } 200 | 201 | open func offsetForChild(at index: Int) -> CGFloat { 202 | return (CGFloat(index) * containerView.bounds.width) + ((containerView.bounds.width - view.bounds.width) * 0.5) 203 | } 204 | 205 | open func offsetForChild(viewController: UIViewController) throws -> CGFloat { 206 | guard let index = viewControllers.index(of: viewController) else { 207 | throw PagerTabStripError.viewControllerOutOfBounds 208 | } 209 | return offsetForChild(at: index) 210 | } 211 | 212 | open func pageFor(contentOffset: CGFloat) -> Int { 213 | let result = virtualPageFor(contentOffset: contentOffset) 214 | return pageFor(virtualPage: result) 215 | } 216 | 217 | open func virtualPageFor(contentOffset: CGFloat) -> Int { 218 | return Int((contentOffset + 1.5 * pageWidth) / pageWidth) - 1 219 | } 220 | 221 | open func pageFor(virtualPage: Int) -> Int { 222 | if virtualPage < 0 { 223 | return 0 224 | } 225 | if virtualPage > viewControllers.count - 1 { 226 | return viewControllers.count - 1 227 | } 228 | return virtualPage 229 | } 230 | 231 | open func updateContent() { 232 | if lastSize.width != containerView.bounds.size.width { 233 | lastSize = containerView.bounds.size 234 | containerView.contentOffset = CGPoint(x: pageOffsetForChild(at: currentIndex), y: 0) 235 | } 236 | lastSize = containerView.bounds.size 237 | 238 | let pagerViewControllers = pagerTabStripChildViewControllersForScrolling ?? viewControllers 239 | containerView.contentSize = CGSize(width: containerView.bounds.width * CGFloat(pagerViewControllers.count), height: containerView.contentSize.height) 240 | 241 | for (index, childController) in pagerViewControllers.enumerated() { 242 | let pageOffsetForChild = self.pageOffsetForChild(at: index) 243 | if fabs(containerView.contentOffset.x - pageOffsetForChild) < containerView.bounds.width { 244 | if childController.parent != nil { 245 | childController.view.frame = CGRect(x: offsetForChild(at: index), y: 0, width: view.bounds.width, height: containerView.bounds.height) 246 | childController.view.autoresizingMask = [.flexibleHeight, .flexibleWidth] 247 | } else { 248 | childController.beginAppearanceTransition(true, animated: false) 249 | addChildViewController(childController) 250 | childController.view.frame = CGRect(x: offsetForChild(at: index), y: 0, width: view.bounds.width, height: containerView.bounds.height) 251 | childController.view.autoresizingMask = [.flexibleHeight, .flexibleWidth] 252 | containerView.addSubview(childController.view) 253 | childController.didMove(toParentViewController: self) 254 | childController.endAppearanceTransition() 255 | } 256 | } else { 257 | if childController.parent != nil { 258 | childController.beginAppearanceTransition(false, animated: false) 259 | childController.willMove(toParentViewController: nil) 260 | childController.view.removeFromSuperview() 261 | childController.removeFromParentViewController() 262 | childController.endAppearanceTransition() 263 | } 264 | } 265 | } 266 | 267 | let oldCurrentIndex = currentIndex 268 | let virtualPage = virtualPageFor(contentOffset: containerView.contentOffset.x) 269 | let newCurrentIndex = pageFor(virtualPage: virtualPage) 270 | currentIndex = newCurrentIndex 271 | preCurrentIndex = currentIndex 272 | let changeCurrentIndex = newCurrentIndex != oldCurrentIndex 273 | 274 | if let progressiveDeledate = self as? PagerTabStripIsProgressiveDelegate, pagerBehaviour.isProgressiveIndicator { 275 | 276 | let (fromIndex, toIndex, scrollPercentage) = progressiveIndicatorData(virtualPage) 277 | progressiveDeledate.updateIndicator(for: self, fromIndex: fromIndex, toIndex: toIndex, withProgressPercentage: scrollPercentage, indexWasChanged: changeCurrentIndex) 278 | } else { 279 | delegate?.updateIndicator(for: self, fromIndex: min(oldCurrentIndex, pagerViewControllers.count - 1), toIndex: newCurrentIndex) 280 | } 281 | } 282 | 283 | open func reloadPagerTabStripView() { 284 | guard isViewLoaded else { return } 285 | for childController in viewControllers where childController.parent != nil { 286 | childController.beginAppearanceTransition(false, animated: false) 287 | childController.willMove(toParentViewController: nil) 288 | childController.view.removeFromSuperview() 289 | childController.removeFromParentViewController() 290 | childController.endAppearanceTransition() 291 | } 292 | reloadViewControllers() 293 | containerView.contentSize = CGSize(width: containerView.bounds.width * CGFloat(viewControllers.count), height: containerView.contentSize.height) 294 | if currentIndex >= viewControllers.count { 295 | currentIndex = viewControllers.count - 1 296 | } 297 | preCurrentIndex = currentIndex 298 | containerView.contentOffset = CGPoint(x: pageOffsetForChild(at: currentIndex), y: 0) 299 | updateContent() 300 | } 301 | 302 | // MARK: - UIScrollDelegate 303 | 304 | open func scrollViewDidScroll(_ scrollView: UIScrollView) { 305 | if containerView == scrollView { 306 | updateContent() 307 | lastContentOffset = scrollView.contentOffset.x 308 | } 309 | } 310 | 311 | open func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { 312 | if containerView == scrollView { 313 | lastPageNumber = pageFor(contentOffset: scrollView.contentOffset.x) 314 | } 315 | } 316 | 317 | open func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { 318 | if containerView == scrollView { 319 | pagerTabStripChildViewControllersForScrolling = nil 320 | (navigationController?.view ?? view).isUserInteractionEnabled = true 321 | updateContent() 322 | } 323 | } 324 | 325 | // MARK: - Orientation 326 | 327 | open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { 328 | super.viewWillTransition(to: size, with: coordinator) 329 | isViewRotating = true 330 | pageBeforeRotate = currentIndex 331 | coordinator.animate(alongsideTransition: nil) { [weak self] _ in 332 | guard let me = self else { return } 333 | me.isViewRotating = false 334 | me.currentIndex = me.pageBeforeRotate 335 | me.preCurrentIndex = me.currentIndex 336 | me.updateIfNeeded() 337 | } 338 | } 339 | 340 | // MARK: Private 341 | 342 | private func progressiveIndicatorData(_ virtualPage: Int) -> (Int, Int, CGFloat) { 343 | let count = viewControllers.count 344 | var fromIndex = currentIndex 345 | var toIndex = currentIndex 346 | let direction = swipeDirection 347 | 348 | if direction == .left { 349 | if virtualPage > count - 1 { 350 | fromIndex = count - 1 351 | toIndex = count 352 | } else { 353 | if self.scrollPercentage >= 0.5 { 354 | fromIndex = max(toIndex - 1, 0) 355 | } else { 356 | toIndex = fromIndex + 1 357 | } 358 | } 359 | } else if direction == .right { 360 | if virtualPage < 0 { 361 | fromIndex = 0 362 | toIndex = -1 363 | } else { 364 | if self.scrollPercentage > 0.5 { 365 | fromIndex = min(toIndex + 1, count - 1) 366 | } else { 367 | toIndex = fromIndex - 1 368 | } 369 | } 370 | } 371 | let scrollPercentage = pagerBehaviour.isElasticIndicatorLimit ? self.scrollPercentage : ((toIndex < 0 || toIndex >= count) ? 0.0 : self.scrollPercentage) 372 | return (fromIndex, toIndex, scrollPercentage) 373 | } 374 | 375 | private func reloadViewControllers() { 376 | guard let dataSource = datasource else { 377 | fatalError("dataSource must not be nil") 378 | } 379 | viewControllers = dataSource.viewControllers(for: self) 380 | // viewControllers 381 | guard !viewControllers.isEmpty else { 382 | fatalError("viewControllers(for:) should provide at least one child view controller") 383 | } 384 | viewControllers.forEach { if !($0 is IndicatorInfoProvider) { fatalError("Every view controller provided by PagerTabStripDataSource's viewControllers(for:) method must conform to InfoProvider") }} 385 | 386 | } 387 | 388 | private var pagerTabStripChildViewControllersForScrolling: [UIViewController]? 389 | private var lastPageNumber = 0 390 | private var lastContentOffset: CGFloat = 0.0 391 | private var pageBeforeRotate = 0 392 | private var lastSize = CGSize(width: 0, height: 0) 393 | internal var isViewRotating = false 394 | internal var isViewAppearing = false 395 | 396 | } 397 | -------------------------------------------------------------------------------- /Pods/XLPagerTabStrip/Sources/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 | 27 | public struct SegmentedPagerTabStripSettings { 28 | 29 | public struct Style { 30 | public var segmentedControlColor: UIColor? 31 | } 32 | 33 | public var style = Style() 34 | } 35 | 36 | open class SegmentedPagerTabStripViewController: PagerTabStripViewController, PagerTabStripDataSource, PagerTabStripDelegate { 37 | 38 | @IBOutlet weak public var segmentedControl: UISegmentedControl! 39 | 40 | open var settings = SegmentedPagerTabStripSettings() 41 | 42 | public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { 43 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 44 | pagerBehaviour = PagerTabStripBehaviour.common(skipIntermediateViewControllers: true) 45 | delegate = self 46 | datasource = self 47 | } 48 | 49 | required public init?(coder aDecoder: NSCoder) { 50 | super.init(coder: aDecoder) 51 | pagerBehaviour = PagerTabStripBehaviour.common(skipIntermediateViewControllers: true) 52 | delegate = self 53 | datasource = self 54 | } 55 | 56 | private(set) var shouldUpdateSegmentedControl = true 57 | 58 | open override func viewDidLoad() { 59 | super.viewDidLoad() 60 | segmentedControl = segmentedControl ?? UISegmentedControl() 61 | if segmentedControl.superview == nil { 62 | navigationItem.titleView = segmentedControl 63 | } 64 | segmentedControl.tintColor = settings.style.segmentedControlColor ?? segmentedControl.tintColor 65 | segmentedControl.addTarget(self, action: #selector(SegmentedPagerTabStripViewController.segmentedControlChanged(_:)), for: .valueChanged) 66 | reloadSegmentedControl() 67 | } 68 | 69 | open override func reloadPagerTabStripView() { 70 | super.reloadPagerTabStripView() 71 | if isViewLoaded { 72 | reloadSegmentedControl() 73 | } 74 | } 75 | 76 | func reloadSegmentedControl() { 77 | segmentedControl.removeAllSegments() 78 | for (index, item) in viewControllers.enumerated() { 79 | let child = item as! IndicatorInfoProvider // swiftlint:disable:this force_cast 80 | if let image = child.indicatorInfo(for: self).image { 81 | segmentedControl.insertSegment(with: image, at: index, animated: false) 82 | } else { 83 | segmentedControl.insertSegment(withTitle: child.indicatorInfo(for: self).title, at: index, animated: false) 84 | } 85 | } 86 | segmentedControl.selectedSegmentIndex = currentIndex 87 | } 88 | 89 | @objc func segmentedControlChanged(_ sender: UISegmentedControl) { 90 | let index = sender.selectedSegmentIndex 91 | updateIndicator(for: self, fromIndex: currentIndex, toIndex: index) 92 | shouldUpdateSegmentedControl = false 93 | moveToViewController(at: index) 94 | } 95 | 96 | // MARK: - PagerTabStripDelegate 97 | 98 | open func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int) { 99 | if shouldUpdateSegmentedControl { 100 | segmentedControl.selectedSegmentIndex = toIndex 101 | } 102 | } 103 | 104 | // MARK: - UIScrollViewDelegate 105 | 106 | open override func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { 107 | super.scrollViewDidEndScrollingAnimation(scrollView) 108 | shouldUpdateSegmentedControl = true 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Pods/XLPagerTabStrip/Sources/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 | -------------------------------------------------------------------------------- /Pods/XLPagerTabStrip/Sources/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 | 27 | public struct TwitterPagerTabStripSettings { 28 | 29 | public struct Style { 30 | public var dotColor = UIColor(white: 1, alpha: 0.4) 31 | public var selectedDotColor = UIColor.white 32 | public var portraitTitleFont = UIFont.systemFont(ofSize: 18) 33 | public var landscapeTitleFont = UIFont.systemFont(ofSize: 15) 34 | public var titleColor = UIColor.white 35 | } 36 | 37 | public var style = Style() 38 | } 39 | 40 | open class TwitterPagerTabStripViewController: PagerTabStripViewController, PagerTabStripDataSource, PagerTabStripIsProgressiveDelegate { 41 | 42 | open var settings = TwitterPagerTabStripSettings() 43 | 44 | public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { 45 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 46 | pagerBehaviour = .progressive(skipIntermediateViewControllers: true, elasticIndicatorLimit: true) 47 | delegate = self 48 | datasource = self 49 | } 50 | 51 | required public init?(coder aDecoder: NSCoder) { 52 | super.init(coder: aDecoder) 53 | pagerBehaviour = .progressive(skipIntermediateViewControllers: true, elasticIndicatorLimit: true) 54 | delegate = self 55 | datasource = self 56 | } 57 | 58 | open override func viewDidLoad() { 59 | super.viewDidLoad() 60 | 61 | if titleView.superview == nil { 62 | navigationItem.titleView = titleView 63 | } 64 | 65 | // keep watching the frame of titleView 66 | titleView.addObserver(self, forKeyPath: "frame", options: [.new, .old], context: nil) 67 | 68 | guard let navigationController = navigationController else { 69 | fatalError("TwitterPagerTabStripViewController should be embedded in a UINavigationController") 70 | } 71 | titleView.frame = CGRect(x: 0, y: 0, width: navigationController.navigationBar.frame.width, height: navigationController.navigationBar.frame.height) 72 | titleView.addSubview(titleScrollView) 73 | titleView.addSubview(pageControl) 74 | reloadNavigationViewItems() 75 | } 76 | 77 | open override func reloadPagerTabStripView() { 78 | super.reloadPagerTabStripView() 79 | guard isViewLoaded else { return } 80 | 81 | reloadNavigationViewItems() 82 | setNavigationViewItemsPosition(updateAlpha: true) 83 | } 84 | 85 | // MARK: - PagerTabStripDelegate 86 | 87 | open func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int, withProgressPercentage progressPercentage: CGFloat, indexWasChanged: Bool) { 88 | 89 | // move indicator scroll view 90 | guard let distance = distanceValue else { return } 91 | var xOffset: CGFloat = 0 92 | if fromIndex < toIndex { 93 | xOffset = distance * CGFloat(fromIndex) + distance * progressPercentage 94 | } else if fromIndex > toIndex { 95 | xOffset = distance * CGFloat(fromIndex) - distance * progressPercentage 96 | } else { 97 | xOffset = distance * CGFloat(fromIndex) 98 | } 99 | 100 | titleScrollView.contentOffset = CGPoint(x: xOffset, y: 0) 101 | 102 | // update alpha of titles 103 | setAlphaWith(offset: xOffset, andDistance: distance) 104 | 105 | // update page control page 106 | pageControl.currentPage = currentIndex 107 | } 108 | 109 | open func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int) { 110 | fatalError() 111 | } 112 | 113 | open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 114 | guard object as AnyObject === titleView && keyPath == "frame" && change?[NSKeyValueChangeKey.kindKey] as? UInt == NSKeyValueChange.setting.rawValue else { return } 115 | 116 | let oldRect = (change![NSKeyValueChangeKey.oldKey]! as AnyObject).cgRectValue 117 | let newRect = (change![NSKeyValueChangeKey.oldKey]! as AnyObject).cgRectValue 118 | if (oldRect?.equalTo(newRect!))! { 119 | titleScrollView.frame = CGRect(x: 0, y: 0, width: titleView.frame.width, height: titleScrollView.frame.height) 120 | setNavigationViewItemsPosition(updateAlpha: true) 121 | } 122 | } 123 | 124 | deinit { 125 | if isViewLoaded { 126 | titleView.removeObserver(self, forKeyPath: "frame") 127 | } 128 | } 129 | 130 | open override func viewDidLayoutSubviews() { 131 | super.viewDidLayoutSubviews() 132 | setNavigationViewItemsPosition(updateAlpha: false) 133 | } 134 | 135 | // MARK: - Helpers 136 | 137 | private lazy var titleView: UIView = { 138 | let navigationView = UIView() 139 | navigationView.autoresizingMask = [.flexibleWidth, .flexibleHeight] 140 | return navigationView 141 | }() 142 | 143 | private lazy var titleScrollView: UIScrollView = { [unowned self] in 144 | let titleScrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: self.view.bounds.width, height: 44)) 145 | titleScrollView.autoresizingMask = [.flexibleWidth, .flexibleHeight] 146 | titleScrollView.bounces = true 147 | titleScrollView.scrollsToTop = false 148 | titleScrollView.delegate = self 149 | titleScrollView.showsVerticalScrollIndicator = false 150 | titleScrollView.showsHorizontalScrollIndicator = false 151 | titleScrollView.isPagingEnabled = true 152 | titleScrollView.isUserInteractionEnabled = false 153 | titleScrollView.alwaysBounceHorizontal = true 154 | titleScrollView.alwaysBounceVertical = false 155 | return titleScrollView 156 | }() 157 | 158 | private lazy var pageControl: FXPageControl = { [unowned self] in 159 | let pageControl = FXPageControl() 160 | pageControl.backgroundColor = .clear 161 | pageControl.dotSize = 3.8 162 | pageControl.dotSpacing = 4.0 163 | pageControl.dotColor = self.settings.style.dotColor 164 | pageControl.selectedDotColor = self.settings.style.selectedDotColor 165 | pageControl.isUserInteractionEnabled = false 166 | return pageControl 167 | }() 168 | 169 | private var childTitleLabels = [UILabel]() 170 | 171 | private func reloadNavigationViewItems() { 172 | // remove all child view controller header labels 173 | childTitleLabels.forEach { $0.removeFromSuperview() } 174 | childTitleLabels.removeAll() 175 | for (index, item) in viewControllers.enumerated() { 176 | let child = item as! IndicatorInfoProvider // swiftlint:disable:this force_cast 177 | let indicatorInfo = child.indicatorInfo(for: self) 178 | let navTitleLabel: UILabel = { 179 | let label = UILabel() 180 | label.text = indicatorInfo.title 181 | label.font = UIApplication.shared.statusBarOrientation.isPortrait ? settings.style.portraitTitleFont : settings.style.landscapeTitleFont 182 | label.textColor = settings.style.titleColor 183 | label.alpha = 0 184 | return label 185 | }() 186 | navTitleLabel.alpha = currentIndex == index ? 1 : 0 187 | navTitleLabel.textColor = settings.style.titleColor 188 | titleScrollView.addSubview(navTitleLabel) 189 | childTitleLabels.append(navTitleLabel) 190 | } 191 | } 192 | 193 | private func setNavigationViewItemsPosition(updateAlpha: Bool) { 194 | guard let distance = distanceValue else { return } 195 | let isPortrait = UIApplication.shared.statusBarOrientation.isPortrait 196 | let navBarHeight: CGFloat = navigationController!.navigationBar.frame.size.height 197 | for (index, label) in childTitleLabels.enumerated() { 198 | if updateAlpha { 199 | label.alpha = currentIndex == index ? 1 : 0 200 | } 201 | label.font = isPortrait ? settings.style.portraitTitleFont : settings.style.landscapeTitleFont 202 | let viewSize = label.intrinsicContentSize 203 | let originX = distance - viewSize.width/2 + CGFloat(index) * distance 204 | let originY = (CGFloat(navBarHeight) - viewSize.height) / 2 205 | label.frame = CGRect(x: originX, y: originY - 2, width: viewSize.width, height: viewSize.height) 206 | label.tag = index 207 | } 208 | 209 | let xOffset = distance * CGFloat(currentIndex) 210 | titleScrollView.contentOffset = CGPoint(x: xOffset, y: 0) 211 | 212 | pageControl.numberOfPages = childTitleLabels.count 213 | pageControl.currentPage = currentIndex 214 | let viewSize = pageControl.sizeForNumber(ofPages: childTitleLabels.count) 215 | let originX = distance - viewSize.width / 2 216 | pageControl.frame = CGRect(x: originX, y: navBarHeight - 10, width: viewSize.width, height: viewSize.height) 217 | } 218 | 219 | private func setAlphaWith(offset: CGFloat, andDistance distance: CGFloat) { 220 | for (index, label) in childTitleLabels.enumerated() { 221 | label.alpha = { 222 | if offset < distance * CGFloat(index) { 223 | return (offset - distance * CGFloat(index - 1)) / distance 224 | } else { 225 | return 1 - ((offset - distance * CGFloat(index)) / distance) 226 | } 227 | }() 228 | } 229 | } 230 | 231 | private var distanceValue: CGFloat? { 232 | return navigationController.map { $0.navigationBar.convert($0.navigationBar.center, to: titleView) }?.x 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ParallaxHeader 2 | A sample iOS implementation [XLPagerTabStrip](https://github.com/xmartlabs/XLPagerTabStrip) with header view 3 | 4 | ## Notes 5 | References to [this article](https://sexyswift.wordpress.com/2016/08/25/swift-parallax-scrolling-with-header-sticking-at-top-nested-uiscrollview/) 6 | or you can checkout [here](https://github.com/bibekdari/DParallexScrolling/blob/master/Parallex%20Scrolling/ViewController.swift) 7 | 8 | ## Video Walkthrough 9 | 10 | Here's a walkthrough of implemented user stories: 11 | 12 | ![walkthrough.gif](walkthrough.gif) 13 | 14 | GIF created with [LiceCap](http://www.cockos.com/licecap/). 15 | 16 | ## License 17 | 18 | Copyright [2018] [Tri Ngo Minh] 19 | 20 | Licensed under the Apache License, Version 2.0 (the "License"); 21 | you may not use this file except in compliance with the License. 22 | You may obtain a copy of the License at 23 | 24 | http://www.apache.org/licenses/LICENSE-2.0 25 | 26 | Unless required by applicable law or agreed to in writing, software 27 | distributed under the License is distributed on an "AS IS" BASIS, 28 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 29 | See the License for the specific language governing permissions and 30 | limitations under the License. -------------------------------------------------------------------------------- /walkthrough.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngominhtrint/ParallaxHeader/ad2d06ce4c25488110eccf79304515ee3a00435e/walkthrough.gif --------------------------------------------------------------------------------