Mở rộng / thu gọn phần trong UITableView trong iOS


113

Ai đó có thể cho tôi biết cách để thực hiện UITableViewmở rộng / hình ảnh động đóng mở trong sectionscủaUITableView như dưới đây?

hoặc là



Câu trả lời:


109

Bạn phải tạo hàng tiêu đề tùy chỉnh của riêng mình và đặt hàng đó làm hàng đầu tiên của mỗi phần. Việc phân lớp phụ UITableViewhoặc các tiêu đề đã có sẽ rất khó khăn. Dựa trên cách chúng hoạt động hiện nay, tôi không chắc bạn có thể dễ dàng thực hiện các hành động từ chúng. Bạn có thể thiết lập ô để XEM như tiêu đề và thiết lập ô tableView:didSelectRowAtIndexPathđể mở rộng hoặc thu gọn theo cách thủ công phần nó nằm trong đó.

Tôi sẽ lưu trữ một mảng boolean tương ứng với giá trị "đã sử dụng" của mỗi phần của bạn. Sau đó, bạn có thể có tableView:didSelectRowAtIndexPathtrên mỗi hàng tiêu đề tùy chỉnh của mình chuyển đổi giá trị này và sau đó tải lại phần cụ thể đó.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.row == 0) {
        ///it's the first row of any section so it would be your custom section header

        ///put in your code to toggle your boolean value here
        mybooleans[indexPath.section] = !mybooleans[indexPath.section];

        ///reload this section
        [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
    }
}

Sau đó, đặt numberOfRowsInSectionđể kiểm tra mybooleansgiá trị và trả về 1 nếu phần đó không được mở rộng hoặc 1+ số mục trong phần nếu nó được mở rộng.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    if (mybooleans[section]) {
        ///we want the number of people plus the header cell
        return [self numberOfPeopleInGroup:section] + 1;
    } else {
        ///we just want the header cell
        return 1;
    }
}

Ngoài ra, bạn sẽ cần cập nhật cellForRowAtIndexPathđể trả về ô tiêu đề tùy chỉnh cho hàng đầu tiên trong bất kỳ phần nào.


2
nếu bạn đã sử dụng ứng dụng Beejive, bạn sẽ biết rằng tiêu đề phần có thể thu gọn của chúng thực sự "nổi" ở đầu bảng ngay cả khi bạn đã cuộn qua một phần của phần của nó, giống như các tiêu đề phần thông thường của Apple. điều đó là không thể nếu bạn chỉ cần thêm một ô vào đầu phần
user102008

Giải pháp thanh lịch tốt đẹp! user102008 có một điểm trên tiêu đề nổi, nhưng trong trường hợp bạn thực sự muốn các "phần" cuộn, đây là một cách tiếp cận tuyệt vời.
Nick Cipollina

@mjdth làm ơn cho tôi biết bất kỳ mã mẫu nào bcz tôi cần ẩn / bỏ ẩn ô cụ thể .. cảm ơn trước
Bajaj

11
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)sectionlà cách tốt hơn để cung cấp "tiêu đề tùy chỉnh của riêng bạn", vì đó chính xác là những gì nó được thiết kế để làm.
William Denniss

điều này ban đầu hiệu quả với tôi khi tôi chỉ có một phần, nhưng ngay sau khi tôi có nhiều phần hơn, tôi nhận được lỗi "số hàng không hợp lệ cập nhật không hợp lệ". tôi biết giải pháp này cũ hơn, nhưng giải pháp này sẽ chỉ hoạt động cho một phần? Nếu chúng ta có nhiều hơn một phần, chúng ta có cần thêm mã thực sự thêm / xóa các hàng không ??
skinsfan00atg

103

Một số mã mẫu để tạo hoạt ảnh cho hành động mở rộng / thu gọn bằng cách sử dụng tiêu đề phần xem bảng được Apple cung cấp tại đây: Hoạt ảnh và cử chỉ của Chế độ xem bảng

Chìa khóa của cách tiếp cận này là triển khai - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)sectionvà trả về một UIView tùy chỉnh bao gồm một nút (thường có cùng kích thước với chính chế độ xem tiêu đề). Bằng cách phân lớp con UIView và sử dụng lớp đó cho dạng xem tiêu đề (như mẫu này), bạn có thể dễ dàng lưu trữ dữ liệu bổ sung như số phần.



không nhớ, nhưng tại sao mã mẫu không hoạt động trên iOS 4 trước?
samwize

1
tôi không biết. nó chỉ thông báo "iOS 4.0.2 trở lên"
user102008

1
Mã được cập nhật hiện tại tại liên kết có lỗi và có thể bị lỗi dễ dàng
Ankit Srivastava

1
Giống như Ankit Srivastava đã đề cập trước đây, thật dễ dàng để phá vỡ ví dụ mã này: chỉ cần sao chép và dán tất cả các từ điển mục trong danh sách PlaysAndQuotations.plist (tôi đã kiểm tra điều này với 30 mục trong từ điển gốc) - Bây giờ hãy khởi động ứng dụng và mở lần chơi đầu tiên - sau đó, bạn cuộn xuống cho đến khi bạn thấy một mũi tên trỏ xuống (tôi nghĩ điều này đến từ dequeueReusableHeaderFooterViewWithIdentifier) - nhấp vào mũi tên đó và cuộn trở lại lần phát đầu tiên và cố gắng đóng nó -> NSInternalInconsistencyException (iOS 8.4 / iPhone 5s)
Raimund Wege

22

Tôi đã có một giải pháp tuyệt vời lấy cảm hứng từ Hoạt ảnh và Cử chỉ của Chế độ xem bảng của Apple . Tôi đã xóa các phần không cần thiết khỏi mẫu của Apple và dịch nó thành nhanh chóng.

Tôi biết câu trả lời là khá dài, nhưng tất cả mã là cần thiết. May mắn thay, bạn chỉ có thể sao chép và dán hầu hết mã và chỉ cần sửa đổi một chút ở bước 1 và 3

1. tạo SectionHeaderView.swiftSectionHeaderView.xib

import UIKit

protocol SectionHeaderViewDelegate {
    func sectionHeaderView(sectionHeaderView: SectionHeaderView, sectionOpened: Int)
    func sectionHeaderView(sectionHeaderView: SectionHeaderView, sectionClosed: Int)
}

class SectionHeaderView: UITableViewHeaderFooterView {

    var section: Int?
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var disclosureButton: UIButton!
    @IBAction func toggleOpen() {
        self.toggleOpenWithUserAction(true)
    }
    var delegate: SectionHeaderViewDelegate?

    func toggleOpenWithUserAction(userAction: Bool) {
        self.disclosureButton.selected = !self.disclosureButton.selected

        if userAction {
            if self.disclosureButton.selected {
                self.delegate?.sectionHeaderView(self, sectionClosed: self.section!)
            } else {
                self.delegate?.sectionHeaderView(self, sectionOpened: self.section!)
            }
        }
    }

    override func awakeFromNib() {
        var tapGesture: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "toggleOpen")
        self.addGestureRecognizer(tapGesture)
        // change the button image here, you can also set image via IB.
        self.disclosureButton.setImage(UIImage(named: "arrow_up"), forState: UIControlState.Selected)
        self.disclosureButton.setImage(UIImage(named: "arrow_down"), forState: UIControlState.Normal)
    }

}

các SectionHeaderView.xib(quan điểm với nền màu xám) nên trông giống như thế này trong một tableview (bạn có thể tùy chỉnh nó theo nhu cầu của bạn, tất nhiên): nhập mô tả hình ảnh ở đây

Ghi chú:

a) toggleOpenhành động phải được liên kết vớidisclosureButton

b) disclosureButtontoggleOpen hành động không cần thiết. Bạn có thể xóa 2 thứ này nếu không cần nút.

2. tạo SectionInfo.swift

import UIKit

class SectionInfo: NSObject {
    var open: Bool = true
    var itemsInSection: NSMutableArray = []
    var sectionTitle: String?

    init(itemsInSection: NSMutableArray, sectionTitle: String) {
        self.itemsInSection = itemsInSection
        self.sectionTitle = sectionTitle
    }
}

3. trong chế độ xem bảng của bạn

import UIKit

class TableViewController: UITableViewController, SectionHeaderViewDelegate  {

    let SectionHeaderViewIdentifier = "SectionHeaderViewIdentifier"

    var sectionInfoArray: NSMutableArray = []

    override func viewDidLoad() {
        super.viewDidLoad()

        let sectionHeaderNib: UINib = UINib(nibName: "SectionHeaderView", bundle: nil)
        self.tableView.registerNib(sectionHeaderNib, forHeaderFooterViewReuseIdentifier: SectionHeaderViewIdentifier)

        // you can change section height based on your needs
        self.tableView.sectionHeaderHeight = 30

        // You should set up your SectionInfo here
        var firstSection: SectionInfo = SectionInfo(itemsInSection: ["1"], sectionTitle: "firstSection")
        var secondSection: SectionInfo = SectionInfo(itemsInSection: ["2"], sectionTitle: "secondSection"))
        sectionInfoArray.addObjectsFromArray([firstSection, secondSection])
    }

    // MARK: - Table view data source

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return sectionInfoArray.count
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if self.sectionInfoArray.count > 0 {
            var sectionInfo: SectionInfo = sectionInfoArray[section] as! SectionInfo
            if sectionInfo.open {
                return sectionInfo.open ? sectionInfo.itemsInSection.count : 0
            }
        }
        return 0
    }

    override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let sectionHeaderView: SectionHeaderView! = self.tableView.dequeueReusableHeaderFooterViewWithIdentifier(SectionHeaderViewIdentifier) as! SectionHeaderView
        var sectionInfo: SectionInfo = sectionInfoArray[section] as! SectionInfo

        sectionHeaderView.titleLabel.text = sectionInfo.sectionTitle
        sectionHeaderView.section = section
        sectionHeaderView.delegate = self
        let backGroundView = UIView()
        // you can customize the background color of the header here
        backGroundView.backgroundColor = UIColor(red:0.89, green:0.89, blue:0.89, alpha:1)
        sectionHeaderView.backgroundView = backGroundView
        return sectionHeaderView
    }

    func sectionHeaderView(sectionHeaderView: SectionHeaderView, sectionOpened: Int) {
        var sectionInfo: SectionInfo = sectionInfoArray[sectionOpened] as! SectionInfo
        var countOfRowsToInsert = sectionInfo.itemsInSection.count
        sectionInfo.open = true

        var indexPathToInsert: NSMutableArray = NSMutableArray()
        for i in 0..<countOfRowsToInsert {
            indexPathToInsert.addObject(NSIndexPath(forRow: i, inSection: sectionOpened))
        }
        self.tableView.insertRowsAtIndexPaths(indexPathToInsert as [AnyObject], withRowAnimation: .Top)
    }

    func sectionHeaderView(sectionHeaderView: SectionHeaderView, sectionClosed: Int) {
        var sectionInfo: SectionInfo = sectionInfoArray[sectionClosed] as! SectionInfo
        var countOfRowsToDelete = sectionInfo.itemsInSection.count
        sectionInfo.open = false
        if countOfRowsToDelete > 0 {
            var indexPathToDelete: NSMutableArray = NSMutableArray()
            for i in 0..<countOfRowsToDelete {
                indexPathToDelete.addObject(NSIndexPath(forRow: i, inSection: sectionClosed))
            }
            self.tableView.deleteRowsAtIndexPaths(indexPathToDelete as [AnyObject], withRowAnimation: .Top)
        }
    }
}

1
cảm ơn vì đã nỗ lực về điều này! Với một dự án mẫu nhỏ trên github nó sẽ là một câu trả lời tốt hơn
Max MacLeod

Cảm ơn bạn đã cung cấp câu trả lời chi tiết. Dự án mẫu sẽ tốt hơn.
Thiha Aung

20

Để triển khai phần bảng có thể thu gọn trong iOS, điều kỳ diệu là làm thế nào để kiểm soát số lượng hàng cho mỗi phần hoặc chúng ta có thể quản lý chiều cao của hàng cho mỗi phần.

Ngoài ra, chúng ta cần tùy chỉnh tiêu đề phần để chúng ta có thể nghe sự kiện nhấn từ khu vực tiêu đề (cho dù đó là một nút hay toàn bộ tiêu đề).

Làm thế nào để đối phó với tiêu đề? Rất đơn giản, chúng tôi mở rộng lớp UITableViewCell và tạo một ô tiêu đề tùy chỉnh như sau:

import UIKit

class CollapsibleTableViewHeader: UITableViewCell {

    @IBOutlet var titleLabel: UILabel!
    @IBOutlet var toggleButton: UIButton!

}

sau đó sử dụng viewForHeaderInSection để kết nối ô tiêu đề:

override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
  let header = tableView.dequeueReusableCellWithIdentifier("header") as! CollapsibleTableViewHeader

  header.titleLabel.text = sections[section].name
  header.toggleButton.tag = section
  header.toggleButton.addTarget(self, action: #selector(CollapsibleTableViewController.toggleCollapse), forControlEvents: .TouchUpInside)

  header.toggleButton.rotate(sections[section].collapsed! ? 0.0 : CGFloat(M_PI_2))

  return header.contentView
}

hãy nhớ rằng chúng ta phải trả về contentView vì hàm này mong đợi một UIView được trả về.

Bây giờ chúng ta hãy xử lý phần có thể thu gọn, đây là chức năng bật tắt bật tắt phần chống có thể thu gọn của từng phần:

func toggleCollapse(sender: UIButton) {
  let section = sender.tag
  let collapsed = sections[section].collapsed

  // Toggle collapse
  sections[section].collapsed = !collapsed

  // Reload section
  tableView.reloadSections(NSIndexSet(index: section), withRowAnimation: .Automatic)
}

phụ thuộc vào cách bạn quản lý dữ liệu phần, trong trường hợp này, tôi có dữ liệu phần như sau:

struct Section {
  var name: String!
  var items: [String]!
  var collapsed: Bool!

  init(name: String, items: [String]) {
    self.name = name
    self.items = items
    self.collapsed = false
  }
}

var sections = [Section]()

sections = [
  Section(name: "Mac", items: ["MacBook", "MacBook Air", "MacBook Pro", "iMac", "Mac Pro", "Mac mini", "Accessories", "OS X El Capitan"]),
  Section(name: "iPad", items: ["iPad Pro", "iPad Air 2", "iPad mini 4", "Accessories"]),
  Section(name: "iPhone", items: ["iPhone 6s", "iPhone 6", "iPhone SE", "Accessories"])
]

cuối cùng, những gì chúng ta cần làm là dựa vào giá đỡ có thể thu gọn của từng phần, kiểm soát số hàng của phần đó:

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  return (sections[section].collapsed!) ? 0 : sections[section].items.count
}

Tôi có một bản demo hoạt động đầy đủ trên Github của mình: https://github.com/jeantimex/ios-swift-collapsible-table-section

bản giới thiệu

Nếu bạn muốn triển khai các phần có thể thu gọn trong bảng kiểu được nhóm lại, tôi có một bản trình diễn khác với mã nguồn tại đây: https://github.com/jeantimex/ios-swift-collapsible-table-section-in-grouped-section

Hy vọng rằng sẽ giúp.


Xin chào, tôi đã thực hiện phần tiêu đề tùy chỉnh của mình trên tệp xib và đăng ký nib vào Bộ điều khiển Chế độ xem bảng của tôi. Khi tôi xóa một phần và cố gắng mở rộng / thu gọn lại, tôi gặp lỗi nghiêm trọng cho biết chỉ mục nằm ngoài phạm vi. Có cách nào để sửa lỗi này không? Cảm ơn!
iamhx

giải pháp rất đẹp và sạch sẽ!
Joel

10

Tôi có một giải pháp tốt hơn là bạn nên thêm UIButton vào tiêu đề phần và đặt kích thước của nút này bằng kích thước phần, nhưng làm cho nó ẩn bằng màu nền rõ ràng, sau đó bạn có thể dễ dàng kiểm tra phần nào được nhấp để mở rộng hoặc thu gọn


3
Theo ý kiến ​​của tôi, giải pháp này tốt hơn so với câu trả lời được chấp nhận, bởi vì về mặt ngữ nghĩa, bạn giữ tiêu đề của mình dưới dạng tiêu đề và bạn không sử dụng hàng giả để mô phỏng tiêu đề. Phương thức tableView:numberOfRowsInSection:sẽ không bị ảnh hưởng và bạn sẽ tiếp tục có thể sử dụng nó cho những gì nó thực sự có nghĩa. Cũng vậy tableView:cellForRowAtIndexPath:.
Cœur

Vì vậy, bạn nhấn vào nút trong tiêu đề phần, nhưng làm thế nào để xác định phần nào nên được tải lại?
memmons 16/10/13

@Answerbot Xin chào, Điều này cực kỳ dễ dàng bằng cách đặt thẻ cho nút bằng cách sử dụng cùng một giá trị với chỉ mục phần.
Son Nguyen

Tôi sợ bạn sẽ nói điều đó. Việc lạm dụng thuộc tính thẻ cho những thứ như chỉ mục tableView là một lựa chọn thiết kế kém.
memmons

Tôi chưa bao giờ thấy bất kỳ giải pháp "tuyệt vời" nào cho vấn đề, đó là lý do tại sao tôi hy vọng bạn có một cách tiếp cận khác. Câu trả lời tốt nhất mà tôi đã thấy là dự án tham chiếu của Apple. Apple phân lớp a UITableViewHeaderFooterViewvà thêm một thuộc sectiontính và định nghĩa a SectionHeaderViewDelegatecung cấp lệnh gọi lại để mở / đóng phần. ( developer.apple.com/library/ios/samplecode/TableViewUpdates/… )
memmons

7

Cuối cùng tôi chỉ tạo một headerView có chứa một nút (tôi đã xem giải pháp của Sơn Nguyễn ở trên, nhưng lỗi mã của tôi .. trông thì có vẻ nhiều nhưng nó khá đơn giản):

khai báo một vài bools cho bạn phần

bool customerIsCollapsed = NO;
bool siteIsCollapsed = NO;

... mã

bây giờ trong các phương pháp đại biểu tableview của bạn ...

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, _tblSearchResults.frame.size.width, 35)];

    UILabel *lblSection = [UILabel new];
    [lblSection setFrame:CGRectMake(0, 0, 300, 30)];
    [lblSection setFont:[UIFont fontWithName:@"Helvetica-Bold" size:17]];
    [lblSection setBackgroundColor:[UIColor clearColor]];
    lblSection.alpha = 0.5;
    if(section == 0)
    {
        if(!customerIsCollapsed)
            [lblSection setText:@"Customers    --touch to show--"];
        else
            [lblSection setText:@"Customers    --touch to hide--"];
    }
    else
    {
        if(!siteIsCollapsed)
            [lblSection setText:@"Sites    --touch to show--"];
        else
            [lblSection setText:@"Sites    --touch to hide--"];    }

    UIButton *btnCollapse = [UIButton buttonWithType:UIButtonTypeCustom];
    [btnCollapse setFrame:CGRectMake(0, 0, _tblSearchResults.frame.size.width, 35)];
    [btnCollapse setBackgroundColor:[UIColor clearColor]];
    [btnCollapse addTarget:self action:@selector(touchedSection:) forControlEvents:UIControlEventTouchUpInside];
    btnCollapse.tag = section;


    [headerView addSubview:lblSection];
    [headerView addSubview:btnCollapse];

    return headerView;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    if(section == 0)
    {
        if(customerIsCollapsed)
            return 0;
        else
            return _customerArray.count;
    }
    else if (section == 1)
    {
        if(siteIsCollapsed)
            return 0;
        else
        return _siteArray.count;

    }
    return 0;
}

và cuối cùng là hàm được gọi khi bạn chạm vào một trong các nút tiêu đề phần:

- (IBAction)touchedSection:(id)sender
{
    UIButton *btnSection = (UIButton *)sender;

    if(btnSection.tag == 0)
    {
        NSLog(@"Touched Customers header");
        if(!customerIsCollapsed)
            customerIsCollapsed = YES;
        else
            customerIsCollapsed = NO;

    }
    else if(btnSection.tag == 1)
    {
        NSLog(@"Touched Site header");
        if(!siteIsCollapsed)
            siteIsCollapsed = YES;
        else
            siteIsCollapsed = NO;

    }
    [_tblSearchResults reloadData];
}

Tôi chỉ tự hỏi, ướt hơn, phần này sẽ thu gọn và mở rộng hoạt ảnh hoặc không có hoạt ảnh. Nếu không có hoạt ảnh nó sẽ trông rất tệ. làm thế nào chúng ta có thể thêm hoạt ảnh vào nó?
Sam

@Sam nếu bạn sử dụng một cái gì đó giống như [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];trong phương pháp thu gọn / mở rộng, nó sẽ hoạt hình độc đáo.
William Denniss

5

Đây là cách tốt nhất tôi tìm thấy để tạo các ô xem bảng có thể mở rộng

tệp .h

  NSMutableIndexSet *expandedSections;

tệp .m

if (!expandedSections)
    {
        expandedSections = [[NSMutableIndexSet alloc] init];
    }
   UITableView *masterTable = [[UITableView alloc] initWithFrame:CGRectMake(0,100,1024,648) style:UITableViewStyleGrouped];
    masterTable.delegate = self;
    masterTable.dataSource = self;
    [self.view addSubview:masterTable];

Phương pháp đại biểu chế độ xem bảng

- (BOOL)tableView:(UITableView *)tableView canCollapseSection:(NSInteger)section
{
    // if (section>0) return YES;

    return YES;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 4;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if ([self tableView:tableView canCollapseSection:section])
    {
        if ([expandedSections containsIndex:section])
        {
            return 5; // return rows when expanded
        }

        return 1; // only top row showing
    }

    // Return the number of rows in the section.
    return 1;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] ;
    }

    // Configure the cell...

    if ([self tableView:tableView canCollapseSection:indexPath.section])
    {
        if (!indexPath.row)
        {
            // first row
            cell.textLabel.text = @"Expandable"; // only top row showing

            if ([expandedSections containsIndex:indexPath.section])
            {

                UIImageView *imView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"UITableContract"]];
                cell.accessoryView = imView;
            }
            else
            {

                UIImageView *imView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"UITableExpand"]];
                cell.accessoryView = imView;
            }
        }
        else
        {
            // all other rows
            if (indexPath.section == 0) {
                cell.textLabel.text = @"section one";
            }else if (indexPath.section == 1) {
                cell.textLabel.text = @"section 2";
            }else if (indexPath.section == 2) {
                cell.textLabel.text = @"3";
            }else {
                cell.textLabel.text = @"some other sections";
            }

            cell.accessoryView = nil;
            cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
        }
    }
    else
    {
        cell.accessoryView = nil;
        cell.textLabel.text = @"Normal Cell";

    }

    return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if ([self tableView:tableView canCollapseSection:indexPath.section])
    {
        if (!indexPath.row)
        {
            // only first row toggles exapand/collapse
            [tableView deselectRowAtIndexPath:indexPath animated:YES];

            NSInteger section = indexPath.section;
            BOOL currentlyExpanded = [expandedSections containsIndex:section];
            NSInteger rows;


            NSMutableArray *tmpArray = [NSMutableArray array];

            if (currentlyExpanded)
            {
                rows = [self tableView:tableView numberOfRowsInSection:section];
                [expandedSections removeIndex:section];

            }
            else
            {
                [expandedSections addIndex:section];
                rows = [self tableView:tableView numberOfRowsInSection:section];
            }


            for (int i=1; i<rows; i++)
            {
                NSIndexPath *tmpIndexPath = [NSIndexPath indexPathForRow:i 
                                                               inSection:section];
                [tmpArray addObject:tmpIndexPath];
            }

            UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

            if (currentlyExpanded)
            {
                [tableView deleteRowsAtIndexPaths:tmpArray 
                                 withRowAnimation:UITableViewRowAnimationTop];

                UIImageView *imView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"UITableExpand"]];
                cell.accessoryView = imView;
            }
            else
            {
                [tableView insertRowsAtIndexPaths:tmpArray 
                                 withRowAnimation:UITableViewRowAnimationTop];

                UIImageView *imView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"UITableContract"]];
                cell.accessoryView = imView;
            }
        }
    }

    NSLog(@"section :%d,row:%d",indexPath.section,indexPath.row);

}

8
Bạn có thể nên gắn cờ các câu hỏi là trùng lặp chính xác thay vì chỉ gửi spam cùng một câu trả lời cho tất cả chúng.
casperMột

nếu một phần đã được mở rộng và phần khác được nhấp, nó mang lại cho lỗi
Shivam

chào bạn, Chỉ mục đã chọn có thể thay đổi như thế nào? heightForRowAtIndexPath làm thế nào để làm việc với mã của bạn?
Gami Nilesh

xin chào, làm thế nào để điều hướng đến bộ điều khiển chế độ xem khác trên đã chọn hàng mở rộng?
Arbaz Shaikh

1

Vì vậy, dựa trên giải pháp 'nút trong tiêu đề', đây là một cách triển khai gọn gàng và tối giản:

  • bạn theo dõi các phần được thu gọn (hoặc mở rộng) trong một thuộc tính
  • bạn gắn thẻ nút với chỉ mục phần
  • bạn đặt trạng thái đã chọn trên nút đó để thay đổi hướng mũi tên (như △ và ▽)

Đây là mã:

@interface MyTableViewController ()
@property (nonatomic, strong) NSMutableIndexSet *collapsedSections;
@end

...

@implementation MyTableViewController

- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (!self)
        return;
    self.collapsedSections = [NSMutableIndexSet indexSet];
    return self;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // if section is collapsed
    if ([self.collapsedSections containsIndex:section])
        return 0;

    // if section is expanded
#warning incomplete implementation
    return [super tableView:tableView numberOfRowsInSection:section];
}

- (IBAction)toggleSectionHeader:(UIView *)sender
{
    UITableView *tableView = self.tableView;
    NSInteger section = sender.tag;

    MyTableViewHeaderFooterView *headerView = (MyTableViewHeaderFooterView *)[self tableView:tableView viewForHeaderInSection:section];

    if ([self.collapsedSections containsIndex:section])
    {
        // section is collapsed
        headerView.button.selected = YES;
        [self.collapsedSections removeIndex:section];
    }
    else
    {
        // section is expanded
        headerView.button.selected = NO;
        [self.collapsedSections addIndex:section];
    }

    [tableView beginUpdates];
    [tableView reloadSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:UITableViewRowAnimationAutomatic];
    [tableView endUpdates];
}

@end

1

Tôi đã tìm thấy một cách khác tương đối đơn giản để giải quyết vấn đề đó. Bằng cách sử dụng phương pháp này, chúng tôi sẽ không bắt buộc phải thay đổi ô của chúng tôi mà hầu như luôn liên quan đến chỉ mục mảng dữ liệu, có khả năng gây ra sự lộn xộn trong bộ điều khiển chế độ xem của chúng tôi.

Đầu tiên, chúng tôi thêm các thuộc tính sau vào lớp bộ điều khiển của chúng tôi:

@property (strong, nonatomic) NSMutableArray* collapsedSections;
@property (strong, nonatomic) NSMutableArray* sectionViews;

collapsedSectionssẽ lưu số phần đã thu gọn. sectionViewssẽ lưu trữ chế độ xem phần tùy chỉnh của chúng tôi.

Tổng hợp nó:

@synthesize collapsedSections;
@synthesize sectionViews;

Khởi tạo nó:

- (void) viewDidLoad
{
    [super viewDidLoad];

    self.collapsedSections = [NSMutableArray array];
    self.sectionViews      = [NSMutableArray array];
}

Sau đó, chúng ta phải kết nối UITableView của mình để nó có thể được truy cập từ bên trong lớp bộ điều khiển chế độ xem của chúng ta:

@property (strong, nonatomic) IBOutlet UITableView *tblMain;

Kết nối nó từ XIB để xem bộ điều khiển bằng cách sử dụng bình ctrl + dragthường.

Sau đó, chúng tôi tạo chế độ xem dưới dạng tiêu đề phần tùy chỉnh cho chế độ xem bảng của chúng tôi bằng cách triển khai đại biểu UITableView này:

- (UIView*) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    // Create View
    CGRect frame = CGRectZero;

    frame.origin = CGPointZero;

    frame.size.height = 30.f;
    frame.size.width  = tableView.bounds.size.width;

    UIView* view = [[UIView alloc] initWithFrame:frame];

    [view setBackgroundColor:[UIColor blueColor]];

    // Add label for title
    NSArray* titles = @[@"Title 1", @"Title 2", @"Title 3"];

    NSString* selectedTitle = [titles objectAtIndex:section];

    CGRect labelFrame = frame;

    labelFrame.size.height = 30.f;
    labelFrame.size.width -= 20.f;
    labelFrame.origin.x += 10.f;

    UILabel* titleLabel = [[UILabel alloc] initWithFrame:labelFrame];

    [titleLabel setText:selectedTitle];
    [titleLabel setTextColor:[UIColor whiteColor]];

    [view addSubview:titleLabel];

    // Add touch gesture
    [self attachTapGestureToView:view];

    // Save created view to our class property array
    [self saveSectionView:view inSection:section];

    return view;
}

Tiếp theo, chúng tôi triển khai phương thức để lưu tiêu đề phần tùy chỉnh đã tạo trước đó của chúng tôi trong thuộc tính lớp:

- (void) saveSectionView:(UIView*) view inSection:(NSInteger) section
{
    NSInteger sectionCount = [self numberOfSectionsInTableView:[self tblMain]];

    if(section < sectionCount)
    {
        if([[self sectionViews] indexOfObject:view] == NSNotFound)
        {
            [[self sectionViews] addObject:view];
        }
    }
}

Thêm vào UIGestureRecognizerDelegatetệp .h của bộ điều khiển chế độ xem của chúng tôi:

@interface MyViewController : UIViewController<UITableViewDelegate, UITableViewDataSource, UIGestureRecognizerDelegate>

Sau đó, chúng tôi tạo phương pháp attachTapGestureToView:

- (void) attachTapGestureToView:(UIView*) view
{
    UITapGestureRecognizer* tapAction = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTap:)];

    [tapAction setDelegate:self];

    [view addGestureRecognizer:tapAction];
}

Phương pháp trên sẽ thêm trình nhận dạng cử chỉ chạm vào tất cả chế độ xem phần mà chúng tôi đã tạo trước đây. Tiếp theo, chúng ta nên triển khai onTap:bộ chọn

- (void) onTap:(UITapGestureRecognizer*) gestureRecognizer
{
    // Take view who attach current recognizer
    UIView* sectionView = [gestureRecognizer view]; 

    // [self sectionViews] is Array containing our custom section views
    NSInteger section = [self sectionNumberOfView:sectionView];

    // [self tblMain] is our connected IBOutlet table view
    NSInteger sectionCount = [self numberOfSectionsInTableView:[self tblMain]];

    // If section more than section count minus one set at last
    section = section > (sectionCount - 1) ? 2 : section;

    [self toggleCollapseSection:section];
}

Phương thức trên sẽ được gọi khi người dùng chạm vào bất kỳ phần nào trong chế độ xem bảng của chúng tôi. Phương pháp này tìm kiếm số phần chính xác dựa trênsectionViews mảng của chúng tôi mà chúng tôi đã tạo trước đó.

Ngoài ra, chúng tôi thực hiện phương pháp để có được phần wihch của chế độ xem tiêu đề thuộc về.

- (NSInteger) sectionNumberOfView:(UIView*) view
{
    UILabel* label = [[view subviews] objectAtIndex:0];

    NSInteger sectionNum = 0;

    for(UIView* sectionView in [self sectionViews])
    {
        UILabel* sectionLabel = [[sectionView subviews] objectAtIndex:0];

        //NSLog(@"Section: %d -> %@ vs %@", sectionNum, [label text], [sectionLabel text]);

        if([[label text] isEqualToString:[sectionLabel text]])
        {
            return sectionNum;
        }

        sectionNum++;
    }

    return NSNotFound;
}

Tiếp theo, chúng ta phải triển khai phương thức toggleCollapseSection:

- (void) toggleCollapseSection:(NSInteger) section
{
    if([self isCollapsedSection:section])
    {
        [self removeCollapsedSection:section];
    }
    else
    {
        [self addCollapsedSection:section];
    }

    [[self tblMain] reloadSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:UITableViewRowAnimationFade];
}

Phương thức này sẽ chèn / xóa số phần vào collapsedSectionsmảng mà chúng ta đã tạo trước đó. Khi một số phần được chèn vào mảng đó, điều đó có nghĩa là phần đó phải được thu gọn và mở rộng nếu khác.

Tiếp theo chúng ta thực hiện removeCollapsedSection:, addCollapsedSection:sectionisCollapsedSection:section

- (BOOL)isCollapsedSection:(NSInteger) section
{
    for(NSNumber* existing in [self collapsedSections])
    {
        NSInteger current = [existing integerValue];

        if(current == section)
        {
            return YES;
        }
    }

    return NO;
}

- (void)removeCollapsedSection:(NSInteger) section
{
    [[self collapsedSections] removeObjectIdenticalTo:[NSNumber numberWithInteger:section]];
}

- (void)addCollapsedSection:(NSInteger) section
{
    [[self collapsedSections] addObject:[NSNumber numberWithInteger:section]];
}

Ba phương thức này chỉ là những trợ giúp giúp chúng ta truy cập collapsedSectionsmảng dễ dàng hơn .

Cuối cùng, triển khai ủy quyền dạng xem bảng này để các dạng xem phần tùy chỉnh của chúng ta trông đẹp mắt.

- (CGFloat) tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    return 30.f; // Same as each custom section view height
}

Hy vọng nó giúp.


1

Tôi đã sử dụng NSDictionary làm nguồn dữ liệu, điều này trông giống như rất nhiều mã, nhưng nó thực sự đơn giản và hoạt động rất tốt! ở đây trông như thế nào

Tôi đã tạo một enum cho các phần

typedef NS_ENUM(NSUInteger, TableViewSection) {

    TableViewSection0 = 0,
    TableViewSection1,
    TableViewSection2,
    TableViewSectionCount
};

thuộc tính phần:

@property (nonatomic, strong) NSMutableDictionary * sectionsDisctionary;

Một phương pháp trả về các phần của tôi:

-(NSArray <NSNumber *> * )sections{

    return @[@(TableViewSection0), @(TableViewSection1), @(TableViewSection2)];
}

Và sau đó thiết lập dữ liệu của tôi soruce:

-(void)loadAndSetupData{

    self.sectionsDisctionary = [NSMutableDictionary dictionary];

    NSArray * sections = [self sections];

    for (NSNumber * section in sections) {

    NSArray * sectionObjects = [self objectsForSection:section.integerValue];

    [self.sectionsDisctionary setObject:[NSMutableDictionary dictionaryWithDictionary:@{@"visible" : @YES, @"objects" : sectionObjects}] forKey:section];
    }
}

-(NSArray *)objectsForSection:(NSInteger)section{

    NSArray * objects;

    switch (section) {

        case TableViewSection0:

            objects = @[] // objects for section 0;
            break;

        case TableViewSection1:

            objects = @[] // objects for section 1;
            break;

        case TableViewSection2:

            objects = @[] // objects for section 2;
            break;

        default:
            break;
    }

    return objects;
}

Các phương pháp tiếp theo sẽ giúp bạn biết khi nào một phần được mở và cách phản hồi nguồn dữ liệu của tableview:

Trả lời phần này cho nguồn dữ liệu:

/**
 *  Asks the delegate for a view object to display in the header of the specified section of the table view.
 *
 *  @param tableView The table-view object asking for the view object.
 *  @param section   An index number identifying a section of tableView .
 *
 *  @return A view object to be displayed in the header of section .
 */
- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{

    NSString * headerName = [self titleForSection:section];

    YourCustomSectionHeaderClass * header = (YourCustomSectionHeaderClass *)[tableView dequeueReusableHeaderFooterViewWithIdentifier:YourCustomSectionHeaderClassIdentifier];

    [header setTag:section];
    [header addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)]];
    header.title = headerName;
    header.collapsed = [self sectionIsOpened:section];


    return header;
}

/**
 * Asks the data source to return the number of sections in the table view
 *
 * @param An object representing the table view requesting this information.
 * @return The number of sections in tableView.
 */
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    // Return the number of sections.

    return self.sectionsDisctionary.count;
}

/**
 * Tells the data source to return the number of rows in a given section of a table view
 *
 * @param tableView: The table-view object requesting this information.
 * @param section: An index number identifying a section in tableView.
 * @return The number of rows in section.
 */
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{

    BOOL sectionOpened = [self sectionIsOpened:section];
    return sectionOpened ? [[self objectsForSection:section] count] : 0;
}

Công cụ:

/**
 Return the section at the given index

 @param index the index

 @return The section in the given index
 */
-(NSMutableDictionary *)sectionAtIndex:(NSInteger)index{

    NSString * asectionKey = [self.sectionsDisctionary.allKeys objectAtIndex:index];

    return [self.sectionsDisctionary objectForKey:asectionKey];
}

/**
 Check if a section is currently opened

 @param section the section to check

 @return YES if is opened
 */
-(BOOL)sectionIsOpened:(NSInteger)section{

    NSDictionary * asection = [self sectionAtIndex:section];
    BOOL sectionOpened = [[asection objectForKey:@"visible"] boolValue];

    return sectionOpened;
}


/**
 Handle the section tap

 @param tap the UITapGestureRecognizer
 */
- (void)handleTapGesture:(UITapGestureRecognizer*)tap{

    NSInteger index = tap.view.tag;

    [self toggleSection:index];
}

Chuyển đổi chế độ hiển thị phần

/**
 Switch the state of the section at the given section number

 @param section the section number
 */
-(void)toggleSection:(NSInteger)section{

    if (index >= 0){

        NSMutableDictionary * asection = [self sectionAtIndex:section];

        [asection setObject:@(![self sectionIsOpened:section]) forKey:@"visible"];

        [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:UITableViewRowAnimationFade];
    }
}

0
// -------------------------------------------------------------------------------
//  tableView:viewForHeaderInSection:
// -------------------------------------------------------------------------------
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {

    UIView *mView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 20, 20)];
    [mView setBackgroundColor:[UIColor greenColor]];

    UIImageView *logoView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 5, 20, 20)];
    [logoView setImage:[UIImage imageNamed:@"carat.png"]];
    [mView addSubview:logoView];

    UIButton *bt = [UIButton buttonWithType:UIButtonTypeCustom];
    [bt setFrame:CGRectMake(0, 0, 150, 30)];
    [bt setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
    [bt setTag:section];
    [bt.titleLabel setFont:[UIFont systemFontOfSize:20]];
    [bt.titleLabel setTextAlignment:NSTextAlignmentCenter];
    [bt.titleLabel setTextColor:[UIColor blackColor]];
    [bt setTitle: @"More Info" forState: UIControlStateNormal];
    [bt addTarget:self action:@selector(addCell:) forControlEvents:UIControlEventTouchUpInside];
    [mView addSubview:bt];
    return mView;

}

#pragma mark - Suppose you want to hide/show section 2... then
#pragma mark  add or remove the section on toggle the section header for more info

- (void)addCell:(UIButton *)bt{

    // If section of more information
    if(bt.tag == 2) {

        // Initially more info is close, if more info is open
        if(ifOpen) {
            DLog(@"close More info");

            // Set height of section
            heightOfSection = 0.0f;

            // Reset the parameter that more info is closed now
            ifOpen = NO;
        }else {
            // Set height of section
            heightOfSection = 45.0f;
            // Reset the parameter that more info is closed now
            DLog(@"open more info again");
            ifOpen = YES;
        }
        //[self.tableView reloadData];
        [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:2] withRowAnimation:UITableViewRowAnimationFade];
    }

}// end addCell
#pragma mark -
#pragma mark  What will be the height of the section, Make it dynamic

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{

    if (indexPath.section == 2) {
        return heightOfSection;
    }else {
        return 45.0f;
    }

// vKj


0
This action will happen in your didSelectRowAtIndexPath, when you will try to hide or show number of cell in a  section

first of all declare a global variable numberOfSectionInMoreInfo in .h file and in your viewDidLoad set suppose to numberOfSectionInMoreInfo = 4.

Now use following logic: 


 // More info link
        if(row == 3) {

            /*Logic: We are trying to hide/show the number of row into more information section */

            NSString *log= [NSString stringWithFormat:@"Number of section in more %i",numberOfSectionInMoreInfo];

            [objSpineCustomProtocol showAlertMessage:log];

            // Check if the number of rows are open or close in view
            if(numberOfSectionInMoreInfo > 4) {

                // close the more info toggle
                numberOfSectionInMoreInfo = 4;

            }else {

                // Open more info toggle
                numberOfSectionInMoreInfo = 9;

            }

            //reload this section
            [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationFade];

// vKj


Tại sao hai câu trả lời? Có vẻ như bạn đã không cung cấp hai giải pháp khác nhau cho vấn đề.
Cristik

0

Mở rộng câu trả lời này được viết bằng Objective C, tôi đã viết phần sau cho những câu viết bằng Swift

Ý tưởng là sử dụng các phần trong bảng và đặt số hàng trong phần thành 1 (thu gọn) và 3 (mở rộng) khi nhấn vào hàng đầu tiên trong phần đó

Bảng quyết định có bao nhiêu hàng để vẽ dựa trên một mảng các giá trị Boolean

Bạn sẽ cần tạo hai hàng trong bảng phân cảnh và cung cấp cho chúng các số nhận dạng tái sử dụng 'CollapsingRow' và 'GroupHeading'

import UIKit

class CollapsingTVC:UITableViewController{

    var sectionVisibilityArray:[Bool]!// Array index corresponds to section in table

    override func viewDidLoad(){
        super.viewDidLoad()
        sectionVisibilityArray = [false,false,false]
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
    }

    override func numberOfSections(in tableView: UITableView) -> Int{
        return sectionVisibilityArray.count
    }
    override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat{
        return 0
    }

    // numberOfRowsInSection - Get count of entries
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        var rowsToShow:Int = 0
        if(sectionVisibilityArray[section]){
            rowsToShow = 3 // Or however many rows should be displayed in that section
        }else{
            rowsToShow = 1
        }
        return rowsToShow
    }// numberOfRowsInSection


    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
        if(indexPath.row == 0){
            if(sectionVisibilityArray[indexPath.section]){
                sectionVisibilityArray[indexPath.section] = false
            }else{
                sectionVisibilityArray[indexPath.section] = true
            }
            self.tableView.reloadSections([indexPath.section], with: .automatic)
        }
    }

    // cellForRowAtIndexPath - Get table cell corresponding to this IndexPath
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        var cell:UITableViewCell

        if(indexPath.row == 0){
             cell = tableView.dequeueReusableCell(withIdentifier: "GroupHeading", for: indexPath as IndexPath)
        }else{
            cell = tableView.dequeueReusableCell(withIdentifier: "CollapsingRow", for: indexPath as IndexPath)
        }

        return cell

    }// cellForRowAtIndexPath

}

0

Một số mã mẫu để tạo hoạt ảnh cho một hành động mở rộng / thu gọn bằng cách sử dụng tiêu đề phần xem bảng được Apple cung cấp tại Table View Animations and Gestures .

Chìa khóa của phương pháp này là thực hiện

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section

và trả về một UIView tùy chỉnh bao gồm một nút (thường có cùng kích thước với chính chế độ xem tiêu đề). Bằng cách phân lớp phụ UIView và sử dụng nó cho dạng xem tiêu đề (như mẫu này), bạn có thể dễ dàng lưu trữ dữ liệu bổ sung như số phần.


0

Tôi đã làm điều tương tự bằng cách sử dụng nhiều phần.

class SCTierBenefitsViewController: UIViewController {
    @IBOutlet private weak var tblTierBenefits: UITableView!
    private var selectedIndexPath: IndexPath?
    private var isSelected:Bool = false

    override func viewDidLoad() {
        super.viewDidLoad()

        tblTierBenefits.register(UINib(nibName:"TierBenefitsTableViewCell", bundle: nil), forCellReuseIdentifier:"TierBenefitsTableViewCell")
        tblTierBenefits.register(UINib(nibName:"TierBenefitsDetailsCell", bundle: nil), forCellReuseIdentifier:"TierBenefitsDetailsCell")

        tblTierBenefits.rowHeight = UITableViewAutomaticDimension;
        tblTierBenefits.estimatedRowHeight = 44.0;
        tblTierBenefits.tableFooterView = UIView()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

}

extension SCTierBenefitsViewController : UITableViewDataSource{

    func numberOfSections(in tableView: UITableView) -> Int {
        return 7
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return (isSelected && section == selectedIndexPath?.section) ? 2 : 1 
    }

    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return  0.01
    }

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        return nil
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        switch indexPath.row {
        case 0:
            let cell:TierBenefitsTableViewCell = tableView.dequeueReusableCell(withIdentifier: "TierBenefitsTableViewCell")! as! TierBenefitsTableViewCell
            cell.selectionStyle = .none
            cell.contentView.setNeedsLayout()
            cell.contentView.layoutIfNeeded()
            return cell

        case 1:
            let cell:TierBenefitsDetailsCell = tableView.dequeueReusableCell(withIdentifier: "TierBenefitsDetailsCell")! as! TierBenefitsDetailsCell
            cell.selectionStyle = .none
            return cell

        default:
            break
        }

        return UITableViewCell()
    }
}

extension SCTierBenefitsViewController : UITableViewDelegate{

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if indexPath.row == 0 {

            if let _selectedIndexPath = selectedIndexPath ,selectedIndexPath?.section == indexPath.section {
                tblTierBenefits.beginUpdates()
                expandCollapse(indexPath: _selectedIndexPath, isExpand: false)
                selectedIndexPath = nil
            }
            else{
                tblTierBenefits.beginUpdates()
                if selectedIndexPath != nil {
                    tblTierBenefits.reloadSections([(selectedIndexPath?.section)!], with: .none)
                }
                expandCollapse(indexPath: indexPath, isExpand: true)
            }
        }
    }

    private func  expandCollapse(indexPath: IndexPath?,isExpand: Bool){
        isSelected = isExpand
        selectedIndexPath = indexPath
        tblTierBenefits.reloadSections([(indexPath?.section)!], with: .none)
        tblTierBenefits.endUpdates()
    }

}

0

Tôi đang thêm giải pháp này để hoàn thiện và chỉ ra cách làm việc với tiêu đề phần.

import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    @IBOutlet var tableView: UITableView!
    var headerButtons: [UIButton]!
    var sections = [true, true, true]

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.dataSource = self
        tableView.delegate = self

        let section0Button = UIButton(type: .detailDisclosure)
        section0Button.setTitle("Section 0", for: .normal)
        section0Button.addTarget(self, action: #selector(section0Tapped), for: .touchUpInside)

        let section1Button = UIButton(type: .detailDisclosure)
        section1Button.setTitle("Section 1", for: .normal)
        section1Button.addTarget(self, action: #selector(section1Tapped), for: .touchUpInside)

        let section2Button = UIButton(type: .detailDisclosure)
        section2Button.setTitle("Section 2", for: .normal)
        section2Button.addTarget(self, action: #selector(section2Tapped), for: .touchUpInside)

        headerButtons = [UIButton]()
        headerButtons.append(section0Button)
        headerButtons.append(section1Button)
        headerButtons.append(section2Button)
    }

    func numberOfSections(in tableView: UITableView) -> Int {
        return sections.count
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return sections[section] ? 3 : 0
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cellReuseId = "cellReuseId"
        let cell = UITableViewCell(style: .default, reuseIdentifier: cellReuseId)
        cell.textLabel?.text = "\(indexPath.section): \(indexPath.row)"
        return cell
    }

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        return headerButtons[section]
    }

    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 44
    }

    @objc func section0Tapped() {
        sections[0] = !sections[0]
        tableView.reloadSections([0], with: .fade)
    }

    @objc func section1Tapped() {
        sections[1] = !sections[1]
        tableView.reloadSections([1], with: .fade)
    }

    @objc func section2Tapped() {
        sections[2] = !sections[2]
        tableView.reloadSections([2], with: .fade)
    }

}

Liên kết đến ý chính: https://gist.github.com/pawelkijowskizimperium/fe1e8511a7932a0d40486a2669316d2c


0

trong hỗ trợ cho giải pháp @ jean.timex, hãy sử dụng mã bên dưới nếu bạn muốn mở một phần bất kỳ lúc nào. tạo một biến như: var expandSection = -1;

func toggleSection(_ header: CollapsibleTableViewHeader, section: Int) {
    let collapsed = !sections[section].collapsed
    // Toggle collapse
    sections[section].collapsed = collapsed
    header.setCollapsed(collapsed)
    tableView.reloadSections(NSIndexSet(index: section) as IndexSet, with: .automatic)
    if (expandedSection >= 0 && expandedSection != section){
        sections[expandedSection].collapsed = true
        tableView.reloadSections(NSIndexSet(index: expandedSection) as IndexSet, with: .automatic)
    }
    expandedSection = section;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.