Breite und Höhe Entspricht der SuperView mit programmgesteuertem Autolayout?


82

Ich habe nach vielen Ausschnitten im Netz gesucht und kann immer noch keine Antwort auf mein Problem finden. Meine Frage ist, dass ich eine scrollView (SV) habe und programmgesteuert eine Schaltfläche in scrollView (SV) mit derselben Breite und Höhe der Übersicht hinzufügen möchte, die scrollView (SV) ist, damit die Gerätetaste beim Drehen des Benutzers denselben Rahmen hat von scrollView (SV). Wie mache ich das NSLayout / NSLayoutConstraint? Vielen Dank

Antworten:


125

Wenn jemand nach einer Swift-Lösung sucht, würde ich eine Swift- ErweiterungUIView erstellen, die Ihnen jedes Mal hilft, wenn Sie einen Subviews-Frame an seine Superviews-Grenzen binden möchten:

Swift 2:

extension UIView {

    /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
    /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        superview.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[subview]-0-|", options: .DirectionLeadingToTrailing, metrics: nil, views: ["subview": self]))
        superview.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[subview]-0-|", options: .DirectionLeadingToTrailing, metrics: nil, views: ["subview": self]))
    }

}

Swift 3:

extension UIView {

    /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
    /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
    }
}

Swift 4.2:

extension UIView {

    /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
    /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        self.topAnchor.constraint(equalTo: superview.topAnchor, constant: 0).isActive = true
        self.bottomAnchor.constraint(equalTo: superview.bottomAnchor, constant: 0).isActive = true
        self.leadingAnchor.constraint(equalTo: superview.leadingAnchor, constant: 0).isActive = true
        self.trailingAnchor.constraint(equalTo: superview.trailingAnchor, constant: 0).isActive = true

    }
}

Dann nenne es einfach so :

// after adding as a subview, e.g. `view.addSubview(subview)`
subview.bindFrameToSuperviewBounds()

Wenn Sie eine benutzerdefinierte UIView mit einer .xib erstellen, sollten die bindFrameToSuperviewBounds innerhalb von 'required init? (coder aDecoder)' direkt nach self.addSubview (self.view)
user1603721

Es ist erwähnenswert, dass Lösungen im visuellen Format nicht sicherheitsbereichsfreundlich sind. Wenn Sie dies beispielsweise in einer Ansicht aufrufen, die sich in einem Navigationscontroller befindet, in dem Navigationsleisten und Symbolleisten angezeigt werden, wird Ihre Ansicht unter der Navigationsleiste und unter der Symbolleiste angezeigt, wenn sie so weit unten liegt.
Andy Ibanez

Dies funktioniert auch als Lösung für Swift 5. Ich konnte meine benutzerdefinierte Unteransicht mithilfe von AutoLayout nicht an die Größe der übergeordneten Ansicht anpassen. Dies zu verwenden, sobald die Unteransicht hinzugefügt wurde, hat als Zauber gewirkt.
toni_piu

Die Swift 4.2-Lösung funktioniert gut. Sie können es sogar etwas kürzer machen, indem Sie das constant: 0Teil entfernen .
Zyphrax

69

Ich bin mir nicht sicher, ob dies der effizienteste Weg ist, aber es funktioniert.

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.translatesAutoresizingMaskIntoConstraints = NO;
// initialize


[coverForScrolView addSubview:button];

NSLayoutConstraint *width =[NSLayoutConstraint
                                    constraintWithItem:button
                                    attribute:NSLayoutAttributeWidth
                                    relatedBy:0
                                    toItem:coverForScrolView
                                    attribute:NSLayoutAttributeWidth
                                    multiplier:1.0
                                    constant:0];
NSLayoutConstraint *height =[NSLayoutConstraint
                                     constraintWithItem:button
                                     attribute:NSLayoutAttributeHeight
                                     relatedBy:0
                                     toItem:coverForScrolView
                                     attribute:NSLayoutAttributeHeight
                                     multiplier:1.0
                                     constant:0];
NSLayoutConstraint *top = [NSLayoutConstraint
                                   constraintWithItem:button
                                   attribute:NSLayoutAttributeTop
                                   relatedBy:NSLayoutRelationEqual
                                   toItem:coverForScrolView
                                   attribute:NSLayoutAttributeTop
                                   multiplier:1.0f
                                   constant:0.f];
NSLayoutConstraint *leading = [NSLayoutConstraint
                                       constraintWithItem:button
                                       attribute:NSLayoutAttributeLeading
                                       relatedBy:NSLayoutRelationEqual
                                       toItem:coverForScrolView
                                       attribute:NSLayoutAttributeLeading
                                       multiplier:1.0f
                                       constant:0.f];
[coverForScrolView addConstraint:width];
[coverForScrolView addConstraint:height];
[coverForScrolView addConstraint:top];
[coverForScrolView addConstraint:leading];

4
Es wäre viel effizienter zu bedienenNSLayoutConstraint.activateConstraints([width, height, top, leading])
Berik

Sie können verwenden[coverForScrolView addConstraints:@[width, height, top, leading]];
Islam Q.

1
Es ist erwähnenswert (Jahre später), dass dieser Code extrem veraltet ist . Es ist jetzt viel einfacher, Einschränkungen hinzuzufügen - siehe jede Antwort 2017 unten
Fattie

49

Dieser Link kann Ihnen helfen, folgen Sie den Anweisungen: http://www.raywenderlich.com/20881/beginning-auto-layout-part-1-of-2

EDIT:

Verwenden Sie das folgende Code-Snippet, wobei die Unteransicht Ihr Subivew ist.

[subview setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"H:|-0-[subview]-0-|"
                           options:NSLayoutFormatDirectionLeadingToTrailing
                           metrics:nil
                           views:NSDictionaryOfVariableBindings(subview)]];
[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"V:|-0-[subview]-0-|"
                           options:NSLayoutFormatDirectionLeadingToTrailing
                           metrics:nil
                           views:NSDictionaryOfVariableBindings(subview)]];

4
In diesem Fall könnte das visuelle Format auch sein V:|[subview]|undH:|[subview]|
Gustavo Barbosa

4
Es ist erwähnenswert (Jahre später), dass dieser Code extrem veraltet ist . Es ist jetzt viel einfacher, Einschränkungen hinzuzufügen - siehe jede Antwort 2017 unten
Fattie

18

addConstraintund removeConstraintMethoden für UIView werden veraltet sein, daher lohnt es sich, die Funktionen zur Erstellung von Einschränkungen zu verwenden:

view.topAnchor.constraint(equalTo: superView.topAnchor, constant: 0).isActive = true
view.bottomAnchor.constraint(equalTo: superView.bottomAnchor, constant: 0).isActive = true
view.leadingAnchor.constraint(equalTo: superView.leadingAnchor, constant: 0).isActive = true
view.trailingAnchor.constraint(equalTo: superView.trailingAnchor, constant: 0).isActive = true

Das funktioniert gut. Sie können es sogar etwas kürzer machen, indem Sie das constant: 0Teil entfernen .
Zyphrax

7

Ansatz 1: Über die UIView-Erweiterung

Hier ist ein funktionalerer Ansatz in Swift 3+ mit einer Vorbedingung anstelle einer print(die in der Konsole leicht zugrunde gehen kann). Dieser meldet Programmiererfehler als fehlgeschlagene Builds.

Fügen Sie diese Erweiterung Ihrem Projekt hinzu:

extension UIView {
    /// Adds constraints to the superview so that this view has same size and position.
    /// Note: This fails the build if the `superview` is `nil` – add it as a subview before calling this.
    func bindEdgesToSuperview() {
        guard let superview = superview else {
            preconditionFailure("`superview` was nil – call `addSubview(view: UIView)` before calling `bindEdgesToSuperview()` to fix this.")
        }
        translatesAutoresizingMaskIntoConstraints = false
        ["H:|-0-[subview]-0-|", "V:|-0-[subview]-0-|"].forEach { visualFormat in
            superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: visualFormat, options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
        }
    }
}

Nennen Sie es jetzt einfach so:

// after adding as a subview, e.g. `view.addSubview(subview)`
subview.bindEdgesToSuperview()

Beachten Sie, dass die obige Methode bereits in mein HandyUIKit- Framework integriert ist, das Ihrem Projekt auch einige weitere praktische UI-Helfer hinzufügt.


Ansatz 2: Verwenden eines Frameworks

Wenn Sie in Ihrem Projekt viel mit programmatischen Einschränkungen arbeiten, empfehle ich Ihnen, SnapKit zu testen . Dies erleichtert das Arbeiten mit Einschränkungen erheblich und ist weniger fehleranfällig .

Befolgen Sie die Installationsanweisungen in den Dokumenten, um SnapKit in Ihr Projekt aufzunehmen. Dann importiert es an der Spitze der Swift - Datei:

import SnapKit

Jetzt können Sie genau das Gleiche erreichen:

subview.snp.makeConstraints { make in
    make.edges.equalToSuperview()
}

6

Swift 3:

import UIKit

extension UIView {

    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
    }

}

Wenn Sie den Code nur geändert haben, um Swift 3-kompatibel zu machen, sollten Sie lieber die Antwort des Originalplakats bearbeiten, anstatt eine neue Antwort zu veröffentlichen (da dies keine Änderung der Absicht des Originalplakats darstellt). Wenn Sie nicht genügend Punkte für eine Bearbeitung haben, kommentieren Sie den ursprünglichen Beitrag mit einem Hinweis auf die Änderungen, die erforderlich sind, um Swift 3 zu entsprechen. Das ursprüngliche Poster (oder jemand anderes, der Ihren Kommentar sieht) würde dann wahrscheinlich die Antwort aktualisieren. Auf diese Weise halten wir den Thread frei von doppelten Antworten und veraltetem Code.
Jeehut

Hey @Dschee - ich stimme dir vollkommen zu, aber wir liegen "falsch". Die "Konsens" -Ansicht auf der Website zum Guten oder Schlechten ist das Gegenteil von dem, was Sie hier ausdrücken. meta.stackoverflow.com/questions/339024/… (Ich ignoriere ständig die Konsensentscheidung, tue, was sinnvoll ist, und bekomme dann Ärger von den Mods :))
Fattie

2

Swift 4 mit NSLayoutConstraint:

footerBoardImageView.translatesAutoresizingMaskIntoConstraints = false
let widthConstraint  = NSLayoutConstraint(item: yourview, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: superview, attribute: NSLayoutAttribute.width, multiplier: 1, constant: 0)
let heightConstraint = NSLayoutConstraint(item: yourview, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: superview, attribute: NSLayoutAttribute.height, multiplier: 1, constant: 0)
superview.addConstraints([widthConstraint, heightConstraint])

1

Als ergänzende Antwort und eine Antwort für diejenigen, die nicht gegen die Einbeziehung von Bibliotheken von Drittanbietern sind, bietet die PureLayout- Bibliothek eine Methode, um genau dies zu tun. Sobald die Bibliothek installiert ist, ist es so einfach wie

myView.autoPinEdgesToSuperviewEdges()

Es gibt andere Bibliotheken, die je nach Geschmack ähnliche Funktionen bieten können, z. Mauerwerk , Kartographie .


0

Im Anschluss an die Lösung von @ Dschee finden Sie hier eine schnelle 3.0-Syntax: (Bitte beachten Sie: Dies ist nicht meine Lösung , ich habe sie gerade für Swift 3.0 behoben.)

extension UIView {

    /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
    /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
    superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
}

1
Wenn Sie den Code nur geändert haben, um Swift 3-kompatibel zu machen, sollten Sie lieber die Antwort des Originalplakats bearbeiten, anstatt eine neue Antwort zu veröffentlichen (da dies keine Änderung der Absicht des Originalplakats darstellt). Wenn Sie nicht genügend Punkte für eine Bearbeitung haben, kommentieren Sie den ursprünglichen Beitrag mit einem Hinweis auf die Änderungen, die erforderlich sind, um Swift 3 zu entsprechen. Das ursprüngliche Poster (oder jemand anderes, der Ihren Kommentar sieht) würde dann wahrscheinlich die Antwort aktualisieren. Auf diese Weise halten wir den Thread frei von doppelten Antworten und veraltetem Code.
Jeehut

Ich stimme Ihnen vollkommen zu, @Dschee, aber es gibt (für mich absurde) Kommentare zu Meta, dass "wir das bei SO nicht machen" ... meta.stackoverflow.com/questions/339024/…
Fattie

@ JoeBlow Nachdem ich die Diskussion hinter Ihrem Link gelesen habe, denke ich tatsächlich, dass dies auch Sinn macht. Ich stimme dem Kommentar von Patrick Haugh (zur Frage) zu, obwohl eine neue Antwort in Kombination mit einem Kommentar zur ursprünglichen Antwort gegeben werden sollte. Es ist dann Sache des Originalplakats, seine Antwort zu aktualisieren (um die zukünftigen Upvotes zu erhalten) oder nicht. Danke für den Link!
Jeehut

Huh, okay, das ist alles ein großer Teil dessen, warum ich in der Vergangenheit ein treuer Hit und Läufer geblieben bin. Ich greife nach meiner Antwort, konvertiere sie in die aktuelle Syntax und fahre mit meiner Codierung fort. Der Hauptgrund, warum ich auf diese Weise gepostet habe, ist, dass ich beim Unterrichten von Swift oft gefragt wurde, wie ich die Lösung in der aktuellen Version von Swift finden kann, da neue Codierer Probleme beim Aktualisieren ihrer Funktionsdeklarationen hatten. Es war eine der Hauptursachen für Frustration, aber auch eine Gelegenheit, die beiden Codestile gegenüberzustellen. Der Student war in der Lage, ähnliche Änderungen in anderen Codefragmenten als Ergebnis vorherzusagen.
James Larcombe

0

Ich musste die Übersicht vollständig abdecken. Die anderen würden das bei Orientierungsänderungen nicht tun. Also habe ich eine neue geschrieben, die dies tut - mit einem beliebigen Größenmultiplikator von 20. Sie können jederzeit auf Ihre Bedürfnisse eingehen. Beachten Sie auch, dass diese Ansicht die Unteransicht viel größer macht als die Übersicht, was sich möglicherweise von den Anforderungen unterscheidet.

extension UIView {
    func coverSuperview() {
        guard let superview = self.superview else {
            assert(false, "Error! `superview` was nil – call `addSubview(_ view: UIView)` before calling `\(#function)` to fix this.")
            return
        }
        self.translatesAutoresizingMaskIntoConstraints = false
        let multiplier = CGFloat(20.0)
        NSLayoutConstraint.activate([
            self.heightAnchor.constraint(equalTo: superview.heightAnchor, multiplier: multiplier),
            self.widthAnchor.constraint(equalTo: superview.widthAnchor, multiplier: multiplier),
            self.centerXAnchor.constraint(equalTo: superview.centerXAnchor),
            self.centerYAnchor.constraint(equalTo: superview.centerYAnchor),
            ])
    }
}

0

Ich habe die besten Elemente aus den anderen Antworten ausgewählt:

extension UIView {
  /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
  /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
  func bindFrameToSuperviewBounds() {
    guard let superview = self.superview else {
      print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
      return
    }

    self.translatesAutoresizingMaskIntoConstraints = false

    NSLayoutConstraint.activate([
      self.topAnchor.constraint(equalTo: superview.topAnchor),
      self.bottomAnchor.constraint(equalTo: superview.bottomAnchor),
      self.leadingAnchor.constraint(equalTo: superview.leadingAnchor),
      self.trailingAnchor.constraint(equalTo: superview.trailingAnchor)
    ])
  }
}

Sie können es beispielsweise in Ihrer benutzerdefinierten UIView folgendermaßen verwenden:

let myView = UIView()
myView.backgroundColor = UIColor.red

self.addSubview(myView)
myView.bindFrameToSuperviewBounds()
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.