uitableviewcell switch stepper ios objective-c swift uitableview dropdown

switch - Lista desplegable en UITableView en iOS



uitableviewcell in swift (12)

Aquí hay una solución basada en MVC .

Cree una clase de modelo ClsMenuGroup para sus secciones

class ClsMenuGroup: NSObject { // We can also add Menu group''s name and other details here. var isSelected:Bool = false var arrMenus:[ClsMenu]! }

Cree una clase de modelo ClsMenu para sus filas

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 } }

Cree una matriz de grupos en su ViewController

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(){ } }

He usado HDTableDataSource en lugar de los métodos de fuente de datos de Tableview. Puede encontrar un ejemplo de HDTableDataSource de Github.

Las ventajas del código anterior son

  1. En cualquier momento puede cambiar el orden de cualquier menú o sección o menú y sección de intercambio, sin cambiar otras funciones.
  2. No necesitará agregar un código largo de otra cosa si ladder en los métodos de delegado de su vista de tabla
  3. Puede especificar ícono, título u otro atributo para su elemento de menú por separado, como agregar un recuento de credenciales, cambiar el color del menú seleccionado, etc.
  4. También puede usar varias celdas o secciones aplicando cambios menores al código existente

¿Cómo crear este tipo de vista de tabla en iOS?

Aquí, si tocamos en la 1ra fila ''Cuenta'', automáticamente se desplazará con algunas filas más que se muestran en Imagen. Y si nuevamente tocamos Cuenta, esa vista se ocultará.


La forma más fácil y natural de implementar esto es a través de celdas de vista de tabla. Sin vistas de celda expandibles, sin encabezados de sección, celdas simples y simples (después de todo, estamos en una vista de tabla).

El diseño es el siguiente:

  • utilizando un enfoque MVVM, cree una clase CollapsableViewModel que contenga la información necesaria para configurar la celda: etiqueta, imagen
  • Además del anterior, hay dos campos adicionales: children , que es una matriz de objetos CollapsableViewModel , y isCollapsed , que mantiene el estado del menú desplegable
  • el controlador de vista contiene una referencia a la jerarquía de CollapsableViewModel , así como una lista plana que contiene los modelos de vista que se mostrarán en la pantalla (la propiedad displayedRows )
  • cada vez que se toca una celda, verifique si tiene elementos insertRowsAtIndexPaths() y agregue o elimine filas tanto en las filas displayedRows como en la vista de tabla, a través de las insertRowsAtIndexPaths() y deleteRowsAtIndexPaths() .

El código Swift es el siguiente (tenga en cuenta que el código solo utiliza la propiedad de label del modelo de vista para mantenerlo limpio):

import UIKit class CollapsableViewModel { let label: String let image: UIImage? let children: [CollapsableViewModel] var isCollapsed: Bool init(label: String, image: UIImage? = nil, children: [CollapsableViewModel] = [], isCollapsed: Bool = true) { self.label = label self.image = image self.children = children self.isCollapsed = isCollapsed } } class CollapsableTableViewController: UITableViewController { let data = [ CollapsableViewModel(label: "Account", image: nil, children: [ CollapsableViewModel(label: "Profile"), CollapsableViewModel(label: "Activate account"), CollapsableViewModel(label: "Change password")]), CollapsableViewModel(label: "Group"), CollapsableViewModel(label: "Events", image: nil, children: [ CollapsableViewModel(label: "Nearby"), CollapsableViewModel(label: "Global"), ]), CollapsableViewModel(label: "Deals"), ] var displayedRows: [CollapsableViewModel] = [] override func viewDidLoad() { super.viewDidLoad() displayedRows = data } override func numberOfSections(in tableView: UITableView) -> Int { return 1 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return displayedRows.count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "CellIdentifier") ?? UITableViewCell() let viewModel = displayedRows[indexPath.row] cell.textLabel!.text = viewModel.label return cell } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: false) let viewModel = displayedRows[indexPath.row] if viewModel.children.count > 0 { let range = indexPath.row+1...indexPath.row+viewModel.children.count let indexPaths = range.map { IndexPath(row: $0, section: indexPath.section) } tableView.beginUpdates() if viewModel.isCollapsed { displayedRows.insert(contentsOf: viewModel.children, at: indexPath.row + 1) tableView.insertRows(at: indexPaths, with: .automatic) } else { displayedRows.removeSubrange(range) tableView.deleteRows(at: indexPaths, with: .automatic) } tableView.endUpdates() } viewModel.isCollapsed = !viewModel.isCollapsed } }

La contraparte de Objective-C es fácil de traducir, agregué la versión Swift solo porque es más corta y más legible.

Con un par de pequeños cambios, el código puede usarse para generar listas desplegables de múltiples niveles.

Editar

La gente me preguntó sobre los separadores, esto se puede lograr agregando una clase personalizada CollapsibleTableViewCell que se configura con un modelo de vista (finalmente, mueva la lógica de configuración de la celda desde el controlador a donde pertenece: la celda). Los créditos para la lógica del separador solo para algunas de las celdas van a las personas que responden this pregunta SO.

En primer lugar, actualice el modelo, agregue una propiedad needsSeparator que le indique a la celda de la vista de tabla que represente o no el separador:

class CollapsableViewModel { let label: String let image: UIImage? let children: [CollapsableViewModel] var isCollapsed: Bool var needsSeparator: Bool = true init(label: String, image: UIImage? = nil, children: [CollapsableViewModel] = [], isCollapsed: Bool = true) { self.label = label self.image = image self.children = children self.isCollapsed = isCollapsed for child in self.children { child.needsSeparator = false } self.children.last?.needsSeparator = true } }

Luego, agregue la clase de celda:

class CollapsibleTableViewCell: UITableViewCell { let separator = UIView(frame: .zero) func configure(withViewModel viewModel: CollapsableViewModel) { self.textLabel?.text = viewModel.label if(viewModel.needsSeparator) { separator.backgroundColor = .gray contentView.addSubview(separator) } else { separator.removeFromSuperview() } } override func layoutSubviews() { super.layoutSubviews() let separatorHeight = 1 / UIScreen.main.scale separator.frame = CGRect(x: separatorInset.left, y: contentView.bounds.height - separatorHeight, width: contentView.bounds.width-separatorInset.left-separatorInset.right, height: separatorHeight) } }

cellForRowAtIndexPath debería modificarse para devolver este tipo de celdas:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = (tableView.dequeueReusableCell(withIdentifier: "CollapsibleTableViewCell") as? CollapsibleTableViewCell) ?? CollapsibleTableViewCell(style: .default, reuseIdentifier: "CollapsibleTableViewCell") cell.configure(withViewModel: displayedRows[indexPath.row]) return cell }

Un último paso, elimine los separadores de celdas de vista de tabla predeterminados, ya sea de xib o del código ( tableView.separatorStyle = .none ).


Me gusta la solución @Cristik, hace algún tiempo tuve el mismo problema y mi solución sigue los mismos principios; así que esto es lo que propongo en función de los requisitos que tenía:

  1. Para hacerlo más general, los elementos de la tabla no deben heredar de una clase especializada para la funcionalidad expansiva, sino que debe haber un protocolo que defina las propiedades necesarias

  2. No debería haber una restricción en la cantidad de niveles que podemos expandir. Entonces la tabla puede tener opción, subopción, subopción, etc.

  3. La vista de tabla debe mostrar u ocultar las celdas utilizando cualquiera de las animaciones habituales (sin reloadData )

  4. La acción de expansión no debe estar necesariamente unida al usuario que selecciona la celda, la celda podría tener un interruptor UIS, por ejemplo

La versión simplificada de la implementación ( https://github.com/JuanjoArreola/ExpandableCells ) es la siguiente:

Primero el protocolo:

protocol CellDescriptor: class { var count: Int { get } var identifier: String! { get } }

Una celda no expandible siempre tiene un recuento de 1:

extension CellDescriptor { var count: Int { return 1 } }

Entonces el protocolo celular expandible:

protocol ExpandableCellDescriptor: CellDescriptor { var active: Bool { get set } var children: [CellDescriptor] { get set } subscript(index: Int) -> CellDescriptor? { get } func indexOf(cellDescriptor: CellDescriptor) -> Int? }

Lo bueno de Swift es que podemos escribir parte de la implementación en una extensión de protocolo y todas las clases conformes pueden usar la implementación predeterminada, por lo que podemos escribir el subscript count y la implementación indexOf y, además, indexOf otras funciones útiles como esta:

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) } }

La implementación completa está en el archivo CellDescriptor.swift

Además, en el mismo archivo, hay una clase llamada CellDescriptionArray que implementa ExpandableCellDescriptor y no muestra una celda por sí misma

Ahora, cualquier clase puede ajustarse a los protocolos anteriores sin la necesidad de heredar de una clase específica, para el código de ejemplo en github creé un par de clases: Option y ExpandableOption , así es como se ve ExpandableOption :

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? }

Y esta es una de las subclases UITableViewCell:

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 } }

Tenga en cuenta que puede configurar la celda y su clase de la manera que desee, puede agregar imágenes, etiquetas, interruptores, etc. sin restricciones ni cambios en los protocolos necesarios.

Finalmente en TableViewController creamos el árbol de opciones:

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) ... }

El resto de la implementación ahora es muy simple:

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) } }

Puede parecer una gran cantidad de código, pero la mayor parte se escribe solo una vez, la mayor parte de la información necesaria para dibujar la vista de la tabla existe correctamente en el archivo CellDescriptor.swift, el código de configuración de la celda existe dentro de las subclases UITableViewCell y hay relativamente pocos códigos en el TableViewController en sí.

Espero eso ayude.


Necesita un TableView plegable. Para lograr eso, en su TableView debe realizar un seguimiento de qué secciones se contraen (contraen) y cuáles se expanden. Para esto, necesita mantener un conjunto de índices de secciones que se expanden, o una matriz booleana donde el valor de cada índice indica si la sección correspondiente se expande o no. Verifique los valores en el índice específico mientras asigna altura a una fila determinada. Consulte este enlace para obtener más ayuda.

Puede obtener información sobre las vistas de tabla seccionales here .

Hay bibliotecas de terceros disponibles en Github que pueden salvarlo del hustel. Eche un vistazo a CollapsableTableView o CollapsableTable-Swift


No hay un control integrado para vistas de árbol como vistas en el marco de iOS: UIKit . Como lo señalaron otros usuarios, probablemente la solución más simple (sin usar ninguna biblioteca externa) es agregar alguna lógica personalizada al delegado y fuente de datos de UITableView para imitar el comportamiento deseado.

Afortunadamente, hay algunas bibliotecas de código abierto que le permiten implementar la vista de árbol deseada como vista sin preocuparse por los detalles de las operaciones de expansión / colapso. Hay un par de ellos disponibles para la plataforma iOS. En la mayoría de los casos, estas bibliotecas simplemente envuelven UITableView y le proporcionan una interfaz fácil de programar que le permite concentrarse en su problema y no en los detalles de implementación de la vista de árbol.

Personalmente, soy el autor de la biblioteca RATreeView que tiene como objetivo minimizar el costo necesario para crear una vista de árbol como vistas en iOS. Puede consultar proyectos de ejemplo (disponibles en Objective-c y Swift ) para verificar cómo funciona y se comporta este control. Usando mi control, es realmente simple crear la vista que desea:

  1. DataObject estructura DataObject se usará para mantener información sobre el nodo de vista de árbol; será responsable de mantener información sobre el título de la celda, su imagen (si la celda tiene imagen) y sus elementos secundarios (si la celda tiene elementos secundarios).

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]()) } }

  1. Declararemos el protocolo TreeTableViewCell e implementaremos dos celdas que se TreeTableViewCell a ese protocolo. Una de estas celdas se usará para mostrar elementos raíz y otra se usará para mostrar elementos secundarios de los elementos raíz.

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 } }

  1. En nuestro controlador de vista (MVC) o modelo de vista (MVVM) definimos la estructura de datos responsable de la copia de seguridad de nuestra vista de árbol.

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]

  1. A continuación, tendremos que implementar un par de métodos desde la fuente de datos de RATreeView .

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 }

Tenga en cuenta que al usar mi biblioteca no tiene que preocuparse por la expansión y el colapso de la celda: RATreeView lo RATreeView . Usted es responsable solo de los datos que se utilizan para configurar las celdas; el resto lo maneja el control mismo.


Podría tener Cuenta como una celda que se expande al tocar para revelar tres botones ("Perfil", "Activar cuenta", "Cambiar contraseña"), pero eso crea un problema: tocar alrededor de cada uno de los tres botones contará como "seleccionado por el usuario la celda de la cuenta "y el activador -tableView:didSelectRowAtIndexPath: con la expansión / colapso resultante de la celda.

O puede hacer que cada una de las opciones ocultas ("Perfil", "Activar cuenta", "Cambiar contraseña") sea una celda de vista de tabla separada. Pero no sé cómo podría animar las tres celdas en su totalidad expandiéndose y contrayéndose (en lugar de expandirse por separado desde la altura cero hasta la completamente expandida).

Entonces, quizás la mejor solución es:

  1. Tenga las celdas pares (índices: 0, 2, 4 ...) para cumplir con el rol de "Título del menú" y "Alternar abrir / cerrar menú" (hacia las celdas impares asociadas que se describen a continuación).
  2. Intercale las celdas del "cuerpo del menú" (inicialmente contraídas), cada una con un botón por opción (por ejemplo, "Perfil", "Activar cuenta", "Cambiar contraseña"), dispuestas verticalmente, en los índices impares (1, 3, 5. ..). Use target-action para responder al usuario seleccionando cada opción / botón.
  3. Implemente el método de delegado de vista de tabla para que solo se puedan seleccionar las celdas pares (encabezados de menú) e implemente la lógica de selección para expandir / contraer la correspondiente celda impar (dentro de -tableView: didSelectRowAtIndexPath :). Por ejemplo, al seleccionar la celda en el índice 0 ("Cuenta") se obtiene expandir / contraer la celda en el índice 1 (menú con las opciones "Perfil", "Activar cuenta", "Cambiar contraseña").

No es el uso más elegante de UITableView, pero hará el trabajo.


Por lo general, lo hago estableciendo la altura de la fila. Por ejemplo, tiene dos elementos de menú con listas desplegables:

  • Menú 1
    • Artículo 1.1
    • Artículo 1.2
    • Artículo 1.3
  • Menú 2
    • Artículo 2.1
    • Artículo 2.2

Por lo tanto, debe crear una vista de tabla con 2 secciones. La primera sección contiene 4 filas (Menú 1 y sus elementos) y la sección de segundos contiene 3 filas (Menú 2 y sus elementos).

Siempre establece la altura solo para la primera fila de la sección. Y si el usuario hace clic en la primera fila, expande las filas de esta sección configurando la altura y vuelve a cargar esta sección.


Según la respuesta de @sticker, puede vincular el tiempo de ejecución

objc_setAssociatedObject

para el índice de sección, y use su lógica. Y mientras usa tapgesture en la vista de encabezado, puede obtener el índice de sección como

objc_getAssociatedObject. UITapGestureRecognizer *singleTapRecogniser = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(gestureHandler:)] autorelease]; [singleTapRecogniser setDelegate:self]; singleTapRecogniser.numberOfTouchesRequired = 1; singleTapRecogniser.numberOfTapsRequired = 1; [sectionHeaderView addGestureRecognizer:singleTapRecogniser];

Si desea una biblioteca de terceros, puede probar this solución.


Si no desea utilizar ninguna biblioteca externa, puede hacer 2 celdas personalizadas. Uno que se muestra antes de expandirse y el otro después de expandirse (con diferentes identificadores). Y cuando hace clic en la celda, verifique si la celda está expandida o no. De lo contrario, use el identificador de celda expandida; de lo contrario, el identificador de celda no expandida.

Es la mejor y limpia manera de crear una celda de vista de tabla expandida.


la manera fácil de hacer esto es usar el encabezado de sección UITableView como celda-> y establecer el número de fila en 0 y section.count para colapsar y expandir el estado.

  • .Este es el encabezado de la sección TableView, se expande -> section.count else return 0.

    -Célula normal

    -Célula normal

    -Célula normal

  • .Este es el encabezado de la sección TableView, se expande -> section.count else return 0.

    -Célula normal

    -Célula normal

    -Célula normal


Puede configurar fácilmente una celda para MIRAR como un encabezado, y configurar tableView: didSelectRowAtIndexPath para expandir o contraer la sección en la que se encuentra manualmente. Si almacenara una matriz de booleanos correspondiente al valor "gastado" de cada una de sus secciones. Luego, puede hacer que tableView:didSelectRowAtIndexPath en cada una de las filas de encabezado personalizadas alterne este valor y luego vuelva a cargar esa sección específica.

- (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]; } }

Luego configuraría su número numberOfRowsInSection para verificar el valor de mybooleans y devolver 1 si la sección no se expande, o 1+ el número de elementos en la sección, si se expande.

- (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; } }

También tendría que actualizar su cellForRowAtIndexPath para devolver una celda de encabezado personalizada para la primera fila de cualquier section .

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section es la mejor manera de proporcionar su "encabezado personalizado", ya que eso es exactamente lo que está diseñado para hacer.

Para obtener más detalles, consulte esta Answer o esta PKCollapsingTableViewSections .

Además, puede obtener este tipo de vistas de tabla usando setIndentationLevel . Consulte este DemoCode para este ejemplo. Creo que esta es la mejor solución para las vistas de tabla desplegables.

Si desea crear un encabezado simple y un menú desplegable de celda, consulte STCollapseTableView .

Espero, esto es lo que estás buscando. Cualquier inquietud vuelve a mí. :)


@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]; } }

Espero que ayude :)