Автоматическая компоновка: получить высоту 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 случайным образом размером в раскадровку) для настройки ячеек. Я определил ограничения для ребер и друг друга.
![]()
- При построении две ячейки получают конфигурацию, содержащую одно и то же изображение, но они извлекаются из двух отдельных файлов разного размера и разрешений (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]()
В несколько более сложном моем проекте я хочу, чтобы разные изображения автоматически соответствовали ширине экрана независимо от того, являются ли они портретными или ландшафтными, а в случае, если их ширина меньше ширины экрана, их следует увеличить. Как и в проекте выше, каждая высота ячейки должна соответствовать его индивидуальному контенту, как определено ограничениями в раскадровке. Во всяком случае, он начинается с проблемы, обсуждавшейся выше: как я могу позволить этим двум ячейкам иметь одинаковые, правильную высоту, обнимать ее содержимое, как это определено в раскадровке?
Спасибо за помощь!
Ответы
Ответ 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];
}