2

I have a custom UIView which has 2 UILabel (same width) and one UIImageView (known width of 44pt). Width sizes are given as an example, they can change but It is exact that UILabels should has same width and UIImage has a 44 point width. I want to add this view to UINavigationBar' titleView BUT ImageView should be in the center of navigation bar.

(60 width) UILabel---UIImageView (44 width) ---UILabel (60 width)

I want is that UILabels to have maximum two line and adJustFontSizeToFitWidth true. I'm giving specific width and height to title view but labels get two line but their font size doesn't change even they don't fit the view.

How I add titleView:

 navigationItem.titleView = myTitleView
    let widthOfItem: CGFloat = 30.0
    let pading: CGFloat = 40
    let aWidth: CGFloat = (self.navigationController?.navigationBar.frame.width)! - CGFloat(1) * widthOfItem * 2.0 - pading

    myTitleView { (make) in
        make.width.equalTo(aWidth)
        make.height.equalTo(44)
    }

MyCustomView:

override func layoutSubviews() {
    super.layoutSubviews()
    let preferredWidth = (bounds.width / 2) - 56
    firstLabel.preferredMaxLayoutWidth = preferredWidth
    secondLabel.preferredMaxLayoutWidth = preferredWidth
}

private func setupViews() {

    addSubview(firstLabel)
    addSubview(myImageView)
    addSubview(secondLabel)

    firstLabel.font = .myFont(.bold, size: 36)
    firstLabel.adjustsFontSizeToFitWidth = true
    firstLabel.minimumScaleFactor = 0.5
    firstLabel.textColor = .textPrimary
    firstLabel.numberOfLines = 2
    firstLabel.lineBreakMode = .byWordWrapping
    firstLabel.textAlignment = .right

    myImageView.contentMode = .scaleAspectFit
    myImageView.clipsToBounds = true
    myImageView.layer.minificationFilter = .trilinear
    myImageView.layer.cornerRadius = currencyImageSize.height / 2

    secondLabel.font = . myFont(.bold, size: 36)
    secondLabel.translatesAutoresizingMaskIntoConstraints = false
    secondLabel.textColor = .textPrimary
    secondLabel.numberOfLines = 2
    secondLabel.adjustsFontSizeToFitWidth = true
    secondLabel.baselineAdjustment = .none
    secondLabel.minimumScaleFactor = 0.5
    secondLabel.lineBreakMode = .byWordWrapping
    secondLabel.textAlignment = .left

    firstLabel.snp.makeConstraints { (make) in
        make.leading.equalToSuperview()
        make.top.bottom.equalToSuperview()
        make.trailing.equalTo(myImageView.snp.leading).offset(-12)
    }

    myImageView.snp.makeConstraints { (make) in
        make.height.equalToSuperview()
        make.width.equalTo(myImageView.snp.height)
        make.centerX.equalToSuperview()
    }
   secondLabel.snp.makeConstraints { (make) in
        make.top.bottom.equalToSuperview()
        make.leading.equalTo(myImageView.snp.trailing).offset(12)
        make.trailing.equalToSuperview()
    }
}
Emre Önder
  • 2,408
  • 2
  • 23
  • 73
  • 1
    Try putting them in a horizontal `UIStackView`. – koen May 14 '20 at 13:05
  • The problem is that they have different size. I mean for example 20 width for image and 60, 60 for UILAbels. Can I do that in stack view? – Emre Önder May 14 '20 at 14:05
  • Yes, each subview inside a stack view can have a different width. – koen May 14 '20 at 14:06
  • It doesn't work. I can't get what I want. I edited my question – Emre Önder May 14 '20 at 14:25
  • In your case you only need a constraint for the fixed width of the two labels, the stackview should take care of everything else. See for instance here: https://stackoverflow.com/questions/47949136/programmatically-creating-layout-using-stack-view-and-constraints-not-working – koen May 14 '20 at 17:49
  • So when I give same width to UILabels and no width to imageView, should it work as expected which is imageView on the center and two labels left and right side of it? – Emre Önder May 15 '20 at 06:10
  • Did you try it? – koen May 18 '20 at 14:13
  • Yes. It doesn't work sorry. There is a problem with using .lineBreakMode, .minimumScaleFactor, and .adjustsFontSizeToFitWidth at the same time. Can you please share code example? – Emre Önder May 18 '20 at 14:45
  • Please note that `adjustsFontSizeToFitWidth` is *only intended for use with a single-line label*. See here: https://developer.apple.com/documentation/uikit/uilabel/1620546-adjustsfontsizetofitwidth – koen May 18 '20 at 14:59
  • Aaa can't I do that both adjustFontSizeToFitWidth and two-line label? – Emre Önder May 18 '20 at 18:37
  • That's what that links suggests. But there seem to be workarounds, see eg here: https://stackoverflow.com/questions/4382976/multiline-uilabel-with-adjustsfontsizetofitwidth/7261514 – koen May 18 '20 at 18:44

1 Answers1

0

Maybe this code will work?


class CustomNavClass: UIView {

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup_view()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setup_view()
    }

    private func setup_view() {
        backgroundColor = .lightGray

        let image = UIImageView(image: .init())
        addSubview(image)

        image.translatesAutoresizingMaskIntoConstraints = false
        image.backgroundColor = .green

        NSLayoutConstraint.activate([
            image.centerXAnchor.constraint(equalTo: centerXAnchor),
            image.widthAnchor.constraint(equalToConstant: 44),
            image.heightAnchor.constraint(equalToConstant: 44),

            image.topAnchor.constraint(equalTo: topAnchor, constant: 3),
            image.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -3)
        ])

        let leftLabel = UILabel()
        leftLabel.text = "Label label left long label will go here, naturally"
        leftLabel.numberOfLines = 2
        leftLabel.adjustsFontSizeToFitWidth = true
        leftLabel.backgroundColor = .red

        addSubview(leftLabel)

        leftLabel.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            leftLabel.leadingAnchor.constraint(equalTo: leadingAnchor),
            leftLabel.centerYAnchor.constraint(equalTo: centerYAnchor),
            leftLabel.trailingAnchor.constraint(equalTo: image.leadingAnchor, constant: -10)
        ])

        let rightLabel = UILabel()
        rightLabel.text = "Label label right long label will go here, naturally"
        rightLabel.numberOfLines = 2
        rightLabel.adjustsFontSizeToFitWidth = true
        rightLabel.backgroundColor = .blue

        addSubview(rightLabel)

        rightLabel.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            rightLabel.trailingAnchor.constraint(equalTo: trailingAnchor),
            rightLabel.centerYAnchor.constraint(equalTo: centerYAnchor),
            rightLabel.leadingAnchor.constraint(equalTo: image.trailingAnchor, constant: 10)
        ])
    }
}

The code does not use a UIStackView as its limited to its function (specifically, only one UIView can be stretched out on .fill distribution property.

This instead creates layout constraints:

  1. UIImageView that is pegged at the center(with a 3 top and bottom constraint, optional, you can use centerY constraint or the like).
  2. Leading UILabel is pegged to leading edge and trailing edge with UIImageView's leading edge.
  3. Trailing UILabel is pegged to trailing edge and leading edge with UIImageView's trailing edge.

rantingmong
  • 103
  • 6
  • The problem is that there is a left bar button item on navigation bar so can you please show me how you center image in this scenario? – Emre Önder May 18 '20 at 08:10
  • Should the left bar button item be shown alongside the left label? I'm actually expecting this though the methodology to do left bar button item is hacky. And it requires the view to be removed from `titleView` and placed inside `UINavigationBar`'s subview. – rantingmong May 18 '20 at 08:15
  • Yea yea. It is in navigation bar NOT in title view. It is a usual back button but as you know when you have some item on the left side title views width is total (screen width - bar button item) so that centering something is hacky – Emre Önder May 18 '20 at 08:17
  • Oki! I shall make my edits with those requirements. – rantingmong May 18 '20 at 08:20
  • I tested your code It is working when u set text earlier and with default font but when I make font size 36PT bold and set text after view is created, It is not working. Left label even goes beyond title views bounds. – Emre Önder May 18 '20 at 08:35
  • It won't. Since the requested font size is too big even with font size adjustments. – rantingmong May 18 '20 at 08:38
  • and also It breaks when I add linebreakMode of byWordWrap – Emre Önder May 18 '20 at 08:40
  • I tried increasing the text size and adding byWordWrap it works on my end. https://imgur.com/a/T4cZ0SY – rantingmong May 18 '20 at 08:45
  • Also, I was able to center the image view I deferred the constraint construction for centerX after setting the titleView property! – rantingmong May 18 '20 at 08:47
  • In your screenshot, labels frame goes beyond titleView. It should decrease the font size that's why we add adjustFontSizeToFitWidth right? – Emre Önder May 18 '20 at 08:54
  • It should, but I forgot to add constraints to the top and bottom edges of the label. I added that and the text just got truncated. – rantingmong May 18 '20 at 09:00
  • Yes that was the problem actually. What I'm looking for the answer for :) – Emre Önder May 18 '20 at 09:03
  • Ah, after experimenting with .lineBreakMode, .minimumScaleFactor, and .adjustsFontSizeToFitWidth. I think lineBreakMode and adjustFontSizeToFitWidth is mutually exclusive, as iOS will not know where to peg the actual width of the UILabel. – rantingmong May 18 '20 at 09:09
  • I think It is something to do with contentHuggingPriority – Emre Önder May 18 '20 at 10:08
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/214116/discussion-between-rantingmong-and-emre-onder). – rantingmong May 18 '20 at 15:55