如何为UINavigationBar设置0.5pt自定义颜色底部边框?
嘿,完全懂你的需求——就是想要默认导航栏那根0.5pt(Retina屏上1px)的细线条边框,但换成自己想要的颜色对吧?之前用shadowImage生成纯色图片的方法,出来的是1pt(2px)的边框,确实不够精致。我给你分享两个靠谱的解决方案:
方法一:改进shadowImage的生成逻辑,适配屏幕像素精度
问题出在你之前生成图片的尺寸是CGSize(width:1, height:1),在Retina屏上会被系统拉伸成2px高度。我们需要生成像素级精确的0.5pt高度图片,也就是根据屏幕缩放因子来调整图片尺寸:
首先修改你的UIImage扩展:
extension UIImage { public static func hairlineImage(with color: UIColor?) -> UIImage? { let screenScale = UIScreen.main.scale // 0.5pt 对应 1px(Retina屏),所以高度设为 1/screenScale 点 let hairlineHeight = 1 / screenScale let rect = CGRect(origin: .zero, size: CGSize(width: 1, height: hairlineHeight)) // 用UIGraphicsBeginImageContextWithOptions指定屏幕缩放因子,保证图片像素精确 UIGraphicsBeginImageContextWithOptions(rect.size, false, screenScale) guard let context = UIGraphicsGetCurrentContext() else { UIGraphicsEndImageContext() return nil } let actualColor = color ?? .clear context.setFillColor(actualColor.cgColor) context.fill(rect) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image } }
然后设置shadowImage的时候调用这个新方法:
let navigationBarProxy = UINavigationBar.appearance() navigationBarProxy.shadowImage = UIImage.hairlineImage(with: myBorderColor)
这个方法完全贴合系统的shadowImage机制,不会破坏导航栏的原有布局,而且能精准生成1px高度的边框。
方法二:直接给UINavigationBar添加自定义CALayer
如果觉得图片生成的方式有点绕,也可以直接给导航栏底部加一个0.5pt高度的图层,这种方式更直观,还能支持更复杂的边框样式(比如渐变):
写一个UINavigationBar的扩展来封装这个逻辑:
extension UINavigationBar { func setCustomHairlineBorder(color: UIColor) { // 先移除已存在的自定义边框,避免重复添加 sublayers?.forEach { layer in if layer.name == "CustomNavigationBarHairline" { layer.removeFromSuperlayer() } } let hairlineLayer = CALayer() hairlineLayer.name = "CustomNavigationBarHairline" hairlineLayer.backgroundColor = color.cgColor let hairlineHeight: CGFloat = 0.5 // 把图层放在导航栏底部 hairlineLayer.frame = CGRect( x: 0, y: bounds.height - hairlineHeight, width: bounds.width, height: hairlineHeight ) layer.addSublayer(hairlineLayer) // 监听导航栏bounds变化,横竖屏切换或设备旋转时自动调整宽度 addObserver(self, forKeyPath: "bounds", options: .new, context: nil) } override open func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { guard keyPath == "bounds" else { super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) return } // 更新边框宽度 sublayers?.forEach { layer in if layer.name == "CustomNavigationBarHairline" { let hairlineHeight: CGFloat = 0.5 layer.frame = CGRect( x: 0, y: bounds.height - hairlineHeight, width: bounds.width, height: hairlineHeight ) } } } deinit { // 记得移除观察者,避免内存泄漏 removeObserver(self, forKeyPath: "bounds") } }
使用的时候,全局设置或者在单个导航栏上调用都行:
// 全局设置 UINavigationBar.appearance().setCustomHairlineBorder(color: myBorderColor) // 单个导航栏设置(比如某个ViewController里) navigationController?.navigationBar.setCustomHairlineBorder(color: myBorderColor)
关于你提到的背景图片嵌入边框的方法
这个方法确实不够优雅,因为不同设备的导航栏高度不一样(比如带刘海的iPhone和普通iPhone,横竖屏状态),图片拉伸的时候很容易导致边框变形或者位置偏移,所以不太推荐。
内容的提问来源于stack exchange,提问作者Nicolas Miari
相关产品推荐
相关产品推荐

