UIStackView 计算错误的 UIImageView 高度
UIStackView miscalculating UIImageView height
我有一个 UIStackView
包含在 UICollectionViewCell
中,代表社交网络上的 post(类似于 Instagram 的单元格)。 UIStackView
包含作者 header(自定义 UIView
),UIImageView
用于内容图像,UILabel
用于 post body,还有一个 UIToolbar
表示操作。最终视图看起来像 this.
UICollectionViewCell 的高度是通过设置大小调整单元格来设置的,如下所示:
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
NTVFeedBlogCollectionViewCell *cell = [[NTVFeedBlogCollectionViewCell alloc] initWithFrame:CGRectMake(10.0f, 0.0f, collectionView.frame.size.width - 20.0f, 900000.0)];
// ...
// Configure the cell
// ...
[cell setNeedsLayout];
[cell layoutIfNeeded];
CGSize size = [cell systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
return CGSizeMake(CGRectGetWidth(collectionView.frame) - 20.0f, size.height);
}
问题是 UIImageView 似乎增加了 UICollectionViewCell 的大小,没有增加图像视图的大小。 This is the result 当我将大图像添加到图像视图时。期望的结果是图像保持 1:1 纵横比,限制在 UIStackView 的宽度内。
body 标签和其余内容之间的间隙大小会根据我使用的图像而变化。这让我相信 UIStackView
只是在 大小调整 的单元格中以某种方式考虑了图像大小,因为在视觉上图像视图是其正确的大小。正如您在第一张图片 posted 中看到的,当未设置图片视图的图片时,单元格布局完美。
以下是堆栈视图、图像视图和标签的设置代码:
self.stackView = [[UIStackView alloc] initWithFrame:CGRectZero];
self.stackView.translatesAutoresizingMaskIntoConstraints = NO;
self.stackView.axis = UILayoutConstraintAxisVertical;
self.stackView.distribution = UIStackViewDistributionFill;
self.stackView.alignment = UIStackViewAlignmentFill;
self.stackView.spacing = 10.0f;
self.stackView.layoutMargins = UIEdgeInsetsMake(10.0f, 10.0f, 0.0f, 10.0f);
self.stackView.layoutMarginsRelativeArrangement = YES;
[self addSubview:self.stackView];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[stackView]-0-|"
options:0
metrics:nil
views:@{@"stackView": self.stackView}]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[stackView]-0-|"
options:0
metrics:nil
views:@{@"stackView": self.stackView}]];
self.imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
[self.imageView setImage:[UIImage imageNamed:@"sample_image.png"]];
self.imageView.translatesAutoresizingMaskIntoConstraints = NO;
self.imageView.contentMode = UIViewContentModeScaleAspectFill;
self.imageView.clipsToBounds = YES;
self.imageView.layer.masksToBounds = YES;
self.imageView.backgroundColor = [UIColor greenColor];
[self.imageView setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisVertical];
[self.imageView setContentCompressionResistancePriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisVertical];
[self.stackView addArrangedSubview:self.imageView];
[self.stackView addConstraint:[NSLayoutConstraint constraintWithItem:self.imageView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.stackView
attribute:NSLayoutAttributeWidth
multiplier:1.0f
constant:0.0f]];
self.bodyLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self.bodyLabel setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisVertical];
[self.bodyLabel setContentCompressionResistancePriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisVertical];
self.bodyLabel.font = [UIFont ntv_lightFontWithSize:17.0f];
self.bodyLabel.textColor = [UIColor whiteColor];
self.bodyLabel.numberOfLines = 0;
self.bodyLabel.text = @"While the stack view allows you to layout its contents without using Auto Layout directly, you still need to use Auto Layout to position the stack view, itself.";
[self.stackView addArrangedSubview:self.bodyLabel];
self.accessoryView = [[NTVAccessoryView alloc] initWithFrame:CGRectZero];
self.accessoryView.viewModel = [NTVAccessoryManagerViewModel_Stubbed new];
[self.stackView addArrangedSubview:self.accessoryView];
有什么想法吗?
我们想通了!如文档中所述,UIStackView
使用其 arrangedSubviews
的 intrinsicContentSize
来计算其高度。
UIImageView
将其 intrinsicContentSize
报告为图像的整体大小(如您所料)。 UIStackView
忽略了我的身高限制,只是 使用图像视图的 intrinsicContentSize
。
为了解决这个问题,我创建了一个 UIImageView
子类,允许调用者设置 intrinsicContentSize
。可以找到此代码 here.
然后,在我的 UICollectionViewCell
布局传递完成后,我正在设置 UIImageView 子类' intrinsicContentSize
:
- (void)layoutSubviews {
[super layoutSubviews];
// Constrain the image view's content size to match the stackview's width.
self.imageView.constrainedSize = CGSizeMake(self.stackView.frame.size.width, self.stackView.frame.size.width);
[super layoutSubviews];
}
如果您不想以编程方式进行,另一种解决方案是垂直使用 2 个 UIStackView。包含 UIImageView 的顶部 UIStackView,将其高度约束设置为占用 superview 高度的 70%,并让底部 UIStackView 紧挨着顶部,因此它将占据剩余的 30% 高度。
这样无论你的图片有多大,它总是占据70%的高度。
我有一个 UIStackView
包含在 UICollectionViewCell
中,代表社交网络上的 post(类似于 Instagram 的单元格)。 UIStackView
包含作者 header(自定义 UIView
),UIImageView
用于内容图像,UILabel
用于 post body,还有一个 UIToolbar
表示操作。最终视图看起来像 this.
UICollectionViewCell 的高度是通过设置大小调整单元格来设置的,如下所示:
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
NTVFeedBlogCollectionViewCell *cell = [[NTVFeedBlogCollectionViewCell alloc] initWithFrame:CGRectMake(10.0f, 0.0f, collectionView.frame.size.width - 20.0f, 900000.0)];
// ...
// Configure the cell
// ...
[cell setNeedsLayout];
[cell layoutIfNeeded];
CGSize size = [cell systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
return CGSizeMake(CGRectGetWidth(collectionView.frame) - 20.0f, size.height);
}
问题是 UIImageView 似乎增加了 UICollectionViewCell 的大小,没有增加图像视图的大小。 This is the result 当我将大图像添加到图像视图时。期望的结果是图像保持 1:1 纵横比,限制在 UIStackView 的宽度内。
body 标签和其余内容之间的间隙大小会根据我使用的图像而变化。这让我相信 UIStackView
只是在 大小调整 的单元格中以某种方式考虑了图像大小,因为在视觉上图像视图是其正确的大小。正如您在第一张图片 posted 中看到的,当未设置图片视图的图片时,单元格布局完美。
以下是堆栈视图、图像视图和标签的设置代码:
self.stackView = [[UIStackView alloc] initWithFrame:CGRectZero];
self.stackView.translatesAutoresizingMaskIntoConstraints = NO;
self.stackView.axis = UILayoutConstraintAxisVertical;
self.stackView.distribution = UIStackViewDistributionFill;
self.stackView.alignment = UIStackViewAlignmentFill;
self.stackView.spacing = 10.0f;
self.stackView.layoutMargins = UIEdgeInsetsMake(10.0f, 10.0f, 0.0f, 10.0f);
self.stackView.layoutMarginsRelativeArrangement = YES;
[self addSubview:self.stackView];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[stackView]-0-|"
options:0
metrics:nil
views:@{@"stackView": self.stackView}]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[stackView]-0-|"
options:0
metrics:nil
views:@{@"stackView": self.stackView}]];
self.imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
[self.imageView setImage:[UIImage imageNamed:@"sample_image.png"]];
self.imageView.translatesAutoresizingMaskIntoConstraints = NO;
self.imageView.contentMode = UIViewContentModeScaleAspectFill;
self.imageView.clipsToBounds = YES;
self.imageView.layer.masksToBounds = YES;
self.imageView.backgroundColor = [UIColor greenColor];
[self.imageView setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisVertical];
[self.imageView setContentCompressionResistancePriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisVertical];
[self.stackView addArrangedSubview:self.imageView];
[self.stackView addConstraint:[NSLayoutConstraint constraintWithItem:self.imageView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.stackView
attribute:NSLayoutAttributeWidth
multiplier:1.0f
constant:0.0f]];
self.bodyLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self.bodyLabel setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisVertical];
[self.bodyLabel setContentCompressionResistancePriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisVertical];
self.bodyLabel.font = [UIFont ntv_lightFontWithSize:17.0f];
self.bodyLabel.textColor = [UIColor whiteColor];
self.bodyLabel.numberOfLines = 0;
self.bodyLabel.text = @"While the stack view allows you to layout its contents without using Auto Layout directly, you still need to use Auto Layout to position the stack view, itself.";
[self.stackView addArrangedSubview:self.bodyLabel];
self.accessoryView = [[NTVAccessoryView alloc] initWithFrame:CGRectZero];
self.accessoryView.viewModel = [NTVAccessoryManagerViewModel_Stubbed new];
[self.stackView addArrangedSubview:self.accessoryView];
有什么想法吗?
我们想通了!如文档中所述,UIStackView
使用其 arrangedSubviews
的 intrinsicContentSize
来计算其高度。
UIImageView
将其 intrinsicContentSize
报告为图像的整体大小(如您所料)。 UIStackView
忽略了我的身高限制,只是 使用图像视图的 intrinsicContentSize
。
为了解决这个问题,我创建了一个 UIImageView
子类,允许调用者设置 intrinsicContentSize
。可以找到此代码 here.
然后,在我的 UICollectionViewCell
布局传递完成后,我正在设置 UIImageView 子类' intrinsicContentSize
:
- (void)layoutSubviews {
[super layoutSubviews];
// Constrain the image view's content size to match the stackview's width.
self.imageView.constrainedSize = CGSizeMake(self.stackView.frame.size.width, self.stackView.frame.size.width);
[super layoutSubviews];
}
如果您不想以编程方式进行,另一种解决方案是垂直使用 2 个 UIStackView。包含 UIImageView 的顶部 UIStackView,将其高度约束设置为占用 superview 高度的 70%,并让底部 UIStackView 紧挨着顶部,因此它将占据剩余的 30% 高度。
这样无论你的图片有多大,它总是占据70%的高度。