เป็นไปได้ไหมที่จะใช้ AutoLayout กับ tableHeaderView ของ UITableView


102

ตั้งแต่ฉันค้นพบว่าAutoLayoutฉันใช้มันทุกที่ตอนนี้ฉันกำลังพยายามใช้กับไฟล์tableHeaderView.

ฉันทำsubclassของUIViewทุกอย่างที่เพิ่ม (ป้าย ฯลฯ ... ) ผมอยากมีข้อ จำกัด ของพวกเขาแล้วฉันเพิ่มนี้CustomViewไป'UITableViewtableHeaderView

ทุกอย่างทำงานได้ดียกเว้นUITableViewเสมอแสดงข้างต้นCustomViewโดยข้างต้นผมหมายถึงCustomViewคือภายใต้UITableViewดังนั้นจึงไม่สามารถมองเห็นได้!

ดูเหมือนว่าไม่ว่าฉันจะทำอะไรค่าheightของUITableView' tableHeaderViewจะเป็น0 เสมอ (ดังนั้นคือความกว้าง x และ y)

คำถามของฉัน: มันเป็นไปได้ที่ทุกคนจะบรรลุนี้โดยไม่ต้องตั้งกรอบด้วยตนเอง ?

แก้ไข: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 .. เพียงสร้างมุมมองในฉากเดียวกันที่มี tableview แล้วลากไปที่ตาราง
Mariam K.

ฉันพยายามหลีกเลี่ยง IB มากที่สุดเท่าที่จะทำได้จนถึงตอนนี้ฉันไม่ต้องใช้มันถ้าฉันไม่สามารถใช้งานได้ฉันจะพยายามกับ IB
ItsASecret

1
Apple แนะนำให้นักพัฒนาใช้ IB ทุกครั้งที่เป็นไปได้ในเรื่องการกำหนดค่าอัตโนมัติ ช่วยในการหลีกเลี่ยงปัญหาความไม่ลงรอยกันได้มาก
Mariam K.

โซลูชันการกำหนดระบบอัตโนมัติที่แท้จริงอยู่ที่นี่
malex

คำตอบ:


136

ฉันถามและตอบคำถามที่คล้ายกันที่นี่ โดยสรุปฉันเพิ่มส่วนหัวหนึ่งครั้งและใช้เพื่อค้นหาความสูงที่ต้องการ จากนั้นสามารถใช้ความสูงดังกล่าวกับส่วนหัวได้และส่วนหัวจะถูกตั้งค่าเป็นครั้งที่สองเพื่อแสดงการเปลี่ยนแปลง

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

อัปเดตมกราคม 2558

น่าเสียดายที่สิ่งนี้ดูเหมือนว่าจำเป็น นี่คือขั้นตอนการจัดวางเวอร์ชันที่รวดเร็ว:

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
Laszlo

9
บ่อยครั้งที่ฉันทำให้ฉันประหลาดใจว่าการทำอะไรที่ไม่สำคัญแบบนี้เป็นเรื่องยุ่งยากมากแค่ไหน
TylerJames

5
หมายเหตุ: หากคุณต้องการรับความกว้างที่แน่นอนเป็น tableView คุณควรได้รับความสูงโดยมีลำดับความสำคัญแนวนอนที่ต้องการlet 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 = 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 สำหรับ rect):

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

ฉันมีข้อยกเว้นนั้นเช่นกัน แต่การเพิ่มหมวดหมู่ใน UITableView ได้รับการแก้ไขแล้วฉันพบในคำตอบนั้น: stackoverflow.com/questions/12610783/…
ItsASecret

ฉันจะลองทำตามที่คุณแนะนำ แต่พรุ่งนี้เช้าเวลา 01:34 น. ฉันจะเข้านอนขอบคุณมากที่สละเวลาตอบ! (แต่ฉันไม่ต้องการระบุความสูงจริงๆฉันต้องการให้คำนวณโดยข้อ จำกัด ที่ฉันตั้งค่าไว้บนป้ายกำกับใน
CustomView

ฉันได้ลองแล้วและใช่การตั้งค่าเฟรมได้ผล แต่ฉันกำลังมองหาวิธีหลีกเลี่ยงการตั้งค่าเฟรมฉันจะมองต่อไปและถ้าฉันไม่พบอะไรอีกฉันจะยอมรับคำตอบของคุณ
ItsASecret

1
ฉันได้รับข้อยกเว้นนี้ (กำลังทดสอบ 7.1) translatesAutoresizingMaskIntoConstraints = NOถ้ามุมมองส่วนหัวมีเพิ่ม การเปิดการแปลจะช่วยป้องกันข้อผิดพลาด - ฉันสงสัยว่าUITableViewตั้งแต่ 7.1 ไม่ได้พยายามที่จะกำหนดมุมมองส่วนหัวโดยอัตโนมัติและต้องการบางสิ่งบางอย่างด้วยการตั้งค่าเฟรมล่วงหน้า
Benjohn

17

ฉันเห็นวิธีการมากมายที่นี่ทำสิ่งที่ไม่จำเป็นมากมาย แต่คุณไม่จำเป็นต้องใช้การจัดวางอัตโนมัติในมุมมองส่วนหัวมากนัก คุณเพียงแค่ต้องสร้างไฟล์ 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 ด้วยส่วนหัวความสูงแบบไดนามิกพร้อมป้ายกำกับหลายบรรทัด
Ben Scheirman

1
คุณยังสามารถลบ FlexibleHeight-Autoresizing-Option ใน IB ได้
d4Rk

ฉันพยายามตั้งค่าความสูงของ tableFooterView (ผ่าน xib / nib) และไม่ประสบความสำเร็จในการตั้งค่าเฟรมความสูง layoutIfNeeded () ฯลฯ แต่ในที่สุดวิธีนี้ก็อนุญาตให้ฉันตั้งค่าได้
vikzilla

อย่าลืมตั้งค่าข้อจำกัดความสูงเป็นมุมมองทั้งหมดในไฟล์ xib
Denis Kutlubaev

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

มันใช้งานได้ให้ข้อ จำกัด การจัดวางอัตโนมัติที่เหมาะสมกับมุมมองย่อยของ tableheaderview ทั้งหมด หากคุณพลาดข้อ จำกัด เพียงข้อเดียวก็จะไม่ทำงาน
abhimuralidharan

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 transform ใช้เพื่อย้ายส่วนท้ายไปที่ด้านล่างสุดของ 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

นอกจากนี้ควรสังเกตด้วยว่าในกรณีนี้การแทนที่ needConstraintBasedLayout ในคลาสย่อยของคุณจะส่งผลให้มีการดำเนินการเลย์เอาต์ผ่านอย่างไรก็ตามผลลัพธ์ของพาสเลย์เอาต์นี้จะถูกละเว้นและเฟรมระบบตั้งค่าเป็นความกว้างของ tableView และความสูง 0


3

สิ่งต่อไปนี้ใช้ได้ผลสำหรับฉัน

  1. ใช้เก่าธรรมดาUIViewเป็นมุมมองส่วนหัว
  2. เพิ่มมุมมองย่อยลงในสิ่งนั้น UIView
  3. ใช้การจัดวางอัตโนมัติในมุมมองย่อย

ประโยชน์หลักที่ฉันเห็นคือการ จำกัด การคำนวณเฟรม Apple ควรอัปเดตUITableViewAPI เพื่อให้ง่ายขึ้น

ตัวอย่างการใช้ 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 หลังจากการอัปเดตส่วนหัวของ super call จะถูก จำกัด โดยการใส่ความสูงเป็น CGRectGetMaxY (yourview.frame) + padding

UPD: ทางออกที่ง่ายที่สุดที่เคย : ดังนั้นในมุมมองของส่วนหัว subview สถานที่และขาไปซ้าย , ขวา , ด้านบน ในมุมมองย่อยนั้นวางมุมมองย่อยของคุณด้วยข้อ จำกัด ด้านความสูงอัตโนมัติ หลังจากนั้นให้งานทั้งหมดในการกำหนดค่าอัตโนมัติ (ไม่จำเป็นต้องคำนวณ)

- (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 height และอัปเดตโดยกำหนดใหม่ ใช้งานได้เหมือนมีเสน่ห์!

ยังใช้ได้กับมุมมองส่วนท้าย


1
สิ่งนี้เกิดขึ้นสำหรับฉันใน iOS 10
Simon

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

ฉันรู้ว่านี่เป็นโพสต์เก่า แต่หลังจากอ่านบทความ SO ทั้งหมดเกี่ยวกับเรื่องนี้และผ่านช่วงบ่ายมาตลอดการเล่นกับสิ่งนี้ในที่สุดฉันก็พบวิธีแก้ปัญหาที่สะอาดและเรียบง่ายมาก

ก่อนอื่นลำดับชั้นมุมมองของฉันมีลักษณะดังนี้:

  1. มุมมองตาราง
    1. ดู tableHeaderView
      1. ดูด้วยเต้าเสียบที่เรียกว่าheaderView

ตอนนี้อยู่ใน View (หมายเลข 3) ฉันตั้งค่าข้อ จำกัด ทั้งหมดตามปกติที่ฉันจะรวมพื้นที่ด้านล่างลงในคอนเทนเนอร์ สิ่งนี้จะทำให้คอนเทนเนอร์ (เช่น 3. ดูเช่น 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 คุณควรอัปเดตเฟรมของมุมมองย่อยดังนั้นในสถานการณ์เช่นนี้จึงควรเรียกที่ต้องการ MaxLayoutWidth ของ UILabel ก่อน 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;
}

สมมติว่าคุณตั้งชื่อมุมมองส่วนหัวของ tableview ของคุณเป็น 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

ใด ๆ ตามข้อ จำกัดสามารถที่ดีUIViewtableHeaderView

หนึ่งในความต้องการที่จะตั้งtableFooterViewก่อนและจากนั้นกำหนดข้อ จำกัด ต่อท้ายเพิ่มเติมเกี่ยวกับและtableFooterViewtableHeaderView

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

ฉันได้หาวิธีแก้ปัญหาเบื้องต้นแล้ว ห่อมุมมองส่วนหัว xib ที่เขียนอัตโนมัติของคุณใน uiview wrapper ที่ว่างเปล่าและกำหนดมุมมองส่วนหัวให้กับคุณสมบัติ 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 UITableViewHeaderFooterViewเหมือนUITableViewCellผลงานเป๊ะ


9
คำถามtableHeaderViewไม่เกี่ยวกับส่วนหัวของส่วน
Rpranata
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.