From a2893a2e328f807f58882ba496eca62bdf43228d Mon Sep 17 00:00:00 2001 From: Sasha Weiss Date: Tue, 17 Feb 2026 09:51:35 -0800 Subject: [PATCH] Add TablePreviewViewController, use in CLVBackupProgressView previews --- Signal.xcodeproj/project.pbxproj | 4 ++ ...istViewController+BackupProgressView.swift | 45 +++++++------- .../Utils/TablePreviewViewController.swift | 59 +++++++++++++++++++ 3 files changed, 87 insertions(+), 21 deletions(-) create mode 100644 SignalUI/Utils/TablePreviewViewController.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index a4a2b5dd2f..bb7ec9af8d 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -246,6 +246,7 @@ 3406D32E25DD80D600885B14 /* payments_spinner.json in Resources */ = {isa = PBXBuildFile; fileRef = 3406D32A25DD80D600885B14 /* payments_spinner.json */; }; 3406D33125DD832800885B14 /* payments_spinner_info_dark.json in Resources */ = {isa = PBXBuildFile; fileRef = 3406D32F25DD832700885B14 /* payments_spinner_info_dark.json */; }; 3406D33225DD832800885B14 /* payments_spinner_dark.json in Resources */ = {isa = PBXBuildFile; fileRef = 3406D33025DD832800885B14 /* payments_spinner_dark.json */; }; + 3407BB4B271D9DCD0084CBAF /* TablePreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3407BB4A271D9DCD0084CBAF /* TablePreviewViewController.swift */; }; 340D900024FEE6A9007B5504 /* GroupInviteLinksUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 340D8FFF24FEE6A9007B5504 /* GroupInviteLinksUI.swift */; }; 3414896925C9B6490098E3ED /* CurrencyPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3414896825C9B6490098E3ED /* CurrencyPickerViewController.swift */; }; 3415217525B0CB31009F177F /* CVAttachmentProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3415217425B0CB30009F177F /* CVAttachmentProgressView.swift */; }; @@ -4231,6 +4232,7 @@ 3406D32A25DD80D600885B14 /* payments_spinner.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = payments_spinner.json; sourceTree = ""; }; 3406D32F25DD832700885B14 /* payments_spinner_info_dark.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = payments_spinner_info_dark.json; sourceTree = ""; }; 3406D33025DD832800885B14 /* payments_spinner_dark.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = payments_spinner_dark.json; sourceTree = ""; }; + 3407BB4A271D9DCD0084CBAF /* TablePreviewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TablePreviewViewController.swift; sourceTree = ""; }; 340B02B61F9FD31800F9CFEC /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = translations/he.lproj/Localizable.strings; sourceTree = ""; }; 340B06C623C8DA2600929588 /* StorageService+GroupsV2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "StorageService+GroupsV2.swift"; sourceTree = ""; }; 340B870D23DF3E3A00BE0AFC /* GroupV2UpdatesImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupV2UpdatesImpl.swift; sourceTree = ""; }; @@ -8879,6 +8881,7 @@ B95967832D52976A00F9A800 /* NavigationPreviewController.swift */, B9A53B922CF7928A0000578B /* SheetPreviewViewController.swift */, C1D9B1522B7E949500D94595 /* SpamReportingUIUtils.swift */, + 3407BB4A271D9DCD0084CBAF /* TablePreviewViewController.swift */, D9DD708D2E67BC48003E1A45 /* URL+Support.swift */, ); path = Utils; @@ -17520,6 +17523,7 @@ 88F5FA9428EBD4CF007AA1BF /* StorySharing.swift in Sources */, 3402A9DA271D953B0084CBAE /* SUIEnvironment.swift in Sources */, B9D721762C87B8EB007EDA85 /* SwiftUI+Animations.swift in Sources */, + 3407BB4B271D9DCD0084CBAF /* TablePreviewViewController.swift in Sources */, 3402AA7E271D9E180084CBAE /* TappableStackView.swift in Sources */, 3402AA80271D9E180084CBAE /* TappableView.swift in Sources */, B99288002CF124AC000D62C4 /* Text+Links.swift in Sources */, diff --git a/Signal/src/ViewControllers/HomeView/Chat List/ChatListViewController+BackupProgressView.swift b/Signal/src/ViewControllers/HomeView/Chat List/ChatListViewController+BackupProgressView.swift index f35eeb152d..b0110b856c 100644 --- a/Signal/src/ViewControllers/HomeView/Chat List/ChatListViewController+BackupProgressView.swift +++ b/Signal/src/ViewControllers/HomeView/Chat List/ChatListViewController+BackupProgressView.swift @@ -71,7 +71,7 @@ class CLVBackupProgressView: BackupProgressView.Delegate { private let store: Store weak var chatListViewController: ChatListViewController? - let backupProgressViewCell: UITableViewCell + lazy var backupProgressViewCell: UITableViewCell = Self.tableViewCell(wrapping: backupProgressView) private let backupProgressView: BackupProgressView private let state: AtomicValue @@ -85,16 +85,20 @@ class CLVBackupProgressView: BackupProgressView.Delegate { self.deviceSleepManager = DependenciesBridge.shared.deviceSleepManager.owsFailUnwrap("Missing DeviceSleepManager!") self.store = Store() - self.backupProgressViewCell = UITableViewCell() - self.backupProgressViewCell.backgroundColor = .Signal.background self.backupProgressView = BackupProgressView(viewState: nil) self.state = AtomicValue(State(), lock: .init()) - self.backupProgressViewCell.contentView.addSubview(self.backupProgressView) - self.backupProgressView.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets(hMargin: 12, vMargin: 12)) self.backupProgressView.delegate = self } + fileprivate static func tableViewCell(wrapping backupProgressView: BackupProgressView) -> UITableViewCell { + let cell = UITableViewCell() + cell.backgroundColor = .Signal.background + cell.contentView.addSubview(backupProgressView) + backupProgressView.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets(hMargin: 12, vMargin: 12)) + return cell + } + // MARK: - var shouldBeVisible: Bool { @@ -692,26 +696,25 @@ private class BackupProgressView: UIView { #if DEBUG -private class BackupProgressViewPreviewViewController: UIViewController { - private let state: BackupProgressView.ViewState? - +private class BackupProgressViewPreviewViewController: TablePreviewViewController { init(state: BackupProgressView.ViewState?) { - self.state = state - super.init(nibName: nil, bundle: nil) + super.init { _ -> [UITableViewCell] in + return [ + CLVBackupProgressView.tableViewCell(wrapping: BackupProgressView( + viewState: state, + )), + { + let cell = UITableViewCell() + var content = cell.defaultContentConfiguration() + content.text = "Imagine this is a ChatListCell :)" + cell.contentConfiguration = content + return cell + }(), + ] + } } required init?(coder: NSCoder) { fatalError() } - - override func viewDidLoad() { - super.viewDidLoad() - - let progressView = BackupProgressView(viewState: state) - view.addSubview(progressView) - progressView.autoPinEdgesToSuperviewSafeArea( - with: UIEdgeInsets(margin: 16), - excludingEdge: .bottom, - ) - } } @available(iOS 17, *) diff --git a/SignalUI/Utils/TablePreviewViewController.swift b/SignalUI/Utils/TablePreviewViewController.swift new file mode 100644 index 0000000000..c924946924 --- /dev/null +++ b/SignalUI/Utils/TablePreviewViewController.swift @@ -0,0 +1,59 @@ +// +// Copyright 2025 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +// + +import UIKit + +#if DEBUG + +/// A minimal `UITableViewController` for displaying `UITableViewCell`s in Xcode +/// `#Previews`. +open class TablePreviewViewController: UITableViewController { + + private let cellBlock: (UITableView) -> [UITableViewCell] + private var cells: [UITableViewCell] = [] + + public init( + style: UITableView.Style = .plain, + cellBlock: @escaping (UITableView) -> [UITableViewCell], + ) { + self.cellBlock = cellBlock + super.init(style: style) + } + + @available(*, unavailable) + public required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override public func viewDidLoad() { + super.viewDidLoad() + cells = cellBlock(tableView) + } + + // MARK: UITableViewDataSource + + override public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return cells.count + } + + override public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + return cells[indexPath.row] + } +} + +@available(iOS 17, *) +#Preview { + TablePreviewViewController { _ in + return (0..<5).map { i in + let cell = UITableViewCell() + var content = cell.defaultContentConfiguration() + content.text = "Row \(i)" + cell.contentConfiguration = content + return cell + } + } +} + +#endif