具有类似 scaleAspectFill 行为的 UINavigationBar 背景图像

UINavigationBar background image with scaleAspectFill-like behavior

tl;dr

我正在尝试为 UINavigationBar 设置背景图片。有没有办法配置导航栏,使其使用类似 scaleAspectFill 的行为(作为 UIImageViewcontentMode 设置为 scaleAspectFill)?


目标

我有一张大致适合景观导航栏尺寸的图像 iPhone(64pt 高,大约 800pt 宽;不过这里的确切尺寸应该无关紧要),我用 navigationBar.setBackgroundImage(img, for: .default) 设置了它。该图像是一张模糊的照片,因此不需要像素完美。顺便说一句——我正在使用 UINavigationBar.appearance 一次设置所有导航栏的背景。

  1. 我想要它做什么:纵向显示图像的中心部分,横向显示不裁剪的完整图像。如果高度不完全匹配(正如 iPhone X 所预期的那样),它应该放大一点以填充额外的高度。

  2. 作为后备解决方案,如果宽度较小的图像居中显示并通过拉伸第一个和最后一个像素列填充额外的水平 space,我也会接受。

到目前为止我尝试了什么

您可以尝试将 UIImageViewcontentMode = .scaleAspectFill 直接添加到 UINavigationBar 中,这样做:

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let imgView = UIImageView(image: UIImage(named: "desert"))
        imgView.contentMode = .scaleAspectFill
        imgView.frame = self.navigationController!.navigationBar.frame
        self.navigationController?.navigationBar.addSubview(imgView)
        self.navigationController?.navigationBar.sendSubview(toBack: imgView)
    }
}

结果是:

通过向 UINavigationController 的视图添加图像视图来解决,如 here 所述。图像视图附加到导航控制器视图的顶部、左侧和右侧以及导航栏的底部。

对比文章中的例子,我没有使用自定义导航栏;相反,我将图像视图插入现有导航栏后面。请注意,导航栏必须是透明的才能使其正常工作。此外,图像视图必须被裁剪,否则它会奇怪地超出导航栏的底部。

也许这对其他人有帮助,所以这里有一些代码(这应该只应用 一次 到导航控制器,所以也许你最好把这段代码在子类中而不是在扩展中):

extension UINavigationController {
  func setBackgroundImage(_ image: UIImage) {
    navigationBar.isTranslucent = true
    navigationBar.barStyle = .blackTranslucent

    let logoImageView = UIImageView(image: image)
    logoImageView.contentMode = .scaleAspectFill
    logoImageView.clipsToBounds = true
    logoImageView.translatesAutoresizingMaskIntoConstraints = false

    view.insertSubview(logoImageView, belowSubview: navigationBar)
    NSLayoutConstraint.activate([
      logoImageView.leftAnchor.constraint(equalTo: view.leftAnchor),
      logoImageView.rightAnchor.constraint(equalTo: view.rightAnchor),
      logoImageView.topAnchor.constraint(equalTo: view.topAnchor),
      logoImageView.bottomAnchor.constraint(equalTo: navigationBar.bottomAnchor)
    ])
  }
}