it-swarm.com.de

Wie verwende ich das automatische Layout, um andere Ansichten zu verschieben, wenn eine Ansicht ausgeblendet ist?

Ich habe meine benutzerdefinierte Zelle in IB entworfen, sie in Unterklassen unterteilt und meine Verkaufsstellen mit meiner benutzerdefinierten Klasse verbunden. Ich habe drei Unteransichten im Zellinhalt: UIView (cdView) und zwei Labels (titleLabel und emailLabel). Abhängig von den für jede Zeile verfügbaren Daten möchte ich manchmal UIView und zwei Beschriftungen in meiner Zelle und manchmal nur zwei Beschriftungen anzeigen lassen. Was ich versuche zu tun, ist das Festlegen von Einschränkungen auf diese Weise, wenn ich die UIView-Eigenschaft auf "hidden" setze oder sie aus dem Überblick entferne. Die beiden Labels werden nach links verschoben. Ich habe versucht, UIView als führende Einschränkung auf Superview (Zelleninhalt) für 10px und UILabels führende Einschränkungen für 10 px für die nächste Ansicht (UIView) festzulegen. Später in meinem Code 

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(IndexPath *)indexPath {
...
Record *record = [self.records objectAtIndex:indexPath.row];

if ([record.imageURL is equalToString:@""]) {
     cell.cdView.hidden = YES;
}

Ich verstecke meine cell.cdView und möchte, dass die Beschriftungen nach links verschoben werden, obwohl sie sich in derselben Position in Cell befinden. Ich habe versucht, cell.cdView aus dem Superview zu entfernen, aber es hat auch nicht funktioniert. Ich habe ein Bild beigefügt, um zu erklären, worum es mir geht.

cell

Ich weiß, wie man das programmgesteuert macht und suche nicht nach dieser Lösung. Was ich möchte, ist das Festlegen von Einschränkungen in IB und ich erwarte, dass meine Unteransichten dynamisch verschoben werden, wenn andere Ansichten entfernt oder ausgeblendet werden. Kann man dies in IB mit automatischem Layout machen?

.....
289
Guferos

Es ist möglich, aber Sie müssen etwas mehr Arbeit leisten. Es gibt ein paar konzeptionelle Dinge, die Sie zuerst aus dem Weg räumen können:

  • Verborgene Ansichten, auch wenn sie nicht zeichnen, noch teilnehmen im automatischen Layout und in der Regel behalten ihre Frames, wobei andere verwandte Ansichten an ihrem Platz verbleiben.
  • Wenn Sie eine Ansicht aus ihrer Übersicht entfernen, werden auch alle verwandten Einschränkungen aus dieser Ansichtshierarchie entfernt.

In Ihrem Fall bedeutet dies wahrscheinlich:

  • Wenn Sie Ihre linke Ansicht als ausgeblendet festlegen, bleiben die Beschriftungen an Ort und Stelle, da diese linke Ansicht immer noch Platz beansprucht (auch wenn sie nicht sichtbar ist).
  • Wenn Sie die linke Ansicht entfernen, werden Ihre Beschriftungen wahrscheinlich mehrdeutig eingeschränkt, da Sie keine Einschränkungen mehr für die linken Ränder der Beschriftungen haben.

Was Sie tun müssen, ist vernünftigerweise over-constrain Ihre Etiketten. Lassen Sie Ihre vorhandenen Einschränkungen (10 Pkt. Abstand zur anderen Ansicht) allein, fügen Sie jedoch eine weitere Einschränkung hinzu: Legen Sie die linken Ränder der Beschriftungen 10 Pts vom linken Rand des Superviews entfernt mit einer nicht erforderlichen Priorität fest (die standardmäßige hohe Priorität funktioniert wahrscheinlich gut).

Wenn Sie möchten, dass sie sich nach links bewegen, entfernen Sie die linke Ansicht insgesamt. Die verbindliche 10-Punkt-Einschränkung für die linke Ansicht verschwindet zusammen mit der Ansicht, auf die sie sich bezieht, und es bleibt nur eine Einschränkung mit hoher Priorität, dass die Beschriftungen 10 Punkte von ihrem Superview entfernt sind. Beim nächsten Layout-Durchlauf sollten sie dann nach links erweitert werden, bis sie die Breite der Überblendung ausfüllen, jedoch nicht für Ihren Abstand um die Kanten.

Eine wichtige Einschränkung: Wenn Sie Ihre linke Ansicht immer wieder im Bild haben möchten, müssen Sie sie nicht nur wieder in die Ansichtshierarchie einfügen, sondern Sie müssen auch alle Einschränkungen wieder herstellen gleichzeitig erstellen. Dies bedeutet, dass Sie einen Weg benötigen, um die 10pt-Abstandsbeschränkung zwischen der Ansicht und ihren Beschriftungen wieder herzustellen, wenn diese Ansicht erneut angezeigt wird.

339
Tim

Das Hinzufügen oder Entfernen von Einschränkungen während der Laufzeit ist ein schwerer Vorgang, der die Leistung beeinträchtigen kann. Es gibt jedoch eine einfachere Alternative.

Richten Sie für die Ansicht, die Sie ausblenden möchten, eine Breitenbeschränkung ein. Beschränken Sie die anderen Ansichten mit einer führenden horizontalen Lücke auf diese Ansicht.

Aktualisieren Sie zum Ausblenden den .constant der Breitenbeschränkung auf 0.f. Die anderen Ansichten bewegen sich automatisch nach links, um die Position einzunehmen.

Sehen Sie meine andere Antwort hier für weitere Details:

Wie kann ich Labelbeschränkungen während der Laufzeit ändern?

194
Max MacLeod

Für diejenigen, die nur iOS 8+ unterstützen, gibt es eine neue boolesche Eigenschaft active . Es wird helfen, nur erforderliche Einschränkungen dynamisch zu aktivieren

P.S. Constraint-Auslass muss strong sein, nicht schwach

Beispiel:

@IBOutlet weak var optionalView: UIView!
@IBOutlet var viewIsVisibleConstraint: NSLayoutConstraint!
@IBOutlet var viewIsHiddenConstraint: NSLayoutConstraint!

func showView() {
    optionalView.isHidden = false
    viewIsVisibleConstraint.isActive = true
    viewIsHiddenConstraint.isActive = false
}

func hideView() {
    optionalView.isHidden = true
    viewIsVisibleConstraint.isActive = false
    viewIsHiddenConstraint.isActive = true
}

Um einen Fehler im Storyboard zu beheben, müssen Sie das Kontrollkästchen Installed für eine dieser Einschränkungen deaktivieren.

UIStackView (iOS 9+)
Eine weitere Option besteht darin, Ihre Ansichten in UIStackView einzuhüllen. Sobald die Ansicht ausgeblendet ist, wird UIStackView das Layout automatisch aktualisieren

74
Silmaril

UIStackView positioniert seine Ansichten automatisch neu, wenn die hidden-Eigenschaft in einer ihrer Unteransichten geändert wird (iOS 9+). 

UIView.animateWithDuration(1.0) { () -> Void in
   self.mySubview.hidden = !self.mySubview.hidden
}

Gehe zu 11:48 in diesem WWDC-Video für eine Demo:

Geheimnisse des automatischen Layouts, Teil 1

56
bonus

Mein Projekt verwendet eine benutzerdefinierte @IBDesignable-Unterklasse von UILabel (um Konsistenz in Farbe, Schriftart, Einfügungen usw. zu gewährleisten), und ich habe Folgendes implementiert:

override func intrinsicContentSize() -> CGSize {
    if hidden {
        return CGSizeZero
    } else {
        return super.intrinsicContentSize()
    }
}

Dadurch kann die Label-Unterklasse am automatischen Layout teilnehmen, nimmt jedoch keinen Platz ein, wenn sie ausgeblendet ist.

17
Robert Atkins

Für die Googlers: Aufbauend auf Max 'Antwort: Um das Problem der Polsterung zu lösen, das viele bemerkt haben, habe ich einfach die Höhe des Etiketts erhöht und diese Höhe als Trennzeichen anstelle der eigentlichen Polsterung verwendet. Diese Idee kann für jedes Szenario mit Ansichten erweitert werden.

Hier ist ein einfaches Beispiel:

IB Screenshot

In diesem Fall ordne ich die Höhe des Author - Labels einer geeigneten IBOutlet zu:

@property (retain, nonatomic) IBOutlet NSLayoutConstraint* authorLabelHeight;

und wenn ich die Höhe der Beschränkung auf 0.0f setze, behalten wir die "Auffüllung" bei, da die Höhe der Play - Schaltfläche dies zulässt.

13
jterry

Am Ende habe ich 2 Xibs erstellt. Eine mit der linken Ansicht und eine ohne. Ich habe beide im Controller registriert und dann entschieden, welche Elemente während cellForRowAtIndexPath verwendet werden sollen.

Sie verwenden dieselbe UITableViewCell-Klasse. Der Nachteil ist, dass der Inhalt zwischen den Xibs doppelt vorhanden ist, aber diese Zellen sind ziemlich einfach. Der Vorteil ist, dass ich nicht über eine Menge Code verfügt, um manuell das Entfernen von Ansichten, das Aktualisieren von Einschränkungen usw. zu verwalten.

Im Allgemeinen ist dies wahrscheinlich eine bessere Lösung, da sie technisch unterschiedliche Layouts haben und daher unterschiedliche Xibs aufweisen sollten.

[self.table registerNib:[UINib nibWithNibName:@"TrackCell" bundle:nil] forCellReuseIdentifier:@"TrackCell"];
[self.table registerNib:[UINib nibWithNibName:@"TrackCellNoImage" bundle:nil] forCellReuseIdentifier:@"TrackCellNoImage"];

TrackCell *cell = [tableView dequeueReusableCellWithIdentifier:(appDelegate.showImages ? @"TrackCell" : @"TrackCellNoImage") forIndexPath:indexPath];
8

verbinden Sie die Einschränkung zwischen uiview und den Bezeichnungen als IBOutlet und setzen Sie das Prioritätsmitglied auf einen niedrigeren Wert, wenn hidden = YES eingestellt ist

8
n0c

In diesem Fall ordne ich die Höhe des Author-Labels einem entsprechenden IBOutlet zu:

@property (retain, nonatomic) IBOutlet NSLayoutConstraint* authorLabelHeight;

und wenn ich die Höhe der Beschränkung auf 0,0f setze, behalten wir die "Auffüllung" bei, da die Höhe des Play-Buttons dies erlaubt.

cell.authorLabelHeight.constant = 0;

 enter image description here  enter image description here

6
Mitul Marsoniya

Verwenden Sie zwei UIStackView-Funktionen Horizontal und Vertikal. Wenn einige Unteransichten des Stapels ausgeblendet sind und andere Stapelunteransichten verschoben werden, verwenden Sie Verteilung -> Proportional füllen für den vertikalen Stapel mit zwei UILabels und müssen für die erste UIView feste Breiten- und Höhenkonstanten festlegen enter image description here

5

Versuchen Sie dies, ich habe folgenden Code implementiert,

Ich habe eine Ansicht auf ViewController, in der weitere drei Ansichten hinzugefügt wurden. Wenn eine Ansicht ausgeblendet wird, werden sich die anderen beiden Ansichten verschieben. 

1.ViewController.h Datei 

#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@property (strong, nonatomic) IBOutlet UIView *viewOne;
@property (strong, nonatomic) IBOutlet UIView *viewTwo;
@property (strong, nonatomic) IBOutlet UIView *viewThree;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewOneWidth;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewTwoWidth;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewThreeWidth;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewBottomWidth;
@end

2.ViewController.m

 #import "ViewController.h"
 @interface ViewController ()
{
  CGFloat viewOneWidthConstant;
  CGFloat viewTwoWidthConstant;
  CGFloat viewThreeWidthConstant;
  CGFloat viewBottomWidthConstant;
}
@end

@implementation ViewController
@synthesize viewOne, viewTwo, viewThree;

- (void)viewDidLoad {
  [super viewDidLoad];
 // Do any additional setup after loading the view, typically from a 
  nib.

  /*
   0  0   0
   0  0   1
   0  1   0
   0  1   1
   1  0   0
   1  0   1
   1  1   0
   1  1   1
   */

  //    [viewOne setHidden:NO];
  //    [viewTwo setHidden:NO];
  //    [viewThree setHidden:NO];

  //    [viewOne setHidden:NO];
  //    [viewTwo setHidden:NO];
  //    [viewThree setHidden:YES];

  //    [viewOne setHidden:NO];
  //    [viewTwo setHidden:YES];
  //    [viewThree setHidden:NO];

  //    [viewOne setHidden:NO];
  //    [viewTwo setHidden:YES];
  //    [viewThree setHidden:YES];


  //    [viewOne setHidden:YES];
  //    [viewTwo setHidden:NO];
  //    [viewThree setHidden:NO];

  //    [viewOne setHidden:YES];
  //    [viewTwo setHidden:NO];
  //    [viewThree setHidden:YES];

 //    [viewOne setHidden:YES];
 //    [viewTwo setHidden:YES];
 //    [viewThree setHidden:NO];

//    [viewOne setHidden:YES];
//    [viewTwo setHidden:YES];
//    [viewThree setHidden:YES];

 [self hideShowBottomBar];
  }

- (void)hideShowBottomBar
{
  BOOL isOne = !viewOne.isHidden;
  BOOL isTwo = !viewTwo.isHidden;
  BOOL isThree = !viewThree.isHidden;

  viewOneWidthConstant = _viewOneWidth.constant;
  viewTwoWidthConstant = _viewTwoWidth.constant;
  viewThreeWidthConstant = _viewThreeWidth.constant;
  viewBottomWidthConstant = _viewBottomWidth.constant;

   if (isOne && isTwo && isThree) {
    // 0    0   0
    _viewOneWidth.constant = viewBottomWidthConstant / 3;
    _viewTwoWidth.constant = viewBottomWidthConstant / 3;
    _viewThreeWidth.constant = viewBottomWidthConstant / 3;
    }
    else if (isOne && isTwo && !isThree) {
     // 0    0   1
    _viewOneWidth.constant = viewBottomWidthConstant / 2;
    _viewTwoWidth.constant = viewBottomWidthConstant / 2;
    _viewThreeWidth.constant = 0;
    }
   else if (isOne && !isTwo && isThree) {
    // 0    1   0
    _viewOneWidth.constant = viewBottomWidthConstant / 2;
    _viewTwoWidth.constant = 0;
    _viewThreeWidth.constant = viewBottomWidthConstant / 2;
    }
    else if (isOne && !isTwo && !isThree) {
    // 0    1   1
    _viewOneWidth.constant = viewBottomWidthConstant;
    _viewTwoWidth.constant = 0;
    _viewThreeWidth.constant = 0;
   }
   else if (!isOne && isTwo && isThree) {
    // 1    0   0
    _viewOneWidth.constant = 0;
    _viewTwoWidth.constant = viewBottomWidthConstant / 2;
    _viewThreeWidth.constant = viewBottomWidthConstant / 2;
   }
   else if (!isOne && isTwo && !isThree) {
    // 1    0   1
    _viewOneWidth.constant = 0;
    _viewTwoWidth.constant = viewBottomWidthConstant;
    _viewThreeWidth.constant = 0;
   }
   else if (!isOne && !isTwo && isThree) {
    // 1    1   0
    _viewOneWidth.constant = 0;
    _viewTwoWidth.constant = 0;
    _viewThreeWidth.constant = viewBottomWidthConstant;
   }
   else if (isOne && isTwo && isThree) {
    // 1    1   1
    _viewOneWidth.constant = 0;
    _viewTwoWidth.constant = 0;
    _viewThreeWidth.constant = 0;
   }
  }

 - (void)didReceiveMemoryWarning {
  [super didReceiveMemoryWarning];
 // Dispose of any resources that can be recreated.
 }
 @end

 enter image description here  enter image description here  enter image description here

Hoffnung Also diese Logik wird jemandem helfen.

2
Jaywant Khedkar

In meinem Fall setze ich die Konstante der height constraint auf 0.0f und auch die hidden-Eigenschaft auf YES.

Um die Ansicht (mit den Subviews) wieder anzuzeigen, habe ich das Gegenteil getan: Ich habe die Höhenkonstante auf einen Wert ungleich Null gesetzt und die hidden-Eigenschaft auf NO gesetzt.

1
testing

Dies ist eine andere Lösung mit Prioritätsbeschränkung. Die Idee ist die Breite auf 0 gesetzt.

  1. containeransicht (orange) erstellen und Breite einstellen .  enter image description here

  2. erstellen Sie eine Inhaltsansicht (rot) und setzen Sie den Nachlauf 10pt auf Superview (orange). Beachten Sie nachfolgende Leerzeichen-Einschränkungen. Es gibt zwei nachfolgende Einschränkungen mit unterschiedlicher Priorität. Low (= 10) und High (<= 10). Dies ist wichtig, um Mehrdeutigkeiten zu vermeiden .  enter image description here

  3. Setzen Sie die Breite der orangefarbenen Ansicht auf 0, um die Ansicht auszublenden .  enter image description here

1
mnemonic23

Wie in no_scene vorgeschlagen, können Sie dies definitiv tun, indem Sie die Priorität der Einschränkung zur Laufzeit ändern. Dies war für mich viel einfacher, da ich mehr als eine blockierende Sicht hatte, die entfernt werden müsste.

Hier ist ein Ausschnitt aus ReactiveCocoa:

RACSignal* isViewOneHiddenSignal = RACObserve(self.viewModel, isViewOneHidden);
RACSignal* isViewTwoHiddenSignal = RACObserve(self.viewModel, isViewTwoHidden);
RACSignal* isViewThreeHiddenSignal = RACObserve(self.viewModel, isViewThreeHidden);
RAC(self.viewOne, hidden) = isViewOneHiddenSignal;
RAC(self.viewTwo, hidden) = isViewTwoHiddenSignal;
RAC(self.viewThree, hidden) = isViewThreeHiddenSignal;

RAC(self.viewFourBottomConstraint, priority) = [[[[RACSignal
    combineLatest:@[isViewOneHiddenSignal,
                    isViewTwoHiddenSignal,
                    isViewThreeHiddenSignal]]
    and]
    distinctUntilChanged]
    map:^id(NSNumber* allAreHidden) {
        return [allAreHidden boolValue] ? @(780) : @(UILayoutPriorityDefaultHigh);
    }];

RACSignal* updateFramesSignal = [RACObserve(self.viewFourBottomConstraint, priority) distinctUntilChanged];
[updateFramesSignal
    subscribeNext:^(id x) {
        @strongify(self);
        [self.view setNeedsUpdateConstraints];
        [UIView animateWithDuration:0.3 animations:^{
            [self.view layoutIfNeeded];
        }];
    }];
0
skensell

Ich denke, das ist die einfachste Antwort. Bitte überprüfen Sie, ob sie gut funktioniert

        StackFullView.layer.isHidden = true
        Task_TopSpaceSections.constant = 0.   //your constrain of top view

überprüfen Sie hier https://www.youtube.com/watch?v=EBulMWMoFuw

0
Gowthaman M

So würde ich meine Ansichten neu ausrichten, um Ihre Lösung zu erhalten:

  1. Ziehen Sie eine UIImageView und ziehen Sie sie nach links.
  2. Ziehen Sie eine UIView per Drag & Drop rechts neben UIImageView. 
  3. Ziehen Sie zwei UILabels innerhalb dieses UIView-Objekts ab, deren führende und nachstehende Einschränkungen Null sind.
  4. Legen Sie die führende Einschränkung von UIView, die 2 Beschriftungen enthält, auf Superview anstelle von UIImagView fest.
  5. Wenn UIImageView ausgeblendet ist, setzen Sie die Konstante für die führende Einschränkung auf 10 px (Superview). ELSE, setzen Sie die Konstante der führenden Bedingung auf 10 px + UIImageView.width + 10 px.

Ich habe eine eigene Daumenregel erstellt. Wenn Sie eine Uiview, deren Einschränkungen möglicherweise betroffen sind, ein-/ausblenden möchten, fügen Sie alle betroffenen/abhängigen Unteransichten in einer Uiview hinzu und aktualisieren die Konstante für die führende/nachlaufende/obere/untere Bedingung programmgesteuert. 

0
Deepak Thakur

Falls dies jemandem hilft, habe ich eine Hilfsklasse für die Verwendung von visuellem Format Einschränkungen erstellt. Ich verwende es in meiner aktuellen App.

AutolayoutHelper

Es könnte ein wenig auf meine Bedürfnisse zugeschnitten sein, aber Sie finden es vielleicht nützlich oder möchten es modifizieren und einen eigenen Helfer erstellen.

Ich danke Tim für seine Antwort oben , diese Antwort zu UIScrollView und auch diese Anleitung .

0
Ferran Maylinch

der beste Weg, dies zu tun, ist das Deaktivieren von Einschränkungen mit isActive = false. Beachten Sie jedoch, dass das Deaktivieren einer Einschränkung sie entfernt und freigibt, sodass Sie über starke Auslässe verfügen müssen.

0
Hogdotmac

Dies ist eine alte Frage, aber ich hoffe trotzdem, dass es hilft. Von Android aus haben Sie auf dieser Plattform eine praktische Methode, isVisible, um sie aus der Ansicht auszublenden, aber nicht den Rahmen berücksichtigen, wenn das automatische Layout die Ansicht zeichnet.

mit extension und "extend" uiview könnten Sie eine ähnliche Funktion in ios ausführen (nicht sicher, warum es nicht bereits in UIKit ist) Hier eine Implementierung in Swift 3:

    func isVisible(_ isVisible: Bool) {
        self.isHidden = !isVisible
        self.translatesAutoresizingMaskIntoConstraints = isVisible
        if isVisible { //if visible we remove the hight constraint 
            if let constraint = (self.constraints.filter{$0.firstAttribute == .height}.first){
                self.removeConstraint(constraint)
            }
        } else { //if not visible we add a constraint to force the view to have a hight set to 0
            let height = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal , toItem: nil, attribute: .notAnAttribute, multiplier: 0, constant: 0)
            self.addConstraint(height)
        }
        self.layoutIfNeeded()
    }
0

Ich werde die horizontale Stapelansicht verwenden. Es kann den Rahmen entfernen, wenn die Unteransicht ausgeblendet ist. 

In der Abbildung unten ist die rote Ansicht der eigentliche Container für Ihren Inhalt und enthält 10pt Leerzeichen bis zum orangefarbenen Superview (ShowHideView). Verbinden Sie dann ShowHideView einfach mit IBOutlet und zeigen Sie es programmatisch an.

  1. Dies ist, wenn die Ansicht sichtbar ist/installiert ist.

 view is visible

  1. Dies ist, wenn die Ansicht ausgeblendet/nicht installiert ist.

 view is hidden/removed

0
mnemonic23