feat: make the carousel looping infinitely

pull/941/head
Ryan Zhao 2 years ago
parent b11ac1f6f8
commit 8164d4400d

@ -113,7 +113,7 @@
7B4C75CB26B37E0F0000AC89 /* UnsendRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B4C75CA26B37E0F0000AC89 /* UnsendRequest.swift */; };
7B4C75CD26BB92060000AC89 /* DeletedMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B4C75CC26BB92060000AC89 /* DeletedMessageView.swift */; };
7B50D64D28AC7CF80086CCEC /* silence.aiff in Resources */ = {isa = PBXBuildFile; fileRef = 7B50D64C28AC7CF80086CCEC /* silence.aiff */; };
7B5233C42900E90F00F8F375 /* PagedScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B5233C32900E90F00F8F375 /* PagedScrollView.swift */; };
7B5233C42900E90F00F8F375 /* SessionLabelCarouselView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B5233C32900E90F00F8F375 /* SessionLabelCarouselView.swift */; };
7B5233C6290636D700F8F375 /* _011_DisappearingMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B5233C5290636D700F8F375 /* _011_DisappearingMessage.swift */; };
7B7037432834B81F000DCF35 /* ReactionContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B7037422834B81F000DCF35 /* ReactionContainerView.swift */; };
7B7037452834BCC0000DCF35 /* ReactionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B7037442834BCC0000DCF35 /* ReactionView.swift */; };
@ -1185,7 +1185,7 @@
7B4C75CA26B37E0F0000AC89 /* UnsendRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsendRequest.swift; sourceTree = "<group>"; };
7B4C75CC26BB92060000AC89 /* DeletedMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeletedMessageView.swift; sourceTree = "<group>"; };
7B50D64C28AC7CF80086CCEC /* silence.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; path = silence.aiff; sourceTree = "<group>"; };
7B5233C32900E90F00F8F375 /* PagedScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PagedScrollView.swift; sourceTree = "<group>"; };
7B5233C32900E90F00F8F375 /* SessionLabelCarouselView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionLabelCarouselView.swift; sourceTree = "<group>"; };
7B5233C5290636D700F8F375 /* _011_DisappearingMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _011_DisappearingMessage.swift; sourceTree = "<group>"; };
7B7037422834B81F000DCF35 /* ReactionContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionContainerView.swift; sourceTree = "<group>"; };
7B7037442834BCC0000DCF35 /* ReactionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionView.swift; sourceTree = "<group>"; };
@ -2386,7 +2386,7 @@
C374EEEA25DA3CA70073A857 /* ConversationTitleView.swift */,
FD4B200D283492210034334B /* InsetLockableTableView.swift */,
7B9F71C828470667006DFE7B /* ReactionListSheet.swift */,
7B5233C32900E90F00F8F375 /* PagedScrollView.swift */,
7B5233C32900E90F00F8F375 /* SessionLabelCarouselView.swift */,
);
path = "Views & Modals";
sourceTree = "<group>";
@ -5763,7 +5763,7 @@
B835249B25C3AB650089A44F /* VisibleMessageCell.swift in Sources */,
B8D0A25025E3678700C1835E /* LinkDeviceVC.swift in Sources */,
B894D0752339EDCF00B4D94D /* NukeDataModal.swift in Sources */,
7B5233C42900E90F00F8F375 /* PagedScrollView.swift in Sources */,
7B5233C42900E90F00F8F375 /* SessionLabelCarouselView.swift in Sources */,
7B93D07727CF1A8A00811CB6 /* MockDataGenerator.swift in Sources */,
7B1B52D828580C6D006069F2 /* EmojiPickerSheet.swift in Sources */,
7B4C75CD26BB92060000AC89 /* DeletedMessageView.swift in Sources */,

@ -15,7 +15,7 @@ final class ConversationTitleView: UIView {
return UIView.layoutFittingExpandedSize
}
private lazy var pagedScrollViewWidth = pagedScrollView.set(.width, to: 200)
private lazy var labelCarouselViewWidth = labelCarouselView.set(.width, to: 200)
// MARK: - UI Components
@ -31,49 +31,13 @@ final class ConversationTitleView: UIView {
return result
}()
private lazy var pagedScrollView: PagedScrollView = {
let result = PagedScrollView()
return result
}()
private lazy var subtitleLabel: UILabel = {
let result: UILabel = UILabel()
result.font = .systemFont(ofSize: Values.verySmallFontSize)
result.themeTextColor = .textPrimary
result.lineBreakMode = .byTruncatingTail
return result
}()
private lazy var userCountLabel: UILabel = {
let result: UILabel = UILabel()
result.font = .systemFont(ofSize: Values.verySmallFontSize)
result.themeTextColor = .textPrimary
result.lineBreakMode = .byTruncatingTail
return result
}()
private lazy var notificationSettingsLabel: UILabel = {
let result: UILabel = UILabel()
result.font = .systemFont(ofSize: Values.verySmallFontSize)
result.themeTextColor = .textPrimary
result.lineBreakMode = .byTruncatingTail
return result
}()
private lazy var disappearingMessageSettingLabel: UILabel = {
let result: UILabel = UILabel()
result.font = .systemFont(ofSize: Values.verySmallFontSize)
result.themeTextColor = .textPrimary
result.lineBreakMode = .byTruncatingTail
private lazy var labelCarouselView: SessionLabelCarouselView = {
let result = SessionLabelCarouselView()
return result
}()
private lazy var stackView: UIStackView = {
let result = UIStackView(arrangedSubviews: [ titleLabel, pagedScrollView ])
let result = UIStackView(arrangedSubviews: [ titleLabel, labelCarouselView ])
result.axis = .vertical
result.alignment = .center
@ -169,13 +133,13 @@ final class ConversationTitleView: UIView {
)
)
ThemeManager.onThemeChange(observer: self.subtitleLabel) { [weak self] theme, _ in
ThemeManager.onThemeChange(observer: self.labelCarouselView) { [weak self] theme, _ in
guard let textPrimary: UIColor = theme.color(for: .textPrimary) else { return }
var slides: [UIView?] = []
var labelStrings: [NSAttributedString] = []
if Date().timeIntervalSince1970 <= (mutedUntilTimestamp ?? 0) {
self?.notificationSettingsLabel.attributedText = NSAttributedString(
let notificationSettingsLabelString = NSAttributedString(
string: "\u{e067} ",
attributes: [
.font: UIFont.ows_elegantIconsFont(10),
@ -183,8 +147,8 @@ final class ConversationTitleView: UIView {
]
)
.appending(string: "Muted")
self?.notificationSettingsLabel.isHidden = false
slides.append(self?.notificationSettingsLabel)
labelStrings.append(notificationSettingsLabelString)
} else if onlyNotifyForMentions{
let imageAttachment = NSTextAttachment()
imageAttachment.image = UIImage(named: "NotifyMentions.png")?.withTint(textPrimary)
@ -195,28 +159,31 @@ final class ConversationTitleView: UIView {
height: Values.verySmallFontSize
)
self?.notificationSettingsLabel.attributedText = NSAttributedString(attachment: imageAttachment)
let notificationSettingsLabelString = NSAttributedString(attachment: imageAttachment)
.appending(string: " ")
.appending(string: "view_conversation_title_notify_for_mentions_only".localized())
self?.notificationSettingsLabel.isHidden = false
slides.append(self?.notificationSettingsLabel)
labelStrings.append(notificationSettingsLabelString)
}
if let userCount: Int = userCount {
switch threadVariant {
case .contact: break
case .closedGroup:
self?.userCountLabel.attributedText = NSAttributedString(
string: "\(userCount) member\(userCount == 1 ? "" : "s")"
)
case .openGroup:
self?.userCountLabel.attributedText = NSAttributedString(
string: "\(userCount) active member\(userCount == 1 ? "" : "s")"
)
}
slides.append(self?.userCountLabel)
let userCountLabelString: NSAttributedString = {
switch threadVariant {
case .contact: return NSAttributedString(string: "") // Should not happen
case .closedGroup:
return NSAttributedString(
string: "\(userCount) member\(userCount == 1 ? "" : "s")"
)
case .openGroup:
return NSAttributedString(
string: "\(userCount) active member\(userCount == 1 ? "" : "s")"
)
}
}()
labelStrings.append(userCountLabelString)
}
if let config = disappearingMessagesConfig, config.isEnabled == true {
@ -229,25 +196,25 @@ final class ConversationTitleView: UIView {
height: Values.verySmallFontSize
)
self?.disappearingMessageSettingLabel.attributedText = NSAttributedString(attachment: imageAttachment)
let disappearingMessageSettingLabelString = NSAttributedString(attachment: imageAttachment)
.appending(string: " ")
.appending(string: config.type == .disappearAfterRead ? "DISAPPERING_MESSAGES_TYPE_AFTER_READ_TITLE".localized() : "DISAPPERING_MESSAGES_TYPE_AFTER_SEND_TITLE".localized())
.appending(string: " - ")
.appending(string: floor(config.durationSeconds).formatted(format: .short))
self?.disappearingMessageSettingLabel.isHidden = false
slides.append(self?.disappearingMessageSettingLabel)
labelStrings.append(disappearingMessageSettingLabelString)
}
self?.pagedScrollView.update(
with: slides.compactMap{ $0 },
slideSize: CGSize(
width: self?.pagedScrollViewWidth.constant ?? 0,
self?.labelCarouselView.update(
with: labelStrings,
labelSize: CGSize(
width: self?.labelCarouselViewWidth.constant ?? 0,
height: 20
),
shouldAutoScroll: false
)
self?.pagedScrollView.isHidden = (slides.count == 0)
self?.labelCarouselView.isHidden = (labelStrings.count == 0)
}
// Contact threads also have the call button to compensate for

@ -3,20 +3,21 @@
import UIKit
import SessionUIKit
final class PagedScrollView: UIView, UIScrollViewDelegate {
final class SessionLabelCarouselView: UIView, UIScrollViewDelegate {
private static let autoScrollingTimeInterval: TimeInterval = 10
private var slides: [UIView] = []
private var slideSize: CGSize = .zero
private var labelStrings: [NSAttributedString] = []
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 shouldArrowsShow: Bool = false {
private var shouldScroll: Bool = false {
didSet {
arrowLeft.isHidden = !shouldArrowsShow
arrowRight.isHidden = !shouldArrowsShow
arrowLeft.isHidden = !shouldScroll
arrowRight.isHidden = !shouldScroll
pageControl.isHidden = !shouldScroll
}
}
@ -67,10 +68,10 @@ final class PagedScrollView: UIView, UIScrollViewDelegate {
// MARK: - Initialization
init(slides: [UIView] = [], slideSize: CGSize = .zero, shouldAutoScroll: Bool = false) {
init(labelStrings: [NSAttributedString] = [], labelSize: CGSize = .zero, shouldAutoScroll: Bool = false) {
super.init(frame: .zero)
setUpViewHierarchy()
self.update(with: slides, slideSize: slideSize, shouldAutoScroll: shouldAutoScroll)
self.update(with: labelStrings, labelSize: labelSize, shouldAutoScroll: shouldAutoScroll)
}
required init?(coder: NSCoder) {
@ -79,29 +80,47 @@ final class PagedScrollView: UIView, UIScrollViewDelegate {
// MARK: - Content
public func update(with slides: [UIView] = [], slideSize: CGSize = .zero, shouldAutoScroll: Bool = false) {
self.slides = slides
self.slideSize = slideSize
public func update(with labelStrings: [NSAttributedString] = [], labelSize: CGSize = .zero, shouldAutoScroll: Bool = false) {
self.labelStrings = labelStrings
self.labelSize = labelSize
self.shouldAutoScroll = shouldAutoScroll
self.shouldArrowsShow = slides.count > 1
self.shouldScroll = labelStrings.count > 1
pageControl.numberOfPages = slides.count
if self.shouldScroll {
let first: NSAttributedString = labelStrings.first!
let last: NSAttributedString = labelStrings.last!
self.labelStrings.append(first)
self.labelStrings.insert(last, at: 0)
}
pageControl.numberOfPages = labelStrings.count
pageControl.currentPage = 0
pageControl.isHidden = (slides.count == 1)
let contentSize = CGSize(width: slideSize.width * CGFloat(slides.count), height: slideSize.height)
let contentSize = CGSize(width: labelSize.width * CGFloat(self.labelStrings.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.slides.forEach {
self.labelStrings.forEach {
let wrapper: UIView = UIView()
wrapper.set(.width, to: slideSize.width)
wrapper.set(.height, to: slideSize.height)
wrapper.addSubview($0)
$0.center(in: wrapper)
wrapper.set(.width, to: labelSize.width)
wrapper.set(.height, to: labelSize.height)
let label: UILabel = UILabel()
label.font = .systemFont(ofSize: Values.verySmallFontSize)
label.themeTextColor = .textPrimary
label.lineBreakMode = .byTruncatingTail
label.attributedText = $0
wrapper.addSubview(label)
label.center(in: wrapper)
stackView.addArrangedSubview(wrapper)
}
@ -132,15 +151,15 @@ final class PagedScrollView: UIView, UIScrollViewDelegate {
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
guard self.labelStrings.count != 0 else { return }
let targetPage = (self.pageControl.currentPage + 1) % self.labelStrings.count
self.scrollView.scrollRectToVisible(
CGRect(
origin: CGPoint(
x: Int(self.slideSize.width) * targetPage,
x: Int(self.labelSize.width) * targetPage,
y: 0
),
size: self.slideSize
size: self.labelSize
),
animated: true
)
@ -153,7 +172,43 @@ final class PagedScrollView: UIView, UIScrollViewDelegate {
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let pageIndex = round(scrollView.contentOffset.x/slideSize.width)
pageControl.currentPage = Int(pageIndex)
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.labelStrings.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.labelStrings.count - 2
scrollView.setContentOffset(
CGPoint(
x: Int(self.labelSize.width) * realLastIndex,
y: 0
),
animated: false
)
}
}
}
Loading…
Cancel
Save