UIImageView mit Aspect Fit ausrichten

79

Für mein UIImageView wähle ich Aspect Fit (InterfaceBuilder), aber wie kann ich die vertikale Ausrichtung ändern?

Raegtime
quelle
1
versuchen Sie es mit der oberen und unteren Eigenschaft von imageview
rithik
Funktioniert nicht, wenn Sie Aspect Fit eingestellt haben.
Ethan Allen
5
Auf GitHub gibt es ein Projekt namens UIImageViewAligned , das dies perfekt macht.
Daniel Storm

Antworten:

40

[BEARBEITEN - dieser Code ist ein bisschen schimmelig aus dem Jahr 2011 und alle außer ich habe die Mods von @ ArtOfWarefare integriert]

Sie können dies nicht mit UIImageView tun. Ich habe eine einfache UIView-Unterklasse erstellt MyImageView, die eine UIImageView enthält. Code unten.

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

@interface MyImageView : UIView {
    UIImageView *_imageView;
}

@property (nonatomic, assign) UIImage *image;

@end

und

// 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
XJones
quelle
3
Zwei kleine Dinge , die ich tun musste , um diese Arbeit zu machen: 1 - I umgesetzt initWithCoder(I grundsätzlich kopieren Sie initWithFrameaber ersetzt die [super initWithFrame:]mit [super initWithCoder:]) und 2 - I in einer Linie hinzugefügt , if (!self.image) return;um layoutSubviewsso , dass es nicht mit ungültiger Geometrie , wenn ein Bild isn abstürze würde‘ t zugewiesen, wenn es aufgerufen wird. Mit diesen beiden kleinen Änderungen funktioniert es erstaunlich.
ArtOfWarfare
Nebenbemerkung: Bei ARC muss die deallocMethode entfernt werden.
Raptor
35

Wenn Sie Storyboards verwenden, kann dies mit Einschränkungen erreicht werden ...

Zunächst ein UIView mit dem gewünschten endgültigen Frame / den gewünschten Einschränkungen. Fügen Sie der UIView eine UIImageView hinzu. Stellen Sie den contentMode auf Aspect Fill ein. Stellen Sie sicher, dass der UIImageView-Frame das gleiche Verhältnis wie das Bild hat (dies vermeidet später Storyboard-Warnungen). Befestigen Sie die Seiten mit Standardbeschränkungen an der UIView. Befestigen Sie das obere ODER untere Ende (je nachdem, wo Sie es ausrichten möchten) mit Standardbeschränkungen an der UIView. Fügen Sie der UIImageView schließlich eine Einschränkung des Seitenverhältnisses hinzu (stellen Sie sicher, dass das Verhältnis das Bild ist).

Michael Platt
quelle
@Michael Platt, das klingt so verwirrend, dass dies in iOS 7 nicht funktioniert, da Sie eine Äquivalenz der Seitenverhältnisse benötigen, die in iOS 8+ verfügbar ist? Haben Sie einen Beispielcode?
Özgür
2
Ich habe dies auch implementiert, es funktioniert. Dies kann verwirrend erscheinen, da die Lösung die Einrichtung von Einschränkungen für das automatische Layout beschreibt. Für andere Entwickler verwendete meine Implementierung Bilder, die aus dem Web geladen wurden. Daher musste ich die Einschränkung des UIImageView-Seitenverhältnisses programmgesteuert basierend auf der Breite / Höhe des Bildes entfernen und erneut hinzufügen.
Michael DePhillips
3
Ich habe die UIImageView-Erweiterung mit der @ MichaelPlatt-Lösung erstellt. gist.github.com/megimix/d0bbea45bd3e1e4d615fbd5c30b54da7
Megimix
15

Dies ist etwas schwierig, da es keine Möglichkeit gibt, weitere Ausrichtungsregeln festzulegen, wenn Sie bereits einen Inhaltsmodus ausgewählt haben ( .scaleAspectFit).

Aber hier ist eine Problemumgehung:

Zuerst müssen Sie die Größe Ihres Quellbilds explizit ändern, indem Sie die Abmessungen berechnen (wenn es in einem UIImageViewmit wäre contentMode = .scaleAspectFit).

extension UIImage {

    func aspectFitImage(inRect rect: CGRect) -> UIImage? {
        let width = self.size.width
        let height = self.size.height
        let aspectWidth = rect.width / width
        let aspectHeight = rect.height / height
        let scaleFactor = aspectWidth > aspectHeight ? 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()
    }
}

Dann müssen Sie diese Funktion einfach für Ihr Originalbild aufrufen, indem Sie den Rahmen Ihrer imageView übergeben und das Ergebnis Ihrer UIImageView.imageEigenschaft zuweisen . Stellen Sie außerdem sicher, dass Sie contentModehier ( oder sogar im Interface Builder ) die gewünschte Bildansicht einstellen !

let image = UIImage(named: "MySourceImage")
imageView.image = image?.aspectFitImage(inRect: imageView.frame)
imageView.contentMode = .left
Choofie
quelle
4

Versuchen Sie die Einstellung:

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

Das hat bei mir funktioniert.

Jonathan Molina
quelle
3

Ich habe UIImageViewAligned verwendet, um die Ausrichtung des Bildes dank des Entwicklers zu ändern

UIImageViewAligned

Milap Kundalia
quelle
2

Ich weiß, dass dies ein alter Thread ist, aber ich dachte, ich würde mitteilen, was ich getan habe, um den abgeschnittenen Bereich in Interface Builder einfach vom oberen zum unteren Rand der Bildansicht zu ändern, falls jemand das gleiche Problem hatte wie ich. Ich hatte eine UIImageView, die die Ansicht meines ViewControllers füllte, und versuchte, die Oberseite unabhängig von der Größe des Gerätebildschirms gleich zu halten.

  1. Ich habe den Retina 4-Formfaktor angewendet (Editor-> Retina 4-Formfaktor anwenden).

  2. Ich habe die Höhe und Breite festgehalten.

Wenn sich die Größe des Bildschirms ändert, hat UIImageView tatsächlich dieselbe Größe, und der Ansichts-Controller schneidet nur das ab, was sich außerhalb des Bildschirms befindet. Der Rahmenursprung bleibt bei 0,0, sodass die untere und rechte Seite des Bildes abgeschnitten werden und nicht die obere.

Hoffe das hilft.

Freude
quelle
1

Sie können dies tun, indem Sie zuerst skalieren und dann die Größe ändern. Hier ist zu erwähnen, dass ich von der Größe abhängig war. Ich meine, ich musste das Bild von 34px hoch und egal wie breit haben.

Ermitteln Sie also das Verhältnis zwischen der tatsächlichen Höhe des Inhalts und der Höhe der Ansicht (34 px) und skalieren Sie dann auch die Breite.

So habe ich es gemacht:

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];

Hoffe das hilft.

George
quelle
1

Ich bin zu folgender Lösung gekommen:

  1. Stellen Sie den UIImageViewInhaltsmodus auf top:imageView.contentMode = .top
  2. Passen Sie die Bildgröße an die UIImageView-Grenzen an

Zum Laden und Ändern der Bildgröße verwende ich Kingfisher :

let size = imageView.bounds.size
let processor = ResizingImageProcessor(referenceSize: size, mode: .aspectFit)
imageView.kf.setImage(with: URL(string: imageUrl), options: [.processor(processor), .scaleFactor(UIScreen.main.scale)])
Murlakatam
quelle
Dies funktioniert, aber das Bild wird verschwommen. Ich habe es behoben, indem ich die Bildschirmgröße des Geräts und den Skalierungsfaktor des Geräts wie folgt verwendet habe: let processor = ResizingImageProcessor (referenceSize: view.frame.size, mode: .aspectFit); imageView.kf.setImage (mit: URL, Optionen: [. Prozessor (Prozessor), .scaleFactor (UIScreen.main.scale)])
Alexander Maslew
Genial. Danke Alter. Ich wollte mich links ausrichten und das funktionierte
Kunal Gupta
0

Ich habe dies gelöst, indem ich UIImageView unterklassifiziert und die setImage: -Methode überschrieben habe. Die Unterklasse speichert zuerst ihre ursprünglichen Werte für Ursprung und Größe, damit sie die ursprüngliche Satzgröße als Begrenzungsrahmen verwenden kann.

Ich habe den Inhaltsmodus auf UIViewContentModeAspectFit eingestellt. Innerhalb von setImage: Ich habe das Verhältnis von Bildbreite zu Bildhöhe erfasst und dann die Bildansicht so angepasst, dass sie dem gleichen Verhältnis wie das Bild entspricht. Nach der Größenänderung habe ich meine Rahmeneigenschaften angepasst, um die Bildansicht an derselben Stelle wie zuvor festzulegen, und dann das Super-SetImage: aufgerufen.

Dies führt zu einer Bildansicht, deren Rahmen genau an das Bild angepasst ist, sodass die Aspektanpassung funktioniert und die Rahmeneigenschaften der Bildansicht die Bildansicht an die Stelle bringen, an der sie sein sollte, um den gleichen Effekt zu erzielen.

Hier ist ein Code, den ich verwendet habe:

Erstens, und ich finde es im Allgemeinen ziemlich nützlich, ist eine Kategorie in UIView, die es einfach macht, Rahmeneigenschaften in einer Ansicht über Eigenschaften wie links, rechts, oben, unten, Breite, Höhe usw. festzulegen.

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

Dies ist die Unterklasse von UIImageView. Es ist nicht vollständig getestet, sollte aber die Idee vermitteln. Dies könnte erweitert werden, um Ihre eigenen neuen Modi für die Ausrichtung festzulegen.

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
slemke
quelle
Verwenden Sie BottomCenteredImageView, anstatt eine UIImageView in Ihrer VC zu verwenden.
Slemke
Dies hat bei mir nicht funktioniert ... es hat das Seitenverhältnis meiner Ansicht geändert, um es an das Seitenverhältnis meines Bildes anzupassen, was nicht das war, was ich wollte ... Die Antwort, die XJones gab, funktionierte wunderbar mit den wenigen Änderungen, die ich in der Kommentare unter seiner Antwort.
ArtOfWarfare
0

Wenn Sie aspectFit erreichen und leere Räume entfernen müssen

Vergessen Sie nicht, die Breitenbeschränkung Ihrer Bildansicht aus dem Storyboard zu entfernen und zu genießen

Klasse SelfSizedImageView: 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()
}}
Maxim Golovlev
quelle
-6

Verwenden Sie das automatische Layout, um ein maßstabsgetreues Bild auszurichten. Beispiel für ein rechtsbündiges Bild nach dem Anpassen in UIImageView:

  1. Erstellen Sie UIImageView, das das Bild enthält
  2. Fügen Sie automatische Einschränkungen für rechts, oben, unten und Breite hinzu
  3. Stellen Sie das Bild ein:
myUIImageView.contentMode = .ScaleAspectFit
myUIImageView.translatesAutoresizingMaskIntoConstraints = false
myUIImageView.image = UIImage(named:"pizza.png")

Ihr skaliertes Bild mit Aspektanpassung ist jetzt in der UIImageView rechtsbündig ausgerichtet

+----------------------------------+
|                           [IMAGE]|
+----------------------------------+

Ändern Sie die Einschränkungen so, dass sie in der Bildansicht unterschiedlich ausgerichtet werden.

Pizzamonster
quelle
Dies ist nicht heiß, es funktioniert, wenn Sie Aspektanpassung verwenden, hängt die Ausrichtung stark von der tatsächlichen Größe des Bildes ab
Abhishek Maurya