Trailing and Leading constraints in Swift programmatically (NSLayoutConstraints)

若如初见. 提交于 2019-12-04 02:08:57

You need to use the leading and trailing attributes, not the leadingMargin and trailingMargin attributes:

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    ...
    ...
    view!.addSubview(gamePreview)
    gamePreview.translatesAutoresizingMaskIntoConstraints = false

    view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Trailing, relatedBy: .Equal, toItem: view, attribute: .Trailing, multiplier: 1, constant: 0))            
    view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Leading, relatedBy: .Equal, toItem: view, attribute: .Leading, multiplier: 1, constant: 0))

    view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Top, relatedBy: .Equal, toItem: self.topLayoutGuide, attribute: .Bottom, multiplier: 1, constant: 0))
    view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute,multiplier: 1, constant: 131))
}

Guessed it thanks to @Paulw11... this is the solution

view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Trailing, relatedBy: .Equal, toItem: view, attribute: .Trailing, multiplier: 1, constant: 0))     
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Leading, relatedBy: .Equal, toItem: view, attribute: .Leading, multiplier: 1, constant: 0))        
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Top, relatedBy: .Equal, toItem: self.topLayoutGuide, attribute: .Bottom, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute,multiplier: 1, constant: 131))

Also I managed to rewrite it as it follows using AutoLayoutDSL-Swift for anyone interested

view => gamePreview.trailing == view.trailing
     => gamePreview.leading == view.leading
     => gamePreview.height == 131
     => gamePreview.top == view.top + self.navigationController!.navigationBar.bounds.height + UIApplication.sharedApplication().statusBarFrame.size.height

The last line is a little long because view.top refers to the really view top and doesn't consider padding added by both statusBar and navigationBar heights. They could be replaced as constants, waiting if someone comes up with a more elegant solution.

SWARTY

SWIFT 4 Update

stackView.translatesAutoresizingMaskIntoConstraints = false

stackView.addConstraint(NSLayoutConstraint(item: stackView, attribute: .trailing, relatedBy: .equal, toItem: stackView, attribute: .trailing, multiplier: 1, constant: 0))
stackView.addConstraint(NSLayoutConstraint(item: stackView, attribute: .leading, relatedBy: .equal, toItem: stackView, attribute: .leading, multiplier: 1, constant: 0))

stackView.addConstraint(NSLayoutConstraint(item: stackView, attribute: .top, relatedBy: .equal, toItem: self.addLayoutGuide, attribute: .bottom, multiplier: 1, constant: 0))
stackView.addConstraint(NSLayoutConstraint(item: stackView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute,multiplier: 1, 
constant: 131))

Why don't you use constraint activation? You could do this:

    var anchors = [NSLayoutConstraint]()
    anchors.append(view.topAnchor.constraint(equalTo: gamePreview.topAnchor, constant: 0))
    anchors.append(view.leadingAnchor.constraint(equalTo: gamePreview.leadingAnchor, constant: 0))
    anchors.append(view.trailingAnchor.constraint(equalTo: gamePreview.trailingAnchor, constant: 0))
    anchors.append(view.heightAnchor.constraint(equalTo: gamePreview.heightAnchor, multiplier: 1, constant: 131))
    NSLayoutConstraint.activate(anchors)

This is another. Hope it helps. You could swap view and gamePreview depending on the parent / child, or you could create a general helper extension for your constraints. I found one in a tutorial provided by a YouTuber Here and slightly modified it to accept leading/trailing, left/right constraints. You could also add safeLayoutGuide to the below as well, which makes it future-friendly. Something like this:

    func anchor(_ top: UIView? = nil, left: UIView? = nil, bottom: UIView? = nil, right: UIView? = nil, width: UIView? = nil, height: UIView? = nil, topConstant: CGFloat = 0, leftConstant: CGFloat = 0, bottomConstant: CGFloat = 0, rightConstant: CGFloat = 0, widthConstant: CGFloat = 0, heightConstant: CGFloat = 0, isForLeading: Bool = false) -> [NSLayoutConstraint] {
    translatesAutoresizingMaskIntoConstraints = false

    var anchors = [NSLayoutConstraint]()
    if #available(iOS 11.0, *) {
        if let top = top {
            anchors.append(topAnchor.constraint(equalTo: top.safeAreaLayoutGuide.topAnchor, constant: topConstant))
        }

        if let left = left {
            if isForLeading  {
                anchors.append(leadingAnchor.constraint(equalTo: left.safeAreaLayoutGuide.leadingAnchor, constant: leftConstant))
            } else {
                anchors.append(leftAnchor.constraint(equalTo: left.safeAreaLayoutGuide.leftAnchor, constant: leftConstant))
            }
        }

        if let bottom = bottom {
            anchors.append(bottomAnchor.constraint(equalTo: bottom.safeAreaLayoutGuide.bottomAnchor, constant: -bottomConstant))
        }

        if let right = right {
            if isForLeading  {
                anchors.append(trailingAnchor.constraint(equalTo: right.safeAreaLayoutGuide.leadingAnchor, constant: rightConstant))
            } else {
                anchors.append(rightAnchor.constraint(equalTo: right.safeAreaLayoutGuide.rightAnchor, constant: -rightConstant))
            }
        }
        if let width = width {
            anchors.append(widthAnchor.constraint(equalTo: width.safeAreaLayoutGuide.widthAnchor, multiplier: 1, constant: widthConstant))
        } else if widthConstant > 0 {
            anchors.append(widthAnchor.constraint(equalToConstant: widthConstant))
        }

        if let height = height {
            anchors.append(heightAnchor.constraint(equalTo: height.safeAreaLayoutGuide.heightAnchor, multiplier: 1, constant: widthConstant))
        } else if heightConstant > 0 {
            anchors.append(widthAnchor.constraint(equalToConstant: heightConstant))
        }
        anchors.forEach({$0.isActive = true})
    } else {
        if let top = top {
            anchors.append(topAnchor.constraint(equalTo: top.topAnchor, constant: topConstant))
        }

        if let left = left {
            if isForLeading  {
                anchors.append(leadingAnchor.constraint(equalTo: left.leadingAnchor, constant: leftConstant))
            } else {
                anchors.append(leftAnchor.constraint(equalTo: left.leftAnchor, constant: leftConstant))
            }
        }

        if let bottom = bottom {
            anchors.append(bottomAnchor.constraint(equalTo: bottom.bottomAnchor, constant: -bottomConstant))
        }

        if let right = right {
            if isForLeading  {
                anchors.append(trailingAnchor.constraint(equalTo: right.leadingAnchor, constant: rightConstant))
            } else {
                anchors.append(rightAnchor.constraint(equalTo: right.rightAnchor, constant: -rightConstant))
            }
        }

        if let width = width {
            anchors.append(widthAnchor.constraint(equalTo: width.widthAnchor, multiplier: 1, constant: widthConstant))
        } else if widthConstant > 0 {
            anchors.append(widthAnchor.constraint(equalToConstant: widthConstant))
        }

        if let height = height {
            anchors.append(heightAnchor.constraint(equalTo: height.heightAnchor, multiplier: 1, constant: widthConstant))
        } else if heightConstant > 0 {
            anchors.append(widthAnchor.constraint(equalToConstant: heightConstant))
        }
        anchors.forEach({$0.isActive = true})
    }

    return anchors
}

And then call it like this:

        _ = view.anchor(gamePreview, left: gamePreview, right: gamePreview, height: gamePreview, heightConstant: 131, isForLeading: true)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!