Comment utiliser Auto-layout pour déplacer d'autres vues quand une vue est cachée?

j'ai conçu ma cellule personnalisée en IB, Je l'AI sous-classée et j'ai relié mes prises à ma classe personnalisée. J'ai trois résumés dans le contenu de la cellule qui sont: UIView (cdView) et deux étiquettes (titleLabel et emailLabel). Selon les données disponibles pour chaque ligne, parfois je veux avoir uivi et deux étiquettes affichées dans ma cellule et parfois seulement deux étiquettes. Ce que j'essaie de faire, c'est de définir les contraintes de cette façon si je mets la propriété UIView sur hidden ou je la supprime de superview les deux. les étiquettes se déplacent vers la gauche. J'ai essayé de définir UIView leading constraint à Superview (Cell content) pour 10px et UILabels leading Constraints pour 10 px à la vue suivante (UIView). Plus tard dans mon code

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

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

je cache ma cellule.cdView et moi aimerions que les étiquettes se déplacent vers la gauche mais elles restent dans la même position dans la cellule. J'ai essayé de retirer la cellule.cdView de superview mais ça n'a pas marché non plus. J'ai joint une image pour clarifier ce que je suis sur le.

cell

je sais comment faire cela programmatiquement et je ne cherche pas cette solution. Ce que je veux, c'est définir des contraintes dans IB et je m'attends à ce que mes sous-vues bougent dynamiquement si d'autres vues sont supprimées ou cachées. Est-il possible de le faire en IB avec Auto-layout?

.....
274
demandé sur Cœur 2013-08-05 23:16:01

19 réponses

c'est possible, mais vous devrez faire un peu de travail supplémentaire. Il y a quelques choses conceptuelles à faire avant tout:

  • vues cachées, même si elles ne dessinent pas, encore participer dans la mise en page automatique et habituellement conserver leurs cadres , laissant d'autres vues connexes à leur place.
  • lors de la suppression d'une vue de sa superview, toutes les contraintes connexes sont également supprimés de cette hiérarchie de vue.

Dans votre cas, cela signifie probablement:

  • si vous définissez votre vue de gauche pour être cachée, les étiquettes restent en place, puisque cette vue de gauche prend encore de l'espace (même si elle n'est pas visible).
  • si vous supprimez votre vue de gauche, vos étiquettes seront probablement laissées avec des contraintes ambiguës, puisque vous n'avez plus de contraintes pour les bords de gauche de vos étiquettes.

ce que vous devez faire est judicieusement outrepasser les limites vos étiquettes. Laissez vos contraintes existantes (espace de 10pts à l'autre vue) en paix, mais ajoutez une autre contrainte: éloignez les bords de gauche de vos étiquettes de 10pts du bord gauche de leur superview avec une priorité non requise (la haute priorité par défaut fonctionnera probablement bien).

ensuite, quand vous voulez qu'ils se déplacent à gauche, supprimez complètement la vue à gauche. Obligatoire La contrainte 10pt vers la vue de gauche disparaîtra avec la vue à laquelle elle se rapporte, et il vous restera juste une contrainte de haute priorité que les étiquettes soient à 10pts de leur superview. Lors de la prochaine passe de mise en page, cela devrait les amener à s'étendre à gauche jusqu'à ce qu'ils remplissent la largeur de la superview mais pour votre espacement autour des bords.

une mise en garde importante: si vous voulez que votre vue de gauche revienne dans l'image, non seulement vous devez l'ajouter de nouveau dans la hiérarchie de la vue, mais vous avez aussi rétablir toutes ses contraintes en même temps. Cela signifie que vous avez besoin d'un moyen pour placer votre contrainte d'espacement 10pt entre la vue et ses étiquettes de retour chaque fois que cette vue est montrée à nouveau.

329
répondu Tim 2013-08-05 19:29:19

ajouter ou supprimer des contraintes pendant l'exécution est une opération lourde qui peut affecter les performances. Cependant, il existe une solution plus simple.

Pour la vue que vous souhaitez masquer, définissez une largeur de contrainte. Limiter les autres points de vue avec un écart horizontal menant à ce point de vue.

pour masquer, mettre à jour le .constant de la contrainte de largeur à 0.F. Les autres vues bougeront automatiquement à gauche pour prendre position.

Voir mon autre réponse ici pour plus de détails:

comment modifier les contraintes d'étiquette pendant l'exécution?

186
répondu Max MacLeod 2017-05-23 12:26:36

pour ceux qui soutiennent iOS 8+ seulement, il y a une nouvelle propriété booléenne active . Il aidera à activer seulement les contraintes nécessaires dynamiquement

P. S. Contrainte de la prise de courant doit être fort , pas faible



exemple:

@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
}

aussi pour corriger une erreur dans storyboard vous devez décocher Case à cocher Installed pour l'une de ces contraintes.



UIStackView (iOS 9+)

Une option de plus est d'envelopper vos vues dans UIStackView . Une fois la vue cachée UIStackView mettra à jour la mise en page automatiquement

67
répondu Silmaril 2018-03-16 16:24:31

UIStackView repositionne automatiquement ses vues lorsque la propriété hidden est modifiée sur l'un de ses sous-vues (iOS 9+).

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

Sauter 11:48 dans cette de la WWDC vidéo pour une démonstration:

Mystères de la Mise en page Automatique, Partie 1

48
répondu FFire 2018-02-23 15:32:47

mon projet utilise une sous-classe personnalisée @IBDesignable de UILabel (pour assurer la cohérence dans la couleur, la police, les inserts, etc.) et j'ai mis en œuvre quelque chose comme ceci:

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

cela permet à la sous-classe de l'étiquette de prendre part à la mise en page automatique, mais de ne pas prendre place lorsqu'elle est cachée.

14
répondu Robert Atkins 2015-03-24 13:43:57

pour les googleurs: en s'appuyant sur la réponse de Max, pour résoudre le problème de rembourrage que beaucoup ont remarqué, j'ai simplement augmenté la hauteur de l'étiquette et utilisé cette hauteur comme le séparateur au lieu de rembourrage réel. Cette idée pourrait être développée pour n'importe quel scénario contenant des vues.

voici un exemple simple:

IB Screenshot

dans ce cas, je cartographie la hauteur du Auteur étiquette à une appropriée IBOutlet :

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

et quand je mets la hauteur de la contrainte à 0.0f , nous préservons le" rembourrage", parce que la hauteur du bouton Play le permet.

11
répondu jterry 2014-06-19 00:55:45

ce que j'ai fini par faire était de créer 2 xibs. Un avec la vue de gauche et un sans. J'ai enregistré les deux dans le contrôleur et j'ai ensuite décidé lequel utiliser pendant cellForRowAtIndexPath.

ils utilisent la même classe de Viewcell Utilisable. L'inconvénient est qu'il y a une certaine duplication du contenu entre les xibs, mais ces cellules sont assez basiques. Le bon côté est que je n'ai pas un tas de code pour gérer manuellement la suppression de la vue, la mise à jour des contraintes, etc.

en général, c'est probablement une meilleure solution car ils sont techniquement différents layouts et donc devraient avoir des xibs différents.

[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];
7
répondu Michael Peterson 2014-12-05 22:29:51

connecter la contrainte entre uiview et labels en tant Qu'IBOutlet et définir un membre de priorité à une valeur moindre lorsqu'elle est définie hidden = YES

6
répondu n0c 2013-11-06 15:11:20

dans ce cas, j'affiche la hauteur de l'étiquette de L'auteur sur un IBOutlet approprié:

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

et quand je mets la hauteur de la contrainte à 0.0 f, nous préservons le" padding", parce que la hauteur du bouton Play le permet.

cell.authorLabelHeight.constant = 0;

enter image description here enter image description here

5
répondu Mitul Marsoniya 2015-12-01 07:56:37

utiliser deux UIStackView Horizontal et Vertical, quand une vue de subview dans la pile est cachée autres sous-vues de la pile seront déplacés, utiliser la Distribution - > remplir proportionnellement pour la pile verticale avec deux UILabels et ont besoin de la largeur et de la hauteur définie constaints pour la première UIView enter image description here

4
répondu Roman Solodyashkin 2017-04-05 15:32:03

dans mon cas, j'ai placé la constante de la contrainte de hauteur 151970920" à 0.0f et j'ai aussi placé la propriété hidden à YES .

pour montrer la vue (avec les sous-vues) encore une fois j'ai fait le contraire: j'ai placé la constante de hauteur à une valeur non-nulle et j'ai placé la propriété hidden à NO .

1
répondu testing 2015-06-11 13:08:35

essayez ceci, j'ai implémenté ci-dessous le code,

j'ai une vue sur ViewController dans cela ajouté trois autres vues, quand n'importe quelle vue est cachée d'autres deux vue se déplacent,suivez les étapes ci-dessous. ,

1.ViewController.h Fichier

#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

espérons que cette logique aidera quelqu'un.

1
répondu Jaywant Khedkar 2018-02-08 11:31:13

Comme no_scene suggéré, vous pouvez certainement le faire en modifiant la priorité de la contrainte à l'exécution. C'était beaucoup plus facile pour moi parce que j'ai eu plus d'une vue de blocage qui devrait être supprimé.

voici un extrait de réactivation:

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
répondu skensell 2015-03-10 08:40:02

dans le cas où cela aide quelqu'un, j'ai construit une classe helper pour utiliser format visuel contraintes. Je l'utilise dans mon application actuelle.

AutolayoutHelper

Il pourrait être un peu adaptée à mes besoins, mais vous trouverez peut-être utile ou vous pouvez le modifier et créer votre propre helper.

je dois remercier Tim pour sa réponse ci-dessus , ce réponse à propos de UIScrollView et aussi ce tutoriel .

0
répondu Ferran Maylinch 2017-05-23 12:02:56

Voici comment je vais réaligner mes uiviews pour obtenir votre solution:

  1. Glisser-déposer d'un UIImageView et placez le à gauche.
  2. drag drop one UIView et placez-le à droite de UIImageView.
  3. drag drop deux UILabels à l'intérieur de cet UIView dont les contraintes de tête et de queue sont nulles.
  4. définit la contrainte principale D'UIView contenant 2 labels à superview au lieu de UIImagView.
  5. si UIImageView est caché, définissez la contrainte principale constante à 10 px pour superview. Autrement, définissez la contrainte principale constante à 10 px + UIImageView.Largeur + 10 px.

j'ai créé ma propre règle. Chaque fois que vous devez cacher / afficher un uiview dont les contraintes pourraient être affectées, ajoutez tous les sous-vues affectées / dépendantes à l'intérieur d'un uivi et mettez à jour sa constante de contraintes leading / trailing / top / bottom de façon programmatique.

0
répondu Deepak Thakur 2017-07-12 14:34:07

C'est une vieille question mais j'espère quand même qu'elle aidera. Venant D'Android, dans cette plate-forme, vous avez une méthode pratique isVisible pour le cacher de la vue, mais aussi ne pas avoir le cadre considéré lorsque l'autolayout tirer la vue.

en utilisant extension et" extend "uiview vous pourriez faire une fonction similaire dans ios (pas sûr pourquoi il n'est pas déjà dans UIKit) voici une implémentation dans 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{"151900920".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
répondu Vincent Choubard 2017-10-23 16:15:32

la bonne façon de le faire est de désactiver les contraintes avec isActive = false. notez cependant que désactiver une contrainte la supprime et la libère, de sorte que vous devez avoir des débouchés solides pour eux.

0
répondu Hogdotmac 2018-02-06 13:15:54

j'utiliserai horizontal stackview. Il peut supprimer le cadre lorsque le subview est caché.

dans l'image ci-dessous, la vue Rouge est le conteneur réel pour votre contenu et a 10pt espace de traînée à Orange superview (ShowHideView), puis il suffit de connecter ShowHideView à IBOutlet et show/hide/remove it programatically.

  1. c'est lorsque la vue est visible/installée.

view is visible

  1. c'est lorsque la vue est cachée/non installée.

view is hidden/removed

0
répondu mnemonic23 2018-03-05 09:45:07

Ce mon autre solution à l'aide de la priorité de la contrainte. L'idée est de définir la largeur de 0.

  1. créer une vue du conteneur (orange) et définir la largeur. enter image description here

  2. créer une vue de contenu (rouge) et définir l'espace de fuite 10pt à superview (orange). Notez les contraintes d'espace de fuite, il y a 2 contraintes de fuite avec une priorité différente. Faible (=10) et élevé (<=10). Ceci est important pour éviter toute ambiguïté. enter image description here

  3. Réglez la largeur de la vue orange à 0 pour cacher la vue. enter image description here

0
répondu mnemonic23 2018-03-05 10:03:56