Выравнивание UIImageView с Aspect Fit

79

для своего UIImageView я выбираю Aspect Fit (InterfaceBuilder), но как я могу изменить вертикальное выравнивание?

Raegtime
источник
1
попробуйте с верхним и нижним свойством imageview
rithik
Не работает, если у вас установлен Aspect Fit.
Итан Аллен
5
На GitHub есть проект под названием UIImageViewAligned , который отлично с этим справляется .
Дэниел Сторм,

Ответы:

40

[РЕДАКТИРОВАТЬ - этот код немного заплесневел, взятый из 2011 года, и все, кроме я включил моды @ ArtOfWarefare]

Вы не можете сделать это с UIImageView. Я создал простой подкласс UIView, MyImageViewкоторый содержит UIImageView. Код ниже.

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

@interface MyImageView : UIView {
    UIImageView *_imageView;
}

@property (nonatomic, assign) UIImage *image;

@end

и

// 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
источник
3
Две маленькие вещи , которые я должен был сделать , чтобы сделать эту работу: 1 - я реализовал initWithCoder(я в основном скопировали , initWithFrameно заменил [super initWithFrame:]с [super initWithCoder:]) и 2 - я добавил в линию , if (!self.image) return;чтобы layoutSubviewsтаким образом , чтобы он не врезаться с недопустимой геометрией , если изображение ISN» t назначается при вызове. С этими двумя небольшими изменениями он работает потрясающе.
ArtOfWarfare
примечание: с ARC deallocметод должен быть удален.
Raptor
35

При использовании раскадровки это может быть достигнуто с ограничениями ...

Во-первых, UIView с желаемым окончательным кадром / ограничениями. Добавьте UIImageView в UIView. Установите для contentMode значение Aspect Fill. Сделайте фрейм UIImageView таким же соотношением, что и изображение (это позволяет избежать предупреждений о раскадровке позже). Прикрепите стороны к UIView, используя стандартные ограничения. Прикрепите верхнюю или нижнюю часть (в зависимости от того, где вы хотите выровнять) к UIView, используя стандартные ограничения. Наконец, добавьте ограничение соотношения сторон к UIImageView (убедившись, что соотношение сторон соответствует изображению).

Майкл Платт
источник
@Michael Platt, это звучит так запутанно, поэтому это не может работать в iOS 7, поскольку вам требуется эквивалентность соотношений сторон, которая доступна в iOS 8+? У вас есть пример кода?
Озгюр
2
Я это тоже реализовал, это работает. Это может показаться сбивающим с толку, поскольку решение описывает настройку ограничений автоматической компоновки. Для других разработчиков в моей реализации использовались изображения, загруженные из Интернета, поэтому мне пришлось удалить и повторно добавить ограничение соотношения сторон UIImageView программно на основе ширины / высоты изображения.
Майкл ДеФиллипс,
3
Я создал расширение UIImageView с помощью решения @MichaelPlatt. gist.github.com/megimix/d0bbea45bd3e1e4d615fbd5c30b54da7
megimix
15

Это немного сложно, поскольку нет возможности установить дополнительные правила выравнивания, если вы уже выбрали режим содержимого ( .scaleAspectFit).

Но вот обходной путь:

Сначала необходимо явно изменить размер исходного изображения, вычислив размеры (если оно будет в UIImageViewс 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()
    }
}

Затем вам просто нужно вызвать эту функцию на исходном изображении, передав фрейм imageView и присвоить результат своему UIImageView.imageсвойству. Кроме того, убедитесь, что вы установили contentModeздесь желаемое изображение ( или даже в Интерфейсном Разработчике )!

let image = UIImage(named: "MySourceImage")
imageView.image = image?.aspectFitImage(inRect: imageView.frame)
imageView.contentMode = .left
чуфи
источник
4

Попробуйте установить:

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

Это сработало для меня.

Джонатан Молина
источник
3

Я использовал UIImageViewAligned для изменения выравнивания изображения благодаря разработчику

UIImageViewAligned

Милап Кундалия
источник
2

Я знаю, что это старый поток, но я подумал, что поделюсь тем, что сделал, чтобы легко изменить обрезанную область сверху вниз в представлении изображения в Interface Builder, на случай, если у кого-то возникнет такая же проблема, как и у меня. У меня был UIImageView, который заполнял View моего ViewController, и я пытался сделать верхнюю часть такой же, независимо от размера экрана устройства.

  1. Я применил форм-фактор Retina 4 (Редактор-> Применить форм-фактор Retina 4).

  2. Прикрепил высоту и ширину.

Теперь, когда экран меняет размер, UIImageView фактически имеет тот же размер, а контроллер представления просто отсекает то, что находится за пределами экрана. Исходная точка кадра остается на 0,0, поэтому нижняя и правая часть изображения обрезаются, а не верхняя.

Надеюсь это поможет.

GLee
источник
1

Вы можете сделать это, сначала изменив масштаб, а затем изменив размер. Здесь следует упомянуть, что я был обусловлен ростом. Я имею в виду, что у меня должно было быть изображение высотой 34 пикселя независимо от ширины.

Итак, получите соотношение между фактической высотой содержимого и высотой представления (34 пикселя), а затем также масштабируйте ширину.

Вот как я это сделал:

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

Надеюсь это поможет.

Джордж
источник
1

Я пришел к следующему решению:

  1. Установите UIImageViewрежим содержимого на top:imageView.contentMode = .top
  2. Изменить размер изображения в соответствии с границами UIImageView

Для загрузки и изменения размера изображения я использую 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)])
Мурлакатам
источник
Это работает, но изображение становится размытым. Я исправил это, используя размер экрана устройства и коэффициент масштабирования устройства следующим образом: let processor = ResizingImageProcessor (referenceSize: view.frame.size, mode: .aspectFit); imageView.kf.setImage (with: url, options: [. processor (processor), .scaleFactor (UIScreen.main.scale)])
Александр Маслев 02
Потрясающе. Спасибо приятель. Я хотел выровнять по левому краю, и это сработало
Кунал Гупта
0

Я решил эту проблему, создав подкласс UIImageView и переопределив метод setImage :. Подкласс сначала сохранит свои исходные значения для источника и размера, чтобы он мог использовать исходный установленный размер в качестве ограничивающей рамки.

Я установил режим содержимого на UIViewContentModeAspectFit. Внутри setImage: я взял отношение ширины изображения к высоте, а затем изменил размер изображения, чтобы он соответствовал тому же соотношению, что и изображение. После изменения размера я скорректировал свойства кадра, чтобы установить вид изображения на том же месте, что и раньше, а затем вызвал super setImage :.

Это приводит к тому, что рама изображения настраивается так, чтобы она точно соответствовала изображению, поэтому соотношение сторон работает, а свойства рамки просмотра изображения делают тяжелую работу по размещению вида изображения там, где оно должно быть, чтобы получить тот же эффект.

Вот код, который я использовал:

Во-первых, и я считаю, что это довольно полезно в целом, это категория в UIView, которая упрощает установку свойств фрейма в представлении с помощью таких свойств, как left, right, top, bottom, width, height и т. Д.

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

Это подкласс UIImageView. Он не полностью протестирован, но должен донести идею. Его можно расширить, чтобы установить свои собственные новые режимы выравнивания.

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
источник
Вместо использования UIImageView в вашем VC используйте BottomCenteredImageView.
slemke
Это не сработало для меня ... это изменило соотношение сторон моего представления, чтобы оно соответствовало соотношению сторон моего изображения, что было не тем, что я хотел ... Ответ, предоставленный XJones, прекрасно работал с несколькими изменениями, которые я отметил комментарии под его ответом.
ArtOfWarfare
0

Если вам нужно достичь аспектаFit и избавиться от пустых пространств

Не забудьте удалить ограничение ширины вашего изображения из раскадровки и наслаждайтесь

class 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()
}}
Максим Головлев
источник
-6

Для выравнивания изображения по размеру используйте автоматический макет. Пример изображения с выравниванием по правому краю после масштабирования по размеру в UIImageView:

  1. Создайте UIImageView, который содержит изображение
  2. Добавить автоматические ограничения для правого, верхнего, нижнего, ширины
  3. Установите изображение:
myUIImageView.contentMode = .ScaleAspectFit
myUIImageView.translatesAutoresizingMaskIntoConstraints = false
myUIImageView.image = UIImage(named:"pizza.png")

Теперь масштабированное изображение, соответствующее размеру изображения, выровнено по правому краю в UIImageView.

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

Измените ограничения, чтобы по-разному выровнять изображение.

пицца
источник
Это не круто, это работает, если вы используете форматирование, выравнивание сильно зависит от фактического размера изображения
Абхишек Маурья