Skip to content

Commit adc0522

Browse files
committed
Bookmarks filter buttons needed same iPad/MacOS sidebar fix to fix title layout and button colours
1 parent cd3cf66 commit adc0522

2 files changed

Lines changed: 124 additions & 28 deletions

File tree

App/Navigation/NavigationController.swift

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ private struct SidebarButtonView: View {
4040
private struct SidebarImageButtonView: View {
4141
let image: Image
4242
let accessibilityLabel: String?
43+
var pointSize: CGFloat = 20
44+
var horizontalPadding: CGFloat = 2
4345
let action: () -> Void
4446

4547
@SwiftUI.Environment(\.theme) private var theme
@@ -48,9 +50,14 @@ private struct SidebarImageButtonView: View {
4850
let color = theme[color: "navigationBarTextColor"] ?? .white
4951
Button(action: action) {
5052
image
53+
.resizable()
54+
.scaledToFit()
55+
.frame(width: pointSize, height: pointSize)
5156
.foregroundStyle(color)
5257
}
5358
.buttonStyle(.plain)
59+
.frame(width: pointSize, height: pointSize)
60+
.padding(.horizontal, horizontalPadding)
5461
.glassEffect(.identity)
5562
.accessibilityLabel(accessibilityLabel ?? "")
5663
}
@@ -95,6 +102,8 @@ final class SidebarTitleView: UIView {
95102
.font(.system(size: 17, weight: .semibold))
96103
.applyFontDesign(if: useRoundedFont)
97104
.foregroundStyle(swiftUIColor)
105+
.lineLimit(1)
106+
.truncationMode(.tail)
98107
.glassEffect(.identity)
99108

100109
let hosting = UIHostingController(rootView: AnyView(content))
@@ -555,10 +564,32 @@ final class NavigationController: UINavigationController, Themeable {
555564
target: AnyObject?,
556565
action: Selector?
557566
) -> UIBarButtonItem {
567+
let hostingView = Self.makeSidebarImageHostingView(
568+
image: image,
569+
accessibilityLabel: accessibilityLabel,
570+
target: target,
571+
action: action
572+
)
573+
return UIBarButtonItem(customView: hostingView)
574+
}
575+
576+
/// Builds a UIHostingController-hosted view wrapping `SidebarImageButtonView`
577+
/// for use as a `UIBarButtonItem.customView`. Callers that need direct
578+
/// access to the hosting view (e.g. to animate alpha or anchor a popover)
579+
/// can use this directly instead of `makeImageBarButtonItem`.
580+
@available(iOS 26.0, *)
581+
static func makeSidebarImageHostingView(
582+
image: UIImage,
583+
accessibilityLabel: String?,
584+
pointSize: CGFloat = 20,
585+
target: AnyObject?,
586+
action: Selector?
587+
) -> UIView {
558588
let swiftUIImage = Image(uiImage: image.withRenderingMode(.alwaysTemplate))
559589
let content = SidebarImageButtonView(
560590
image: swiftUIImage,
561-
accessibilityLabel: accessibilityLabel
591+
accessibilityLabel: accessibilityLabel,
592+
pointSize: pointSize
562593
) {
563594
if let target = target as? NSObject, let action {
564595
target.perform(action, with: nil)
@@ -567,9 +598,17 @@ final class NavigationController: UINavigationController, Themeable {
567598

568599
let hosting = UIHostingController(rootView: AnyView(content))
569600
hosting.view.backgroundColor = .clear
570-
let size = hosting.sizeThatFits(in: CGSize(width: CGFloat.greatestFiniteMagnitude, height: 44))
571-
hosting.view.frame = CGRect(origin: .zero, size: size)
572-
return UIBarButtonItem(customView: hosting.view)
601+
hosting.view.translatesAutoresizingMaskIntoConstraints = false
602+
// Enforce a tight size (image + small horizontal padding) so the
603+
// bar button item doesn't reserve the hosting view's natural
604+
// (often larger) fitting size.
605+
let totalWidth = pointSize + 4 // 2pt padding on each side
606+
NSLayoutConstraint.activate([
607+
hosting.view.widthAnchor.constraint(equalToConstant: totalWidth),
608+
hosting.view.heightAnchor.constraint(equalToConstant: pointSize),
609+
])
610+
hosting.view.frame = CGRect(x: 0, y: 0, width: totalWidth, height: pointSize)
611+
return hosting.view
573612
}
574613

575614
/// Configures button appearance attributes for iOS 26 liquid glass compatibility.

App/View Controllers/Threads/BookmarksTableViewController.swift

Lines changed: 81 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -588,9 +588,9 @@ final class BookmarksTableViewController: TableViewController {
588588

589589
private var currentFilter: BookmarkFilter = .all
590590
private var filterButton: UIBarButtonItem!
591-
private var filterButtonView: UIButton!
591+
private var filterButtonView: UIButton?
592592
private var searchButton: UIBarButtonItem!
593-
private var searchButtonView: UIButton!
593+
private var searchButtonView: UIButton?
594594
private var searchBar: UISearchBar!
595595
private var searchBarContainerView: UIView!
596596
private var isSearchVisible = false
@@ -614,36 +614,92 @@ final class BookmarksTableViewController: TableViewController {
614614
setupFilterButton()
615615
setupSearchButton()
616616
setupSearchBar()
617-
navigationItem.leftBarButtonItem = editButtonItem
618-
navigationItem.rightBarButtonItems = [filterButton, searchButton]
617+
618+
if #available(iOS 26.0, *),
619+
UIDevice.current.userInterfaceIdiom == .pad,
620+
let filterHosting = filterButton.customView,
621+
let searchHosting = searchButton.customView {
622+
// Combine the two right-side icons into a single customView so
623+
// UINavigationBar's inter-item spacing doesn't apply between
624+
// them — they end up crowded together with only the HStack
625+
// spacing in between, giving the title more room.
626+
let stack = UIStackView(arrangedSubviews: [searchHosting, filterHosting])
627+
stack.axis = .horizontal
628+
stack.spacing = 4
629+
stack.alignment = .center
630+
navigationItem.rightBarButtonItems = [UIBarButtonItem(customView: stack)]
631+
632+
// Balance the right side so the title lands near the nav-bar
633+
// center. Use a customView spacer (UINavigationBar honors an
634+
// explicit customView width more reliably than .fixedSpace).
635+
let spacer = UIBarButtonItem(customView: UIView(frame: CGRect(x: 0, y: 0, width: 72, height: 44)))
636+
navigationItem.leftBarButtonItems = [editButtonItem, spacer]
637+
} else {
638+
navigationItem.leftBarButtonItem = editButtonItem
639+
navigationItem.rightBarButtonItems = [filterButton, searchButton]
640+
}
619641

620642
themeDidChange()
621643
}
622644

623645
private func setupFilterButton() {
624-
filterButtonView = UIButton(type: .system)
625-
filterButtonView.setImage(UIImage(systemName: "line.3.horizontal.decrease"), for: .normal)
626-
filterButtonView.accessibilityLabel = LocalizedString("bookmarks.filter.button.accessibility-label")
627-
filterButtonView.addTarget(self, action: #selector(filterButtonTapped), for: .touchUpInside)
646+
let label = LocalizedString("bookmarks.filter.button.accessibility-label")
647+
648+
if #available(iOS 26.0, *),
649+
UIDevice.current.userInterfaceIdiom == .pad,
650+
let image = UIImage(systemName: "line.3.horizontal.decrease") {
651+
// iPad sidebar on iOS 26 renders bar buttons inside a glass panel,
652+
// which tints UIButton images via vibrancy. Use the SwiftUI
653+
// `.glassEffect(.identity)` path to bypass that compositing so
654+
// the theme's navigationBarTextColor is preserved.
655+
let hosting = NavigationController.makeSidebarImageHostingView(
656+
image: image,
657+
accessibilityLabel: label,
658+
target: self,
659+
action: #selector(filterButtonTapped)
660+
)
661+
filterButton = UIBarButtonItem(customView: hosting)
662+
} else {
663+
let button = UIButton(type: .system)
664+
button.setImage(UIImage(systemName: "line.3.horizontal.decrease"), for: .normal)
665+
button.accessibilityLabel = label
666+
button.addTarget(self, action: #selector(filterButtonTapped), for: .touchUpInside)
628667

629-
if #available(iOS 26.0, *) {
630-
filterButtonView.tintAdjustmentMode = .normal
631-
}
668+
if #available(iOS 26.0, *) {
669+
button.tintAdjustmentMode = .normal
670+
}
632671

633-
filterButton = UIBarButtonItem(customView: filterButtonView)
672+
filterButtonView = button
673+
filterButton = UIBarButtonItem(customView: button)
674+
}
634675
}
635-
676+
636677
private func setupSearchButton() {
637-
searchButtonView = UIButton(type: .system)
638-
searchButtonView.setImage(UIImage(named: "quick-look"), for: .normal)
639-
searchButtonView.accessibilityLabel = LocalizedString("bookmarks.search.button.accessibility-label")
640-
searchButtonView.addTarget(self, action: #selector(searchButtonTapped), for: .touchUpInside)
678+
let label = LocalizedString("bookmarks.search.button.accessibility-label")
679+
680+
if #available(iOS 26.0, *),
681+
UIDevice.current.userInterfaceIdiom == .pad,
682+
let image = UIImage(named: "quick-look") {
683+
let hosting = NavigationController.makeSidebarImageHostingView(
684+
image: image,
685+
accessibilityLabel: label,
686+
target: self,
687+
action: #selector(searchButtonTapped)
688+
)
689+
searchButton = UIBarButtonItem(customView: hosting)
690+
} else {
691+
let button = UIButton(type: .system)
692+
button.setImage(UIImage(named: "quick-look"), for: .normal)
693+
button.accessibilityLabel = label
694+
button.addTarget(self, action: #selector(searchButtonTapped), for: .touchUpInside)
641695

642-
if #available(iOS 26.0, *) {
643-
searchButtonView.tintAdjustmentMode = .normal
644-
}
696+
if #available(iOS 26.0, *) {
697+
button.tintAdjustmentMode = .normal
698+
}
645699

646-
searchButton = UIBarButtonItem(customView: searchButtonView)
700+
searchButtonView = button
701+
searchButton = UIBarButtonItem(customView: button)
702+
}
647703
}
648704

649705
private func setupSearchBar() {
@@ -697,8 +753,9 @@ final class BookmarksTableViewController: TableViewController {
697753
filterMenuVC.modalTransitionStyle = .crossDissolve
698754
filterMenuVC.presentationController?.delegate = self
699755

700-
if filterButtonView.superview != nil {
701-
let buttonFrameInView = view.convert(filterButtonView.bounds, from: filterButtonView)
756+
let anchor: UIView? = filterButtonView ?? filterButton?.customView
757+
if let anchor, anchor.superview != nil {
758+
let buttonFrameInView = view.convert(anchor.bounds, from: anchor)
702759
filterMenuVC.setSourceButtonRect(buttonFrameInView)
703760
}
704761

@@ -782,7 +839,7 @@ final class BookmarksTableViewController: TableViewController {
782839
case .textSearch(_):
783840
isFilterActive = true
784841
}
785-
842+
786843
let isFilterMenuOpen = filterPopoverController != nil
787844

788845
if #available(iOS 26.0, *) {

0 commit comments

Comments
 (0)