From e452780de434270f795c5eebc486315801953d5e Mon Sep 17 00:00:00 2001 From: Lakr233 Date: Fri, 25 Sep 2026 20:28:50 +0700 Subject: [PATCH 1/2] Process list from edge to edge, headings pinned above it The inset cards boxed a table that is read across its columns. A plain table runs each row the full width, and keeps its section header pinned while the rows scroll, so the column headings stay over the figures they name. The header padding iOS 15 adds above a plain section goes, so the headings sit straight under the search bar, and an empty footer keeps the separators a plain table draws below its last row out from under the overlays. The glass sidebar keeps its clear rows and rounded selection. Co-Authored-By: Claude Opus 5.5 --- Inspector/ProcessListViewController.swift | 32 +++++++++++++++-------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/Inspector/ProcessListViewController.swift b/Inspector/ProcessListViewController.swift index d6f2b9a..ff3c5cb 100644 --- a/Inspector/ProcessListViewController.swift +++ b/Inspector/ProcessListViewController.swift @@ -34,7 +34,10 @@ final class ProcessListViewController: UITableViewController, UISearchResultsUpd init(model: ProcessListModel) { self.model = model - super.init(style: .insetGrouped) + // A plain table runs the rows from edge to edge, and its section + // header stays pinned while they scroll, so the column headings stay + // in sight over whatever is below them. + super.init(style: .plain) } @available(*, unavailable) @@ -64,6 +67,11 @@ final class ProcessListViewController: UITableViewController, UISearchResultsUpd clearsSelectionOnViewWillAppear = false tableView.register(ProcessRowCell.self, forCellReuseIdentifier: ProcessRowCell.reuseIdentifier) tableView.estimatedRowHeight = metrics.rowHeight + // From iOS 15 a plain table pads above every section header; the + // headings sit straight under the search bar instead. + if #available(iOS 15.0, *) { + tableView.sectionHeaderTopPadding = 0 + } dataSource = ProcessListDataSource(tableView: tableView) { [weak self] tableView, indexPath, identity in let cell = tableView.dequeueReusableCell( withIdentifier: ProcessRowCell.reuseIdentifier, @@ -119,9 +127,9 @@ final class ProcessListViewController: UITableViewController, UISearchResultsUpd } } - // From iOS 26 the primary column floats as a glass sidebar, where grouped - // cards read as gray boxes on glass. Beside the detail column the rows go - // bare, like any sidebar; pushed full-screen they keep their cards. + // From iOS 26 the primary column floats as a glass sidebar, where opaque + // rows read as a gray slab on glass. Beside the detail column the rows go + // bare, like any sidebar; pushed full-screen they keep their background. private var usesSidebarAppearance: Bool { guard #available(iOS 26.0, *) else { return false } return splitViewController?.isCollapsed == false @@ -133,16 +141,16 @@ final class ProcessListViewController: UITableViewController, UISearchResultsUpd let isSidebar = usesSidebarAppearance guard isSidebar != appliedSidebarAppearance else { return } appliedSidebarAppearance = isSidebar - tableView.backgroundColor = isSidebar ? .clear : .systemGroupedBackground + tableView.backgroundColor = isSidebar ? .clear : .systemBackground for cell in tableView.visibleCells { style(cell, isSidebar: isSidebar) } } - // A sidebar row has no card, and its selection is a rounded highlight - // rather than a gray slab from edge to edge. + // A sidebar row has no background, and its selection is a rounded + // highlight rather than a gray slab from edge to edge. private func style(_ cell: UITableViewCell, isSidebar: Bool) { - cell.backgroundColor = isSidebar ? .clear : .secondarySystemGroupedBackground + cell.backgroundColor = isSidebar ? .clear : .systemBackground guard isSidebar != (cell.selectedBackgroundView is SidebarSelectionView) else { return } cell.selectedBackgroundView = isSidebar ? SidebarSelectionView() : nil } @@ -190,7 +198,9 @@ final class ProcessListViewController: UITableViewController, UISearchResultsUpd } statsButton.isEnabled = !model.rows.isEmpty renderLiveUpdatesItem() - tableView.tableFooterView = model.rows.isEmpty ? nil : creditsView + // A plain table ruled the empty space below the last row with + // separators, over the overlay too; an empty footer stops them. + tableView.tableFooterView = model.rows.isEmpty ? UIView() : creditsView renderOverlay() } @@ -555,7 +565,7 @@ private final class ProcessListDataSource: UITableViewDiffableDataSource