kmn
kmn

Reputation: 2655

Sizing label with Autolayout

Description of the issue

I have a label to the left side of a plot view in an iOS App. I have succeeded in rotatif the label 90 degrees, as pictured below, using the code copied in a following section :

enter image description here

However, as soon as I change the text to "Speed (Km/h):, the label becomes wider, as shown below :

enter image description here

Layout

There are no relevant contraints were set in interface builder. The only contraints set here are : - vertical centering of the label - plot view's right, top, and bottom edges stick to view's right, top, and bottom edges

The rest is set in code

enter image description here

Code

func setup(speedArray: [Float32]) {

    //Convert speed to Km/h from m/s
    let speedArrayKMH = speedArray.map({$0*3.6})

    //Draw Chart
    //This only styles the chart (colors, user interaction, etc...
    //no code in this fuction that affects layout)
    setupSpeedChart(speedArray: speedArrayKMH)

    //
    //  Customise speed label
    //
    //Label text
    chartTitleLabel.textAlignment = .center
    //Text color
    self.chartTitleLabel.textColor = BoxTextColor
    //Rotate
    chartTitleLabel.transform = CGAffineTransform(rotationAngle: CGFloat(-Double.pi/2))
    //Constrain
    let C1 = NSLayoutConstraint(item: chartTitleLabel, attribute: .leadingMargin, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1, constant: 0)
    let C2 = NSLayoutConstraint(item: chartTitleLabel, attribute: .trailingMargin, relatedBy: .equal, toItem: speedLineChart, attribute: .leading, multiplier: 1, constant: 0)

    NSLayoutConstraint.activate([C1, C2])

}

What I've tried

I've tried a number of construit and size combinations, including :

nothing seems to work

Adding the following constraint for label width for example produces this :

let C3 = NSLayoutConstraint(item: chartTitleLabel, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 45)

enter image description here

Upvotes: 1

Views: 165

Answers (2)

DonMag
DonMag

Reputation: 77433

The issue lies in this part of the description of transform in Apple's docs: https://developer.apple.com/documentation/uikit/uiview/1622459-transform

In iOS 8.0 and later, the transform property does not affect Auto Layout. Auto layout calculates a view’s alignment rectangle based on its untransformed frame.

So, when you change the text of the label, your constraints are related to the untransformed frame.

The fix is to embed the label in a "container" UIView and constrain the container's width to the height of the label, and the height to the width of the label.

Set up your xib like this (I use contrasting background colors to make it easy to see frames):

enter image description here

Note that I've given the container view width and height constraints of 80. Edit each of those constraints and set them as "Placeholders":

enter image description here

We'll be setting new constraints in code, so these will be removed at build time, but will satisfy IB's layout checking.

In awakeFromNib() in your custom class, add the new width and height constraints and apply the rotation transform:

override func awakeFromNib() {
    super.awakeFromNib()

    // set HUGGING priority on title label to REQUIRED for both axis
    chartTitleLabel.setContentHuggingPriority(.required, for: .horizontal)
    chartTitleLabel.setContentHuggingPriority(.required, for: .vertical)

    NSLayoutConstraint.activate([

        // constrain title container view WIDTH equal to title label HEIGHT
        // set the constant to add padding on left and right of rotated title label
        // here it is set to 12, which gives 6-pts padding on each side
        chartTitleContainerView.widthAnchor.constraint(equalTo: chartTitleLabel.heightAnchor, constant: 12.0),

        // constrain title container view HEIGHT equal to title label WIDTH
        chartTitleContainerView.heightAnchor.constraint(equalTo: chartTitleLabel.widthAnchor, constant: 0.0),

        ])

    // rotate the title label
    chartTitleLabel.transform = CGAffineTransform(rotationAngle: CGFloat(-Double.pi/2))

    // un-comment after development
    //      chartTitleLabel.backgroundColor = .clear
    //      chartTitleContainerView.backgroundColor = .clear

}

Now, in your setup() func (where, I'm assuming, you add the "plot view" as a subview), add the constraints for the plot view:

func setup(speedArray: [Float32]) {

    //Convert speed to Km/h from m/s
    let speedArrayKMH = speedArray.map({$0*3.6})

    // assuming setupSpeedChart() creates speedLineChart view and adds it to self

    //Draw Chart
    //This only styles the chart (colors, user interaction, etc...
    //no code in this fuction that affects layout)
    setupSpeedChart(speedArray: speedArrayKMH)

    NSLayoutConstraint.activate([

        // constrain chart view LEADING to title container view TRAILING
        speedLineChart.leadingAnchor.constraint(equalTo: chartTitleContainerView.trailingAnchor, constant: 0.0),

        // constrain chart view top, bottom and trailing to self
        speedLineChart.topAnchor.constraint(equalTo: topAnchor, constant: 0.0),
        speedLineChart.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0.0),
        speedLineChart.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0.0),

        ])

}

This is the result:

enter image description here

and with the "dev" colors turned off:

enter image description here

You can now change the text of the title label and it will remain centered vertically and horizontally, without changing the horizontal size / spacing.

Upvotes: 0

Mohammad Yunus
Mohammad Yunus

Reputation: 214

there are few key thing missing first you forgot to use translateAutoReaizingMaskIntoConstraints

go to storyboard select your label go to inspector under label there is autoshrink set it to minimum font size set the size to 11

then do the following in view didload

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    myLabel.text = "speed in km"
    myLabel.textAlignment = .center
    myLabel.transform = CGAffineTransform(rotationAngle: CGFloat(-Double.pi/2))
    myLabel.translatesAutoresizingMaskIntoConstraints = false
    let c1 = NSLayoutConstraint(item: myLabel, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 200)
    let c2 = NSLayoutConstraint(item: myLabel, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 60)

    let c3 = NSLayoutConstraint(item: myLabel, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: -60)
    let c4 = NSLayoutConstraint(item: myLabel, attribute: .centerY, relatedBy: .equal, toItem: myImageView, attribute: .centerY, multiplier: 1, constant: 0)

    NSLayoutConstraint.activate([c1, c2, c3, c4])

}

I have tried to update the text to new text with more character count than previous and I worked just fine

this is before changing text this is after changing the text

Upvotes: 1

Related Questions