Das Erscheinungsbild der Bildlaufkante auf UITabBar funktioniert nicht beim Versuch, UITabBarController mit einem benutzIOS

Programmierung für iOS
Anonymous
 Das Erscheinungsbild der Bildlaufkante auf UITabBar funktioniert nicht beim Versuch, UITabBarController mit einem benutz

Post by Anonymous »

Hinweis: Das Folgende ist etwas lang, aber es gibt einfach keine Möglichkeit, den Code weiter zu reduzieren und das Problem trotzdem zu veranschaulichen.

Aus Gründen, auf die ich nicht näher eingehen werde, erstelle ich meine eigene Implementierung von UITabBarController. Dies ist eine UIViewController-Unterklasse. Ich füge unten eine UITabBar hinzu und je nachdem, welche Registerkarte ausgewählt ist, zeige ich den entsprechenden Ansichtscontroller an, genau wie UITabBarController. Meine Anforderungen sind weitaus komplexer, aber ich habe diese Frage und ihren Code auf ein Problem eingegrenzt, das ich bei meinem Versuch habe, das Standardverhalten zu reproduzieren.
In meiner Test-App erstelle ich meinen benutzerdefinierten Tab-Bar-Controller mit einigen Tabs, die jeweils aus einem UINavigationController mit einer Unterklasse eines Table View Controllers als Root-View-Controller bestehen. Es funktioniert alles, außer dass ich beim Scrollen zum unteren Rand der Tabellenansicht nicht den Effekt des Erscheinungsbilds der Bildlaufkante erhalte. Unter iOS 18 oder früher wird mit einem standardmäßigen UITabBarController die Tab-Leiste klar (wie die Navigationsleiste), wenn Sie zum Ende der Tabelle scrollen.
Unter iOS 26 ist das Problem ähnlich, aber etwas anders. In meiner benutzerdefinierten Implementierung erhalte ich nicht den Soft-Fade-Effekt für die Zeilen, die hinter der Tab-Leiste angezeigt werden, wie bei einem Standard-UITabBarController.
Meine Frage läuft darauf hinaus, was in meinem Code fehlt, damit der Bildlaufkanteneffekt (iOS 18) / Soft-Fade-Effekt (iOS 26) wie in einem Standard-UITabBarController funktioniert?
Das Folgende ist ein Teil des Codes für die Einstellung die UITabBar in meiner benutzerdefinierten Tab-Bar-Controller-Klasse einrichten. Vollständig funktionsfähiger Code erscheint weiter unten.

Code: Select all

let tabBar = UITabBar()

private func setupTabBar() {
let std = UITabBarAppearance()
std.configureWithDefaultBackground()
tabBar.standardAppearance = std

// This appearance is never triggered.  What is needed to make this work?
let se = UITabBarAppearance()
se.configureWithTransparentBackground()
tabBar.scrollEdgeAppearance = se

tabBar.delegate = self
tabBar.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tabBar)
NSLayoutConstraint.activate([
tabBar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tabBar.trailingAnchor.constraint(equalTo: view.trailingAnchor),
])
if #available(iOS 26.0, *) {
// Under iOS 26 this is now nice and simple - or at least is seems to be.
tabBar.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
} else {
// Under iOS 18 and earlier, this is the only way I could get the tab bar to
// appear correctly and fully fill-in the safe area.
tabBar.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
}
}
Um eine funktionierende Test-App zu erstellen, um den obigen Code nützlich zu machen, erstellen Sie ein neues iOS-App-Projekt basierend auf einer Storyboard-Schnittstelle und Swift mit Xcode 16 oder Xcode 26.
Fügen Sie eine neue Datei mit dem Namen MyTabBarController.swift hinzu und fügen Sie den folgenden vollständigen Code in die neue Datei ein:

Code: Select all

import UIKit

class MyTabBarController: UIViewController {
let tabBar = UITabBar()

var viewControllers: [UIViewController] {
get {
_viewControllers
}
set {
setViewControllers(newValue, animated: false)
}
}

// Assumes there will be at least one controller provided - crashes if there are none - fine for my needs
func setViewControllers(_ viewControllers: [UIViewController], animated: Bool) {
// Cleanup the currently selected view controller, if any.
if !_viewControllers.isEmpty {
removeViewController(_viewControllers[selectedIndex])
}

_viewControllers = viewControllers
selectedIndex = 0

// Update the tab bar
tabBar.setItems(viewControllers.map({ $0.tabBarItem }), animated:animated)
tabBar.selectedItem = tabBar.items?.first

//
addViewController(viewControllers[selectedIndex])
}

init() {
super.init(nibName: nil, bundle: nil)
}

required init?(coder: NSCoder) {
super.init(coder: coder)
}

private var selectedIndex = 0
private var _viewControllers = [UIViewController]()

private func removeViewController(_ controller: UIViewController?) {
if let controller {
controller.willMove(toParent: nil)
controller.view.removeFromSuperview()
controller.removeFromParent()
}
}

private func addViewController(_ controller: UIViewController?) {
if let controller {
addChild(controller)
controller.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(controller.view)
NSLayoutConstraint.activate([
controller.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
controller.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
controller.view.topAnchor.constraint(equalTo: view.topAnchor),
controller.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
controller.didMove(toParent: self)

accountForTabBar()

// Ensure the tab bar stays on top
view.bringSubviewToFront(tabBar)
}
}

private func selectViewController(at index: Int) {
// Swap out the current view controller with the newly selected one
removeViewController(_viewControllers[selectedIndex])

selectedIndex = index
tabBar.selectedItem = tabBar.items?[index] // This is needed under iOS 26 otherwise the selected tab is gets reset to 0

let controller = _viewControllers[index]
addViewController(controller)
}

private func accountForTabBar() {
// Make sure the tab bar has been added and there is at least one controller
guard tabBar.superview != nil &&  !_viewControllers.isEmpty else { return }

// Ensure the current view controller's bottom safe area is adjusted to account for the tab bar
let controller = _viewControllers[selectedIndex];
// I don't like the need for a different adjustment as of iOS 26.
// Does this suggest a [url=viewtopic.php?t=26065]problem[/url] with my code?
if #available(iOS 26.0, *) {
controller.additionalSafeAreaInsets = .init(top: 0, left: 0, bottom: tabBar.frame.height - view.safeAreaInsets.bottom, right: 0)
} else {
controller.additionalSafeAreaInsets = .init(top: 0, left: 0, bottom: tabBar.frame.height, right: 0)
}
}

private func setupTabBar() {
let std = UITabBarAppearance()
std.configureWithDefaultBackground()
tabBar.standardAppearance = std

// This appearance is never triggered.  What is needed to make this work?
let se = UITabBarAppearance()
se.configureWithTransparentBackground()
tabBar.scrollEdgeAppearance = se

tabBar.delegate = self
tabBar.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tabBar)
NSLayoutConstraint.activate([
tabBar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tabBar.trailingAnchor.constraint(equalTo: view.trailingAnchor),
])
if #available(iOS 26.0, *) {
// Under iOS 26 this is now nice and simple - or at least is seems to be.
tabBar.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
} else {
// Under iOS 18 and earlier, this is the only way I could get the tab bar to
// appear correctly and fully fill-in the safe area.
tabBar.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
}
}

override func viewDidLoad() {
super.viewDidLoad()

setupTabBar()
}

override func viewSafeAreaInsetsDidChange() {
super.viewSafeAreaInsetsDidChange()

accountForTabBar() // Update to reflect new safe area
}

override func viewIsAppearing(_ animated: Bool) {
super.viewIsAppearing(animated)

accountForTabBar() // Ensure initial view controller is adjusted properly
}
}

extension MyTabBarController: UITabBarDelegate {
func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
// User tapped on a tab item - update which view controller is being shown
let index = tabBar.items?.firstIndex(of: item) ?? 0
selectViewController(at: index)
}
}
Fügen Sie eine neue Datei mit dem Namen TableViewController.swift hinzu und fügen Sie den folgenden Code ein:

Code: Select all

import UIKit

// Show 20 simple rows to test scroll edge functionality with the tab bar
class TableViewController: UITableViewController {
init() {
super.init(style: .plain)

self.title = "Table"
tabBarItem.image = UIImage(systemName: "list.bullet")
navigationItem.rightBarButtonItem = editButtonItem
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func viewDidLoad() {
super.viewDidLoad()

view.backgroundColor = .systemGreen
}
}

extension TableViewController {
override func numberOfSections(in tableView: UITableView) -> Int {
1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
20
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") ?? UITableViewCell(style: .default, reuseIdentifier: "cell")
cell.textLabel?.text = "Row \(indexPath.row + 1)"
return cell
}

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath)
}
}
Ersetzen Sie abschließend den Inhalt von ViewController.swift durch den folgenden Code:

Code: Select all

import UIKit

// Change this to extend UITabBarController to see the difference in behavior
class ViewController: MyTabBarController {
override func viewDidLoad() {
super.viewDidLoad()

// Just as an example - show a bunch of tabs
viewControllers = [
UINavigationController(rootViewController: TableViewController()),
UINavigationController(rootViewController: TableViewController()),
UINavigationController(rootViewController: TableViewController()),
UINavigationController(rootViewController: TableViewController()),
]
}
}
Stellen Sie sicher, dass das Bereitstellungsziel des Projekts iOS 18 (oder sogar iOS 15, 16 oder 17) ist. Erstellen Sie die App und führen Sie sie auf einem iPhone mit iOS 18 (oder iOS 26) aus (echt oder simuliert). Sie sehen eine Tab-Leiste mit vier identischen Tabs. Jede Registerkarte verfügt über eine Tabellenansicht mit 20 Zeilen.
iOS 18:
Scrollen Sie auf einer der Registerkarten nach unten, damit Sie die gesamte Zeile 20 sehen können. Beachten Sie, dass die Registerkartenleiste nicht klar wird. Vergleichen Sie damit, wie sich die Navigationsleiste ändert, wenn Sie nach oben scrollen (die gesamte Zeile 1 ist sichtbar).
iOS 26:
Beachten Sie, dass Zeilen hinter der Tab-Leiste überhaupt nicht ausgeblendet werden. Scrollen Sie ein wenig nach unten und bemerken Sie, wie die Zeilen hinter der Navigationsleiste ausgeblendet werden. Dasselbe sollte mit Zeilen hinter der Tab-Leiste passieren.
Bearbeiten Sie zum Vergleich ViewController.swift so, dass die ViewController-Klasse UITabBarController anstelle von MyTabBarController erweitert. Erstellen Sie den Build und führen Sie ihn erneut aus. Beachten Sie dabei, wie sich die Registerkartenleiste verhält. Ich versuche, dasselbe Verhalten in der Tab-Leiste zu reproduzieren.
Ich weiß nur nicht, welcher Code in MyTabBarController fehlt, um seine UITabBar einzurichten. Was macht UITabBarController, was ich nicht tue? Ich habe SO, die Apple-Entwicklerforen und GitHub durchsucht und bisher kein Beispiel gefunden, das diese Funktionalität anspricht.

Hier sind eine Reihe von Bildern, die unterschiedliche Ergebnisse zeigen.

Code: Select all

ViewController
erweitert UITabBarController, iOS 18, Tab-Leiste zeigt Standarddarstellung:
Image

Code: Select all

ViewController
erweitert UITabBarController, iOS 18, Tab-Leiste zeigt das Erscheinungsbild der Bildlaufkante (die Tab-Leiste ist klar, so dass das vollständige Grün der Tabellenansicht angezeigt wird):
Image

Code: Select all

ViewController
erweitert MyTabBarController, iOS 18, Tab-Leiste zeigt Standarddarstellung (das funktioniert einwandfrei):
Image

Code: Select all

ViewController
erweitert MyTabBarController, iOS 18, Tab-Leiste zeigt fälschlicherweise immer noch das Standard-Erscheinungsbild (das teilweise durchscheinend ist und nur einen Hauch von Grün aus der Tabellenansicht zulässt):
Image

Code: Select all

ViewController
erweitert UITabBarController, iOS 26, Tab-Leiste mit Standarddarstellung und ausgeblendeten Zeilen hinter der Tab-Leiste (geändert auf 3 statt 4 Tabs, um den Effekt besser zu sehen):
Image

Code: Select all

ViewController
erweitert MyTabBarController, iOS 26, Tab-Leiste zeigt Standarddarstellung, aber die Zeilen werden nicht hinter der Tab-Leiste ausgeblendet:
Image

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post