WIP: encapsulate carousel view

pull/782/head
Ryan Zhao 1 year ago
parent d1aacf24c2
commit 838969efa6

@ -110,6 +110,7 @@
7B1D74AC27BDE7510030B423 /* Promise+Timeout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B1D74AB27BDE7510030B423 /* Promise+Timeout.swift */; };
7B1D74B027C365960030B423 /* Timer+MainThread.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B1D74AF27C365960030B423 /* Timer+MainThread.swift */; };
7B2561C22978B307005C086C /* MediaInfoVC+MediaInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B2561C12978B307005C086C /* MediaInfoVC+MediaInfoView.swift */; };
7B2561C429874851005C086C /* SessionCarouselView+Info.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B2561C329874851005C086C /* SessionCarouselView+Info.swift */; };
7B3A392E2977791E002FE4AC /* MediaInfoVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B3A392D2977791E002FE4AC /* MediaInfoVC.swift */; };
7B3A3930297A3919002FE4AC /* MediaInfoVC+MediaPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B3A392F297A3919002FE4AC /* MediaInfoVC+MediaPreviewView.swift */; };
7B3A39322980D02B002FE4AC /* SessionCarouselView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B3A39312980D02B002FE4AC /* SessionCarouselView.swift */; };
@ -1179,6 +1180,7 @@
7B1D74AB27BDE7510030B423 /* Promise+Timeout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Promise+Timeout.swift"; sourceTree = "<group>"; };
7B1D74AF27C365960030B423 /* Timer+MainThread.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Timer+MainThread.swift"; sourceTree = "<group>"; };
7B2561C12978B307005C086C /* MediaInfoVC+MediaInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MediaInfoVC+MediaInfoView.swift"; sourceTree = "<group>"; };
7B2561C329874851005C086C /* SessionCarouselView+Info.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionCarouselView+Info.swift"; sourceTree = "<group>"; };
7B2DB2AD26F1B0FF0035B509 /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = si.lproj/Localizable.strings; sourceTree = "<group>"; };
7B3A392D2977791E002FE4AC /* MediaInfoVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaInfoVC.swift; sourceTree = "<group>"; };
7B3A392F297A3919002FE4AC /* MediaInfoVC+MediaPreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MediaInfoVC+MediaPreviewView.swift"; sourceTree = "<group>"; };
@ -2587,6 +2589,7 @@
FD37EA0828AA2D27003AE748 /* SessionTableViewModel.swift */,
FD37EA0628AA2CCA003AE748 /* SessionTableViewController.swift */,
7B3A39312980D02B002FE4AC /* SessionCarouselView.swift */,
7B2561C329874851005C086C /* SessionCarouselView+Info.swift */,
);
path = Shared;
sourceTree = "<group>";
@ -5580,6 +5583,7 @@
buildActionMask = 2147483647;
files = (
FD52090928B59411006098F6 /* ScreenLockUI.swift in Sources */,
7B2561C429874851005C086C /* SessionCarouselView+Info.swift in Sources */,
FDF2220B2818F38D000A4995 /* SessionApp.swift in Sources */,
FD7115EB28C5D78E00B47552 /* ThreadSettingsViewModel.swift in Sources */,
B8041AA725C90927003C2166 /* TypingIndicatorCell.swift in Sources */,

@ -14,17 +14,27 @@ final class MediaInfoVC: BaseVC {
private lazy var mediaInfoView: MediaInfoView = MediaInfoView(attachment: nil)
private lazy var mediaCarouselView: SessionCarouselView = {
let result: SessionCarouselView = SessionCarouselView(
slices: self.attachments.map {
MediaPreviewView(
attachment: $0,
isOutgoing: self.isOutgoing
info: SessionCarouselView.Info(
slices: self.attachments.map {
MediaPreviewView(
attachment: $0,
isOutgoing: self.isOutgoing
)
},
sliceSize: CGSize(
width: Self.mediaSize,
height: Self.mediaSize
),
shouldShowPageControl: true,
pageControlHeight: 10,
shouldShowArrows: true,
arrowsSize: CGSize(
width: 20,
height: 30
)
},
sliceSize: CGSize(
width: Self.mediaSize,
height: Self.mediaSize
)
)
result.set(.height, to: Self.mediaSize)
return result
}()
@ -63,9 +73,11 @@ final class MediaInfoVC: BaseVC {
let stackView: UIStackView = UIStackView(arrangedSubviews: [ mediaCarouselView, mediaInfoView ])
stackView.axis = .vertical
stackView.alignment = .center
stackView.spacing = Values.largeSpacing
self.view.addSubview(stackView)
stackView.center(in: self.view)
stackView.pin([ UIView.HorizontalEdge.leading, UIView.HorizontalEdge.trailing ], to: self.view)
stackView.center(.vertical, in: self.view)
}
}

@ -0,0 +1,39 @@
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
import UIKit
import SessionUIKit
import SessionUtilitiesKit
extension SessionCarouselView {
public struct Info {
let slices: [UIView]
let sliceSize: CGSize
let sliceCount: Int
let shouldShowPageControl: Bool
let pageControlHeight: CGFloat
let pageControlScale: CGFloat // This is to control the size of the dots
let shouldShowArrows: Bool
let arrowsSize: CGSize
// MARK: - Initialization
init(
slices: [UIView] = [],
sliceSize: CGSize = .zero,
shouldShowPageControl: Bool = true,
pageControlHeight: CGFloat = 0,
pageControlScale: CGFloat = 1,
shouldShowArrows: Bool = true,
arrowsSize: CGSize = .zero
) {
self.slices = slices
self.sliceSize = sliceSize
self.sliceCount = slices.count
self.shouldShowPageControl = shouldShowPageControl && (self.sliceCount > 1)
self.pageControlHeight = pageControlHeight
self.pageControlScale = pageControlScale
self.shouldShowArrows = shouldShowArrows && (self.sliceCount > 1)
self.arrowsSize = arrowsSize
}
}
}

@ -1,18 +1,12 @@
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import UIKit
import SessionUIKit
import SessionUtilitiesKit
final class SessionCarouselView: UIView, UIScrollViewDelegate {
private let slicesForLoop: [UIView]
private let sliceSize: CGSize
private let sliceCount: Int
// MARK: - Settings
public var showPageControl: Bool = true {
didSet {
self.pageControl.isHidden = !showPageControl
}
}
private let info: SessionCarouselView.Info
// MARK: - UI
private lazy var scrollView: UIScrollView = {
@ -22,8 +16,8 @@ final class SessionCarouselView: UIView, UIScrollViewDelegate {
result.showsHorizontalScrollIndicator = false
result.showsVerticalScrollIndicator = false
result.contentSize = CGSize(
width: self.sliceSize.width * CGFloat(self.slicesForLoop.count),
height: self.sliceSize.height
width: self.info.sliceSize.width * CGFloat(self.slicesForLoop.count),
height: self.info.sliceSize.height
)
return result
@ -31,23 +25,52 @@ final class SessionCarouselView: UIView, UIScrollViewDelegate {
private lazy var pageControl: UIPageControl = {
let result: UIPageControl = UIPageControl()
result.numberOfPages = self.sliceCount
result.numberOfPages = self.info.sliceCount
result.currentPage = 0
result.isHidden = !self.info.shouldShowPageControl
result.set(.height, to: self.info.pageControlHeight)
result.transform = CGAffineTransform(scaleX: self.info.pageControlScale, y: self.info.pageControlScale)
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(slices: [UIView], sliceSize: CGSize) {
self.sliceCount = slices.count
if self.sliceCount > 1, let copyOfFirstSlice: UIView = slices.first?.copyView(), let copyOfLastSlice: UIView = slices.last?.copyView() {
init(info: SessionCarouselView.Info) {
self.info = info
if self.info.sliceCount > 1,
let copyOfFirstSlice: UIView = self.info.slices.first?.copyView(),
let copyOfLastSlice: UIView = self.info.slices.last?.copyView()
{
self.slicesForLoop = [copyOfLastSlice]
.appending(contentsOf: slices)
.appending(contentsOf: self.info.slices)
.appending(copyOfFirstSlice)
} else {
self.slicesForLoop = slices
self.slicesForLoop = self.info.slices
}
self.sliceSize = sliceSize
super.init(frame: CGRect.zero)
setUpViewHierarchy()
@ -62,19 +85,22 @@ final class SessionCarouselView: UIView, UIScrollViewDelegate {
}
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.sliceSize.width * CGFloat(self.slicesForLoop.count))
stackView.set(.height, to: self.sliceSize.height)
stackView.set(.width, to: self.info.sliceSize.width * CGFloat(self.slicesForLoop.count))
stackView.set(.height, to: self.info.sliceSize.height)
addSubview(self.scrollView)
scrollView.pin(to: self)
scrollView.set(.width, to: self.sliceSize.width)
scrollView.set(.height, to: self.sliceSize.height)
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.sliceSize.width) * (self.sliceCount > 1 ? 1 : 0),
x: Int(self.info.sliceSize.width) * (self.info.sliceCount > 1 ? 1 : 0),
y: 0
),
animated: false
@ -83,13 +109,21 @@ final class SessionCarouselView: UIView, UIScrollViewDelegate {
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/sliceSize.width))
if self.sliceCount > 1 {
let maybeCurrentPageIndex: Int = Int(round(scrollView.contentOffset.x/self.info.sliceSize.width))
if self.info.sliceCount > 1 {
if maybeCurrentPageIndex == 0 {
return pageControl.numberOfPages - 1
}
@ -108,7 +142,7 @@ final class SessionCarouselView: UIView, UIScrollViewDelegate {
if pageControl.currentPage == 0 {
scrollView.setContentOffset(
CGPoint(
x: Int(self.sliceSize.width) * 1,
x: Int(self.info.sliceSize.width) * 1,
y: 0
),
animated: false
@ -119,11 +153,32 @@ final class SessionCarouselView: UIView, UIScrollViewDelegate {
let realLastIndex: Int = self.slicesForLoop.count - 2
scrollView.setContentOffset(
CGPoint(
x: Int(self.sliceSize.width) * realLastIndex,
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
)
}
}

Loading…
Cancel
Save