具有行展开和折叠选项的表视图的最佳方法
best approach for tableview with rows expand and collapse option
我的视图包含 table视图和搜索栏。当我开始搜索时,它应该搜索 table 视图。以下是 table 查看所附图片的示例数据:
学生 1、学生 2 是 table
中的不同部分
假设我搜索 "S" 那么结果应该如下所示,其中匹配的行应该是可见的,不匹配的行应该是隐藏的,按钮 "Show Remaining" 应该是可见的。当我点击 "Show Remaining" 按钮时,它应该显示同一部分的剩余行:
我如何实现这一点,如果可能,请提供上述场景的示例。
有许多可用于展开和折叠的示例 table。
你可以参考下面的例子
http://www.appcoda.com/expandable-table-view/
通过在 plist 文件中动态设置。您可以保持可展开和可折叠 table视图。
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let currentCellDescriptor = getCellDescriptorForIndexPath(indexPath)
let cell = tableView.dequeueReusableCellWithIdentifier(currentCellDescriptor["cellIdentifier"] as! String, forIndexPath: indexPath) as! CustomCell
if currentCellDescriptor["cellIdentifier"] as! String == "idCellNormal" {
if let primaryTitle = currentCellDescriptor["primaryTitle"] {
cell.textLabel?.text = primaryTitle as? String
}
if let secondaryTitle = currentCellDescriptor["secondaryTitle"] {
cell.detailTextLabel?.text = secondaryTitle as? String
}
}
else if currentCellDescriptor["cellIdentifier"] as! String == "idCellTextfield" {
cell.textField.placeholder = currentCellDescriptor["primaryTitle"] as? String
}
else if currentCellDescriptor["cellIdentifier"] as! String == "idCellSwitch" {
cell.lblSwitchLabel.text = currentCellDescriptor["primaryTitle"] as? String
let value = currentCellDescriptor["value"] as? String
cell.swMaritalStatus.on = (value == "true") ? true : false
}
else if currentCellDescriptor["cellIdentifier"] as! String == "idCellValuePicker" {
cell.textLabel?.text = currentCellDescriptor["primaryTitle"] as? String
}
else if currentCellDescriptor["cellIdentifier"] as! String == "idCellSlider" {
let value = currentCellDescriptor["value"] as! String
cell.slExperienceLevel.value = (value as NSString).floatValue
}
return cell
}
对于 UITableView 中的 collapsing/expanding 行为,我想不出任何一般的 "best approach" 行为。我怀疑这就是苹果没有提供的原因。相反,他们给了我们足够的工具来按照我们希望的方式实施它。
在你的具体情况下,我有下一个愿景。
我不知道,也不需要知道,您究竟是如何处理任务的模型方面的,以下代码仅用于此答案的目的,即 - 为您提供您寻求的折叠行为。
我建议以这种方式实现您想要的行为。在 ViewController 中,您可以控制搜索栏和 table 查看添加字段
@property NSArray* filteredStudents;
@property NSMutableIndexSet* expandedStudents;
当没有进行搜索时,这些属性将为 nil(或空实例)。搜索开始后(输入第一个符号或点击 "Search" 按钮 - 无论您最喜欢什么) - 实例化它们。
filteredStudents
- 包含满足您的搜索条件的那些学生的引用的数组。每次执行搜索时都会更新。每次更新它时 - 执行 [tableView reloadData].
expandedStudents
- 索引集告诉您用户在哪些部分按下了 "Show all"。不要忘记 - 它只包含部分编号。因此,当 filteredStudents
数组更改时,您必须手动更新它。例如,当您输入 "S" - 您会显示两个学生。您对第二个学生按显示全部。 expandStudents
现在有数字 1
了。比你输入 "Se",只剩下第二个学生。在这种情况下,您必须将 expandedStudents
中的 1
替换为 0
,因为第二个学生的部分成为第一个。
另一个属性
@property BOOL searchMode;
每当显示过滤结果时,此 属性 应该为 YES。否 - 否则。
接下来,像这样修改 UITableViewDataSource 和 Delegate 方法
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
if(searchMode) {
return self.filteredStudents.count;
}
else {
//Your current behaviour here
}
}
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if(searchMode) {
if([self.expandedStudents containsIndex:section]) { // Button "Show all" was already tapped for this student
return /* Number of all fields you need to show for student. Just like you do now. It is for expanded section */
}
else {
return /* Number of fields that satisfy search criteria for student self.filteredStudents[section] */
}
}
else {
//Your current behaviour here
}
}
- (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if(searchMode) {
if(![self.expandedStudents containsIndex:section] && /* indexPath corresponds to place, where "Show All" should be */) {
//Create "Show All" cell
}
else {
//Create cell that contains info from filtered student fields
}
}
else {
//Your current behaviour here
}
}
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if(searchMode && ![self.expandedStudents containsIndex:section] && /* indexPath corresponds to place, where "Show All" should be */) {
//"Show All" button pressed
[self.expandedStudents addIndex:indexPath.section];
[tableView beginUpdates];
/* For each row, that wasn't presented in collapsed student perform insertRowsAtIndexPaths:withRowAnimation: method */
[tableView endUpdates];
}
else {
//Your current behaviour
}
}
我已经根据您的要求创建了代码,请在下面找到 url 下载代码并查看它。
Link : https://www.dropbox.com/s/22ijwgxybn98wyj/ExpandCollapse.zip?dl=0
Environment : Xcode 8 and Objective-C
突出显示我完成的代码。
我已经根据你的结构创建了学生的NSObject,创建了两个(StudentListCell和ShowMoreCell)UITableViewCell
用于显示学生的记录和"Show Remaining button"记录。
我也拿了两个(arrStudentInfo, arrSearchInfo)NSMutablebleArray
来保存搜索记录和原始记录
之后我在 ViewController.m
初始化了 student 的 NSObject。请找到以下代码。
- (void)setupData {
self.arrStudentInfo = [[NSMutableArray alloc] init];
self.arrSearchInfo = [[NSMutableArray alloc] init];
StudentInfo *student_1 = [[StudentInfo alloc] init];
student_1.name = @"Sia S";
student_1.style = @"S";
student_1.trend = @"S";
student_1.age = @"60";
student_1.locale = @"Node";
student_1.street = @"BR";
student_1.city = @"JP";
student_1.country = @"US";
student_1.isOpen = YES;
student_1.isShowMore = NO;
[self.arrStudentInfo addObject:student_1];
StudentInfo *student_2 = [[StudentInfo alloc] init];
student_2.name = @"Nitin";
student_2.style = @"N";
student_2.trend = @"N";
student_2.age = @"40";
student_2.locale = @"Node";
student_2.street = @"BR";
student_2.city = @"SP";
student_2.country = @"US";
student_2.isOpen = YES;
student_2.isShowMore = NO;
[self.arrStudentInfo addObject:student_2];
}
我创建了return当前激活的方法,这意味着搜索记录或数组的原始记录。
- (NSMutableArray *)getCurrentArray {
if(self.isSearch)
return self.arrSearchInfo;
else
return self.arrStudentInfo;
}
根据场景加载数据,
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
StudentInfo *studentInfo = [self getCurrentArray][indexPath.section];
if(indexPath.row == SHOW_MORE_INDEX && studentInfo.isShowMore == NO) {
static NSString *strCellIdentifier = @"ShowMoreCell";
ShowMoreCell *cell = [tableView dequeueReusableCellWithIdentifier:strCellIdentifier];
cell.btnShowMore.tag = indexPath.section;
[cell.btnShowMore addTarget:self action:@selector(clickONShowMore:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
else {
static NSString *strCellIdentifier = @"StudentListCell";
StudentListCell *cell = [tableView dequeueReusableCellWithIdentifier:strCellIdentifier];
[cell setupCell:studentInfo indexPath:indexPath];
return cell;
}
}
用于搜索学生姓名,
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
[searchBar resignFirstResponder];
self.isSearch = YES;
NSPredicate *predicateStudent = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"self.name CONTAINS[cd] '%@'",searchBar.text]];
NSArray *arrGetSearch = [self.arrStudentInfo filteredArrayUsingPredicate:predicateStudent];
if(self.arrSearchInfo.count)
[self.arrSearchInfo removeAllObjects];
[self.arrSearchInfo addObjectsFromArray:arrGetSearch];
[self.tblView reloadData];
}
如果用户在 UISearchbar
上单击 "X" 按钮,则再次加载原始记录。
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
if([searchText isEqualToString:@""] || searchText==nil) {
[searchBar resignFirstResponder];
self.isSearch = NO;
[self.tblView reloadData];
}
}
希望这对你有用。
我的视图包含 table视图和搜索栏。当我开始搜索时,它应该搜索 table 视图。以下是 table 查看所附图片的示例数据:
学生 1、学生 2 是 table
中的不同部分假设我搜索 "S" 那么结果应该如下所示,其中匹配的行应该是可见的,不匹配的行应该是隐藏的,按钮 "Show Remaining" 应该是可见的。当我点击 "Show Remaining" 按钮时,它应该显示同一部分的剩余行:
我如何实现这一点,如果可能,请提供上述场景的示例。
有许多可用于展开和折叠的示例 table。
你可以参考下面的例子
http://www.appcoda.com/expandable-table-view/
通过在 plist 文件中动态设置。您可以保持可展开和可折叠 table视图。
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let currentCellDescriptor = getCellDescriptorForIndexPath(indexPath)
let cell = tableView.dequeueReusableCellWithIdentifier(currentCellDescriptor["cellIdentifier"] as! String, forIndexPath: indexPath) as! CustomCell
if currentCellDescriptor["cellIdentifier"] as! String == "idCellNormal" {
if let primaryTitle = currentCellDescriptor["primaryTitle"] {
cell.textLabel?.text = primaryTitle as? String
}
if let secondaryTitle = currentCellDescriptor["secondaryTitle"] {
cell.detailTextLabel?.text = secondaryTitle as? String
}
}
else if currentCellDescriptor["cellIdentifier"] as! String == "idCellTextfield" {
cell.textField.placeholder = currentCellDescriptor["primaryTitle"] as? String
}
else if currentCellDescriptor["cellIdentifier"] as! String == "idCellSwitch" {
cell.lblSwitchLabel.text = currentCellDescriptor["primaryTitle"] as? String
let value = currentCellDescriptor["value"] as? String
cell.swMaritalStatus.on = (value == "true") ? true : false
}
else if currentCellDescriptor["cellIdentifier"] as! String == "idCellValuePicker" {
cell.textLabel?.text = currentCellDescriptor["primaryTitle"] as? String
}
else if currentCellDescriptor["cellIdentifier"] as! String == "idCellSlider" {
let value = currentCellDescriptor["value"] as! String
cell.slExperienceLevel.value = (value as NSString).floatValue
}
return cell
}
对于 UITableView 中的 collapsing/expanding 行为,我想不出任何一般的 "best approach" 行为。我怀疑这就是苹果没有提供的原因。相反,他们给了我们足够的工具来按照我们希望的方式实施它。
在你的具体情况下,我有下一个愿景。 我不知道,也不需要知道,您究竟是如何处理任务的模型方面的,以下代码仅用于此答案的目的,即 - 为您提供您寻求的折叠行为。
我建议以这种方式实现您想要的行为。在 ViewController 中,您可以控制搜索栏和 table 查看添加字段
@property NSArray* filteredStudents;
@property NSMutableIndexSet* expandedStudents;
当没有进行搜索时,这些属性将为 nil(或空实例)。搜索开始后(输入第一个符号或点击 "Search" 按钮 - 无论您最喜欢什么) - 实例化它们。
filteredStudents
- 包含满足您的搜索条件的那些学生的引用的数组。每次执行搜索时都会更新。每次更新它时 - 执行 [tableView reloadData].
expandedStudents
- 索引集告诉您用户在哪些部分按下了 "Show all"。不要忘记 - 它只包含部分编号。因此,当 filteredStudents
数组更改时,您必须手动更新它。例如,当您输入 "S" - 您会显示两个学生。您对第二个学生按显示全部。 expandStudents
现在有数字 1
了。比你输入 "Se",只剩下第二个学生。在这种情况下,您必须将 expandedStudents
中的 1
替换为 0
,因为第二个学生的部分成为第一个。
另一个属性
@property BOOL searchMode;
每当显示过滤结果时,此 属性 应该为 YES。否 - 否则。
接下来,像这样修改 UITableViewDataSource 和 Delegate 方法
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
if(searchMode) {
return self.filteredStudents.count;
}
else {
//Your current behaviour here
}
}
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if(searchMode) {
if([self.expandedStudents containsIndex:section]) { // Button "Show all" was already tapped for this student
return /* Number of all fields you need to show for student. Just like you do now. It is for expanded section */
}
else {
return /* Number of fields that satisfy search criteria for student self.filteredStudents[section] */
}
}
else {
//Your current behaviour here
}
}
- (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if(searchMode) {
if(![self.expandedStudents containsIndex:section] && /* indexPath corresponds to place, where "Show All" should be */) {
//Create "Show All" cell
}
else {
//Create cell that contains info from filtered student fields
}
}
else {
//Your current behaviour here
}
}
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if(searchMode && ![self.expandedStudents containsIndex:section] && /* indexPath corresponds to place, where "Show All" should be */) {
//"Show All" button pressed
[self.expandedStudents addIndex:indexPath.section];
[tableView beginUpdates];
/* For each row, that wasn't presented in collapsed student perform insertRowsAtIndexPaths:withRowAnimation: method */
[tableView endUpdates];
}
else {
//Your current behaviour
}
}
我已经根据您的要求创建了代码,请在下面找到 url 下载代码并查看它。
Link : https://www.dropbox.com/s/22ijwgxybn98wyj/ExpandCollapse.zip?dl=0
Environment : Xcode 8 and Objective-C
突出显示我完成的代码。
我已经根据你的结构创建了学生的NSObject,创建了两个(StudentListCell和ShowMoreCell)UITableViewCell
用于显示学生的记录和"Show Remaining button"记录。
我也拿了两个(arrStudentInfo, arrSearchInfo)NSMutablebleArray
来保存搜索记录和原始记录
之后我在 ViewController.m
初始化了 student 的 NSObject。请找到以下代码。
- (void)setupData {
self.arrStudentInfo = [[NSMutableArray alloc] init];
self.arrSearchInfo = [[NSMutableArray alloc] init];
StudentInfo *student_1 = [[StudentInfo alloc] init];
student_1.name = @"Sia S";
student_1.style = @"S";
student_1.trend = @"S";
student_1.age = @"60";
student_1.locale = @"Node";
student_1.street = @"BR";
student_1.city = @"JP";
student_1.country = @"US";
student_1.isOpen = YES;
student_1.isShowMore = NO;
[self.arrStudentInfo addObject:student_1];
StudentInfo *student_2 = [[StudentInfo alloc] init];
student_2.name = @"Nitin";
student_2.style = @"N";
student_2.trend = @"N";
student_2.age = @"40";
student_2.locale = @"Node";
student_2.street = @"BR";
student_2.city = @"SP";
student_2.country = @"US";
student_2.isOpen = YES;
student_2.isShowMore = NO;
[self.arrStudentInfo addObject:student_2];
}
我创建了return当前激活的方法,这意味着搜索记录或数组的原始记录。
- (NSMutableArray *)getCurrentArray {
if(self.isSearch)
return self.arrSearchInfo;
else
return self.arrStudentInfo;
}
根据场景加载数据,
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
StudentInfo *studentInfo = [self getCurrentArray][indexPath.section];
if(indexPath.row == SHOW_MORE_INDEX && studentInfo.isShowMore == NO) {
static NSString *strCellIdentifier = @"ShowMoreCell";
ShowMoreCell *cell = [tableView dequeueReusableCellWithIdentifier:strCellIdentifier];
cell.btnShowMore.tag = indexPath.section;
[cell.btnShowMore addTarget:self action:@selector(clickONShowMore:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
else {
static NSString *strCellIdentifier = @"StudentListCell";
StudentListCell *cell = [tableView dequeueReusableCellWithIdentifier:strCellIdentifier];
[cell setupCell:studentInfo indexPath:indexPath];
return cell;
}
}
用于搜索学生姓名,
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
[searchBar resignFirstResponder];
self.isSearch = YES;
NSPredicate *predicateStudent = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"self.name CONTAINS[cd] '%@'",searchBar.text]];
NSArray *arrGetSearch = [self.arrStudentInfo filteredArrayUsingPredicate:predicateStudent];
if(self.arrSearchInfo.count)
[self.arrSearchInfo removeAllObjects];
[self.arrSearchInfo addObjectsFromArray:arrGetSearch];
[self.tblView reloadData];
}
如果用户在 UISearchbar
上单击 "X" 按钮,则再次加载原始记录。
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
if([searchText isEqualToString:@""] || searchText==nil) {
[searchBar resignFirstResponder];
self.isSearch = NO;
[self.tblView reloadData];
}
}
希望这对你有用。