Create SidebarViewController, a UICollectionViewController subclass, to replace the UITableViewController subclass previously in use. This will get us the modern sidebar appearance.

This commit is contained in:
Brent Simmons
2025-02-02 21:43:10 -08:00
parent c2ff8b1ff5
commit 85f25556f9
5 changed files with 98 additions and 29 deletions

View File

@@ -136,6 +136,7 @@ extension AppImage {
extension AppImage {
#if os(iOS)
static var allUnread = systemImage("largecircle.fill.circle")
static var articleExtractorOffSF = systemImage("doc.plaintext")
static var articleExtractorOnSF = appImage("articleExtractorOnSF")
static var articleExtractorOffTinted = articleExtractorOff.tinted(color: AppColor.accent)!
@@ -158,7 +159,9 @@ extension AppImage {
static var previousArticle = systemImage("chevron.up")
static var safari = systemImage("safari")
static var settings = systemImage("gear")
static var starred = systemImage("star.fill")
static var timelineStar = systemImage("star.fill").withTintColor(AppColor.star, renderingMode: .alwaysOriginal)
static var today = systemImage("sun.max.fill")
static var trash = systemImage("trash")
// IconImages
@@ -167,20 +170,9 @@ extension AppImage {
// TODO: handle color palette change
static var starredFeed: IconImage = {
let image = systemImage("star.fill")
return IconImage(image, isSymbol: true, isBackgroundSuppressed: true, preferredColor: AppColor.star.cgColor)
}()
static var todayFeed: IconImage = {
let image = systemImage("sun.max.fill")
return IconImage(image, isSymbol: true, isBackgroundSuppressed: true, preferredColor: UIColor.systemOrange.cgColor)
}()
static var unreadFeed: IconImage = {
let image = systemImage("largecircle.fill.circle")
return IconImage(image, isSymbol: true, isBackgroundSuppressed: true, preferredColor: AppColor.secondaryAccent.cgColor)
}()
static var starredFeed = IconImage(starred, isSymbol: true, isBackgroundSuppressed: true, preferredColor: AppColor.star.cgColor)
static var todayFeed = IconImage(today, isSymbol: true, isBackgroundSuppressed: true, preferredColor: UIColor.systemOrange.cgColor)
static var unreadFeed = IconImage(allUnread, isSymbol: true, isBackgroundSuppressed: true, preferredColor: AppColor.secondaryAccent.cgColor)
static var folder: IconImage = {
let image = systemImage("folder.fill")

View File

@@ -14,7 +14,7 @@ final class MainWindowController {
let window: UIWindow
let rootSplitViewController: RootSplitViewController
let sidebarViewController = MainFeedViewController()
let sidebarViewController = SidebarViewController()
let timelineViewController = TimelineViewController()
let articleViewController = ArticleViewController()

View File

@@ -13,17 +13,17 @@ final class RootSplitViewController: UISplitViewController {
var coordinator: SceneCoordinator! {
didSet {
sidebarViewController.coordinator = coordinator
// sidebarViewController.coordinator = coordinator
timelineViewController.coordinator = coordinator
articleViewController.coordinator = coordinator
}
}
private let sidebarViewController: MainFeedViewController
private let sidebarViewController: SidebarViewController
private let timelineViewController: TimelineViewController
private let articleViewController: ArticleViewController
init(sidebarViewController: MainFeedViewController,
init(sidebarViewController: SidebarViewController,
timelineViewController: TimelineViewController,
articleViewController: ArticleViewController) {

View File

@@ -50,7 +50,7 @@ final class SceneCoordinator: NSObject, UndoableCommandRunner {
private var rootSplitViewController: RootSplitViewController!
private var mainFeedViewController: MainFeedViewController!
private var mainFeedViewController: MainFeedViewController?
private var mainTimelineViewController: TimelineViewController?
private var articleViewController: ArticleViewController?
@@ -279,7 +279,7 @@ final class SceneCoordinator: NSObject, UndoableCommandRunner {
super.init()
self.mainFeedViewController = rootSplitViewController.viewController(for: .primary) as? MainFeedViewController
self.mainFeedViewController.coordinator = self
self.mainFeedViewController?.coordinator = self
self.mainFeedViewController?.navigationController?.delegate = self
self.mainTimelineViewController = rootSplitViewController.viewController(for: .supplementary) as? TimelineViewController
@@ -741,7 +741,7 @@ final class SceneCoordinator: NSObject, UndoableCommandRunner {
}
currentFeedIndexPath = indexPath
mainFeedViewController.updateFeedSelection(animations: animations)
mainFeedViewController?.updateFeedSelection(animations: animations)
if deselectArticle {
selectArticle(nil)
@@ -1168,14 +1168,14 @@ final class SceneCoordinator: NSObject, UndoableCommandRunner {
addNavViewController.modalPresentationStyle = .formSheet
addNavViewController.preferredContentSize = AddFeedViewController.preferredContentSizeForFormSheetDisplay
mainFeedViewController.present(addNavViewController, animated: true)
mainFeedViewController?.present(addNavViewController, animated: true)
}
func showAddFolder() {
let addNavViewController = UIStoryboard.add.instantiateViewController(withIdentifier: "AddFolderViewControllerNav") as! UINavigationController
addNavViewController.modalPresentationStyle = .formSheet
addNavViewController.preferredContentSize = AddFolderViewController.preferredContentSizeForFormSheetDisplay
mainFeedViewController.present(addNavViewController, animated: true)
mainFeedViewController?.present(addNavViewController, animated: true)
}
func showFullScreenImage(image: UIImage, imageTitle: String?, transitioningDelegate: UIViewControllerTransitioningDelegate) {
@@ -1223,7 +1223,7 @@ final class SceneCoordinator: NSObject, UndoableCommandRunner {
if currentArticle != nil {
articleViewController?.openInAppBrowser()
} else {
mainFeedViewController.openInAppBrowser()
mainFeedViewController?.openInAppBrowser()
}
}
@@ -1270,7 +1270,7 @@ final class SceneCoordinator: NSObject, UndoableCommandRunner {
/// `SFSafariViewController` or `SettingsViewController`,
/// otherwise, this function does nothing.
func dismissIfLaunchingFromExternalAction() {
guard let presentedController = mainFeedViewController.presentedViewController else { return }
guard let presentedController = mainFeedViewController?.presentedViewController else { return }
if presentedController.isKind(of: SFSafariViewController.self) {
presentedController.dismiss(animated: true, completion: nil)
@@ -1462,7 +1462,7 @@ private extension SceneCoordinator {
updateExpandedNodes?()
let changes = rebuildShadowTable()
mainFeedViewController.reloadFeeds(initialLoad: initialLoad, changes: changes, completion: completion)
mainFeedViewController?.reloadFeeds(initialLoad: initialLoad, changes: changes, completion: completion)
}
}
@@ -2072,7 +2072,7 @@ private extension SceneCoordinator {
self.treeControllerDelegate.resetFilterExceptions()
if let indexPath = self.indexPathFor(smartFeed) {
self.selectFeed(indexPath: indexPath) {
self.mainFeedViewController.focus()
self.mainFeedViewController?.focus()
}
}
})
@@ -2093,7 +2093,7 @@ private extension SceneCoordinator {
if let folderNode = self.findFolderNode(folderName: folderName, beginningAt: accountNode), let indexPath = self.indexPathFor(folderNode) {
self.selectFeed(indexPath: indexPath) {
self.mainFeedViewController.focus()
self.mainFeedViewController?.focus()
}
}
})
@@ -2106,7 +2106,7 @@ private extension SceneCoordinator {
}
self.discloseFeed(feed, initialLoad: true) {
self.mainFeedViewController.focus()
self.mainFeedViewController?.focus()
}
}
}

View File

@@ -0,0 +1,77 @@
//
// SidebarViewController.swift
// NetNewsWire-iOS
//
// Created by Brent Simmons on 2/2/25.
// Copyright © 2025 Ranchero Software. All rights reserved.
//
import Foundation
import UIKit
final class SidebarViewController: UICollectionViewController {
enum Section {
case smartFeeds
}
struct SidebarItem: Hashable, Identifiable {
let id: UUID = UUID()
let title: String
let icon: UIImage?
}
typealias DataSource = UICollectionViewDiffableDataSource<Section, SidebarViewController.SidebarItem>
private lazy var dataSource = createDataSource()
init() {
super.init(collectionViewLayout: Self.createSidebarLayout())
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
applySnapshot()
}
}
private extension SidebarViewController {
static func createSidebarLayout() -> UICollectionViewLayout {
let configuration = UICollectionLayoutListConfiguration(appearance: .sidebar)
return UICollectionViewCompositionalLayout.list(using: configuration)
}
private func createDataSource() -> DataSource {
let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, SidebarItem> { (cell, indexPath, item) in
var content = UIListContentConfiguration.cell()
content.text = item.title
content.image = item.icon
cell.contentConfiguration = content
}
dataSource = UICollectionViewDiffableDataSource<Section, SidebarItem>(collectionView: collectionView) { (collectionView, indexPath, item) in
return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: item)
}
return dataSource
}
private func applySnapshot() {
var snapshot = NSDiffableDataSourceSnapshot<Section, SidebarItem>()
snapshot.appendSections([.smartFeeds])
snapshot.appendItems([
SidebarItem(title: "Today", icon: AppImage.today),
SidebarItem(title: "All Unread", icon: AppImage.allUnread),
SidebarItem(title: "Starred", icon: AppImage.starred)
])
dataSource.apply(snapshot, animatingDifferences: true)
}
}