UINavigationBar滚动隐藏异常:切换场景后状态异常求助
Hey there! Let’s tackle that tricky navigation bar issue you’re facing. I see you’ve been trying to hide/show the UINavigationBar by tweaking its frame and subview alphas during scroll, but glitches pop up when switching controllers or re-launching the app. Let’s break down what’s going wrong and fix it properly.
What’s Causing the Glitches?
The core problem here is that you’re directly manipulating the navigation bar’s frame and relying on hardcoded internal subview indices (like subviews[2]). UIKit’s internal view hierarchy can change across iOS versions, and the UINavigationController has its own built-in layout logic that clashes with manual frame edits. When you switch controllers or restart the app, the system resets the navigation bar’s state, which conflicts with your custom adjustments—leading to those weird visual bugs.
A Reliable Solution
Instead of hacking the navigation bar’s internal structure, use UIKit-friendly methods like CGAffineTransform for smooth animations, and sync the navigation bar’s state during controller lifecycle events to avoid cross-controller glitches. Here’s an improved implementation:
class GDMainViewController: UIViewController, UIScrollViewDelegate { // Track scroll state private var lastContentOffset: CGFloat = 0 // Navigation bar constants private let navBarHeight: CGFloat = 44 private let statusBarHeight = UIApplication.shared.statusBarFrame.height override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // Reset nav bar state when entering the view to avoid carryover glitches resetNavigationBar() } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) // Restore nav bar before leaving, so other controllers aren't affected resetNavigationBar() } private func resetNavigationBar() { guard let navigationBar = navigationController?.navigationBar else { return } UIView.animate(withDuration: 0.3) { navigationBar.transform = .identity navigationBar.alpha = 1.0 // Reset all visible subviews (buttons, title, etc.) navigationBar.subviews.forEach { subview in if !subview.isHidden && !(subview is UIImageView) { // Skip background views subview.alpha = 1.0 } } } } func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { lastContentOffset = scrollView.contentOffset.y // Dismiss keyboard if url field is active if urlField.isFirstResponder { urlField.resignFirstResponder() } } func scrollViewDidScroll(_ scrollView: UIScrollView) { guard let navigationBar = navigationController?.navigationBar else { return } let currentOffset = scrollView.contentOffset.y let deltaY = currentOffset - lastContentOffset // Calculate desired navigation bar translation var newTranslationY = navigationBar.transform.ty if deltaY > 0 { // Upward scroll: hide nav bar newTranslationY = max(newTranslationY - deltaY, -navBarHeight) } else { // Downward scroll: show nav bar newTranslationY = min(newTranslationY - deltaY, 0) } // Apply smooth transform and alpha fade let visibilityProgress = abs(newTranslationY) / navBarHeight navigationBar.transform = CGAffineTransform(translationX: 0, y: newTranslationY) navigationBar.alpha = 1 - (visibilityProgress * 0.8) // Soft fade for background // Fade nav bar content (buttons, title) navigationBar.subviews.forEach { subview in if !subview.isHidden && !(subview is UIImageView) { subview.alpha = 1 - visibilityProgress } } lastContentOffset = currentOffset } func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { guard let navigationBar = navigationController?.navigationBar else { return } if !decelerate { // Auto-snap to fully hidden or visible state let targetTranslationY: CGFloat = navigationBar.transform.ty < -navBarHeight/2 ? -navBarHeight : 0 UIView.animate(withDuration: 0.3) { navigationBar.transform = CGAffineTransform(translationX: 0, y: targetTranslationY) let progress = abs(targetTranslationY) / self.navBarHeight navigationBar.alpha = 1 - (progress * 0.8) navigationBar.subviews.forEach { subview in if !subview.isHidden && !(subview is UIImageView) { subview.alpha = 1 - progress } } } } } }
Key Improvements
- No internal subview hacks: Instead of relying on
subviews[2], we filter subviews by type to avoid breaking on iOS updates. - Transform over frame edits: Using
CGAffineTransformis UIKit’s recommended way to animate views, avoiding conflicts with the navigation controller’s layout system. - Lifecycle sync: Resetting the navigation bar in
viewWillAppearandviewWillDisappearensures no state leaks to other controllers or persists after app restarts. - Smoother animations: The progress-based alpha fade and snap-to-state animation match system-level behavior for a more polished feel.
内容的提问来源于stack exchange,提问作者Raffi

