Как убрать нижнюю линию UINavigationBar: 3 рабочих способа

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: почему эта проклятая линия не даёт спать разработчикам?

Если вы когда-нибудь пытались сделать дизайн приложения «как в Apple Music» или «как в Telegram» — вы наверняка сталкивались с этим. Вы создаёте идеальный UINavigationBar с красивым градиентом, прозрачностью или кастомным цветом, запускаете симулятор — и видите эту тонкую серую или чёрную линию (1 пиксель) внизу. Она выглядит как артефакт, как грязь на стекле. И да, она бесит.

Эта 1px линия — не баг, а фича. Это shadowImage + backgroundImage, которые Apple зачем-то оставила по умолчанию. Даже в iOS 17 она всё ещё появляется, если вы не скажете системе: «Эй, убери это». Вопрос на Stack Overflow по этой теме набрал тысячи просмотров и десятки ответов. Значит, тема реально больная.

В этой статье я покажу 3 рабочих подхода на Swift, как скрыть нижнюю линию UINavigationBar. Мы разберём как старые способы (iOS 13-), так и современные через UINavigationBarAppearance. Поехали!

Способ 1: Классика — через backgroundImage и shadowImage

Самый старый и проверенный метод. Он работает начиная с iOS 5 (да, ещё с тех времён, когда скевоморфизм был в моде). Идея простая: мы устанавливаем пустое изображение в качестве фона и тени.

Шаг 1: Создаём пустое изображение

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

Шаг 2: Применяем к navigationBar

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

Плюсы: Работает везде, даже на старых iOS.
Минусы: Не всегда корректно работает с scrollEdgeAppearance и большими заголовками. Если у вас есть UISearchBar — линия может вернуться.

Способ 2: Современный — через UINavigationBarAppearance (iOS 13+)

Начиная с iOS 13 Apple представила UINavigationBarAppearance. Это единый объект, который управляет всеми состояниями: стандартным, скроллом и компактным. Идеально для тех, кто хочет «один раз настроил — и забыл».

Полный пример для UIViewController

func setupTransparentNavigationBar() {
    let appearance = UINavigationBarAppearance()
    appearance.configureWithTransparentBackground()
    appearance.backgroundColor = .clear
    appearance.shadowColor = .clear  // убирает линию
    
    navigationController?.navigationBar.standardAppearance = appearance
    navigationController?.navigationBar.scrollEdgeAppearance = appearance
    navigationController?.navigationBar.compactAppearance = appearance
}

Обратите внимание: shadowColor = .clear — это ключевая строка. Если её не написать, линия останется. configureWithTransparentBackground() делает фон прозрачным, но не трогает тень.

Если вам нужен цветной фон, но без линии:

let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = .systemBlue
appearance.shadowColor = .clear

navigationController?.navigationBar.standardAppearance = appearance
navigationController?.navigationBar.scrollEdgeAppearance = appearance

Плюсы: Чистый код, легко комбинировать с большими заголовками и поиском.
Минусы: iOS 13+ только. Если проект поддерживает iOS 12 — придётся вернуться к первому способу.

Способ 3: Хардкор — через searchBar и делегаты

Бывает, что линия появляется только при активации UISearchController. Это отдельная боль. Вот решение специально для этого случая.

Прячем линию при активном поиске

class SearchNavController: UINavigationController, UISearchControllerDelegate {
    
    func searchController(_ searchController: UISearchController, 
                          willPresentSearchController: UISearchController) {
        hideBottomLine()
    }
    
    func searchController(_ searchController: UISearchController, 
                          didDismissSearchController: UISearchController) {
        restoreBottomLine()
    }
    
    private func hideBottomLine() {
        let empty = UIImage()
        navigationBar.setBackgroundImage(empty, for: .default)
        navigationBar.shadowImage = empty
    }
    
    private func restoreBottomLine() {
        navigationBar.setBackgroundImage(nil, for: .default)
        navigationBar.shadowImage = nil
    }
}

Плюсы: Тонкий контроль, работает как часы.
Минусы: Много кода, легко забыть восстановить линию после скролла.

Сравнение подходов: что выбрать?

Давайте сведём всё в таблицу (мысленно, но вы поняли):

  • Способ 1 — для legacy-проектов (iOS 12 и ниже). Просто, но может сломаться на iPad с Split View.
  • Способ 2 — современный стандарт. Используйте его, если ваше приложение поддерживает iOS 13+. Это рекомендация Apple.
  • Способ 3 — для экстренных случаев, когда стандартные методы не помогают (например, при кастомном UISearchBar).

Как проверить, что линия исчезла?

Самый простой способ — запустить симулятор и посмотреть на границу между UINavigationBar и view. Если линия всё ещё видна — скорее всего, вы забыли применить appearance ко всем состояниям. Проверьте:

print(navigationController?.navigationBar.standardAppearance.shadowColor)
print(navigationController?.navigationBar.scrollEdgeAppearance?.shadowColor)

Если оба nil или .clear — линия ушла. Если нет — ищите, где переопределяется appearance (например, в UIScrollViewDelegate).

Бонус: Как убрать линию в SwiftUI?

Если вы используете SwiftUI, то NavigationView тоже имеет эту линию. Убрать её можно через .toolbarBackground:

NavigationView {
    ContentView()
        .toolbarBackground(.hidden, for: .navigationBar)
}

Или через UIToolbar.appearance(), если вам нужен глобальный эффект. Но это уже тема для отдельной статьи.

Что делать, если ничего не помогло?

Иногда линия возвращается из-за того, что вы используете кастомный UINavigationController (например, UISplitViewController на iPad).

Блоги

Рекомендаций по книгам