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.
		
		
		
		
		
			
		
			
				
	
	
		
			203 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Swift
		
	
			
		
		
	
	
			203 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Swift
		
	
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
 | 
						|
 | 
						|
import UIKit
 | 
						|
import SessionUIKit
 | 
						|
import SessionUtilitiesKit
 | 
						|
 | 
						|
final class SessionCarouselView: UIView, UIScrollViewDelegate {
 | 
						|
    private let slicesForLoop: [UIView]
 | 
						|
    private let info: SessionCarouselView.Info
 | 
						|
    var delegate: SessionCarouselViewDelegate?
 | 
						|
    
 | 
						|
    // MARK: - UI
 | 
						|
    private lazy var scrollView: UIScrollView = {
 | 
						|
        let result: UIScrollView = UIScrollView()
 | 
						|
        result.delegate = self
 | 
						|
        result.isPagingEnabled = true
 | 
						|
        result.showsHorizontalScrollIndicator = false
 | 
						|
        result.showsVerticalScrollIndicator = false
 | 
						|
        result.contentSize = CGSize(
 | 
						|
            width: self.info.sliceSize.width * CGFloat(self.slicesForLoop.count),
 | 
						|
            height: self.info.sliceSize.height
 | 
						|
        )
 | 
						|
        result.layer.cornerRadius = self.info.cornerRadius
 | 
						|
        result.layer.masksToBounds = true
 | 
						|
        
 | 
						|
        return result
 | 
						|
    }()
 | 
						|
    
 | 
						|
    private lazy var pageControl: UIPageControl = {
 | 
						|
        let result: UIPageControl = UIPageControl()
 | 
						|
        result.numberOfPages = self.info.sliceCount
 | 
						|
        result.currentPage = 0
 | 
						|
        result.isHidden = !self.info.shouldShowPageControl
 | 
						|
        result.transform = CGAffineTransform(
 | 
						|
            scaleX: self.info.pageControlStyle.size.rawValue,
 | 
						|
            y: self.info.pageControlStyle.size.rawValue
 | 
						|
        )
 | 
						|
        
 | 
						|
        return result
 | 
						|
    }()
 | 
						|
    
 | 
						|
    private lazy var arrowLeft: UIButton = {
 | 
						|
        let result = UIButton(type: .custom)
 | 
						|
        result.setImage(UIImage(systemName: "chevron.left")?.withRenderingMode(.alwaysTemplate), for: .normal)
 | 
						|
        result.addTarget(self, action: #selector(scrollToPreviousSlice), for: .touchUpInside)
 | 
						|
        result.themeTintColor = .textPrimary
 | 
						|
        result.set(.width, to: self.info.arrowsSize.width)
 | 
						|
        result.set(.height, to: self.info.arrowsSize.height)
 | 
						|
        result.isHidden = !self.info.shouldShowArrows
 | 
						|
        
 | 
						|
        return result
 | 
						|
    }()
 | 
						|
 | 
						|
    private lazy var arrowRight: UIButton = {
 | 
						|
        let result = UIButton(type: .custom)
 | 
						|
        result.setImage(UIImage(systemName: "chevron.right")?.withRenderingMode(.alwaysTemplate), for: .normal)
 | 
						|
        result.addTarget(self, action: #selector(scrollToNextSlice), for: .touchUpInside)
 | 
						|
        result.themeTintColor = .textPrimary
 | 
						|
        result.set(.width, to: self.info.arrowsSize.width)
 | 
						|
        result.set(.height, to: self.info.arrowsSize.height)
 | 
						|
        result.isHidden = !self.info.shouldShowArrows
 | 
						|
        
 | 
						|
        return result
 | 
						|
    }()
 | 
						|
    
 | 
						|
    // MARK: - Lifecycle
 | 
						|
    
 | 
						|
    init(info: SessionCarouselView.Info) {
 | 
						|
        self.info = info
 | 
						|
        if self.info.sliceCount > 1,
 | 
						|
           let copyOfFirstSlice: UIView = self.info.copyOfFirstSlice,
 | 
						|
           let copyOfLastSlice: UIView = self.info.copyOfLastSlice
 | 
						|
        {
 | 
						|
            self.slicesForLoop = [copyOfLastSlice]
 | 
						|
                .appending(contentsOf: self.info.slices)
 | 
						|
                .appending(copyOfFirstSlice)
 | 
						|
        } else {
 | 
						|
            self.slicesForLoop = self.info.slices
 | 
						|
        }
 | 
						|
        
 | 
						|
        super.init(frame: CGRect.zero)
 | 
						|
        setUpViewHierarchy()
 | 
						|
    }
 | 
						|
 | 
						|
    override init(frame: CGRect) {
 | 
						|
        preconditionFailure("Use init(attachment:) instead.")
 | 
						|
    }
 | 
						|
 | 
						|
    required init?(coder: NSCoder) {
 | 
						|
        preconditionFailure("Use init(attachment:) instead.")
 | 
						|
    }
 | 
						|
 | 
						|
    private func setUpViewHierarchy() {
 | 
						|
        set(.width, to: self.info.sliceSize.width + Values.largeSpacing + 2 * self.info.arrowsSize.width)
 | 
						|
        set(.height, to: self.info.sliceSize.height)
 | 
						|
        
 | 
						|
        let stackView: UIStackView = UIStackView(arrangedSubviews: self.slicesForLoop)
 | 
						|
        stackView.axis = .horizontal
 | 
						|
        stackView.set(.width, to: self.info.sliceSize.width * CGFloat(self.slicesForLoop.count))
 | 
						|
        stackView.set(.height, to: self.info.sliceSize.height)
 | 
						|
        
 | 
						|
        addSubview(self.scrollView)
 | 
						|
        scrollView.center(in: self)
 | 
						|
        scrollView.set(.width, to: self.info.sliceSize.width)
 | 
						|
        scrollView.set(.height, to: self.info.sliceSize.height)
 | 
						|
        scrollView.addSubview(stackView)
 | 
						|
        scrollView.setContentOffset(
 | 
						|
            CGPoint(
 | 
						|
                x: Int(self.info.sliceSize.width) * (self.info.sliceCount > 1 ? 1 : 0),
 | 
						|
                y: 0
 | 
						|
            ),
 | 
						|
            animated: false
 | 
						|
        )
 | 
						|
        
 | 
						|
        addSubview(self.pageControl)
 | 
						|
        self.pageControl.center(.horizontal, in: self)
 | 
						|
        self.pageControl.pin(.bottom, to: .bottom, of: self)
 | 
						|
        
 | 
						|
        addSubview(self.arrowLeft)
 | 
						|
        self.arrowLeft.pin(.leading, to: .leading, of: self)
 | 
						|
        self.arrowLeft.center(.vertical, in: self)
 | 
						|
        
 | 
						|
        addSubview(self.arrowRight)
 | 
						|
        self.arrowRight.pin(.trailing, to: .trailing, of: self)
 | 
						|
        self.arrowRight.center(.vertical, in: self)
 | 
						|
    }
 | 
						|
    
 | 
						|
    // MARK: - UIScrollViewDelegate
 | 
						|
    
 | 
						|
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
 | 
						|
        let pageIndex: Int = {
 | 
						|
            let maybeCurrentPageIndex: Int = Int(round(scrollView.contentOffset.x/self.info.sliceSize.width))
 | 
						|
            if self.info.sliceCount > 1 {
 | 
						|
                if maybeCurrentPageIndex == 0 {
 | 
						|
                    return pageControl.numberOfPages - 1
 | 
						|
                }
 | 
						|
                if maybeCurrentPageIndex == self.slicesForLoop.count - 1 {
 | 
						|
                    return 0
 | 
						|
                }
 | 
						|
                return maybeCurrentPageIndex - 1
 | 
						|
            }
 | 
						|
            return maybeCurrentPageIndex
 | 
						|
        }()
 | 
						|
 | 
						|
        pageControl.currentPage = pageIndex
 | 
						|
    }
 | 
						|
 | 
						|
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
 | 
						|
        setCorrectCotentOffsetIfNeeded(scrollView)
 | 
						|
        delegate?.carouselViewDidScrollToNewSlice(currentPage: pageControl.currentPage)
 | 
						|
    }
 | 
						|
    
 | 
						|
    func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
 | 
						|
        setCorrectCotentOffsetIfNeeded(scrollView)
 | 
						|
        delegate?.carouselViewDidScrollToNewSlice(currentPage: pageControl.currentPage)
 | 
						|
    }
 | 
						|
    
 | 
						|
    private func setCorrectCotentOffsetIfNeeded(_ scrollView: UIScrollView) {
 | 
						|
        if pageControl.currentPage == 0 {
 | 
						|
            scrollView.setContentOffset(
 | 
						|
                CGPoint(
 | 
						|
                    x: Int(self.info.sliceSize.width) * 1,
 | 
						|
                    y: 0
 | 
						|
                ),
 | 
						|
                animated: false
 | 
						|
            )
 | 
						|
        }
 | 
						|
 | 
						|
        if pageControl.currentPage == pageControl.numberOfPages - 1 {
 | 
						|
            let realLastIndex: Int = self.slicesForLoop.count - 2
 | 
						|
            scrollView.setContentOffset(
 | 
						|
                CGPoint(
 | 
						|
                    x: Int(self.info.sliceSize.width) * realLastIndex,
 | 
						|
                    y: 0
 | 
						|
                ),
 | 
						|
                animated: false
 | 
						|
            )
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    // MARK: - Interaction
 | 
						|
    
 | 
						|
    @objc func scrollToNextSlice() {
 | 
						|
        self.scrollView.setContentOffset(
 | 
						|
            CGPoint(
 | 
						|
                x: self.scrollView.contentOffset.x + self.info.sliceSize.width,
 | 
						|
                y: 0
 | 
						|
            ),
 | 
						|
            animated: true
 | 
						|
        )
 | 
						|
    }
 | 
						|
    
 | 
						|
    @objc func scrollToPreviousSlice() {
 | 
						|
        self.scrollView.setContentOffset(
 | 
						|
            CGPoint(
 | 
						|
                x: self.scrollView.contentOffset.x - self.info.sliceSize.width,
 | 
						|
                y: 0
 | 
						|
            ),
 | 
						|
            animated: true
 | 
						|
        )
 | 
						|
    }
 | 
						|
}
 |