Как убрать нижнюю линию (1px) у UINavigationBar в iOS
Почему стандартный подход может не работать
Проблема, с которой вы столкнулись, очень распространена. Дело в том, что UINavigationBar имеет сложную иерархию вложенных представлений, и нижняя линия (тень) может быть не просто shadowImage, а отдельным UIImageView или CALayer. Кроме того, начиная с iOS 13, Apple изменила работу с внешним видом навигационной панели, и старые методы перестали быть надёжными.
Вот три проверенных способа, которые гарантированно работают в iOS 13+ (и обратно совместимы с более старыми версиями).
Способ 1: Использование UINavigationBarAppearance (рекомендуемый для iOS 13+)
Это самый современный и правильный способ. Создаём кастомный объект UINavigationBarAppearance, настраиваем его и применяем ко всем состояниям панели.
// В viewDidLoad вашего UIViewController
if #available(iOS 13.0, *) {
let appearance = UINavigationBarAppearance()
// Убираем нижнюю линию
appearance.shadowColor = .clear
// (Опционально) Задаём цвет фона, иначе он станет прозрачным
appearance.backgroundColor = .white // или ваш кастомный цвет
// Применяем ко всем состояниям
navigationController?.navigationBar.standardAppearance = appearance
navigationController?.navigationBar.scrollEdgeAppearance = appearance
navigationController?.navigationBar.compactAppearance = appearance
} else {
// Fallback для iOS 12 и ниже
navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
navigationController?.navigationBar.shadowImage = UIImage()
}
Важно: Если вы используете UIScrollView или UITableView с большим заголовком (prefersLargeTitles = true), обязательно настройте и scrollEdgeAppearance. Без него при скролле вверх линия всё равно появится.
Способ 2: Полное удаление через поиск в иерархии (работает на всех iOS)
Если по какой-то причине первый способ не сработал (например, вы используете сторонние библиотеки, которые переопределяют внешний вид), можно найти и удалить линию напрямую через обход subviews.
extension UINavigationBar {
func hideBottomShadow() {
// Ищем UIImageView, который является тенью
for view in self.subviews {
// У стандартной линии высота около 0.5-1 пикселя
if view.frame.height <= 1.0 {
view.isHidden = true
view.alpha = 0
}
}
}
}
// Использование:
navigationController?.navigationBar.hideBottomShadow()
Минус: Этот метод менее надёжен, так как Apple может изменить иерархию в будущих версиях iOS. Используйте его только как запасной вариант.
Способ 3: Работа с UIToolbar (если линия идёт от TabBar)
Вы упомянули, что используете UITabBarController. Иногда нижняя линия может принадлежать не UINavigationBar, а самому UITabBar. Попробуйте убрать тень и у него:
// Убираем линию у TabBar
tabBarController?.tabBar.shadowImage = UIImage()
tabBarController?.tabBar.backgroundImage = UIImage()
Проверка результата
После применения любого из способов рекомендую запустить приложение на реальном устройстве или симуляторе с разными версиями iOS. Иногда в Xcode Preview линия всё ещё отображается, но на устройстве её нет.
Если ни один из способов не помог — проверьте, не переопределяете ли вы UINavigationBar через UIAppearance в другом месте проекта (например, в AppDelegate).
Альтернативный подход: полная кастомизация через подкласс UINavigationBar
Если вам нужно полное управление внешним видом навигационной панели и вы хотите избежать любых неожиданных линий, создайте свой подкласс UINavigationBar и переопределите метод layoutSubviews.
class CustomNavigationBar: UINavigationBar {
override func layoutSubviews() {
super.layoutSubviews()
// Прячем все тени и линии
for view in self.subviews {
if let imageView = view as? UIImageView,
imageView.frame.height <= 1.0 {
imageView.isHidden = true
}
}
}
}
// В AppDelegate или при создании UINavigationController:
let navController = UINavigationController(navigationBarClass: CustomNavigationBar.self, toolbarClass: nil)
Плюсы: Работает для всех экранов в приложении, не требует повторного кода в каждом UIViewController. Линия гарантированно исчезнет, даже если iOS обновит внутреннюю структуру.
Минусы: Требует изменения архитектуры, если вы уже используете стандартный UINavigationController из Storyboard. В этом случае придётся пересоздавать его программно или менять класс в Storyboard.
Этот метод особенно полезен, если вы разрабатываете библиотеку или SDK, где нужно гарантировать единообразное поведение на всех устройствах и версиях iOS.