J'ai conçu ma cellule personnalisée dans IB, je l'ai subdivisée et j'ai connecté mes prises à ma classe personnalisée. J'ai trois sous-vues dans le contenu de la cellule qui sont: UIView (cdView) et deux étiquettes (titleLabel et emailLabel). En fonction des données disponibles pour chaque ligne, je souhaite parfois avoir UIView et deux étiquettes affichées dans ma cellule et parfois seulement deux étiquettes. Ce que j'essaie de faire est de définir les contraintes de cette manière si je mets la propriété UIView sur masquée ou si je la supprime de la vue d'ensemble, les deux étiquettes se déplaceront à gauche. J'ai essayé de définir la contrainte principale d'UIView sur Superview (contenu de la cellule) pour 10px et UILabels contraignant les contraintes pour 10 px vers 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 mon cell.cdView et j'aimerais que les étiquettes se déplacent vers la gauche, mais restent dans la même position dans Cell. J'ai essayé de supprimer cell.cdView de Superview mais cela n'a pas fonctionné non plus. J'ai joint l'image pour clarifier ce que je suis.
Je sais comment faire cela par programmation 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 se déplacent de manière dynamique si d'autres vues sont supprimées ou masquées. Est-il possible de faire cela dans IB avec auto-layout?
.....
C'est possible, mais vous devrez faire un peu de travail supplémentaire. Il y a quelques éléments conceptuels à résoudre en premier:
Dans votre cas, cela signifie probablement:
Ce que vous devez faire, c'est bien contraindre _ vos étiquettes. Laissez vos contraintes existantes (espace de 10 points par rapport à l'autre vue), mais ajoutez une autre contrainte: placez les bords gauches de vos étiquettes à 10 points du bord gauche de leur superview avec une priorité non requise (la priorité haute par défaut fonctionnera probablement bien).
Ensuite, lorsque vous souhaitez les déplacer à gauche, supprimez complètement la vue de gauche. La contrainte obligatoire de 10 points sur la vue de gauche disparaîtra avec la vue à laquelle elle se rapporte, et il ne vous restera plus qu'une contrainte de priorité élevée voulant que les étiquettes soient distantes de 10 points de leur vue. Lors du passage suivant, cela devrait les amener à s’agrandir à gauche jusqu’à ce qu’ils remplissent la largeur de la vue supérieure, mais pour votre espacement sur les bords.
Une mise en garde importante: si vous souhaitez que votre vue gauche revienne dans l'image, vous devez non seulement la rajouter dans la hiérarchie des vues, mais vous devez également rétablir toutes ses contraintes en même temps. Cela signifie que vous avez besoin d'un moyen de rétablir votre contrainte d'espacement de 10 points entre la vue et ses étiquettes chaque fois que cette vue est à nouveau affichée.
L'ajout ou la suppression de contraintes pendant l'exécution est une opération très lourde susceptible d'affecter les performances. Cependant, il existe une alternative plus simple.
Pour la vue que vous souhaitez masquer, définissez une contrainte de largeur. Contraignez les autres vues avec un écart horizontal dominant par rapport à cette vue.
Pour masquer, mettez à jour le .constant
de la contrainte de largeur en 0.f. Les autres vues se déplaceront automatiquement à gauche pour prendre position.
Voir mon autre réponse ici pour plus de détails:
Comment changer les contraintes d'étiquette pendant l'exécution?
Pour ceux qui ne prennent en charge que iOS 8+, il existe une nouvelle propriété booléenne active . Cela aidera à activer uniquement les contraintes nécessaires de manière dynamique
P.S. La sortie de contrainte doit être forte, 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
}
Pour corriger une erreur dans le storyboard, vous devez également décocher la case Installed
pour l'une de ces contraintes .
UIStackView (iOS 9+)
Une autre option consiste à envelopper vos vues dans UIStackView
. Une fois la vue masquée, UIStackView
mettra automatiquement la mise à jour
UIStackView
repositionne automatiquement ses vues lorsque la propriété hidden
est modifiée sur l'une de ses sous-vues (iOS 9+).
UIView.animateWithDuration(1.0) { () -> Void in
self.mySubview.hidden = !self.mySubview.hidden
}
Aller à 11:48 dans cette vidéo WWDC pour une démonstration:
Les mystères de la mise en page automatique, première partie
Mon projet utilise une sous-classe @IBDesignable
personnalisée de UILabel
(pour assurer la cohérence de la couleur, de la police, des encarts, etc.) et j'ai implémenté les éléments suivants:
override func intrinsicContentSize() -> CGSize {
if hidden {
return CGSizeZero
} else {
return super.intrinsicContentSize()
}
}
Cela permet à la sous-classe d'étiquettes de participer à la mise en page automatique, mais ne prend aucun espace lorsqu'elle est masquée.
Pour les Googlers: en me basant 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 séparateur au lieu d'un rembourrage réel. Cette idée pourrait être développée pour tout scénario contenant des vues.
Voici un exemple simple:
Dans ce cas, je mappe la hauteur de Author label sur une IBOutlet
appropriée:
@property (retain, nonatomic) IBOutlet NSLayoutConstraint* authorLabelHeight;
et lorsque je règle la hauteur de la contrainte sur 0.0f
, nous conservons le "padding", car la hauteur du bouton Play le permet.
Ce que j'ai fini par créer, c'est créer 2 xibs. Un avec la vue de gauche et l'autre sans. Je me suis inscrit à la fois dans le contrôleur et ensuite décidé lequel utiliser pendant cellForRowAtIndexPath.
Ils utilisent la même classe UITableViewCell. L'inconvénient est qu'il y a une duplication du contenu entre les xibs, mais ces cellules sont assez basiques. L'avantage est que je n'ai pas beaucoup de code pour gérer manuellement la suppression de la vue, les contraintes de mise à jour, etc.
En général, il s’agit probablement d’une meilleure solution car il s’agit de dispositions techniques différentes qui doivent donc comporter des xib 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];
connecte la contrainte entre uiview et les étiquettes en tant que IBOutlet et définit le membre prioritaire sur une valeur inférieure s'il est masqué = YES
Dans ce cas, je mappe la hauteur de l'étiquette Author sur un IBOutlet approprié:
@property (retain, nonatomic) IBOutlet NSLayoutConstraint* authorLabelHeight;
et lorsque je règle la hauteur de la contrainte sur 0.0f, nous conservons le "padding", car la hauteur du bouton Play le permet.
cell.authorLabelHeight.constant = 0;
Utilisez deux UIStackView Horizontal et Vertical, quand une vue de sous-vue dans la pile est masquée, d'autres sous-vues de pile seront déplacées, utilisez Distribution -> Remplir proportionnellement pour la pile verticale avec deux étiquettes UIL et vous devez définir des constances de largeur et de hauteur pour la première utilisation d'UIView
Essayez ceci, j'ai implémenté le code ci-dessous,
J'ai une vue sur ViewController dans cette trois autres vues ajoutées, Quand une vue est masquée, deux autres vues 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
Espérons que cette logique aidera quelqu'un.
Dans mon cas, j'ai défini la constante de la contrainte height sur 0.0f
et la propriété hidden
sur YES
.
Pour afficher à nouveau la vue (avec les sous-vues), j’ai fait l’opposé: j’ai défini la constante de hauteur sur une valeur non nulle et la propriété hidden
à NO
.
Ceci est une autre solution utilisant la contrainte de priorité. L'idée est de définir la largeur à 0.
créer une vue du contenu (rouge) et définir un espace de fin 10 points sur superview (orange). Remarquez les contraintes d'espace de fin, il y a 2 contraintes de fin avec une priorité différente. Faible (= 10) et élevé (<= 10). Ceci est important pour éviter toute ambiguïté .
Définissez la largeur de la vue orange sur 0 pour masquer la vue .
Comme no_scene l'a suggéré, vous pouvez certainement le faire en modifiant la priorité de la contrainte lors de l'exécution. Cela a été beaucoup plus facile pour moi car j'avais plus d'une vue bloquante qui devrait être supprimée.
Voici un extrait utilisant 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];
}];
}];
Je pense que c'est la réponse la plus simple s'il vous plaît vérifier s'il fonctionne bien
StackFullView.layer.isHidden = true
Task_TopSpaceSections.constant = 0. //your constrain of top view
vérifier ici https://www.youtube.com/watch?v=EBulMWMoFuw
Voici comment je réalignerais mes uiviews pour obtenir votre solution:
J'ai créé ma propre règle du pouce. Chaque fois que vous devez masquer/afficher toute vue uiview dont les contraintes peuvent être affectées, ajoutez toutes les sous-vues concernées/dépendantes dans une vue uiview et mettez à jour sa constante de contrainte de début/fin/haut/bas par programme.
Au cas où cela aiderait quelqu'un, j'ai construit une classe d'assistance pour utiliser les contraintes visual format . Je l'utilise dans mon application actuelle.
Il est peut-être un peu adapté à mes besoins, mais vous le trouverez peut-être utile ou vous voudrez peut-être le modifier et créer votre propre aide.
Je dois remercier Tim pour sa réponse ci-dessus , cette réponse à propos de UIScrollView et aussi ce tutorial .
la bonne façon de le faire est de désactiver les contraintes avec isActive = false. notez cependant que la désactivation d'une contrainte la supprime et la libère, vous devez donc disposer de points de vente puissants.
C'est une vieille question mais j'espère que cela aidera. Venant d'Android, cette plate-forme vous offre une méthode pratique isVisible
pour la masquer de la vue, mais vous ne devez pas non plus prendre en compte le cadre lorsque la fonctionnalité de retrait automatique dessine la vue.
en utilisant extension et "extend" uiview, vous pourriez faire une fonction similaire dans iOS (vous ne savez pas pourquoi ce n’est pas déjà dans UIKit), ici 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{$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()
}
Je vais utiliser stackview horizontal. Il peut supprimer le cadre lorsque la sous-vue est masquée.
Dans l'image ci-dessous, la vue rouge est le conteneur réel de votre contenu et a un espace de fin de 10 points vers orange superview (ShowHideView). Il vous suffit ensuite de connecter ShowHideView à IBOutlet et d'afficher/masquer/supprimer le programme.