私は最後の3〜4時間、これで頭を壁にぶつけてきましたが、理解できないようです。内部にフルスクリーンUITableViewを備えたUIViewControllerがあり(画面上に他の要素がいくつかあるため、UITableViewControllerを使用できません)、tableHeaderViewを自動レイアウトでサイズ変更する必要があります。言うまでもなく、それは協力していません。
以下のスクリーンショットをご覧ください。
OverviewLabel(たとえば、「List overview information here。」というテキスト)には動的コンテンツがあるため、自動レイアウトを使用してサイズを変更し、スーパービューにしています。 tableHeaderViewを除き、すべてがうまくサイズ変更されています。tableHeaderViewは、階層構造のParalax Table Viewのすぐ下にあります。
ヘッダービューのサイズを変更する唯一の方法は、次のコードを使用してプログラムで行うことです。
CGRect headerFrame = self.headerView.frame;
headerFrame.size.height = headerFrameHeight;
self.headerView.frame = headerFrame;
[self.listTableView setTableHeaderView:self.headerView];
この場合、headerFrameHeightは次のようにtableViewHeaderの高さを手動で計算します(innerHeaderViewは白い領域、または2番目の「ビュー」、headerViewはtableHeaderView):
CGFloat startingY = self.innerHeaderView.frame.Origin.y + self.overviewLabel.frame.Origin.y;
CGRect overviewSize = [self.overviewLabel.text
boundingRectWithSize:CGSizeMake(290.f, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName: self.overviewLabel.font}
context:nil];
CGFloat overviewHeight = overviewSize.size.height;
CGFloat overviewPadding = ([self.overviewLabel.text length] > 0) ? 10 : 0; // If there's no overviewText, eliminate the padding in the overall height.
CGFloat headerFrameHeight = ceilf(startingY + overviewHeight + overviewPadding + 21.f + 10.f);
手動の計算は機能しますが、将来物事が変化すると不格好でエラーが発生しやすくなります。私ができるようにしたいのは、他の場所でできるように、提供された制約に基づいてtableHeaderViewを自動サイズ変更することです。しかし、私の人生では、私はそれを理解することはできません。
これについてSOにいくつかの投稿がありますが、明確なものはなく、結局私を混乱させます。ここにいくつかあります:
TranslatesAutoresizingMaskIntoConstraintsプロパティをNOに変更するのは意味がありません。それは単にエラーを引き起こし、概念的には意味をなさないからです。
どんな助けでも本当に感謝されます!
EDIT 1:TomSwiftの提案のおかげで、私はそれを理解することができました。概要の高さを手動で計算する代わりに、次のように計算してから、以前のようにtableHeaderViewを再設定できます。
[self.headerView setNeedsLayout];
[self.headerView layoutIfNeeded];
CGFloat height = [self.innerHeaderView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height + self.innerHeaderView.frame.Origin.y; // adding the Origin because innerHeaderView starts partway down headerView.
CGRect headerFrame = self.headerView.frame;
headerFrame.size.height = height;
self.headerView.frame = headerFrame;
[self.listTableView setTableHeaderView:self.headerView];
Edit 2:他の人が指摘したように、Edit 1に投稿されたソリューションはviewDidLoadで機能しないようです。ただし、viewWillLayoutSubviewsでは機能するようです。以下のコード例:
// Note 1: The variable names below don't match the variables above - this is intended to be a simplified "final" answer.
// Note 2: _headerView was previously assigned to tableViewHeader (in loadView in my case since I now do everything programatically).
// Note 3: autoLayout code can be setup programatically in updateViewConstraints.
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
[_headerWrapper setNeedsLayout];
[_headerWrapper layoutIfNeeded];
CGFloat height = [_headerWrapper systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
CGRect headerFrame = _headerWrapper.frame;
headerFrame.size.height = height;
_headerWrapper.frame = headerFrame;
_tableView.tableHeaderView = _headerWrapper;
}
UIView systemLayoutSizeFittingSize:
メソッドを使用して、ヘッダービューの最小境界サイズを取得する必要があります。
このQ/AでこのAPIの使用に関する詳細な説明を提供します。
私はこれと本当に戦いましたが、viewDidLoadにフレームが設定されていないため、viewDidLoadにセットアップを入れてもうまくいきませんでした。フォームプレゼンテーションでtableViewを表示するとき、iPadでのみ問題に気付きました。
この問題を解決したのは、viewDidLoadではなくviewWillLayoutSubviewsでtableViewHeaderを設定することでした。
func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
if tableView.tableViewHeaderView == nil {
let header: MyHeaderView = MyHeaderView.createHeaderView()
header.setNeedsUpdateConstraints()
header.updateConstraintsIfNeeded()
header.frame = CGRectMake(0, 0, CGRectGetWidth(tableView.bounds), CGFloat.max)
var newFrame = header.frame
header.setNeedsLayout()
header.layoutIfNeeded()
let newSize = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
newFrame.size.height = newSize.height
header.frame = newFrame
self.tableView.tableHeaderView = header
}
}
自動レイアウトを使用して、アニメーションの有無にかかわらずテーブルヘッダーのサイズを変更するエレガントな方法を見つけました。
これをView Controllerに追加するだけです。
func sizeHeaderToFit(tableView: UITableView) {
if let headerView = tableView.tableHeaderView {
let height = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
var frame = headerView.frame
frame.size.height = height
headerView.frame = frame
tableView.tableHeaderView = headerView
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
}
}
動的に変化するラベルに従ってサイズを変更するには:
@IBAction func addMoreText(sender: AnyObject) {
self.label.text = self.label.text! + "\nThis header can dynamically resize according to its contents."
}
override func viewDidLayoutSubviews() {
// viewDidLayoutSubviews is called when labels change.
super.viewDidLayoutSubviews()
sizeHeaderToFit(tableView)
}
制約の変更に応じてサイズ変更をアニメーション化するには:
@IBOutlet weak var makeThisTallerHeight: NSLayoutConstraint!
@IBAction func makeThisTaller(sender: AnyObject) {
UIView.animateWithDuration(0.3) {
self.tableView.beginUpdates()
self.makeThisTallerHeight.constant += 20
self.sizeHeaderToFit(self.tableView)
self.tableView.endUpdates()
}
}
AutoResizingHeaderプロジェクトを参照して、実際の動作を確認してください。 https://github.com/p-Sun/Swift2-iOS9-UI
SystemLayoutSizeFittingSizeを使用するソリューション:ビューの外観ごとにヘッダービューが1回だけ更新される場合に機能します。私の場合、ステータスの変更を反映するためにヘッダービューが複数回更新されました。しかし、systemLayoutSizeFittingSize:は常に同じサイズを報告しました。つまり、最初の更新に対応するサイズ。
SystemLayoutSizeFittingSizeを取得するには、各更新後に正しいサイズを返すために、更新してから再追加する前に最初にテーブルヘッダービューを削除する必要がありました。
self.listTableView.tableHeaderView = nil;
[self.headerView removeFromSuperview];
このソリューションは、tableHeaderViewのサイズを変更し、ここで他の回答のいくつかを使用していたviewDidLayoutSubviews()
メソッドでの無限ループを回避します。
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let headerView = tableView.tableHeaderView {
let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
var headerFrame = headerView.frame
// comparison necessary to avoid infinite loop
if height != headerFrame.size.height {
headerFrame.size.height = height
headerView.frame = headerFrame
tableView.tableHeaderView = headerView
}
}
}
この投稿も参照してください: https://stackoverflow.com/a/34689293/1245231
これはios10とXcode 8でうまくいきました
func layoutTableHeaderView() {
guard let headerView = tableView.tableHeaderView else { return }
headerView.translatesAutoresizingMaskIntoConstraints = false
let headerWidth = headerView.bounds.size.width;
let temporaryWidthConstraints = NSLayoutConstraint.constraintsWithVisualFormat("[headerView(width)]", options: NSLayoutFormatOptions(rawValue: UInt(0)), metrics: ["width": headerWidth], views: ["headerView": headerView])
headerView.addConstraints(temporaryWidthConstraints)
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
let headerSize = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
let height = headerSize.height
var frame = headerView.frame
frame.size.height = height
headerView.frame = frame
self.tableView.tableHeaderView = headerView
headerView.removeConstraints(temporaryWidthConstraints)
headerView.translatesAutoresizingMaskIntoConstraints = true
}
ヘッダービューとフッターの両方で機能し、ヘッダーをフッターに置き換えるだけです
func sizeHeaderToFit() {
if let headerView = tableView.tableHeaderView {
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
var frame = headerView.frame
frame.size.height = height
headerView.frame = frame
tableView.tableHeaderView = headerView
}
}
一般的なAutoLayoutベースのUIView
をAL内部サブビュー構造でtableHeaderView
として使用することは非常に可能です。
必要なのは、前に単純なtableFooterView
を設定することだけです!
self.headerView
が制約ベースのUIView
であるとします。
- (void)viewDidLoad {
........................
self.tableView.tableFooterView = [UIView new];
[self.headerView layoutIfNeeded]; // to set initial size
self.tableView.tableHeaderView = self.headerView;
[self.tableView.leadingAnchor constraintEqualToAnchor:self.headerView.leadingAnchor].active = YES;
[self.tableView.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor].active = YES;
[self.tableView.topAnchor constraintEqualToAnchor:self.headerView.topAnchor].active = YES;
// and the key constraint
[self.tableFooterView.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor].active = YES;
}
self.headerView
がUI回転で高さを変更する場合は、実装する必要があります
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
[coordinator animateAlongsideTransition: ^(id<UIViewControllerTransitionCoordinatorContext> context) {
// needed to resize header height
self.tableView.tableHeaderView = self.headerView;
} completion: NULL];
}
この目的でObjCカテゴリを使用できます
@interface UITableView (AMHeaderView)
- (void)am_insertHeaderView:(UIView *)headerView;
@end
@implementation UITableView (AMHeaderView)
- (void)am_insertHeaderView:(UIView *)headerView {
NSAssert(self.tableFooterView, @"Need to define tableFooterView first!");
[headerView layoutIfNeeded];
self.tableHeaderView = headerView;
[self.leadingAnchor constraintEqualToAnchor:headerView.leadingAnchor].active = YES;
[self.trailingAnchor constraintEqualToAnchor:headerView.trailingAnchor].active = YES;
[self.topAnchor constraintEqualToAnchor:headerView.topAnchor].active = YES;
[self.tableFooterView.trailingAnchor constraintEqualToAnchor:headerView.trailingAnchor].active = YES;
}
@end
また、Swift extension
extension UITableView {
func am_insertHeaderView2(_ headerView: UIView) {
assert(tableFooterView != nil, "Need to define tableFooterView first!")
headerView.layoutIfNeeded()
tableHeaderView = headerView
leadingAnchor.constraint(equalTo: headerView.leadingAnchor).isActive = true
trailingAnchor.constraint(equalTo: headerView.trailingAnchor).isActive = true
topAnchor.constraint(equalTo: headerView.topAnchor).isActive = true
tableFooterView?.trailingAnchor.constraint(equalTo: headerView.trailingAnchor).isActive = true
}
}
このソリューションは私にとって完璧に機能します:
https://spin.atomicobject.com/2017/08/11/Swift-extending-uitableviewcontroller/
UITableViewControllerを拡張します。ただし、UITableViewを使用している場合でも、UITableViewControllerではなくUITableViewを拡張するだけで機能します。 tableViewHeader
の高さを変更するイベントが発生するたびに、メソッドsizeHeaderToFit()
またはsizeFooterToFit()
を呼び出します。
私の場合、viewDidLayoutSubviews
の方がうまく機能しました。 viewWillLayoutSubviews
は、tableView
の白い線を表示します。また、headerView
オブジェクトが既に存在するかどうかのチェックを追加しました。
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
if ( ! self.userHeaderView ) {
// Setup HeaderView
self.userHeaderView = [[[NSBundle mainBundle] loadNibNamed:@"SSUserHeaderView" owner:self options:nil] objectAtIndex:0];
[self.userHeaderView setNeedsLayout];
[self.userHeaderView layoutIfNeeded];
CGFloat height = [self.userHeaderView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
CGRect headerFrame = self.userHeaderView.frame;
headerFrame.size.height = height;
self.userHeaderView.frame = headerFrame;
self.tableView.tableHeaderView = self.userHeaderView;
// Update HeaderView with data
[self.userHeaderView updateWithProfileData];
}
}