จะปรับขนาด tableHeaderView ของ UITableView ได้อย่างไร?


103

ฉันมีปัญหาในการปรับขนาด tableHeaderView มันง่ายไม่ได้ผล

1) สร้าง UITableView และ UIView (100 x 320 px);

2) ตั้งค่า UIView เป็น tableHeaderView ของ UITableView;

3) สร้างและไป ทุกอย่างปกติดี.

ตอนนี้ฉันต้องการปรับขนาด tableHeaderView ดังนั้นฉันจึงเพิ่มรหัสนี้ใน viewDidLoad:

self.tableView.autoresizesSubviews = YES;

self.tableView.tableHeaderView = myHeaderView;
self.tableView.tableFooterView = myFooterView;

CGRect newFrame = self.tableView.tableHeaderView.frame;
newFrame.size.height = newFrame.size.height + 100;
self.tableView.tableHeaderView.frame = newFrame;

ความสูงของตาราง HeaderView ควรปรากฏด้วย 200 แต่ปรากฏพร้อมกับ 100

ถ้าฉันเขียน:

self.tableView.autoresizesSubviews = YES;


CGRect newFrame = myHeaderView.frame;
newFrame.size.height = newFrame.size.height + 100;
myHeaderView.frame = newFrame;


self.tableView.tableHeaderView = myHeaderView;
self.tableView.tableFooterView = myFooterView;

จากนั้นเริ่มต้นด้วยความสูง 200 ตามที่ฉันต้องการ แต่ฉันต้องการแก้ไขในรันไทม์

ฉันได้ลองสิ่งนี้แล้ว แต่ไม่ประสบความสำเร็จ:

self.tableView.autoresizesSubviews = YES;

self.tableView.tableHeaderView = myHeaderView;
self.tableView.tableFooterView = myFooterView;

CGRect newFrame = self.tableView.tableHeaderView.frame;
newFrame.size.height = newFrame.size.height + 100;
self.tableView.tableHeaderView.frame = newFrame;

[self.tableView.tableHeaderView setNeedsLayout];
[self.tableView.tableHeaderView setNeedsDisplay];
[self.tableView setNeedsLayout];
[self.tableView setNeedsDisplay];

ประเด็นคือ: เราจะปรับขนาด tableHeaderView ในรันไทม์ได้อย่างไร ???

มีใครทำได้บ้าง?

ขอบคุณ

iMe

คำตอบ:


180

FYI: ฉันได้รับสิ่งนี้ให้ใช้งานได้โดยการแก้ไข tableHeaderView และตั้งค่าใหม่ ในกรณีนี้ฉันกำลังปรับขนาดของ tableHeaderView เมื่อมุมมองย่อย UIWebView เสร็จสิ้นการโหลด

[webView sizeToFit];
CGRect newFrame = headerView.frame;
newFrame.size.height = newFrame.size.height + webView.frame.size.height;
headerView.frame = newFrame;
[self.tableView setTableHeaderView:headerView];

13
+1 สิ่งนี้ได้ผลสำหรับฉัน การเรียก 'setTableHeaderView' หลังจากมุมมองย่อยของคุณเปลี่ยนขนาดเป็นกุญแจสำคัญ ปัญหาคือมุมมองย่อยของฉันเปลี่ยนขนาดในช่วงหนึ่งวินาทีเป็นภาพเคลื่อนไหว ตอนนี้ฉันกำลังพยายามหาวิธีทำให้ tableHeaderView เคลื่อนไหวด้วย
แอนดรูว์

1
สมบูรณ์แบบขอบคุณมาก สำหรับฉันนี่เป็นตัวอย่างคุณสมบัติที่ไม่พึงปรารถนาอย่างหนึ่งของคุณสมบัติใน Objective-C ไม่มีทางที่เราจะรู้ได้ (และไม่มีเหตุผลที่เราควรรู้) ว่าการตั้งค่าส่วนหัวนั้นมีผลข้างเคียงจากการคำนวณความสูงใหม่ ควรทำโดยอัตโนมัติเมื่อเราอัปเดตความสูงของส่วนหัวหรือเราควรเรียกใช้เช่น[tableView recalculateHeaderHeight]ทุกครั้ง
jakeboxer

48
@ แอนดรูฉันรู้ว่านี่เกือบจะสายไปแล้ว แต่ดีกว่าไม่ช้าไปกว่านั้น: ฉันสามารถเคลื่อนไหวความสูงที่เปลี่ยนไปของมุมมองส่วนหัวตารางได้โดยการตัดsetTableHeaderView:สายด้วย[tableview beginUpdates]และ[tableview endUpdates]
jasongregori

2
ความคิดเห็นที่มีประโยชน์ที่คุณเพิ่ม @jasongregori - ฉันเห็นว่าเทคนิคเดียวกัน (beginUpdates + endUpdates) ไม่เคลื่อนไหวการเปลี่ยนแปลงความสูงสำหรับ tableFooter ดูวิธีที่ทำ tableHeaderView คุณคิดหาวิธีที่ดีในการทำให้ tableFooterView เคลื่อนไหวแล้วหรือยัง?
kris

1
น่าประทับใจมากที่มันใช้งานได้เมื่อทำในบล็อกภาพเคลื่อนไหว UIView แม้ว่าฉันจะไม่คิดว่ามันมีประสิทธิภาพมากในกรณีนั้น
Can

12

คำตอบนี้เก่าและดูเหมือนจะใช้ไม่ได้กับ iOS 7 ขึ้นไป

ฉันพบปัญหาเดียวกันและฉันก็ต้องการให้การเปลี่ยนแปลงเป็นภาพเคลื่อนไหวดังนั้นฉันจึงสร้างคลาสย่อยของ UIView สำหรับมุมมองส่วนหัวของฉันและเพิ่มวิธีการเหล่านี้:

- (void)adjustTableHeaderHeight:(NSUInteger)newHeight{
    NSUInteger oldHeight = self.frame.size.height;
    NSInteger originChange = oldHeight - newHeight;

    [UIView beginAnimations:nil context:nil];

    [UIView setAnimationDuration:1.0f];
    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];

    self.frame = CGRectMake(self.frame.origin.x, 
                        self.frame.origin.y, 
                        self.frame.size.width, 
                        newHeight);

    for (UIView *view in [(UITableView *)self.superview subviews]) {
        if ([view isKindOfClass:[self class]]) {
            continue;
        }
        view.frame = CGRectMake(view.frame.origin.x, 
                            view.frame.origin.y - originChange, 
                            view.frame.size.width, 
                            view.frame.size.height);
    }

    [UIView commitAnimations];
}

- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context{
    [(UITableView *)self.superview setTableHeaderView:self];
}

โดยพื้นฐานแล้วสิ่งนี้จะทำให้มุมมองย่อยทั้งหมดของ UITableView เคลื่อนไหวซึ่งไม่ใช่ประเภทคลาสเดียวกับคลาสการโทร ในตอนท้ายของภาพเคลื่อนไหวจะเรียก setTableHeaderView บน superview (UITableView) - หากไม่มีสิ่งนี้เนื้อหา UITableView จะย้อนกลับไปในครั้งถัดไปที่ผู้ใช้เลื่อน ข้อ จำกัด เดียวที่ฉันพบในตอนนี้คือหากผู้ใช้พยายามเลื่อน UITableView ในขณะที่กำลังเกิดภาพเคลื่อนไหวการเลื่อนจะเคลื่อนไหวราวกับว่ายังไม่ได้ปรับขนาดมุมมองส่วนหัว (ไม่ใช่เรื่องใหญ่หากเป็นภาพเคลื่อนไหว ด่วน).


ทำงานได้อย่างสมบูรณ์ลบภาพเคลื่อนไหวออกเพราะฉันไม่ต้องการมัน
Jiho Kang

ทำงานได้อย่างสมบูรณ์แบบจนกระทั่ง iOS7 ตอบสนอง ... เพิ่มโซลูชันของฉันด้านล่าง
avishic

1
WTF? คุณกำลังยุ่งกับการใช้งานภายในของ UITableView มากจนคุณไม่ควรแปลกใจเลยที่มันไม่ทำงานใน iOS เวอร์ชันใหม่กว่า ... ;)
Daniel Rinser

10

หากคุณต้องการทำให้การเปลี่ยนแปลงเคลื่อนไหวตามเงื่อนไขคุณสามารถทำสิ่งต่อไปนี้:

- (void) showHeader:(BOOL)show animated:(BOOL)animated{

    CGRect closedFrame = CGRectMake(0, 0, self.view.frame.size.width, 0);
    CGRect newFrame = show?self.initialFrame:closedFrame;

    if(animated){
        // The UIView animation block handles the animation of our header view
        [UIView beginAnimations:nil context:nil];
        [UIView setAnimationDuration:0.3];
        [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];

        // beginUpdates and endUpdates trigger the animation of our cells
        [self.tableView beginUpdates];
    }

    self.headerView.frame = newFrame;
    [self.tableView setTableHeaderView:self.headerView];

    if(animated){
        [self.tableView endUpdates];
        [UIView commitAnimations];
    }
}

โปรดทราบว่าแอนิเมชั่นเป็นแบบสองพับ:

  1. ภาพเคลื่อนไหวของเซลล์ด้านล่างไฟล์tableHeaderView. ทำได้โดยใช้beginUpdatesและendUpdates
  2. ภาพเคลื่อนไหวของมุมมองส่วนหัวจริง สิ่งนี้ทำได้โดยใช้UIViewบล็อกภาพเคลื่อนไหว

ในการซิงโครไนซ์ภาพเคลื่อนไหวทั้งสองanimationCurveนี้จะต้องตั้งค่าเป็นUIViewAnimationCurveEaseInOutและระยะเวลาเป็น0.3ซึ่งดูเหมือนว่า UITableView จะใช้สำหรับภาพเคลื่อนไหว

อัปเดต

ฉันสร้างโครงการ Xcode บน gihub ซึ่งทำสิ่งนี้ ตรวจสอบโครงการResizeTableHeaderViewAnimatedในbesi / ios-quickies

ภาพหน้าจอ


2
เทคนิคนี้ใช้ได้ผล แต่ใช้ไม่ได้กับมุมมองตารางที่มีส่วนหัวของส่วน เมื่อคุณใช้การอัปเดตเริ่มต้น / สิ้นสุดการอัปเดตจะรบกวนบล็อกภาพเคลื่อนไหวโดยปล่อยให้ส่วนหัวที่ซ้ำกัน
BlueFish

9

ฉันคิดว่ามันน่าจะใช้ได้ถ้าคุณแค่ตั้งค่าความสูงของ myHeaderView ดังนี้:

CGRect newFrame = myHeaderView.frame;
newFrame.size.height = newFrame.size.height + 100;
myHeaderView.frame = newFrame;

self.tableView.tableHeaderView = myHeaderView;

ใช้งานได้จริง แต่ถ้าใช้ใน viewDidLayoutSubviews
KoCMoHaBTa

6

ใช้โซลูชัน @garrettmoon ด้านบนจนถึง iOS 7
นี่คือโซลูชันที่อัปเดตโดยอิงจาก @ garrettmoon's:

- (void)adjustTableHeaderHeight:(NSUInteger)newHeight animated:(BOOL)animated {

    [UIView beginAnimations:nil context:nil];

    [UIView setAnimationDuration:[CATransaction animationDuration]];
    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];

    self.frame = CGRectMake(self.frame.origin.x,
                        self.frame.origin.y,
                        self.frame.size.width,
                        newHeight);

    [(UITableView *)self.superview setTableHeaderView:self];

    [UIView commitAnimations];
}

- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context{
    [(UITableView *)self.superview setTableHeaderView:self];
}

5

สิ่งนี้ใช้ได้กับฉันใน iOS 7 และ 8 รหัสนี้ทำงานบนตัวควบคุมมุมมองตาราง

[UIView animateWithDuration:0.3 animations:^{
    CGRect oldFrame = self.headerView.frame;
    self.headerView.frame = CGRectMake(oldFrame.origin.x, oldFrame.origin.y, oldFrame.size.width, newHeight);
    [self.tableView setTableHeaderView:self.headerView];
}];

4

เป็นเพราะ setter ของ tableHeaderView

คุณต้องตั้งค่าความสูง UIView ก่อนที่จะตั้งค่า tableHeaderView (จะง่ายกว่ามากถ้า Apple เปิดแหล่งที่มากรอบนี้ ... )


4

บน iOS 9 และต่ำกว่า tableHeaderViewจะไม่จัดวางใหม่หลังจากปรับขนาดแล้ว ปัญหานี้แก้ไขได้แล้วใน iOS 10

ในการแก้ปัญหานี้ให้ใช้รหัสต่อไปนี้:

self.tableView.tableHeaderView = self.tableView.tableHeaderView;

2

บน iOS 9.x การทำเช่นนี้ใช้viewDidLoadงานได้ดี:

var frame = headerView.frame
frame.size.height = 11  // New size
headerView.frame = frame

headerViewจะถูกประกาศเป็น@IBOutlet var headerView: UIView!และเชื่อมต่อบนกระดานเรื่องราวซึ่งวางไว้ที่ด้านบนสุดของ tableView เพื่อทำหน้าที่เป็น tableHeaderView


2

สิ่งนี้มีไว้สำหรับเมื่อคุณใช้การจัดวางอัตโนมัติและตั้งค่าtranslatesAutoresizingMaskIntoConstraints = falseเป็นมุมมองส่วนหัวที่กำหนดเอง

intrinsicContentSizeวิธีที่ดีที่สุดและง่ายที่สุดคือการแทนที่ UITableViewใช้ภายในintrinsicContentSizeเพื่อกำหนดขนาดส่วนหัว / ส่วนท้าย เมื่อคุณแทนที่intrinsicContentSizeในมุมมองที่กำหนดเองแล้วสิ่งที่คุณต้องทำมีดังต่อไปนี้

  1. กำหนดค่าเค้าโครงของมุมมองส่วนหัว / ส่วนท้ายที่กำหนดเอง (มุมมองย่อย)
  2. เรียก invalidateIntrinsicContentSize()
  3. เรียกtableView.setNeedsLayout()และtableView.layoutIfNeeded()

จากนั้นUITableViewส่วนหัว / ส่วนท้ายของจะได้รับการอัปเดตตามที่คุณต้องการ ไม่จำเป็นต้องตั้งค่า nil มุมมองหรือรีเซ็ต

สิ่งหนึ่งที่น่าสนใจจริงๆสำหรับUITableView.tableHeaderViewหรือ.tableFooterViewว่าหลวมความสามารถในการจัดการของUIStackView arrangedSubviewsหากคุณต้องการที่จะใช้UIStackViewเป็น tableHeaderView หรือ tableFooterView คุณต้องฝัง stackView ในUIViewและแทนที่'sUIViewintrinsicContentSize


2

สำหรับรหัสที่ทดสอบอย่างรวดเร็ว 5

override func viewDidLayoutSubviews() {
      super.viewDidLayoutSubviews()

        guard let headerView = self.tblProfile.tableHeaderView else {
            return
        }

        let size = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)

        if headerView.frame.size.height != size.height {
            headerView.frame.size.height = size.height
            self.tblProfile.tableHeaderView = headerView
            self.tblProfile.layoutIfNeeded()
        }
    }

หมายเหตุ:คุณต้องให้ข้อ จำกัด ของมุมมองย่อยทั้งหมดในรูปแบบบนล่างนำหน้าและต่อท้าย จึงจะได้ขนาดที่ต้องการทั้งหมด

อ้างอิงจาก: https://useyourloaf.com/blog/variable-height-table-view-header/


1

การตั้งค่าความสูงสำหรับสถานที่ให้มุมมองส่วนหัวtableView.tableHeaderViewในviewDidLoadดูเหมือนจะไม่ทำงานที่สูงมุมมองส่วนหัวยังคงไม่เปลี่ยนแปลงตามที่คาดไว้

หลังจากต่อสู้กับปัญหานี้มาหลายครั้ง ฉันพบว่าคุณสามารถเปลี่ยนความสูงได้โดยเรียกใช้มุมมองส่วนหัวสร้างตรรกะภายในไฟล์ - (void)didMoveToParentViewController:(UIViewController *)parentวิธีการ

ดังนั้นโค้ดตัวอย่างจะมีลักษณะดังนี้:

- (void)didMoveToParentViewController:(UIViewController *)parent {
    [super didMoveToParentViewController:parent];

    if ( _tableView.tableHeaderView == nil ) {
        UIView *header = [[[UINib nibWithNibName:@"your header view" bundle:nil] instantiateWithOwner:self options:nil] firstObject];

        header.frame = CGRectMake(0, 0, CGRectGetWidth([UIScreen mainScreen].bounds), HeaderViewHeight);

        [_tableView setTableHeaderView:header];
    }
}

0

ฉันพบว่าตัวเริ่มต้น initWithFrame ของ UIView ไม่ให้เกียรติ rect ที่ฉันส่งผ่านอย่างถูกต้องดังนั้นฉันจึงทำสิ่งต่อไปนี้ซึ่งทำงานได้อย่างสมบูรณ์:

 - (id)initWithFrame:(CGRect)aRect {

    CGRect frame = [[UIScreen mainScreen] applicationFrame];

    if ((self = [super initWithFrame:CGRectZero])) {

        // Ugly initialization behavior - initWithFrame will not properly honor the frame we pass
        self.frame = CGRectMake(0, 0, frame.size.width, 200);

        // ...
    }
}

ข้อดีของสิ่งนี้คือการห่อหุ้มไว้ในโค้ดมุมมองของคุณได้ดีขึ้น


การดึงเฟรมออกมาUIScreenเป็นความคิดที่ไม่ดีคุณจะใช้มุมมองของคุณซ้ำได้อย่างไร
Zorayr

0

ฉันได้ใช้การเปลี่ยนความสูงแบบเคลื่อนไหวของส่วนหัวของตารางเพื่อขยายเป็นหน้าจอโดยรวมเมื่อแตะ อย่างไรก็ตามรหัสสามารถช่วยได้ในกรณีอื่น ๆ :

// Swift
@IBAction func tapped(sender: UITapGestureRecognizer) {

    self.tableView.beginUpdates()       // Required to update cells. 

    // Collapse table header to original height
    if isHeaderExpandedToFullScreen {   

        UIView.animateWithDuration(0.5, animations: { () -> Void in
            self.scrollView.frame.size.height = 110   // original height in my case is 110
        })

    }
    // Expand table header to overall screen            
    else {      
        let screenSize = self.view.frame           // "screen" size

        UIView.animateWithDuration(0.5, animations: { () -> Void in
            self.scrollView.frame.size.height = screenSize.height
        })
    }

    self.tableView.endUpdates()  // Required to update cells. 

    isHeaderExpandedToFullScreen= !isHeaderExpandedToFullScreen  // Toggle
}

0

ส่วนหัวปรับขนาด UITableView - UISearchBar พร้อมแถบขอบเขต

ฉันต้องการUITableViewโดยมี a UISearchBarเป็นส่วนหัวของตารางดังนั้นฉันจึงมีลำดับชั้นที่มีลักษณะเช่นนี้

UITableView
  |
  |--> UIView
  |     |--> UISearchBar
  |
  |--> UITableViewCells

วิธีการ UISearchBarDelegate

ตามที่ได้ระบุไว้ที่อื่นหากคุณไม่ได้ setTableViewHeader หลังจากเปลี่ยนแล้วจะไม่มีอะไรเกิดขึ้น

- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar
{
    searchBar.showsScopeBar = YES;
    [UIView animateWithDuration:0.2f animations:^{
        [searchBar sizeToFit];
        CGFloat height = CGRectGetHeight(searchBar.frame);

        CGRect frame = self.tableView.tableHeaderView.frame;
        frame.size.height = height;
        self.tableHeaderView.frame = frame;
        self.tableView.tableHeaderView = self.tableHeaderView;
    }];

    [searchBar setShowsCancelButton:YES animated:YES];
    return YES;
}

- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar
{
    searchBar.showsScopeBar = NO;
    [UIView animateWithDuration:0.f animations:^{
        [searchBar sizeToFit];

        CGFloat height = CGRectGetHeight(searchBar.frame);

        CGRect frame = self.tableView.tableHeaderView.frame;
        frame.size.height = height;
        self.tableHeaderView.frame = frame;
        self.tableView.tableHeaderView = self.tableHeaderView;
    }];

    [searchBar setShowsCancelButton:NO animated:YES];
    return YES;
}

0

หาก headerView แบบกำหนดเองได้รับการออกแบบโดยใช้ autolayout และ headerView จำเป็นต้องได้รับการอัปเดตหลังจากการดึงข้อมูลเว็บหรืองานขี้เกียจที่คล้ายกัน จากนั้นในiOS-Swiftฉันทำสิ่งนี้และได้รับการอัปเดต headerView โดยใช้รหัสร้อง:

//to reload your cell data
self.tableView.reloadData()
dispatch_async(dispatch_get_main_queue(),{
// this is needed to update a specific tableview's headerview layout on main queue otherwise it's won't update perfectly cause reloaddata() is called
  self.tableView.beginUpdates()
  self.tableView.endUpdates()
} 

0

เห็นได้ชัดว่าตอนนี้ Apple ควรจะใช้ UITableViewAutomaticDimension สำหรับ tableHeaderView & tableFooterView ...

สิ่งต่อไปนี้ดูเหมือนจะใช้ได้กับฉันโดยใช้ข้อห้ามของเค้าโครง:

CGSize   s  = [ self  systemLayoutSizeFittingSize : UILayoutFittingCompressedSize ];
CGRect   f  = [ self  frame ];

f.size   = s;

[ self  setFrame : f ];

0

หาก tableHeaderView ของคุณเป็น webView ที่ปรับเนื้อหาได้คุณสามารถลอง:

[self.webView.scrollView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:nil];

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    self.webView.height = self.webView.scrollView.contentSize.height;
    self.tableView.tableHeaderView = self.webView;
}

ฉันทดสอบบน iOS9 และ iOS11 ทำงานได้ดี


-3

[self.tableView reloadData]หลังจากเปลี่ยนความสูงแล้วคุณได้ลอง หรือยัง?


1
มันเกี่ยวกับ tableHeaderView ซึ่งเป็นมุมมองแบบคงที่ - [UITableView reloadData]มีไว้สำหรับมุมมองแบบไดนามิกเท่านั้น (เซลล์) และส่วนหัวที่คุณคิดว่าหมายถึงอย่างเห็นได้ชัด)
Julian F. Weinert
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.