【IOS】模仿windowsphone清單索引的控制項YFMetroListBox,vblistbox索引

來源:互聯網
上載者:User

【IOS】模仿windowsphone清單索引的控制項YFMetroListBox,vblistbox索引

有沒有覺得UITableView內建的右側索引很難用,我一直覺得WindowsPhone中的清單索引非常好用。

所以呢,我們來實作類別似Windows Phone中的清單索引(這就是信仰)。

最終實現:

1.完整的首字母索引                                                     2.Header名稱索引

         

 

想法:這個控制項是該繼承UITableView還是UIView,抑或其他的呢?

想要寫的這個控制項,只是在UITableView的基礎上增加-點擊HeaderView事件-來彈出索引。

彈出索引的大小為控制項的大小,並直接添加到父視圖中。

所以覺得直接繼承UITableView會更加方便,而繼承UIView雖然說在寫法上更簡單些,但是總覺得不太好。

 

碰巧看到一個實現UITableView動畫的例子,於是參照著實現了HeaderView的點擊事件。

https://github.com/applidium/ADLivelyTableView   

原理是通過增加一個中間代理,只在控制項中實現了willDisplayCell協議來控制滑動時的動畫,

如果在VC中使用控制項時也實現了該協議,則讓控制項中的代理髮送該訊息給VC。這樣的話相當

於UITableView的實現部分都不變,動畫都交給控制項來實現。

 -------------------------------------我是分割線----------------------------------------------

1.繼承UITableView   -   增加中間代理

普通流程:UIViewController<UITableViewDelegate>    --->   UITableView           

               tableView.delegate = self;

控制項流程:繼承UITableView :  YFMetroListBox : UITableView

     增加私人屬性:       id<UITableViewDelegate> _selfDelegate;

     UIViewController<UITableViewDelegate>    --->   YFMetroListBox<UITableViewDelegate>         --->         UITableView

           tableView.delegate = self;                                  重寫YFMetroListBox代理見下面代碼

重寫YFMetroListBox設定代理的方法:(這個是繼承自UITableView的屬性)

-(void)setDelegate:(id<UITableViewDelegate>)delegate{//這裡的形參即VC對象    _selfDelegate = delegate;  //讓本類(YFMetroListBox)對VC進行監聽    [super setDelegate:self];  //讓父類(UITableView)對子類的監聽
}
//重寫方法: YFMetroListBox或者父類是否實現了協議- (BOOL)respondsToSelector:(SEL)aSelector {    return [super respondsToSelector:aSelector] || [_selfDelegate respondsToSelector:aSelector]; } //轉寄訊息   - (void)forwardInvocation:(NSInvocation *)anInvocation {   if ([_selfDelegate respondsToSelector:[anInvocation selector]]) {    [anInvocation invokeWithTarget:_selfDelegate];   } else {     [super forwardInvocation:anInvocation];   } }

 

2.YFMetroListBox實現willDisplayHeaderView協議,增加添加HeaderView的點選手勢

彎路1 :剛開始想使用viewForHeaderInSection添加自訂View,之後添加手勢,

但是這樣的話就得在此HeaderView內容,因此直接在初始化的時候傳入了NSArray(感覺不好,但沒其他方案)。

除了上述協議,還有titleForHeaderInSection也是能夠直接設定預設的標題內容的,那麼如何擷取headerView?

再者,上面兩個協議都是UITableViewDataSource中的,要在控制項中這樣做會更加的混亂了

 

解決 :不管VC中如何設定標題,在HeaderView將要顯示的時候,添加手勢就行了

 

#pragma mark -m UITableViewDelegate- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section{  //如果VC也實現了該協議,則發送此訊息  if([_selfDelegate respondsToSelector:@selector(tableView:willDisplayHeaderView:forSection:)]){        [_selfDelegate tableView:tableView willDisplayHeaderView:view forSection:section];    }
  //增加手勢   UITapGestureRecognizer *tapRegesture = [[UITapGestureRecognizer alloc] initWithTarget:self      action:@selector(tapHeaderViewInSection)]; view.gestureRecognizers = @[tapRegesture];}

 

3. 在YFMetroListBox中擷取標題內容數組

彎路2 : 存在headerViewForSection:方法擷取某一section的headerView。由此想到,

既然在VC中設定好了頭標題,那麼我肯定可以在內部動態擷取全部標題。(錯誤的想法)

 

-(NSArray *)getHeaderViewTextArray{    NSMutableArray *array = [NSMutableArray array];    for (int i = 0 ; i < [self numberOfSections]; i++) {//迴圈section數        NSArray *subviews = [[self headerViewForSection:i] subviews];//取得UIlabel的內容        for (id subview in subviews) {            if([subview isKindOfClass:[UILabel class]]){                UILabel *label = (UILabel *)subview;                [array addObject:label.text];                break;            }        }    }  return array;}

但是通過這個方法擷取的的始終只有螢幕中的可見部分的頭標題,而不是全部的頭標題。

原因:因為使用了重用機制,只有顯示的部分被建立了,所以想要擷取全部的內容

是不可能的。這類問題 應該從資料來源觸發。

 解決 :像UITableViewDataSource協議中sectionIndexTitlesForTableView一樣通過協議來實現。

自訂一個協議,在VC中實現該協議,給YFMetroListBox頭標題數組資料。

@protocol YFMetroListBoxDelegate <NSObject>@required- (NSArray<NSString *> *)sectionIndexTitlesForYFMetroListBox:(YFMetroListBox *)metroListBox;//返回的標題頭數組有兩種格式  1.自訂標題頭     2.("#ABCD...Z@"),#表示數字    @暫時表示除了數字 字母  英文 以外的字元。@end

具體的將一組包含數字、中文、英文、符號的資料分組排序成#ABC...Z✿"格式我們在下一篇隨筆給出。

 

4. 畫出點擊後介面

介面有兩種,通過metroListBoxType枚舉屬性來進行設定

分別對應最上面的兩幅圖。這點沒什麼說的,通過上一點擷取標題頭數組。來展示。

如果為YFMetroListBoxTypeAllAlphabet,則將#.A.B.C...Z.@全部畫出,給標題頭數組存在的添加點擊事件。

點擊後通過 [self scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]

atScrollPosition:UITableViewScrollPositionTop animated:NO]進行跳轉。

 

5.重寫  插入、刪除、重新載入資料等方法

到目前為止  基本上就做好了。只不過在添加更改資料來源之後進行reloadData、

或者對section進行insertSections、deleteSections、moveSection等操作,索引列表是不會改變的。

因此要重寫他們,手動的調用更新索引介面的方法。

//Data-(void)reloadData{    [self updateZoomOutView];    [super reloadData];}-(void)reloadSectionIndexTitles{    [self updateZoomOutView];    [super reloadSectionIndexTitles];}// only call insert/delete/reload calls or change the editing state inside an update block.  otherwise things like row count, etc. may be invalid.- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation{    [self updateZoomOutView];    [super insertSections:sections withRowAnimation:animation];}- (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation{    [self updateZoomOutView];    [super deleteSections:sections withRowAnimation:animation];}- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation{    [self updateZoomOutView];    [super reloadSections:sections withRowAnimation:animation];}- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection{    [self updateZoomOutView];    [super moveSection:section toSection:newSection];}

 

這樣就大功告成了。

GitHub地址: YFMetroListBox 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.