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

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

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

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

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

11 ответов

Примечание: версию Swift 3+ можно найти здесь: https://gist.github.com/marcoarment/1105553afba6b4900c10


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

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

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

Основываясь на этом ответе: Использование Auto Layout в 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;
}

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

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

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


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;

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

1.in viewDidLLoad назначьте свой пользовательский headerView для tableHeaderView tableView

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

ХОРОШО! PS: вот соответствующий документ: изменение размера представлений контроллера представления

Что решило мою проблему, так это:

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

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

Я нашел ответ вокилама, чтобы отлично работать. Вот его решение в Swift.

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
}

Другие ответы указали мне правильное направление для правильного изменения размера tableHeaderView. Тем не менее, я получил разрыв между заголовком и ячейками 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()

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

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

Объединение ответов @SwiftArchitect, @vokilam и @mayqiyue и использование программно созданных tableHeaderView вместо пера (хотя я не вижу причин, по которым он не должен работать с пером), и под 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's 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's tableHeaderView property again, as was noted in other answers

Ответ @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

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

Ниже код работал для меня:- Добавьте это в свой класс 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;

}

Другие вопросы по тегам