user6553825
user6553825

Reputation:

Initializing a Custom UIView using only Constraints (IOS)

I have been instructed to create a tic-tac-toe game.

The requirements are:

  1. Use only MVC
  2. Display the views programmatically
  3. Set the Size of the squares with constraints only

Because of this, I have created a Board class and a Square class. The board class it pretty much just a UIView with a background Color. My main problem in the initialization. Every time I use a Custom UIView, I have to init with a CGRect frame. The instructor has told us to not set the frames of the views directly using the CGRect function, but with constraints only.

This is the problem. I am trying to initialize a board inside of another view. Because the board is a subclass of a UIView, I HAVE to set a frame.

How can you initialize a UIView to without a frame so you can only use constraints of a UIView from a parameter you passed in to set the Size of it?

Below is what I'm doing now. I would like to initialize it without the frame parameter, but it's required. I want to initialize this class and use it in the viewDidLoad function of another viewcontroller class. I want to pass in a UIView as a parameter and adjust the constraints according to that specific view.

class Board: UIView {
init(frame: CGRect, view: UIView) {
    super.init(frame: frame)
    self.backgroundColor = .green
    self.translatesAutoresizingMaskIntoConstraints = false
    addConstraints(view: view)
}

func addConstraints(view:UIView){
    NSLayoutConstraint(item: view,
                       attribute: .leading,
                       relatedBy: .equal,
                       toItem: view,
                       attribute: .leading,
                       multiplier: 1,
                       constant: 0).isActive = true
    NSLayoutConstraint(item: view,
                       attribute: .trailing,
                       relatedBy: .equal,
                       toItem: view,
                       attribute: .trailing,
                       multiplier: 1,
                       constant: 0).isActive = true

    NSLayoutConstraint(item: view,
                       attribute: .height,
                       relatedBy: .equal,
                       toItem: view,
                       attribute: .height,
                       multiplier: 1,
                       constant: 0).isActive = true
    NSLayoutConstraint(item: view,
                       attribute: .width,
                       relatedBy: .equal,
                       toItem: view,
                       attribute: .width,
                       multiplier: 1,
                       constant: 0).isActive = true
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

When I create the board class, I would like to set the constraints to the view that I passed in as a parameter when I initialize the class. Frustratingly, It won't take effect because Of the required frame initializer I have to use.

Is there any way around this?

Upvotes: 0

Views: 2631

Answers (3)

Josh Homann
Josh Homann

Reputation: 16327

Your constraints are wrong; you are trying to constrain your view to itself. Are you trying to constrain your view to fill its super view?

    extension UIView {
        func constrainToSuperView() {
            translatesAutoresizingMaskIntoConstraints = false
            let attributes: [NSLayoutAttribute] = [.top, .bottom, .leading, .trailing]
            attributes.forEach { NSLayoutConstraint(item: self, attribute: $0, relatedBy: .equal, toItem: self.superview, attribute: $0, multiplier: 1, constant: 0).isActive = true}
        }
    }

Also NSLayoutAnchor is much more beginner friendly than NSLayoutConstraint since its strongly typed:

    extension UIView {
        func constrainToSuperView() {
            guard let superview = superview else {
                return
            }
            translatesAutoresizingMaskIntoConstraints = false
            topAnchor.constraint(equalTo: superview.topAnchor).isActive = true
            bottomAnchor.constraint(equalTo: superview.bottomAnchor).isActive = true
            leadingAnchor.constraint(equalTo: superview.leadingAnchor).isActive = true
            trailingAnchor.constraint(equalTo: superview.trailingAnchor).isActive = true
        }
    }

Upvotes: 0

Igor Kislyuk
Igor Kislyuk

Reputation: 330

you can do this simply. Here is code

    //create
    let childredView = UIView()
    childredView.translatesAutoresizingMaskIntoConstraints = false
    childredView.backgroundColor = UIColor.purple

    //add it
    self.view.addSubview(childredView)
    self.view.bringSubview(toFront: childredView)

    //set constraints
    let views = ["childrenView": childredView]
    let vertical = NSLayoutConstraint.constraints(withVisualFormat: "V:|[childrenView]|", options: [], metrics: nil, views: views)
    let horizontal = NSLayoutConstraint.constraints(withVisualFormat: "H:|[childrenView]|", options: [], metrics: nil, views: views)
    let all = vertical + horizontal

    //activate them
    NSLayoutConstraint.activate(all)

BTW, you aren't need the frame, because this value is computed. You can create view, add it and set appropriate constraints. Hope this may help you.

Upvotes: 0

Mitchell Hudson
Mitchell Hudson

Reputation: 1113

You can initialize with a default frame. When you apply constraints the constraints will override the original frame you set.

class CustomView: UIView {
    init(frame: CGRect, otherParam: Int) {
        super.init(frame: frame) // Init with in case there are no constraints

        setup()
    }
}

or

class CustomView: UIView {
    init(otherParam: Int) {
        let frame = CGRect(x: 0, y:0, width: 100, height: 100)
        super.init(frame: frame) // Init with a default frame

        setup()
    }
}

Upvotes: 0

Related Questions