Чи можна використовувати AutoLayout з таблицеюHeaderView UITableView?


97

Оскільки я виявив, AutoLayoutщо використовую його скрізь, зараз намагаюся використовувати з tableHeaderView.

Я зробив subclassз UIViewдоданих все (етикеток і т.д. ...) Я хотів з їх обмеженнями, то я додав це CustomViewдо UITableView' tableHeaderView.

Все працює відмінно , за винятком UITableViewзавжди відображається вищеCustomView , по вище я маю на увазі CustomViewце підUITableView тому він не може бути видно!

Здається , що незалежно від того , що я роблю, то heightз UITableView' tableHeaderViewє завжди 0 (так ширина х і у).

Моє запитання: чи можливо взагалі це зробити, не встановлюючи кадр вручну ?

EDIT:CustomView ' , subviewщо я використовую має наступні обмеження:

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

Я не знаю, чи потрібно встановлювати деякі обмеження безпосередньо на CustomView, думаю, висота CustomView визначається обмеженнями на UILabel"заголовок" у ньому.

РЕДАГУВАТИ 2: Після чергового дослідження здається, що висота та ширина CustomView правильно розраховані, але верхня частина CustomView все ще знаходиться на тому ж рівні, що і верхня частина UITableView, і вони рухаються разом, коли я прокручую.


Так, це можливо. Чи можете ви показати код, який ви використовуєте? Важко порадити, не знаючи, які обмеження ви встановили для подання заголовка.
jrturton

Простий спосіб досягти цього - додати цей вигляд у IB до tableView .. просто створіть подання в тій самій сцені, що містить табличний вигляд, і перетягніть його до таблиці.
Маріам К.

Я намагаюся уникати ІБ якнайбільше, поки що мені не довелося ним користуватися, якщо я не зможу змусити його працювати, я спробую з ІБ
ItsASecret

1
Apple радить розробникам по можливості використовувати IB, коли мова йде про авторозкладку. Це дійсно допомагає уникнути багатьох проблем з невідповідністю.
Маріам К.

Істинне повне рішення autolayout знаходиться тут
Malex

Відповіді:


134

Я запитав і відповів на подібне запитання тут . Підсумовуючи, я додаю заголовок один раз і використовую його, щоб знайти необхідну висоту. Потім цю висоту можна застосувати до заголовка, і заголовок встановлюється вдруге, щоб відобразити зміну.

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

Якщо у вас є багаторядкові мітки, це також покладається на спеціальний режим перегляду, що надає перевагу MaxLayoutWidth кожної мітки:

- (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 року

На жаль, це все ще здається необхідним. Ось швидка версія процесу верстки:

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)

8
Альтернативою використанню preferredMaxLayoutWidthє додавання обмеження ширини (що дорівнює ширині подання таблиці) у вигляді заголовка перед використанням systemLayoutSizeFittingSize:.
Benjohn

2
ПРИМІТКА: якщо ви відчуваєте, що заголовок знаходиться над першими клітинками, ви забули скинути властивість заголовка наself.tableView.tableHeaderView
Ласло

7
Часто мене дивує, наскільки складним може бути щось подібне до дрібниць.
TylerJames

5
ПРИМІТКА: Якщо вам потрібно отримати точну ширину як tableView, ви повинні отримати висоту з необхідним горизонтальним пріоритетомlet height = header.systemLayoutSizeFittingSize(CGSizeMake(CGRectGetWidth(self.bounds), 0), withHorizontalFittingPriority: UILayoutPriorityRequired, verticalFittingPriority: UILayoutPriorityFittingSizeLevel).height
JakubKnejzlik

3
Просто використання header.setNeedsLayout() header.layoutIfNeeded() header.frame.size = header.systemLayoutSizeFitting(UILayoutFittingCompressedSize) self.tableHeaderView = headerбуде працювати в iOS 10.2
Kesong Xie

24

Мені не вдалося додати вид заголовка за допомогою обмежень (у коді). Якщо я надаю своєму погляду обмеження ширини та / або висоти, я отримую збій з повідомленням:

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

Коли я додаю подання в розкадровці до подання таблиці, воно не відображає жодних обмежень, і воно прекрасно працює як подання заголовка, тому я думаю, що розміщення подання заголовка не здійснюється за допомогою обмежень. Здається, це не поводиться як звичайний погляд у цьому відношенні.

Ширина - це автоматично ширина подання таблиці, єдине, що вам потрібно встановити, це висота - значення початкових даних ігноруються, тому не має значення, що ви для них вказали. Наприклад, це спрацювало нормально (як і 0,0,0,80 для прямої):

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

У мене було таке виключення теж , але додавши категорію UITableView встановив його , я знайшов його в цій відповіді: stackoverflow.com/questions/12610783 / ...
ItsASecret

Я все ще спробую те, що ви пропонуєте, але завтра вранці, зараз о 1:34 ранку, я лягаю спати, велике спасибі, що знайшли час відповісти! (Але я дійсно хочу не вказувати висоту, я хотів би, щоб вона обчислювалася за обмеженнями, які я встановив на етикетці в CustomView)
ItsASecret

Я спробував, і так, встановлення кадру працює, але я шукав спосіб уникнути встановлення кадру, я буду продовжувати шукати, і якщо не знайду нічого іншого, прийму вашу відповідь
ItsASecret

1
Я отримую цей виняток (наразі тестування 7.1), якщо доданий поданий заголовок має translatesAutoresizingMaskIntoConstraints = NO. Увімкнення перекладу запобігає помилці - я підозрюю UITableView, що з 7.1 не намагається автоматично розкласти свій вид заголовка і хоче щось із попередньо встановленим фреймом.
Benjohn

16

Я бачив тут багато методів, які роблять стільки непотрібних речей, але вам не потрібно так багато, щоб використовувати автоматичне розміщення у поданні заголовка. Вам просто потрібно створити файл 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
    }

Це також працювало для нас на iOS 11 із заголовком динамічної висоти з багаторядковими мітками.
Бен Шайрман,

1
Звичайно, ви також можете видалити опцію flexHeight-Autoresizing-Option в IB.
d4Rk

Я намагався встановити висоту мого tableFooterView (через xib / nib) і не мав успіху встановити фрейм, висоту, layoutIfNeeded () тощо. Але це рішення врешті дозволило мені встановити його.
vikzilla

Не забудьте встановити обмеження висоти для всього подання у файлі xib.
Денис Кутлубаєв

6

Іншим рішенням є відправлення створення подання заголовка до наступного виклику основного потоку:

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

1
@TussLaszlo tableHeaderView- це щось на кшталт баггі з авторозкладкою. Є кілька обхідних шляхів, як цей. Але так як я писав це, я знайшов найкращий і чистий розчин тут stackoverflow.com/a/21099430/127493 викликаючи його - (void)sizeHeaderToFitінviewDidLayoutSubviews
Мартін

4

Код:

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

Це працює. Надайте належні обмеження авторозкладки для всіх підзаголовків таблиці. Якщо ви пропустите єдине обмеження, воно не спрацює.
абхімуралідаран

4

Розширив це рішення 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

Провів день учора, намагаючись змусити tableHeader автоматично змінити розмір / макет правильно. Це рішення працює для мене. Дякую купу.
docchang

Привіт! Не могли б ви пояснити self.tableFooterView.transformчастину? Чому це потрібно?
mrvn

Перетворення @mrvn використовується для переміщення нижнього колонтитула в нижню частину tableView.
k06a

3

Ви можете отримати авторозкладку, щоб надати вам розмір, використовуючи метод 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

Слід також зазначити, що в цьому конкретному випадку перевизначення requireConstraintBasedLayout у вашому підкласі призводить до того, що виконується пропуск макета, однак результати цього проходу макету ігноруються, а системний фрейм встановлюється на ширину tableView та 0 висоту.


3

У мене працювало наступне.

  1. Використовуйте звичайний старий UIViewяк вид заголовка.
  2. Додайте до цього підпрограми UIView
  3. Використовуйте авторозкладку в підпрограмах

Основною перевагою, яку я бачу, є обмеження кадрових розрахунків. Apple дійсно повинна оновитисьUITableView API, щоб зробити це простішим.

Приклад використання SnapKit:

let layoutView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.bounds.width, height: 60))
layoutView.backgroundColor = tableView.backgroundColor
tableView.tableHeaderView = layoutView

let label = UILabel()
layoutView.addSubview(label)
label.text = "I'm the view you really care about"
label.snp_makeConstraints { make in
    make.edges.equalTo(EdgeInsets(top: 10, left: 15, bottom: -5, right: -15))
}

3

Бувають дивні речі. systemLayoutSizeFittingSize чудово працює для iOS9, але в моєму випадку для iOS 8 не працює. Тож цю проблему вирішується досить просто. Просто отримайте посилання на вигляд знизу в заголовку та viewDidLayoutSubviews після границь подання заголовка супервиклику, вставивши висоту як CGRectGetMaxY (yourview.frame) + відступ

UPD: Найпростіше рішення коли-небудь : Отже, у поданні заголовка розмістіть підзапис і закріпіть його ліворуч , праворуч , зверху . У цьому підпрограмі розмістіть свої підпрограми з обмеженнями автоматичної висоти. Після цього віддайте всю роботу в авторозкладку (розрахунок не потрібен)

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    CGFloat height = CGRectGetMaxY(self.tableView.tableHeaderView.subviews.firstObject.frame);
    self.tableView.tableHeaderView.bounds = CGRectMake(0, 0, CGRectGetWidth(self.tableView.bounds), height);
    self.tableView.tableHeaderView = self.tableView.tableHeaderView;
}

В результаті підзапис розширюється / скорочується, як і слід, в кінці він викликає viewDidLayoutSubviews. На той момент ми знаємо фактичний розмір подання, тому встановіть висоту headerView та оновіть його, повторно призначивши. Працює як шарм!

Також працює для подання нижнього колонтитула.


1
Це повторюється для мене в iOS 10.
Саймон,

3

Оновлено для Swift 4.2

extension UITableView {

    var autolayoutTableViewHeader: UIView? {
        set {
            self.tableHeaderView = newValue
            guard let header = newValue else { return }
            header.setNeedsLayout()
            header.layoutIfNeeded()
            header.frame.size = 
            header.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
            self.tableHeaderView = header
        }
        get {
            return self.tableHeaderView
        }
    }
}

2

Ви можете додати обмеження верхнього та горизонтального розташування між заголовком та видом таблиці, щоб правильно розмістити його (якщо сам заголовок містить усі необхідні внутрішні обмеження макета, щоб мати правильний кадр)

у методі tableViewController viewDidLoad

    headerView.translatesAutoresizingMaskIntoConstraints = false

    tableView.tableHeaderView = headerView

    headerView.widthAnchor.constraint(equalTo: tableView.widthAnchor).isActive = true
    headerView.topAnchor.constraint(equalTo: tableView.topAnchor).isActive = true
    headerView.centerXAnchor.constraint(equalTo: tableView.centerXAnchor).isActive = true

1

Моє подання заголовка таблиці - це підклас UIView - я створив єдиний contentView UIView в рамках ініціалізатора, його межі однакові з рамкою подання заголовка таблиці та додав усі мої об’єкти як підпогляд цього.

Потім додайте обмеження для своїх об’єктів у layoutSubviewsметод подання заголовка таблиці, а не в ініціалізатор. Це вирішило аварію.

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:CGRectMake(0, 0, 0, 44.0)];
    if (self) {
        UIView *contentView = [[UIView alloc] initWithFrame:self.bounds];
        contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth;

        // add other objects as subviews of content view

    }
    return self;
}

- (void)layoutSubviews
{
    [super layoutSubviews];

    // remake constraints here
}

1

Мій AutoLayout працює дуже добре:

CGSize headerSize = [headerView systemLayoutSizeFittingSize:CGSizeMake(CGRectGetWidth([UIScreen mainScreen].bounds), 0) withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:UILayoutPriorityFittingSizeLevel];
headerView.frame = CGRectMake(0, 0, headerSize.width, headerSize.height);
self.tableView.tableHeaderView = headerView;

Я зробив це не точно, але ви дали мені гарну ідею - видалити headerView, змінити його рамку та додати назад.
dinesharjani

1

У більшості випадків найкращим рішенням є просто не боротися з фреймворком і не застосовувати авторозмірні маски:

// embrace autoresizing masks and let the framework add the constraints for you
headerView.translatesAutoresizingMaskIntoConstraints = true
headerView.autoresizingMask = [.flexibleWidth, .flexibleHeight]

// figure out what's the best size based on the table view width
let width = self.tableView.frame.width
let targetSize = headerView.systemLayoutSizeFitting(CGSize(width: width, height: CGFloat.greatestFiniteMagnitude), withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)
headerView.frame.size = targetSize
self.tableView.tableHeaderView = headerView

Використовуючи авторозмірні маски, ви розповідаєте фреймворку про те, як ваш вигляд повинен змінювати свій розмір, коли суперперегляд змінює свій розмір. Але ця зміна базується на вихідному кадрі, який ви встановили.


0

Я знаю, що це стара публікація, але, пройшовши всі публікації щодо цього і пройшовши цілий день, граючись із цим, я, нарешті, придумав чисте, але дуже просте рішення

Перш за все, ієрархія My view виглядає так:

  1. Перегляд таблиці
    1. Вид tableHeaderView
      1. Вид з розеткою, яка називається headerView

Тепер всередині подання (№ 3) я встановлюю всі обмеження, як зазвичай, включаючи нижній простір для контейнера. Це зробить контейнер (тобто 3.View, тобто headerView) самостійно розміром, виходячи з його підпоглядів та їх обмежень.

Після цього я встановив обмеження між 3. Viewі 2. Viewдля цих:

  1. Верхній простір для контейнера: 0
  2. Простір до контейнера: 0
  3. Кінцевий простір до контейнера: 0

Зверніть увагу, що я навмисно опускаю нижній простір навмисно.

Як тільки все це буде зроблено в раскадровці, все, що залишилося зробити, це вставити ці три рядки кодів:

if (self.headerView.frame.size.height != self.tableView.tableHeaderView.frame.size.height) {
    UIView *header = self.tableView.tableHeaderView;
    CGRect frame = self.tableView.tableHeaderView.frame;
    frame.size.height = self.headerView.frame.size.height + frame.origin.y;
    header.frame = frame;
    self.tableView.tableHeaderView = header;
}

0

Поради: Якщо ви використовуєте метод setAndLayoutTableHeaderView, вам слід оновити фрейм подань, тому в цій ситуації UILabel's preferMaxLayoutWidth повинен викликати перед викликом systemLayoutSizeFittingSize, не викликати в layoutSubview.

кодове шоу


0

Поділіться моїм підходом.

UITableView+XXXAdditions.m

- (void)xxx_setTableHeaderView:(UIView *)tableHeaderView layoutBlock:(void(^)(__kindof UIView *tableHeaderView, CGFloat *containerViewHeight))layoutBlock {
      CGFloat containerViewHeight = 0;
      UIView *backgroundView = [[UIView alloc] initWithFrame:CGRectZero];
      [backgroundView addSubview:tableHeaderView];
      layoutBlock(tableHeaderView, &containerViewHeight);

      backgroundView.frame = CGRectMake(0, 0, 0, containerViewHeight);

      self.tableHeaderView = backgroundView;
}

Використання.

[self.tableView xxx_setTableHeaderView:myView layoutBlock:^(__kindof UIView * _Nonnull tableHeaderView, CGFloat *containerViewHeight) {
    *containerViewHeight = 170;

    [tableHeaderView mas_makeConstraints:^(MASConstraintMaker *make) {
      make.top.equalTo(@20);
      make.centerX.equalTo(@0);
      make.size.mas_equalTo(CGSizeMake(130, 130));
    }];
  }];

0

У моєму випадку метод з systemLayoutSizeFittingSize з якихось причин не спрацював. У мене спрацювала модифікація рішення, опублікована HotJard (він оригінальне рішення також не працювало в моєму випадку на iOS 8). Що мені потрібно було зробити, це у поданні заголовка розмістити підпрогляд і закріпити його ліворуч, праворуч, зверху (не закріплювати внизу). Помістіть все, що використовує авторозкладку, у цьому підпрогляді, а в коді зробіть так:

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    [self resizeHeaderToFitSubview];
}

- (void)resizeHeaderToFitSubview
{
    UIView *header = self.tableView.tableHeaderView;
    [header setNeedsLayout];
    [header layoutIfNeeded];
    CGFloat height = CGRectGetHeight(header.subviews.firstObject.bounds);
    header.bounds = CGRectMake(0, 0, CGRectGetWidth(self.tableView.bounds), height);
    self.tableView.tableHeaderView = nil;
    self.tableView.tableHeaderView = header;
}

0

Старий пост. Але хороший пост. Ось мої 2 центи.

По-перше, переконайтеся, що ваш заголовок має обмеження, влаштовані так, щоб він міг підтримувати власний розмір власного вмісту. Потім виконайте наступне.

//ViewDidLoad
headerView.translatesAutoresizingMaskIntoConstraints = false
headerView.configure(title: "Some Text A")

//Somewhere else
headerView.update(title: "Some Text B)

private var widthConstrained = false

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    if widthConstrained == false {
        widthConstrained = true
        tableView.addConstraint(NSLayoutConstraint(item: headerView, attribute: .width, relatedBy: .equal, toItem: tableView, attribute: .width, multiplier: 1, constant: 0))
        headerView.layoutIfNeeded()
        tableView.layoutIfNeeded()
    }
}

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    coordinator.animate(alongsideTransition: { (context) in
        self.headerView.layoutIfNeeded()
        self.tableView.layoutIfNeeded()
    }, completion: nil)
}

0

Я зміг досягти цього за допомогою наступного підходу (це працює і для нижнього колонтитула так само).

По-перше, вам знадобляться маленькі UITableView розширення:

Стрімкий 3

extension UITableView {
    fileprivate func adjustHeaderHeight() {
        if let header = self.tableHeaderView {
            adjustFrame(header)
        }
    }

    private func adjustFrame(_ view: UIView) {
        view.frame.size.height = calculatedViewHeight(view)
    }

    fileprivate func calculatedHeightForHeader() -> CGFloat {
        if let header = self.tableHeaderView {
            return calculatedViewHeight(header)
        }
        return 0.0
    }

    private func calculatedViewHeight(_ view: UIView) -> CGFloat {
        view.setNeedsLayout()
        let height = view.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
        return height
    }
}

У реалізації класу контролера подання:

// this is a UIView subclass with autolayout
private var headerView = MyHeaderView()

override func loadView() {
    super.loadView()
    // ...
    self.tableView.tableHeaderView = headerView
    self.tableView.sectionHeaderHeight = UITableViewAutomaticDimension
    // ...
}

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()

    // this is to prevent recursive layout calls
    let requiredHeaderHeight = self.tableView.calculatedHeightForHeader()
    if self.headerView.frame.height != requiredHeaderHeight {
        self.tableView.adjustHeaderHeight()
    }
}

Примітки щодо UIViewреалізації підзаголовка заголовка :

  1. Ви повинні бути на 100% впевнені, що ваш вид заголовка має правильну настройку автоматичного розкладання. Я б порадив почати з простого подання заголовка лише з одним обмеженням висоти та спробувати налаштування вище.

  2. Замінити requiresConstraintBasedLayoutі повернути true:

.

class MyHeaderView: UIView {
   // ...
   override static var requiresConstraintBasedLayout : Bool {
       return true
   }
   // ...
}

0

Для користувачів Xamarin:

public override void ViewDidLayoutSubviews()
{
    base.ViewDidLayoutSubviews();

    TableviewHeader.SetNeedsLayout();
    TableviewHeader.LayoutIfNeeded();

    var height = TableviewHeader.SystemLayoutSizeFittingSize(UIView.UILayoutFittingCompressedSize).Height;
    var frame = TableviewHeader.Frame;
    frame.Height = height;
    TableviewHeader.Frame = frame;
}

Припускаючи, що ви назвали вид заголовка вашого перегляду таблиці як TableviewHeader


0

Ось як ви можете зробити у своєму UIViewController

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    if headerView.frame.size.height == 0 {
      headerView.label.preferredMaxLayoutWidth = view.bounds.size.width - 20
      let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height

      headerView.frame.size = CGSize(width: tableView.bounds.size.width, height: height)
    }
  }

0

Будь-яке засноване на обмеженнях UIViewможе бути хорошим tableHeaderView.

Потрібно встановити tableFooterViewперед, а потім накласти додаткове обмеження на відставання на tableFooterViewта tableHeaderView.

- (void)viewDidLoad {

    ........................
    // let self.headerView is some constraint-based UIView
    self.tableView.tableFooterView = [UIView new];
    [self.headerView layoutIfNeeded];
    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;
    [self.tableFooterView.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor].active = YES;

}

Можна знайти всі деталі і фрагменти коду тут


0

Я придумав обхідний шлях. оберніть свій заголовок автозапуску wrriten xib у порожню обгортку uiview і призначте вид заголовка властивості tableViewHeader tableView.

    UIView *headerWrapper = [[UIView alloc] init];
    AXLHomeDriverHeaderView *headerView = [AXLHomeDriverHeaderView loadViewFromNib];
    [headerWrapper addSubview:headerView];
    [headerView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(headerWrapper);
    }];
    self.tableView.tableHeaderView = headerView;

0

Ось що працює для UITableViewController в iOS 12,

Накресліть UIView в TableView над усіма комірками прототипу для верхнього колонтитула і нижче всіх комірок прототипу для нижнього колонтитула. Налаштуйте верхній і нижній колонтитули за необхідності. Встановіть усі необхідні обмеження.

Тепер використовуйте такі методи розширення

public static class UITableVIewExtensions
{

    public static void MakeHeaderAutoDimension(this UITableView tableView)
    {
        if (tableView.TableHeaderView is UIView headerView) {
            var size = headerView.SystemLayoutSizeFittingSize(UIView.UILayoutFittingCompressedSize);
            if (headerView.Frame.Size.Height != size.Height) {
                var frame = headerView.Frame;
                frame.Height = size.Height;
                headerView.Frame = frame;
                tableView.TableHeaderView = headerView;
                tableView.LayoutIfNeeded();
            }
        }
    }

    public static void MakeFooterAutoDimension(this UITableView tableView)
    {
        if (tableView.TableFooterView is UIView footerView) {
            var size = footerView.SystemLayoutSizeFittingSize(UIView.UILayoutFittingCompressedSize);
            if (footerView.Frame.Size.Height != size.Height) {
                var frame = footerView.Frame;
                frame.Height = size.Height;
                footerView.Frame = frame;
                tableView.TableFooterView = footerView;
                tableView.LayoutIfNeeded();
            }
        }
    }
}

і викликати його в ViewDidLayoutSubviews підкласу UITableViewController

public override void ViewDidLayoutSubviews()
{
    base.ViewDidLayoutSubviews();

    TableView.MakeHeaderAutoDimension();
    TableView.MakeFooterAutoDimension();
}

0

Я зіткнувся з проблемою отримання ширини 375pt, єдиний спосіб, який працював для мене, це ретрансляція tableView, щоб отримати правильну ширину. Я також віддав перевагу AutoLayout перед встановленням розміру кадру.

Ось версія, яка працює для мене:

Xamarin.iOS

public static void AutoLayoutTableHeaderView(this UITableView tableView, UIView header)
{
    tableView.TableHeaderView = header;
    tableView.SetNeedsLayout();
    tableView.LayoutIfNeeded();
    header.WidthAnchor.ConstraintEqualTo(tableView.Bounds.Width).Active = true;       
    tableView.TableHeaderView = header;
}

Swift версія (змінено з відповіді @Ben Packard)

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
        self.setNeedsLayout()
        self.layoutIfNeeded()
        header.widthAnchor.widthAnchor.constraint(equalTo: self.bounds.width).isActive = true
        self.tableHeaderView = header
    }
}


0

Моє рішення - створити такий клас, як цей.

class BaseTableHeaderView: UIView {

    func sizeToFitBasedOnConstraints(width: CGFloat = Screen.width) {
        let size = systemLayoutSizeFitting(CGSize(width: width, height: 10000),
                                              withHorizontalFittingPriority: .required,
                                              verticalFittingPriority: .fittingSizeLevel)
        frame = CGRect(origin: .zero, size: size)
    }

    override func willMove(toSuperview newSuperview: UIView?) {
        sizeToFitBasedOnConstraints()
        super.willMove(toSuperview: newSuperview)
    }

}

Щоб скористатися ним, просто додайте всі свої подання до екземпляра BaseTableHeaderViewта приєднайте до подання таблиці.

let tableHeaderView = BaseTableHeaderView()
tableHeaderView.addSubview(...)
tableView.tableHeaderView = tableHeaderView

Він автоматично змінює розмір на основі своїх обмежень.


-1

Прийнята відповідь є корисною лише для таблиць з одним розділом. Для багатосекційного UITableViewпереконайтеся, що ваш заголовок успадковується від, UITableViewHeaderFooterViewі з вами все буде добре.

Як альтернативу, просто вставте свій поточний заголовок у contentViewфайл a UITableViewHeaderFooterView. Точно як UITableViewCellтвори.


9
питання стосується tableHeaderViewне заголовка розділу.
Рпраната
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.