Skip to content

Instantly share code, notes, and snippets.

@christianselig
Created August 10, 2022 19:38
Show Gist options
  • Save christianselig/e1f12ee50b1f12cb155571d094e11f7e to your computer and use it in GitHub Desktop.
Save christianselig/e1f12ee50b1f12cb155571d094e11f7e to your computer and use it in GitHub Desktop.
import UIKit
class ViewController: UIViewController {
var redBox: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// Trying to migrate some old frame based code to Auto Layout
redBox = UIView()
redBox.backgroundColor = .systemRed
redBox.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(redBox)
NSLayoutConstraint.activate([
redBox.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 50.0),
redBox.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -50.0),
redBox.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
redBox.heightAnchor.constraint(equalToConstant: 50.0)
])
let childViewController = ViewControllerB(holderViewController: self)
childViewController.view.frame = view.bounds
addChild(childViewController)
view.addSubview(childViewController.view)
childViewController.didMove(toParent: self)
view.backgroundColor = .systemBackground
}
}
class ViewControllerB: UIViewController {
private(set) weak var holderViewController: ViewController?
init(holderViewController: ViewController) {
self.holderViewController = holderViewController
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) { fatalError("\(#file) does not implement coder.") }
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .clear
let greenBox = UIView()
greenBox.backgroundColor = .systemGreen
greenBox.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(greenBox)
// Dispatching ahead one cycle allows the parent to be added, but feels gross
// If I *don't* dispatch ahead, the parent isn't part of the hiearchy yet, therefore
// has no common ancestor, and the constraint crashes.
DispatchQueue.main.async {
NSLayoutConstraint.activate([
greenBox.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 50.0),
greenBox.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -50.0),
greenBox.bottomAnchor.constraint(equalTo: self.holderViewController!.redBox.topAnchor),
greenBox.heightAnchor.constraint(equalToConstant: 50.0)
])
}
}
}
@bjtitus
Copy link

bjtitus commented Aug 10, 2022

@rogelin's approach makes sense, IMO. You could set normal constraints in viewDidLoad and then the constraint which relies on the parent in didMove(toParent:):

private var greenBoxBottomConstraint: NSLayoutConstraint?

override func didMove(toParent parent: UIViewController?) {
    super.didMove(toParent: parent)
    resetGreenBoxBottomConstraint(parent: parent)
}

func resetGreenBoxBottomConstraint(parent: UIViewController?) {
    greenBoxBottomConstraint?.isActive = false

    if let redBox = (parent as? ViewController)?.redBox {
        greenBoxBottomConstraint = greenBox.bottomAnchor.constraint(equalTo: redBox.topAnchor)
    } else {
        greenBoxBottomConstraint = greenBox.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
    }

    greenBoxBottomConstraint?.isActive = true
}

I think it'd also be cleaner to expose a custom layout guide instead of redBox. There's an example in my fork.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment