skip to Main Content

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!

2

Answers


  1. 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

    Login or Signup to reply.
  2. 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
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search