Commit aeef84bd authored by Carola Nitz's avatar Carola Nitz

MediaSubcategoryViewController: adding swipable viewcontrollers for the Video...

MediaSubcategoryViewController: adding swipable viewcontrollers for the Video and Audio subcategories
parent a89d9f9b
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14092" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14081.1"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="zg4-fX-zUF">
<rect key="frame" x="0.0" y="0.0" width="80" height="40"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="80" height="40"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="s7f-rk-sQl">
<rect key="frame" x="22" y="12" width="37.5" height="17"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="14"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="tFG-yJ-6Th">
<rect key="frame" x="22.5" y="2.5" width="35" height="35"/>
<constraints>
<constraint firstAttribute="width" constant="35" id="VRw-5F-5WY"/>
<constraint firstAttribute="height" constant="35" id="ojk-Ug-Lgh"/>
</constraints>
</imageView>
</subviews>
</view>
<color key="backgroundColor" red="0.027450980390000001" green="0.72549019609999998" blue="0.60784313729999995" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="tFG-yJ-6Th" firstAttribute="centerY" secondItem="zg4-fX-zUF" secondAttribute="centerY" id="5so-ZP-gRs"/>
<constraint firstItem="tFG-yJ-6Th" firstAttribute="centerX" secondItem="zg4-fX-zUF" secondAttribute="centerX" id="9em-NR-hoa"/>
<constraint firstItem="s7f-rk-sQl" firstAttribute="centerY" secondItem="zg4-fX-zUF" secondAttribute="centerY" id="ZeP-6I-AXE"/>
<constraint firstItem="s7f-rk-sQl" firstAttribute="centerX" secondItem="zg4-fX-zUF" secondAttribute="centerX" id="c15-bZ-hPG"/>
</constraints>
<connections>
<outlet property="imageView" destination="tFG-yJ-6Th" id="Odb-dR-tf4"/>
<outlet property="label" destination="s7f-rk-sQl" id="4gU-tb-BMB"/>
</connections>
<point key="canvasLocation" x="307" y="541"/>
</collectionViewCell>
</objects>
</document>
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14092" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14081.1"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="2CI-9N-dP3" userLabel="Icon Label Cell" customClass="IconLabelCell" customModule="VLC_iOS" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="74" height="70"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="74" height="70"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Tkn-VR-o25">
<rect key="frame" x="20" y="10" width="35" height="35"/>
<constraints>
<constraint firstAttribute="width" constant="35" id="EUF-rI-9Y8"/>
<constraint firstAttribute="height" constant="35" id="RoA-LT-AHV"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rwk-fy-4KB">
<rect key="frame" x="0.0" y="45" width="74" height="20"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="kWi-HY-P6M"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="10"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</view>
<constraints>
<constraint firstItem="rwk-fy-4KB" firstAttribute="top" secondItem="Tkn-VR-o25" secondAttribute="bottom" id="A8G-Sk-d4C"/>
<constraint firstItem="Tkn-VR-o25" firstAttribute="top" secondItem="2CI-9N-dP3" secondAttribute="top" constant="10" id="DaW-j3-w2g"/>
<constraint firstItem="rwk-fy-4KB" firstAttribute="leading" secondItem="2CI-9N-dP3" secondAttribute="leading" id="HFm-qh-fCk"/>
<constraint firstAttribute="bottom" secondItem="rwk-fy-4KB" secondAttribute="bottom" constant="5" id="Q0g-pb-bbf"/>
<constraint firstAttribute="trailing" secondItem="rwk-fy-4KB" secondAttribute="trailing" id="YuE-x9-9EY"/>
<constraint firstItem="Tkn-VR-o25" firstAttribute="centerX" secondItem="2CI-9N-dP3" secondAttribute="centerX" id="wav-eA-a2N"/>
</constraints>
<size key="customSize" width="74" height="50"/>
<connections>
<outlet property="iconImage" destination="Tkn-VR-o25" id="7DO-mm-BJX"/>
<outlet property="iconLabel" destination="rwk-fy-4KB" id="76s-nG-weE"/>
</connections>
<point key="canvasLocation" x="317" y="376"/>
</collectionViewCell>
</objects>
</document>
......@@ -305,3 +305,16 @@
"SORT" = "Sort";
"SORT_BY" = "Sort by";
"VIDEO" = "Video";
/* New strings */
"FOLDER_EMPTY" = "FOLDER_EMPTY";
/* New strings */
"albums" = "albums";
"artists" = "artists";
"genres" = "genres";
"movies" = "movies";
"playlists" = "playlists";
"songs" = "songs";
"videos" = "videos";
"episodes" = "episodes";
......@@ -39,7 +39,9 @@ public class MediaDataSourceAndDelegate: NSObject, UICollectionViewDataSource, U
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let playlistCell = collectionView.dequeueReusableCell(withReuseIdentifier: VLCPlaylistCollectionViewCell.cellIdentifier(), for: indexPath) as? VLCPlaylistCollectionViewCell {
playlistCell.mediaObject = services.mediaDataSource.object(at: indexPath.row, subcategory: mediaType.subcategory)
if let mediaObject = services.mediaDataSource.object(at: indexPath.row, subcategory: mediaType.subcategory) as? NSManagedObject {
playlistCell.mediaObject = mediaObject
}
return playlistCell
}
return UICollectionViewCell()
......
//
// BaseButtonBarPagerTabStripViewController.swift
// VLC-iOS
//
// Created by Carola Nitz on 5/3/18.
// Copyright © 2018 VideoLAN. All rights reserved.
//
import Foundation
class IconLabelCell: UICollectionViewCell {
@IBOutlet weak var iconImage: UIImageView!
@IBOutlet weak var iconLabel: UILabel!
}
public enum SwipeDirection {
case left
case right
case none
}
public struct ButtonBarPagerTabStripSettings {
public struct Style {
public var buttonBarBackgroundColor: UIColor?
public var buttonBarMinimumInteritemSpacing: CGFloat?
public var buttonBarMinimumLineSpacing: CGFloat?
public var selectedBarBackgroundColor = UIColor.black
public var selectedBarHeight: CGFloat = 5
public var selectedBarVerticalAlignment: SelectedBarVerticalAlignment = .bottom
public var buttonBarItemFont = UIFont.systemFont(ofSize: 18)
public var buttonBarItemLeftRightMargin: CGFloat = 8
public var buttonBarItemTitleColor: UIColor?
public var buttonBarItemsShouldFillAvailableWidth = true
// only used if button bar is created programaticaly and not using storyboards or nib files
public var buttonBarHeight: CGFloat?
}
public var style = Style()
}
public struct IndicatorInfo {
public var title: String?
public var image: UIImage?
public var accessibilityLabel: String?
public init(title: String?, image: UIImage?) {
self.title = title
self.accessibilityLabel = title
self.image = image
}
}
public enum ButtonBarItemSpec<CellType: UICollectionViewCell> {
case nibFile(nibName: String, bundle: Bundle?, width:((IndicatorInfo)-> CGFloat))
case cellClass(width:((IndicatorInfo)-> CGFloat))
public var weight: ((IndicatorInfo) -> CGFloat) {
switch self {
case .cellClass(let widthCallback):
return widthCallback
case .nibFile(_, _, let widthCallback):
return widthCallback
}
}
}
public enum PagerScroll {
case no
case yes
case scrollOnlyIfOutOfScreen
}
public enum SelectedBarAlignment {
case left
case center
case right
case progressive
}
public enum SelectedBarVerticalAlignment {
case top
case middle
case bottom
}
open class ButtonBarView: UICollectionView {
open lazy var selectedBar: UIView = { [unowned self] in
let bar = UIView(frame: CGRect(x: 0, y: self.frame.size.height - CGFloat(self.selectedBarHeight), width: 0, height: CGFloat(self.selectedBarHeight)))
bar.layer.zPosition = 9999
return bar
}()
internal var selectedBarHeight: CGFloat = 4 {
didSet {
updateSelectedBarYPosition()
}
}
var selectedBarVerticalAlignment: SelectedBarVerticalAlignment = .bottom
var selectedBarAlignment: SelectedBarAlignment = .center
var selectedIndex = 0
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
addSubview(selectedBar)
}
public override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: layout)
addSubview(selectedBar)
}
open func moveTo(index: Int, animated: Bool, swipeDirection: SwipeDirection, pagerScroll: PagerScroll) {
selectedIndex = index
updateSelectedBarPosition(animated, swipeDirection: swipeDirection, pagerScroll: pagerScroll)
}
open func move(fromIndex: Int, toIndex: Int, progressPercentage: CGFloat, pagerScroll: PagerScroll) {
selectedIndex = progressPercentage > 0.5 ? toIndex : fromIndex
let fromFrame = layoutAttributesForItem(at: IndexPath(item: fromIndex, section: 0))!.frame
let numberOfItems = dataSource!.collectionView(self, numberOfItemsInSection: 0)
var toFrame: CGRect
if toIndex < 0 || toIndex > numberOfItems - 1 {
if toIndex < 0 {
let cellAtts = layoutAttributesForItem(at: IndexPath(item: 0, section: 0))
toFrame = cellAtts!.frame.offsetBy(dx: -cellAtts!.frame.size.width, dy: 0)
} else {
let cellAtts = layoutAttributesForItem(at: IndexPath(item: (numberOfItems - 1), section: 0))
toFrame = cellAtts!.frame.offsetBy(dx: cellAtts!.frame.size.width, dy: 0)
}
} else {
toFrame = layoutAttributesForItem(at: IndexPath(item: toIndex, section: 0))!.frame
}
var targetFrame = fromFrame
targetFrame.size.height = selectedBar.frame.size.height
targetFrame.size.width += (toFrame.size.width - fromFrame.size.width) * progressPercentage
targetFrame.origin.x += (toFrame.origin.x - fromFrame.origin.x) * progressPercentage
selectedBar.frame = CGRect(x: targetFrame.origin.x, y: selectedBar.frame.origin.y, width: targetFrame.size.width, height: selectedBar.frame.size.height)
var targetContentOffset: CGFloat = 0.0
if contentSize.width > frame.size.width {
let toContentOffset = contentOffsetForCell(withFrame: toFrame, andIndex: toIndex)
let fromContentOffset = contentOffsetForCell(withFrame: fromFrame, andIndex: fromIndex)
targetContentOffset = fromContentOffset + ((toContentOffset - fromContentOffset) * progressPercentage)
}
setContentOffset(CGPoint(x: targetContentOffset, y: 0), animated: false)
}
open func updateSelectedBarPosition(_ animated: Bool, swipeDirection: SwipeDirection, pagerScroll: PagerScroll) {
var selectedBarFrame = selectedBar.frame
let selectedCellIndexPath = IndexPath(item: selectedIndex, section: 0)
let attributes = layoutAttributesForItem(at: selectedCellIndexPath)
let selectedCellFrame = attributes!.frame
updateContentOffset(animated: animated, pagerScroll: pagerScroll, toFrame: selectedCellFrame, toIndex: (selectedCellIndexPath as NSIndexPath).row)
selectedBarFrame.size.width = selectedCellFrame.size.width
selectedBarFrame.origin.x = selectedCellFrame.origin.x
if animated {
UIView.animate(withDuration: 0.3, animations: { [weak self] in
self?.selectedBar.frame = selectedBarFrame
})
} else {
selectedBar.frame = selectedBarFrame
}
}
// MARK: - Helpers
private func updateContentOffset(animated: Bool, pagerScroll: PagerScroll, toFrame: CGRect, toIndex: Int) {
guard pagerScroll != .no || (pagerScroll != .scrollOnlyIfOutOfScreen && (toFrame.origin.x < contentOffset.x || toFrame.origin.x >= (contentOffset.x + frame.size.width - contentInset.left))) else { return }
let targetContentOffset = contentSize.width > frame.size.width ? contentOffsetForCell(withFrame: toFrame, andIndex: toIndex) : 0
setContentOffset(CGPoint(x: targetContentOffset, y: 0), animated: animated)
}
private func contentOffsetForCell(withFrame cellFrame: CGRect, andIndex index: Int) -> CGFloat {
let sectionInset = (collectionViewLayout as! UICollectionViewFlowLayout).sectionInset // swiftlint:disable:this force_cast
var alignmentOffset: CGFloat = 0.0
switch selectedBarAlignment {
case .left:
alignmentOffset = sectionInset.left
case .right:
alignmentOffset = frame.size.width - sectionInset.right - cellFrame.size.width
case .center:
alignmentOffset = (frame.size.width - cellFrame.size.width) * 0.5
case .progressive:
let cellHalfWidth = cellFrame.size.width * 0.5
let leftAlignmentOffset = sectionInset.left + cellHalfWidth
let rightAlignmentOffset = frame.size.width - sectionInset.right - cellHalfWidth
let numberOfItems = dataSource!.collectionView(self, numberOfItemsInSection: 0)
let progress = index / (numberOfItems - 1)
alignmentOffset = leftAlignmentOffset + (rightAlignmentOffset - leftAlignmentOffset) * CGFloat(progress) - cellHalfWidth
}
var contentOffset = cellFrame.origin.x - alignmentOffset
contentOffset = max(0, contentOffset)
contentOffset = min(contentSize.width - frame.size.width, contentOffset)
return contentOffset
}
private func updateSelectedBarYPosition() {
var selectedBarFrame = selectedBar.frame
switch selectedBarVerticalAlignment {
case .top:
selectedBarFrame.origin.y = 0
case .middle:
selectedBarFrame.origin.y = (frame.size.height - selectedBarHeight) / 2
case .bottom:
selectedBarFrame.origin.y = frame.size.height - selectedBarHeight
}
selectedBarFrame.size.height = selectedBarHeight
selectedBar.frame = selectedBarFrame
}
override open func layoutSubviews() {
super.layoutSubviews()
updateSelectedBarYPosition()
}
}
open class BaseButtonBarPagerTabStripViewController<ButtonBarCellType: UICollectionViewCell>: PagerTabStripViewController, PagerTabStripDataSource, PagerTabStripIsProgressiveDelegate, UICollectionViewDelegate, UICollectionViewDataSource {
public var settings = ButtonBarPagerTabStripSettings()
public var buttonBarItemSpec: ButtonBarItemSpec<ButtonBarCellType>!
public var changeCurrentIndex: ((_ oldCell: ButtonBarCellType?, _ newCell: ButtonBarCellType?, _ animated: Bool) -> Void)?
public var changeCurrentIndexProgressive: ((_ oldCell: ButtonBarCellType?, _ newCell: ButtonBarCellType?, _ progressPercentage: CGFloat, _ changeCurrentIndex: Bool, _ animated: Bool) -> Void)?
@IBOutlet public weak var buttonBarView: ButtonBarView!
lazy private var cachedCellWidths: [CGFloat]? = { [unowned self] in
return self.calculateWidths()
}()
public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
delegate = self
datasource = self
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
delegate = self
datasource = self
}
open override func viewDidLoad() {
super.viewDidLoad()
let buttonBarViewAux = buttonBarView ?? {
let flowLayout = UICollectionViewFlowLayout()
flowLayout.scrollDirection = .horizontal
let buttonBarHeight = settings.style.buttonBarHeight ?? 44
let yPos: CGFloat;
if #available(iOS 11.0, *) {
yPos = navigationController!.navigationBar.safeAreaInsets.top + navigationController!.navigationBar.frame.height
} else {
yPos = navigationController!.navigationBar.frame.maxY
}
let buttonBar = ButtonBarView(frame: CGRect(x: 0, y: yPos, width: view.frame.size.width, height: buttonBarHeight), collectionViewLayout: flowLayout)
buttonBar.backgroundColor = .orange
buttonBar.selectedBar.backgroundColor = .black
buttonBar.autoresizingMask = .flexibleWidth
var newContainerViewFrame = containerView.frame
newContainerViewFrame.origin.y = buttonBar.frame.maxY
newContainerViewFrame.size.height = containerView.frame.size.height - (buttonBar.frame.maxY - containerView.frame.origin.y)
containerView.frame = newContainerViewFrame
return buttonBar
}()
buttonBarView = buttonBarViewAux
if buttonBarView.superview == nil {
view.addSubview(buttonBarView)
}
if buttonBarView.delegate == nil {
buttonBarView.delegate = self
}
if buttonBarView.dataSource == nil {
buttonBarView.dataSource = self
}
buttonBarView.scrollsToTop = false
let flowLayout = buttonBarView.collectionViewLayout as! UICollectionViewFlowLayout // swiftlint:disable:this force_cast
flowLayout.scrollDirection = .horizontal
flowLayout.minimumInteritemSpacing = settings.style.buttonBarMinimumInteritemSpacing ?? flowLayout.minimumInteritemSpacing
flowLayout.minimumLineSpacing = settings.style.buttonBarMinimumLineSpacing ?? flowLayout.minimumLineSpacing
buttonBarView.showsHorizontalScrollIndicator = false
buttonBarView.backgroundColor = settings.style.buttonBarBackgroundColor ?? buttonBarView.backgroundColor
buttonBarView.selectedBar.backgroundColor = settings.style.selectedBarBackgroundColor
buttonBarView.selectedBarHeight = settings.style.selectedBarHeight
// register button bar item cell
switch buttonBarItemSpec! {
case .nibFile(let nibName, let bundle, _):
buttonBarView.register(UINib(nibName: nibName, bundle: bundle), forCellWithReuseIdentifier:"Cell")
case .cellClass:
buttonBarView.register(ButtonBarCellType.self, forCellWithReuseIdentifier:"Cell")
}
}
open override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
buttonBarView.layoutIfNeeded()
isViewAppearing = true
}
open override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
isViewAppearing = false
}
open override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
guard isViewAppearing || isViewRotating else { return }
// Force the UICollectionViewFlowLayout to get laid out again with the new size if
// a) The view is appearing. This ensures that
// collectionView:layout:sizeForItemAtIndexPath: is called for a second time
// when the view is shown and when the view *frame(s)* are actually set
// (we need the view frame's to have been set to work out the size's and on the
// first call to collectionView:layout:sizeForItemAtIndexPath: the view frame(s)
// aren't set correctly)
// b) The view is rotating. This ensures that
// collectionView:layout:sizeForItemAtIndexPath: is called again and can use the views
// *new* frame so that the buttonBarView cell's actually get resized correctly
cachedCellWidths = calculateWidths()
buttonBarView.collectionViewLayout.invalidateLayout()
// When the view first appears or is rotated we also need to ensure that the barButtonView's
// selectedBar is resized and its contentOffset/scroll is set correctly (the selected
// tab/cell may end up either skewed or off screen after a rotation otherwise)
buttonBarView.moveTo(index: currentIndex, animated: false, swipeDirection: .none, pagerScroll: .scrollOnlyIfOutOfScreen)
buttonBarView.selectItem(at: IndexPath(item: currentIndex, section: 0), animated: false, scrollPosition: [])
}
// MARK: - View Rotation
open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
let yPos: CGFloat;
if #available(iOS 11.0, *) {
yPos = navigationController!.navigationBar.safeAreaInsets.top + navigationController!.navigationBar.frame.height
} else {
yPos = navigationController!.navigationBar.frame.maxY
}
if let buttonBarView = buttonBarView {
buttonBarView.frame = CGRect(x: 0, y: yPos, width: view.frame.size.width, height: buttonBarView.frame.height)
var newContainerViewFrame = containerView.frame
newContainerViewFrame.origin.y = buttonBarView.frame.maxY
newContainerViewFrame.size.height = containerView.frame.size.height - (buttonBarView.frame.maxY - containerView.frame.origin.y)
containerView.frame = newContainerViewFrame
}
}
// MARK: - Public Methods
open override func reloadPagerTabStripView() {
super.reloadPagerTabStripView()
guard isViewLoaded else { return }
buttonBarView.reloadData()
cachedCellWidths = calculateWidths()
buttonBarView.moveTo(index: currentIndex, animated: false, swipeDirection: .none, pagerScroll: .yes)
}
open func calculateStretchedCellWidths(_ minimumCellWidths: [CGFloat], suggestedStretchedCellWidth: CGFloat, previousNumberOfLargeCells: Int) -> CGFloat {
var numberOfLargeCells = 0
var totalWidthOfLargeCells: CGFloat = 0
for minimumCellWidthValue in minimumCellWidths where minimumCellWidthValue > suggestedStretchedCellWidth {
totalWidthOfLargeCells += minimumCellWidthValue
numberOfLargeCells += 1
}
guard numberOfLargeCells > previousNumberOfLargeCells else { return suggestedStretchedCellWidth }
let flowLayout = buttonBarView.collectionViewLayout as! UICollectionViewFlowLayout // swiftlint:disable:this force_cast
let collectionViewAvailiableWidth = buttonBarView.frame.size.width - flowLayout.sectionInset.left - flowLayout.sectionInset.right
let numberOfCells = minimumCellWidths.count
let cellSpacingTotal = CGFloat(numberOfCells - 1) * flowLayout.minimumLineSpacing
let numberOfSmallCells = numberOfCells - numberOfLargeCells
let newSuggestedStretchedCellWidth = (collectionViewAvailiableWidth - totalWidthOfLargeCells - cellSpacingTotal) / CGFloat(numberOfSmallCells)
return calculateStretchedCellWidths(minimumCellWidths, suggestedStretchedCellWidth: newSuggestedStretchedCellWidth, previousNumberOfLargeCells: numberOfLargeCells)
}
open func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int) {
guard shouldUpdateButtonBarView else { return }
buttonBarView.moveTo(index: toIndex, animated: true, swipeDirection: toIndex < fromIndex ? .right : .left, pagerScroll: .yes)
if let changeCurrentIndex = changeCurrentIndex {
let oldCell = buttonBarView.cellForItem(at: IndexPath(item: currentIndex != fromIndex ? fromIndex : toIndex, section: 0)) as? ButtonBarCellType
let newCell = buttonBarView.cellForItem(at: IndexPath(item: currentIndex, section: 0)) as? ButtonBarCellType
changeCurrentIndex(oldCell, newCell, true)
}
}
open func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int, withProgressPercentage progressPercentage: CGFloat, indexWasChanged: Bool) {
guard shouldUpdateButtonBarView else { return }
buttonBarView.move(fromIndex: fromIndex, toIndex: toIndex, progressPercentage: progressPercentage, pagerScroll: .yes)
if let changeCurrentIndexProgressive = changeCurrentIndexProgressive {
let oldCell = buttonBarView.cellForItem(at: IndexPath(item: currentIndex != fromIndex ? fromIndex : toIndex, section: 0)) as? ButtonBarCellType
let newCell = buttonBarView.cellForItem(at: IndexPath(item: currentIndex, section: 0)) as? ButtonBarCellType
changeCurrentIndexProgressive(oldCell, newCell, progressPercentage, indexWasChanged, true)
}
}
// MARK: - UICollectionViewDelegateFlowLayut
@objc open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize {
guard let cellWidthValue = cachedCellWidths?[indexPath.row] else {
fatalError("cachedCellWidths for \(indexPath.row) must not be nil")
}
return CGSize(width: cellWidthValue, height: collectionView.frame.size.height)
}
open func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard indexPath.item != currentIndex else { return }
buttonBarView.moveTo(index: indexPath.item, animated: true, swipeDirection: .none, pagerScroll: .yes)
shouldUpdateButtonBarView = false
let oldCell = buttonBarView.cellForItem(at: IndexPath(item: currentIndex, section: 0)) as? ButtonBarCellType
let newCell = buttonBarView.cellForItem(at: IndexPath(item: indexPath.item, section: 0)) as? ButtonBarCellType
if let changeCurrentIndexProgressive = changeCurrentIndexProgressive {
changeCurrentIndexProgressive(oldCell, newCell, 1, true, true)
}
moveToViewController(at: indexPath.item)
}
// MARK: - UICollectionViewDataSource
open func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return viewControllers.count
}
open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as? ButtonBarCellType else {
fatalError("UICollectionViewCell should be or extend from ButtonBarViewCell")
}
let childController = viewControllers[indexPath.item] as! IndicatorInfoProvider // swiftlint:disable:this force_cast
let indicatorInfo = childController.indicatorInfo(for: self)
configure(cell: cell, for: indicatorInfo)
if let changeCurrentIndexProgressive = changeCurrentIndexProgressive {
changeCurrentIndexProgressive(currentIndex == indexPath.item ? nil : cell, currentIndex == indexPath.item ? cell : nil, 1, true, false)
}
return cell
}
// MARK: - UIScrollViewDelegate
open override func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
super.scrollViewDidEndScrollingAnimation(scrollView)
guard scrollView == containerView else { return }
shouldUpdateButtonBarView = true
}
open func configure(cell: ButtonBarCellType, for indicatorInfo: IndicatorInfo) {
fatalError("You must override this method to set up ButtonBarView cell accordingly")
}
private func calculateWidths() -> [CGFloat] {
let flowLayout = buttonBarView.collectionViewLayout as! UICollectionViewFlowLayout // swiftlint:disable:this force_cast
let numberOfCells = viewControllers.count
var minimumCellWidths = [CGFloat]()
var collectionViewContentWidth: CGFloat = 0
for viewController in viewControllers {
let childController = viewController as! IndicatorInfoProvider // swiftlint:disable:this force_cast
let indicatorInfo = childController.indicatorInfo(for: self)
switch buttonBarItemSpec! {
case .cellClass(let widthCallback):
let width = widthCallback(indicatorInfo)
minimumCellWidths.append(width)
collectionViewContentWidth += width
case .nibFile(_, _, let widthCallback):
let width = widthCallback(indicatorInfo)
minimumCellWidths.append(width)
collectionViewContentWidth += width
}
}
let cellSpacingTotal = CGFloat(numberOfCells - 1) * flowLayout.minimumLineSpacing
collectionViewContentWidth += cellSpacingTotal
let collectionViewAvailableVisibleWidth = buttonBarView.frame.size.width - flowLayout.sectionInset.left - flowLayout.sectionInset.right
if !settings.style.buttonBarItemsShouldFillAvailableWidth || collectionViewAvailableVisibleWidth < collectionViewContentWidth {
return minimumCellWidths
} else {
let stretchedCellWidthIfAllEqual = (collectionViewAvailableVisibleWidth - cellSpacingTotal) / CGFloat(numberOfCells)
let generalMinimumCellWidth = calculateStretchedCellWidths(minimumCellWidths, suggestedStretchedCellWidth: stretchedCellWidthIfAllEqual, previousNumberOfLargeCells: 0)
var stretchedCellWidths = [CGFloat]()
for minimumCellWidthValue in minimumCellWidths {
let cellWidth = (minimumCellWidthValue > generalMinimumCellWidth) ? minimumCellWidthValue : generalMinimumCellWidth
stretchedCellWidths.append(cellWidth)
}
return stretchedCellWidths
}
}
private var shouldUpdateButtonBarView = true
}
// MARK: Protocols
public protocol IndicatorInfoProvider {
func indicatorInfo(for pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo
}
public protocol PagerTabStripDelegate: class {
func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int)
}