mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			240 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Swift
		
	
			
		
		
	
	
			240 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Swift
		
	
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
 | 
						|
 | 
						|
import UIKit
 | 
						|
import SessionUIKit
 | 
						|
 | 
						|
final class SessionLabelCarouselView: UIView, UIScrollViewDelegate {
 | 
						|
    private static let autoScrollingTimeInterval: TimeInterval = 10
 | 
						|
    private var labelInfos: [LabelInfo] = []
 | 
						|
    private var labelSize: 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 shouldScroll: Bool = false {
 | 
						|
        didSet {
 | 
						|
            arrowLeft.isHidden = !shouldScroll
 | 
						|
            arrowRight.isHidden = !shouldScroll
 | 
						|
            pageControl.isHidden = !shouldScroll
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    public struct LabelInfo {
 | 
						|
        let attributedText: NSAttributedString
 | 
						|
        let accessibility: Accessibility?
 | 
						|
        let type: LabelType
 | 
						|
    }
 | 
						|
    
 | 
						|
    // MARK: - Types
 | 
						|
    
 | 
						|
    public enum LabelType {
 | 
						|
        case notificationSettings
 | 
						|
        case userCount
 | 
						|
        case disappearingMessageSetting
 | 
						|
    }
 | 
						|
    
 | 
						|
    public var currentLabelType: LabelType? {
 | 
						|
        let index = pageControl.currentPage + (shouldScroll ? 1 : 0)
 | 
						|
        return self.labelInfos[safe: index]?.type
 | 
						|
    }
 | 
						|
    
 | 
						|
    // MARK: - UI Components
 | 
						|
    
 | 
						|
    public 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.currentPage = 0
 | 
						|
        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(labelInfos: [LabelInfo] = [], labelSize: CGSize = .zero, shouldAutoScroll: Bool = false) {
 | 
						|
        super.init(frame: .zero)
 | 
						|
        setUpViewHierarchy()
 | 
						|
        self.update(with: labelInfos, labelSize: labelSize, shouldAutoScroll: shouldAutoScroll)
 | 
						|
    }
 | 
						|
    
 | 
						|
    required init?(coder: NSCoder) {
 | 
						|
        fatalError("init(coder:) has not been implemented")
 | 
						|
    }
 | 
						|
    
 | 
						|
    // MARK: - Content
 | 
						|
    
 | 
						|
    public func update(with labelInfos: [LabelInfo], labelSize: CGSize = .zero, shouldAutoScroll: Bool = false) {
 | 
						|
        self.labelInfos = labelInfos
 | 
						|
        self.labelSize = labelSize
 | 
						|
        self.shouldAutoScroll = shouldAutoScroll
 | 
						|
        self.shouldScroll = labelInfos.count > 1
 | 
						|
        
 | 
						|
        if self.shouldScroll {
 | 
						|
            let first: LabelInfo = labelInfos.first!
 | 
						|
            let last: LabelInfo = labelInfos.last!
 | 
						|
            self.labelInfos.append(first)
 | 
						|
            self.labelInfos.insert(last, at: 0)
 | 
						|
        }
 | 
						|
        
 | 
						|
        pageControl.numberOfPages = labelInfos.count
 | 
						|
        pageControl.currentPage = 0
 | 
						|
        
 | 
						|
        let contentSize = CGSize(width: labelSize.width * CGFloat(self.labelInfos.count), height: labelSize.height)
 | 
						|
        scrollView.contentSize = contentSize
 | 
						|
        contentWidth.constant = contentSize.width
 | 
						|
        contentHeight.constant = contentSize.height
 | 
						|
        self.scrollView.setContentOffset(
 | 
						|
            CGPoint(
 | 
						|
                x: Int(self.labelSize.width) * (self.shouldScroll ? 1 : 0),
 | 
						|
                y: 0
 | 
						|
            ),
 | 
						|
            animated: false
 | 
						|
        )
 | 
						|
        
 | 
						|
        stackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
 | 
						|
        
 | 
						|
        self.labelInfos.forEach {
 | 
						|
            let wrapper: UIView = UIView()
 | 
						|
            wrapper.set(.width, to: labelSize.width)
 | 
						|
            wrapper.set(.height, to: labelSize.height)
 | 
						|
            let label: UILabel = UILabel()
 | 
						|
            label.font = .systemFont(ofSize: Values.miniFontSize)
 | 
						|
            label.themeTextColor = .textPrimary
 | 
						|
            label.lineBreakMode = .byTruncatingTail
 | 
						|
            label.attributedText = $0.attributedText
 | 
						|
            label.accessibilityIdentifier = $0.accessibility?.identifier
 | 
						|
            label.accessibilityLabel = $0.accessibility?.label
 | 
						|
            label.isAccessibilityElement = true
 | 
						|
            wrapper.addSubview(label)
 | 
						|
            label.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: -4)
 | 
						|
        
 | 
						|
        addSubview(arrowRight)
 | 
						|
        arrowRight.pin(.right, to: .right, of: self)
 | 
						|
        arrowRight.center(.vertical, in: self, withInset: -4)
 | 
						|
        
 | 
						|
        addSubview(pageControl)
 | 
						|
        pageControl.center(.horizontal, in: self)
 | 
						|
        pageControl.pin(.bottom, to: .bottom, of: self)
 | 
						|
        
 | 
						|
        scrollView.addSubview(stackView)
 | 
						|
    }
 | 
						|
    
 | 
						|
    // MARK: - Interaction
 | 
						|
    
 | 
						|
    private func startScrolling() {
 | 
						|
        timer?.invalidate()
 | 
						|
        timer = Timer.scheduledTimerOnMainThread(withTimeInterval: Self.autoScrollingTimeInterval, repeats: true) { _ in
 | 
						|
            guard self.labelInfos.count != 0 else { return }
 | 
						|
            let targetPage = (self.pageControl.currentPage + 1) % self.labelInfos.count
 | 
						|
            self.scrollView.scrollRectToVisible(
 | 
						|
                CGRect(
 | 
						|
                    origin: CGPoint(
 | 
						|
                        x: Int(self.labelSize.width) * targetPage,
 | 
						|
                        y: 0
 | 
						|
                    ),
 | 
						|
                    size: self.labelSize
 | 
						|
                ),
 | 
						|
                animated: true
 | 
						|
            )
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    private func stopScrolling() {
 | 
						|
        timer?.invalidate()
 | 
						|
        timer = nil
 | 
						|
    }
 | 
						|
    
 | 
						|
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
 | 
						|
        let pageIndex: Int = {
 | 
						|
            let maybeCurrentPageIndex: Int = Int(round(scrollView.contentOffset.x/labelSize.width))
 | 
						|
            if self.shouldScroll {
 | 
						|
                if maybeCurrentPageIndex == 0 {
 | 
						|
                    return pageControl.numberOfPages - 1
 | 
						|
                }
 | 
						|
                if maybeCurrentPageIndex == self.labelInfos.count - 1 {
 | 
						|
                    return 0
 | 
						|
                }
 | 
						|
                return maybeCurrentPageIndex - 1
 | 
						|
            }
 | 
						|
            return maybeCurrentPageIndex
 | 
						|
        }()
 | 
						|
        
 | 
						|
        pageControl.currentPage = pageIndex
 | 
						|
    }
 | 
						|
    
 | 
						|
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
 | 
						|
        if pageControl.currentPage == 0 {
 | 
						|
            scrollView.setContentOffset(
 | 
						|
                CGPoint(
 | 
						|
                    x: Int(self.labelSize.width) * 1,
 | 
						|
                    y: 0
 | 
						|
                ),
 | 
						|
                animated: false
 | 
						|
            )
 | 
						|
        }
 | 
						|
        
 | 
						|
        if pageControl.currentPage == pageControl.numberOfPages - 1 {
 | 
						|
            let realLastIndex: Int = self.labelInfos.count - 2
 | 
						|
            scrollView.setContentOffset(
 | 
						|
                CGPoint(
 | 
						|
                    x: Int(self.labelSize.width) * realLastIndex,
 | 
						|
                    y: 0
 | 
						|
                ),
 | 
						|
                animated: false
 | 
						|
            )
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |