I am new to XCode, and I am trying to render a different subview based on a condition. To achieve this, I created two separate files that return a UIView:
This is the occupied space file:
import UIKit
func createOccupiedSpaceView(color: UIColor, playerName: String) -> UIView {
let occupiedView = UIView()
occupiedView.backgroundColor = color
return occupiedView
}
and this is the unoccupied space file:
func createUnoccupiedView(color: UIColor) -> UIView {
let unoccupiedView = UIView()
unoccupiedView.backgroundColor = color
let addLabel = UILabel()
addLabel.text = "ADD NEW PLAYER"
addLabel.anchor(top: unoccupiedView.topAnchor, leading: unoccupiedView.leadingAnchor, bottom: unoccupiedView.bottomAnchor, trailing: unoccupiedView.trailingAnchor, size: CGSize(width: 20, height: 20))
[addLabel].forEach({unoccupiedView.addSubview($0)})
return unoccupiedView
}
I have also created this extension to make my constraints easier
extension UIView {
func anchor(top: NSLayoutYAxisAnchor?, leading: NSLayoutXAxisAnchor?, bottom: NSLayoutYAxisAnchor?, trailing: NSLayoutXAxisAnchor?, padding: UIEdgeInsets = .zero, size: CGSize = .zero) {
translatesAutoresizingMaskIntoConstraints = false
if let top = top {
topAnchor.constraint(equalTo: top, constant: padding.top).isActive = true
}
if let leading = leading {
leadingAnchor.constraint(equalTo: leading, constant: padding.left).isActive = true
}
if let bottom = bottom {
bottomAnchor.constraint(equalTo: bottom, constant: -padding.bottom).isActive = true
}
if let trailing = trailing {
trailingAnchor.constraint(equalTo: trailing, constant: -padding.right).isActive = true
}
if size.width != 0 {
widthAnchor.constraint(equalToConstant: size.width).isActive = true
}
if size.height != 0 {
heightAnchor.constraint(equalToConstant: size.height).isActive = true
}
}
Here is the view controller that runs the code:
import UIKit
class LobbyVC: UIViewController {
private let game: Game
init?(coder: NSCoder, game: Game) {
self.game = game
super.init(coder: coder)
}
required init?(coder: NSCoder) {
fatalError("Use `init(coder:game:)` to initialize an `LobbyVC` instance.")
}
override func viewDidLoad() {
super.viewDidLoad()
// Get appropriate player space views
let VALID_PLAYER_POSITIONS = [1, 2, 3, 4]
var playerDictionary = [Int: UIView]()
for position in VALID_PLAYER_POSITIONS {
playerDictionary[position] = getCorrectView(position: position)
}
}
func getCorrectView(position: Int) -> UIView{
if let player = self.game.players.filter({$0.position == position}).first {
return createOccupiedSpaceView(color: .green, playerName: player.name)
} else {
return createUnoccupiedView(color: .green)
}
}
}
I believe that adding constraints to my unoccupied space is creating issues for me, but I am unsure how to split up my views and make my code cleaner while keeping my views in the same hierarchy. Each time that I try to run my code, I get this error:
Thread 1: "Unable to activate constraint with anchors <NSLayoutYAxisAnchor:0x600001fbc500 \"UILabel:0x12fd12fa0.top\"> and <NSLayoutYAxisAnchor:0x600001fbc040 \"UIView:0x12fd07f70.top\"> because they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies? That's illegal."
Any help on solving would be appreciated!
CodePudding user response:
This means your constraints are wrong. It looks like you may be adding constraints to a view that is not there. Double check your constraints.
Remember the less constraints you use the better. Constraints are also relative to other views.
IE:
View A has top left and right constraints view B has a vertical constraint to view A and left right. Because of the vertical constraint to A you do not need to add a bottom constraint.
https://www.raywenderlich.com/811496-auto-layout-tutorial-in-ios-getting-started
CodePudding user response:
In createUnoccupiedView
, you need to make addLabel
a subview of unoccupiedView
before creating constraints between those two views.
func createUnoccupiedView(color: UIColor) -> UIView {
let unoccupiedView = UIView()
unoccupiedView.backgroundColor = color
let addLabel = UILabel()
addLabel.text = "ADD NEW PLAYER"
// This now happens before adding constraints
// between addLabel and unoccupiedView.
[addLabel].forEach({unoccupiedView.addSubview($0)})
// These constraints are now added after making addLabel
// a subview of unoccupiedView.
addLabel.anchor(top: unoccupiedView.topAnchor, leading: unoccupiedView.leadingAnchor, bottom: unoccupiedView.bottomAnchor, trailing: unoccupiedView.trailingAnchor, size: CGSize(width: 20, height: 20))
return unoccupiedView
}