Gibt es eine Möglichkeit, eine Einschränkung zwischen einer Ansicht und der obersten Layoutanleitung in einer xib-Datei hinzuzufügen?

75

In iOS 7 können wir jetzt eine Einschränkung zwischen einer Ansicht und der obersten Layoutanleitung hinzufügen. Dies ist meiner Meinung nach sehr nützlich, um das Problem mit dem Versatz der Statusleiste in iOS7 zu lösen (insbesondere, wenn die Ansicht keine Navigationsleiste enthält).

In einer Storyboard-Datei kann ich diese Art von Einschränkungen einfach hinzufügen. Halten Sie einfach die Steuertaste gedrückt und ziehen Sie die Ansicht in den Container. Daraufhin wird die Option "Top Space to Top Layout Guide" angezeigt.

Geben Sie hier die Bildbeschreibung ein

Wenn ich jedoch den gleichen Vorgang in einer xib-Datei ausführe, verschwindet diese Option.

Geben Sie hier die Bildbeschreibung ein

Gibt es eine Möglichkeit, diese Art von Einschränkungen in xib-Dateien hinzuzufügen? Oder muss ich sie mit Code hinzufügen?

Henry H Miao
quelle
Ich würde empfehlen, die "eine weitere Frage" als separate Frage zu stellen.
Hpique
1
Ich habe die gegenteilige Frage: Gibt es eine Möglichkeit, die Beschränkung auf Containeroberseite anstelle von Top Layout Guide in einem Storyboard zu beschränken?
Pavel Alexeev

Antworten:

77

Sie sollten sich auf das folgende Beispiel beziehen, dies wird Ihnen definitiv bei Ihrem Problem helfen. Ich habe dies von http://developer.apple.com erhalten .

[button setTranslatesAutoresizingMaskIntoConstraints: NO];
id topGuide = myViewController.topLayoutGuide;
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings (button, topGuide);

[myViewController.view addConstraints:
    [NSLayoutConstraint constraintsWithVisualFormat: @"V:[topGuide]-20-[button]"
    options: 0
    metrics: nil
    views: viewsDictionary]
];
Neeraj Khede
quelle
Ich habe eine UIView hinzugefügt, um die Ansicht des Controllers anzuzeigen, und ich möchte, dass sie im Falle von iOS7 ab 20 und für iOS6 ab 0 beginnt. Ich verwende den obigen Code (anstelle der Schaltfläche verwende ich meine Ansicht), aber dies ist nicht der Fall Arbeiten kann wissen, wie man das löst?
Nuzhat Zari
@NuzhatZari Sie können hier explizit nach Systemversion suchen stackoverflow.com/questions/7848766/…
joslinm
14
Es wäre schön, wenn diese Einschränkungen auch für Xcode für XIBs festgelegt werden könnten ... Xcode sollte sie nur bereitstellen (unabhängig von der Kenntnis der View-Controller-Hierarchie).
Ricardo Sanchez-Saez
1
Gibt es eine Möglichkeit, alle Unteransichten des VC unter der Statusleiste ohne Einschränkungen zu gestalten? Möchten Sie die Top-Layout-Anleitung in Storyboards verwenden?
User123335511231
14

Hier ist meine alternative Lösung.

Suchen Sie eine UIView als Drehpunkt, und legen Sie die Einschränkung für das obere Layout auf einen festen vertikalen Abstand zur Oberseite des Containers fest.

Strg-Ziehen Sie diese Layout-Einschränkung als IBOutlet, z

@property (weak, nonatomic) IBOutlet NSLayoutConstraint *topLayoutConstraint;

Zum Schluss überschreiben Sie einfach die viewWillLayoutSubviews-Methode von UIViewController wie folgt

- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];

    self.topLayoutConstraint.constant = [self.topLayoutGuide length] + YOUR_TOP_CONSTRSINT;
}

Alle obersten Einschränkungen der anderen Ansichten basieren auf dieser Pivot-Ansicht, alles erledigt :)

ananfang
quelle
Dies wird nicht gut mit der Bewegung von topLayoutGuide umgehen (sagen wir, der Benutzer ist in einem Anruf oder verwendet GPS oder etwas anderes führt dazu, dass sich der topLayoutGuide erhöht)
anonymouse
9

Ab iOS 9 können Sie dies auch einfacher mit topLayoutGuides tun anchors:

  • Swift 3

view.topAnchor.constraint(equalTo: self.topLayoutGuide.bottomAnchor).isActive = true

  • ObjC

[controller.view.topAnchor constraintEqualToAnchor:controller.topLayoutGuide.bottomAnchor].active = YES;

Radu Vlad
quelle
mit Abstand beste Lösung!
Kappe
6

Bis jetzt kann ich selbst mit XCode 6 keine Steuerelemente an der obersten Layoutanleitung für .xib-Dateien ausrichten. Stattdessen benutze ich einen alternativen Weg.

Erstens richte ich im Interface Builder die Steuerelemente immer noch am oberen Rand der Ansicht von viewcontroler aus.

Dann ersetze ich in der viewDidLoad-Methode einige Einschränkungen, damit sie anstelle der Hauptansicht am oberen Layoutleitfaden ausgerichtet werden:

- (void)viewDidLoad
{
    [super viewDidLoad];

    NSArray *constraints = self.view.constraints;
    for (NSLayoutConstraint *constraint in constraints) {
        if ( (constraint.firstItem == self.view) && (constraint.firstAttribute == NSLayoutAttributeTop) ) {
            NSLayoutConstraint *newConstraint = [self constraint:constraint replaceFirstItemBy:self.topLayoutGuide attribute:NSLayoutAttributeBottom];
            [self.view removeConstraint:constraint];
            [self.view addConstraint:newConstraint];
        } else if ( (constraint.secondItem == self.view) && (constraint.secondAttribute == NSLayoutAttributeTop) ) {
            NSLayoutConstraint *newConstraint = [self constraint:constraint replaceSecondItemBy:self.topLayoutGuide attribute:NSLayoutAttributeBottom];
            [self.view removeConstraint:constraint];
            [self.view addConstraint:newConstraint];
        }
    }
}

- (NSLayoutConstraint*)constraint:(NSLayoutConstraint*)constraint replaceFirstItemBy:(id)newItem attribute:(NSLayoutAttribute)newAttribute {
    UILayoutPriority priority = constraint.priority;
    NSLayoutRelation relation = constraint.relation;
    id secondItem = constraint.secondItem;
    NSLayoutAttribute secondAttribute = constraint.secondAttribute;
    CGFloat multiplier = constraint.multiplier;
    CGFloat constant = constraint.constant;

    NSLayoutConstraint *newConstraint = [NSLayoutConstraint constraintWithItem:newItem attribute:newAttribute relatedBy:relation toItem:secondItem attribute:secondAttribute multiplier:multiplier constant:constant];
    newConstraint.priority = priority;
    return newConstraint;
}

- (NSLayoutConstraint*)constraint:(NSLayoutConstraint*)constraint replaceSecondItemBy:(id)newItem attribute:(NSLayoutAttribute)newAttribute {
    UILayoutPriority priority = constraint.priority;
    id firstItem = constraint.firstItem;
    NSLayoutAttribute firstAttribute = constraint.firstAttribute;
    NSLayoutRelation relation = constraint.relation;
    CGFloat multiplier = constraint.multiplier;
    CGFloat constant = constraint.constant;

    NSLayoutConstraint *newConstraint = [NSLayoutConstraint constraintWithItem:firstItem attribute:firstAttribute relatedBy:relation toItem:newItem attribute:newAttribute multiplier:multiplier constant:constant];
    newConstraint.priority = priority;
    return newConstraint;
}

Denken Sie, dass dies nicht der beste Weg ist, da wir Objekte ersetzen, die wir im Interface Builder definieren. Aber es kann eine alternative Art sein, über die wir nachdenken können.

Cao Huu Loc
quelle
6

Natürlich können Sie nicht nur Einschränkungen zwischen einer Ansicht und der top layout guideoder bottom layout guideprogrammgesteuert hinzufügen, sondern auch top and bottom layout guidemithilfe der KVConstraintExtensionsMaster- Bibliothek Einschränkungen zwischen einer Ansicht und der Ansicht entfernen und darauf zugreifen .

// create containerView
UIView *containerView = [UIView prepareNewViewForAutoLayout];
[containerView setBackgroundColor:[UIColor brownColor]];
[self.view addSubview:containerView];

// To add Top and Bottom Layout Guide constraint of containerView
[self applyTopLayoutGuideConstraintToView:containerView withPadding:0];
[self applyBottomLayoutGuideConstraintToView:containerView withPadding:50];

// To access top Layout Guide Constraint and update it's constant value.
NSLayoutConstraint *topLayoutGuideConstraint = [self accessAppliedTopLayoutGuideConstraintFromView:containerView];
topLayoutGuideConstraint.constant = 50;

// To access bottom Layout Guide Constraint and update it's constant value with animation
NSLayoutConstraint *bottomLayoutGuideConstraint = [self accessAppliedBottomLayoutGuideConstraintFromView:containerView];
bottomLayoutGuideConstraint.constant = 80;
[self.view updateModifyConstraintsWithAnimation:NULL]; // call this for animation

// To remove Top and Bottom Layout Guide constraint of containerView
[self removeAppliedTopLayoutGuideConstraintFromView:containerView];
[self removeAppliedBottomLayoutGuideConstraintFromView:containerView ];
keshav vishwkarma
quelle
5

Ich denke, der Grund, warum sie in der XIB nicht angezeigt werden, ist, dass in dieser Situation keine Kenntnis der View-Controller-Hierarchie vorhanden ist, während IB in einem Storyboard erkennen kann, wo sich die Hilfslinien aus der View-Controller-Hierarchie befinden, in der sich die Ansicht befindet. Dh wenn es in einem UINavigationController enthalten ist, kann es feststellen, dass sich die obere Layoutanleitung unter der Navigationssymbolleiste befindet.

David Pettigrew
quelle
3
Das sollte keine Rolle spielen. Das ist der springende Punkt dieser Führer. Sie richten sich einfach an ihnen aus, und wenn sich diese VC in einem Navigationscontroller befindet, hat sie eine bestimmte Größe der oberen Layoutführung. Wenn es als modales VC ohne sichtbare Statusleiste dargestellt wird, ist seine Größe 0 usw. Das VC kann und sollte relativ zu seinen Hilfslinien angeordnet werden können, ohne zu wissen, wie groß es ist (zur Entwurfszeit).
CIFilter
0

Cao Huu Locs Antwort mit schneller 4

extension NSLayoutConstraint {

    func constraintReplacing(firstItemWith newFirstItem: UILayoutSupport, withAttribute newFirstAttribute: NSLayoutAttribute) -> NSLayoutConstraint {
        return NSLayoutConstraint(item: newFirstItem, attribute: newFirstAttribute, relatedBy: relation, toItem: secondItem, attribute: secondAttribute, multiplier: multiplier, constant: constant)
    }

    func constraintReplacing(secondItemWith newSecondItem: UILayoutSupport, withAttribute newSecondAttribute: NSLayoutAttribute) -> NSLayoutConstraint {
        return NSLayoutConstraint(item: firstItem as Any, attribute: firstAttribute, relatedBy: relation, toItem: newSecondItem, attribute: newSecondAttribute, multiplier: multiplier, constant: constant)
    }
}

class YourViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        for constraint in view.constraints {
            if constraint.firstItem === view && constraint.firstAttribute == .top {
                let newConstraint = constraint.constraintReplacing(firstItemWith: topLayoutGuide, withAttribute: .bottom)
                view.removeConstraint(constraint)
                view.addConstraint(newConstraint)
            }
            if constraint.secondItem === view && constraint.secondAttribute == .top {
                let newConstraint = constraint.constraintReplacing(secondItemWith: topLayoutGuide, withAttribute: .bottom)
                view.removeConstraint(constraint)
                view.addConstraint(newConstraint)
            }
        }
    }
}
f3dm76
quelle