Подтвердить что ты не робот

Автоматическая компоновка: получить высоту UIImageView, чтобы правильно вычислить высоту ячейки

До сих пор я просто использовал текстовые ярлыки внутри ячеек, которые должны автоматически настраиваться. Обычно я устанавливаю ограничения для всех ребер (просмотр содержимого или соседей) и добавляю следующие строки к моему viewDidLoad():

// 190.0 is the actual height of my custom cell (see next image) in the storyboard containing a label and a randomly sized UIImageView
tableView.estimatedRowHeight = 190.0
tableView.rowHeight = UITableViewAutomaticDimension

Теперь, когда вы пытаетесь включить изображения, я сталкиваюсь с несколькими проблемами. Во время какого-то исследования я нашел несколько подходов, написанных в Objective-C, но в то время как я не уверен, какой из них действительно затрагивал мою центральную проблему, я все еще боюсь сложного кода Objective-C. Итак, чтобы возобновить: моя конечная цель - получить таблицу с правильными размерами (правильными высотами), каждая из которых содержит заголовок и изображение, соответствующее ширине экрана. Эти изображения могут иметь разные пропорции и разные размеры. Чтобы упростить некоторые из проблем, я подготовил новый, более простой проект:

  • Во-первых, я создал класс UITableViewController и CustomCell с двумя IBOutlets title: String и postedImage: UIImage (UIImage случайным образом размером в раскадровку) для настройки ячеек. Я определил ограничения для ребер и друг друга.

  1. При построении две ячейки получают конфигурацию, содержащую одно и то же изображение, но они извлекаются из двух отдельных файлов разного размера и разрешений (900 x 171 против полутонов размером 450 x 86). Режим просмотра UIImage установлен на "Aspect Fit", но поскольку я не мог заставить его работать так, как я хочу, пока не уверен, что это правильная настройка. В то время как я ожидал, что высота ячейки будет рассчитана на основе масштабированного UIImage, включая ограничения, которые я установил, он, по-видимому, основан на начальной высоте файлов изображений (так 171 и 86). Он фактически не адаптируется к фактической высоте представления UIImage (около 70, я бы догадался).

Both cell heights are based on the initial image file height and not on the actual UIImage height

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

Спасибо за помощь!

4b9b3361

Ответ 1

Итак, я думаю, что основная проблема - это проблема курицы и яйца.

Чтобы "соответствовать размеру" изображения на UIImageView, системе нужен размер для представления, и все же мы хотели бы, чтобы высота представления была определена как после, изображение с известной шириной для представления.

Обходной путь заключается в том, чтобы самостоятельно рассчитать соотношение размеров изображения и установить его как ограничение на UIImageView при установке изображения. Таким образом, система автоматического компоновки имеет достаточно информации для определения размера (ширина и соотношение сторон).

Итак, я изменил свой собственный класс ячеек на:

class CustomCell: UITableViewCell {

    @IBOutlet weak var imageTitle: UILabel!

    @IBOutlet weak var postedImageView: UIImageView!

    internal var aspectConstraint : NSLayoutConstraint? {
        didSet {
            if oldValue != nil {
                postedImageView.removeConstraint(oldValue!)
            }
            if aspectConstraint != nil {
                postedImageView.addConstraint(aspectConstraint!)
            }
        }
    }

    override func prepareForReuse() {
        super.prepareForReuse()
        aspectConstraint = nil
    }

    func setPostedImage(image : UIImage) {

        let aspect = image.size.width / image.size.height

        aspectConstraint = NSLayoutConstraint(item: postedImageView, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: postedImageView, attribute: NSLayoutAttribute.Height, multiplier: aspect, constant: 0.0)

        postedImageView.image = image
    }

}

И затем в делегате сделайте это, вместо того, чтобы напрямую установить изображение:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as CustomCell

        cell.imageTitle.text = titles[indexPath.row]

        let image = images[titles[indexPath.row]]!
        cell.setPostedImage(image)

        return cell
}

И вы получите:

enter image description here

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

Ответ 2

Я перевел решение Полларда на версию objc примерно так.

Также я получил предупреждения типа "Невозможно одновременно удовлетворить ограничения". Мое решение задает приоритет вертикального ограничения пространства как 999, а не 1000.

@interface PostImageViewCell()

@property (nonatomic, strong) NSLayoutConstraint *aspectContraint;
@end

@implementation PostImageViewCell

- (void) setAspectContraint:(NSLayoutConstraint *)aspectContraint {

    if (_aspectContraint != nil) {
        [self.postImage removeConstraint:_aspectContraint];
    }
    if (aspectContraint != nil) {
        [self.postImage addConstraint:aspectContraint];
    }
}

- (void) prepareForReuse {
    [super prepareForReuse];
    self.aspectContraint = nil;
}

- (void)setPicture:(UIImage *)image {

    CGFloat aspect = image.size.width / image.size.height;
    self.aspectContraint = [NSLayoutConstraint constraintWithItem:self.postImage
                                                        attribute:NSLayoutAttributeWidth
                                                        relatedBy:NSLayoutRelationEqual
                                                           toItem:self.postImage
                                                        attribute:NSLayoutAttributeHeight
                                                       multiplier:aspect
                                                         constant:0.0];
    [self.postImage setImage:image];
}
@end

Ответ 3

У Майка Полларда отличное решение, но у меня возникают проблемы, когда устройство вращается. С вращением я получаю жалобы Autolayout:

Вероятно, по крайней мере одно из ограничений в следующем списке - это тот, который вы не хотите. Попробуйте это: (1) посмотрите на каждое ограничение и попытайтесь выяснить, чего вы не ожидаете; (2) найти код, который добавил нежелательные ограничения или ограничения и исправить его. (Примечание. Если вы видите NSAutoresizingMaskLayoutConstraints, которые вы не понимаете, обратитесь к документации для свойства UIView, переводитAutoresizingMaskIntoConstraints) , , .

Будет пытаться восстановить, нарушая ограничение NSLayoutConstraint: 0x82ccc110 UIImageView: 0x82ccbed0.width == UIImageView: 0x82ccbed0.height

Самое смешное, что после поворота макет прекрасен. Мне интересно, является ли это жалобой, когда кадр ячейки изменен, но затем Autolayout запускает и исправляет все.

Ответ 4

Работа в Swift 3 + iOS 8

Использование объектов AutoLayout и переменной размера UITableViewCell -

Для просмотра изображения установите ограничения верхнего, нижнего, верхнего и конечного пробелов на 0.

Код для добавления в viewDidLoad()

tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 200

Определить UITableViewDelegate heightForRowAtIndexPath()

extension ViewController: UITableViewDelegate {

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        let image = UIImage(named: list_images[indexPath.row])
        let aspect = (image?.size.width)! / (image?.size.height)!
        let cellHeight = (tableView.frame.width / aspect) + <<Height of image title label>>
        return cellHeight
    }
}

Ответ 5

Вместо пользовательского метода setPostedImage более удобно обновлять ограничение на аспект в updateConstraints. Таким образом, вы можете напрямую изменить изображение UIImageView без дополнительного вспомогательного метода:

static NSLayoutConstraint *constraintWithMultiplier(NSLayoutConstraint *constrain, CGFloat multiplier) {
    return [NSLayoutConstraint constraintWithItem:constrain.firstItem
                                        attribute:constrain.firstAttribute
                                        relatedBy:constrain.relation
                                           toItem:constrain.secondItem
                                        attribute:constrain.secondAttribute
                                       multiplier:multiplier
                                         constant:constrain.constant];
}

-(void)viewDidLoad
{
    UIView *contentView = self.contentView;

    _imageView = [[UIImageView alloc] init];
    _imageView.translatesAutoresizingMaskIntoConstraints = NO;
    _imageView.contentMode = UIViewContentModeScaleAspectFit;
    [contentView addSubview:_imageView];

    _imageAspectConstraint = [_imageView.heightAnchor constraintEqualToAnchor:_imageView.widthAnchor multiplier:1.0f];
    NSArray <NSLayoutConstraint *> *constraints = @[
            [_imageView.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor constant:0],
            [_imageView.trailingAnchor constraintEqualToAnchor:contentView.trailingAnchor constant:0],
            [_imageView.topAnchor constraintEqualToAnchor:contentView.topAnchor constant:0],
            [_imageView.bottomAnchor constraintEqualToAnchor:contentView.bottomAnchor constant:0],
    ];
    [NSLayoutConstraint activateConstraints:constraints];
}

-(void)updateConstraints
{
    const CGSize size = _imageView.image.size;
    const CGFloat multiplier = size.width / size.height;
    _imageAspectConstraint.active = NO;
    _imageAspectConstraint = constraintWithMultiplier(_imageAspectConstraint, multiplier);
    _imageAspectConstraint.active = YES;
    [super updateConstraints];
}