web-dev-qa-db-fra.com

Alignement UIImageView avec Aspect Fit

pour UIImageView, je choisis Aspect Fit (InterfaceBuilder), mais comment puis-je modifier l'alignement vertical?

69
Raegtime

[EDIT - ce code est un peu moisi étant de 2011 et tout sauf que j'ai incorporé les mods de ArtOfWarefare]

Vous ne pouvez pas faire cela avec UIImageView. J'ai créé une simple sous-classe UIView MyImageView qui contient un UIImageView. Code ci-dessous.

// MyImageView.h
#import <UIKit/UIKit.h>

@interface MyImageView : UIView {
    UIImageView *_imageView;
}

@property (nonatomic, assign) UIImage *image;

@end

et

// MyImageView.m

#import "MyImageView.h"

@implementation MyImageView

@dynamic image;

- (id)initWithCoder:(NSCoder*)coder
{
    self = [super initWithCoder:coder];
    if (self) {
        self.clipsToBounds = YES;
        _imageView = [[UIImageView alloc] initWithFrame:self.bounds];
        _imageView.contentMode = UIViewContentModeScaleAspectFill;
        [self addSubview:_imageView];
    }
    return self;
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.clipsToBounds = YES;
        _imageView = [[UIImageView alloc] initWithFrame:self.bounds];
        _imageView.contentMode = UIViewContentModeScaleAspectFill;
        [self addSubview:_imageView];
    }
    return self;
}

- (id)initWithImage:(UIImage *)anImage
{
    self = [self initWithFrame:CGRectZero];
    if (self) {
        _imageView.image = anImage;
        [_imageView sizeToFit];

        // initialize frame to be same size as imageView
        self.frame = _imageView.bounds;        
    }
    return self;
}

// Delete this function if you're using ARC
- (void)dealloc
{
    [_imageView release];
    [super dealloc];
}

- (UIImage *)image
{
    return _imageView.image;
}

- (void)setImage:(UIImage *)anImage
{
    _imageView.image = anImage;
    [self setNeedsLayout];
}

- (void)layoutSubviews
{
    if (!self.image) return;

    // compute scale factor for imageView
    CGFloat widthScaleFactor = CGRectGetWidth(self.bounds) / self.image.size.width;
    CGFloat heightScaleFactor = CGRectGetHeight(self.bounds) / self.image.size.height;

    CGFloat imageViewXOrigin = 0;
    CGFloat imageViewYOrigin = 0;
    CGFloat imageViewWidth;
    CGFloat imageViewHeight;


    // if image is narrow and tall, scale to width and align vertically to the top
    if (widthScaleFactor > heightScaleFactor) {
        imageViewWidth = self.image.size.width * widthScaleFactor;
        imageViewHeight = self.image.size.height * widthScaleFactor;
    }

    // else if image is wide and short, scale to height and align horizontally centered
    else {
        imageViewWidth = self.image.size.width * heightScaleFactor;
        imageViewHeight = self.image.size.height * heightScaleFactor;
        imageViewXOrigin = - (imageViewWidth - CGRectGetWidth(self.bounds))/2;
    }

    _imageView.frame = CGRectMake(imageViewXOrigin,
                                  imageViewYOrigin,
                                  imageViewWidth,
                                  imageViewHeight);
}

- (void)setFrame:(CGRect)frame
{
    [super setFrame:frame];
    [self setNeedsLayout];
}

@end
40
XJones

Si vous utilisez Storyboards, cela peut être réalisé avec des contraintes ...

Tout d'abord un UIView avec le cadre final/les contraintes souhaités. Ajoutez un UIImageView à UIView. Définissez le contentMode sur Aspect Fill. Faites en sorte que le cadre UIImageView ait le même rapport que l’image (ceci évite les avertissements de Storyboard ultérieurement). Épinglez les côtés de UIView à l'aide de contraintes standard. Épinglez le haut OR en bas (en fonction de l’alignement souhaité) sur UIView à l’aide de contraintes standard. Enfin, ajoutez une contrainte de rapport de format à UIImageView (en veillant à ce que le rapport corresponde à celui de l’image).

29
Michael Platt

C’est un peu délicat car il n’est pas possible de définir d’autres règles d’alignement si vous avez déjà sélectionné un mode de contenu (.scaleAspectFit).

Mais voici une solution de contournement à ceci:

Vous devez d’abord redimensionner explicitement votre image source en calculant les dimensions (s’il s’agissait d’une variable UIImageView avec contentMode = .scaleAspectFit).

extension UIImage {

    func aspectFitImage(inRect rect: CGRect) -> UIImage? {
        let width = self.size.width
        let height = self.size.height
        let scaleFactor = width > height ? rect.size.height / height : rect.size.width / width

        UIGraphicsBeginImageContextWithOptions(CGSize(width: width * scaleFactor, height: height * scaleFactor), false, 0.0)
        self.draw(in: CGRect(x: 0.0, y: 0.0, width: width * scaleFactor, height: height * scaleFactor))

        defer {
            UIGraphicsEndImageContext()
        }

        return UIGraphicsGetImageFromCurrentImageContext()
    }
}

Ensuite, vous devez simplement appeler cette fonction sur votre image d'origine en transmettant le cadre de votre imageView et affecter le résultat à votre propriété UIImageView.image. Assurez-vous également que vous définissez la variable contentMode de votre imageView ici ( ou même dans le Générateur d'interface )!

let image = UIImage(named: "MySourceImage")
imageView.image = image?.aspectFitImage(inRect: imageView.frame)
imageView.contentMode = .left
9
choofie

Essayez de régler:

imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.clipsToBounds = YES;

Cela a fonctionné pour moi.

8
Jonathan Molina

J'ai utilisé UIImageViewAligned pour changer l'alignement de l'image grâce au développeur

UIImageViewAligned

3
Milap Kundalia

Je sais que c'est un vieux fil, mais je pensais partager ce que j'avais fait pour changer facilement la région découpée du haut vers le bas de la vue d'image dans Interface Builder, au cas où quelqu'un aurait le même problème que moi. J'avais un UIImageView qui remplissait la vue de mon ViewController et essayais de faire en sorte que le haut reste le même, quelle que soit la taille de l'écran de l'appareil.

  1. J'ai appliqué le facteur de forme retina 4 (Editeur-> Appliquer le facteur de forme Retina 4). 

  2. J'ai épinglé la hauteur et la largeur. 

Désormais, lorsque l'écran change de taille, UIImageView est en fait de la même taille et le contrôleur de vue extrait simplement ce qui n'est pas à l'écran. Le cadre Origin reste à 0,0; le bas et la droite de l'image sont donc coupés, pas le haut.

J'espère que cela t'aides.

2
GLee

Vous pouvez le faire d'abord en le redimensionnant puis en le redimensionnant .. La chose à mentionner ici est que j'ai été conditionné par la hauteur. Je veux dire, je devais avoir une image de 34px haute et peu importe la largeur.

Obtenez donc le rapport entre la hauteur réelle du contenu et la hauteur de la vue (34 px), puis redimensionnez également la largeur.

Voici comment je l'ai fait:

CGSize size = [imageView sizeThatFits:imageView.frame.size];

CGSize actualSize;
actualSize.height = imageView.frame.size.height;
actualSize.width = size.width / (1.0 * (size.height / imageView.frame.size.height));

CGRect frame = imageView.frame;
frame.size = actualSize;
[imageView setFrame:frame];

J'espère que cela t'aides.

1
George

Je suis venu à la solution suivante:

  1. Définissez le mode de contenu UIImageView sur top: imageView.contentMode = .top
  2. Redimensionner l'image pour l'adapter aux limites de UIImageView

Pour charger et redimensionner une image, j'utilise Kingfisher :

let size = imageView.bounds.size
let processor = ResizingImageProcessor(referenceSize: size, mode: .aspectFit)
imageView.kf.setImage(with: URL(string: imageUrl), options: [.processor(processor)])
1
Murlakatam

Si vous avez besoin d'obtenir l'aspect Fit et de vous débarrasser des espaces vides

Ne pas oublier de supprimer la contrainte de largeur de votre imageview du storyboard et profiter

classe à taille humaine ImageView: UIImageView {

override func layoutSubviews() {
    super.layoutSubviews()

    guard let imageSize = image?.size else {
        return
    }

    let viewBounds = bounds
    let imageFactor = imageSize.width / imageSize.height
    let newWidth = viewBounds.height * imageFactor

    let myWidthConstraint = self.constraints.first(where: { $0.firstAttribute == .width })
    myWidthConstraint?.constant = min(newWidth, UIScreen.main.bounds.width / 3)

    layoutIfNeeded()
}}
0
Maxim Golovlev

J'ai résolu ce problème en sous-classant UIImageView et en surchargeant la méthode setImage:. La sous-classe stocke d'abord ses valeurs d'origine pour Origin et size afin de pouvoir utiliser la taille de l'ensemble d'origine en tant que cadre de sélection.

J'ai défini le mode de contenu sur UIViewContentModeAspectFit. À l'intérieur de setImage: j'ai saisi le rapport largeur/hauteur de l'image, puis redimensionné la vue pour l'adapter au même rapport que l'image. Après le redimensionnement, j’ai ajusté les propriétés de mon cadre pour définir la vue de l’image au même endroit que précédemment, puis j’ai appelé le super setImage :.

Cela se traduit par une vue d'image dont le cadre est ajusté pour s'adapter exactement à l'image. Par conséquent, l'ajustement de l'aspect et les propriétés du cadre de la vue d'image font le travail lourd en plaçant la vue de l'image où elle devrait être pour obtenir le même effet.

Voici un code que j'ai utilisé:

Tout d’abord, et je trouve cela très utile en général, c’est une catégorie sur UIView qui facilite la définition des propriétés du cadre sur une vue via des propriétés telles que gauche, droite, haut, bas, largeur, hauteur, etc.

UIImageView + FrameAdditions

@interface UIView (FrameAdditions)

@property CGFloat left, right, top, bottom, width, height;
@property CGPoint Origin;

@end

@implementation UIView (FrameAdditions)

- (CGFloat)left {
    return self.frame.Origin.x;
}

- (void)setLeft:(CGFloat)left {
    self.frame = CGRectMake(left, self.frame.Origin.y, self.frame.size.width, self.frame.size.height);
}

- (CGFloat)right {
    return self.frame.Origin.x + self.frame.size.width;
}

- (void)setRight:(CGFloat)right {
    self.frame = CGRectMake(right - self.frame.size.width, self.frame.Origin.y, self.frame.size.width, self.frame.size.height);
}

- (CGFloat)top {
    return self.frame.Origin.y;
}

- (void)setTop:(CGFloat)top {
    self.frame = CGRectMake(self.frame.Origin.x, top, self.frame.size.width, self.frame.size.height);
}

- (CGFloat)bottom {
    return self.frame.Origin.y + self.frame.size.height;
}

- (void)setBottom:(CGFloat)bottom {
    self.frame = CGRectMake(self.frame.Origin.x, bottom - self.frame.size.height, self.frame.size.width, self.frame.size.height);
}

- (CGFloat)width {
    return self.frame.size.width;
}

- (void)setWidth:(CGFloat)width {
    self.frame = CGRectMake(self.frame.Origin.x, self.frame.Origin.y, width, self.frame.size.height);
}

- (CGFloat)height {
    return self.frame.size.height;
}

- (void)setHeight:(CGFloat)height {
    self.frame = CGRectMake(self.frame.Origin.x, self.frame.Origin.y, self.frame.size.width, height);
}

- (CGPoint)Origin {
    return self.frame.Origin;
}

- (void)setOrigin:(CGPoint)Origin {
    self.frame = CGRectMake(Origin.x, Origin.y, self.frame.size.width, self.frame.size.height);
}

@end

Ceci est la sous-classe de UIImageView. Il n'est pas complètement testé, mais devrait faire passer l'idée. Cela pourrait être étendu pour définir vos propres nouveaux modes d'alignement.

BottomCenteredImageView

@interface BottomCenteredImageView : UIImageView

@end


@interface BottomCenteredImageView() {
    CGFloat originalLeft;
    CGFloat originalBottom;
    CGFloat originalHeight;
    CGFloat originalWidth;
}

@end

@implementation BottomCenteredImageView

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if(self) {
        [self initialize];
    }
    return self;
}

- (void)awakeFromNib {
    [self initialize];
}

- (void)initialize {
    originalLeft = self.frame.Origin.x;
    originalHeight = CGRectGetHeight(self.frame);
    originalWidth = CGRectGetWidth(self.frame);
    originalBottom = self.frame.Origin.y + originalHeight;
}

- (void)setImage:(UIImage *)image {
    if(image) {
        self.width = originalWidth;
        self.height = originalHeight;
        self.left = originalLeft;
        self.bottom = originalBottom;

        float myWidthToHeightRatio = originalWidth/originalHeight;
        float imageWidthToHeightRatio = image.size.width/image.size.height;
        if(myWidthToHeightRatio >= imageWidthToHeightRatio) {
            // Calculate my new width
            CGFloat newWidth = self.height * imageWidthToHeightRatio;
            self.width = newWidth;
            self.left = originalLeft + (originalWidth - self.width)/2;
            self.bottom = originalBottom;
        } else {
            // Calculate my new height
            CGFloat newHeight = self.width / imageWidthToHeightRatio;
            self.height = newHeight;
            self.bottom = originalBottom;
        }
        self.contentMode = UIViewContentModeScaleAspectFit;
        [super setImage:image];
    } else {
        [super setImage:image];
    }
}

@end
0
slemke