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
 | |
|         )
 | |
|     }
 | |
| }
 |