Skip to content

Fix search bar on iPadOS 26#2435

Closed
dnicolson wants to merge 2 commits intoProvenance-Emu:developfrom
dnicolson:patch-3
Closed

Fix search bar on iPadOS 26#2435
dnicolson wants to merge 2 commits intoProvenance-Emu:developfrom
dnicolson:patch-3

Conversation

@dnicolson
Copy link
Contributor

@dnicolson dnicolson commented Nov 29, 2025

User description

What does this PR do

This adjusts the navigation bar item and search bar placement to fix iPadOS 26 layout issues.

Any background context you want to provide

When built against the iOS 26 SDK, the search bar moves to the bottom on iOS but causes layout issues on iPadOS.

Screenshots (important for UI changes)

iPadOS Before iPadOS After
IMG_0091
IMG_0092
IMG_0093
macOS Before macOS After
Screenshot 2025-11-29 at 09 36 38
Screenshot 2025-11-29 at 09 36 10
Screenshot 2025-11-29 at 09 36 53
iOS Before iOS After
IMG_2115 IMG_2116

PR Type

Bug fix


Description

  • Fixes search bar layout issues on iOS 26 SDK

  • Implements platform-specific navigation bar handling

  • Uses stacked search bar placement for iOS 26+

  • Preserves legacy navigation bar styling for older versions


Diagram Walkthrough

flowchart LR
  A["iOS 26+ Detection"] --> B["Use titleView with imageView"]
  A --> C["Set stacked search bar placement"]
  D["Pre-iOS 26"] --> E["Apply tint to logo image"]
  D --> F["Set leftBarButtonItem with logo"]
  D --> G["Configure tint colors"]
Loading

File Walkthrough

Relevant files
Bug fix
SideMenuView.swift
Add iOS 26 conditional navigation bar handling                     

PVUI/Sources/PVSwiftUI/SideMenu/SideMenuView.swift

  • Added iOS 26 availability check to conditionally handle navigation bar
    setup
  • For iOS 26+: uses titleView with UIImageView and sets
    preferredSearchBarPlacement to .stacked
  • For pre-iOS 26: preserves existing logic with tinted logo image and
    leftBarButtonItem configuration
  • Refactored navigation bar customization into version-specific branches
+15/-10 

@qodo-code-review
Copy link

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
No auditing: The new navigation/title/search bar configuration adds UI state changes without any
accompanying audit logging of critical actions, though this UI context may not require
audit trails.

Referred Code
if #available(iOS 26.0, *) {
    let imageView = UIImageView(image: image)
    vc.navigationItem.titleView = imageView
    vc.navigationItem.preferredSearchBarPlacement = .stacked
} else {
    let menuIconTint = themeManager.currentPalette.menuIconTint

    if menuIconTint != .clear {
            image?.applyTintEffectWithColor(menuIconTint)
    }
    let provenanceLogo = UIBarButtonItem(image: image)
    provenanceLogo.tintColor = themeManager.currentPalette.menuIconTint
    vc.navigationItem.leftBarButtonItem = provenanceLogo
    vc.navigationItem.leftBarButtonItem?.tintColor = menuIconTint
    vc.navigationController?.navigationBar.tintColor = menuIconTint
}

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Missing guards: New code assumes image loading succeeds and that navigation components exist without
handling nil cases or providing fallback behavior.

Referred Code
let image = UIImage(named: "provnavicon", in: PVUIBase.BundleLoader.myBundle, with: nil)
if #available(iOS 26.0, *) {
    let imageView = UIImageView(image: image)
    vc.navigationItem.titleView = imageView
    vc.navigationItem.preferredSearchBarPlacement = .stacked
} else {
    let menuIconTint = themeManager.currentPalette.menuIconTint

    if menuIconTint != .clear {
            image?.applyTintEffectWithColor(menuIconTint)
    }
    let provenanceLogo = UIBarButtonItem(image: image)
    provenanceLogo.tintColor = themeManager.currentPalette.menuIconTint
    vc.navigationItem.leftBarButtonItem = provenanceLogo
    vc.navigationItem.leftBarButtonItem?.tintColor = menuIconTint
    vc.navigationController?.navigationBar.tintColor = menuIconTint
}

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Consider platform-specific code for UI

Add a device-specific check (UIDevice.current.userInterfaceIdiom == .pad) to the
existing iOS version check. This will ensure the new navigation bar layout is
applied only to iPadOS, preserving the original layout on iPhones.

Examples:

PVUI/Sources/PVSwiftUI/SideMenu/SideMenuView.swift [410-425]
            if #available(iOS 26.0, *) {
                let imageView = UIImageView(image: image)
                vc.navigationItem.titleView = imageView
                vc.navigationItem.preferredSearchBarPlacement = .stacked
            } else {
                let menuIconTint = themeManager.currentPalette.menuIconTint

                if menuIconTint != .clear {
                        image?.applyTintEffectWithColor(menuIconTint)
                }

 ... (clipped 6 lines)

Solution Walkthrough:

Before:

.introspectViewController(customize: { vc in
    let image = UIImage(named: "provnavicon", ...)

    if #available(iOS 26.0, *) {
        // This block applies to both iPhone and iPad on iOS 26+
        let imageView = UIImageView(image: image)
        vc.navigationItem.titleView = imageView
        vc.navigationItem.preferredSearchBarPlacement = .stacked
    } else {
        // Legacy code for older iOS versions
        let provenanceLogo = UIBarButtonItem(image: image)
        vc.navigationItem.leftBarButtonItem = provenanceLogo
        ...
    }
})

After:

.introspectViewController(customize: { vc in
    let image = UIImage(named: "provnavicon", ...)

    // Check for both iOS version and device type (idiom)
    if #available(iOS 26.0, *), UIDevice.current.userInterfaceIdiom == .pad {
        // This block now only applies to iPad on iOS 26+
        let imageView = UIImageView(image: image)
        vc.navigationItem.titleView = imageView
        vc.navigationItem.preferredSearchBarPlacement = .stacked
    } else {
        // Legacy code used for iPhones and older iOS versions
        let provenanceLogo = UIBarButtonItem(image: image)
        vc.navigationItem.leftBarButtonItem = provenanceLogo
        ...
    }
})
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies that a fix intended for iPadOS introduces a significant UI change on iPhone, and proposes a valid platform-specific check to decouple the behaviors, which is a critical design consideration.

Medium
Possible issue
Apply missing theme tint color

Move the icon tinting logic before the if #available block to ensure it's
applied consistently across all iOS versions, fixing a missing tint on iOS 26+.

PVUI/Sources/PVSwiftUI/SideMenu/SideMenuView.swift [410-414]

+let menuIconTint = themeManager.currentPalette.menuIconTint
+if menuIconTint != .clear {
+    image?.applyTintEffectWithColor(menuIconTint)
+}
+
 if #available(iOS 26.0, *) {
     let imageView = UIImageView(image: image)
     vc.navigationItem.titleView = imageView
     vc.navigationItem.preferredSearchBarPlacement = .stacked
 } else {
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a visual bug where the navigation icon tint is not applied on iOS 26+, and the proposed fix correctly resolves this inconsistency.

Medium
General
Prevent navigation icon distortion

Set the contentMode of the UIImageView to .scaleAspectFit to prevent the
navigation icon from being distorted.

PVUI/Sources/PVSwiftUI/SideMenu/SideMenuView.swift [411-412]

 let imageView = UIImageView(image: image)
+imageView.contentMode = .scaleAspectFit
 vc.navigationItem.titleView = imageView
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: The suggestion improves UI robustness by setting the contentMode on the UIImageView, which is a good practice to prevent potential image distortion in the navigation bar.

Low
  • More

@JoeMatt
Copy link
Member

JoeMatt commented Mar 7, 2026

@claude pleaase review this ticket

@github-actions
Copy link
Contributor

github-actions bot commented Mar 7, 2026

✅ Fixes Applied

Completed

  • 🚀 Agent started
  • 📖 Read PR context (branch: patch-3 from dnicolson/Provenance)
  • 🔨 Fix 1: menuIconTint now applied in iOS 26+ path (imageView.tintColor)
  • 🔨 Fix 3: Added imageView.contentMode = .scaleAspectFit
  • 📤 Pushed to fork branch patch-3
  • 💬 Posted summary comment on PR

Concern 2 (iPhone vs iPad)

Not changed — iOS 26 path retained for both iPhone and iPad as screenshots confirm the titleView approach improves iPhone styling by removing the liquid glass edge artefact.

Completed: 2026-03-07 00:24 UTC

Copilot AI review requested due to automatic review settings March 7, 2026 00:08
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 7, 2026

Code Review — Fix search bar on iPadOS 26

Thanks for the fix! The approach is sound and the screenshots clearly show the improvement. Here's my analysis:

✅ What's Good

  • Correctly identifies the iOS 26 SDK search bar behaviour change
  • Uses preferredSearchBarPlacement = .stacked which is the right API to restore the stacked layout on iPadOS 26
  • Clean #available guard
  • Screenshots confirm the layout is fixed on iPadOS, macOS, and iOS

⚠️ Issues to Address

1. Theme tint missing on iOS 26+ (Medium — visual bug)

The applyTintEffectWithColor and tintColor assignments are inside the else branch, so the Provenance logo will render in its original colours on iOS 26+ instead of the configured menuIconTint.

To fix, apply the tint before the version branch, or add it to the iOS 26 image view:

let image = UIImage(named: "provnavicon", in: PVUIBase.BundleLoader.myBundle, with: nil)
let menuIconTint = themeManager.currentPalette.menuIconTint

// Apply tint regardless of iOS version
if menuIconTint != .clear {
    image?.applyTintEffectWithColor(menuIconTint)
}

if #available(iOS 26.0, *) {
    let imageView = UIImageView(image: image)
    imageView.contentMode = .scaleAspectFit
    imageView.tintColor = menuIconTint   // also set on imageView
    vc.navigationItem.titleView = imageView
    vc.navigationItem.preferredSearchBarPlacement = .stacked
} else {
    // ... existing code
}

2. iPhone affected unnecessarily (Medium — unintended behaviour change)

The #available(iOS 26.0, *) check applies to all iOS 26 devices, not just iPad. On iPhone iOS 26, this replaces the left-aligned logo (leftBarButtonItem) with a centred titleView logo. The PR screenshots show iPhone Before/After look similar, but the logo alignment does change.

Since the issue title is "iPadOS 26", consider scoping the iOS 26 path to iPad only:

if #available(iOS 26.0, *), UIDevice.current.userInterfaceIdiom == .pad {
    // iPad-specific fix
} else {
    // existing path for iPhone (all iOS) and iPad pre-iOS 26
}

If the titleView approach is intentional for iPhone too (e.g. to get consistent logo sizing in iOS 26), please clarify that in the PR description.

3. Missing contentMode (Minor)

UIImageView defaults to .scaleToFill, which can distort the logo if the available title-view frame doesn't match the image aspect ratio. Add:

imageView.contentMode = .scaleAspectFit

Summary

# Severity Item
1 Medium Theme tint not applied on iOS 26+ path
2 Medium iPhone unintentionally affected by iOS 26 branch
3 Minor Missing contentMode = .scaleAspectFit

The core idea is correct — happy to approve once the tint regression and iPad-only scoping are addressed (or the iPhone behaviour change is confirmed intentional).

@JoeMatt
Copy link
Member

JoeMatt commented Mar 7, 2026

Code Review — Fix search bar on iPadOS 26

Thanks for the fix! The approach is sound and the screenshots clearly show the improvement. Here's my analysis:

✅ What's Good

  • Correctly identifies the iOS 26 SDK search bar behaviour change
  • Uses preferredSearchBarPlacement = .stacked which is the right API to restore the stacked layout on iPadOS 26
  • Clean #available guard
  • Screenshots confirm the layout is fixed on iPadOS, macOS, and iOS

⚠️ Issues to Address

1. Theme tint missing on iOS 26+ (Medium — visual bug)

The applyTintEffectWithColor and tintColor assignments are inside the else branch, so the Provenance logo will render in its original colours on iOS 26+ instead of the configured menuIconTint.

To fix, apply the tint before the version branch, or add it to the iOS 26 image view:

let image = UIImage(named: "provnavicon", in: PVUIBase.BundleLoader.myBundle, with: nil)
let menuIconTint = themeManager.currentPalette.menuIconTint

// Apply tint regardless of iOS version
if menuIconTint != .clear {
    image?.applyTintEffectWithColor(menuIconTint)
}

if #available(iOS 26.0, *) {
    let imageView = UIImageView(image: image)
    imageView.contentMode = .scaleAspectFit
    imageView.tintColor = menuIconTint   // also set on imageView
    vc.navigationItem.titleView = imageView
    vc.navigationItem.preferredSearchBarPlacement = .stacked
} else {
    // ... existing code
}

2. iPhone affected unnecessarily (Medium — unintended behaviour change)

The #available(iOS 26.0, *) check applies to all iOS 26 devices, not just iPad. On iPhone iOS 26, this replaces the left-aligned logo (leftBarButtonItem) with a centred titleView logo. The PR screenshots show iPhone Before/After look similar, but the logo alignment does change.

Since the issue title is "iPadOS 26", consider scoping the iOS 26 path to iPad only:

if #available(iOS 26.0, *), UIDevice.current.userInterfaceIdiom == .pad {
    // iPad-specific fix
} else {
    // existing path for iPhone (all iOS) and iPad pre-iOS 26
}

If the titleView approach is intentional for iPhone too (e.g. to get consistent logo sizing in iOS 26), please clarify that in the PR description.

3. Missing contentMode (Minor)

UIImageView defaults to .scaleToFill, which can distort the logo if the available title-view frame doesn't match the image aspect ratio. Add:

imageView.contentMode = .scaleAspectFit

Summary

Severity Item

1 Medium Theme tint not applied on iOS 26+ path
2 Medium iPhone unintentionally affected by iOS 26 branch
3 Minor Missing contentMode = .scaleAspectFit
The core idea is correct — happy to approve once the tint regression and iPad-only scoping are addressed (or the iPhone behaviour change is confirmed intentional).

@claude fix concern 1 and 3, for 2, the screenshots for ios do show an improvement in the styling though, it removes the weird liquid glass edge, so reconsider if we need an ios/ipad distinction or different fix for this perceived alignment issue (it could just be the screenshot cropping making it look off center?)

@github-actions
Copy link
Contributor

github-actions bot commented Mar 7, 2026

Fixes Applied

Addressed concerns 1 and 3 from the review. Concern 2 (iPhone vs iPad scope) was reconsidered — the screenshots show the iOS 26 path actually improves styling on iPhone too by removing the liquid glass edge artefact, so the unified #available(iOS 26.0, *) path is intentional and retained.

Changes in dcb46a2

Fix 1 — Theme tint now applied on iOS 26+

  • Moved let menuIconTint = themeManager.currentPalette.menuIconTint above the #available check so it's available in both branches
  • Added imageView.tintColor = menuIconTint to the iOS 26 path so the Provenance logo renders in the configured tint colour

Fix 3 — Added contentMode = .scaleAspectFit

  • Added imageView.contentMode = .scaleAspectFit in the iOS 26 branch to prevent logo distortion when the title-view frame doesn't match the image aspect ratio

Concern 2 — iPhone/iPad distinction not added

  • The #available(iOS 26.0, *) check intentionally covers both iPhone and iPad: the titleView approach on iPhone removes the liquid glass edge artefact visible in the before screenshots. If this proves problematic on iPhone in practice, the scope can be narrowed later.

@JoeMatt JoeMatt self-assigned this Mar 7, 2026
@JoeMatt JoeMatt removed the request for review from jasarien March 7, 2026 00:41
@JoeMatt JoeMatt enabled auto-merge March 7, 2026 00:41
dnicolson and others added 2 commits March 6, 2026 19:45
- Move menuIconTint extraction before #available block so it is
  available in both paths
- Set imageView.tintColor = menuIconTint on iOS 26+ path to fix
  missing theme tint regression
- Add imageView.contentMode = .scaleAspectFit to prevent logo
  distortion on iOS 26+
- Retain unified iOS 26 path for iPhone and iPad (screenshots confirm
  improved styling removes liquid glass edge artefact on both)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@JoeMatt JoeMatt self-requested a review March 7, 2026 00:46
@JoeMatt
Copy link
Member

JoeMatt commented Mar 7, 2026

Hi @dnicolson! This PR has a rebase conflict with develop — both your change and a recent develop commit (dd507b35 — "side menu ios 26 layout fix") modified SideMenuView.swift.

To unblock, please rebase your branch:

git fetch upstream
git checkout patch-3
git rebase upstream/develop
# resolve any conflicts in SideMenuView.swift, then:
git push --force-with-lease

(If you don't have upstream configured: git remote add upstream https://github.com/Provenance-Emu/Provenance.git)

The CI smoke build failure is also now fixed on our end — the ZipArchive submodule clone issue has been resolved in develop.

JoeMatt added a commit that referenced this pull request Mar 7, 2026
On iOS 26 the search bar was overlapping the nav icon. Use
preferredSearchBarPlacement = .stacked and set the logo via titleView
instead of leftBarButtonItem.

Original fix by @dnicolson in PR #2435.

Co-Authored-By: dnicolson <dnicolson@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@JoeMatt
Copy link
Member

JoeMatt commented Mar 7, 2026

Thanks @dnicolson! Applied your fix directly to develop in 2ca0c82 since the branch had a rebase conflict we couldn't auto-resolve (fork branch). The search bar fix is now live.

@JoeMatt JoeMatt closed this Mar 7, 2026
auto-merge was automatically disabled March 7, 2026 01:36

Pull request was closed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants