Высота заголовка таблицы неверна при использовании автоматического макета, IB и размеров шрифтов

Я пытаюсь создать представление заголовка для моего uiTableView (не заголовок раздела, у меня уже есть). Я установил XIB в построителе интерфейса. Все соединения подключены и прекрасно работают... кроме стола не хватит места! Моя проблема в том, что верхняя часть таблицы немного перекрывает заголовок таблицы.

Мой XIB настроен с автозапуском для всех кнопок, и IB рада, что ограничения не конфликтуют/неоднозначны. В представлении установлен размер Freeform, который в моем случае оказался 320 x 471. Тогда в ограничениях для представления я установил собственный размер для представления того же самого.

Теперь это отлично работает с моей таблицей. Все выглядит великолепно. Но если я вручную изменю любой из шрифтов в представлении заголовка с кодом, макет сделает вид больше, и он окажется под моей таблицей.

Любые идеи о том, как заставить tableviewcontroller оставлять достаточно места для просмотра заголовка после установки шрифтов и размеров? Надеюсь, у меня есть смысл объяснить это.

Ответ 1

Идея состоит в том, чтобы рассчитать высоту заголовка с помощью systemLayoutSizeFittingSize:targetSize.

Возвращает размер представления, который удовлетворяет ограничениям, которые он имеет. Определяет наилучший размер представления с учетом всех ограничений и т.д. его подзонов.

После изменения высоты заголовка необходимо переназначить свойство tableHeaderView для настройки ячеек таблицы.

На основе этого ответа: Использование автоматической компоновки в UITableView для динамических раскладок ячеек и высоты строк в строке

- (void)sizeHeaderToFit
{
    UIView *header = self.tableView.tableHeaderView;

    [header setNeedsLayout];
    [header layoutIfNeeded];

    CGFloat height = [header systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
    CGRect frame = header.frame;

    frame.size.height = height;
    header.frame = frame;

    self.tableView.tableHeaderView = header;
}

Ответ 2

У меня возникла аналогичная проблема, когда я пытался установить заголовок таблицы в пользовательский вид, который я определил с помощью автоматического макета, используя конструктор интерфейса.

Когда я это сделал, я обнаружил, что он будет закрыт заголовком раздела.

Моя работа включала использование "фиктивного представления" в качестве представления заголовка таблицы, а затем добавление моего настраиваемого представления к этому фиктивному представлению в качестве подчиненного. Я думаю, что это позволяет авто макету настраивать внешний вид в соответствии с ограничениями и содержащим рамкой представления. Это, вероятно, не так элегантно, как решение vokilam, но оно работает для меня.


CGRect headerFrame = CGRectMake(0, 0, yourWidth, yourHeight);
UIView *tempView = [[UIView alloc] initWithFrame:headerFrame];
[tempView setBackgroundColor:[UIColor clearColor]];
YourCustomView *customView = [[YourCustomView alloc] initWithFrame: headerFrame];
[tempView addSubview:movieHeader];
self.tableView.tableHeaderView = tempView;

Ответ 3

Поскольку ваш tableHeaderView представляет собой введенную форму xib с автоматическим макетом, поэтому ограничения ваших пользовательских headerView и superView неизвестны. Вы должны добавить constraints своего пользовательского headerView:

1.in viewDidLLoad назначьте свой пользовательский заголовочный файл tableView tableHeaderView

 - (void)viewDidLoad
{
    [super viewDidLoad];
    UIView *yourHeaderView = [[[NSBundle mainBundle] loadNibNamed:@"yourHeaderView" owner:nil options:nil] objectAtIndex:0];
    //important:turn off the translatesAutoresizingMaskIntoConstraints;apple documents for details
     yourHeaderView.translatesAutoresizingMaskIntoConstraints = NO;
    self.tableView.tableHeaderView = yourHeaderView;
}

2.in - (void) updateViewConstraints, добавьте существенные ограничения вашего настраиваемого headerView

- (void)updateViewConstraints
{
    NSDictionary *viewsDictionary = @{@"headerView":yourHeaderView};

    NSArray *constraint_V = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[headerView(121)]"
                                                                    options:0
                                                                    metrics:nil
                                                                      views:viewsDictionary];

    NSArray *constraint_H = [NSLayoutConstraint constraintsWithVisualFormat:@"H:[headerView(320)]"
                                                                    options:0
                                                                    metrics:nil
                                                                      views:viewsDictionary];
    [self.headerView addConstraints:constraint_H];
    [self.headerView addConstraints:constraint_V];

    NSArray *constraint_POS_V = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[headerView]"
                                                                    options:0
                                                                    metrics:nil
                                                                      views:viewsDictionary];

    NSArray *constraint_POS_H = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[headerView]"
                                                                    options:0
                                                                    metrics:nil
                                                                      views:viewsDictionary];
    [self.tableView addConstraints:constraint_POS_V];
    [self.tableView addConstraints:constraint_POS_H];

    [super updateViewConstraints];
}

OK! PS: Вот связанный документ: Изменение размеров представлений диспетчера просмотров

Ответ 4

Что я решил решить эту проблему:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    sizeHeaderToFit()
}

private func sizeHeaderToFit() { 
    if let headerView = tableView.tableHeaderView {

        headerView.setNeedsLayout()
        headerView.layoutIfNeeded()

        let height = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
        var newFrame = headerView.frame

        // Needed or we will stay in viewDidLayoutSubviews() forever
        if height != newFrame.size.height {
            newFrame.size.height = height
            headerView.frame = newFrame

            tableView.tableHeaderView = headerView
        }
    }
}

Я нашел это решение где-то и работал как шарм, я не могу вспомнить, где.

Ответ 5

Я нашел вокилама, чтобы работать отлично. Здесь его решение в Свифт.

func sizeHeaderToFit() {
    guard let header = tableView.tableHeaderView else { return }
    header.setNeedsLayout()
    header.layoutIfNeeded()
    let height = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
    header.frame.size.height = height
    tableView.tableHeaderView = header
}

Ответ 6

В моем случае systemLayoutSizeFittingSize возвращает неправильный размер, потому что в одном из подзонов используется соотношение сторон (ширина к высоте) 4: 1.

После того, как я изменил его на значение ограничения константы (100), он начнет работать как ожидалось.

Ответ 7

Объединение ответов с помощью @SwiftArchitect, @vokilam и @mayqiyue и использование программно созданного tableHeaderView вместо nib (хотя, я не вижу причин, почему он не должен работать с nib), и под iOS 10.x/Xcode 8.2.1, вот что я работаю:

Создайте представление в вашем контроллере представления -viewDidLoad или - init или везде, где вы используете представление таблицы:

MyCustomTableHeaderView *myCustomTableHeaderView = [[MyCustomTableHeaderView alloc] init]; // Don't set its frame
myCustomTableHeaderView.translatesAutoresizingMaskIntoConstraints = NO;
self.myTableView.tableHeaderView = myCustomTableHeaderView;
self.myCustomTableHeaderView = myCustomTableHeaderView;  // We set and update our constraints in separate methods so we store the table header view in a UIView property to access it later

В тех случаях, когда вы настраиваете свои собственные ограничения вида заголовка (может быть в `updateConstraints, но этот метод можно вызвать несколько раз):

// We use Pure Layout in our project, but the workflow is the same:
// Set width and height constraints on your custom view then pin it to the top and left of the table view
// Could also use VFL or NSLayoutConstraint methods
[self.myCustomTableHeaderView autoSetDimension:ALDimensionWidth toSize:CGRectGetWidth(self.superview.frame)]; // Width set to tableview width
[self.myCustomTableHeaderView autoSetDimension:ALDimensionHeight toSize:height]; // Whatever you want your height to be
[self.myCustomTableHeaderView autoPinEdgeToSuperviewEdge:ALEdgeTop];
[self.myCustomTableHeaderView autoPinEdgeToSuperviewEdge:ALEdgeLeft];

// This is important
[self.myCustomTableHeaderView layoutIfNeeded]; // We didn't need to call -setNeedsLayout or reassign our custom header view to the table view tableHeaderView property again, as was noted in other answers

Ответ 8

Другие ответы указали мне в правильном направлении для правильного изменения размера таблицыHeaderView. Тем не менее, я получал промежуток между заголовком и ячейками tableView. Это также может привести к тому, что ваши ячейки будут перекрываться с вашим заголовком, в зависимости от того, увеличивается или уменьшится заголовок.

Я пошел от этого:

 tableView.reloadData()
 let height = tableViewHeader.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
 tableView.tableHeaderView?.frame.size.height = height

Чтобы устранить пробел/перекрытие, мне пришлось только перемещать tableView.reloadData() после того, как я установил новую высоту, а не ранее.

Для этого:

 let height = tableViewHeader.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
 tableView.tableHeaderView?.frame.size.height = height
 tableView.reloadData()

Ответ 9

sizeHeaderToFit, предложенный @vokilam, не работал у меня.

Работает модифицированная версия @mayqiyue, вызывается во время viewDidLoad, проводной высоты, равной ширине в виде таблицы.

// Anchor the headerview, and set width equal to superview
NSDictionary *viewsDictionary = @{@"headerView":self.tableView.tableHeaderView,
                                  @"tableView":self.tableView};

[self.tableView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[headerView]"
                                                                       options:0
                                                                       metrics:nil
                                                                         views:viewsDictionary]];
[self.tableView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[headerView]"
                                                                       options:0
                                                                       metrics:nil
                                                                         views:viewsDictionary]];
[self.tableView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[headerView(==tableView)]"
                                                                       options:0
                                                                       metrics:nil
                                                                         views:viewsDictionary]];
[self.tableView.tableHeaderView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[headerView(121)]"
                                                                                       options:0
                                                                                       metrics:nil
                                                                                         views:viewsDictionary]];

Ответ 10

Ниже кодекс работал у меня: - Добавьте это в свой класс UIView -

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];

if (self) {
    self.frame = CGRectMake(0, 0, self.frame.size.width, [[self class] height]);
}

return self;

}

Ответ 11

Ответ на

@rmigneco работал у меня, поэтому вот его быстрая версия:

let headerFrame = CGRectMake(0, 0, 100, 1);
let tempView = UIView(frame: headerFrame);
tempView.backgroundColor = UIColor.clearColor()

let yourCustomView = CustomView(frame: headerFrame)
tempView.addSubview(yourCustomView)

self.tableView.tableHeaderView = tempView