123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- /*****************************************************************************
- * ButtonBarView.Swift
- * VLC for iOS
- *****************************************************************************
- * Copyright (c) 2018 VideoLAN. All rights reserved.
- * $Id$
- *
- * Authors: Carola Nitz <nitz.carola # googlemail.com>
- *
- * Refer to the COPYING file of the official project for license.
- *****************************************************************************/
- import Foundation
- open class ButtonBarView: UICollectionView {
- open var selectedBar: UIView!
- open var separatorView: UIView!
- internal let selectedBarHeight: CGFloat = 4
- internal let separatorHeight: CGFloat = 1.5
- var selectedIndex = 0
- @available(*, unavailable, message: "use init(frame:)")
- required public init?(coder aDecoder: NSCoder) {
- fatalError()
- }
- public override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
- super.init(frame: frame, collectionViewLayout: layout)
- setup()
- NotificationCenter.default.addObserver(self, selector: #selector(updateTheme), name: .VLCThemeDidChangeNotification, object: nil)
- }
- func setup() {
- scrollsToTop = false
- showsHorizontalScrollIndicator = false
- separatorView = UIView(frame: CGRect(x: 0, y: self.frame.size.height - separatorHeight, width: self.frame.size.width, height: separatorHeight))
- addSubview(separatorView)
- selectedBar = UIView(frame: CGRect(x: 0, y: self.frame.size.height - CGFloat(self.selectedBarHeight), width: 0, height: CGFloat(self.selectedBarHeight)))
- addSubview(selectedBar)
- updateTheme()
- }
- @objc func updateTheme() {
- backgroundColor = PresentationTheme.current.colors.background
- selectedBar.backgroundColor = PresentationTheme.current.colors.orangeUI
- separatorView.backgroundColor = PresentationTheme.current.colors.mediaCategorySeparatorColor
- }
- open func moveTo(index: Int, animated: Bool, swipeDirection: SwipeDirection, pagerScroll: PagerScroll) {
- selectedIndex = index
- updateSubviewPositions(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 updateSubviewPositions(_ 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
- if let strongSelf = self {
- strongSelf.selectedBar.frame = selectedBarFrame
- strongSelf.separatorView.frame = CGRect(x: 0, y: strongSelf.frame.size.height - strongSelf.separatorHeight, width: strongSelf.frame.size.width, height: strongSelf.separatorHeight)
- }
- })
- } else {
- selectedBar.frame = selectedBarFrame
- separatorView.frame = CGRect(x: 0, y: frame.size.height - separatorHeight, width: frame.size.width, height: separatorHeight)
- }
- }
- // 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 alignmentOffset = (frame.size.width - cellFrame.size.width) * 0.5
- 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
- selectedBarFrame.origin.y = frame.size.height - selectedBarHeight
- selectedBarFrame.size.height = selectedBarHeight
- selectedBar.frame = selectedBarFrame
- }
- override open func layoutSubviews() {
- super.layoutSubviews()
- updateSelectedBarYPosition()
- }
- }
|