[iphone] UITableView의 tableHeaderView와 함께 AutoLayout을 사용할 수 있습니까?

내가 AutoLayout어디서나 그것을 사용한다는 것을 알았 기 때문에 이제는 tableHeaderView.

내가 만든 subclassUIView(라벨 등) 그때 나는이 추가, 자신의 제약 원 추가 모든 CustomView받는 UITableViewtableHeaderView.

모든이를 제외하고 잘 작동 UITableView항상 표시 위에CustomView 에 의해 위의 나는 의미 CustomView입니다 아래UITableView 이 볼 수 없도록!

내가 무엇을하든 ‘ heightof the UITableViewtableHeaderView항상 0 인 것 같습니다 (너비, x 및 y).

내 질문 : 프레임을 수동으로 설정하지 않고도이 작업을 수행 할 수 있습니까?

편집 : 내가 사용
하는 CustomViewsubview에는 다음과 같은 제약이 있습니다.

_title = [[UILabel alloc]init];
_title.text = @"Title";
[self addSubview:_title];
[_title keep:[KeepTopInset rules:@[[KeepEqual must:5]]]]; // title has to stay at least 5 away from the supperview Top
[_title keep:[KeepRightInset rules:@[[KeepMin must:5]]]];
[_title keep:[KeepLeftInset rules:@[[KeepMin must:5]]]];
[_title keep:[KeepBottomInset rules:@[[KeepMin must:5]]]];

제약 조건을 수동으로 작성하려면 하나의 제약 조건에 대해 너무 많은 줄이 필요하지만 메서드는 자체 설명이 가능하기 때문에 편리한 라이브러리 ‘KeepLayout’을 사용하고 있습니다.

그리고 UITableView다음과 같은 제약이 있습니다.

_tableView = [[UITableView alloc]init];
_tableView.translatesAutoresizingMaskIntoConstraints = NO;
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.backgroundColor = [UIColor clearColor];
[self.view addSubview:_tableView];
[_tableView keep:[KeepTopInset rules:@[[KeepEqual must:0]]]];// These 4 constraints make the UITableView stays 0 away from the superview top left right and bottom.
[_tableView keep:[KeepLeftInset rules:@[[KeepEqual must:0]]]];
[_tableView keep:[KeepRightInset rules:@[[KeepEqual must:0]]]];
[_tableView keep:[KeepBottomInset rules:@[[KeepEqual must:0]]]];

_detailsView = [[CustomView alloc]init];
_tableView.tableHeaderView = _detailsView;

에서 직접 제약 조건을 설정해야하는지 모르겠지만 CustomViewCustomView의 높이는 UILabel“제목”에 대한 제약 조건에 의해 결정 된다고 생각합니다.

편집 2 : 다른 조사 후 CustomView의 높이와 너비가 올바르게 계산 된 것 같지만 CustomView의 상단은 여전히 ​​UITableView의 상단과 동일한 수준이며 스크롤 할 때 함께 이동합니다.



답변

나는 여기 에서 비슷한 질문을하고 대답했다 . 요약하면 헤더를 한 번 추가하고 필요한 높이를 찾는 데 사용합니다. 그런 다음 해당 높이를 헤더에 적용 할 수 있으며 헤더는 변경 사항을 반영하기 위해 두 번째로 설정됩니다.

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.header = [[SCAMessageView alloc] init];
    self.header.titleLabel.text = @"Warning";
    self.header.subtitleLabel.text = @"This is a message with enough text to span multiple lines. This text is set at runtime and might be short or long.";

    //set the tableHeaderView so that the required height can be determined
    self.tableView.tableHeaderView = self.header;
    [self.header setNeedsLayout];
    [self.header layoutIfNeeded];
    CGFloat height = [self.header systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;

    //update the header's frame and set it again
    CGRect headerFrame = self.header.frame;
    headerFrame.size.height = height;
    self.header.frame = headerFrame;
    self.tableView.tableHeaderView = self.header;
}

여러 줄 레이블이있는 경우 각 레이블의 preferredMaxLayoutWidth를 설정하는 사용자 정의보기에도 의존합니다.

- (void)layoutSubviews
{
    [super layoutSubviews];

    self.titleLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.titleLabel.frame);
    self.subtitleLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.subtitleLabel.frame);
}

또는 더 일반적으로 :

override func layoutSubviews() {
    super.layoutSubviews()  
    for view in subviews {
        guard let label = view as? UILabel where label.numberOfLines == 0 else { continue }
        label.preferredMaxLayoutWidth = CGRectGetWidth(label.frame)
    }
}

2015 년 1 월 업데이트

불행히도 이것은 여전히 ​​필요한 것 같습니다. 다음은 레이아웃 프로세스의 빠른 버전입니다.

tableView.tableHeaderView = header
header.setNeedsLayout()
header.layoutIfNeeded()
header.frame.size = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
tableView.tableHeaderView = header

이것을 UITableView의 확장으로 옮기는 것이 유용하다는 것을 알았습니다.

extension UITableView {
    //set the tableHeaderView so that the required height can be determined, update the header's frame and set it again
    func setAndLayoutTableHeaderView(header: UIView) {
        self.tableHeaderView = header
        header.setNeedsLayout()
        header.layoutIfNeeded()
        header.frame.size = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
        self.tableHeaderView = header
    }
}

용법:

let header = SCAMessageView()
header.titleLabel.text = "Warning"
header.subtitleLabel.text = "Warning message here."
tableView.setAndLayoutTableHeaderView(header)


답변

제약 조건 (코드)을 사용하여 헤더보기를 추가 할 수 없습니다. 내 뷰에 너비 및 / 또는 높이 제약 조건을 지정하면 다음과 같은 메시지와 함께 충돌이 발생합니다.

 "terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Auto Layout still required after executing -layoutSubviews. UITableView's implementation of -layoutSubviews needs to call super."

스토리 보드의 뷰를 테이블 뷰에 추가하면 제약이없고 헤더 뷰로 잘 작동하므로 제약 조건을 사용하여 헤더 뷰의 배치가 수행되지 않는다고 생각합니다. 그런 점에서 정상적인 견해처럼 행동하지 않는 것 같습니다.

너비는 자동으로 테이블보기의 너비이며, 설정해야하는 유일한 것은 높이입니다. 원점 값은 무시되므로 입력 한 값은 중요하지 않습니다. 예를 들어, 이것은 잘 작동했습니다 (rect의 경우 0,0,0,80처럼).

UIView *headerview = [[UIView alloc] initWithFrame:CGRectMake(1000,1000, 0, 80)];
headerview.backgroundColor = [UIColor yellowColor];
self.tableView.tableHeaderView = headerview;


답변

여기에서 불필요한 작업을 많이하는 많은 방법을 보았지만 헤더보기에서 자동 레이아웃을 사용하는 데 그다지 많이 필요하지 않습니다. xib 파일을 만들고 제약 조건을 설정하고 다음과 같이 인스턴스화하면됩니다.

func loadHeaderView () {
        guard let headerView = Bundle.main.loadNibNamed("CourseSearchHeader", owner: self, options: nil)?[0] as? UIView else {
            return
        }
        headerView.autoresizingMask = .flexibleWidth
        headerView.translatesAutoresizingMaskIntoConstraints = true
        tableView.tableHeaderView = headerView
    }


답변

또 다른 해결책은 헤더 뷰 생성을 다음 메인 스레드 호출로 디스패치하는 것입니다.

- (void)viewDidLoad {
    [super viewDidLoad];

    // ....

    dispatch_async(dispatch_get_main_queue(), ^{
        _profileView = [[MyView alloc] initWithNib:@"MyView.xib"];
        self.tableView.tableHeaderView = self.profileView;
    });
}

참고 :로드 된 뷰의 높이가 고정 된 경우 버그를 수정합니다. 헤더 높이가 콘텐츠에만 의존 할 때 시도하지 않았습니다.

편집하다 :

함수 를 구현 하고 호출 하여이 문제에 대한 더 깨끗한 해결책을 찾을 수 있습니다 .viewDidLayoutSubviews

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    [self sizeHeaderToFit];
}


답변

암호:

  extension UITableView {

          func sizeHeaderToFit(preferredWidth: CGFloat) {
            guard let headerView = self.tableHeaderView else {
              return
            }

            headerView.translatesAutoresizingMaskIntoConstraints = false
            let layout = NSLayoutConstraint(
              item: headerView,
              attribute: .Width,
              relatedBy: .Equal,
              toItem: nil,
              attribute:
              .NotAnAttribute,
              multiplier: 1,
              constant: preferredWidth)

            headerView.addConstraint(layout)

            let height = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
            headerView.frame = CGRectMake(0, 0, preferredWidth, height)

            headerView.removeConstraint(layout)
            headerView.translatesAutoresizingMaskIntoConstraints = true

            self.tableHeaderView = headerView
          }
  }


답변

이 솔루션 http://collindonnell.com/2015/09/29/dynamically-sized-table-view-header-or-footer-using-auto-layout/ 을 테이블 바닥 글보기로 확장했습니다 .

@interface AutolayoutTableView : UITableView

@end

@implementation AutolayoutTableView

- (void)layoutSubviews {
    [super layoutSubviews];

    // Dynamic sizing for the header view
    if (self.tableHeaderView) {
        CGFloat height = [self.tableHeaderView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
        CGRect headerFrame = self.tableHeaderView.frame;

        // If we don't have this check, viewDidLayoutSubviews() will get
        // repeatedly, causing the app to hang.
        if (height != headerFrame.size.height) {
            headerFrame.size.height = height;
            self.tableHeaderView.frame = headerFrame;
            self.tableHeaderView = self.tableHeaderView;
        }

        [self.tableHeaderView layoutIfNeeded];
    }

    // Dynamic sizing for the footer view
    if (self.tableFooterView) {
        CGFloat height = [self.tableFooterView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
        CGRect footerFrame = self.tableFooterView.frame;

        // If we don't have this check, viewDidLayoutSubviews() will get
        // repeatedly, causing the app to hang.
        if (height != footerFrame.size.height) {
            footerFrame.size.height = height;
            self.tableFooterView.frame = footerFrame;
            self.tableFooterView = self.tableFooterView;
        }

        self.tableFooterView.transform = CGAffineTransformMakeTranslation(0, self.contentSize.height - footerFrame.size.height);
        [self.tableFooterView layoutIfNeeded];
    }
}

@end


답변

systemLayoutSizeFittingSize 메서드 를 사용하여 크기를 제공하는 자동 레이아웃을 얻을 수 있습니다 .

그런 다음이를 사용하여 응용 프로그램의 프레임을 만들 수 있습니다. 이 기술은 내부적으로 자동 레이아웃을 사용하는 뷰의 크기를 알아야 할 때마다 작동합니다.

신속한 코드는 다음과 같습니다.

//Create the view
let tableHeaderView = CustomTableHeaderView()

//Set the content
tableHeaderView.textLabel.text = @"Hello world"

//Ask auto layout for the smallest size that fits my constraints    
let size = tableHeaderView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)

//Create a frame    
tableHeaderView.frame = CGRect(origin: CGPoint.zeroPoint, size: size)

//Set the view as the header    
self.tableView.tableHeaderView = self.tableHeaderView

또는 Objective-C에서

//Create the view
CustomTableHeaderView *header = [[CustomTableHeaderView alloc] initWithFrame:CGRectZero];

//Set the content
header.textLabel.text = @"Hello world";

//Ask auto layout for the smallest size that fits my constraints
CGSize size = [header systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];

//Create a frame
header.frame = CGRectMake(0,0,size.width,size.height);

//Set the view as the header  
self.tableView.tableHeaderView = header

이 특정 인스턴스에서 하위 클래스에서 requiresConstraintBasedLayout을 재정의하면 레이아웃 패스가 수행되지만이 레이아웃 패스의 결과는 무시되고 시스템 프레임이 tableView의 너비와 높이 0으로 설정됩니다.