Как скрыть нижнюю строку UINavigationBar 1px


443

У меня есть приложение, которому иногда нужна навигационная панель, чтобы гармонировать с контентом.

Кто-нибудь знает, как избавиться или изменить цвет этой надоедливой маленькой планки?

На изображении ниже ситуация у меня есть - я говорю об этой линии высоты 1px ниже "Root View Controller"

введите описание изображения здесь


просмотреть это: stackoverflow.com/questions/19101361/…
Kuldeep

как увеличить 1px высоты навигации?
Суреш Дуришетти

Ответы:


738

Для iOS 13:

Используйте .shadowColorсобственность

Если это свойство равно нулю или содержит чистый цвет, на панели не отображается тень

Например:

let navigationBar = navigationController?.navigationBar
let navigationBarAppearence = UINavigationBarAppearance()
navigationBarAppearence.shadowColor = .clear
navigationBar?.scrollEdgeAppearance = navigationBarAppearence

Для iOS 12 и ниже:

Для этого вам необходимо установить собственное теневое изображение. Но для отображения теневого изображения вам также необходимо установить собственное фоновое изображение, цитата из документации Apple:

Чтобы отображалось пользовательское теневое изображение, пользовательское фоновое изображение также должно быть установлено с помощью метода setBackgroundImage (_: for :). Если используется фоновое изображение по умолчанию, то теневое изображение по умолчанию будет использоваться независимо от значения этого свойства.

Так:

let navigationBar = navigationController!.navigationBar
navigationBar.setBackgroundImage(#imageLiteral(resourceName: "BarBackground"),
                                                        for: .default)
navigationBar.shadowImage = UIImage()

Выше - единственный «официальный» способ скрыть это. К сожалению, это устраняет прозрачность бара.

Я не хочу фоновое изображение, просто цвет

У вас есть эти варианты:

  1. Сплошной цвет, нет прозрачности:

    navigationBar.barTintColor = UIColor.redColor()
    navigationBar.isTranslucent = false
    navigationBar.setBackgroundImage(UIImage(), for: .default)
    navigationBar.shadowImage = UIImage()
  2. Создайте небольшое фоновое изображение, заполненное цветом и используйте его.

  3. Используйте «хакерский» метод, описанный ниже. Это также будет держать бар прозрачным.

Как сохранить бар прозрачным?

Чтобы сохранить прозрачность, вам нужен другой подход, он выглядит как хак, но работает хорошо. Тень, которую мы пытаемся удалить, это UIImageViewгде-то линия волос UINavigationBar. Мы можем найти его и спрятать / показать, когда это необходимо.

Инструкции ниже предполагают, что вам нужно скрыть волосковую линию только в одном контроллере вашего UINavigationController иерархии.

  1. Объявите переменную экземпляра:

    private var shadowImageView: UIImageView?
  2. Добавить метод, который находит эту тень (линия роста волос) UIImageView:

    private func findShadowImage(under view: UIView) -> UIImageView? {
        if view is UIImageView && view.bounds.size.height <= 1 {
            return (view as! UIImageView)
        }
    
        for subview in view.subviews {
            if let imageView = findShadowImage(under: subview) {
                return imageView
            }
        }
        return nil
    }
  3. Добавить / редактировать viewWillAppear/viewWillDisappearметоды:

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
    
        if shadowImageView == nil {
            shadowImageView = findShadowImage(under: navigationController!.navigationBar)
        }
        shadowImageView?.isHidden = true
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
    
        shadowImageView?.isHidden = false
    }

Тот же метод должен также работать для UISearchBar волосяного покрова и (почти) всего, что вам нужно скрыть :)

Большое спасибо @Leo Natan за оригинальную идею!


104
Вы также можете просто установить вид UINavigationBarсобственности:clipsToBounds = YES
cleverbit

3
@richarddas clipsToBounds = YESработает как шарм! Спасибо!
Ромеоальд

3
ClipsToBounds не всегда работает для некоторых макетов. Этот ответ работал идеально для меня. Только я создал подкласс UINavigationBar и использовал приведенный выше код, чтобы скрыть теневое изображение в методах -layoutSubview. Спасибо!
cgossain

8
Просто чтобы заметить, у меня была проблема, когда моя верхняя строка состояния iOS стала полупрозрачной, и я мог видеть мое табличное представление, прокручивающееся позади UINavigationBar. Я исправил это, установив setTranslucent = NO.
Влад

2
В iOS 10 кажется, что когда viewWillAppear вызывается, мы не можем получитьshadowImageView
xi.lin

241

Ниже может помочь просто!

Swift:

self.navigationController?.navigationBar.setValue(true, forKey: "hidesShadow")

Цель C:

[self.navigationController.navigationBar setValue:@(YES) forKeyPath:@"hidesShadow"];

6
Я на Xcode 8 и разработчик для> iOS 8, и ничего из вышеперечисленного не сработало для меня, кроме этого.
Vakas

2
Также на XCode 8 и> iOS 8. Это единственный ответ, который работал для меня.
облачно

1
Это единственный короткий и простой ответ, который работал для меня для iOS 10 и Xcode 8.3.1. Спасибо чувак.
Вишал Сонавейн

1
Задача-C: self.navigationController.navigationBar.shadowImage = [UIImage new];
Марсело душ Сантуш

1
Только у меня это работает в iOS 13.3.1 версии iPad.
Рави

145

Если вы просто хотите использовать сплошной цвет панели навигации и настроили это в своей раскадровке, используйте этот код в своем AppDelegateклассе, чтобы удалить границу в 1 пиксель через прокси внешнего вида:

[[UINavigationBar appearance] setBackgroundImage:[[UIImage alloc] init]
                                  forBarPosition:UIBarPositionAny
                                      barMetrics:UIBarMetricsDefault];

[[UINavigationBar appearance] setShadowImage:[[UIImage alloc] init]];

2
Это устанавливает его глобально для КАЖДОЙ панели навигации в вашей кодовой базе ... в большинстве случаев не то, что вы хотите.
Кристоф

97

Попробуй это:

[[UINavigationBar appearance] setBackgroundImage: [UIImage new]  
                                   forBarMetrics: UIBarMetricsDefault];

[UINavigationBar appearance].shadowImage = [UIImage new];

Ниже изображение имеет объяснение (iOS7 NavigationBar):

введите описание изображения здесь

И проверьте этот вопрос так: iOS7 - изменить цвет границы UINavigationBar


Как сказал Сергей, для принятия теневого изображения необходимо установить пользовательское фоновое изображение.
акашивский

3
сделайте это в вашем AppDelegate.
myusuf3

Отличный ответ ... Спас мой день _ / \ _
Акшай

@RPM Отлично работает в iOS 7,8,9,10. Тот же код для навигационной панели viewControlles в среде для Swift.
Ахрамеев

64

Хотел добавить версию ответа Сергея Свифта. Я создал UIBarExtension.swiftсо следующим:

import Foundation
import UIKit

extension UINavigationBar {
    func hideBottomHairline() {
        self.hairlineImageView?.isHidden = true
    }

    func showBottomHairline() {
        self.hairlineImageView?.isHidden = false
    }
}

extension UIToolbar {
    func hideBottomHairline() {
        self.hairlineImageView?.isHidden = true
    }

    func showBottomHairline() {
        self.hairlineImageView?.isHidden = false
    }
}

extension UIView {
    fileprivate var hairlineImageView: UIImageView? {
        return hairlineImageView(in: self)
    }

    fileprivate func hairlineImageView(in view: UIView) -> UIImageView? {
        if let imageView = view as? UIImageView, imageView.bounds.height <= 1.0 {
            return imageView
        }

        for subview in view.subviews {
            if let imageView = self.hairlineImageView(in: subview) { return imageView }
        }

        return nil
    }
}

Спасибо! Этот должен быть ответом.
PeiweiChen

Он работает с навигационной панелью, но не работает с uitoolbar
TomSawyer

Я нахожу с iOS 12, это больше не работает. :(.
Крис Принс,


Этот должен быть ответом. работает как шарм в IOS 13
COVID19

63

Быстрый способ сделать это:

UINavigationBar.appearance().setBackgroundImage(
    UIImage(),
    forBarPosition: .Any,
    barMetrics: .Default)

UINavigationBar.appearance().shadowImage = UIImage()

2
Это довольно редко, чтобы получить это далеко внизу списка, прежде чем найти самый гладкий ответ. +1!
sudo make установить

3
Он скрывает весь фон UINavigationBar tho: /
user365314

Неправильно, он тоже скрывает весь фон!
TomSawyer

3
Примечание: вы должны установить навигационный бар isTranslucent в false
dmathewwws

19

Простое решение в быстром

   let navigationBar = self.navigationController?.navigationBar
    navigationBar?.setBackgroundImage(UIImage(), forBarPosition: UIBarPosition.Any, barMetrics: UIBarMetrics.Default)
    navigationBar?.shadowImage = UIImage()

3
+1 Из всех ответов это то, что в итоге сработало для меня. Он не испортил строку состояния, как другие ответы, и он решает проблему изменения только одной из панелей контроллера навигации, а не всех панелей навигации в проекте.
JoeGalind

Версия Swift 4.2: navigationBar? .SetBackgroundImage (UIImage (), для: UIBarPosition.any, barMetrics: UIBarMetrics.default) navigationBar? .ShadowImage = UIImage ()
airowe

16

Изучив ответ от Serhil, я создал модуль UINavigationBar + Addition, который легко скрывает линию волос.

#import "UINavigationBar+Addition.h"

- (void)viewDidLoad {
    [super viewDidLoad];

    UINavigationBar *navigationBar = self.navigationController.navigationBar;
    [navigationBar hideBottomHairline];
}

16

В Swift 3.0

Отредактируйте ваш AppDelegate.swift, добавив следующий код в вашу функцию приложения:

// Override point for customization after application launch.

// Remove border in navigationBar
UINavigationBar.appearance().shadowImage = UIImage()
UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .default)

16

Начиная с iOS 13 есть системный API для установки или удаления тени.

UIKit использует shadowImage и свойство shadowColor для определения внешнего вида тени. Когда shadowImage равно nil, на панели отображается тень по умолчанию, тонированная в соответствии со значением в свойстве shadowColor. Если shadowColor равен нулю или содержит цвет clearColor, на панели не отображается тень.

    let appearance = UINavigationBarAppearance()
    appearance.shadowImage = nil
    appearance.shadowColor = nil
    navigationController.navigationBar.standardAppearance = appearance

https://developer.apple.com/documentation/uikit/uibarappearance/3198009-shadowimage


shadowImage и shadowColor не задокументированы в UINavigationBarAppearanceзаголовках !! Я бы никогда не нашел это. Спасибо, это решило мои проблемы на 100%
Роджер Оба

1
Да, у меня тоже хорошо работает в iOS 13 большое спасибо @glotcha.
Йогеш Патель

@ Глотча Верно ....
Дигвиджай

Спасибо. единственное, что у меня
сработало

13

Может также быть скрыт от раскадровки (работает на Xcode 10.1)

Добавив атрибут времени выполнения: hidesShadow - Boolean - True

введите описание изображения здесь


11

Решение pxpgraphics обновлено для Swift 2.0

extension UINavigationBar {

    func hideBottomHairline()
    {
        hairlineImageViewInNavigationBar(self)?.hidden = true
    }

    func showBottomHairline()
    {
        hairlineImageViewInNavigationBar(self)?.hidden = false
    }

    private func hairlineImageViewInNavigationBar(view: UIView) -> UIImageView?
    {
        if let imageView = view as? UIImageView where imageView.bounds.height <= 1
        {
            return imageView
        }

        for subview: UIView in view.subviews
        {
            if let imageView = hairlineImageViewInNavigationBar(subview)
            {
                return imageView
            }
        }

        return nil
    }

}

extension UIToolbar
{

    func hideHairline()
    {
        let navigationBarImageView = hairlineImageViewInToolbar(self)?.hidden = true
    }

    func showHairline()
    {
        let navigationBarImageView = hairlineImageViewInToolbar(self)?.hidden = false
    }

    private func hairlineImageViewInToolbar(view: UIView) -> UIImageView?
    {
        if let imageView = view as? UIImageView where imageView.bounds.height <= 1
        {
            return imageView
        }

        for subview: UIView in view.subviews
        {
            if let imageView = hairlineImageViewInToolbar(subview)
            {
                return imageView
            }
        }

        return nil
    }

}

Привет, новичок в iOS и пытаюсь учить себя. Как мне использовать расширение? У меня есть файл контроллера представления, и я помещаю эти расширения в него вне области видимости класса. Но тогда как мне вызвать hide / showHairline ()? Не совсем понимаю, как использовать расширения, но они используются для очень многих решений, но я просто не понимаю, как они реализованы в реальном коде
Tommy K

Расширения добавляют функциональность вашим существующим классам. Любой расширенный класс, структура, перечисление и т. Д. Будут иметь эти новые функции, доступные вам, когда вам нужно их использовать. Подробнее здесь: developer.apple.com/library/ios/documentation/Swift/Conceptual/…
tf.alves

хм, так что я должен просто быть в состоянии использовать, UINavigationController().navigationBar/toolbar.hide/showBottomHairline()то нет? Я пытаюсь это, но безрезультатно. Я не правильно понимаю?
Томми К


10

Я использую расширение UINavigationBar, которое позволяет мне скрывать / показывать эту тень с помощью UIAppearance API или выбирать, какая панель навигации должна скрывать / показывать эту тень, используя раскадровку (или исходный код). Вот расширение:

import UIKit

private var flatAssociatedObjectKey: UInt8 = 0

/*
  An extension that adds a "flat" field to UINavigationBar. This flag, when
  enabled, removes the shadow under the navigation bar.
 */
@IBDesignable extension UINavigationBar {
    @IBInspectable var flat: Bool {
        get {
            guard let obj = objc_getAssociatedObject(self, &flatAssociatedObjectKey) as? NSNumber else {
                return false
            }
            return obj.boolValue;
        }

        set {
            if (newValue) {
                let void = UIImage()
                setBackgroundImage(void, forBarPosition: .Any, barMetrics: .Default)
                shadowImage = void
            } else {
                setBackgroundImage(nil, forBarPosition: .Any, barMetrics: .Default)
                shadowImage = nil
            }
            objc_setAssociatedObject(self, &flatAssociatedObjectKey, NSNumber(bool: newValue),
                    objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
}

Теперь, чтобы отключить тень на всех панелях навигации, вы должны использовать:

UINavigationBar.appearance().flat = true

Или вы можете включить / отключить это поведение, используя раскадровки:

Раскадровка панели навигации


@ NunoGonçalves, вы можете думать flatAssociatedObjectKeyкак уникальный int (трактуется как указатель) для идентификации вашего связанного объекта. Здесь определяется адрес памяти приватной переменной. Я обновил ответ, чтобы добавить эту вар. Смотрите nshipster.com/associated-objects для получения дополнительной информации.
Alvivi

Хорошее решение, но работает, только если для панели навигации установлено translucentзначение false
ZYiOS

9

Swift 4 проверено решение одной линии

В Viewdidload() поле «Установить значение по умолчанию для пользователя Navigation controller true» для ключа «hidesShadow»

override func viewDidLoad() {
    super.viewDidLoad()

    self.navigationController?.navigationBar.setValue(true, forKey: "hidesShadow")

}

1
Работает отлично !!
Прашант Тукадия

7

Свифт поставь это

UINavigationBar.appearance().setBackgroundImage(UIImage(), forBarPosition: .Any, barMetrics: .Default)
UINavigationBar.appearance().shadowImage = UIImage()

в

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool

И это также удаляет цвет фона.
TomSawyer

7

Другой вариант, если вы хотите сохранить прозрачность и не хотите создавать подклассы в каждом UINavigationControllerприложении:

#import <objc/runtime.h>

@implementation UINavigationController (NoShadow)

+ (void)load {
    Method original = class_getInstanceMethod(self, @selector(viewWillAppear:));
    Method swizzled = class_getInstanceMethod(self, @selector(swizzled_viewWillAppear:));
    method_exchangeImplementations(original, swizzled);
}

+ (UIImageView *)findHairlineImageViewUnder:(UIView *)view {
    if ([view isKindOfClass:UIImageView.class] && view.bounds.size.height <= 1.0) {
        return (UIImageView *)view;
    }

    for (UIView *subview in view.subviews) {
        UIImageView *imageView = [self findHairlineImageViewUnder:subview];
        if (imageView) {
            return imageView;
        }
    }

    return nil;
}

- (void)swizzled_viewWillAppear:(BOOL)animated {
    UIImageView *shadow = [UINavigationController findHairlineImageViewUnder:self.navigationBar];
    shadow.hidden = YES;

    [self swizzled_viewWillAppear:animated];
}

@end

6
Slightly Swift Solution 
func setGlobalAppearanceCharacteristics () {
    let navigationBarAppearace = UINavigationBar.appearance()
    navigationBarAppearace.tintColor = UIColor.white
    navigationBarAppearace.barTintColor = UIColor.blue
    navigationBarAppearace.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
    navigationBarAppearace.shadowImage = UIImage()

}

Спасибо Джеймс! Я не могу понять, почему кто-то отказался от вашего ответа.
Dănuț Mihai Florian

если вам это нужно, вы также можете добавить: self.navigationController.navigationBar.translucent = false self.navigationController.navigationBar.clipsToBounds = false self.navigationController.navigationBar.shadowImage = UIImage () self.navigationController.lueolol.arolorarar ()
Алессандро Орнано

5

Решение в Swift 4.2:

private func removeHairlineFromNavbar() {
    UINavigationBar.appearance().setBackgroundImage(
        UIImage(),
        for: .any,
        barMetrics: .default)
    UINavigationBar.appearance().shadowImage = UIImage()
}

Просто поместите эту функцию в первый Viewcontroller и вызовите ее в viewdidload


4

В iOS8, если вы установите UINavigationBar.barStyleна.Black вы можете установить фон панели в простой цвет без рамки.

В Свифте:

UINavigationBar.appearance().translucent = false
UINavigationBar.appearance().barStyle = UIBarStyle.Black
UINavigationBar.appearance().barTintColor = UIColor.redColor()

Это самое чистое решение и лучший способ сделать это. Если вам не нужно поддерживать <iOS 8, нет причин не использовать этот ответ. Спасибо.
user3344977

Работает отлично. Я также установил это в раскадровке, поэтому даже не нуждался в коде.
Викзилла

1
Это дает вам очень маленькую белую линию вместо стандартной тени.
Вегард

4

Решение двух линий, которое работает для меня. Попробуйте добавить это в метод ViewDidLoad:

navigationController?.navigationBar.setValue(true, forKey: "hidesShadow")
self.extendedLayoutIncludesOpaqueBars = true

только это сработало для меня
Усама бен Аттик

3

Вот очень простое решение:

self.navigationController.navigationBar.clipsToBounds = YES;

37
Область строки состояния также обрезается.
Fury

3

Простое решение - Swift 5

  1. Создать расширение:

    extension UIImage {
    
        class func hideNavBarLine(color: UIColor) -> UIImage? {
    
            let rect = CGRect(x: 0, y: 0, width: 1, height: 1)
            UIGraphicsBeginImageContext(rect.size)
            let context = UIGraphicsGetCurrentContext()
            context?.setFillColor(color.cgColor)
            context?.fill(rect)
    
    
            let navBarLine = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            return navBarLine
        }
    }
  2. Добавьте это к viewDidLoad():

    self.navigationController?.navigationBar.shadowImage = UIImage.hideNavBarLine(color: UIColor.clear)

2

Для пользователей iOS 9 это работало для меня. просто добавьте это:

UINavigationBar.appearance().shadowImage = UIImage()

2

Проблема с настройкой фонового изображения заключается в том, что он удаляет размытие. Вы можете удалить его, не устанавливая фоновое изображение. Смотрите мой ответ здесь .


2

Вы должны добавить представление в нижней части UISearchBar

let rect = searchController.searchBar.frame;
let lineView : UIView = UIView.init(frame: CGRect.init(x: 0, y: rect.size.height-1, width: rect.size.width, height: 1))
lineView.backgroundColor = UIColor.init(hexString: "8CC73E")
searchController.searchBar.addSubview(lineView)

Я не могу поверить, что это все еще проблема - просто столкнулся с этим снова! Но это решение является наилучшим на данный момент; Спасибо @Socheat
RichAppz

1

Я знаю, что это старая ветка, но я нашел решение, которое работает очень хорошо:

Подкласс UINavigationBar. В вашем подклассе UINavigationBar переопределите didAddSubview с помощью следующего кода:

- (void)didAddSubview:(UIView *)subview
{
    [super didAddSubview:subview];

    if ([subview isKindOfClass:[UIImageView class]]) {
        [subview setClipsToBounds:YES];
    }
}

1

Я только что создал расширение для этого ... Извините за форматирование (это мой первый ответ).

Применение:

  override func viewDidLoad() {
    super.viewDidLoad()
    self.navigationController?.hideShadow = true
}

Расширение:

 UINavigationController.swift
//  Created by Ricardo López Rey on 16/7/15.

import Foundation


struct UINavigationControllerExtension {
    static var hideShadowKey : String = "HideShadow"
static let backColor = UIColor(red: 247/255, green: 247/255, blue: 248/255, alpha: 1.0)
}

extension UINavigationController {

    var hideShadow : Bool {
        get {
            if let ret =  objc_getAssociatedObject(self, &UINavigationControllerExtension.hideShadowKey) as? Bool {
                return ret
            } else {
                return false
            }


        }
        set {
            objc_setAssociatedObject(self,&UINavigationControllerExtension.hideShadowKey,newValue, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN_NONATOMIC))

            if newValue {


            self.navigationBar.setBackgroundImage(solidImage(UINavigationControllerExtension.backColor), forBarMetrics: UIBarMetrics.Default)

                self.navigationBar.shadowImage = solidImage(UIColor.clearColor())
            } else {
                self.navigationBar.setBackgroundImage(nil, forBarMetrics: UIBarMetrics.Default)
            }
        }
    }

    private func solidImage(color: UIColor, size: CGSize = CGSize(width: 1,height: 1)) -> UIImage {
        var rect = CGRectMake(0, 0, size.width, size.height)
        UIGraphicsBeginImageContextWithOptions(size, false, 0)
        color.setFill()
        UIRectFill(rect)
        var image: UIImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }


}

1

В AppDelegate это глобально изменило формат NavBar:

 func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

    UINavigationBar.appearance().setBackgroundImage(UIImage(), forBarPosition: UIBarPosition.Any, barMetrics: UIBarMetrics.Default)
    UINavigationBar.appearance().shadowImage = UIImage()
    UINavigationBar.appearance().tintColor = UIColor.whiteColor()
    UINavigationBar.appearance().barTintColor = UIColor.redColor()
    UINavigationBar.appearance().translucent = false
    UINavigationBar.appearance().clipsToBounds = false
    UINavigationBar.appearance().backgroundColor = UIColor.redColor()
    UINavigationBar.appearance().titleTextAttributes = [NSFontAttributeName : (UIFont(name: "FONT NAME", size: 18))!, NSForegroundColorAttributeName: UIColor.whiteColor()] }

Не удалось реализовать что-то другое на конкретном VC, но это поможет 90% людей


1
-(void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    UIImage *emptyImage = [UIImage new];
    self.navigationController.navigationBar.shadowImage = emptyImage;
    [self.navigationController.navigationBar setBackgroundImage:emptyImage forBarMetrics:UIBarMetricsDefault];
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.