【发布时间】:2018-06-18 13:38:33
【问题描述】:
通常,半透明的UINavigationBars 在白色背景之上具有浅灰色。
但是,iOS 11 中的许多导航栏都是白色的。
例如,“文件”应用中的导航栏是白色和半透明的,这与将 barTintColor 设置为白色明显不同。
如何在UINavigationBar 上实现这种效果?
【问题讨论】:
通常,半透明的UINavigationBars 在白色背景之上具有浅灰色。
但是,iOS 11 中的许多导航栏都是白色的。
例如,“文件”应用中的导航栏是白色和半透明的,这与将 barTintColor 设置为白色明显不同。
如何在UINavigationBar 上实现这种效果?
【问题讨论】:
【讨论】:
barTintColor 设置为白色并在不进行子类化的情况下移除阴影产生相同的效果。想要的效果更半透明更接近UIBlurEffectStyle.light
navigationController?.navigationBar.isTranslucent = true navigationController?.navigationBar.shadowImage = UIImage() navigationController?.navigationBar.barTintColor = .white 这应该会产生如您的问题所示的效果。 UIBlurEffectStyle.light 会产生更戏剧化的效果,如果这是你想要的,请参阅 stackoverflow.com/questions/41781078/…
我试图构建一个与文件应用程序具有相同风格的应用程序,但最终遇到了同样的问题。
好像barTintColor为非nil时,UINavigationBar会禁用半透明效果,而且似乎没有公开的方式强制重新开启。
我认为有一个合理的方法来获得这种效果:您可以将导航栏的背景视图替换为您自己的UIVisualEffectView。
可以通过提供虚拟UIImage 对象来抑制背景视图和导航栏的分隔视图。
navigationBar.setBackgroundImage(UIImage(), for: .default)
navigationBar.shadowImage = UIImage()
您需要创建自己的UINavigationBar 子类,隐藏这两个视图,然后添加自己的UIVisualEffectView 子视图并将其定位为始终位于视图堆栈的底部。
这不是一个了不起的解决方案,但它应该有望通过对导航栏类的最小内部黑客攻击来创建所需的效果。
【讨论】:
所以我花了很多时间研究这个问题,我找到了答案,但请注意:它非常非常 hacky。
因此,负责在导航栏上创建这种灰色光晕的视图称为_UIVisualEffectBackdropView。更具体地说,这个视图的层附加了四个过滤器(它们都是私有的CAFilters),其中第一个,名为luminanceCurveMap,为导航栏创建了这种灰色。所以我的解决方案是
UINavigationBar的视图层次结构中找到_UIVisualEffectBackdropView
luminanceCurveMap过滤器。这是我创建的用于在层次结构中查找 _UIVisualEffectBackdropView 的函数:
extension UIView {
fileprivate static func findRecursively(typeName: String, in view: UIView) -> UIView? {
let typeOf = type(of: view)
if String(describing: typeOf) == typeName {
return view
} else {
for subview in view.subviews {
if let found = UIView.findRecursively(typeName: typeName, in: subview) {
return found
}
}
return nil
}
}
}
然后在您的自定义UINavigationController 子类中覆盖viewDidAppear(例如只有viewDidAppear、viewWillAppear 不起作用):
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let backdrop = UIView.findRecursively(typeName: "_UIVisualEffectBackdropView", in: navigationBar) {
let luminanceCurveMapIndex = backdrop.layer.filters?.firstIndex { filter in
if let caFilter = filter as? NSObject, let name = caFilter.value(forKey: "name") as? String {
return name == "luminanceCurveMap"
} else {
return false
}
}
if let index = luminanceCurveMapIndex {
backdrop.layer.filters?.remove(at: index)
}
}
}
我知道这很多,但这就是我想出来的。它保留了所有原生半透明行为,同时为我提供所需的外观。
【讨论】: