자동 레이아웃을 사용하여 tableHeaderView (UITableView)의 높이를 어떻게 설정합니까?


86

이걸로 지난 3 ~ 4 시간 동안 벽에 머리를 박고 있었는데 알아낼 수없는 것 같습니다. 내부에 전체 화면 UITableView가있는 UIViewController가 있고 (화면에 다른 항목이 있으므로 UITableViewController를 사용할 수 없습니다) 자동 레이아웃으로 tableHeaderView의 크기를 조정하고 싶습니다. 말할 필요도없이 그것은 협력하지 않습니다.

아래 스크린 샷을 참조하십시오.

여기에 이미지 설명 입력

OverviewLabel (예 : "List overview information here."텍스트)에는 동적 내용이 있기 때문에 자동 레이아웃을 사용하여 크기를 조정하고 수퍼 뷰입니다. 계층 구조의 Paralax Table View 바로 아래에있는 tableHeaderView를 제외하고는 모든 크기가 멋지게 크기 조정되었습니다.

헤더보기의 크기를 조정하는 유일한 방법은 다음 코드를 사용하는 프로그래밍 방식입니다.

CGRect headerFrame = self.headerView.frame;
headerFrame.size.height = headerFrameHeight;
self.headerView.frame = headerFrame;
[self.listTableView setTableHeaderView:self.headerView];

이 경우 headerFrameHeight는 다음과 같이 tableViewHeader 높이의 수동 계산입니다 (innerHeaderView는 흰색 영역이거나 두 번째 "View", 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의 크기를 자동으로 조정하는 것입니다. 하지만 제 삶을 위해 저는 그것을 이해할 수 없습니다.

이것에 대한 여러 게시물이 있지만 명확하지 않아서 나를 더 혼란스럽게 만들었습니다. 다음은 몇 가지입니다.

translatesAutoresizingMaskIntoConstraints 속성을 NO로 변경하는 것은 실제로 의미가 없습니다. 그 이유는 저에게 오류가 발생하고 어쨌든 개념적으로 이해가되지 않기 때문입니다.

어떤 도움이라도 정말 감사하겠습니다!

편집 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];

편집 2 : 다른 사람들이 언급했듯이 편집 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;
}

2
setTableHeaderViewXcode6에서는 작동하지 않습니다. 문제는 셀이 tableHeaderView에 의해 겹친다는 것입니다. 그러나, Xcode5에서 작동
DawnSong

@DawnSong은 답변을 업데이트 할 수 있도록 Xcode6에 대한 솔루션을 알고 있습니까?
Andrew Cross

1
많은 시도 끝에 tableHeaderView 대신 UITableViewCell을 사용하고 작동합니다.
DawnSong 2015

나도 머리를 부수었다!
Akshit Zaveri

진정한 완전 자동 레이아웃 솔루션은 여기
malex

답변:


35

UIView systemLayoutSizeFittingSize:헤더보기의 최소 경계 크기를 얻으려면 메서드 를 사용해야합니다 .

이 Q / A에서이 API 사용에 대한 추가 논의를 제공합니다.

자동 레이아웃으로 모든 하위보기에 맞게 superview의 크기를 조정하는 방법은 무엇입니까?


높이 = 0으로 돌아오고 있지만 이것이 UITableViewCell이 아니기 때문에 올바르게 구성했는지 100 % 확신하지 못하기 때문에 "systemLayoutSizeFittingSize"를 호출하는 contentView가 없습니다. 내가 시도한 것 : [self.headerView setNeedsLayout]; [self.headerView layoutIfNeeded]; CGFloat 높이 = [self.headerView systemLayoutSizeFittingSize : UILayoutFittingCompressedSize] .height; CGRect headerFrame = self.headerView.frame; headerFrame.size.height = 높이; self.headerView.frame = headerFrame; [self.listTableView setTableHeaderView : self.headerView]; 또한 preferredMax ...가 유효한지 확인했습니다.
Andrew Cross

아하! 그것을 알아 냈고, 나는 self.headerView 대신 [self.innerHeaderView systemLayoutSizeFittingSize ...]를 수행해야했습니다. 이것이 실제 동적 콘텐츠를 가지고 있기 때문입니다. 엄청 고마워!
Andrew Cross

작동하지만 내 앱에는 작은 높이 불일치가 있습니다. 아마도 내 레이블이 속성 텍스트와 동적 유형을 사용하고 있기 때문일 것입니다.
바이오

23

나는 정말로 이것과 싸웠고 프레임이 viewDidLoad에 설정되지 않았기 때문에 viewDidLoad에 설정을 꽂아도 작동하지 않았습니다. 또한 헤더의 캡슐화 된 자동 레이아웃 높이가 0으로 줄어들고있는 수많은 지저분한 경고로 끝났습니다. Form 프레젠테이션에서 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
        }
    }

최소한의 코드로 업데이트 된 솔루션을 보려면 다음을 시도하십시오. stackoverflow.com/a/63594053/3933302
Sourabh Sharma

23

애니메이션을 사용하거나 사용하지 않고 자동 레이아웃을 사용하여 표 머리글의 크기를 조정하는 우아한 방법을 찾았습니다.

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

AutoResizingHeader


안녕 p-sun. 좋은 예를 들어 주셔서 감사합니다. tableViewHeader에 문제가있는 것 같습니다. 귀하의 예제와 같은 제약 조건을 시도했지만 작동하지 않았습니다. 높이를 높이고 싶은 라벨에 제약 조건을 정확히 어떻게 추가해야합니까? 제안 해 주셔서 감사합니다
Bonnke 2016 년

1
확실히 당신은 당신을 길게 만들려면 라벨 훅 확인 @IBOutlet makeThisTaller@IBAction fun makeThisTaller예와 같이합니다. 또한 레이블의 모든면을 tableViewHeader (예 : 위쪽, 아래쪽, 왼쪽 및 오른쪽)로 제한합니다.
p-sun

감사합니다! 마지막으로 다음 줄을 추가하여 해결했습니다. lblFeedDescription.preferredMaxLayoutWidth = lblFeedDescription.bounds.width여기서 label은 크기를 늘리려는 것입니다. 감사 !
Bonnke 2016 년

감사! ViewController 대신 View 내에서이 작업을 수행하는 경우 viewDidLayoutSubviews를 재정의하는 대신 layoutSubviews를 재정의 할 수 있습니다.
Andy Weinstein

4

이 솔루션은 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


1
이 방법도 사용하고 있습니다. 제약이 없기 때문에 최고라고 생각하십시오. 또한 매우 컴팩트합니다.
바이오

3

systemLayoutSizeFittingSize :를 사용하는 솔루션은 헤더보기가 각보기 모양에서 한 번만 업데이트되는 경우 작동합니다. 제 경우에는 헤더보기가 상태 변경을 반영하기 위해 여러 번 업데이트되었습니다. 그러나 systemLayoutSizeFittingSize : 항상 동일한 크기를보고했습니다. 즉, 첫 번째 업데이트에 해당하는 크기입니다.

systemLayoutSizeFittingSize를 얻으려면 : 각 업데이트 후 올바른 크기를 반환하려면 먼저 테이블 헤더 뷰를 업데이트하고 다시 추가하기 전에 제거해야했습니다.

self.listTableView.tableHeaderView = nil;
[self.headerView removeFromSuperview];

2

이것은 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

}

2

머리글보기와 바닥 글 모두에서 작동합니다. 머리글을 바닥 글로 바꿉니다.

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
    }
}

1

iOS 12 이상의 경우 다음 단계를 수행하면 테이블과 헤더 모두에서 자동 레이아웃이 올바르게 작동합니다.

  1. 먼저 tableView를 만든 다음 헤더를 만듭니다.
  2. 헤더 생성 코드 끝에서 다음을 호출합니다.
[headerV setNeedsLayout];
[headerV layoutIfNeeded];
  1. 방향이 변경되면 헤더에 레이아웃을 다시 표시하고 테이블을 다시로드합니다. 이는 방향 변경이보고 된 후 약간 발생해야합니다.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 *NSEC_PER_SEC), dispatch_get_main_queue(), ^{

  [tableV.tableHeaderView setNeedsLayout];
  [tableV.tableHeaderView layoutIfNeeded];
  [tableV reloadData];});

0

제 경우에는 viewDidLayoutSubviews더 잘 작동했습니다. viewWillLayoutSubviewsa의 흰색 선이 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];
    }
}

0

UIViewAL 내부 서브 뷰 구조와 함께 일반 AutoLayout 기반 을 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.headerViewUI 회전에서 높이가 변경 되면 구현해야합니다.

- (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 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
    }
}

0

이 솔루션은 저에게 완벽하게 작동합니다.

https://spin.atomicobject.com/2017/08/11/swift-extending-uitableviewcontroller/

UITableViewController를 확장합니다. 그러나 UITableView를 사용하는 경우에도 여전히 작동합니다. UITableViewController 대신 UITableView를 확장하기 만하면됩니다. 메서드를 호출 sizeHeaderToFit()하거나 높이 sizeFooterToFit()를 변경하는 이벤트가있을 때마다 호출합니다 tableViewHeader.


0

이 게시물 에서 복사

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    
    if let headerView = tableView.tableHeaderView {

        let height = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).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
        }
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.