Andrew V. Jackson
Andrew V. Jackson

Reputation: 283

Why NSLayoutConstraint is optional type?

Usually I avoid to ask a new question here on stackoverflow, because 90% of questions easily solved by searching. But sometimes you can't do in this way.

I have several objects class GameTimer : UIView that I add to the array var timersArray: [GameTimer?] { get set } of my view controller dynamically. I've implemented some small class to handle constraints for that objects. Every time I add a new object, I also add constraints for portrait and landscape mode.

class ConstraintsContainer {

    var portraitConstraints: [NSLayoutConstraint] = []
    var landscapeConstraints: [NSLayoutConstraint] = []

func setGeneralConstraints(timerView: GameTimer?, timerViewSize: CGFloat) {
    timerView?.translatesAutoresizingMaskIntoConstraints = false
    timerView?.heightAnchor.constraint(equalToConstant: timerViewSize).isActive = true
    timerView?.widthAnchor.constraint(equalToConstant: timerViewSize).isActive = true
    print("General constraints")
}

func getDeviceOrientation() {

        if UIDevice.current.orientation == UIDeviceOrientation.portrait || UIDevice.current.orientation == UIDeviceOrientation.portraitUpsideDown {
            NSLayoutConstraint.activate(portraitConstraints)
        } else {
            NSLayoutConstraint.activate(landscapeConstraints)
        }
    }
}

When I try to use this class in my view controller

var constraintsContainer = ConstraintsContainer()

and set some values to the instance

constraintsContainer.portraitConstraints = [
            timersArray[0]?.topAnchor.constraint(equalTo:    view.topAnchor, constant: view.frame.height/4.5),
            (timersArray[0]?.centerXAnchor.constraint(equalTo: view.centerXAnchor))
        ]

        constraintsContainer.landscapeConstraints = [
            (timersArray[0]?.topAnchor.constraint(equalTo: view.topAnchor, constant: view.frame.height/6.5)),
            (timersArray[0]?.centerXAnchor.constraint(equalTo: view.centerXAnchor))
        ]

I have an error warning from XCode telling me that "value of optional type 'NSLayoutConstraint?' is not unwrapped" and suggests me to unwrap every constraint like this (timersArray[0]?.topAnchor.constraint(equalTo: view.topAnchor, constant: view.frame.height/4.5))!

So, can't get a clue why NSLayoutConstraint type appears optional here, considering it's reference class NSLayoutConstraint : NSObject. Is it possible to avoid it to be an optional? The reason - when I deactivate constraints for particular object, then clear constraints from constraintsContainer and remove object of my GameTimer class type from timersArray and have implemented this unwrapping I caught an error -

Thread 1:EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)

 func deactivate() {

    NSLayoutConstraint.deactivate(constraintsContainer.portraitConstraints)
    NSLayoutConstraint.deactivate(constraintsContainer.landscapeConstraints)
    constraintsContainer.portraitConstraints.removeAll()
    constraintsContainer.landscapeConstraints.removeAll()
}

Why NSLayoutConstraint appears to be optional in my particular case?

Upvotes: 0

Views: 757

Answers (1)

Abdelahad Darwish
Abdelahad Darwish

Reputation: 6067

the process of querying, calling properties, subscripts and methods on an optional that may be 'nil' is defined as optional chaining. Optional chaining return two values −

if the optional contains a 'value' then calling its related property, methods and subscripts returns values.

if the optional contains a 'nil' value all its its related property, methods and subscripts returns nil.

Since multiple queries to methods, properties and subscripts are grouped together failure to one chain will affect the entire chain and results in 'nil' value.

and your timersArray is Optional ?

Upvotes: 2

Related Questions