Home > Net >  Understanding layout anchors in Swift
Understanding layout anchors in Swift

Time:01-02

So, I have this ViewController where I render an image inside a subview.

class ViewController: UIViewController {
  override func viewDidLoad() {
    super.viewDidLoad()
    view.backgroundColor = .systemBackground
    
    let childView = UIView()
    childView.backgroundColor = .red
    
    let imageView = UIImageView(image: UIImage(systemName: "tray"))
    imageView.contentMode = .scaleAspectFill
    
    childView.addSubview(imageView)
    
    imageView.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
      imageView.heightAnchor.constraint(equalToConstant: 100.0)
    ])
    
    view.addSubview(childView)
    
    childView.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
      childView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor)
    ])
  }
}

As you can see for some reason, the image moves to the left of screen. What is the cause of that?

enter image description here

One more thing I noticed is the subview should have a background color of red as specified, but somehow it's transparent. Why is that?

I expect the result to be something like,

enter image description here

CodePudding user response:

You are adding way too few constraints. The horizontal position and size of the childView are not constrained at all, so the size just defaults to (0, 0), making the view not visible at all, which is why you don't see the red background.

First, let's constrain the horizontal position. This seems to be what you intended:

NSLayoutConstraint.activate([
    // wouldn't it be better to use safeAreaLayoutGuide?
    childView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
    childView.leftAnchor.constraint(equalTo: view.layoutMarginsGuide.leftAnchor)
])

Then, the size of the childView should be the same as the imageView, so you should activate these constraints too:

imageView.leftAnchor.constraint(equalTo: childView.leftAnchor),
imageView.rightAnchor.constraint(equalTo: childView.rightAnchor),
imageView.topAnchor.constraint(equalTo: childView.topAnchor),
imageView.bottomAnchor.constraint(equalTo: childView.bottomAnchor),

There is one more thing though - the size of the image view at this point is not what you expect. The image view has a height of 100, but its width is still 24, which is the intrinsic size of the "tray" image. scaleAspectFill does scale the image to the size you want, but the views' widths stay at 24, and since the scaling is done from the centre of the view, the scaled up image appears to be "off centred".

I think that in general, you'll just have to manually calculate the width you want:

let width = 100 * image.size.width / image.size.height

and constrain both width and height:

imageView.heightAnchor.constraint(equalToConstant: 100.0),
imageView.widthAnchor.constraint(equalToConstant: width),

However, with SF symbols, you can get a bigger image simply by:

let image = UIImage(systemName: "tray", withConfiguration: UIImage.SymbolConfiguration(pointSize: 100))

You don't need any height or width constraints on the image view.

  • Related