web-dev-qa-db-ja.com

Swift =プログラムで(NSLayoutConstraints)のトレーリング制約とリーディング制約

ViewControllerにビューをxibから追加しています。次に、実際に適合するように制約を設定します

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    ...
    ...
    view!.addSubview(gamePreview)
    gamePreview.translatesAutoresizingMaskIntoConstraints = false
    if #available(iOS 9.0, *) {
        // Pin the leading Edge of myView to the margin's leading Edge
        gamePreview.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor).active = true
        //Pin the trailing Edge of myView to the margin's trailing Edge
        gamePreview.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor).active = true

    } else {
        // Fallback on earlier versions
        view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .TrailingMargin, relatedBy: .Equal, toItem: view, attribute: .TrailingMargin, multiplier: 1, constant: 0))

        view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .LeadingMargin, relatedBy: .Equal, toItem: view, attribute: .LeadingMargin, 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))
}

私がやろうとしていること:ビューを実際にトップに制限するビューに合わせて、先頭に、ViewControllerのビューにトレーリングし、高さをプレフィックスします。メインビューに追加するビューには独自の透明な背景ビューがあるため、マージンは必要ありません(ビューはデバイスの幅サイズであるため)。

Ifの最初の2行は実際にはiOS9>でのみ利用可能であるため、同じことをしようとしているので、ifで等しいと思われる行を2組配置しました(私の試みでは)。 elseステートメントで、すべてのデバイスに対して(iOS 8以降)。

これは私が得るものです:

左側にiOS9以上、右側にiOS8以上。透明な背景は、何が起こるかを示すために赤で色付けされました(画像の異なる高さで気にしないでください、アプリでは同じ高さで、代わりに左右の追加されたマージンを見てください)

私も試してみましたAutoLayoutDSL-Swift、しかし助けにはならない、私はそれを熟知していないが、すべての試みは事態を悪化させただけだった。

従来のNSLayoutConstraintsメソッドを使用してこれらの制約を記述するにはどうすればよいですか。また、AutoLayoutDSLまたはそのフォークのようなフレームワークを使用して、すべてをより良い方法で記述するにはどうすればよいですか。 (または代替手段ですが、現在は主に公式ライブラリに関心があります)

12
BlackBox

leading属性とtrailing属性ではなく、leadingMargin属性とtrailingMargin属性を使用する必要があります。

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))
}
22
Paulw11

@ Paulw11のおかげでそれを推測した...これは解決策です

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))

また、興味のある人のためにAutoLayoutDSL-Swiftを使用して、次のように書き換えることができました

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

View.topは実際のビュートップを参照し、statusBarとnavigationBarの両方の高さによって追加されるパディングを考慮しないため、最後の行は少し長くなります。誰かがよりエレガントな解決策を思い付くのを待って、定数として置き換えることができます。

3
BlackBox

Swift 4アップデート

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))
1
SWARTY

制約のアクティベーションを使用しないのはなぜですか?これを行うことができます:

    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)

これは別です。それが役に立てば幸い。親/子に応じてviewgamePreviewを交換するか、制約の汎用ヘルパー拡張を作成できます。 YouTuber Here が提供するチュートリアルで1つを見つけて、先行/後行、左/右の制約を受け入れるように少し変更しました。また、以下にsafeLayoutGuideを追加することもできます。これにより、将来的に使いやすくなります。このようなもの:

    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
}

そして、次のように呼び出します:

        _ = view.anchor(gamePreview, left: gamePreview, right: gamePreview, height: gamePreview, heightConstant: 131, isForLeading: true)
0
Septronic