You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

模仿Tinder导航栏:UINavigationItem的titleView按钮异常求助

问题分析与解决方案

你遇到的两个核心问题:图片未显示为圆形标题不显示,我来逐个帮你排查和解决:


一、图片无法变成圆形的原因及修复

你的代码里存在两个关键问题:

1. Kingfisher多处理器的使用方式错误

当你在Kingfisher的options数组中多次传入.processor()时,只有最后一个处理器会生效,前面的会被覆盖。你需要用ComposedProcessor把缩放和圆角处理器组合起来,确保两个效果都能应用:

// 组合处理器:先缩放,再添加圆角
let composedProcessor = ImageProcessors.ComposedProcessor([downsampling, rounded])
matchAvatarButton.kf.setImage(
    with: self.match.pictureUrls[0] as Resource,
    for: .normal,
    placeholder: nil,
    options: [.processor(composedProcessor)]
)

2. 缩放模式与圆角的匹配问题

你使用了.aspectFit的缩放模式,这会让图片保持比例适配到30x30的尺寸,如果原图不是正方形,缩放后会出现上下/左右的空白区域,此时设置cornerRadius:15只会给整个30x30的容器加圆角,但空白区域是透明的,看起来就还是矩形。

要实现真正的圆形头像,建议先把图片裁剪为正方形,再缩放,最后加圆角:

// 先裁剪为正方形,再缩放,最后圆角
let cropProcessor = CroppingImageProcessor(size: CGSize(width: 30, height: 30), anchor: .center)
let downsampling = ResizingImageProcessor(referenceSize: CGSize(width: 30, height: 30), mode: .aspectFill)
let rounded = RoundCornerImageProcessor(cornerRadius: 15)
let composedProcessor = ImageProcessors.ComposedProcessor([cropProcessor, downsampling, rounded])

另外,也可以直接给按钮的imageView设置圆角(不过Kingfisher处理更高效):

matchAvatarButton.imageView?.layer.cornerRadius = 15
matchAvatarButton.imageView?.clipsToBounds = true

二、标题不显示的原因及修复

1. 导航栏titleView的尺寸问题

UIButton作为titleView时,系统不会自动根据内容调整它的尺寸,如果你在Storyboard里设置的按钮宽度/高度太小,标题就会被截断或隐藏。建议手动设置按钮的尺寸:

// 在设置完内容后,手动设置按钮的intrinsicContentSize或者直接设置frame
matchAvatarButton.sizeToFit()
// 或者强制设置一个足够容纳图片和标题的尺寸
matchAvatarButton.frame = CGRect(x: 0, y: 0, width: 60, height: 50)

2. 边缘偏移计算错误

你的centerButtonVertically方法里的contentEdgeInsets设置可能有问题,导致标题被推出按钮的可视区域。可以简化这个方法,改用更直观的方式实现图片在上、标题在下的布局:

extension UIButton {
    func setImageTopTitleBottom(padding: CGFloat = 6) {
        guard let imageSize = imageView?.frame.size, let titleSize = titleLabel?.frame.size else { return }
        // 调整图片的偏移:向上移动标题高度+padding的一半
        imageEdgeInsets = UIEdgeInsets(top: -(titleSize.height + padding)/2, left: 0, bottom: 0, right: -titleSize.width)
        // 调整标题的偏移:向下移动图片高度+padding的一半
        titleEdgeInsets = UIEdgeInsets(top: 0, left: -imageSize.width, bottom: -(imageSize.height + padding)/2, right: 0)
        // 设置内容的内边距,确保整体居中
        contentEdgeInsets = UIEdgeInsets(top: padding/2, left: 0, bottom: padding/2, right: 0)
    }
}

然后替换原来的matchAvatarButton.centerButtonVertically()matchAvatarButton.setImageTopTitleBottom()

3. 按钮标题的基础设置检查

确保你没有忽略这些细节:

  • 按钮的titleColor和导航栏背景色对比明显(比如导航栏是白色,标题设为黑色)
  • 标题标签的numberOfLines设置为0,避免过长被截断:matchAvatarButton.titleLabel?.numberOfLines = 0
  • 按钮的contentHorizontalAlignment设置为.centermatchAvatarButton.contentHorizontalAlignment = .center

完整修复后的代码示例

override func viewDidLoad() {
    super.viewDidLoad()
    
    // 配置按钮基础属性
    matchAvatarButton.setTitle(self.match.shortName, for: .normal)
    matchAvatarButton.setTitleColor(.black, for: .normal) // 确保标题可见
    matchAvatarButton.titleLabel?.numberOfLines = 0
    matchAvatarButton.contentHorizontalAlignment = .center
    
    // 配置Kingfisher图片处理器
    let cropProcessor = CroppingImageProcessor(size: CGSize(width: 30, height: 30), anchor: .center)
    let downsampling = ResizingImageProcessor(referenceSize: CGSize(width: 30, height: 30), mode: .aspectFill)
    let rounded = RoundCornerImageProcessor(cornerRadius: 15)
    let composedProcessor = ImageProcessors.ComposedProcessor([cropProcessor, downsampling, rounded])
    
    // 加载图片
    matchAvatarButton.kf.setImage(
        with: self.match.pictureUrls[0] as Resource,
        for: .normal,
        placeholder: nil,
        options: [.processor(composedProcessor)]
    )
    
    // 调整图片和标题的布局
    matchAvatarButton.setImageTopTitleBottom()
    
    // 设置按钮尺寸,确保内容能显示
    matchAvatarButton.sizeToFit()
    // 或者设置一个固定尺寸
    // matchAvatarButton.frame = CGRect(x: 0, y: 0, width: 60, height: 50)
    
    // 赋值给导航栏titleView
    navigationItem.titleView = matchAvatarButton
}

extension UIButton {
    func setImageTopTitleBottom(padding: CGFloat = 6) {
        guard let imageSize = imageView?.frame.size, let titleSize = titleLabel?.frame.size else { return }
        imageEdgeInsets = UIEdgeInsets(top: -(titleSize.height + padding)/2, left: 0, bottom: 0, right: -titleSize.width)
        titleEdgeInsets = UIEdgeInsets(top: 0, left: -imageSize.width, bottom: -(imageSize.height + padding)/2, right: 0)
        contentEdgeInsets = UIEdgeInsets(top: padding/2, left: 0, bottom: padding/2, right: 0)
    }
}

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

火山引擎 最新活动