模仿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设置为.center:matchAvatarButton.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




