Search code examples
iosuibuttonuikituinavigationbarkingfisher

Button with an asynchronously loaded image as titleView in UINavigationItem


I'm trying to reproduce this kind of navigation bar from the Tinder app: enter image description here

So I'm trying to set a button as the titleView of my UINavigationItem, with a title and an image. I'm using the following category to align the title under the image:

extension UIButton {

    func centerButtonVertically(padding: CGFloat = 6.0) {
        guard
            let imageViewSize = self.imageView?.frame.size,
            let titleLabelSize = self.titleLabel?.frame.size else {
                return
        }

        let totalHeight = imageViewSize.height + titleLabelSize.height + padding

        self.imageEdgeInsets = UIEdgeInsets(
            top: -(totalHeight - imageViewSize.height),
            left: 0.0,
            bottom: 0.0,
            right: -titleLabelSize.width
        )

        self.titleEdgeInsets = UIEdgeInsets(
            top: 0.0,
            left: -imageViewSize.width,
            bottom: -(totalHeight - titleLabelSize.height),
            right: 0.0
        )

        self.contentEdgeInsets = UIEdgeInsets(
            top: 0.0,
            left: 0.0,
            bottom: titleLabelSize.height,
            right: 0.0
        )
    }
}

And I'm using Kingfisher to download, resize and round the image:

override func viewDidLoad() {
    super.viewDidLoad()

    let rounded = RoundCornerImageProcessor(cornerRadius: 15)
    let downsampling = ResizingImageProcessor(referenceSize: CGSize(width: 30.0, height: 30.0), mode: .aspectFit)
    matchAvatarButton.setTitle(self.match.shortName, for: .normal)
    matchAvatarButton.contentMode = .scaleAspectFit
    matchAvatarButton.kf.setImage(with: self.match.pictureUrls[0] as Resource, for: .normal, placeholder: nil, options: [.processor(downsampling), .processor(rounded)])
    matchAvatarButton.centerButtonVertically()
    ...
}

Note that the button is set as a titleView of my navigation bar in the storyboard, and then I'm modifying the button through its outlet reference in viewDidLoad.

But the result is far from what I'm trying to achieve:

enter image description here

The image remains rectangular and I would like it to be in a circle. And for some weird reason, I don't see the title.

Any idea of what I'm missing?


Solution

  • Here is the result: Custom Title

    1. Put a UIView in the navigation bar in the storyboard instead of a button.
    2. Put the button and the title inside that view. (set button type to custom)
    3. Get the NavigationBar Height
    4. Use custom init for the RoundCornerImageProcessor

    Here is the code:

    class ViewController: UIViewController {
    
            @IBOutlet weak var centerView: UIView!
            @IBOutlet weak var imageButton: UIButton!
            @IBOutlet weak var label: UILabel!
    
            override func viewDidLoad() {
                super.viewDidLoad()
    
                // Custom sizes
                let centerViewWidth = 120
                let imageSide = 30
    
                // Custom init gives a rounded image
                let rounded = RoundCornerImageProcessor(cornerRadius: CGFloat(imageSide/2), targetSize: CGSize(width: imageSide,height: imageSide), roundingCorners: RectCorner(arrayLiteral: .all), backgroundColor: .clear)
                // Downsample to two times the container size to get a better resolution
                let downsampling = ResizingImageProcessor(referenceSize: CGSize(width: imageSide*2, height: imageSide*2), mode: .aspectFit)
    
    
                // Setup frame and positions
                let centerViewHeight = Int(self.navigationController?.navigationBar.frame.height ?? 50)
                centerView.frame = CGRect(x: 0, y: 0, width: centerViewWidth, height: centerViewHeight)
                imageButton.frame = CGRect(x: centerViewWidth/2-imageSide/2, y: 0, width: imageSide, height: imageSide)
                label.frame = CGRect(x: 0, y: imageSide, width: centerViewWidth, height: centerViewHeight-imageSide)
    
                // Quick image download
                let imgUrl = "https://via.placeholder.com/150/771796"
                let resource = ImageResource(downloadURL: URL(string: imgUrl)!)
                imageButton.kf.setImage(with: resource, for: .normal, placeholder: nil, options: [.processor(downsampling), .processor(rounded)])
    
                // Setup title
                label.text = "Custom title!"
                label.textAlignment = .center
    
            }
        }