2

I'm trying to programmatically create a view that is centered inside a designated superview. I can almost achieve this behavior, however the centered view does not respect the dynamic height of its content. Here is the function I created to make it happen:

static func overlayWithButton(onView view: UIView, buttonText: String, theme: Theme = .dark, action: Action? = nil) -> UIView {
    // create overlay
    let overlay = UIView()
    overlay.translatesAutoresizingMaskIntoConstraints = false
    let leftInset: CGFloat = 20
    let rightInset: CGFloat = 20

    // Style overlay
    overlay.backgroundColor = theme == .dark ? UIColor.black : UIColor.white
    overlay.alpha = 0.75
    overlay.cornerRadius = 10

    // create button
    let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
    button.translatesAutoresizingMaskIntoConstraints = false

    // Style Button
    button.backgroundColor = UIColor.clear
    button.setTitle(buttonText, for: .normal)
    button.setTitleColor(theme == .dark ? lightBlueButtonText : UIColor.black, for: .normal)
    button.titleLabel?.lineBreakMode = .byWordWrapping
    button.titleLabel?.textAlignment = .center
    button.titleLabel?.numberOfLines = 0
    button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 20)
    button.isEnabled = true

    // add button action
    if let action = action {
        button.add(for: .touchUpInside, action)
    }

    // add constraints
    overlay.addSubview(button)
    button.centerXAnchor.constraint(equalTo: overlay.centerXAnchor).isActive = true
    button.leftAnchor.constraint(greaterThanOrEqualTo: overlay.leftAnchor).isActive = true
    button.rightAnchor.constraint(lessThanOrEqualTo: overlay.rightAnchor).isActive = true

    button.topAnchor.constraint(equalTo: overlay.topAnchor).isActive = true
    button.bottomAnchor.constraint(equalTo: overlay.bottomAnchor).isActive = true

    view.addSubview(overlay)
    overlay.leftAnchor.constraint(equalTo: view.leftAnchor, constant: leftInset).isActive = true
    overlay.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -rightInset).isActive = true
    overlay.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true

    return overlay
}

This works with short button titles: it works! However, with longer title, the button content is clipped: enter image description here Using reveal I can see that the title / button label is responding as intended. it does not work I've been at this for quite a while now, and I can't get the centered overlay to expand its height to match the intrinsic content size of its contents. Any ideas?

Cognitio
  • 410
  • 1
  • 4
  • 14
  • Can you set the height anchor for the overlay equal to the height anchor for the button? – Zig Sep 13 '17 at 21:06
  • I get the same result if I do that instead of the top/bottom anchors, and the same result if I do that in addition to the top/bottom anchors. – Cognitio Sep 13 '17 at 21:12
  • I suspect that this is something to do with the translatesAutoResizingMaskIntoContraints getting rid of the intrinsic content size of the UIButton, but I need to set that to true, otherwise all the other constraints applied to the button don't work. – Cognitio Sep 13 '17 at 21:13
  • Are you calling `setNeedsLayout` and `layoutIfNeeded` ? – GetSwifty Sep 13 '17 at 21:47
  • Where should I be making those calls? – Cognitio Sep 13 '17 at 22:00
  • Interestingly enough, when I change the button to a normal label, things work the way I expect. This means it has something to do with the fact that it is a UIButton that contains a UILabel (button.titleLabel) that is throwing everything off. – Cognitio Sep 13 '17 at 22:19
  • Hi ! Have you checked my Answer ? – Badal Shah Sep 15 '17 at 04:02
  • @BadalShah Yes, and thank you for putting so much thought into the response! Unfortunately I require a programmatic solution (as specified in my question). Also, and perhaps was less clear about this, but I need to content centered in the view to be a button allowed to overflow multiple lines of text. – Cognitio Sep 15 '17 at 04:09

2 Answers2

0

After extensive digging, it turns out the problem is due to a funky interaction between a UIButton and its label. See this stack overflow post for more details on that specific issue. While none of the top-voted solutions succeeded in helping me fix the issue, there was a solution further down that did help. The solution lies in creating a UIButton subclass that overrides intrinsicContentSize and layoutSubviews. Code for such a subclass here:

class LongTitleButton: UIButton {

    override var intrinsicContentSize: CGSize {
        return self.titleLabel?.intrinsicContentSize ?? super.intrinsicContentSize
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        if let titleLabel = titleLabel {
            titleLabel.preferredMaxLayoutWidth = titleLabel.frame.size.width
        }
    }

}

Alternatively, since this issue seems to be specific to UIButton, an equally effective alternative solution would be to simply use a UILabel instead of a UIButton, placing a UIGestureRecognizer on the label instead.

Cognitio
  • 410
  • 1
  • 4
  • 14
0

I got your Problem , You Don't need to take All programatically and set Size According to your Text.

Here i have Attached Step by Step Guid , Please Follow this .

Step 1 :- Take one View (We call it PopupView) and One Label as Subview in PopupView.

enter image description here

Step 2 :- Set Initial Constrain to PopupView and UILabel as Describe below.

PopupView = LeadingConstrain(20) and TrialingConstrain(20) (From its Superview = mainview)
  UILabel = LeadingConstrain(10) and TrialingConstrain(10) (From its Superview =popupView)

enter image description here

Step 3 :- Most tricky - Press RighClick of Mouse and Drag PopUpview to UILabel.Now You need to Press Shift key and Click on Topspace to Container and Bottom Space to Container..

 PopupView Topspace to UILabel Top Space
 popupview BottomSpace to UILabel Bottom Space
 set UIlabe number of Line = 0 
 and LineBreak = WordwrapMode

enter image description here

Step 4 :- Now Set Constrain to PopupView and Update Frames.

Set Center Horizontally in Container
Set Center Vertically in Container

enter image description here

Step 5 :- in ThNe Last Step, Click on PopupView . select Top Constrain of view and Set Aprroproate Distance of UILabel to View from top and Follow same for Bottom.

enter image description here

Step 6 :- Now see Preview for Short label on Multiple Devices.

enter image description here

Step 7 :- Now Add long Line text in UILabel , Run in Simulator and Check for Portrait and LandScape both Mode.

enter image description here

I hope now your issue will be clear. If you need any help , feel free to ask in Comment.

Here i have Also Attached Demo for Your Reference , UILabel with Center Dynamic Height

Badal Shah
  • 7,541
  • 2
  • 30
  • 65