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
- En cualquier momento puede cambiar el orden de cualquier menú o sección o menú y sección de intercambio, sin cambiar otras funciones.
- No necesitará agregar un código largo de otra cosa si ladder en los métodos de delegado de su vista de tabla
- 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.
- También puede usar varias celdas o secciones aplicando cambios menores al código existente
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 objetosCollapsableViewModel
, yisCollapsed
, 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 propiedaddisplayedRows
) -
cada vez que se toca una celda, verifique si tiene elementos
insertRowsAtIndexPaths()
y agregue o elimine filas tanto en las filasdisplayedRows
como en la vista de tabla, a través de lasinsertRowsAtIndexPaths()
ydeleteRowsAtIndexPaths()
.
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:
-
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
-
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.
-
La vista de tabla debe mostrar u ocultar las celdas utilizando cualquiera de las animaciones habituales (sin
reloadData
) -
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:
-
DataObject
estructuraDataObject
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]())
}
}
-
Declararemos el protocolo
TreeTableViewCell
e implementaremos dos celdas que seTreeTableViewCell
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
}
}
- 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]
-
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:
- 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).
- 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.
- 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 :)