diff --git a/Segmentio/Source/Segmentio.swift b/Segmentio/Source/Segmentio.swift index b4797b9..f7213ef 100644 --- a/Segmentio/Source/Segmentio.swift +++ b/Segmentio/Source/Segmentio.swift @@ -334,12 +334,8 @@ open class Segmentio: UIView { if let indicatorLayer = indicatorLayer, let options = segmentioOptions.indicatorOptions { let item = itemInSuperview(ratio: options.ratio) let context = contextForItem(item) - - let points = Points( - context: context, - item: item, - pointY: indicatorPointY() - ) + + let points = Points(context: context, item: item, atIndex: selectedSegmentioIndex, allItems: segmentioItems, pointY: indicatorPointY(), position: segmentioOptions.segmentPosition) moveShapeLayer( indicatorLayer, @@ -352,12 +348,8 @@ open class Segmentio: UIView { if let selectedLayer = selectedLayer { let item = itemInSuperview() let context = contextForItem(item) - - let points = Points( - context: context, - item: item, - pointY: bounds.midY - ) + + let points = Points(context: context, item: item, atIndex: selectedSegmentioIndex, allItems: segmentioItems, pointY: bounds.midY, position: segmentioOptions.segmentPosition) moveShapeLayer( selectedLayer, @@ -378,7 +370,7 @@ open class Segmentio: UIView { let item = itemInSuperview() let context = contextForItem(item) - if context.isLastOrPrelastVisibleCell == true { + if context.isLastOrPrelastVisibleCell == true && selectedSegmentioIndex != -1 { let newIndex = selectedSegmentioIndex + (context.isLastCell ? 0 : 1) let newIndexPath = IndexPath(item: newIndex, section: numberOfSections - 1) segmentioCollectionView?.scrollToItem( @@ -465,13 +457,24 @@ open class Segmentio: UIView { if let collectionView = segmentioCollectionView { collectionViewWidth = collectionView.frame.width - let maxVisibleItems = segmentioOptions.maxVisibleItems > segmentioItems.count ? CGFloat(segmentioItems.count) : CGFloat(segmentioOptions.maxVisibleItems) - cellWidth = floor(collectionViewWidth / maxVisibleItems) - + cellWidth = segmentWidth(for: IndexPath(row: selectedSegmentioIndex, section: 0)) + var x: CGFloat = 0 + switch segmentioOptions.segmentPosition { + case .fixed: + x = floor(CGFloat(selectedSegmentioIndex) * cellWidth - collectionView.contentOffset.x) + case .dynamic: + guard selectedSegmentioIndex != -1 else { + break + } + for i in 0.. CGFloat { + var width: CGFloat = 0 + if let collectionView = segmentioCollectionView { + let collectionViewWidth = collectionView.frame.width + switch segmentioOptions.segmentPosition { + case .fixed(let maxVisibleItems): + let maxItems = maxVisibleItems > segmentioItems.count ? CGFloat(segmentioItems.count) : CGFloat(maxVisibleItems) + width = maxItems == 0 ? 0 : floor(collectionViewWidth / CGFloat(maxItems)) + case .dynamic: + if segmentioItems.count == 0 { + break + } + var dynamicWidth: CGFloat = 0 + for item in segmentioItems { + dynamicWidth += item.intrinsicWidth + } + width = dynamicWidth > collectionViewWidth ? segmentioItems[indexPath.row].intrinsicWidth : segmentioItems[indexPath.row].intrinsicWidth + ((collectionViewWidth - dynamicWidth) / CGFloat(segmentioItems.count)) + } + } + return width + } // MARK: - Indicator point Y @@ -526,6 +553,10 @@ open class Segmentio: UIView { // MARK: - UICollectionViewDataSource extension Segmentio: UICollectionViewDataSource { + + public func numberOfSections(in collectionView: UICollectionView) -> Int { + return 1 + } public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return segmentioItems.count @@ -569,8 +600,7 @@ extension Segmentio: UICollectionViewDelegate { extension Segmentio: UICollectionViewDelegateFlowLayout { public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { - let maxVisibleItems = segmentioOptions.maxVisibleItems > segmentioItems.count ? CGFloat(segmentioItems.count) : CGFloat(segmentioOptions.maxVisibleItems) - return CGSize( width: floor(collectionView.frame.width / maxVisibleItems), height: collectionView.frame.height) + return CGSize(width: segmentWidth(for: indexPath), height: collectionView.frame.height) } } @@ -583,7 +613,7 @@ extension Segmentio: UIScrollViewDelegate { if isPerformingScrollAnimation { return } - + if let options = segmentioOptions.indicatorOptions, let indicatorLayer = indicatorLayer { let item = itemInSuperview(ratio: options.ratio) moveShapeLayer( @@ -609,36 +639,71 @@ extension Segmentio: UIScrollViewDelegate { extension Segmentio.Points { - init(context: Segmentio.Context, item: Segmentio.ItemInSuperview, pointY: CGFloat) { + init(context: Segmentio.Context, item: Segmentio.ItemInSuperview, atIndex index: Int, allItems: [SegmentioItem], pointY: CGFloat, position: SegmentioPosition) { let cellWidth = item.cellFrameInSuperview.width var startX = item.startX var endX = item.endX - - if context.isFirstCell == false && context.isLastCell == false { - if context.isLastOrPrelastVisibleCell == true { - let updatedStartX = item.collectionViewWidth - (cellWidth * 2) + ((cellWidth - item.shapeLayerWidth) / 2) - startX = updatedStartX - let updatedEndX = updatedStartX + item.shapeLayerWidth - endX = updatedEndX + + switch position { + case .fixed(_): + if context.isFirstCell == false && context.isLastCell == false { + if context.isLastOrPrelastVisibleCell == true { + let updatedStartX = item.collectionViewWidth - (cellWidth * 2) + ((cellWidth - item.shapeLayerWidth) / 2) + startX = updatedStartX + let updatedEndX = updatedStartX + item.shapeLayerWidth + endX = updatedEndX + } + + if context.isFirstOrSecondVisibleCell == true { + let updatedEndX = (cellWidth * 2) - ((cellWidth - item.shapeLayerWidth) / 2) + endX = updatedEndX + let updatedStartX = updatedEndX - item.shapeLayerWidth + startX = updatedStartX + } + + if context.isFirstCell == true { + startX = (cellWidth - item.shapeLayerWidth) / 2 + endX = startX + item.shapeLayerWidth + } + + if context.isLastCell == true { + startX = item.collectionViewWidth - cellWidth + (cellWidth - item.shapeLayerWidth) / 2 + endX = startX + item.shapeLayerWidth + } } - - if context.isFirstOrSecondVisibleCell == true { - let updatedEndX = (cellWidth * 2) - ((cellWidth - item.shapeLayerWidth) / 2) - endX = updatedEndX - let updatedStartX = updatedEndX - item.shapeLayerWidth - startX = updatedStartX + case .dynamic: + // If the collection content view is not completely visible... + // We have to calculate the final position of the item + var dynamicWidth: CGFloat = 0 + for item in allItems { + dynamicWidth += item.intrinsicWidth + } + if item.collectionViewWidth < dynamicWidth { + startX = 0 + endX = 0 + var spaceBefore: CGFloat = 0 + var spaceAfter: CGFloat = 0 + var i = 0 + for item in allItems { + if i < index { + spaceBefore += item.intrinsicWidth + } else if i > index { + spaceAfter += item.intrinsicWidth + } + i += 1 + } + // Cell will try to position itself in the middle, unless it can't because + // the collection view has reached the beginning or end + if spaceBefore < (item.collectionViewWidth - cellWidth) / 2 { + startX = spaceBefore + } else if spaceAfter < (item.collectionViewWidth - cellWidth) / 2 { + startX = item.collectionViewWidth - spaceAfter - item.cellFrameInSuperview.width + } else { + startX = (item.collectionViewWidth / 2) - (cellWidth / 2 ) + } + endX = startX + cellWidth } - } - - if context.isFirstCell == true { - startX = (cellWidth - item.shapeLayerWidth) / 2 - endX = startX + item.shapeLayerWidth - } - - if context.isLastCell == true { - startX = item.collectionViewWidth - cellWidth + (cellWidth - item.shapeLayerWidth) / 2 - endX = startX + item.shapeLayerWidth } startPoint = CGPoint(x: startX, y: pointY) diff --git a/Segmentio/Source/SegmentioOptions.swift b/Segmentio/Source/SegmentioOptions.swift index 9c6f641..66bf964 100644 --- a/Segmentio/Source/SegmentioOptions.swift +++ b/Segmentio/Source/SegmentioOptions.swift @@ -16,6 +16,12 @@ public struct SegmentioItem { public var image: UIImage? public var badgeCount: Int? public var badgeColor: UIColor? + public var intrinsicWidth: CGFloat { + let label = UILabel() + label.text = self.title + label.sizeToFit() + return label.intrinsicContentSize.width + } public init(title: String?, image: UIImage?) { self.title = title @@ -116,6 +122,13 @@ public struct SegmentioIndicatorOptions { } +// MARK: - Position + +public enum SegmentioPosition { + case dynamic + case fixed(maxVisibleItems: Int) +} + // MARK: - Control options public enum SegmentioStyle: String { @@ -155,7 +168,7 @@ public typealias SegmentioStates = (defaultState: SegmentioState, selectedState: public struct SegmentioOptions { var backgroundColor: UIColor - var maxVisibleItems: Int + var segmentPosition: SegmentioPosition var scrollEnabled: Bool var horizontalSeparatorOptions: SegmentioHorizontalSeparatorOptions? var verticalSeparatorOptions: SegmentioVerticalSeparatorOptions? @@ -168,7 +181,7 @@ public struct SegmentioOptions { public init() { self.backgroundColor = .lightGray - self.maxVisibleItems = 4 + self.segmentPosition = .fixed(maxVisibleItems: 4) self.scrollEnabled = true self.horizontalSeparatorOptions = SegmentioHorizontalSeparatorOptions() @@ -188,9 +201,9 @@ public struct SegmentioOptions { self.animationDuration = 0.1 } - public init(backgroundColor: UIColor, maxVisibleItems: Int, scrollEnabled: Bool, indicatorOptions: SegmentioIndicatorOptions?, horizontalSeparatorOptions: SegmentioHorizontalSeparatorOptions?, verticalSeparatorOptions: SegmentioVerticalSeparatorOptions?, imageContentMode: UIViewContentMode, labelTextAlignment: NSTextAlignment, labelTextNumberOfLines: Int, segmentStates: SegmentioStates, animationDuration: CFTimeInterval) { + public init(backgroundColor: UIColor, segmentPosition: SegmentioPosition, scrollEnabled: Bool, indicatorOptions: SegmentioIndicatorOptions?, horizontalSeparatorOptions: SegmentioHorizontalSeparatorOptions?, verticalSeparatorOptions: SegmentioVerticalSeparatorOptions?, imageContentMode: UIViewContentMode, labelTextAlignment: NSTextAlignment, labelTextNumberOfLines: Int, segmentStates: SegmentioStates, animationDuration: CFTimeInterval) { self.backgroundColor = backgroundColor - self.maxVisibleItems = maxVisibleItems + self.segmentPosition = segmentPosition self.scrollEnabled = scrollEnabled self.indicatorOptions = indicatorOptions self.horizontalSeparatorOptions = horizontalSeparatorOptions