背景
結(jié)合上一個(gè)篩選框豆赏,寫一個(gè)商品的排序?qū)Ш礁茉啊?shí)現(xiàn)特定種類商品的綜合、銷量筒狠,價(jià)格排序猪狈。
創(chuàng)建視圖
把每一個(gè)選項(xiàng)卡做成自定義的視圖ScreenNavBarItem,給這個(gè)view三種狀態(tài):未選中辩恼,單次選中雇庙,雙次選中(選中狀態(tài)下有升序和降序兩種)。
/**
item的樣式
- ItemSelelctTypeNone: 未選中
- ItemSelelctTypeSingleType: 單擊
- ItemSelelctTypeDoubleType: 雙擊
*/
typedef NS_ENUM(NSInteger,ItemSelelctType) {
ItemSelelctTypeNone = 0,
ItemSelelctTypeSingleType = 1,
ItemSelelctTypeDoubleType = 2
};
不同的狀態(tài)他們都有不同的樣式灶伊。比如title的文案變色還有圖片更改疆前。這部分的處理交給數(shù)據(jù)模型。
好處有:
- 可以通過創(chuàng)建符合約定的model聘萨,就能創(chuàng)建任意多的item竹椒。
- 如果item需要更改樣式的話,我們只需要改變model然后刷新就好了米辐。
其中的itemCode用來做這個(gè)item唯一標(biāo)識(shí)碾牌,方便后面的點(diǎn)擊事件的邏輯處理。
@interface ScreenNavDataModel : NSObject
@property(nonatomic, copy) NSString *title;
@property(nonatomic, copy) NSString *normalImage;
@property(nonatomic, copy) NSString *selectSingleImage;
@property(nonatomic, copy) NSString *selectDoubleImage;
@property(nonatomic, assign) NSInteger itemCode;
@property(nonatomic, assign) ItemSelelctType type;
@end
然后添加item的初始化方法和外部刷新方法儡循。
/**
初始化方法
@param model 數(shù)據(jù)源
@return 實(shí)例
*/
- (instancetype)initWithItemModel:(ScreenNavDataModel *)model;
/**
更新item的樣式
@param type 樣式的枚舉
*/
- (void)updateWithType:(ItemSelelctType)type;
然后將這些按鈕組裝起來舶吗,放入一個(gè)view(AllProductScreenNavBar)內(nèi)。由于item的個(gè)數(shù)不固定择膝,所以要做好寬度的適配誓琼。同樣它也需要一個(gè)初始化和刷新的方法。
#import <UIKit/UIKit.h>
#import "ScreenNavDataModel.h"
#import "ScreenNavBarItem.h"
typedef void(^itemSelect)(ScreenNavBarItem *item, NSInteger selectIndex);
@interface AllProductScreenNavBar : UIView
@property(nonatomic, copy) itemSelect itemSelect;
/**
初始化
@param dataArray item數(shù)據(jù)源
@return 實(shí)例
*/
- (instancetype)initWithScreenNavData:(NSArray<ScreenNavDataModel *> *) dataArray;
/**
更新視圖
@param dataModel 更新選中方案的數(shù)組
*/
- (void)updateWithScreenNavData:(NSArray<ScreenNavDataModel *> *)dataModel;
@end
接下來是要思考處理頁面的交互了肴捉。
頁面交互
在AllProductScreenNavBar中腹侣,我們定義了一個(gè)block來傳遞事件。參數(shù)item是為了能夠拿到item的code標(biāo)識(shí)齿穗。通過給item添加手勢(shì)傲隶,點(diǎn)擊后回觸發(fā)這個(gè)block。我們通過計(jì)算的方式拿到點(diǎn)擊的index窃页。
- (void)userSelectIndexItem:(UITapGestureRecognizer *)tap {
if (self.itemSelect) {
//找到點(diǎn)擊的item跺株,并將其他item的選中狀態(tài)置空
CGFloat centerX = CGRectGetMidX(tap.view.frame);
NSInteger selectIndex = 0;
for (int i = 0; i < self.itemArray.count; i ++) {
CGFloat minX = i*self.unitWidth;
CGFloat maxX = (1+i)*self.unitWidth;
if (centerX > minX && centerX < maxX ) {
selectIndex = i;
}
}
ScreenNavBarItem *item = self.itemArray[selectIndex];
// ScreenNavBarItem *item = (ScreenNavBarItem *)tap.view;
self.itemSelect(item,selectIndex);
}
}
其實(shí)我們可以只把item block出去复濒,然后controller里面遍歷一下,如果block出去的item == listModel里面的某一個(gè)就可以拿到這個(gè)index乒省∏删保或者直接定義itemCode 就是當(dāng)前item的下標(biāo)也可以。
在ViewController中我們初始化一個(gè)AllProductScreenNavBar的實(shí)例袖扛,并寫一下他的事件處理砸泛。
_bar = [[AllProductScreenNavBar alloc] initWithScreenNavData:_listModel.list];
__weak typeof(self) weakSelf = self;
_bar.itemSelect = ^(ScreenNavBarItem *item, NSInteger selectIndex) {
//更改item的點(diǎn)擊類型
//點(diǎn)擊行(無>單擊,點(diǎn)擊>雙擊蛆封,雙擊>單擊)
//非點(diǎn)擊行 任意狀態(tài)>無
[weakSelf.listModel handleItemModelArrayWithItemSelect:selectIndex];
//根據(jù)點(diǎn)擊狀態(tài)和code得到排序類型
item.currentType = weakSelf.listModel.list[selectIndex].type;
ProductSortType type = [weakSelf currentItemState:item.currentItemCode itemCurrentType:item.currentType];
//處理事件
[weakSelf eventWithSortType:type];
[weakSelf updateData];
};
因?yàn)楹Y選和其他的排序是可以共存的唇礁,所以要在篩選框的代理方法中處理一下這種特殊情況。主要是篩選框的點(diǎn)擊狀態(tài)這塊需要和其他的item分開惨篱。有一點(diǎn)需要注意垒迂,如果這個(gè)狀態(tài)沒有更改就不用刷新。比如重復(fù)點(diǎn)開篩選框關(guān)閉篩選框操作妒蛇,這個(gè)時(shí)候狀態(tài)就不必更改
- (void)alertViewDidSelectSureButtonWithId:(NSString *)tagId {
if (tagId.length) {
_tagId = tagId;
//傳回來的ID
ScreenNavDataModel *model4 = _listModel.list[3];
if (model4.type != ItemSelelctTypeSingleType) {
model4.type = ItemSelelctTypeSingleType;
[self updateData];
}
_StatusLabel.text = [NSString stringWithFormat:@"%@+tagId=%@",[_StatusLabel.text substringToIndex:4],_tagId];
}
}
- (void)alertViewDidSelectResetButtonClick {
_tagId = @"";
_lastScreenModel = nil;
ScreenNavDataModel *model4 = _listModel.list[3];
if (model4.type != ItemSelelctTypeNone) {
model4.type = ItemSelelctTypeNone;
_StatusLabel.text = [NSString stringWithFormat:@"%@+tagId被重置",[_StatusLabel.text substringToIndex:4]];
[self updateData];
}
}
反思
當(dāng)初這塊的代碼是走一步看一步寫的沒有思考那么多机断,很多地方都沒有考慮周到,散發(fā)出一種壞味道绣夺。從項(xiàng)目中抽離出來后吏奸,給重構(gòu)了一遍。 本來判斷的排序類型是放在ScreenNavBarItem的陶耍,然后通過硬編碼的方式根據(jù)每個(gè)model的title來區(qū)分不同的類型在block出去奋蔚,這樣很不友好,不利于復(fù)用烈钞。如果需要一個(gè)新的排序的話泊碑,還得修改這個(gè)item.
- (ProductSortType)currentItemState {
if ([_title isEqualToString:@"綜合"]) {
return ProductSortTypeComprehensive;
}
if ([_title isEqualToString:@"銷量"]) {
return ProductSortTypeSalesDesc;
}
if ([_title isEqualToString:@"價(jià)格"]) {
if (_currentType == ItemSelelctTypeSingleType) {
return ProductSortTypePriceAsc;
} else if (_currentType == ItemSelelctTypeDoubleType) {
return ProductSortTypePriceDesc;
}
}
if ([_title isEqualToString:@"篩選"]) {
return ProductSortTypeScreening;
}
return 0;
}
還有數(shù)據(jù)處理部分放在了listModel里面,借用了
- (NSString *)substringToIndex:(NSUInteger)to;
這種方式來返回一個(gè)新的listModel毯欣。以前是在controller里面處理馒过。
代碼
https://github.com/bumingxialuo/NavTool
如果本文中的方法或者思路對(duì)你有些幫助,點(diǎn)個(gè)star哦酗钞。