Avatar ·

Как убрать нижнюю линию (1px) у UINavigationBar в iOS

Всем привет! Я новичок в iOS-разработке и столкнулся с такой проблемой: при создании кастомного дизайна приложения мне нужно убрать стандартную серую линию внизу UINavigationBar (ту самую, которая толщиной в 1 пиксель). Я использую Swift, проект под iOS 14+. Я перепробовал несколько способов из интернета, но ни один не сработал до конца. Пробовал установить `shadowImage` в пустой UIImage, но это не помогло. Также пытался менять `backgroundImage` для `barMetrics`, но линия всё равно оставалась. Вот мой текущий код, который не работает: ```swift // В viewDidLoad navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default) navigationController?.navigationBar.shadowImage = UIImage() navigationController?.navigationBar.isTranslucent = false ``` Я использую UINavigationController, встроенный в UITabBarController. Может быть, это как-то влияет? Или я что-то упускаю? Подскажите, пожалуйста, как гарантированно убрать эту нижнюю линию, чтобы она не появлялась ни при каких обстоятельствах — ни при скролле, ни при смене ориентации экрана? Заранее спасибо за помощь! <<>>

Почему стандартный подход может не работать

Проблема, с которой вы столкнулись, очень распространена. Дело в том, что 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.