如何隐藏UINavigationBar 1px底线


442

我有一个应用程序,有时需要其导航栏与内容融合在一起。

有谁知道如何摆脱或改变这个烦人的小酒吧的颜色?

在下面的图片中,我遇到了-我正在谈论“ Root View Controller”下方的1px高度线

在此处输入图片说明



如何增加1px的导航高度?
Suresh Durishetti

Answers:


736

对于iOS 13:

使用.shadowColor物业

如果此属性为nil或包含纯色,则该条不显示阴影

例如:

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()

上面是隐藏它的唯一“官方”方法。不幸的是,它消除了bar的半透明性。

我不想要背景图片,只想要颜色

您有以下选择:

  1. 纯色,无半透明:

    navigationBar.barTintColor = UIColor.redColor()
    navigationBar.isTranslucent = false
    navigationBar.setBackgroundImage(UIImage(), for: .default)
    navigationBar.shadowImage = UIImage()
    
  2. 创建充满色彩的小背景图像并使用它。

  3. 使用下面描述的“ hacky”方法。它还将使栏保持半透明。

如何保持酒吧半透明?

为了保持透明度,您需要另一种方法,它看起来像hack,但效果很好。我们要消除的阴影是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的初衷!


102
您也可以设置UINavigationBar属性的视图:clipsToBounds = YES
cleverbit14年

3
@richarddas clipsToBounds = YES就像一个魅力!谢谢!
romeouald 2014年

3
clipsToBounds不适用于某些布局。这个答案对我来说很完美。只有我创建了UINavigationBar子类,并使用以上代码在-layoutSubview方法中隐藏了阴影图像。谢谢!
cgossain 2015年

7
只是要注意,我的iOS顶部状态栏变得半透明,我可以看到表格视图滚动到UINavigationBar后面。我通过设置setTranslucent = NO来解决此问题。
Vlad

2
在iOS 10中,似乎什么时候viewWillAppear 被调用都无法得到shadowImageView
xi.lin

239

下面可以帮助您!

迅速:

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上。这是唯一对我有用的答案。
cloudcal 2013年

1
这是唯一适用于iOS 10和Xcode 8.3.1的简短而简单的答案。谢啦。
维沙尔·索纳瓦妮

1
Objective-C: self.navigationController.navigationBar.shadowImage = [UIImage新];
Marcelo dos Santos

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
这会将其设置为代码库中每个导航栏的全局设置...大多数情况下不是您想要的。
克里斯多夫(Christoph)

97

尝试这个:

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

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

下图有说明(iOS7 NavigationBar):

在此处输入图片说明

并检查以下问题: iOS7-更改UINavigationBar边框颜色


正如Serhii所说,必须设置自定义背景图像才能接受阴影图像。
akashivskyy 2013年

3
在您的AppDelegate中执行此操作。
myusuf3 2014年

好的答案...救了我的一天_ / \ _
Akshay

@RPM在iOS 7,8,9,10中非常有效。有在viewControlles的导航栏相同的代码的斯威夫特。
Akhrameev

64

想要添加Serhii答案的Swift版本。我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

它可与navigationBar一起使用,但不适用于uitoolbar
TomSawyer

我发现使用iOS 12 不能再使用了。:(。
克里斯王子

适用于出色的iOS 13
ttorbik

这应该是答案。在iOS 13中像魅力一样工作
COVID19 '19

63

快速的方法:

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

UINavigationBar.appearance().shadowImage = UIImage()

2
在找到最圆滑的答案之前,将其排在最前面是很罕见的。+1!
sudo make install

3
它隐藏了UINavigationBar的整个背景,它是:/
user365314

错误,它也隐藏了整个背景!
TomSawyer '16

3
注意:您必须将navigationBar的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的答案之后,我创建了一个pod 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为nil或包含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%解决了我的问题
Roger Oba

1
是的,它在iOS 13中对我也很好用,非常感谢@glotcha。
Yogesh Patel

@glotcha对..
Digvijay

谢谢。唯一对我
有用的

13

也可以从情节提要中隐藏(在Xcode 10.1上使用)

通过添加运行时属性:hidesShadow-布尔值-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()然后不?我正在尝试此操作,但无济于事。我的理解不正确吗?
Tommy K


10

我使用UINavigationBar扩展名,该扩展名使我能够使用UIAppearance API来隐藏/显示阴影,或者选择要使用Storyboard(或源代码)隐藏/显示该阴影的导航栏。这是扩展名:

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 2015年

好的解决方案,但仅在导航栏设置translucent为false
时才有效

9

Swift 4经过测试的 一线解决方案

Viewdidload() 设置导航控制器的用户默认值true中,键“ hidesShadow”为true

override func viewDidLoad() {
    super.viewDidLoad()

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

}

1
完美地工作!
Prashant Tukadiya

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.navigationBar.backgroundColor = UIColor.blueColor ()
亚历山德罗·奥纳诺

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你可以设置栏的背景为素色无边界。

在Swift中:

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

这是最干净的解决方案,也是实现此目的的最佳方法。如果您不必支持<iOS 8,则没有理由不使用此答案。谢谢。
user3344977 '02

效果很好。我也在情节提要中设置了它,因此甚至不需要任何代码。
vikzilla '16

1
这样做会给您很小的白线,而不是标准阴影。
Vegard


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

设置背景图像的问题是可以消除模糊。您可以删除它而无需设置背景图像。在这里查看我的答案。


2

您应该在UIS​​earchBar的底部添加一个视图

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];
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.