0

I'm trying center subview. But it doesn't seem expected. How can I fix it?

here is my codes

private lazy var uploadButton: UIButton = {
        let button = UIButton()
        let innerView = UIView(frame: CGRect(x: 0, y: 0, width: 136, height: 63))
        innerView.backgroundColor = .cyan
        innerView.center = CGPoint(x: button.frame.size.width/2, y: button.frame.size.height/2)
        button.addSubview(innerView)
        button.layer.cornerRadius = 30
        button.backgroundColor = .white
        button.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.25).cgColor
        button.layer.shadowOpacity = 1
        button.layer.shadowOffset = CGSize.zero
        button.layer.shadowRadius = 6
        return button
    }()
view.addSubview(uploadButton)
uploadButton.translatesAutoresizingMaskIntoConstraints = false
// ...
uploadButton.bottomAnchor.constraint(equalTo: nextButton.topAnchor, constant: -69),
            uploadButton.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 38),
            uploadButton.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: -38),
            uploadButton.heightAnchor.constraint(equalToConstant: 171)
// all active

enter image description here

Suee97
  • 627
  • 1
  • 6
  • 13
  • 1
    Does this answer your question? [How to center a subview of UIView](https://stackoverflow.com/questions/11251988/how-to-center-a-subview-of-uiview) – lazarevzubov Mar 15 '23 at 06:14
  • @lazarevzubov I've tried `innerView.center = button.convert(button.center, to: button.superview)` but I shows same ui.. – Suee97 Mar 15 '23 at 06:18
  • There's many different ways to achieve the result listed in answers. – lazarevzubov Mar 15 '23 at 07:01

2 Answers2

2

There are different layout systems available in iOS

  1. Auto Layout
  2. Frame-Based Layout
  3. Stack Views
  4. SwiftUI

Now, why this inconsistency arises in your snippet?

Your code snippet above has used both Auto Layout and Frame-based Layout which will barely give you desired layout or offer a consistent way for maintenance.

Let's have a look at one case.

  • uploadButton is based on autolayout.
  • innerView is based on frame-based layout.
  • Auto layout will dynamically calculate the view size of your uploadButton, so when you want to set innerView center using button.frame.size early it won't work as the width and height are zero that time, and will be calculated later upon constraint activation.
  • If uploadButton had frame-based layout only then button.frame.size related calculations would have made sense.

TLDR

Stick with only ONE layout system to maintain consistency and intuitiveness as mixing multiple systems can lead to confusion and maintenance issues.

Code

Here, is a working auto layout based view.

private lazy var innerView: UIView = {
    let innerView = UIView()
    innerView.translatesAutoresizingMaskIntoConstraints = false
    innerView.backgroundColor = .cyan
    return innerView
}()

private lazy var uploadButton: UIButton = {
    let button = UIButton()
    button.translatesAutoresizingMaskIntoConstraints = false
    button.backgroundColor = .white
    return button
}()

....
....

// then in viewDidLoad() where you structure view elements
view.addSubview(uploadButton)
uploadButton.addSubview(innerView)

// Try replacing this constraint activation part with any utility extension 
// or storyboard or available library with less code 

let uploadButtonConstraints = [
    uploadButton.leftAnchor.constraint(equalTo: view.leftAnchor),
    uploadButton.rightAnchor.constraint(equalTo: view.rightAnchor),
    uploadButton.topAnchor.constraint(equalTo: view.topAnchor),
    uploadButton.bottomAnchor.constraint(equalTo: view.bottomAnchor)
]
NSLayoutConstraint.activate(uploadButtonConstraints)

let innerViewConstraints = [
    innerView.heightAnchor.constraint(equalToConstant: 63), // hardcoding is bad
    innerView.widthAnchor.constraint(equalToConstant: 136),
    innerView.centerXAnchor.constraint(equalTo: uploadButton.centerXAnchor),
    innerView.centerYAnchor.constraint(equalTo: uploadButton.centerYAnchor)
]
NSLayoutConstraint.activate(innerViewConstraints)
....

Result

enter image description here

Offtopic?

When designing an API for developers in a language or platform, it is ideal to provide a single approach to accomplish a task. This simplifies the API and minimizes confusion, for example, as demonstrated by the guiding principles of Go language design.

However, as a language or platform evolves, it goes through various phases of development, such as the transition of iOS app layout from Frame-based to Auto Layout to SwiftUI. To maintain backward compatibility previous APIs are kept available as well, which results in developers being exposed to multiple approaches to achieve the same goal or view, leading to confusion when those approaches get mixed.

tanmoy
  • 1,276
  • 1
  • 10
  • 28
0

innerView.center = CGPoint(x: button.frame.size.width/2, y: button.frame.size.height/2) button.addSubview(innerView)

I think your code is failing because innerView doesn't have a superview at the time you set its center property. From the docs:

The center point is specified in points in the coordinate system of its superview. Setting this property updates the origin of the rectangle in the frame property appropriately.

What you want is to set innerView's position in button. Accordingly, you should add innerView as a subview of button first, and then set its center. So, swapping those two lines should solve your problem.

Caleb
  • 124,013
  • 19
  • 183
  • 272