Reputation: 1
I am trying to get some initialization code to run after NSLayoutConstraints have applied on an NSView. The reason I want the code to run at this point is because the initialization depends on the frame width.
I have tried putting the initialization code in the init method, but this does not work because the views frame has not been setup with respect to the constraints yet. I also tried putting it in the views layout method, which does set the view up correctly. But it does not work because whenever my view gets resized, it will reset to the init state.
Perhaps the way the app is laid out is the issue. I have a ViewController: NSSplitViewController containing two split view items. The view in question is one of the split view items.
Overall I am a bit confused with how the layout constraints are being applied, and when they finish applying. Here is a simplified version of my code
class ViewController: NSSplitViewController {
let someVC = SomeViewController()
let emptyVC = EmptyViewController()
override func viewDidLoad() {
super.viewDidLoad()
let someViewItem = NSSplitViewItem(viewController: someVC)
let emptyViewItem = NSSplitViewItem(viewController: emptyVC)
addSplitViewItem(someViewItem)
addSplitViewItem(emptyViewItem)
NSLayoutConstraint.activate([
someVC.view.heightAnchor.constraint(lessThanOrEqualTo: splitView.heightAnchor)
])
}
override func viewDidAppear() {
print("ViewController.viewDidAppear: ", view.frame.size.width)
}
}
class SomeViewController: NSSplitViewController {
let someView = SomeView()
override func viewDidLoad() {
super.viewDidLoad()
someView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(someView)
NSLayoutConstraint.activate([
someView.widthAnchor.constraint(lessThanOrEqualTo: someView.heightAnchor),
someView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.8),
someView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
someView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
someView.bottomAnchor.constraint(lessThanOrEqualTo: view.bottomAnchor, constant: -60)
])
}
override func viewDidAppear() {
print("SomeViewController.viewDidAppear: ", view.frame.size.width)
}
}
class SomeView: NSView {
var squareSize: CGFloat = 0
var context: CGContext?
override init(frame: NSRect) {
super.init(frame: frame)
print("SomeView.init: ", frame.size.width)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layout() {
print("SomeView.layout: ", frame.size.width)
}
}
class EmptyViewController: NSSplitViewController {
}
Here is the output on launch:
SomeView.init: 0.0
SomeView.layout: 149.0
ViewController.viewDidAppear: 480.0
SomeViewController.viewDidAppear: 187.0
SomeView.layout: 149.0
SomeView.layout: 505.0
SomeView.layout: 504.5
I have a function that takes in the squareSize = frame.size.width/8 and provides me with a list of initial positions. Once the app is up and running it's fine, because the draw function is able to get the correct frame width. The issue is only when the application first loads.
Edit
I found a way around it. Since it does not seem like there is a way to run code when the layout is finalized for only the first time, I changed the way the state is represented.
Before I have these properties in my class
var x: CGFloat
var y: CGFloat
var row: Int {
return Int(location.y / ChessPiece.squareSize)
}
var col: Int {
return Int(location.x / ChessPiece.squareSize)
}
Now I am doing
var row: Int
var col: Int
private var _x: CGFloat?
private var _y: CGFloat?
var x: CGFloat {
get {
return _x ?? CGFloat(col) * ChessPiece.squareSize + (ChessPiece.squareSize / 2)
}
set {
_x = newValue
}
}
var y: CGFloat {
get {
return _y ?? CGFloat(row) * ChessPiece.squareSize + (ChessPiece.squareSize / 2)
}
set {
_y = newValue
}
}
func moveTo(_ row: Int, _ col: Int) {
self.row = row
self.col = col
_x = nil
_y = nil
}
Upvotes: 0
Views: 37