How to Remove the Bottom Line of UINavigationBar: 3 Working Methods

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: Why Does This Damned Line Keep Developers Up at Night?

\n\n

If you've ever tried to make an app design "like Apple Music" or "like Telegram" — you've definitely encountered this. You create a perfect UINavigationBar with a beautiful gradient, transparency, or a custom color, launch the simulator — and you see this thin gray or black line (1 pixel) at the bottom. It looks like an artifact, like dirt on glass. And yes, it's annoying.

\n\n

This 1px line is not a bug, it's a feature. It's the shadowImage + backgroundImage that Apple left by default for some reason. Even in iOS 17 it still appears if you don't tell the system: "Hey, remove this." The Stack Overflow question on this topic has thousands of views and dozens of answers. So, it's a real pain point.

\n\n

In this article, I'll show you 3 working approaches in Swift to hide the bottom line of UINavigationBar. We'll cover both old methods (iOS 13-) and modern ones using UINavigationBarAppearance. Let's go!

\n\n

Method 1: Classic — via backgroundImage and shadowImage

\n\n

The oldest and most proven method. It works starting from iOS 5 (yes, back when skeuomorphism was in fashion). The idea is simple: we set an empty image as the background and shadow.

\n\n

Step 1: Create an empty image

\n\n
extension UIImage {\n    static func pixelImage(withColor color: UIColor = .clear) -> UIImage {\n        let rect = CGRect(x: 0, y: 0, width: 1, height: 1)\n        UIGraphicsBeginImageContext(rect.size)\n        color.setFill()\n        UIRectFill(rect)\n        let image = UIGraphicsGetImageFromCurrentImageContext()\n        UIGraphicsEndImageContext()\n        return image ?? UIImage()\n    }\n}
\n\n

Step 2: Apply to navigationBar

\n\n
func hideBottomLine() {\n    let emptyImage = UIImage.pixelImage()\n    navigationController?.navigationBar.setBackgroundImage(emptyImage, for: .default)\n    navigationController?.navigationBar.shadowImage = emptyImage\n}
\n\n

Pros: Works everywhere, even on older iOS versions.
\nCons: Doesn't always work correctly with scrollEdgeAppearance and large titles. If you have a UISearchBar — the line may return.

\n\n

Method 2: Modern — via UINavigationBarAppearance (iOS 13+)

\n\n

Starting with iOS 13, Apple introduced UINavigationBarAppearance. This is a single object that manages all states: standard, scroll, and compact. Ideal for those who want to "set it once and forget it."

\n\n

Full example for UIViewController

\n\n
func setupTransparentNavigationBar() {\n    let appearance = UINavigationBarAppearance()\n    appearance.configureWithTransparentBackground()\n    appearance.backgroundColor = .clear\n    appearance.shadowColor = .clear  // removes the line\n    \n    navigationController?.navigationBar.standardAppearance = appearance\n    navigationController?.navigationBar.scrollEdgeAppearance = appearance\n    navigationController?.navigationBar.compactAppearance = appearance\n}
\n\n

Note: shadowColor = .clear is the key line. If you don't write it, the line will remain. configureWithTransparentBackground() makes the background transparent but doesn't touch the shadow.

\n\n

If you need a colored background but without the line:

\n\n
let appearance = UINavigationBarAppearance()\nappearance.configureWithOpaqueBackground()\nappearance.backgroundColor = .systemBlue\nappearance.shadowColor = .clear\n\nnavigationController?.navigationBar.standardAppearance = appearance\nnavigationController?.navigationBar.scrollEdgeAppearance = appearance
\n\n

Pros: Clean code, easy to combine with large titles and search.
\nCons: iOS 13+ only. If the project supports iOS 12 — you'll have to

Blogs

Book Recommendations