// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. import UIKit import SessionUIKit final class PagedScrollView: UIView, UIScrollViewDelegate { private static let autoScrollingTimeInterval: TimeInterval = 10 private var slides: [UIView] = [] private var slideSize: CGSize = .zero private var shouldAutoScroll: Bool = false private var timer: Timer? private lazy var contentWidth = stackView.set(.width, to: 0) private lazy var contentHeight = stackView.set(.height, to: 0) private var shouldArrowsShow: Bool = false { didSet { arrowLeft.isHidden = !shouldArrowsShow arrowRight.isHidden = !shouldArrowsShow } } // MARK: - UI Components private lazy var scrollView: UIScrollView = { let result = UIScrollView(frame: .zero) result.isPagingEnabled = true result.showsVerticalScrollIndicator = false result.showsHorizontalScrollIndicator = false result.delegate = self return result }() private lazy var stackView: UIStackView = { let result = UIStackView() result.axis = .horizontal return result }() private lazy var pageControl: UIPageControl = { let result = UIPageControl(frame: .zero) result.themeCurrentPageIndicatorTintColor = .textPrimary result.themePageIndicatorTintColor = .textSecondary result.themeTintColor = .textPrimary result.set(.height, to: 5) result.transform = CGAffineTransform(scaleX: 0.5, y: 0.5) return result }() private lazy var arrowLeft: UIImageView = { let result = UIImageView(image: UIImage(systemName: "chevron.left")?.withRenderingMode(.alwaysTemplate)) result.themeTintColor = .textPrimary result.set(.height, to: 10) result.set(.width, to: 5) return result }() private lazy var arrowRight: UIImageView = { let result = UIImageView(image: UIImage(systemName: "chevron.right")?.withRenderingMode(.alwaysTemplate)) result.themeTintColor = .textPrimary result.set(.height, to: 10) result.set(.width, to: 5) return result }() // MARK: - Initialization init(slides: [UIView] = [], slideSize: CGSize = .zero, shouldAutoScroll: Bool = false) { super.init(frame: .zero) setUpViewHierarchy() self.update(with: slides, slideSize: slideSize, shouldAutoScroll: shouldAutoScroll) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: - Content public func update(with slides: [UIView] = [], slideSize: CGSize = .zero, shouldAutoScroll: Bool = false) { self.slides = slides self.slideSize = slideSize self.shouldAutoScroll = shouldAutoScroll self.shouldArrowsShow = slides.count > 1 pageControl.numberOfPages = slides.count pageControl.currentPage = 0 pageControl.isHidden = (slides.count == 1) let contentSize = CGSize(width: slideSize.width * CGFloat(slides.count), height: slideSize.height) scrollView.contentSize = contentSize contentWidth.constant = contentSize.width contentHeight.constant = contentSize.height stackView.arrangedSubviews.forEach { $0.removeFromSuperview() } self.slides.forEach { let wrapper: UIView = UIView() wrapper.set(.width, to: slideSize.width) wrapper.set(.height, to: slideSize.height) wrapper.addSubview($0) $0.center(in: wrapper) stackView.addArrangedSubview(wrapper) } if self.shouldAutoScroll { startScrolling() } } private func setUpViewHierarchy() { addSubview(scrollView) scrollView.pin(to: self) addSubview(arrowLeft) arrowLeft.pin(.left, to: .left, of: self) arrowLeft.center(.vertical, in: self, withInset: -2) addSubview(arrowRight) arrowRight.pin(.right, to: .right, of: self) arrowRight.center(.vertical, in: self, withInset: -2) addSubview(pageControl) pageControl.center(.horizontal, in: self) pageControl.pin(.bottom, to: .bottom, of: self, withInset: -1) scrollView.addSubview(stackView) } private func startScrolling() { timer?.invalidate() timer = Timer.scheduledTimerOnMainThread(withTimeInterval: Self.autoScrollingTimeInterval, repeats: true) { _ in guard self.slides.count != 0 else { return } let targetPage = (self.pageControl.currentPage + 1) % self.slides.count self.scrollView.scrollRectToVisible( CGRect( origin: CGPoint( x: Int(self.slideSize.width) * targetPage, y: 0 ), size: self.slideSize ), animated: true ) } } private func stopScrolling() { timer?.invalidate() timer = nil } func scrollViewDidScroll(_ scrollView: UIScrollView) { let pageIndex = round(scrollView.contentOffset.x/slideSize.width) pageControl.currentPage = Int(pageIndex) } }