2011-01-08 87 views
50

擴大比方說,我們有一個自定義的UITableViewCell的UITableViewCell上點擊

所以每當我單擊單元格的自定義按鈕..它應擴大到一定程度(你可以說40高度更多...),當我點擊再次到相同的自定義按鈕,它應該摺疊到先前的高度。

開發者請指導我..我怎樣才能實現這個任務

+0

注意:如果你想擴展區域包含新的可點擊的細胞(行)(而不僅僅是擴大爲單擊的單元格的空間),參見[gcamp的答案](http://stackoverflow.com/a/ 199364分之7253288)。它開始於一組關閉的*部分*;點擊部分標題打開該部分,顯示其單元格。 – ToolmakerSteve 2017-03-12 01:30:00

回答

65

執行heightForRowAtIndexPath來計算正確的高度。然後在您的按鈕的代碼,迫使表重新評估每個單元的高度與beginUpdates加endUpdates:

[self.tableView beginUpdates]; 
[self.tableView endUpdates]; 

更改到的tableview細胞的高度會自動heightForRowAtIndexPath計算和變化也將被動畫。

實際上,您可能會選擇單元格而不是您的單元格上的按鈕,而不是在didSelectRowAtIndexPath中執行此操作。

8

我做了一個可重用的組件,將做你在談論什麼。這很容易使用,並有一個演示項目。

GCRetractableSectionController在GitHub上。

+0

這正是我所需要的。 UITableviewCell中的UITableview。完善。謝謝。 :) – ScarletWitch 2015-10-29 07:57:24

+0

對我來說,這是完美的解決方案 - 我想知道如何讓擴大的區域包含更多可點擊的單元行,而不僅僅是一個單元格。 – ToolmakerSteve 2017-03-12 01:25:09

2

我使用了Gcamp的源代碼並創建了自己的版本。

1)在loadView方法中,初始化一個可變數組,您將在其中保存段的展開或非展開狀態。將擴展狀態保存在單獨的數組中是非常重要的,當表視圖滾動時不會破壞它們(例如,如果將它存儲在headerView中,它將被重繪,並且忘記它展開或不展開的天氣)。在我的情況下,它是_sectionStatuses數組。

- (void)loadView 
{ 
    // At the beginning all sections are expanded 
    _sectionStates = [NSMutableArray arrayWithCapacity:self.tableView.numberOfSections]; 
    for (int i = 0; i < self.tableView.numberOfSections; i++) { 
     _sectionStates[i] = [NSNumber numberWithBool:YES]; 
    } 
} 

2)爲具有用於展開的按鈕的部分創建自定義headerView。使用委託模式將您的headerView中按鈕的動作委託給TableViewController。您可以在Gcamp的源代碼中找到合適的圖像。

3)創建一個操作來刪除或添加行。這裏_foldersArray是我的結構,它包含所有的數據。我的部分的headerView - MCExpandableAccountHeaderView知道它是自己的部分編號 - 當我爲每個部分創建標題視圖時,我將其轉移到那裏。將它轉移到此方法非常重要,因爲您必須知道哪個節現在被展開或拉伸。

- (void)expandClicked:(MCAccountHeaderView *)sender 
{ 
MCExpandableAccountHeaderView *expandableAccountHeaderView = (MCExpandableAccountHeaderView*)sender; 

// Finding a section, where a button was tapped 
NSInteger section = expandableAccountHeaderView.section; 

// Number of rows, that must be in a section when it is expanded 
NSUInteger contentCount = [_foldersArray[section - 1][@"folders"] count]; 

// Change a saved status of a section 
BOOL expanded = [_sectionStates[section] boolValue]; 
expanded = ! expanded; 
expandableAccountHeaderView.expanded = expanded; 
_sectionStates[section] = [NSNumber numberWithBool:expanded]; 

// Animation in a table 
[self.tableView beginUpdates]; 

NSMutableArray* modifiedIndexPaths = [[NSMutableArray alloc] init]; 
for (NSUInteger i = 0; i < contentCount; i++) { 
    NSIndexPath* indexPath = [NSIndexPath indexPathForRow:i inSection:section]; 
    [modifiedIndexPaths addObject:indexPath]; 
} 

if (expandableAccountHeaderView.expanded) [self.tableView insertRowsAtIndexPaths:modifiedIndexPaths withRowAnimation:UITableViewRowAnimationFade]; 
else [self.tableView deleteRowsAtIndexPaths:modifiedIndexPaths withRowAnimation:UITableViewRowAnimationFade]; 

[self.tableView endUpdates]; 

// Scroll to the top of current expanded section 
if (expandableAccountHeaderView.expanded) [self.tableView scrollToRowAtIndexPath:INDEX_PATH(0, section) atScrollPosition:UITableViewScrollPositionTop animated:YES]; 
} 

4)根據擴展與否,在一個段中返回正確的數字或​​行也很重要。

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{ 
    BOOL expanded = [_sectionStates[section] boolValue]; 

    return expanded ? [_foldersArray[section - 1][@"folders"] count] : 0; 
} 
92

因爲這是完全正確的,所以我不打算在這裏說任何與公認的答案相抵觸的東西。但是,我將詳細討論如何實現這一點。如果您不想閱讀所有這些內容,並且更願意在工作項目中使用源代碼,則我已上傳example project to GitHub

其基本思想是在方法-tableView: heightForRowAtIndexPath:內部有一個條件,用於確定當前單元格是否應該展開。這將通過在-tableView: didSelectRowAtIndexPath:之內調用表格上的開始/結束更新來觸發。在本例中,我將演示如何製作允許一次擴展一個單元格的表格視圖。

您需要做的第一件事是聲明對NSIndexPath對象的引用。不過你想要的,你可以這樣做,但我建議使用這樣的屬性聲明:

@property (strong, nonatomic) NSIndexPath *expandedIndexPath; 

注:你並不需要創建裏面viewDidLoad中該指數的路徑,或者其他類似方法。索引最初爲零的事實只意味着表格最初不會有擴大的行。如果你寧願表具有擴展您所選擇的一排開始,您可以添加這樣的事情您的viewDidLoad方法:

NSInteger row = 1; 
NSInteger section = 2; 
self.expandedIndexPath = [NSIndexPath indexPathForRow:row inSection:section]; 

的下一步是頭頂到您的UITableViewDelegate方法-tableView: didSelectRowAtIndexPath:添加基於用戶選擇來改變擴展的小區索引的邏輯。這裏的想法是檢查剛纔選擇的索引路徑是否存儲在expandedIndexPath變量中的索引路徑中。如果兩者匹配,那麼我們知道用戶正試圖取消選擇擴展單元,在這種情況下,我們將該變量設置爲零。否則,我們將expandedIndexPath變量設置爲剛剛選擇的索引。這一切都在beginUpdates/endUpdates調用之間完成,以允許表視圖自動處理過渡動畫。

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    [tableView beginUpdates]; // tell the table you're about to start making changes 

    // If the index path of the currently expanded cell is the same as the index that 
    // has just been tapped set the expanded index to nil so that there aren't any 
    // expanded cells, otherwise, set the expanded index to the index that has just 
    // been selected. 
    if ([indexPath compare:self.expandedIndexPath] == NSOrderedSame) { 
     self.expandedIndexPath = nil; 
    } else { 
     self.expandedIndexPath = indexPath; 
    } 

    [tableView endUpdates]; // tell the table you're done making your changes 
} 

然後,最後一步是在另一個UITableViewDelegate方法-tableView: heightForRowAtIndexPath:。此方法將在您爲表確定需要更新的每個索引路徑觸發beginUpdates後調用。這是您將比較expandedIndexPath與當前正在重新評估的索引路徑的位置。

如果兩個索引路徑相同,那麼這是您希望展開的單元格,否則它的高度應該是正常的。我使用了100和44的值,但你可以使用任何適合你需要的東西。

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    // Compares the index path for the current cell to the index path stored in the expanded 
    // index path variable. If the two match, return a height of 100 points, otherwise return 
    // a height of 44 points. 
    if ([indexPath compare:self.expandedIndexPath] == NSOrderedSame) { 
     return 100.0; // Expanded height 
    } 
    return 44.0; // Normal height 
} 
+0

你能否解釋一下table view在新版本中如何處理動畫,其中`beginUpdates`和`endUpdates`之間什麼都沒有?謝謝 – Leo 2016-02-25 14:22:57

+0

應該是被接受的答案!很好的答案! – fabersky 2016-04-27 14:36:36

9

我爲此創建了一個開源庫。您只需在您的代碼中實施摺疊並展開代表,然後在代碼中輸入即可!!你也可以執行任何繪圖和動畫。退房this

enter image description here

+2

項目可以改進,如果你subview而不是使用viewWithTag的tableview單元格 – SleepsOnNewspapers 2015-06-03 01:21:23

0
initialize iSelectedIndex = -1; and declare 
UITableView *urTableView; 

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ 

return 10; //Section count 

} 

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

return 3; //row count 

} 

- (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] autorelease]; 

} 

[cell.textLabel setText:[NSString stringWithFormat:@"sec:%d,row:%d",indexPath.section,indexPath.row]]; 

return cell; 

} 


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

// adding a label with the tap gesture to the header in each section 

headerLabel = [[UILabel alloc]init]; 

headerLabel.tag = section; 

headerLabel.userInteractionEnabled = YES; 

headerLabel.backgroundColor = [UIColor greenColor]; 

headerLabel.text = [NSString stringWithFormat:@"Header No.%d",section]; 

headerLabel.frame = CGRectMake(0, 0, tableView.tableHeaderView.frame.size.width, tableView.tableHeaderView.frame.size.height); 

UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(gestureTapped:)]; 

[headerLabel addGestureRecognizer:tapGesture]; 

return headerLabel; 

} 

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{ 

return 50.0; //adjust the height as you need 

} 

- (void)gestureTapped:(UITapGestureRecognizer *)sender{ 

UIView *theSuperview = self.view; // whatever view contains 

CGPoint touchPointInSuperview = [sender locationInView:theSuperview]; 

UIView *touchedView = [theSuperview hitTest:touchPointInSuperview withEvent:nil]; 

if([touchedView isKindOfClass:[UILabel class]]) 
{ 

    if (iSelectedIndex != touchedView.tag) { //if new header is selected , need to expand 

     iSelectedIndex = touchedView.tag; 

    }else{ // if the header is already expanded , need to collapse 

     iSelectedIndex = -1; 

    } 

    [urTableView beginUpdates]; 

    [urTableView endUpdates]; 

} 

} 

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

// Show or hide cell 

float height = 0.0; 

if (indexPath.section == iSelectedIndex) { 

    height = 44.0; // Show the cell - adjust the height as you need 

} 

return height; 

} 
8

而不是使用[tableView beginUpdates][tableView endUpdates]的,我使用的是didSelectRowAtIndexPath方法裏面[tableView reloadRowsAtIndexPath:... withRowAnimation:...]方法。

我更喜歡這個,因爲當我展開我的UITableViewCell時,當我使用開始&末端更新方法時,我有一些應該顯示的元素的問題。另一點是,你可以像一些動畫之間進行選擇:上,下,左,右......

0

爲了增加爲0x7FFFFFFF的答案,我發現我需要一個額外的條件if語句didSelectRowAtIndexPath方法內 - 因此:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{ 

    [tableView beginUpdates]; 

    if (self.expandedIndexPath && [indexPath compare:self.expandedIndexPath] == NSOrderedSame) { 
     self.expandedIndexPath = nil; 
    } else { 
     self.expandedIndexPath = indexPath; 
    } 

    [tableView endUpdates]; 

} 
0

這是米克的答案,但對於斯威夫特4.(IndexPath取代NSIndexPath,它配備了一個空IndexPath爲無會崩潰斯威夫特此外,比較方法是現在不同了。您不能再使用NSOrderedSame)

聲明expandedIndexPath屬性。

var expandedIndexPath = IndexPath() 

可選的viewDidLoad部分。

expandedIndexPath = IndexPath(row: 1, section: 2) 

然後didSelectRow部分。

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 
    tableView.beginUpdates() 

    if indexPath.compare(expandedIndexPath) == .orderedSame { 
     expandedIndexPath = IndexPath() 
    } else { 
     expandedIndexPath = indexPath 
    } 

    tableView.endUpdates() 
} 

然後是heightForRow部分。

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 
    if indexPath.compare(expandedIndexPath) == .orderedSame { 
     return 100 
    } 

    return 44 
}