Search code examples
swiftlayoutxib

Self.frame does not match device dimensions


I'm trying to get the size of my component in relation to the device, however it ignores the real size and considers the "placeholder" of the xib

it's funny because I have two screens with the same component, in one the xib has the look set like the iPhone 11 in the other the iPhone SE (2s generation), ​​when I run the project, the layout calculation and the value of self.frame is different, even both the screens having the same identical component with the same configuration

Important I'm not using auto layout inside xib

It is not possible to use UIScreen in this case as we have other examples where the component is not centered on the screen.

private func drawPosition() {
    searchView.addSubview(searchIcon)
    searchView.backgroundColor = .red
    searchIcon.backgroundColor = .yellow
    
    let widht = self.frame.size.width - 12
    widthLeftView = widht / 2
    
    widthConstraint = searchView.widthAnchor.constraint(equalToConstant: widthLeftView)

    NSLayoutConstraint.activate([
        searchView.heightAnchor.constraint(equalToConstant: searchViewSize),
        widthConstraint,
        searchIcon.heightAnchor.constraint(equalToConstant: searchIconSize),
        searchIcon.widthAnchor.constraint(equalToConstant: searchIconSize),
        searchIcon.centerYAnchor.constraint(equalTo: searchView.centerYAnchor),
        searchIcon.trailingAnchor.constraint(
            equalTo: searchView.trailingAnchor,
            constant: 0)
    ])
    searchBarTextField.leftView = searchView
    searchBarTextField.leftViewMode = .always
}

I tried to set the didLayoutSubViews in the controller but nothing changed

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    
    self.searchBar.setNeedsLayout()
    self.searchBar.layoutIfNeeded()
}

xib:

enter image description here

enter image description here

enter image description here


Solution

  • How I'd to it:

    Either add a super view to embed your searchView & searchIcon. This is a placeholder view, you can set its constraints to half the side of its superview (what you do with self.frame.width).
    This way, no more "- 12" calculation.
    Just let the icon to be at right side, then instead of setting the width of the searchView, set its leading to that placeholder, and the trailing to icon.

    Another way, would be to keep a reference to the width constraints:

    var searchViewWidthConstraint: NSLayoutConstraint
    

    And override layoutSubviews() (if you are in a UIView) or viewDidLayoutSubviews(if you are in a (UIViewController):

    override func layoutSubviews() {
        super.layoutSubviews()
        searchViewWidthConstraint.constant = self.frame.width - 12
    }
    

    When viewDidLoad() is called, the frame of the different views are the one as seen in InterfaceBuilder. Then, the constraints are applied/calculated (and called again each needed time), and the frame adapts. That's why self.frame wasn't correct yet