Ist es möglich, AutoLayout mit tableHeaderView von UITableView zu verwenden?


96

Seit ich herausgefunden habe, dass AutoLayoutich es überall benutze, versuche ich es jetzt mit einem zu benutzen tableHeaderView.

Ich habe eine subclassvon UIViewhinzugefügt alles (Etiketten etc ...) ich mit ihren Zwängen wollte, dann habe ich diese CustomViewmit dem UITableView' tableHeaderView.

Alles funktioniert gut , außer den UITableViewimmer Displays über die CustomView, von oben Ich meine das CustomViewist unter dem UITableViewso ist es nicht zu sehen!

Es scheint , dass egal , was ich tue, die heightvon den UITableView' tableHeaderViewist immer 0 (so ist die Breite x und y).

Meine Frage: Ist das überhaupt möglich, ohne den Rahmen manuell einzustellen ?

EDIT: Das CustomView' subview, das ich benutze, hat diese Einschränkungen:

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

Ich verwende eine praktische Bibliothek 'KeepLayout', da das manuelle Schreiben von Einschränkungen ewig dauert und viel zu viele Zeilen für eine einzelne Einschränkung enthält, die Methoden jedoch selbsterklärend sind.

Und das UITableViewhat diese Einschränkungen:

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

Ich weiß nicht, ob ich einige Einschränkungen direkt CustomViewfür das festlegen muss. Ich denke, die Höhe der benutzerdefinierten Ansicht wird durch die Einschränkungen für den darin enthaltenen UILabel"Titel" bestimmt.

BEARBEITEN 2: Nach einer weiteren Untersuchung scheinen Höhe und Breite der benutzerdefinierten Ansicht korrekt berechnet zu sein, aber die Oberseite der benutzerdefinierten Ansicht befindet sich immer noch auf der gleichen Ebene wie die Oberseite der UITableView und sie bewegen sich beim Scrollen zusammen.


Ja, es ist möglich. Können Sie den von Ihnen verwendeten Code anzeigen? Es ist schwierig zu beraten, ohne zu wissen, welche Einschränkungen Sie in der Kopfzeilenansicht festgelegt haben.
Jrturton

Eine einfache Möglichkeit, dies zu erreichen, besteht darin, diese Ansicht in IB zur Tabellenansicht hinzuzufügen. Erstellen Sie einfach die Ansicht in derselben Szene, die die Tabellenansicht enthält, und ziehen Sie sie in die Tabelle.
Mariam K.

Ich versuche, IB so weit wie möglich zu vermeiden. Bisher musste ich es nicht verwenden. Wenn ich es nicht zum
Laufen

1
Apple empfiehlt Entwicklern, IB beim Autolayout nach Möglichkeit zu verwenden. Es hilft wirklich dabei, viele Inkonsistenzprobleme zu vermeiden.
Mariam K.

Die wahre vollständige Autolayout-Lösung ist hier
Malex

Antworten:


134

Ich habe hier eine ähnliche Frage gestellt und beantwortet . Zusammenfassend füge ich den Header einmal hinzu und benutze ihn, um die erforderliche Höhe zu finden. Diese Höhe kann dann auf den Header angewendet werden, und der Header wird ein zweites Mal festgelegt, um die Änderung widerzuspiegeln.

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

Wenn Sie mehrzeilige Beschriftungen haben, hängt dies auch von der benutzerdefinierten Ansicht ab, in der die bevorzugte maximale Layoutbreite für jede Beschriftung festgelegt ist:

- (void)layoutSubviews
{
    [super layoutSubviews];

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

oder vielleicht allgemeiner:

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

Update Januar 2015

Leider scheint dies immer noch notwendig. Hier ist eine schnelle Version des Layoutprozesses:

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

Ich fand es nützlich, dies in eine Erweiterung von UITableView zu verschieben:

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

Verwendung:

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

8
Eine Alternative zur Verwendung preferredMaxLayoutWidthbesteht darin, der Kopfzeile vor der Verwendung eine Breitenbeschränkung (die der Breite der Tabellenansicht entspricht) hinzuzufügen systemLayoutSizeFittingSize:.
Benjohn

2
HINWEIS: Wenn Sie feststellen, dass sich der Header über den ersten Zellen befindet, haben Sie vergessen, die Header-Eigenschaft aufself.tableView.tableHeaderView
Laszlo

7
Oft wundert es mich, wie schwierig es sein kann, so etwas völlig Triviales zu tun.
TylerJames

5
HINWEIS: Wenn Sie die genaue Breite als tableView erhalten möchten, sollten Sie die Höhe mit der erforderlichen horizontalen Priorität erhaltenlet 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 = headerwürde unter iOS 10.2
funktionieren

24

Ich konnte keine Header-Ansicht mithilfe von Einschränkungen (im Code) hinzufügen. Wenn ich meiner Ansicht eine Breiten- und / oder Höhenbeschränkung gebe, wird ein Absturz mit der Meldung angezeigt:

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

Wenn ich meiner Tabellenansicht eine Ansicht im Storyboard hinzufüge, werden keine Einschränkungen angezeigt, und sie funktioniert gut als Kopfzeilenansicht. Daher denke ich, dass die Platzierung der Kopfzeilenansicht nicht mithilfe von Einschränkungen erfolgt. Es scheint sich in dieser Hinsicht nicht wie eine normale Sichtweise zu verhalten.

Die Breite ist automatisch die Breite der Tabellenansicht. Sie müssen lediglich die Höhe festlegen. Die Ursprungswerte werden ignoriert, sodass es keine Rolle spielt, was Sie für diese eingeben. Zum Beispiel hat dies gut funktioniert (wie auch 0,0,0,80 für das Rect):

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

Ich hatte diese Ausnahme auch, aber das Hinzufügen einer Kategorie zu UITableView hat sie behoben. Ich fand sie in dieser Antwort: stackoverflow.com/questions/12610783/…
ItsASecret

Ich werde immer noch versuchen, was Sie vorschlagen, aber morgen früh ist es 01:34 Uhr. Ich gehe ins Bett. Vielen Dank, dass Sie sich die Zeit genommen haben, um zu antworten! (Aber ich möchte wirklich keine Höhe angeben, ich möchte, dass sie anhand der Einschränkungen berechnet wird, die ich für das Etikett in der CustomView festgelegt habe.)
ItsASecret

Ich habe es versucht und ja, das Einstellen des Rahmens funktioniert, aber ich habe nach einer Möglichkeit gesucht, das Einstellen des Rahmens zu vermeiden. Ich werde weiter suchen und wenn ich nichts anderes finde, werde ich Ihre Antwort akzeptieren
ItsASecret

1
Ich erhalte diese Ausnahme (derzeit wird 7.1 getestet), wenn die hinzugefügte Header-Ansicht vorhanden ist translatesAutoresizingMaskIntoConstraints = NO. Das Aktivieren der Übersetzung verhindert den Fehler. Ich vermute, dass UITableViewab 7.1 nicht versucht wird, die Header-Ansicht automatisch auszurichten, und dass etwas mit dem voreingestellten Frame gewünscht wird.
Benjohn

16

Ich habe hier viele Methoden gesehen, die so viel unnötiges getan haben, aber Sie brauchen nicht so viel, um das automatische Layout in der Kopfzeile zu verwenden. Sie müssen nur Ihre xib-Datei erstellen, Ihre Einschränkungen festlegen und sie wie folgt instanziieren:

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
    }

Dies funktionierte auch für uns unter iOS 11 mit einem dynamischen Höhenheader mit mehrzeiligen Beschriftungen.
Ben Scheirman

1
Sie können die flexibleHeight-Autoresizing-Option natürlich auch in IB entfernen.
d4Rk

Ich habe versucht, die Höhe meiner tableFooterView (über eine xib / nib) festzulegen, und hatte keinen Erfolg beim Festlegen des Rahmens, der Höhe, des layoutIfNeeded () usw. Aber diese Lösung ermöglichte es mir schließlich, sie festzulegen.
Vikzilla

Vergessen Sie nicht, die Höhenbeschränkung für die gesamte Ansicht in einer xib-Datei festzulegen.
Denis Kutlubaev

6

Eine andere Lösung besteht darin, die Erstellung der Header-Ansicht an den nächsten Haupt-Thread-Aufruf zu senden:

- (void)viewDidLoad {
    [super viewDidLoad];

    // ....

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

Hinweis: Der Fehler wird behoben, wenn die geladene Ansicht eine feste Höhe hat. Ich habe es nicht versucht, wenn die Höhe des Headers nur vom Inhalt abhängt.

EDIT:

Sie können eine sauberere Lösung für dieses Problem finden, indem Sie diese Funktion implementieren und aufrufenviewDidLayoutSubviews

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    [self sizeHeaderToFit];
}

1
@TussLaszlo tableHeaderViewsind eine Art Buggy mit Autolayout. Es gibt einige Problemumgehungen wie diese. Aber da ich dies schrieb, ich habe eine bessere und sauberere Lösung hier stackoverflow.com/a/21099430/127493 durch den Aufruf seine - (void)sizeHeaderToFitinviewDidLayoutSubviews
Martin

4

Code:

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

Es funktioniert. Geben Sie allen Tableheaderview-Unteransichten die richtigen Autolayout-Einschränkungen. Wenn Sie eine einzelne Einschränkung übersehen, funktioniert dies nicht.
Abhimuralidharan

3

Mithilfe der systemLayoutSizeFittingSize- Methode können Sie Autolayout erhalten, um eine Größe bereitzustellen .

Sie können dies dann verwenden, um den Rahmen für Ihre Anwendung zu erstellen. Diese Technik funktioniert immer dann, wenn Sie die Größe einer Ansicht kennen müssen, die intern Autolayout verwendet.

Der Code in Swift sieht aus wie

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

Oder in Ziel-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

Es sollte auch beachtet werden, dass in diesem speziellen Fall das Überschreiben von requireConstraintBasedLayout in Ihrer Unterklasse dazu führt, dass ein Layoutdurchlauf ausgeführt wird. Die Ergebnisse dieses Layoutdurchlaufs werden jedoch ignoriert und der Systemrahmen auf die Breite von tableView und 0 height gesetzt.


3

Folgendes hat bei mir funktioniert.

  1. Verwenden Sie eine einfache alte UIViewals Kopfansicht.
  2. Fügen Sie dazu Unteransichten hinzu UIView
  3. Verwenden Sie Autolayout für die Unteransichten

Der Hauptvorteil, den ich sehe, ist die Begrenzung der Rahmenberechnungen. Apple sollte die UITableViewAPI wirklich aktualisieren , um dies zu vereinfachen.

Beispiel mit 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

Seltsame Dinge passieren. systemLayoutSizeFittingSize funktioniert hervorragend für iOS9, in meinem Fall jedoch nicht für iOS 8. Dieses Problem lässt sich also ganz einfach lösen. Holen Sie sich einfach den Link zur Unteransicht in der Kopfzeile und in viewDidLayoutSubviews, nachdem die Kopfansicht der Super-Call-Aktualisierung begrenzt ist, indem Sie die Höhe als CGRectGetMaxY (yourview.frame) + padding einfügen

UPD: Die einfachste Lösung aller Zeiten : Platzieren Sie in der Kopfansicht die Unteransicht und heften Sie sie nach links , rechts und oben . Platzieren Sie in dieser Unteransicht Ihre Unteransichten mit Einschränkungen für die automatische Höhe. Geben Sie danach den gesamten Auftrag für das Autolayout ab (keine Berechnung erforderlich).

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

Infolgedessen wird die Unteransicht wie gewünscht erweitert / verkleinert und am Ende viewDidLayoutSubviews aufgerufen. Zu diesem Zeitpunkt kennen wir die tatsächliche Größe der Ansicht. Legen Sie daher die Höhe der Header-Ansicht fest und aktualisieren Sie sie durch erneutes Zuweisen. Klappt wunderbar!

Funktioniert auch für die Fußzeilenansicht.


1
Dies ist eine Schleife für mich in iOS 10.
Simon

3

Erweiterte diese Lösung http://collindonnell.com/2015/09/29/dynamically-sized-table-view-header-or-footer-using-auto-layout/ für die Tabellenfußansicht:

@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

Habe gestern einen Tag damit verbracht, den tableHeader dazu zu bringen, die Größe / das Layout automatisch automatisch zu ändern. Diese Lösung funktioniert bei mir. Vielen Dank.
Docchang

Hallo! Könnten Sie bitte einen self.tableFooterView.transformTeil erklären ? Warum ist es notwendig?
Mrvn

Die @ mrvn-Transformation wird verwendet, um die Fußzeile an den unteren Rand der Tabellenansicht zu verschieben.
k06a

3

Aktualisiert für 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

Sie können eine obere + horizontale Positionsbeschränkung zwischen Kopfzeile und Tabellenansicht hinzufügen, um sie korrekt zu platzieren (wenn die Kopfzeile selbst alle erforderlichen internen Layoutbeschränkungen enthält, um einen korrekten Rahmen zu erhalten).

in der tableViewController viewDidLoad-Methode

    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

Meine Tabellenkopfansicht ist eine UIView-Unterklasse. Ich habe im Initialisierer eine einzelne contentView-UIView erstellt, deren Grenzen mit dem Rahmen der Tabellenkopfansicht übereinstimmen, und alle meine Objekte als Unteransicht hinzugefügt.

Fügen Sie dann die Einschränkungen für Ihre Objekte in der layoutSubviewsMethode der Tabellenkopfansicht und nicht im Initialisierer hinzu. Das hat den Absturz gelöst.

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

Mein AutoLayout funktioniert sehr gut:

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;

Ich habe das nicht genau gemacht, aber Sie haben mir eine gute Idee gegeben - das HeaderView entfernen, seinen Frame neu einstellen und es wieder hinzufügen.
Dinesharjani

1

In den meisten Fällen besteht die beste Lösung einfach darin, das Framework nicht zu bekämpfen und Autoresizing-Masken zu verwenden:

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

Durch die Verwendung von Masken zur automatischen Größenänderung teilen Sie dem Framework mit, wie Ihre Ansicht ihre Größe ändern soll, wenn die Größe der Übersicht geändert wird. Diese Änderung basiert jedoch auf dem von Ihnen festgelegten Anfangsrahmen.


0

Ich weiß, dass dies ein alter Beitrag ist, aber nachdem ich alle diesbezüglichen SO-Beiträge durchgesehen und einen ganzen Nachmittag damit verbracht hatte, habe ich endlich eine saubere und doch sehr einfache Lösung gefunden

Zunächst sieht meine Ansichtshierarchie folgendermaßen aus:

  1. Tabellenansicht
    1. Aussicht tableHeaderView
      1. Ansicht mit einem Ausgang namens headerView

Jetzt in der Ansicht (Nr. 3) habe ich alle Einschränkungen so eingerichtet, wie ich es normalerweise tun würde, einschließlich des unteren Bereichs zum Container. Dadurch wird die Größe des Containers (dh 3.View, dh HeaderView) basierend auf seinen Unteransichten und deren Einschränkungen angepasst.

Danach setze ich die Einschränkungen zwischen 3. Viewund 2. Viewauf diese:

  1. Oberer Platz zum Container: 0
  2. Führungsraum zum Container: 0
  3. Leerzeichen zum Container: 0

Beachten Sie, dass ich den unteren Bereich absichtlich weglasse.

Sobald dies alles im Storyboard erledigt ist, müssen Sie nur noch diese drei Codezeilen einfügen:

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

Tipps: Wenn Sie die Methode setAndLayoutTableHeaderView verwenden, sollten Sie den Frame der Unteransichten aktualisieren. In dieser Situation sollte UILabels PreferredMaxLayoutWidth vor dem Aufruf von systemLayoutSizeFittingSize aufgerufen werden. Rufen Sie nicht layoutSubview auf.

Code anzeigen


0

Teile meinen Ansatz.

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

Verwendung.

[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

In meinem Fall hat die Methode mit systemLayoutSizeFittingSize aus irgendeinem Grund nicht funktioniert. Was für mich funktioniert hat, ist eine Modifikation der von HotJard veröffentlichten Lösung (seine ursprüngliche Lösung hat in meinem Fall auch unter iOS 8 nicht funktioniert). Was ich tun musste, war in der Kopfansicht eine Unteransicht zu platzieren und sie nach links, rechts, oben zu heften (nicht nach unten zu pinnen). Fügen Sie alles mit Autolayout in diese Unteransicht ein und tun Sie dies im Code:

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

Ein alter Beitrag. Aber ein guter Beitrag. Hier sind meine 2 Cent.

Stellen Sie zunächst sicher, dass in Ihrer Header-Ansicht die Einschränkungen so angeordnet sind, dass sie die eigene intrinsische Inhaltsgröße unterstützen. Dann machen Sie folgendes.

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

Ich konnte es durch den folgenden Ansatz erreichen (dies funktioniert für die Fußzeile auf die gleiche Weise).

Zunächst benötigen Sie eine kleine UITableViewErweiterung:

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

In Ihrer Ansicht Controller-Klassenimplementierung:

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

Hinweise zur UIViewSubview-Implementierung eines Headers :

  1. Sie müssen 100% sicher sein, dass Ihre Header-Ansicht das richtige Autolayout-Setup hat. Ich würde empfehlen, mit einer einfachen Header-Ansicht mit nur einer hohen Einschränkung zu beginnen und das obige Setup auszuprobieren.

  2. Überschreiben requiresConstraintBasedLayoutund zurück true:

.

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

0

Für Xamarin-Benutzer:

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

Angenommen, Sie haben die Header-Ansicht Ihrer Tabellenansicht als TableviewHeader bezeichnet


0

Hier ist, wie Sie in Ihrem tun können 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

Jede auf Einschränkungen basierende UIViewkann gut sein tableHeaderView.

Man muss ein tableFooterViewvorher setzen und dann zusätzliche nachfolgende Einschränkungen für tableFooterViewund auferlegen 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;

}}

Alle Details und Codefragmente finden Sie hier


0

Ich habe eine Problemumgehung gefunden. Wickeln Sie Ihre Autibayout-XIB-Header-Ansicht in einen leeren Uiview-Wrapper und weisen Sie die Header-Ansicht der tableViewHeader-Eigenschaft von tableView zu.

    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

Folgendes funktioniert für UITableViewController in iOS 12:

Zeichnen Sie eine UIView in die Tabellenansicht über allen Prototypzellen für die Kopfzeile und unter allen Prototypzellen für die Fußzeile. Richten Sie Ihre Kopf- und Fußzeile nach Bedarf ein. Legen Sie alle erforderlichen Einschränkungen fest.

Verwenden Sie nun die folgenden Erweiterungsmethoden

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

und rufen Sie es in ViewDidLayoutSubviews der Unterklasse von UITableViewController auf

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

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

0

Ich bin auf das Problem gestoßen, die Breite 375pt zu erhalten. Die einzige Möglichkeit, die für mich funktioniert hat, besteht darin, die tableView weiterzuleiten, um die richtige Breite zu erhalten. Ich habe auch AutoLayout der Einstellung der Rahmengröße vorgezogen.

Hier ist die Version, die für mich funktioniert:

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

Schnelle Version (geändert von @Ben Packard Antwort)

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

Meine Lösung besteht darin, eine neue Klasse wie diese zu erstellen.

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

}

Um es zu verwenden, fügen Sie einfach alle Ihre Unteransichten zu einer Instanz von hinzu BaseTableHeaderViewund hängen Sie sie an Ihre Tabellenansicht an.

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

Die Größe wird automatisch basierend auf den Einschränkungen geändert.


-1

Akzeptierte Antworten sind nur für Tabellen mit einem einzelnen Abschnitt nützlich. UITableViewStellen UITableViewHeaderFooterViewSie für mehrere Abschnitte sicher, dass Ihr Header von erbt und es Ihnen gut geht.

Als Alternative einbetten können Sie den aktuellen Header in der contentViewvon einem UITableViewHeaderFooterView. Genau wie UITableViewCellfunktioniert.


9
Frage ist tableHeaderViewnicht die Abschnittsüberschrift.
Rpranata
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.