You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

iPhone X及以上机型导航栏圆角后无法顶屏问题求助

解决iPhone X+机型导航栏圆角后不贴合顶部的问题

问题回顾

在iPhone X及以上机型中,设置导航栏圆角后出现了导航栏无法贴合屏幕顶部的情况,当前效果如图:
导航栏顶部留白效果

对应的实现代码如下:

import UIKit
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        self.navigationController?.navigationBar.layer.cornerRadius = 20
        self.navigationController?.navigationBar.clipsToBounds = true
        self.navigationController?.navigationBar.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
    }
}

问题原因

其实核心问题出在clipsToBounds = true上。iPhone X及以上机型的顶部有刘海区域,系统默认会让导航栏延伸覆盖到刘海下方(也就是利用了安全区域的top inset)。但当你开启clipsToBounds后,导航栏的layer会把超出自身范围的部分(也就是刘海区域的延伸部分)给裁剪掉,这就导致顶部出现了留白。

推荐解决方法

方法一:用自定义背景视图实现圆角(最稳妥)

我们可以绕过直接修改导航栏layer的方式,给导航栏添加一个自定义的背景视图,只让这个背景显示底部圆角,这样既保留导航栏顶部的全屏延伸,又能实现你想要的视觉效果:

override func viewDidLoad() {
    super.viewDidLoad()
    
    // 移除导航栏默认的背景和阴影
    navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
    navigationController?.navigationBar.shadowImage = UIImage()
    
    // 创建自定义背景视图
    let customBgView = UIView()
    // 这里可以自定义背景色,默认用导航栏的背景色
    customBgView.backgroundColor = navigationController?.navigationBar.backgroundColor ?? .systemBlue
    customBgView.layer.cornerRadius = 20
    customBgView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
    customBgView.clipsToBounds = true
    
    // 添加到导航栏并设置约束
    navigationController?.navigationBar.addSubview(customBgView)
    customBgView.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        customBgView.topAnchor.constraint(equalTo: navigationController!.navigationBar.topAnchor),
        customBgView.leadingAnchor.constraint(equalTo: navigationController!.navigationBar.leadingAnchor),
        customBgView.trailingAnchor.constraint(equalTo: navigationController!.navigationBar.trailingAnchor),
        customBgView.bottomAnchor.constraint(equalTo: navigationController!.navigationBar.bottomAnchor)
    ])
    
    // 把背景视图放到最底层,避免遮挡导航栏的按钮和标题
    navigationController?.navigationBar.sendSubviewToBack(customBgView)
}

这个方法的好处是完全保留了导航栏的系统行为,比如自动适配刘海、横竖屏切换,同时视觉效果完全符合需求。

方法二:调整导航栏的frame(不推荐)

如果你一定要直接修改导航栏的layer,可以关闭导航栏的半透明属性,然后手动调整frame让它顶部贴合屏幕:

override func viewDidLoad() {
    super.viewDidLoad()
    
    // 关闭半透明,避免系统自动调整布局
    navigationController?.navigationBar.isTranslucent = false
    navigationController?.navigationBar.layer.cornerRadius = 20
    navigationController?.navigationBar.clipsToBounds = true
    navigationController?.navigationBar.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
    
    // 手动设置导航栏frame,覆盖顶部安全区
    if #available(iOS 11.0, *) {
        guard let window = UIApplication.shared.windows.first else { return }
        let topSafeHeight = window.safeAreaInsets.top
        navigationController?.navigationBar.frame = CGRect(
            x: 0,
            y: 0,
            width: view.frame.width,
            height: 44 + topSafeHeight
        )
    }
}

不过这个方法有个弊端:关闭isTranslucent会改变导航栏的默认样式,而且横竖屏切换时需要额外处理frame,兼容性不如方法一。

内容的提问来源于stack exchange,提问作者user12408353

火山引擎 最新活动