Avec Auto Layout, comment rendre la taille d'une UIImageView dynamic en fonction de l'image?

Je veux que mon UIImageView augmente ou diminue en fonction de la taille de l'image affichée. Mais je veux qu'il rest centré verticalement et à 10 pts du bord d'attaque de la superview.

Cependant, si je mets ces deux contraintes, il se plaint que je n'ai pas mis suffisamment de contraintes pour satisfaire Auto Layout. Pour moi, il semble décrire parfaitement ce que je veux. Qu'est-ce que je rate?

La taille insortingnsèque de la vue d'image dépend déjà de la taille de l'image. Vos hypothèses (et les contraintes sont correctes).

Toutefois, si vous avez configuré votre vue d'image dans le Générateur d'interface et que vous ne lui avez pas fourni d'image, le système de layout (Générateur d'interface) ne connaîtra pas la taille de votre vue d'image au moment de la compilation. Votre layout est ambiguë car la vue de votre image peut être de plusieurs tailles. C'est ce qui jette les erreurs.

Une fois que vous avez défini la propriété d'image de votre vue d'image, la taille insortingnsèque de la vue d'image est définie par la taille de l'image. Si vous définissez la vue lors de l'exécution, vous pouvez faire exactement ce qu'Anna a mentionné et fournir un générateur d'interface avec une taille insortingnsèque «d'espace réservé» dans l'inspecteur des propriétés de l'affichage de l'image. Cela indique au constructor d'interface, "utilisez cette taille pour l'instant, je vous donnerai une taille réelle plus tard". Les contraintes d'espace réservé sont ignorées lors de l'exécution.

Votre autre option consiste à affecter directement l'image à la vue de l'image dans le générateur d'interface (mais je suppose que vos images sont dynamics, cela ne fonctionnera donc pas pour vous).

Voici le problème essentiel: la seule façon dont UIImageView interagit avec Auto Layout est via sa propriété insortingnsicContentSize . Cette propriété fournit la taille insortingnsèque de l'image elle-même, et rien de plus. Cela crée trois situations, avec des solutions différentes.

cas 1: voir avec la taille de l'image

Dans le premier cas, si vous placez un UIImageView dans un context où les contraintes Auto Layout externes n'affectent que sa position, alors le insortingnsicContentSize la vue déclare qu'il veut être la taille de son image, et Auto Layout redimensionne automatiquement la vue pour l'égaler taille de son image. C'est parfois exactement ce que vous voulez, et alors la vie est bonne!

cas 2: taille de la vue entièrement contrainte, préservant le format de l'image

À d'autres moments, vous êtes dans un cas différent. Vous savez exactement la taille que vous souhaitez prendre pour la vue d'image, mais vous souhaitez également qu'elle conserve le format de l'image affichée. Dans ce cas, vous pouvez utiliser la mise en forme automatique pour limiter la taille de l'image, tout en définissant l'ancienne propriété .scaleAspectFit sur .scaleAspectFit dans la vue de l'image. Cette propriété amènera l'affichage de l'image à resize l'image affichée, en ajoutant un remplissage si nécessaire. Et si c'est ce que vous voulez, la vie est belle!

cas 3: taille de la vue partiellement contrainte, adaptation au format de l'image

Mais souvent, vous êtes dans le troisième cas délicat. Vous voulez utiliser la layout automatique pour déterminer partiellement la taille de la vue, tout en permettant au rapport d'aspect de l'image de déterminer l'autre partie. Par exemple, vous pouvez utiliser les contraintes Auto Layout pour déterminer une dimension de la taille (comme la largeur, dans un défilement vertical), tout en vous appuyant sur la vue pour indiquer à Auto Layout qu'une hauteur correspondant au format de l'image ( donc les objects qui défilent ne sont pas trop courts ou trop grands).

Vous ne pouvez pas configurer une vue d'image ordinaire pour cela car une vue d'image ne communique que son insortingnsicContentSize . Ainsi, si vous placez une vue d'image dans un context où la layout automatique contraint une dimension (par exemple en la restreignant à une largeur réduite), la vue d'image n'indique pas à Auto Layout qu'elle veut resize sa hauteur en tandem. Il continue à signaler sa taille de contenu insortingnsèque, avec la hauteur non modifiée de l'image elle-même.

Afin de configurer l'affichage de l'image pour indiquer à Auto Layout qu'il veut prendre le format de l'image qu'il contient, vous devez append une nouvelle contrainte à cet effet. De plus, vous devrez mettre à jour cette contrainte chaque fois que l'image elle-même est mise à jour. Vous pouvez créer une sous-class UIImageView qui le fait, donc le comportement est automatique. Voici un exemple:

 public class ScaleAspectFitImageView : UIImageView { /// constraint to maintain same aspect ratio as the image private var aspectRatioConstraint:NSLayoutConstraint? = nil required public init?(coder aDecoder: NSCoder) { super.init(coder:aDecoder) self.setup() } public override init(frame:CGRect) { super.init(frame:frame) self.setup() } public override init(image: UIImage!) { super.init(image:image) self.setup() } public override init(image: UIImage!, highlightedImage: UIImage?) { super.init(image:image,highlightedImage:highlightedImage) self.setup() } override public var image: UIImage? { didSet { self.updateAspectRatioConstraint() } } private func setup() { self.contentMode = .scaleAspectFit self.updateAspectRatioConstraint() } /// Removes any pre-existing aspect ratio constraint, and adds a new one based on the current image private func updateAspectRatioConstraint() { // remove any existing aspect ratio constraint if let c = self.aspectRatioConstraint { self.removeConstraint(c) } self.aspectRatioConstraint = nil if let imageSize = image?.size, imageSize.height != 0 { let aspectRatio = imageSize.width / imageSize.height let c = NSLayoutConstraint(item: self, atsortingbute: .width, relatedBy: .equal, toItem: self, atsortingbute: .height, multiplier: aspectRatio, constant: 0) // a priority above fitting size level and below low c.priority = (UILayoutPriorityDefaultLow + UILayoutPriorityFittingSizeLevel) / 2.0 self.addConstraint(c) self.aspectRatioConstraint = c } } } 

Plus de commentaires sont disponibles dans cet article

Si vous lisez les documents sur UIImageView, ils disent:

La définition de la propriété image ne modifie pas la taille d'un UIImageView. Appelez sizeToFit pour ajuster la taille de la vue pour qu'elle corresponde à l'image.

Voici une AspectFit UIImageView AspectFit UIImageView -derived qui fonctionne avec Auto Layout lorsqu'une seule dimension est contrainte. L'autre sera réglé automatiquement. Il conservera le rapport d'aspect de l'image et n'appenda aucune marge autour de l'image.

Il a modifié l'implémentation Objective-C de l' idée @algal avec les différences suivantes:

  • La priority = (UILayoutPriorityDefaultLow + UILayoutPriorityFittingSizeLevel) / 2.0 expression priority = (UILayoutPriorityDefaultLow + UILayoutPriorityFittingSizeLevel) / 2.0 qui évalue à 150 n'est pas suffisante pour battre la priorité de 1000 de la contrainte de taille de contenu d'image par défaut. La priorité de contrainte d'aspect a donc été augmentée à 1000 également.
  • il supprime \ re-ajoute la contrainte de rapport d'aspect seulement si nécessaire. Gardez à l'esprit qu'il s'agit d'une opération lourde, il n'est donc pas nécessaire de le faire si le rapport d'aspect est le même. De plus, image réglage de l' image peut être appelé plusieurs fois, c'est donc une bonne idée de ne pas provoquer de calculs de layout supplémentaires ici.
  • Vous ne savez pas pourquoi nous devons surcharger le required public init?(coder aDecoder: NSCoder) et public override init(frame:CGRect) , donc ces deux sont retirés. Ils ne sont pas remplacés par UIImageView , c'est pourquoi il est inutile d'append la contrainte d'aspect jusqu'à ce qu'une image soit définie.

AspectKeepUIImageView.h :

 NS_ASSUME_NONNULL_BEGIN @interface AspectKeepUIImageView : UIImageView - (instancetype)initWithImage:(nullable UIImage *)image; - (instancetype)initWithImage:(nullable UIImage *)image highlightedImage:(nullable UIImage *)highlightedImage; @end NS_ASSUME_NONNULL_END 

AspectKeepUIImageView.m :

 #import "AspectKeepUIImageView.h" @implementation AspectKeepUIImageView { NSLayoutConstraint *_aspectContraint; } - (instancetype)initWithImage:(nullable UIImage *)image { self = [super initWithImage:image]; [self initInternal]; return self; } - (instancetype)initWithImage:(nullable UIImage *)image highlightedImage:(nullable UIImage *)highlightedImage { self = [super initWithImage:image highlightedImage:highlightedImage]; [self initInternal]; return self; } - (void)initInternal { self.contentMode = UIViewContentModeScaleAspectFit; [self updateAspectConstraint]; } - (void)setImage:(UIImage *)image { [super setImage:image]; [self updateAspectConstraint]; } - (void)updateAspectConstraint { CGSize imageSize = self.image.size; CGFloat aspectRatio = imageSize.height > 0.0f ? imageSize.width / imageSize.height : 0.0f; if (_aspectContraint.multiplier != aspectRatio) { [self removeConstraint:_aspectContraint]; _aspectContraint = [NSLayoutConstraint constraintWithItem:self atsortingbute:NSLayoutAtsortingbuteWidth relatedBy:NSLayoutRelationEqual toItem:self atsortingbute:NSLayoutAtsortingbuteHeight multiplier:aspectRatio constant:0.f]; _aspectContraint.priority = UILayoutPriorityRequired; [self addConstraint:_aspectContraint]; } } @end