Вирівнювання UIImageView за формою Aspect Fit


79

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


1
спробуйте з властивостями зверху та знизу
imageview

Не працює, якщо у вас встановлено 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

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+? Чи є у вас приклад коду?
Özgür

2
Я це також реалізував, це працює. Це може здатися заплутаним, оскільки рішення описує налаштування автоматичного обмеження макета. Для інших розробників в моїй реалізації використовувалися зображення, завантажені з Інтернету, тому мені довелося видалити та повторно додати обмеження пропорцій UIImageView програмно на основі ширини / висоти зображення.
Michael DePhillips

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тут бажаний imageView ( або навіть у Конструкторі інтерфейсів )!

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



2

Я знаю, що це стара тема, але я думав поділитися тим, що я зробив, щоб легко змінити обрізану область зверху до низу подання зображень у Interface Builder, на випадок, якщо хтось мав таку ж проблему, як і я. У мене був UIImageView, який заповнював вигляд мого ViewController, і намагався зробити так, щоб верх залишався незмінним, незалежно від розміру екрана пристрою.

  1. Я застосував форм-фактор retina 4 (Editor-> Apply Retina 4 Form Factor).

  2. Я закріпив висоту і ширину.

Тепер, коли екран змінює розмір, UIImageView насправді має однаковий розмір, і контролер перегляду просто відсікає те, що поза екраном. Початок кадру залишається рівним 0,0, тому нижня та права частина зображення обрізані, а не верхня.

Сподіваюся, це допомагає.


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 (з: url, параметри: [. процесор (процесор), .scaleFactor (UIScreen.main.scale)])
Олександр Маслю 02

Приголомшливо Дякую, чувак. Я хотів вирівняти ліворуч, і це спрацювало
Куналь Гупта,

0

Я вирішив це шляхом підкласу UIImageView та заміни методу setImage: Спочатку підклас зберігає вихідні значення для походження та розміру, щоб він міг використовувати початковий розмір набору як обмежувальну рамку.

Я встановив режим вмісту на UIViewContentModeAspectFit. Усередині setImage: я схопив співвідношення ширини та висоти зображення, а потім змінив розмір подання зображення, щоб відповідати такому співвідношенню, як і зображення. Після зміни розміру я відрегулював властивості кадру, щоб встановити вигляд зображення на тому самому місці, що і раніше, а потім зателефонував super setImage :.

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

Ось деякий код, який я використовував:

По-перше, і я вважаю це досить корисним загалом, це категорія на UIView, яка полегшує встановлення властивостей кадру у вигляді за допомогою таких властивостей, як лівий, правий, верхній, нижній, ширина, висота тощо.

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

Замість того, щоб використовувати UIImageView у своєму ВК, використовуйте BottomCenteredImageView.
slemke

Це не спрацювало для мене ... це змінило співвідношення сторін мого подання, щоб воно відповідало співвідношенню сторін мого зображення, яке було не тим, що я хотів ... Надана відповідь XJones чудово спрацювала з кількома змінами, які я зазначив у коментарі під його відповіддю.
ArtOfWarfare

0

Якщо вам потрібно домогтися аспекту Fit і позбутися порожніх просторів

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

клас 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]|
+----------------------------------+

Змініть обмеження, щоб по-різному вирівняти в режимі перегляду зображення.


Це не гаряче, це працює, якщо ви використовуєте пристосування сторін, вирівнювання сильно залежить від фактичного розміру зображення
Абхішек Маурія
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.