首页 文章

自动布局:获取UIImageView高度以正确计算单元格高度

提问于
浏览
35

到目前为止,我一直只是在单元格中使用自动调整大小的文本标签 . 我通常将约束放在所有边(内容视图或邻居)上,并将以下行添加到我的 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代码问题 . 所以要恢复:我的最终目标是获得一个具有正确大小(正确的高度)单元格的表格,每个单元格包含一个符合屏幕宽度的 Headers 和图像 . 那些图像可以具有不同的纵横比和不同的尺寸 . 为了简化一些挑战,我准备了一个新的,更简单的项目:

  • 首先,我创建了一个 UITableViewController 和一个 CustomCell 类,其中包含两个IBOutlets title: StringpostedImage: UIImage (故事板中随机大小的UIImage)来配置单元格 . 我定义了边缘和彼此的约束 .

  • 构建时,两个单元格配置为包含相同的图像,但它们是从具有不同大小和分辨率的两个单独文件中获取的(900 x 171与半尺寸450 x 86) . UIImage视图模式设置为"Aspect Fit"但由于我不能确定这是正确的设置't get it run the way I want so far I' . 虽然我曾期望根据重新调整的UIImage计算单元格高度,包括我设置的约束,但它似乎是基于图像文件的初始高度(所以171和86) . 它没有猜测) .

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

在我的一个更复杂的项目中,我想要不同的图像自动适应屏幕的宽度,无论它们是纵向还是横向 - 如果它们的宽度小于屏幕的宽度,它们应该按比例放大 . 与上面的项目一样,每个单元格高度应符合故事板中约束所定义的各个内容 . 无论如何,它从上面讨论的问题开始:我怎样才能让这两个单元格具有相同的正确高度,拥抱故事板中定义的内容?

万分感谢您的帮助!

5 回答

  • 1

    所以我认为潜在的问题是一种鸡和蛋的问题 .

    为了'aspect fit'图像到 UIImageView 系统需要一个视图的大小,但我们希望确定视图的高度 after 方面拟合给定视图的已知宽度的图像 .

    解决方法是自己计算图像的纵横比,并在设置图像时将其设置为 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

    希望这可以帮助!

  • 72

    我将Pollard的解决方案翻译成objc版本 .
    我也收到了像"Unable to simultaneously satisfy constraints"这样的警告 . 我的解决方案是将垂直空间约束的优先级设置为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
    
  • 0

    Swift 3 + iOS 8 工作

    使用AutoLayout和可变大小的UITableViewCell对象 -

    对于imageview,将top,bottom,leading和trailing space constraint设置为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
        }
    }
    
  • 7

    而不是使用自定义 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];
    }
    
  • 0

    Swift 4

    使用 UIImage extension 计算图像高度 .

    您可以根据图像的AspectRatio使用以下 extensionUIImage 来获取高度 .

    extension UIImage {
        var cropRatio: CGFloat {
            let widthRatio = CGFloat(self.size.width / self.size.height)
            return widthRatio
        }
    }
    

    所以,在你的 override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat 方法中 .

    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
                let currentImage = dataSource[indexPath.row].image
                let imageCrop = currentImage.cropRatio
                var _height = tableView.frame.width / imageCrop
                return _height
    }
    

相关问题