我正在创建一个应用程序,它将在中有一个问题,UILabel
并在中显示选择题答案UITableView
,每行显示一个选择题。问题和答案会有所不同,因此我需要UITableView
保持高度变化。
我想sizeToFit
在桌子旁找个工作。将表格的框架设置为所有内容的高度。
谁能建议我如何实现这一目标?
- sizeToFit
方法?
我正在创建一个应用程序,它将在中有一个问题,UILabel
并在中显示选择题答案UITableView
,每行显示一个选择题。问题和答案会有所不同,因此我需要UITableView
保持高度变化。
我想sizeToFit
在桌子旁找个工作。将表格的框架设置为所有内容的高度。
谁能建议我如何实现这一目标?
- sizeToFit
方法?
Answers:
其实我自己找到了答案。
我只是创建一个新CGRect
的tableView.frame
与height
的table.contentSize.height
这台的高度UITableView
到height
它的内容。由于代码修改了UI,因此请不要忘记在主线程中运行它:
dispatch_async(dispatch_get_main_queue(), ^{
//This code will run in the main thread:
CGRect frame = self.tableView.frame;
frame.size.height = self.tableView.contentSize.height;
self.tableView.frame = frame;
});
无需KVO,DispatchQueue或自行设置约束的Swift 5和4.2解决方案。
该解决方案基于Gulz的答案。
1)创建以下子类UITableView
:
import UIKit
final class ContentSizedTableView: UITableView {
override var contentSize:CGSize {
didSet {
invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
layoutIfNeeded()
return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
}
}
2)将a添加UITableView
到您的布局中,并在所有侧面设置约束。将其类别设置为ContentSizedTableView
。
3)您应该看到一些错误,因为Storyboard没有考虑我们的子类intrinsicContentSize
。通过打开大小检查器并将internalContentSize重写为占位符值来解决此问题。这是设计时间的替代。在运行时,它将在ContentSizedTableView
类中使用重写
更新:更改了Swift 4.2的代码。如果您使用的是早期版本,请使用UIViewNoIntrinsicMetric
代替UIView.noIntrinsicMetric
tableView
是 IntrinsicTableView
在故事板。我不需要将表格视图嵌入另一个视图中UIView
。
tableView
为IntrinsicTableView
。另外,不需要额外包装UIView
。您当然可以使用任何现有的父视图:)
tableView.sizeToFit()
sizeToFit()
打电话
快速解决方案
跟着这些步骤:
1-在情节提要中为表格设置高度限制。
2-从情节提要中拖动高度约束,并在视图控制器文件中为其创建@IBOutlet。
@IBOutlet weak var tableHeight: NSLayoutConstraint!
3-然后,您可以使用以下代码动态更改桌子的高度:
override func viewWillLayoutSubviews() {
super.updateViewConstraints()
self.tableHeight?.constant = self.table.contentSize.height
}
更新资料
如果为您剪切了最后一行,请尝试在willDisplay单元格函数中调用viewWillLayoutSubviews():
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
self.viewWillLayoutSubviews()
}
我已经在iOS 7中尝试过了,对我有用
- (void)viewDidLoad
{
[super viewDidLoad];
[self.tableView sizeToFit];
}
在表视图上为contentSize属性添加观察者,并相应地调整框架大小
[your_tableview addObserver:self forKeyPath:@"contentSize" options:0 context:NULL];
然后在回调中:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
CGRect frame = your_tableview.frame;
frame.size = your_tableview.contentSize;
your_tableview.frame = frame;
}
希望这会帮助你。
我在滚动视图中有一个表格视图,不得不计算tableView的高度并相应地调整其大小。这些是我采取的步骤:
0)将UIView添加到您的scrollView中(可能无需此步骤即可工作,但我这样做是为了避免任何可能的冲突)-这将是表视图的容器视图。如果执行此步骤,则将视图边框设置为tableview的边框。
1)创建UITableView的子类:
class IntrinsicTableView: UITableView {
override var contentSize:CGSize {
didSet {
self.invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
self.layoutIfNeeded()
return CGSize(width: UIViewNoIntrinsicMetric, height: contentSize.height)
}
}
2)在Storyboard中将表视图的类设置为IntrinsicTableView:屏幕快照:http : //joxi.ru/a2XEENpsyBWq0A
3)将heightConstraint设置为表格视图
4)将表格的IBoutlet拖到ViewController
5)将表格高度约束的IBoutlet拖到ViewController
6)将此方法添加到您的ViewController中:
override func viewWillLayoutSubviews() {
super.updateViewConstraints()
self.yourTableViewsHeightConstraint?.constant = self.yourTableView.intrinsicContentSize.height
}
希望这可以帮助
如果您不想自己跟踪表视图的内容大小更改,则可能会发现此子类很有用。
protocol ContentFittingTableViewDelegate: UITableViewDelegate {
func tableViewDidUpdateContentSize(_ tableView: UITableView)
}
class ContentFittingTableView: UITableView {
override var contentSize: CGSize {
didSet {
if !constraints.isEmpty {
invalidateIntrinsicContentSize()
} else {
sizeToFit()
}
if contentSize != oldValue {
if let delegate = delegate as? ContentFittingTableViewDelegate {
delegate.tableViewDidUpdateContentSize(self)
}
}
}
}
override var intrinsicContentSize: CGSize {
return contentSize
}
override func sizeThatFits(_ size: CGSize) -> CGSize {
return contentSize
}
}
override public var intrinsicContentSize: CGSize { //... return someCGSize }
如果您的contentSize是不正确的,这是因为它基于estimatedRowHeight(自动),请在使用之前
tableView.estimatedRowHeight = 0;
Swift 3,iOS 10.3
解决方法1:
只要把self.tableview.sizeToFit()
在cellForRowAt indexPath
函数。确保将tableview的高度设置为高于所需的高度。如果您在tableview下没有视图,这是一个很好的解决方案。但是,如果有的话,底部的tableview约束将不会更新(我没有尝试解决它,因为我想出了解决方案2)
例:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "TestCell", for: indexPath) as? TestCell {
cell.configureCell(data: testArray[indexPath.row])
self.postsTableView.sizeToFit()
return cell
}
return UITableViewCell()
}
解决方案2: 在情节提要中设置tableview高度约束并将其拖动到ViewController。如果您知道单元格的平均高度,并且知道数组包含多少个元素,则可以执行以下操作:
tableViewHeightConstraint.constant = CGFloat(testArray.count) * 90.0 // Let's say 90 is the average cell height
*编辑:
在我尝试了所有解决方案之后,每个解决方案都在解决问题,但是还没有完全解决,这就是可以完全解释和解决此问题的答案。
Mimo的答案和Anooj VM的答案都很棒,但是如果列表很大,则存在一个小问题,框架的高度可能会切断某些单元格。
所以。我对答案做了一些修改:
dispatch_async(dispatch_get_main_queue()) {
//This code will run in the main thread:
CGFloat newHeight=self.tableView.contentSize.height;
CGFloat screenHeightPermissible=(self.view.bounds.size.height-self.tableView.frame.origin.y);
if (newHeight>screenHeightPermissible)
{
//so that table view remains scrollable when 'newHeight' exceeds the screen bounds
newHeight=screenHeightPermissible;
}
CGRect frame = self.tableView.frame;
frame.size.height = newHeight;
self.tableView.frame = frame;
}
如果使用AutoLayout,则有更好的方法:更改确定高度的约束。只需计算表内容的高度,然后找到约束并进行更改即可。这是一个示例(假设确定表的高度的约束实际上是具有“等于”关系的高度约束):
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
for constraint in tableView.constraints {
if constraint.firstItem as? UITableView == tableView {
if constraint.firstAttribute == .height {
constraint.constant = tableView.contentSize.height
}
}
}
}
myTableHeightConstraint.constant = tableView.contentSize.height
这对于使用自动版式的我来说是有效的,并且只有一个部分的表格视图。
func getTableViewContentHeight(tableView: UITableView) -> CGFloat {
tableView.bounds = CGRect(x: 0, y: 0, width: 300, height: 40)
let rows = tableView.numberOfRows(inSection: 0)
var height = CGFloat(0)
for n in 0...rows - 1 {
height = height + tableView.rectForRow(at: IndexPath(row: n, section: 0)).height
}
return height
}
我在设置自动版式时调用此函数(此处的示例使用SnapKit,但您知道了):
let height = getTableViewContentHeight(tableView: myTableView)
myTableView.snp.makeConstraints {
...
...
$0.height.equalTo(height)
}
我希望UITableView仅与单元格的组合高度一样高;我遍历单元格并累积单元格的总高度。由于此时表视图的大小为CGRect.zero,因此我需要设置范围以遵守单元格定义的自动布局规则。我将大小设置为应该足够大的任意值。实际尺寸将在以后由自动版面系统计算。
我做了一些不同的事情,实际上我的TableView是在scrollview内部,所以我不得不将高度限制设为0。
然后在运行时我进行了以下更改,
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
self.viewWillLayoutSubviews()
}
override func viewWillLayoutSubviews() {
super.updateViewConstraints()
DispatchQueue.main.async {
self.tableViewHeightConstraint?.constant = self.myTableView.contentSize.height
self.view.layoutIfNeeded()
}
}
作为Anooj VM答案的扩展,我建议以下内容仅在内容大小更改时刷新。
此方法还禁用了正确滚动,并支持更大的列表和旋转。因为contentSize的更改是在主线程上调度的,所以不需要dispatch_async。
- (void)viewDidLoad {
[super viewDidLoad];
[self.tableView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:NULL];
}
- (void)resizeTableAccordingToContentSize:(CGSize)newContentSize {
CGRect superviewTableFrame = self.tableView.superview.bounds;
CGRect tableFrame = self.tableView.frame;
BOOL shouldScroll = newContentSize.height > superviewTableFrame.size.height;
tableFrame.size = shouldScroll ? superviewTableFrame.size : newContentSize;
[UIView animateWithDuration:0.3
delay:0
options:UIViewAnimationOptionCurveLinear
animations:^{
self.tableView.frame = tableFrame;
} completion: nil];
self.tableView.scrollEnabled = shouldScroll;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if ([change[NSKeyValueChangeKindKey] unsignedIntValue] == NSKeyValueChangeSetting &&
[keyPath isEqualToString:@"contentSize"] &&
!CGSizeEqualToSize([change[NSKeyValueChangeOldKey] CGSizeValue], [change[NSKeyValueChangeNewKey] CGSizeValue])) {
[self resizeTableAccordingToContentSize:[change[NSKeyValueChangeNewKey] CGSizeValue]];
}
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
[self resizeTableAccordingToContentSize:self.tableView.contentSize]; }
- (void)dealloc {
[self.tableView removeObserver:self forKeyPath:@"contentSize"];
}
Musa almatri的objc版本
(void)viewWillLayoutSubviews
{
[super updateViewConstraints];
CGFloat desiredHeight = self.tableView.contentSize.height;
// clamp desired height, if needed, and, in that case, leave scroll Enabled
self.tableHeight.constant = desiredHeight;
self.tableView.scrollEnabled = NO;
}
您可以尝试此自定义 AGTableView
使用情节提要或以编程方式设置TableView高度限制。(此类自动获取高度约束并将内容视图高度设置为yourtableview高度)。
class AGTableView: UITableView {
fileprivate var heightConstraint: NSLayoutConstraint!
override init(frame: CGRect, style: UITableViewStyle) {
super.init(frame: frame, style: style)
self.associateConstraints()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.associateConstraints()
}
override open func layoutSubviews() {
super.layoutSubviews()
if self.heightConstraint != nil {
self.heightConstraint.constant = self.contentSize.height
}
else{
self.sizeToFit()
print("Set a heightConstraint to Resizing UITableView to fit content")
}
}
func associateConstraints() {
// iterate through height constraints and identify
for constraint: NSLayoutConstraint in constraints {
if constraint.firstAttribute == .height {
if constraint.relation == .equal {
heightConstraint = constraint
}
}
}
}
}
注意如果在设置高度时有任何问题,请yourTableView.layoutSubviews()
。
基于fl034的答案。但是对于Xamarin.iOS用户:
[Register("ContentSizedTableView")]
public class ContentSizedTableView : UITableView
{
public ContentSizedTableView(IntPtr handle) : base(handle)
{
}
public override CGSize ContentSize { get => base.ContentSize; set { base.ContentSize = value; InvalidateIntrinsicContentSize(); } }
public override CGSize IntrinsicContentSize
{
get
{
this.LayoutIfNeeded();
return new CGSize(width: NoIntrinsicMetric, height: ContentSize.Height);
}
}
}
我的Swift 5实现是将tableView的最高约束设置为其内容(contentSize.height
)的大小。此方法假定您正在使用自动布局。这段代码应该放在cellForRowAt
tableView方法中。
tableView.heightAnchor.constraint(equalToConstant: tableView.contentSize.height).isActive = true