È possibile utilizzare AutoLayout con tableHeaderView di UITableView?


97

Da quando ho scoperto di AutoLayoutusarlo ovunque, ora sto cercando di usarlo con un file tableHeaderView.

Ho fatto una subclassdelle UIViewaggiunto tutto (etichette, ecc ...) Ho voluto con i loro vincoli, poi ho aggiunto questo CustomViewal UITableView' tableHeaderView.

Tutto funziona bene, tranne i UITableViewsempre visualizzato sopra la CustomView, da sopra mi riferisco al CustomViewè sotto il UITableViewmodo che non può essere visto!

Sembra che non importa quello che faccio, il heightdel UITableView' tableHeaderViewè sempre 0 (così è la larghezza, xey).

La mia domanda: è possibile farlo senza impostare manualmente la cornice ?

EDIT: L CustomView' subviewche sto usando ha questi vincoli:

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

Sto usando una comoda libreria 'KeepLayout' perché la scrittura manuale dei vincoli richiede un'eternità e troppe righe per un singolo vincolo, ma i metodi si spiegano da soli.

E UITableViewha questi vincoli:

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

Non so se devo impostare alcuni vincoli direttamente sul CustomView, penso che l'altezza del CustomView sia determinato dai vincoli sul UILabel"titolo" in esso.

MODIFICA 2: Dopo un'altra indagine sembra che l'altezza e la larghezza di CustomView siano calcolate correttamente, ma la parte superiore di CustomView è ancora allo stesso livello della parte superiore di UITableView e si muovono insieme quando scorro.


Sì, è possibile. Puoi mostrare il codice che stai usando? È difficile consigliare senza sapere quali vincoli hai impostato nella visualizzazione dell'intestazione.
jrturton

Un modo semplice per ottenere questo risultato è aggiungere quella vista in IB a tableView ... basta creare la vista nella stessa scena contenente la tableview e trascinarla sulla tabella.
Mariam K.

Sto cercando di evitare l'IB il più possibile, finora non ho dovuto usarlo, se non riesco a farlo funzionare proverò con IB
ItsASecret

1
Apple consiglia agli sviluppatori di utilizzare IB quando possibile quando si tratta di layout automatico. Aiuta davvero a evitare molti problemi di incoerenza.
Mariam K.

La vera soluzione completa di layout automatico è qui
malex

Risposte:


134

Ho chiesto e risposto a una domanda simile qui . In sintesi, aggiungo una volta l'intestazione e la utilizzo per trovare l'altezza richiesta. Tale altezza può quindi essere applicata all'intestazione e l'intestazione viene impostata una seconda volta per riflettere la modifica.

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

Se hai etichette su più righe, questo si basa anche sulla visualizzazione personalizzata che imposta la PreferredMaxLayoutWidth di ciascuna etichetta:

- (void)layoutSubviews
{
    [super layoutSubviews];

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

o forse più in generale:

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

Aggiornamento gennaio 2015

Purtroppo questo sembra ancora necessario. Ecco una versione rapida del processo di layout:

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

Ho trovato utile spostarlo in un'estensione su 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
    }
}

Utilizzo:

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

8
Un'alternativa all'uso preferredMaxLayoutWidthè l'aggiunta di un vincolo di larghezza (uguale alla larghezza della vista tabella) nella vista intestazione prima dell'uso systemLayoutSizeFittingSize:.
Benjohn

2
NOTA: se riscontri che l'intestazione è sopra le prime celle, ti sei dimenticato di reimpostare la proprietà dell'intestazione suself.tableView.tableHeaderView
Laszlo

7
Spesso mi stupisce quanto possa essere complicato fare qualcosa di completamente banale come questo.
TylerJames

5
NOTA: se è necessario ottenere la larghezza esatta come tableView, è necessario ottenere l'altezza con la priorità orizzontale richiestalet height = header.systemLayoutSizeFittingSize(CGSizeMake(CGRectGetWidth(self.bounds), 0), withHorizontalFittingPriority: UILayoutPriorityRequired, verticalFittingPriority: UILayoutPriorityFittingSizeLevel).height
JakubKnejzlik

3
Just Use header.setNeedsLayout() header.layoutIfNeeded() header.frame.size = header.systemLayoutSizeFitting(UILayoutFittingCompressedSize) self.tableHeaderView = headerfunzionerebbe su iOS 10.2
Kesong Xie

24

Non sono stato in grado di aggiungere una visualizzazione dell'intestazione utilizzando i vincoli (nel codice). Se do alla mia vista un vincolo di larghezza e / o altezza, ottengo un arresto anomalo con il messaggio che dice:

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

Quando aggiungo una vista nello storyboard alla mia vista tabella, non mostra vincoli e funziona bene come vista di intestazione, quindi penso che il posizionamento della vista di intestazione non sia fatto utilizzando vincoli. Non sembra comportarsi come una visione normale a tale riguardo.

La larghezza è automaticamente la larghezza della vista tabella, l'unica cosa che devi impostare è l'altezza: i valori di origine vengono ignorati, quindi non importa cosa inserisci per quelli. Ad esempio, questo ha funzionato bene (come 0,0,0,80 per il rect):

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

Ho avuto tale eccezione troppo ma l'aggiunta di una categoria per UITableView riparato l'ho trovato in questa risposta: stackoverflow.com/questions/12610783/...
ItsASecret

Proverò ancora quello che suggerisci, ma domani mattina, sono le 1:34. Vado a letto, grazie mille per aver dedicato del tempo a rispondere! (Ma voglio davvero non specificare un'altezza, vorrei che fosse calcolata dai vincoli che ho impostato sull'etichetta nel CustomView)
ItsASecret

L'ho provato e sì l'impostazione della cornice funziona, ma stavo cercando un modo per evitare di impostare la cornice, continuerò a cercare e se non trovo nient'altro accetterò la tua risposta
ItsASecret

1
Ottengo questa eccezione (attualmente test 7.1) se la vista dell'intestazione aggiunta ha translatesAutoresizingMaskIntoConstraints = NO. L'attivazione della traduzione previene l'errore: sospetto che a UITableViewpartire dalla versione 7.1 non tenti di eseguire il layout automatico della sua visualizzazione dell'intestazione e desideri qualcosa con il frame preimpostato.
Benjohn

16

Ho visto molti metodi qui fare così tante cose non necessarie, ma non è necessario così tanto per usare il layout automatico nella vista dell'intestazione. Devi solo creare il tuo file xib, inserire i tuoi vincoli e istanziarlo in questo modo:

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
    }

Questo ha funzionato anche per noi su iOS 11 con un'intestazione di altezza dinamica con etichette multilinea.
Ben Scheirman

1
Ovviamente puoi anche rimuovere l'opzione flexibleHeight-Autoresizing in IB.
d4Rk

Ho cercato di impostare l'altezza del mio tableFooterView (tramite xib / nib) e non ho avuto successo nell'impostare frame, height, layoutIfNeeded (), ecc. Ma questa soluzione mi ha finalmente permesso di impostarlo.
vikzilla

Non dimenticare di impostare il vincolo di altezza per l'intera vista in un file xib.
Denis Kutlubaev,

6

Un'altra soluzione è inviare la creazione della vista dell'intestazione alla successiva chiamata del thread principale:

- (void)viewDidLoad {
    [super viewDidLoad];

    // ....

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

Nota: risolve il bug quando la vista caricata ha un'altezza fissa. Non ho provato quando l'altezza dell'intestazione dipende solo dal suo contenuto.

MODIFICARE :

È possibile trovare una soluzione più pulita a questo problema implementando questa funzione e richiamandolaviewDidLayoutSubviews

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    [self sizeHeaderToFit];
}

1
@TussLaszlo tableHeaderViewsono una specie di buggy con il layout automatico. Ci sono alcune soluzioni alternative, come questa. Ma da quando ho scritto questo, ho trovato una soluzione migliore e più pulita qui stackoverflow.com/a/21099430/127493 chiamando il suo - (void)sizeHeaderToFitinviewDidLayoutSubviews
Martin

4

Codice:

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

Funziona Assegna i vincoli di layout automatico appropriati a tutte le sottoview di tableheaderview. Se perdi un singolo vincolo, non funzionerà.
abhimuralidharan

4

Estesa questa soluzione http://collindonnell.com/2015/09/29/dynamically-sized-table-view-header-or-footer-using-auto-layout/ per la visualizzazione del piè di pagina della tabella:

@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

Ho trascorso un giorno ieri cercando di ottenere il ridimensionamento / layout automatico corretto di tableHeader. Questa soluzione funziona per me. Grazie mille.
docchang

Ciao! Potresti spiegare una self.tableFooterView.transformparte? Perché è necessario?
mrvn

La trasformazione @mrvn viene utilizzata per spostare il piè di pagina nella parte inferiore di tableView.
k06a

3

Puoi ottenere il layout automatico per fornirti una dimensione utilizzando il metodo systemLayoutSizeFittingSize .

È quindi possibile utilizzarlo per creare la cornice per la propria applicazione. Questa tecnica funziona ogni volta che è necessario conoscere la dimensione di una vista che utilizza internamente il layout automatico.

Il codice in swift sembra

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

O in 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

Va anche notato che in questa particolare istanza, l'override di requireConstraintBasedLayout nella tua sottoclasse comporta l'esecuzione di un passaggio di layout, tuttavia i risultati di questo passaggio di layout vengono ignorati e il frame di sistema impostato sulla larghezza di tableView e 0 di altezza.


3

Quanto segue ha funzionato per me.

  1. Usa un vecchio semplice UIViewcome visualizzazione dell'intestazione.
  2. Aggiungi sottoviste a quello UIView
  3. Usa il layout automatico nelle visualizzazioni secondarie

Il vantaggio principale che vedo è limitare i calcoli dei frame. Apple dovrebbe davvero aggiornare UITableViewl'API di per renderlo più semplice.

Esempio utilizzando 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

Succedono cose strane. systemLayoutSizeFittingSize funziona alla grande per iOS9, ma non per iOS 8 nel mio caso. Quindi questo problema si risolve abbastanza facilmente. Basta ottenere il collegamento alla vista inferiore nell'intestazione e in viewDidLayoutSubviews dopo i limiti della vista dell'intestazione dell'aggiornamento della super chiamata inserendo l'altezza come CGRectGetMaxY (yourview.frame) + padding

UPD: La soluzione più semplice in assoluto : quindi, nella vista dell'intestazione, posiziona la vista secondaria e fissala a sinistra , a destra , in alto . In quella sottoview posiziona le tue sottoview con vincoli di altezza automatica. Dopodiché dai tutto il lavoro all'autolayout (nessun calcolo richiesto)

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

Di conseguenza, la sottoview si espande / si restringe come dovrebbe, alla fine chiama viewDidLayoutSubviews. Al momento conosciamo la dimensione effettiva della vista, quindi imposta l'altezza di headerView e aggiornala riassegnando. Funziona come un fascino!

Funziona anche per la visualizzazione piè di pagina.


1
Questo loop per me in iOS 10.
Simon

3

Aggiornato per 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

puoi aggiungere il vincolo di posizione in alto + orizzontale tra l'intestazione e la vista tabella, per posizionarlo correttamente (se l'intestazione stessa contiene tutti i vincoli di layout interni necessari per avere una cornice corretta)

nel metodo 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

La mia vista dell'intestazione della tabella è una sottoclasse UIView: ho creato una singola UIView contentView all'interno dell'inizializzatore, con i suoi limiti uguali al frame della vista dell'intestazione della tabella e ho aggiunto tutti i miei oggetti come una sottoview di quella.

Quindi aggiungi i vincoli per i tuoi oggetti all'interno del layoutSubviewsmetodo della vista dell'intestazione della tabella piuttosto che all'interno dell'inizializzatore. Quello ha risolto l'incidente.

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

Il mio layout automatico funziona molto bene:

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;

Non l'ho fatto esattamente ma mi hai dato una buona idea: rimuovere headerView, reimpostare la sua cornice e aggiungerla di nuovo.
dinesharjani

1

Per la maggior parte dei casi la soluzione migliore è semplicemente non combattere il framework e abbracciare maschere di ridimensionamento automatico:

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

Usando le maschere di ridimensionamento automatico stai dicendo al framework come la tua vista dovrebbe cambiare le sue dimensioni quando la superview cambia le sue dimensioni. Ma questa modifica si basa sul frame iniziale che hai impostato.


0

So che questo è un vecchio post ma dopo aver esaminato tutti i post SO riguardanti questo e aver passato un intero pomeriggio a giocare con questo, ho finalmente trovato una soluzione pulita e tuttavia molto semplice

Prima di tutto, la mia gerarchia di visualizzazione è simile a questa:

  1. Visualizza come tabella
    1. Visualizza tableHeaderView
      1. Visualizza con una presa chiamata headerView

Ora all'interno della vista (n. 3), ho impostato tutti i vincoli come farei normalmente includendo lo spazio inferiore al contenitore. Questo farà sì che il contenitore (cioè 3.View cioè headerView) si ridimensioni in base alle sue sottoview e ai loro vincoli.

Dopodiché, ho impostato i vincoli tra 3. Viewe 2. Viewa questi:

  1. Spazio superiore al contenitore: 0
  2. Spazio iniziale al contenitore: 0
  3. Spazio finale nel contenitore: 0

Si noti che ometto intenzionalmente lo spazio inferiore intenzionalmente.

Una volta che tutto ciò è stato fatto nello storyboard, tutto ciò che resta da fare è incollare quelle tre righe di codici:

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

Suggerimenti: se si utilizza il metodo setAndLayoutTableHeaderView, è necessario aggiornare il frame delle visualizzazioni secondarie, quindi in questa situazione il preferredMaxLayoutWidth di UILabel dovrebbe chiamare prima che systemLayoutSizeFittingSize venga chiamato, non chiamare in layoutSubview.

spettacolo di codice


0

Condividi il mio approccio.

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

Utilizzo.

[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

Nel mio caso il metodo con systemLayoutSizeFittingSize per qualche motivo non ha funzionato. Ciò che ha funzionato per me è una modifica della soluzione pubblicata da HotJard (anche la sua soluzione originale non ha funzionato nel mio caso su iOS 8). Quello che dovevo fare è nella vista intestazione posizionare una vista secondaria e fissarla a sinistra, a destra, in alto (non bloccare in basso). Metti tutto usando il layout automatico in quella sottoview e nel codice fai questo:

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

Un vecchio post. Ma un buon post. Ecco i miei 2 centesimi.

In primo luogo, assicurati che la visualizzazione dell'intestazione abbia i suoi vincoli disposti in modo che possa supportare la propria dimensione del contenuto intrinseco. Quindi procedi come segue.

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

Sono stato in grado di ottenerlo con il seguente approccio (funziona allo stesso modo per il piè di pagina).

Innanzitutto, avrai bisogno di una piccola UITableViewestensione:

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

Nella tua implementazione della classe del controller di visualizzazione:

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

Note sull'implementazione UIViewdella sottoview di un'intestazione:

  1. Devi essere sicuro al 100% che la visualizzazione dell'intestazione abbia la corretta configurazione del layout automatico. Consiglierei di iniziare con una semplice visualizzazione dell'intestazione con un solo vincolo di altezza e provare la configurazione sopra.

  2. Ignora requiresConstraintBasedLayoute restituisci true:

.

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

0

Per gli utenti 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;
}

Supponendo che tu abbia chiamato la vista dell'intestazione della tua vista tabella come TableviewHeader


0

Ecco come puoi fare nel tuo 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

Qualsiasi basato su vincoli UIViewpuò essere un bene tableHeaderView.

È necessario impostare un tableFooterViewprima e quindi imporre un vincolo finale aggiuntivo su tableFooterViewe 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;

}

Si possono trovare tutti i dettagli e frammenti di codice qui


0

Ho trovato una soluzione alternativa. avvolgere la vista dell'intestazione xib wrriten del layout automatico in un wrapper uiview vuoto e assegnare la vista dell'intestazione alla proprietà tableViewHeader di 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

Ecco cosa funziona per UITableViewController in ios 12,

Trascina una UIView nel TableView sopra tutte le celle prototipo per l'intestazione e sotto tutte le celle prototipo per il piè di pagina. Imposta l'intestazione e il piè di pagina in base alle esigenze. Imposta tutti i vincoli richiesti.

Ora usa i seguenti metodi di estensione

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

e chiamalo in ViewDidLayoutSubviews della sottoclasse di UITableViewController

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

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

0

Ho riscontrato il problema di ottenere la larghezza di 375pt, l'unico modo che ha funzionato per me è stato il relayout del tableView per ottenere la larghezza corretta. Ho anche preferito AutoLayout rispetto all'impostazione della dimensione del frame.

Ecco la versione che funziona per me:

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

Versione Swift (modificata dalla risposta di @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

La mia soluzione è creare una nuova classe come questa.

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

}

Per usarlo, aggiungi semplicemente tutte le tue visualizzazioni secondarie su un'istanza di BaseTableHeaderViewe collegalo alla visualizzazione tabella.

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

Si ridimensionerà automaticamente in base ai suoi vincoli.


-1

La risposta accettata è utile solo per le tabelle con una singola sezione. Per la multi-sezione, UITableViewassicurati che la tua intestazione erediti daUITableViewHeaderFooterView e andrà tutto bene.

In alternativa, incorpora la tua intestazione corrente nel file contentViewdi un file UITableViewHeaderFooterView. Esattamente come UITableViewCellfunziona.


9
la domanda tableHeaderViewnon riguarda l'intestazione della sezione.
Rpranata
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.