很多app中都需要城市選擇拙徽,最近自己寫了一個(gè)城市篩選、搜索功能,以及tableView雙表聯(lián)動(dòng),上拉自動(dòng)至下一個(gè)分類,tableView的折疊布局膀钠。
Demo -- 傳送門
一掏湾、雙表聯(lián)動(dòng)
1、上拉至下一個(gè)分類
- 給rightTableView添加footRefresh 在刷新回調(diào)的時(shí)候進(jìn)行操作肿嘲,給leftTableView選中至當(dāng)前的下一個(gè)indexPathRow 并同時(shí)將下一個(gè)rightTableView分類的dataSource傳送過去融击,并刷新leftTableView、rightTableView兩個(gè)表雳窟。將leftTableView當(dāng)前選中的indexPath滾動(dòng)到頂部砚嘴。
#pragma mark - rightTableView刷新回調(diào)
- (void)childTableCallback {
LBWeakSelf(self);
self.childTableView.freshFinishCallback = ^ {
LBStrongSelf(self);
[self setCurrentIndexWithRow:self.currentIndex.row+1];
};
}
- (void)setCurrentIndexWithRow:(NSInteger)row {
NSIndexPath *currentIndex = [NSIndexPath indexPathForRow:row inSection:0];
// 調(diào)用leftTableView的didSelectRowAtIndexPath方法實(shí)現(xiàn)選中下一個(gè)indexPathRow
[self.baseTableView tableView:self.baseTableView.baseTableView didSelectRowAtIndexPath:currentIndex];
// 記錄當(dāng)前選中的indexPath
self.currentIndex = currentIndex;
[self.baseTableView setValue:self.currentIndex forKey:@"selectedIndex"];
[self.baseTableView.baseTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:row inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
- 點(diǎn)擊leftTableView 將當(dāng)前的分類數(shù)據(jù)傳送給rightTableView并刷新rightTableView。
#pragma mark - leftTableView點(diǎn)擊回調(diào)
- (void)baseTableViewCallback {
LBWeakSelf(self);
self.baseTableView.didSelectCellCallback = ^(NSIndexPath *indexPath, UITableViewCell *currentBaseCell) {
LBStrongSelf(self);
LBModel* model = self.dataSource[indexPath.row];
// 數(shù)據(jù)源傳給rightTableView
[self.childTableView setValue:model.cityList forKey:@"childData"];
// 刷新rightTableView
[self.childTableView reload];
self.currentIndex = indexPath;
};
}
2涩拙、所有區(qū)聯(lián)動(dòng)
- 監(jiān)聽rightTableView滾動(dòng)际长,并獲取當(dāng)前屏幕第一個(gè)section回調(diào)給leftTableView進(jìn)行刷表同時(shí)將當(dāng)前的section滾動(dòng)到頂部
#pragma mark - rightTableView滾動(dòng)監(jiān)聽回調(diào)
- (void)scrollwithIndex:(NSIndexPath *)index {
self.selectedIndex = index;
//tableview滾動(dòng)到指定的行:
[self.baseTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:index.row inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
[self.baseTableView reloadData];
}
二、tableView折疊
- 定義BOOL值isFlod 記錄是否可以展開兴泥,定義currentIndex記錄當(dāng)前展開的index
// 是否可以展開
@property(nonatomic, assign)BOOL isFlod;
// 當(dāng)前展開的index
@property(nonatomic, assign)NSInteger currentIndex;
- 給tableViewSection添加UIButton并設(shè)置tag為當(dāng)前section
sectionBtn.tag = section;
sectionBtn.selected = self.currentIndex==section&&self.isFlod==YES?YES:NO;
- Button點(diǎn)擊方法設(shè)置isFlod值和currentIndex值并刷新tableView
- (void)sectionSelect:(UIButton*)sender {
self.currentIndex = sender.tag;
sender.selected = !sender.selected;
self.isFlod = sender.selected;
[self.foldTableView reloadData];
}
- 在tableView數(shù)據(jù)源方法中工育,判斷當(dāng)前的section是否為展開狀態(tài)并給出當(dāng)前section有多少row
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (self.currentIndex == section) {
LBModel* childModel = self.dataSource[section];
return self.isFlod==YES?childModel.cityList.count:0;
}
return 0;
}
三、城市篩選
- 前面2個(gè)section分別是歷史的篩選和熱門篩選tableViewCell嵌套的collectionView搓彻,后面是按照首字母A~Z的分區(qū)城市的tableViewCell如绸,這個(gè)不多說了,由于我的地址數(shù)據(jù)是按照省市區(qū)分的旭贬,所以自己又重新排了一次數(shù)據(jù)結(jié)構(gòu)怔接。
漢字轉(zhuǎn)拼音
//獲取拼音首字母(傳入漢字字符串, 返回大寫拼音首字母)
+(NSString *)FirstCharactor:(NSString *)pString {
//轉(zhuǎn)成了可變字符串
NSMutableString *pStr = [NSMutableString stringWithString:pString];
//先轉(zhuǎn)換為帶聲調(diào)的拼音
CFStringTransform((CFMutableStringRef)pStr,NULL, kCFStringTransformMandarinLatin,NO);
//再轉(zhuǎn)換為不帶聲調(diào)的拼音
CFStringTransform((CFMutableStringRef)pStr,NULL, kCFStringTransformStripDiacritics,NO);
//多音字處理
if ([[(NSString *)pString substringToIndex:1] compare:@"長"] == NSOrderedSame) {
[pStr replaceCharactersInRange:NSMakeRange(0, 5) withString:@"chang"];
}
if ([[(NSString *)pString substringToIndex:1] compare:@"沈"] == NSOrderedSame) {
[pStr replaceCharactersInRange:NSMakeRange(0, 4) withString:@"shen"];
}
if ([[(NSString *)pString substringToIndex:1] compare:@"廈"] == NSOrderedSame) {
[pStr replaceCharactersInRange:NSMakeRange(0, 3) withString:@"xia"];
}
if ([[(NSString *)pString substringToIndex:1] compare:@"地"] == NSOrderedSame) {
[pStr replaceCharactersInRange:NSMakeRange(0, 3) withString:@"di"];
}
if ([[(NSString *)pString substringToIndex:1] compare:@"重"] == NSOrderedSame) {
[pStr replaceCharactersInRange:NSMakeRange(0, 5) withString:@"chong"];
}
//轉(zhuǎn)化為大寫拼音
NSString *pPinYin = [pStr uppercaseString]; // lowercaseString 轉(zhuǎn)小寫
/*如果要返回所有的漢字轉(zhuǎn)拼音,直接return pPinYin就可以了稀轨,轉(zhuǎn)完的是中間帶有空格的拼音扼脐,下面是去掉中間空格的方法*/
/**去空格
NSString *ciytString = [NSString stringWithFormat:@"%@", pPinYin];
NSString *cityName = [ciytString stringByReplacingOccurrencesOfString:@" " withString:@""]; */
//獲取并返回首字母
return [pPinYin substringToIndex:1];
}
判斷字符串中是否包含有漢字
// 是否包含漢字
- (BOOL)includeChinese {
for(int i=0; i< [self length];i++) {
int a =[self characterAtIndex:i];
if( a >0x4e00&& a <0x9fff){
return YES;
}
}
return NO;
}
- 最上面2個(gè)section的cell點(diǎn)擊回調(diào)
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
LBCollectionCell * cell = (LBCollectionCell *)[collectionView cellForItemAtIndexPath:indexPath];
LBCollectionCell * currentCell;
if (self.index.section==0&&indexPath.row!=0) {
currentCell = (LBCollectionCell *)[collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
}
if (self.index.section==1&&indexPath.row!=[self.selectedHotIndex integerValue]) {
currentCell = (LBCollectionCell *)[collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForRow:[self.selectedHotIndex integerValue] inSection:0]];
}
cell.title.backgroundColor = LBUIColorWithRGB(0x3CB371, .5);
cell.title.textColor = LBUIColorWithRGB(0x228B22, 1);
currentCell.title.backgroundColor = LBUIColorWithRGB(0xF5F5F5, 1);
currentCell.title.textColor = LBUIColorWithRGB(0x130202, 1);
if (self.collectionCallback) {
self.collectionCallback(self.dataArray[indexPath.item],indexPath.item,self.index);
}
}
#pragma mark - 歷史和熱門cell點(diǎn)擊的回調(diào)--historyAndHotCellCallback
- (void)historyAndHotCellCallback:(LBHistoryCell *)cell {
LBWeakSelf(self);
cell.collectionCallback = ^(NSString *selectText,NSInteger collectionSelectIndex,NSIndexPath *index) {
LBStrongSelf(self);
if (index.section==1) { // 熱門回調(diào),存入緩存
[LBUserDefaultTool saveHotSelectedData:collectionSelectIndex];
} else { // 歷史回調(diào)
NSMutableDictionary* dict = self.dataSource[1];
NSMutableArray* cellArr = dict[@"cityName"];
// 清熱門選中緩存奋刽,重新存
[LBUserDefaultTool removeHotSelected];
// 查找熱門中的數(shù)據(jù)與歷史里選中的數(shù)據(jù)相同
[cellArr enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
if ([object isEqualToString:selectText]) {
[LBUserDefaultTool saveHotSelectedData:idx];
}
}];
}
if (self.selectCallback) {
self.selectCallback(selectText);
}
};
}
- 下面tableViewCell點(diǎn)擊回調(diào)
#pragma mark - tableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:NO];
// 下面tableView點(diǎn)擊
if (indexPath.section!=0&&indexPath.section!=1) {
[LBUserDefaultTool removeHotSelected];
NSMutableDictionary* dict = self.dataSource[indexPath.section];
NSString* selectText = dict[@"cityName"][indexPath.row];
if (self.selectCallback) {
self.selectCallback(selectText);
}
}
// cell選中的標(biāo)記符變化和titleLabel顏色變化
LBCityCell *cell = [tableView cellForRowAtIndexPath:indexPath];
LBCityCell *currentCell = [tableView cellForRowAtIndexPath:self.currentSelectedIndex];
currentCell.cityLabel.textColor = LBUIColorWithRGB(0x130202, 1);
currentCell.selectedImg.alpha = 0;
cell.selectedImg.alpha = 1;
cell.cityLabel.textColor = LBUIColorWithRGB(0x228B22, 1);
}
四瓦侮、城市搜索
- UITextField實(shí)時(shí)監(jiān)聽input輸入,input的length大于1佣谐,去遍歷所有數(shù)據(jù)查找肚吏,利用上面漢字轉(zhuǎn)拼音并去空格的方法,與input的數(shù)據(jù)作對比狭魂,如果城市的拼音數(shù)據(jù)包含了input的數(shù)據(jù)罚攀,拼接成model并存入searchArray數(shù)組中作為searchTableView的dataSource展示。
// 搜索input實(shí)時(shí)搜索
- (void)setSearchWithInputText:(NSString *)inputText {
if (inputText.length > 0) { // 這里判斷l(xiāng)ength要大于0雌澄,是因?yàn)闈h字轉(zhuǎn)拼音的方法不能傳空值
[self.searchArray removeAllObjects];
// length大于1斋泄,進(jìn)行數(shù)據(jù)對比
if ([NSString transform:inputText].length > 1) {
// 循環(huán)所有數(shù)據(jù)
[self.dataSource enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
NSDictionary* dict = object;
// firstText A~Z字母
NSString* firstText = dict[@"cityId"];
// 輸入的(漢字轉(zhuǎn)拼音)首字母是否屬于A~Z之間 并拼接數(shù)據(jù)存入搜索數(shù)組searchArray中
if ([[NSString FirstCharactor:inputText] isEqualToString:firstText]) {
NSMutableDictionary* tempDict = @{}.mutableCopy;
NSMutableArray* tempArr = @[].mutableCopy;
tempDict[@"cityId"] = firstText;
// 遍歷所有城市的數(shù)組
[dict[@"cityName"] enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
NSString* cityName = object;
// 所有城市的拼音包含輸入的拼音,就是符合的數(shù)據(jù)掷伙,存入搜索的數(shù)組中
if ([[NSString transform:cityName] containsString:[NSString transform:inputText]]) {
[tempArr addObject:cityName];
}
}];
tempDict[@"cityName"] = tempArr;
[self.searchArray addObject:tempDict];
}
}];
}
self.searchTableView.alpha = [NSString transform:inputText].length>1?1:0;
[self.searchTableView setValue:self.searchArray forKey:@"dataSource"];
} else {
self.searchTableView.alpha = 0;
}
}
五是己、自定義indexView
- 定義一個(gè)indexView又兵,實(shí)現(xiàn)dataSource和delegate方法
@protocol LBIndexViewDataSource <NSObject>
// 返回一共多少個(gè)section
- (NSInteger)numberOfItemViewForSectionIndexView:(LBIndexView *)sectionIndexView;
// 返回每個(gè)section的title
- (NSString *)sectionIndexView:(LBIndexView *)sectionIndexView
titleForSection:(NSInteger)section;
@end
@protocol LBIndexViewDelegate <NSObject>
// 點(diǎn)擊IndexItemView事件
- (void)sectionIndexView:(LBIndexView *)sectionIndexView
didSelectSection:(NSInteger)section;
@end
注意:避免delegate方法里section里的row為空任柜,滾動(dòng)到頂部crash卒废,可以進(jìn)行section刪除,我這里沒操作(小伙伴們可以自行操作)
#pragma mark - LBIndexViewDataSource
- (NSInteger)numberOfItemViewForSectionIndexView:(LBIndexView *)sectionIndexView {
return self.cityTableView.numberOfSections;
}
- (NSString *)sectionIndexView:(LBIndexView *)sectionIndexView titleForSection:(NSInteger)section {
NSMutableArray* sectionArr = @[].mutableCopy;
[self.dataSource enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
NSMutableDictionary* dict = object;
[sectionArr addObject:dict[@"cityId"]];
}];
return sectionArr[section];
}
#pragma mark - LBIndexViewDelegate
- (void)sectionIndexView:(LBIndexView *)sectionIndexView didSelectSection:(NSInteger)section {
NSMutableDictionary* dict = self.dataSource[section];
NSMutableDictionary* dictionary = self.dataSource.count<=section?self.dataSource[section+1]:@{}.mutableCopy;
NSMutableArray* arr = [NSMutableArray arrayWithArray:dict[@"cityName"]];
NSMutableArray* array = [NSMutableArray arrayWithArray:dictionary[@"cityName"]];
if (arr.count <= 0) {
section = section+1;
if (array.count <= 0) {
section = section+1;
}
}
// 避免section里面的row為空宙地,滾動(dòng)到頂部crash
[self.cityTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
- 下面是indexView的幾個(gè)屬性配置
// 是否顯示選中提示圖摔认,默認(rèn)是YES
@property(nonatomic, assign)BOOL isShowCallout;
// 選中提示圖的樣式
@property(nonatomic, assign)NSInteger calloutViewType;
// 選中背景的樣式
@property(nonatomic, assign)NSInteger titleBGViewType;
// 提示圖的主題色
@property(nonatomic, strong)UIColor* schemeColor;
- 實(shí)現(xiàn)reloadIndeView方法
// 創(chuàng)建數(shù)據(jù)源
- (void)reloadIndexView {
NSInteger numberOfItems = 0;
if (_dataSource && [_dataSource respondsToSelector:@selector(numberOfItemViewForSectionIndexView:)]) {
numberOfItems = [_dataSource numberOfItemViewForSectionIndexView:self];
}
for (int i = 0; i < numberOfItems; i++) {
if (_dataSource && [_dataSource respondsToSelector:@selector(sectionIndexView:titleForSection:)]) {
LBIndexItemView* itemView = [LBIndexItemView new];
itemView.section = i;
itemView.titleLabel.text = [_dataSource sectionIndexView:self titleForSection:i];
itemView.schemeColor = self.schemeColor?self.schemeColor:LBUIColorWithRGB(0x228B22, 1);
[self.itemViewList addObject:itemView];
[self addSubview:itemView];
}
}
[self layoutItemViews];
}
// 布局
- (void)layoutItemViews {
if (self.itemViewList.count) {
self.itemViewHeight = LBScreenH/3*2/(CGFloat)(self.itemViewList.count);
}
__block CGFloat offsetY = 0.f;
[self.itemViewList enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
LBIndexItemView *itemView = object;
[itemView setHighlighted:NO animated:NO section:idx type:self.titleBGViewType];
[itemView mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.offset(0);
make.top.offset(offsetY);
make.width.mas_equalTo(idx==0||idx==1?LBFit(30):self.itemViewHeight);
make.height.mas_equalTo(self.itemViewHeight);
}];
offsetY += self.itemViewHeight;
}];
}
分別實(shí)現(xiàn)4個(gè)touch方法,也是indexView的核心
- touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
self.bgView.hidden = NO;
UITouch *touch = [touches anyObject];
// 獲取到touch的point
CGPoint touchPoint = [touch locationInView:self];
// 遍歷indexView的數(shù)據(jù)源宅粥,做點(diǎn)擊操作和highlight操作
for (LBIndexItemView *itemView in self.itemViewList) {
if (CGRectContainsPoint(itemView.frame, touchPoint)) {
[self selectItemViewForSection:itemView.section];
self.highlightedItemIndex = itemView.section;
return;
}
}
self.highlightedItemIndex = -1;
}
- touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
self.bgView.hidden = NO;
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self];
for (LBIndexItemView *itemView in self.itemViewList) {
if (CGRectContainsPoint(itemView.frame, touchPoint)) {
if (itemView.section != self.highlightedItemIndex) {
[self selectItemViewForSection:itemView.section];
self.highlightedItemIndex = itemView.section;
return;
}
}
}
}
- touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
self.bgView.hidden = YES;
// 取消所有higlight的item
[self unhighlightAllItems];
self.highlightedItemIndex = -1;
}
- touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self touchesCancelled:touches withEvent:event];
}