我曾經開發過一個iphone應用程序,它顯示了大量的輸入,這些輸入分爲不一樣的類別,在`UITableView`...若要更改其中一個輸入的值,用戶按下表視圖中的對應行,並在出現的單獨屏幕中更改該值。表視圖爲每一個類別有一個節,每一個節包含每一個輸入的表格單元格(行)。編程
問題是輸入的數量變得很是很是大,因此它沒有給用戶一個很是好的概述。從桌面滾動到底部甚至很乏味。數組
咱們決定用戶應該可以經過簡單地按下節的標題來摺疊和展開表中的部分(類別)。咱們要求實現這一目標的代碼應該是可重用的,而且要求對現有代碼進行儘量少的更改。緩存
另外,若是你想一塊兒進階,不妨添加一下交流羣[1012951431](),選擇加入一塊兒交流,一塊兒學習。期待你的加入!(進羣獲取本文源碼)多線程
下面的屏幕截圖顯示了表視圖及其可摺疊部分的外觀:app
我認爲實現上述目標的最佳方法是建立 UITableView 類,命名爲 CollapsableTableView ...這確保了代碼是可重用的。若是操做正確,則不須要對 UITableView- 他們會像一個普通的人同樣處理桌子上的風景`UITableView`...惟一必要的更改是更改 UITableView 在西布文件到這個新的子類。,以確保客戶端能夠像普通用戶同樣使用表視圖。UITableView ,咱們必須嘗試容許徹底經過 UITableView 類,包括 UITableViewDelegate ,以及 UITableViewDataSource 協議。iphone
可摺疊表視圖必須以某種方式跟蹤哪些區段被摺疊(收縮)以及哪些部分被展開。可能最明顯的方法是維護一組已展開的節的索引,或者一個布爾數組,其中每一個索引的值指示對應的節是否展開。可是,若是咱們假設表視圖的客戶端能夠添加和刪除節(在咱們的場景中是這樣的),那麼節的索引將不會保持固定,所以處理索引充其量也是很麻煩的。所以,咱們必須爲節找到不一樣的標識符。爲此,咱們可使用章節的標題文本。固然,這假定節的標題文本惟一地標識該節,而且其標題文本保持不變,但考慮到必須堅持使用 UITableView 同窗們,這多是咱們能作的最好的了。這還假設客戶端實現了 tableView:titleForHeaderInSection: 的選擇器 UITableViewDelegate 全部表格單元格的協議。在咱們的項目中,狀況就是這樣。在使用代碼節中,咱們將解釋咱們的類是如何支持實現 tableView:viewForHeaderInSection: 選擇器。ide
爲了更容易地管理頭視圖,咱們建立了一個 UIViewController 類,命名爲 CollapsableTableViewHeaderViewController ...對於這門課,有兩個西布一號西布用於平面佈局的表,而用於具備分組佈局的表。此類包含視圖中可操做的全部標籤的`IB`出口。它存儲一個布爾值,指示區段是否摺疊。此視圖控制器類還確保它的視圖在用戶點擊它時通知咱們,以便 CollapsableTableView 才能採起必要的行動。佈局
下面是.h.的檔案 CollapsableTableViewHeaderViewController: 學習
#import <UIKit/UIKit.h> #import "TapDelegate.h" #import "CollapsableTableViewTapRecognizer.h" @interface CollapsableTableViewHeaderViewController : UIViewController { IBOutlet UILabel *collapsedIndicatorLabel,*titleLabel,*detailLabel; CollapsableTableViewTapRecognizer* tapRecognizer; BOOL viewWasSet; id<TapDelegate> tapDelegate; NSString* fullTitle; BOOL isCollapsed; } @property (nonatomic, retain) NSString* fullTitle; @property (nonatomic, readonly) UILabel* titleLabel; @property (nonatomic, retain) NSString* titleText; @property (nonatomic, readonly) UILabel* detailLabel; @property (nonatomic, retain) NSString* detailText; @property (nonatomic, assign) id<TapDelegate> tapDelegate; @property (nonatomic, assign) BOOL isCollapsed; @end
collapsedIndicatorLabel 是否顯示「-」或「+」的小標籤,取決於區段是否摺疊。當 isCollapsed 的文本被更改。 collapsedIndicatorLabel 則相應地設置爲「-」或「+」。 titleLabel 是包含標題和 detailLabel 顯示標題右側的可選詳細文本。優化
下面是`TapDelegate`議定書:
#import <UIKit/UIKit.h> @protocol TapDelegate - (void) view:(UIView*) view tappedWithIdentifier:(NSString*) identifier; @end
這個 view:tappedWithIdentifier: 當頭視圖被點擊時調用選擇器 CollapsableTableView 實現 TapDelegate 協議,使其可以摺疊或展開相應的報頭以響應此協議。調用選擇器時,將使用 view 參數的標頭的標題字符串。 identifier 參數,以便`CollapsableTableView`能夠進行查找,以肯定標題當前是否已摺疊,以及其當前節索引是什麼。
在該項目的第一個已發佈的實現中,該選擇器由 CollapsableTableViewHeaderViewController 對應的標題視圖。由於在該版本中, CollapsableTableView 存儲(並所以保留) CollapsableTableViewHeaderViewControllers 其全部章節。可是,爲了提升實現的內存效率--特別是對於具備多個節的表 --CollapsableTableView 因此它再也不這樣作了。所以,結果是 CollapsableTableViewHeaderViewController 在頭視圖出如今表中後不久,就會從內存中釋放報頭視圖(報頭)。UIView 只要它在表中可見,它仍然保留在內存中)。這意味着當頭視圖被點擊時,可能沒有。 CollapsableTableViewHeaderViewController 調用 TapDelegate 選擇器。
在咱們尋找這個問題的解決方案以前,讓咱們看看頭視圖的點擊是如何在 CollapsableTableViewHeaderViewController.m.
- (void) setView:(UIView*) newView { if (viewWasSet) { [self.view removeGestureRecognizer:tapRecognizer]; [tapRecognizer release]; } [super setView:newView]; tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(headerTapped)]; [self.view addGestureRecognizer:tapRecognizer]; viewWasSet = YES; } - (void) headerTapped { [tapDelegate viewTapped:self.view ofViewController:self]; }
因此咱們推翻了 setView: 方法 UIViewController 類以添加 UITapGestureRecognizer 到 UIView 分配給 CollapsableTableViewHeaderViewController ...這,這個 UITapGestureRecognizer 的方法被配置爲調用 CollapsableTableViewHeaderViewController 每當頭視圖被點擊時。在新代碼中,此技術再也不起做用,由於 CollapsableTableViewHeaderViewController 將常常在用戶點擊頭時被解除分配。
這個問題的惟一解決方案多是最明顯的,就是配置 UITapGestureRecognizer 若要調用對象中的選擇器,當用戶單擊標頭視圖時,該對象將不會被釋放。該對象的一些選擇以下:
第二個選擇將不起做用,由於咱們沒法控制 UIView 傳遞到 setView: 方法來自(也就是說,咱們不能使用子類)。 UIView 爲了給它添加一個額外的方法,也許咱們能夠將傳入的內容包裝起來。 UIView 類的子類中的 UIView 咱們本身的,但咱們不要去那裏!)向 CollapsableTableView 是一個選項,儘管添加沒有參數的方法是不行的,由於 CollapsableTableView 不知道哪一個標題已被點擊。然而,在文件中 UITapGestureRecognizer ,咱們看到替代選擇器類型是一個選擇器,它接受 UITapGestureRecognizer 對象做爲參數。可是,咱們必須把 UITapGestureRecognizer 以添加存儲標題字符串的屬性。因此若是咱們必須把 UITapGestureRecognizer,使用第三個選項並配置 UITapGestureRecognizer 調用其內部的選擇器。這是在實現中採起的方法:咱們使用 UITapGestureRecognizer ,咱們稱之爲 CollapsableTableViewTapRecognizer ,定義以下:
#import <Foundation/Foundation.h> #import "TapDelegate.h" @interface CollapsableTableViewTapRecognizer : UITapGestureRecognizer { id<TapDelegate> tapDelegate; NSString* fullTitle; UIView* tappedView; } @property (nonatomic, assign) id<TapDelegate> tapDelegate; @property (nonatomic, retain) NSString* fullTitle; @property (nonatomic, assign) UIView* tappedView; - (id) initWithTitle:(NSString*) theFullTitle andTappedView:(UIView*) theTappedView andTapDelegate:(id<TapDelegate>) theTapDelegate; @end
在 initWithTitle:andTappedView:andTapDelegate: 方法,CollapsableTableViewTapRecognizer 對象配置爲調用私有方法。headerTapped 當景物被點擊的時候。
- (void) headerTapped { [tapDelegate view:tappedView tappedWithIdentifier:fullTitle]; }
讓咱們回到 CollapsableTableView 如今。當它從客戶端得到一個節的標題時,它須要可以進行一次查找,以查看標頭是否摺疊,以及標頭的節索引是什麼。爲此,咱們保持兩個獨立的 NSMutableDictionary 對象:將標題標題映射爲指示標頭是否摺疊的布爾值的對象,以及將標題標題映射爲整數(給定標頭節索引的整數)的對象。咱們還可使用一個字典在指定的索引上查找該節的標題(固然,每當客戶端從表中添加或刪除一個節時,就必須更新該字典)。
那麼,如何 CollapsableTableView 其實是塌陷和擴張部分?那麼,摺疊的部分只會有0行,因此即便客戶端將返回該節的正常行數, CollapsableTableView 將返回摺疊節的行數爲0,或由客戶端返回的擴展節的行數。這代表 CollapsableTableView 須要攔截對 tableView:numberOfRowsInSection: 方法。它還必須返回 CollapsableTableViewHeaderViewController 對於每一個部分,所以它還必須攔截對 tableView:viewForHeaderInSection: 方法。因此爲了 CollapsableTableView 爲了可以響應這兩個選擇器,它必須實現 UITableViewDelegate 而 UITableViewDataSource 協議,並在運行時將其委託和數據源屬性設置爲.自己!可是,必須將對這些協議的選擇器的許多調用轉發給客戶端,所以 CollapsableTableView 存儲真實委託和數據源的引用,以便爲這些狀況提供參考。
- (void) setDelegate:(id <UITableViewDelegate>) newDelegate { [super setDelegate:self]; realDelegate = newDelegate; } - (void) setDataSource:(id <UITableViewDataSource>) newDataSource { [super setDataSource:self]; realDataSource = newDataSource; }
的接口文件CollapsableTableView:
#import <Foundation/Foundation.h> #import "TapDelegate.h" #define COLLAPSED_INDICATOR_LABEL_TAG 36 #define BUSY_INDICATOR_TAG 37 @interface CollapsableTableView : UITableView <UITableViewDelegate,UITableViewDataSource,TapDelegate> { id<UITableViewDelegate> realDelegate; id<UITableViewDataSource> realDataSource; id<CollapsableTableViewDelegate> collapsableTableViewDelegate; ... } @property (nonatomic,assign) id<CollapsableTableViewDelegate> collapsableTableViewDelegate; @property (nonatomic,retain) NSString* collapsedIndicator; @property (nonatomic,retain) NSString* expandedIndicator; @property (nonatomic,assign) BOOL showBusyIndicator; @property (nonatomic,assign) BOOL sectionsInitiallyCollapsed; @property (nonatomic,readonly) NSDictionary* headerTitleToIsCollapsedMap; - (void) setIsCollapsed:(BOOL) isCollapsed forHeaderWithTitle:(NSString*) headerTitle; - (void) setIsCollapsed:(BOOL) isCollapsed forHeaderWithTitle:(NSString*) headerTitle andView:(UIView*) headerView; - (void) setIsCollapsed:(BOOL) isCollapsed forHeaderWithTitle:(NSString*) headerTitle withRowAnimation:(UITableViewRowAnimation) rowAnimation; - (void) setIsCollapsed:(BOOL) isCollapsed forHeaderWithTitle:(NSString*) headerTitle andView:(UIView*) headerView withRowAnimation:(UITableViewRowAnimation) rowAnimation; @end
執行 CollapsableTableView 從討論到這一點,基本上都是這樣。下面的段落簡要解釋了該類的公共屬性和方法的用途。
這個 collapsableTableViewDelegate 屬性能夠設置爲實現 CollapsableTableViewDelegate 屬性,以便每當某個區段摺疊或展開時,以及當該對象完成摺疊或展開時,都會通知該對象。
默認摺疊和展開指示符(默認值分別爲「+」和「-」)可使用 collapsedIndicator 和 expandedIndicator 財產。
這個 showBusyIndicator 屬性的默認值爲YES,若是設置,則會致使活動指示符視圖( SPINTER )(位於標頭視圖中的子視圖中具備由BUSY_INDICATOR_TAG)當摺疊或展開頁眉視圖的部分花費的時間超過0.5秒時,要在標題視圖上動畫。
這個 sectionsInitiallyCollapsed 屬性的默認值爲NO,並控制新的部分是否會首先被摺疊或不折疊。
這個 headerTitleToIsCollapsedMap 物業供應 NSDictionary 將標題的標題字符串映射到 NSNumber 對象,該對象包含指示標頭是否摺疊的布爾值。
這個 setIsCollapsed:forHeaderWithTitle: ...方法將用於以編程方式摺疊或展開區段。若是客戶端具備對 UIView 對於相應的標頭,它能夠調用包含 andView: 用那個`UIView`做爲參數。若是調用了另外兩個方法中的任何一個,則必須從新加載相應的部分(和頭視圖),而且動畫有時會比... andView: 使用方法。
須要添加到 Xcode 項目以便使用 CollapsableTableView 類中的 CollapsableTableView 文件夾中的壓縮文件(下載源代碼).
另外,若是你想一塊兒進階,不妨添加一下交流羣[1012951431](),選擇加入一塊兒交流,一塊兒學習。期待你的加入!(進羣獲取本文源碼)
CollapsableTableView 徹底能夠像普通的 UITableView ,只要客戶端實現 tableView:titleForHeaderInSection: 選擇器(相對於 tableView:viewForHeaderInSection: )用於全部表單元格。惟一必要的更改是更改 UITableView 在西布到 CollapsableTableView ...要作到這一點,請打開西布文件,選擇 UITableView ,打開身份檢查器並鍵入「 CollapsableTableView 「」類「字段旁邊。
執行 CollapsableTableView 也容許使用 tableView:viewForHeaderInSection: ,但在這裏,它沒法訪問標頭的標題字符串,它一般用做標頭的標識符。相反,它使用字符串「標記%i」,其中%i是tag返回的視圖的屬性(若是tag爲0,但區段索引不是0,此數字默認爲 CollapsableTableView )。這意味着,若是客戶端返回某些單元格的視圖(而不是頭文本字符串),而且若是它能夠添加和刪除部分,則必須分配惟一的tag對應於每一個區段的視圖的編號。
若是客戶端返回某些單元格的視圖,則這些視圖能夠包含一個標籤,該標籤指示是否摺疊了標頭。簡單地設置tag屬性設置爲 COLLAPSED_INDICATOR_LABEL_TAG 中定義的 CollapsableTableView.h ...這個 CollapsableTableView 而後,每當區段摺疊或展開時,該標籤的文本將設置爲「+」或「-」(除非 collapsedIndicator或expandedIndicator 屬性已設置爲不一樣的字符串)。
的客戶端。CollapsableTableView 可能不知道它不適用於常規的 UITableView 但若是它知道 UITableView 是 CollapsableTableView ,它能夠將對象強制轉換爲後一種類型,並使用headerTitleToIsCollapsedMap屬性以肯定哪些區段已摺疊,而setIsCollapsed:forHeaderWithTitle: 方法以編程方式摺疊或展開區段。
正如在實施部分,CollapsableTableView 還容許將細節文本顯示在標題的右側。若要使用此功能,請在 tableView:titleForHeaderInSection: ,返回表單的字符串「Header Text\Details Text」。
可摺疊表視圖如今緩存全部標頭視圖的高度。這實際上消除了在摺疊或擴展區段時發生的過分延遲。
翻譯地址:https://www.codeproject.com/Articles/240435/Collapsable-Table-View-for-iOS