iOS中UITableView中的下拉列表
如何在iOS中创建这种类型的tableview??? 在这里,若我们点击第一行“Account”,那个么它会自动滚动显示更多的行,如图所示。iOS中UITableView中的下拉列表,ios,objective-c,swift,uitableview,dropdown,Ios,Objective C,Swift,Uitableview,Dropdown,如何在iOS中创建这种类型的tableview??? 在这里,若我们点击第一行“Account”,那个么它会自动滚动显示更多的行,如图所示。 如果我们再次点击帐户,则该视图将被隐藏。通常我通过设置行高来实现。例如,您有两个带有下拉列表的菜单项: 菜单1 项目1.1 项目1.2 项目1.3 菜单2 项目2.1 项目2.2 因此,您必须创建一个包含两个部分的表视图。第一部分包含4行(菜单1及其项),第二部分包含3行(菜单2及其项) 始终仅为节中的第一行设置高度。如果用户单击第一行,您可以
如果我们再次点击帐户,则该视图将被隐藏。通常我通过设置行高来实现。例如,您有两个带有下拉列表的菜单项:
- 菜单1
- 项目1.1
- 项目1.2
- 项目1.3
- 菜单2
- 项目2.1
- 项目2.2
始终仅为节中的第一行设置高度。如果用户单击第一行,您可以通过设置高度来展开此部分行并重新加载此部分。如果您不喜欢使用任何外部库,则可以创建2个自定义单元格。一个在展开前显示,另一个在展开后显示(具有不同的标识符)。单击单元格时,检查单元格是否展开。如果不是,则使用扩展单元标识符,否则使用非扩展单元标识符
这是创建扩展表视图单元格的最佳且干净的方法。简单的方法是使用UITableView节头作为单元格->并将行数设置为0,将节数设置为折叠和扩展状态
- 。这是TableViewSection标题,isExpand->section.count else 返回0 -正常细胞 -正常细胞 -正常细胞
- 。这是TableViewSection标题,isExpand->section.count else 返回0 -正常细胞 -正常细胞 -正常细胞
或者,您可以将Account作为一个单元格,点击后展开,显示三个按钮(“配置文件”、“激活帐户”、“更改密码”),但这会产生一个问题:点击三个按钮中的每一个按钮将计为“用户选择的帐户单元格”并触发
-tableView:didSelectRowAtIndexPath:
,从而展开/折叠单元格
或者,您可以将每个隐藏选项(“配置文件”、“激活帐户”、“更改密码”)设置为单独的表视图单元格。但我不知道如何将这三个单元格作为一个整体来设置动画(而不是分别从零高度扩展到完全扩展)
因此,也许最好的解决方案是:
这不是UITableView最优雅的用法,但可以完成这项工作。您可以轻松地将单元格设置为标题,并设置
表格视图:didselectRowatinexPath
以手动展开或折叠它所在的部分。如果我存储一个与“expended”对应的布尔数组每个部分的值。然后,您可以让每个自定义标题行上的tableView:didSelectRowAtIndexPath
切换该值,然后重新加载该特定部分
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0) {
///it's the first row of any section so it would be your custom section header
///put in your code to toggle your boolean value here
mybooleans[indexPath.section] = !mybooleans[indexPath.section];
///reload this section
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
}
}
然后设置数字numberofrowsinssection
以检查mybooleans
值,如果未展开节,则返回1;如果展开节,则返回1+节中的项数
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (mybooleans[section]) {
///we want the number of people plus the header cell
return [self numberOfPeopleInGroup:section] + 1;
} else {
///we just want the header cell
return 1;
}
}
您还必须更新单元格for行索引路径
,以返回任何节
中第一行的自定义标题单元格
-(UIView*)tableView:(UITableView*)tableView view for headerinsection:(NSInteger)section
是提供“自己的自定义标题”的更好方法,因为这正是它的设计目的
有关更多详细信息,请参阅此或此
此外,您还可以使用setIndentationLevel
获取这种类型的表视图。请参考此示例。我认为这是下拉表视图的最佳解决方案
如果您想制作一个简单的标题和单元格下拉列表,请参阅
希望,这就是你要找的。如有任何问题,请与我联系。:) 通过表视图单元格实现此功能的最简单、最自然的方法。没有展开的单元格视图,没有节标题,没有简单的单元格(毕竟我们是在一个表视图中) 设计如下:
- 使用MVVM方法,创建一个包含配置单元格所需信息的
类:标签、图像CollapsableViewModel
- 除上述字段外,还有两个额外字段:
,whi子字段
objc_getAssociatedObject. UITapGestureRecognizer *singleTapRecogniser = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(gestureHandler:)] autorelease]; [singleTapRecogniser setDelegate:self]; singleTapRecogniser.numberOfTouchesRequired = 1; singleTapRecogniser.numberOfTapsRequired = 1; [sectionHeaderView addGestureRecognizer:singleTapRecogniser];
class ClsMenuGroup: NSObject { // We can also add Menu group's name and other details here. var isSelected:Bool = false var arrMenus:[ClsMenu]! }
class ClsMenu: NSObject { var strMenuTitle:String! var strImageNameSuffix:String! var objSelector:Selector! // This is the selector method which will be called when this menu is selected. var isSelected:Bool = false init(pstrTitle:String, pstrImageName:String, pactionMehod:Selector) { strMenuTitle = pstrTitle strImageNameSuffix = pstrImageName objSelector = pactionMehod } }
class YourViewController: UIViewController, UITableViewDelegate { @IBOutlet var tblMenu: UITableView! var objTableDataSource:HDTableDataSource! var arrMenuGroups:[AnyObject]! // MARK: - View Lifecycle override func viewDidLoad() { super.viewDidLoad() if arrMenuGroups == nil { arrMenuGroups = Array() } let objMenuGroup = ClsMenuGroup() objMenuGroup.arrMenus = Array() var objMenu = ClsMenu(pstrTitle: "Manu1", pstrImageName: "Manu1.png", pactionMehod: "menuAction1") objMenuGroup.arrMenus.append(objMenu) objMenu = ClsMenu(pstrTitle: "Menu2", pstrImageName: "Menu2.png", pactionMehod: "menuAction2") objMenuGroup.arrMenus.append(objMenu) arrMenuGroups.append(objMenuGroup) configureTable() } func configureTable(){ objTableDataSource = HDTableDataSource(items: nil, cellIdentifier: "SideMenuCell", configureCellBlock: { (cell, item, indexPath) -> Void in let objTmpGroup = self.arrMenuGroups[indexPath.section] as! ClsMenuGroup let objTmpMenu = objTmpGroup.arrMenus[indexPath.row] let objCell:YourCell = cell as! YourCell objCell.configureCell(objTmpMenu) // This method sets the IBOutlets of cell in YourCell.m file. }) objTableDataSource.sectionItemBlock = {(objSection:AnyObject!) -> [AnyObject]! in let objMenuGroup = objSection as! ClsMenuGroup return (objMenuGroup.isSelected == true) ? objMenuGroup.arrMenus : 0 } objTableDataSource.arrSections = self.arrMenuGroups tblMenu.dataSource = objTableDataSource tblMenu.reloadData() } // MARK: - Tableview Delegate func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { let objTmpGroup = self.arrMenuGroups[indexPath.section] as! ClsMenuGroup let objTmpMenu = objTmpGroup.arrMenus[indexPath.row] if objTmpMenu.objSelector != nil && self.respondsToSelector(objTmpMenu.objSelector) == true { self.performSelector(objTmpMenu.objSelector) // Call the method for the selected menu. } tableView.reloadData() } func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let arrViews:[AnyObject] = NSBundle.mainBundle().loadNibNamed("YourCustomSectionView", owner: self, options: nil) let objHeaderView = arrViews[0] as! UIView objHeaderView.sectionToggleBlock = {(objSection:AnyObject!) -> Void in let objMenuGroup = objSection as! ClsMenuGroup objMenuGroup.isSelected = !objMenuGroup.isSelected tableView.reloadData() } return objHeaderView } // MARK: - Menu methods func menuAction1(){ } func menuAction2(){ } }
@interface TestTableViewController () { BOOL showMenu; } @implementation TestTableViewController - (void)viewDidLoad { [super viewDidLoad]; // Uncomment the following line to preserve selection between presentations. // self.clearsSelectionOnViewWillAppear = NO; // Uncomment the following line to display an Edit button in the navigation bar for this view controller. // self.navigationItem.rightBarButtonItem = self.editButtonItem; [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"accountMenu"]; [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"accountSubMenu"]; } #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 2; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { if (section == 0) { // Account Menu return 1; } if (showMenu) { // Profile/Private Account/Change Password return 3; } // Hidden Account Menu return 0; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell; if (indexPath.section == 0) { cell = [tableView dequeueReusableCellWithIdentifier:@"accountMenu" forIndexPath:indexPath]; cell.textLabel.text = @"Account"; } else { cell = [tableView dequeueReusableCellWithIdentifier:@"accountSubMenu" forIndexPath:indexPath]; switch (indexPath.row) { case 0: cell.textLabel.text = @"Profile"; break; case 1: cell.textLabel.text = @"Private Account"; break; case 2: cell.textLabel.text = @"Change Password"; break; default: break; } } return cell; } -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.section == 0) { // Click on Account Menu showMenu = !showMenu; [tableView reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationAutomatic]; } }
class DataObject { let name : String let imageURL : NSURL? private(set) var children : [DataObject] init(name : String, imageURL : NSURL?, children: [DataObject]) { self.name = name self.imageURL = imageURL self.children = children } convenience init(name : String) { self.init(name: name, imageURL: nil, children: [DataObject]()) } }
protocol TreeTableViewCell { func setup(withTitle title: String, imageURL: NSURL?, isExpanded: Bool) } class ChildTreeTableViewCell : UITableViewCell, TreeTableViewCell { func setup(withTitle title: String, imageURL: NSURL?, isExpanded: Bool) { //implementation goes here } } class RootTreeTableViewCell : UITableViewCell, TreeTableViewCell { func setup(withTitle title: String, imageURL: NSURL?, isExpanded: Bool) { //implementation goes here } }
let profileDataObject = DataObject(name: "Profile") let privateAccountDataObject = DataObject(name: "Private Account") let changePasswordDataObject = DataObject(name: "Change Password") let accountDataObject = DataObject(name: "Account", imageURL: NSURL(string: "AccountImage"), children: [profileDataObject, privateAccountDataObject, changePasswordDataObject]) let groupDataObject = DataObject(name: "Group", imageURL: NSURL(string: "GroupImage"), children: []) let eventDataObject = DataObject(name: "Event", imageURL: NSURL(string: "EventImage"), children: []) let dealsDataObject = DataObject(name: "Deals", imageURL: NSURL(string: "DealsImage"), children: []) data = [accountDataObject, groupDataObject, eventDataObject, dealsDataObject]
func treeView(treeView: RATreeView, numberOfChildrenOfItem item: AnyObject?) -> Int { if let item = item as? DataObject { return item.children.count //return number of children of specified item } else { return self.data.count //return number of top level items here } } func treeView(treeView: RATreeView, child index: Int, ofItem item: AnyObject?) -> AnyObject { if let item = item as? DataObject { return item.children[index] //we return child of specified item here (using provided `index` variable) } else { return data[index] as AnyObject //we return root item here (using provided `index` variable) } } func treeView(treeView: RATreeView, cellForItem item: AnyObject?) -> UITableViewCell { let cellIdentifier = item ? “TreeTableViewChildCell” : “TreeTableViewCellRootCell” let cell = treeView.dequeueReusableCellWithIdentifier(cellIdentifier) as! TreeTableViewCell //TreeTableViewCell is a protocol which is implemented by two kinds of //cells - the one responsible for root items in the tree view and another //one responsible for children. As we use protocol we don't care //which one is truly being used here. Both of them can be //configured using data from `DataItem` object. let item = item as! DataObject let isExpanded = treeView.isCellForItemExpanded(item) //this variable can be used to adjust look of the cell by determining whether cell is expanded or not cell.setup(withTitle: item.name, imageURL: item.imageURL, expanded: isExpanded) return cell }
protocol CellDescriptor: class { var count: Int { get } var identifier: String! { get } }
extension CellDescriptor { var count: Int { return 1 } }
protocol ExpandableCellDescriptor: CellDescriptor { var active: Bool { get set } var children: [CellDescriptor] { get set } subscript(index: Int) -> CellDescriptor? { get } func indexOf(cellDescriptor: CellDescriptor) -> Int? }
extension ExpandableCellDescriptor { var count: Int { var total = 1 if active { children.forEach({ total += $0.count }) } return total } var countIfActive: Int { ... } subscript(index: Int) -> CellDescriptor? { ... } func indexOf(cellDescriptor: CellDescriptor) -> Int? { ... } func append(cellDescriptor: CellDescriptor) { children.append(cellDescriptor) } }
class ExpandableOption: ExpandableCellDescriptor { var delegate: ExpandableCellDelegate? var identifier: String! var active: Bool = false { didSet { delegate?.expandableCell(self, didChangeActive: active) } } var children: [CellDescriptor] = [] var title: String? }
class SwitchTableViewCell: UITableViewCell, CellDescrptionConfigurable { @IBOutlet weak var titleLabel: UILabel! @IBOutlet weak var switchControl: UISwitch! var cellDescription: CellDescriptor! { didSet { if let option = cellDescription as? ExpandableOption { titleLabel.text = option.title switchControl.on = option.active } } } @IBAction func activeChanged(sender: UISwitch) { let expandableCellDescriptor = cellDescription as! ExpandableCellDescriptor expandableCellDescriptor.active = sender.on } }
var options = CellDescriptionArray() override func viewDidLoad() { super.viewDidLoad() let account = ExpandableOption(identifier: "ExpandableCell", title: "Account") let profile = Option(identifier: "SimpleCell", title: "Profile") let isPublic = ExpandableOption(identifier: "SwitchCell", title: "Public") let caption = Option(identifier: "SimpleCell", title: "Anyone can see this account") isPublic.append(caption) account.append(profile) account.append(isPublic) options.append(account) let group = ExpandableOption(identifier: "ExpandableCell", title: "Group") group.append(Option(identifier: "SimpleCell", title: "Group Settings")) options.append(group) ... }
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return options.count } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let option = options[indexPath.row]! let cell = tableView.dequeueReusableCellWithIdentifier(option.identifier, forIndexPath: indexPath) (cell as! CellDescrptionConfigurable).cellDescription = option (option as? ExpandCellInformer)?.delegate = self return cell } func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { guard let option = options[indexPath.row] else { return } guard let expandableOption = option as? ExpandableOption else { return } if expandableOption.identifier == "ExpandableCell" { expandableOption.active = !expandableOption.active } } func expandableCell(expandableCell: ExpandableCellDescriptor, didChangeActive active: Bool) { guard let index = options.indexOf(expandableCell) else { return } var indexPaths = [NSIndexPath]() for row in 1..<expandableCell.countIfActive { indexPaths.append(NSIndexPath(forRow: index + row, inSection: 0)) } if active { tableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.Fade) } else { tableView.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.Fade) } }