)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/README.md:
--------------------------------------------------------------------------------
1 | # XLPagerTabStrip
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------