ios objective-c uitableview datepicker tableviewcell

ios - Expandir y contraer UITableViewCells con DatePicker



objective-c (6)

Estoy compartiendo mi respuesta:

Estoy haciendo todo sin storyboard

Swift 3

1.1 agregar el datePicker

var travelDatePicker: UIDatePicker = { let datePicker = UIDatePicker() datePicker.timeZone = NSTimeZone.local datePicker.backgroundColor = UIColor.white datePicker.layer.cornerRadius = 5.0 datePicker.datePickerMode = .date datePicker.addTarget(self, action: #selector(TableViewController.datePickerValueChanged(_:)), for: .valueChanged) return datePicker }()

1.2 y su método

func datePickerValueChanged(_ sender: UIDatePicker){ let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd" let dateString = dateFormatter.string(from: travelDatePicker.date) self.shareCell.textLabel?.text = "/(dateString)" print("changed") print("Selected value /(dateString)") }

2. Luego, en loadView, muestra la fecha en la celda de arriba con formato

let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd" let dateString = dateFormatter.string(from: travelDatePicker.date) self.shareCell.textLabel?.text = "/(dateString)" travelDatePicker.isHidden = true

3. agregue datePicker a la celda

self.datePickerCell.backgroundColor = UIColor.red self.datePickerCell.addSubview(self.travelDatePicker) self.travelDatePicker.frame = CGRect(x: 0, y: 0, width: 500, height: 216) self.datePickerCell.accessoryType = UITableViewCellAccessoryType.none

4. establecer la altura de la celda

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { if indexPath.section == 1 && indexPath.row == 1{ let height: CGFloat = travelDatePicker.isHidden ? 0.0 : 216.0 return height } return 44.0 }

  1. y finalmente establezca la declaración if en didSelectAt

if (indexPath.section == 1 && indexPath.row == 0) {

travelDatePicker.isHidden = !travelDatePicker.isHidden UIView.animate(withDuration: 0.3, animations: { () -> Void in self.tableView.beginUpdates() // apple bug fix - some TV lines hide after animation self.tableView.deselectRow(at: indexPath, animated: true) self.tableView.endUpdates() }) }

El código completo está aquí con otros elementos, solo tiene la sensación de que funciona la aplicación

import Foundation import UIKit class TableViewController: UITableViewController { var firstNameCell: UITableViewCell = UITableViewCell() var lastNameCell: UITableViewCell = UITableViewCell() var shareCell: UITableViewCell = UITableViewCell() var datePickerCell: UITableViewCell = UITableViewCell() var cityToCell: UITableViewCell = UITableViewCell() var cityFromCell: UITableViewCell = UITableViewCell() var firstNameText: UITextField = UITextField() var lastNameText: UITextField = UITextField() var travelDatePicker: UIDatePicker = { let datePicker = UIDatePicker() datePicker.timeZone = NSTimeZone.local datePicker.backgroundColor = UIColor.white datePicker.layer.cornerRadius = 5.0 datePicker.datePickerMode = .date datePicker.addTarget(self, action: #selector(TableViewController.datePickerValueChanged(_:)), for: .valueChanged) return datePicker }() override func loadView() { super.loadView() let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd" let dateString = dateFormatter.string(from: travelDatePicker.date) self.shareCell.textLabel?.text = "/(dateString)" travelDatePicker.isHidden = true // set the title self.title = "User Options" // construct first name cell, section 0, row 0 self.firstNameCell.backgroundColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.5) self.firstNameText = UITextField(frame: self.firstNameCell.contentView.bounds.insetBy(dx: 15, dy: 0)) self.firstNameText.placeholder = "First Name" self.firstNameCell.addSubview(self.firstNameText) // construct last name cell, section 0, row 1 self.lastNameCell.backgroundColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.5) self.lastNameText = UITextField(frame: self.lastNameCell.contentView.bounds.insetBy(dx: 15, dy: 0)) self.lastNameText.placeholder = "Last Name" self.lastNameCell.addSubview(self.lastNameText) // construct share cell, section 1, row 0 self.shareCell.backgroundColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.5) self.shareCell.accessoryType = UITableViewCellAccessoryType.checkmark self.datePickerCell.backgroundColor = UIColor.red self.datePickerCell.addSubview(self.travelDatePicker) self.travelDatePicker.frame = CGRect(x: 0, y: 0, width: 500, height: 216) self.datePickerCell.accessoryType = UITableViewCellAccessoryType.none self.cityToCell.textLabel?.text = "Kiev" self.cityToCell.backgroundColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.5) self.cityToCell.accessoryType = UITableViewCellAccessoryType.none self.cityFromCell.textLabel?.text = "San Francisco" self.cityFromCell.backgroundColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.5) self.cityFromCell.accessoryType = UITableViewCellAccessoryType.none } func datePickerValueChanged(_ sender: UIDatePicker){ let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd" let dateString = dateFormatter.string(from: travelDatePicker.date) self.shareCell.textLabel?.text = "/(dateString)" print("changed") print("Selected value /(dateString)") } // Return the number of sections override func numberOfSections(in tableView: UITableView) -> Int { return 2 } // Return the number of rows for each section in your static table override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { switch(section) { case 0: return 2 // section 0 has 2 rows case 1: return 4 // section 1 has 1 row default: fatalError("Unknown number of sections") } } override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { if indexPath.section == 1 && indexPath.row == 1{ let height: CGFloat = travelDatePicker.isHidden ? 0.0 : 216.0 return height } return 44.0 } // Return the row for the corresponding section and row override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { switch(indexPath.section) { case 0: switch(indexPath.row) { case 0: return self.firstNameCell // section 0, row 0 is the first name case 1: return self.lastNameCell // section 0, row 1 is the last name default: fatalError("Unknown row in section 0") } case 1: switch(indexPath.row) { case 0: return self.shareCell // section 1, row 0 is the share option case 1: return self.datePickerCell case 2: return self.cityToCell case 3: return self.cityFromCell default: fatalError("Unknown row in section 1") } default: fatalError("Unknown section") } } // Customize the section headings for each section override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { switch(section) { case 0: return "Profile" case 1: return "Social" default: fatalError("Unknown section") } } // Configure the row selection code for any cells that you want to customize the row selection override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { // Handle social cell selection to toggle checkmark if(indexPath.section == 1 && indexPath.row == 0) { // deselect row tableView.deselectRow(at: indexPath as IndexPath, animated: false) // toggle check mark if(self.shareCell.accessoryType == UITableViewCellAccessoryType.none) { self.shareCell.accessoryType = UITableViewCellAccessoryType.checkmark; } else { self.shareCell.accessoryType = UITableViewCellAccessoryType.none; } } if(indexPath.section == 1 && indexPath.row == 0) { travelDatePicker.isHidden = !travelDatePicker.isHidden UIView.animate(withDuration: 0.3, animations: { () -> Void in self.tableView.beginUpdates() // apple bug fix - some TV lines hide after animation self.tableView.deselectRow(at: indexPath, animated: true) self.tableView.endUpdates() }) } } }

Estoy construyendo una aplicación que permite al usuario seleccionar fechas desde una UITableView. TableView es estático y está agrupado. He analizado muchas preguntas, incluida esta , tratando de descubrir cómo lograr esto, pero nada parece funcionar de manera óptima. La aplicación de calendario de Apple presenta una animación muy suave y agradable que ninguno de los ejemplos que he tenido ha logrado recrear.

Este es mi resultado deseado:

¿Podría alguien dirigirme a un tutorial o explicar cómo puedo lograr una animación tan suave con la forma más concisa y directa de hacerlo, como vemos en la aplicación de calendario?

¡Muchas gracias!

Erik


Implementé la respuesta de @ thorb65 en Swift, y funciona como un encanto. Incluso si configuré dos selectores de fecha (p. Ej., "Inicio" y "fin" como en el calendario), y los configuré para que el abierto se colapse automáticamente al expandir el otro (es decir, "uno abierto a la vez máximo") política, al igual que Calendar), las animaciones (concurrentes) siguen siendo fluidas.

Sin embargo, una cosa con la que tuve problemas fue encontrar las restricciones correctas de la función de autoinducción. Lo siguiente me dio el mismo comportamiento que Calendar.app:

  1. Contraer de abajo hacia arriba (el contenido del selector de fecha no se mueve)

Se resiste de UIDatePicker hacia sí mismo:

  • Altura

Restringe desde UIDatePicker contra la vista de contenido de UITableViewCell:

  • Espacio líder en margen de contenedor
  • Espacio posterior al margen del contenedor
  • Margen superior de espacio a contenedor

Animación resultante

El "Espacio inferior al margen del contenedor" se deja explícitamente fuera, para imponer una altura fija a lo largo de la animación (esto recrea el comportamiento de Calendar.app, donde la celda de la vista de tabla "se abre" para mostrar el selector de fecha inmóvil y de altura fija debajo).

  1. Contraer de abajo hacia arriba (el selector de fecha se mueve uniformemente)

Se resiste de UIDatePicker hacia sí mismo:

  • Altura

Restringe desde UIDatePicker contra la vista de contenido de UITableViewCell:

  • Espacio líder en margen de contenedor
  • Espacio posterior al margen del contenedor
  • Centrar verticalmente en el contenedor externo

Animación resultante

Observe la diferencia que hacen las restricciones en la animación de colapso / expansión.

EDITAR: Este es el código rápido

Propiedades:

// "Start Date" (first date picker) @IBOutlet weak var startDateLabel: UILabel! @IBOutlet weak var startDatePicker: UIDatePicker! var startDatePickerVisible:Bool? // "End Date" (second date picker) @IBOutlet weak var endDateLabel: UILabel! @IBOutlet weak var endDatePicker: UIDatePicker! var endDatePickerVisible:Bool? private var startDate:NSDate private var endDate:NSDate // Backup date labels'' initial text color, to restore on collapse // (we change it to control tint while expanded, like calendar.app) private var dateLabelInitialTextColor:UIColor!

Métodos UIViewController:

override func viewDidLoad() { super.viewDidLoad() // Set pickers to their initial values (e.g., "now" and "now + 1hr" ) startDatePicker.date = startDate startDateLabel.text = formatDate(startDate) endDatePicker.date = endDate endDateLabel.text = formatDate(endDate) // Backup (unselected) date label color dateLabelInitialTextColor = startDateLabel.textColor } override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) startDatePickerVisible = false startDatePicker.hidden = true endDatePickerVisible = false endDatePicker.hidden = true }

UITableViewDelegate métodos:

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { var height:CGFloat = 44 // Default if indexPath.row == 3 { // START DATE PICKER ROW if let startDatePickerVisible = startDatePickerVisible { height = startDatePickerVisible ? 216 : 0 } } else if indexPath.row == 5 { // END DATE PICKER ROW if let endDatePickerVisible = endDatePickerVisible { height = endDatePickerVisible ? 216 : 0 } } return height } override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { switch indexPath.row { case 2: // [ A ] START DATE // Collapse the other date picker (if expanded): if endDatePickerVisible! { hideDatePickerCell(containingDatePicker: endDatePicker) } // Expand: if startDatePickerVisible! { hideDatePickerCell(containingDatePicker: startDatePicker) } else{ showDatePickerCell(containingDatePicker: startDatePicker) } case 4: // [ B ] END DATE // Collapse the other date picker (if expanded): if startDatePickerVisible!{ hideDatePickerCell(containingDatePicker: startDatePicker) } // Expand: if endDatePickerVisible! { hideDatePickerCell(containingDatePicker: endDatePicker) } else{ showDatePickerCell(containingDatePicker: endDatePicker) } default: break } tableView.deselectRowAtIndexPath(indexPath, animated: true) }

Acciones de control del selector de fecha:

@IBAction func dateChanged(sender: AnyObject) { guard let picker = sender as? UIDatePicker else { return } let dateString = formatDate(picker.date) if picker == startDatePicker { startDateLabel.text = dateString } else if picker == endDatePicker { endDateLabel.text = dateString } }

Métodos Auxiliares: (animación, formateo de fecha)

@IBAction func dateChanged(sender: AnyObject) { guard let picker = sender as? UIDatePicker else { return } let dateString = formatDate(picker.date) if picker == startDatePicker { startDateLabel.text = dateString } else if picker == endDatePicker { endDateLabel.text = dateString } } func showDatePickerCell(containingDatePicker picker:UIDatePicker) { if picker == startDatePicker { startDatePickerVisible = true startDateLabel.textColor = myAppControlTintColor } else if picker == endDatePicker { endDatePickerVisible = true endDateLabel.textColor = myAppControlTintColor } tableView.beginUpdates() tableView.endUpdates() picker.hidden = false picker.alpha = 0.0 UIView.animateWithDuration(0.25) { () -> Void in picker.alpha = 1.0 } } func hideDatePickerCell(containingDatePicker picker:UIDatePicker) { if picker == startDatePicker { startDatePickerVisible = false startDateLabel.textColor = dateLabelInitialTextColor } else if picker == endDatePicker { endDatePickerVisible = false endDateLabel.textColor = dateLabelInitialTextColor } tableView.beginUpdates() tableView.endUpdates() UIView.animateWithDuration(0.25, animations: { () -> Void in picker.alpha = 0.0 }, completion:{ (finished) -> Void in picker.hidden = true } ) }


Las 2 respuestas anteriores me permitieron resolver este problema. Merecen el crédito, estoy agregando esto un recordatorio para mí - formato de resumen.

Esta es mi versión de las respuestas anteriores.

1. Como se indicó anteriormente, agregue el selector a la celda que desea mostrar / ocultar.

2. Agregue restricciones para el selector en el constructor de interfaz - centro X / centro Y / igual altura / ancho igual a la vista de contenido de la celda

3. Conecte el selector a VC

@IBOutlet weak var dobDatePicker: UIDatePicker!

También puede controlar el arrastre y agregar un método que registre los cambios de fecha

@IBAction func dateChanged(sender: UIDatePicker) { // updates ur label in the cell above dobLabel.text = "/(dobDatePicker.date)" }

4. En viewDidLoad

dobDatePicker.date = NSDate() dobLabel.text = "/(dobDatePicker.date)" // my label in cell above dobDatePicker.hidden = true

5. Configuración de las alturas de las celdas, en mi ejemplo, la celda que quiero expandir es la sección 0, fila 3 ... configúrelo como la celda que desea expandir / ocultar. Si tiene muchas celdas con varias alturas, esto permite eso.

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { if indexPath.section == 0 && indexPath.row == 3 { let height:CGFloat = dobDatePicker.hidden ? 0.0 : 216.0 return height } return super.tableView(tableView, heightForRowAtIndexPath: indexPath) }

6. Seleccionando la celda de arriba para expandir la que está debajo, otra vez ajústelo a la celda que tocará para mostrar la celda a continuación.

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { let dobIndexPath = NSIndexPath(forRow: 2, inSection: 0) if dobIndexPath == indexPath { dobDatePicker.hidden = !dobDatePicker.hidden UIView.animateWithDuration(0.3, animations: { () -> Void in self.tableView.beginUpdates() // apple bug fix - some TV lines hide after animation self.tableView.deselectRowAtIndexPath(indexPath, animated: true) self.tableView.endUpdates() }) } }


Solo pensé que agregaría mis dos centavos también. En realidad estoy programando en Xamarin y tuve que hacer algunos pequeños ajustes para que funcione en el marco de Xamarin.

Todos los principios son los mismos, pero como Xamarin utiliza una clase separada para TableViewSource, la gestión de los delegados es diferente. Por supuesto, siempre puede asignar UITableViewDelegates si también lo desea en el UIViewController, pero tenía curiosidad si podía hacerlo funcionar de esta manera:

Para empezar, clasifiqué tanto la celda del selector de fecha (datePickerCell) como la celda del selector (selectorCell). Nota al margen, estoy haciendo esto 100% programáticamente sin StoryBoard

datePickerCell:

using System; using UIKit; namespace DatePickerInTableViewCell { public class CustomDatePickerCell : UITableViewCell { //======================================================================================================================================== // PRIVATE CLASS PROPERTIES //======================================================================================================================================== private UIDatePicker datePicker; private bool datePickerVisible; private Boolean didUpdateConstraints; //======================================================================================================================================== // PUBLIC CLASS PROPERTIES //======================================================================================================================================== public event EventHandler dateChanged; //======================================================================================================================================== // Constructor //======================================================================================================================================== /// <summary> /// Initializes a new instance of the <see cref="DatePickerInTableViewCell.CustomDatePickerCell"/> class. /// </summary> public CustomDatePickerCell (string rid) : base(UITableViewCellStyle.Default, rid) { Initialize (); } //======================================================================================================================================== // PUBLIC OVERRIDES //======================================================================================================================================== /// <summary> /// Layout the subviews. /// </summary> public override void LayoutSubviews () { base.LayoutSubviews (); ContentView.AddSubview (datePicker); datePicker.Hidden = true; AutoresizingMask = UIViewAutoresizing.FlexibleHeight | UIViewAutoresizing.FlexibleWidth; foreach (UIView view in ContentView.Subviews) { view.TranslatesAutoresizingMaskIntoConstraints = false; } ContentView.SetNeedsUpdateConstraints (); } /// <summary> /// We override the UpdateConstraints to allow us to only set up our constraint rules one time. Since /// we need to use this method to properly call our constraint rules at the right time we use a boolean /// as a flag so that we only fix our auto layout once. Afterwards UpdateConstraints runs as normal. /// </summary> public override void UpdateConstraints () { if (NeedsUpdateConstraints () && !didUpdateConstraints) { setConstraints (); didUpdateConstraints = true; } base.UpdateConstraints (); } //======================================================================================================================================== // PUBLIC METHODS //======================================================================================================================================== /// <summary> /// Allows us to determine the visibility state of the cell from the tableViewSource. /// </summary> /// <returns><c>true</c> if this instance is visible; otherwise, <c>false</c>.</returns> public bool IsVisible() { return datePickerVisible; } /// <summary> /// Allows us to show the datePickerCell from the tableViewSource. /// </summary> /// <param name="tableView">Table view.</param> public void showDatePicker(ref UITableView tableView) { datePickerVisible = true; tableView.BeginUpdates (); tableView.EndUpdates (); datePicker.Hidden = false; datePicker.Alpha = 0f; UIView.Animate( 0.25, ()=> { datePicker.Alpha = 1f;} ); } public void hideDatePicker(ref UITableView tableView) { datePickerVisible = false; tableView.BeginUpdates (); tableView.EndUpdates (); UIView.Animate( 0.25, ()=> { datePicker.Alpha = 0f;}, ()=> {datePicker.Hidden = true;} ); } //======================================================================================================================================== // PRIVATE METHODS //======================================================================================================================================== /// <summary> /// We make sure the UIDatePicker is center in the cell. /// </summary> private void setConstraints() { datePicker.CenterXAnchor.ConstraintEqualTo(ContentView.CenterXAnchor).Active = true; } /// <summary> /// Init class properties. /// </summary> private void Initialize() { datePicker = new UIDatePicker (); datePickerVisible = false; datePicker.TimeZone = Foundation.NSTimeZone.LocalTimeZone; datePicker.Calendar = Foundation.NSCalendar.CurrentCalendar; datePicker.ValueChanged += (object sender, EventArgs e) => { if(dateChanged != null) { dateChanged (datePicker, EventArgs.Empty); } }; } } }

Célula Selector

using System; using UIKit; namespace DatePickerInTableViewCell { ///<summary> /// ///</summary> public class CustomDatePickerSelectionCell : UITableViewCell { //======================================================================================================================================== // PRIVATE CLASS PROPERTIES //======================================================================================================================================== private UILabel prefixLabel; private UILabel dateLabel; private UILabel timeLabel; private Boolean didUpdateConstraints; private UIColor originalLableColor; private UIColor editModeLabelColor; //======================================================================================================================================== // PUBLIC CLASS PROPERTIES //======================================================================================================================================== //======================================================================================================================================== // Constructor //======================================================================================================================================== /// <summary> /// Initializes a new instance of the <see cref="DatePickerInTableViewCell.CustomDatePickerSelectionCell"/> class. /// </summary> public CustomDatePickerSelectionCell (string rid) : base(UITableViewCellStyle.Default, rid) { Initialize (); } //======================================================================================================================================== // PUBLIC OVERRIDES //======================================================================================================================================== /// <summary> /// We override the UpdateConstraints to allow us to only set up our constraint rules one time. Since /// we need to use this method to properly call our constraint rules at the right time we use a boolean /// as a flag so that we only fix our auto layout once. Afterwards UpdateConstraints runs as normal. /// </summary> public override void UpdateConstraints () { if (NeedsUpdateConstraints () && !didUpdateConstraints) { setConstraints (); didUpdateConstraints = true; } base.UpdateConstraints (); } public override void LayoutSubviews () { base.LayoutSubviews (); AutoresizingMask = UIViewAutoresizing.FlexibleHeight | UIViewAutoresizing.FlexibleWidth; timeLabel.TextAlignment = UITextAlignment.Right; prefixLabel.Text = "On: "; dateLabel.Text = DateTime.Now.ToString ("MMM d, yyyy"); timeLabel.Text = DateTime.Now.ToShortTimeString (); ContentView.AddSubviews (new UIView[]{ prefixLabel, dateLabel, timeLabel }); foreach (UIView view in ContentView.Subviews) { view.TranslatesAutoresizingMaskIntoConstraints = false; } ContentView.SetNeedsUpdateConstraints (); } //======================================================================================================================================== // PUBLIC METHODS //======================================================================================================================================== public void willUpdateDateTimeLables(string date, string time) { dateLabel.Text = date; timeLabel.Text = time; } public void willEditDateTime() { dateLabel.TextColor = editModeLabelColor; timeLabel.TextColor = editModeLabelColor; } public void didEditDateTime() { dateLabel.TextColor = originalLableColor; timeLabel.TextColor = originalLableColor; } //======================================================================================================================================== // PRIVATE METHODS //======================================================================================================================================== private void Initialize() { prefixLabel = new UILabel (); dateLabel = new UILabel (); timeLabel = new UILabel (); originalLableColor = dateLabel.TextColor; editModeLabelColor = UIColor.Red; } private void setConstraints() { var cellMargins = ContentView.LayoutMarginsGuide; prefixLabel.LeadingAnchor.ConstraintEqualTo (cellMargins.LeadingAnchor).Active = true; dateLabel.LeadingAnchor.ConstraintEqualTo (prefixLabel.TrailingAnchor).Active = true; timeLabel.LeadingAnchor.ConstraintEqualTo (dateLabel.TrailingAnchor).Active = true; timeLabel.TrailingAnchor.ConstraintEqualTo (cellMargins.TrailingAnchor).Active = true; dateLabel.WidthAnchor.ConstraintEqualTo (ContentView.WidthAnchor, 2f / 7f).Active = true; prefixLabel.HeightAnchor.ConstraintEqualTo (ContentView.HeightAnchor, 1).Active = true; timeLabel.HeightAnchor.ConstraintEqualTo (ContentView.HeightAnchor, 1).Active = true; dateLabel.HeightAnchor.ConstraintEqualTo (ContentView.HeightAnchor, 1).Active = true; } } }

Entonces, como pueden ver, tengo algunos métodos expuestos de cada célula para facilitar la comunicación necesaria. Luego tuve que crear una instancia de estas celdas en mi tableViewSource. Tal vez hay una forma menos de acoplamiento de hacer esto, pero no pude resolverlo fácilmente. Creo que tengo mucha menos experiencia en programación iOS que mis antecesores anteriores :). Dicho esto, con las celdas disponibles en el alcance de la clase hace que sea muy fácil llamar y acceder a las celdas en los métodos RowSelected y GetHeightForRow.

TableViewSource

using System; using UIKit; using System.Collections.Generic; namespace DatePickerInTableViewCell { public class TableViewSource : UITableViewSource { //======================================================================================================================================== // PRIVATE CLASS PROPERTIES //======================================================================================================================================== private const string datePickerIdentifier = "datePickerCell"; private const string datePickerActivateIdentifier = "datePickerSelectorCell"; private const int datePickerRow = 1; private const int datePickerSelectorRow = 0; private List<UITableViewCell> datePickerCells; private CustomDatePickerCell datePickerCell; private CustomDatePickerSelectionCell datePickerSelectorCell; //======================================================================================================================================== // PUBLIC CLASS PROPERTIES //======================================================================================================================================== //======================================================================================================================================== // Constructor //======================================================================================================================================== /// <summary> /// Initializes a new instance of the <see cref="DatePickerInTableViewCell.TableViewSource"/> class. /// </summary> public TableViewSource () { initDemoDatePickerCells (); } //======================================================================================================================================== // PUBLIC OVERRIDES //======================================================================================================================================== public override UITableViewCell GetCell (UITableView tableView, Foundation.NSIndexPath indexPath) { UITableViewCell cell = null; if (indexPath.Row == datePickerSelectorRow) { cell = tableView.DequeueReusableCell (datePickerActivateIdentifier); cell = cell ?? datePickerCells[indexPath.Row]; return cell; } if (indexPath.Row == datePickerRow) { cell = tableView.DequeueReusableCell (datePickerIdentifier) as CustomDatePickerCell; cell = cell ?? datePickerCells[indexPath.Row]; return cell; } return cell; } public override nint RowsInSection (UITableView tableview, nint section) { return datePickerCells.Count; } public override nfloat GetHeightForRow (UITableView tableView, Foundation.NSIndexPath indexPath) { float height = (float) tableView.RowHeight; if (indexPath.Row == datePickerRow) { height = datePickerCell.IsVisible () ? DefaultiOSDimensions.heightForDatePicker : 0f; } return height; } public override void RowSelected (UITableView tableView, Foundation.NSIndexPath indexPath) { if (indexPath.Row == datePickerSelectorRow) { if (datePickerCell != null) { if (datePickerCell.IsVisible ()) { datePickerCell.hideDatePicker (ref tableView); datePickerSelectorCell.didEditDateTime (); } else { datePickerCell.showDatePicker (ref tableView); datePickerSelectorCell.willEditDateTime (); } } } tableView.DeselectRow (indexPath, true); } //======================================================================================================================================== // PUBLIC METHODS //======================================================================================================================================== //======================================================================================================================================== // PRIVATE METHODS //======================================================================================================================================== private void willUpdateDateChanged(Object sender, EventArgs args) { var picker = sender as UIDatePicker; var dateTime = picker.Date.ToDateTime (); if (picker != null && dateTime != null) { var date = dateTime.ToString ("MMM d, yyyy"); var time = dateTime.ToShortTimeString (); datePickerSelectorCell.willUpdateDateTimeLables (date, time); } } private void initDemoDatePickerCells() { datePickerCell = new CustomDatePickerCell (datePickerIdentifier); datePickerSelectorCell = new CustomDatePickerSelectionCell (datePickerActivateIdentifier); datePickerCell.dateChanged += willUpdateDateChanged; datePickerCells = new List<UITableViewCell> () { datePickerSelectorCell, datePickerCell }; } } }

Espero que el código sea bastante auto explicativo. El método toDateTime btw es solo un método de extensión para convertir NSDateTime a un objeto .Net DateTime. La referencia se puede encontrar aquí: https://forums.xamarin.com/discussion/27184/convert-nsdate-to-datetime y DefaultiOSDimensions es solo una pequeña clase estática que uso para realizar un seguimiento de las dimensiones típicas, como cellHeight (44pts) ) o en el caso de heightForDatePicker ; 216. Parece que funciona bien para mí en mi simulador. Todavía tengo que probar en dispositivos reales. Espero que ayude a alguien!


También he estado trabajando en esto, y pensé que podría compartir mi solución, que se deriva de las que ya proporcionamos aquí.

Lo que noté es que hay un montón de código en los otros ejemplos específicos de elementos individuales, y entonces lo que hice fue crear una clase de ''administrador'' para tratar con cualquier elemento.

Aquí esta lo que hice:

El CellShowHideDetail almacena detalles sobre el artículo que desea mostrar u ocultar. Estos detalles incluyen la celda en la que se encuentra y también la celda que se tocará para alternar entre mostrar y ocultar:

public class CellShowHideDetail { var item: UIView var indexPath_ToggleCell: IndexPath var indexPath_ItemCell: IndexPath var desiredHeight: CGFloat init(item: UIView, indexPath_ToggleCell: IndexPath, indexPath_ItemCell: IndexPath, desiredHeight: CGFloat) { self.item = item self.indexPath_ToggleCell = indexPath_ToggleCell self.indexPath_ItemCell = indexPath_ItemCell self.desiredHeight = desiredHeight //By default cells are not expanded: self.item.isHidden = true } }

Tenga en cuenta que UIView es una clase principal de la mayoría (¿todos?) De los elementos de la interfaz de usuario.

A continuación tenemos el administrador, que procesará tantos de estos artículos como desee:

import Foundation import UIKit public class CellShowHideManager { var cellItems: [CellShowHideDetail] init() { cellItems = [] } func addItem(item: CellShowHideDetail) { cellItems.append(item) } func getRowHeight(indexPath: IndexPath) -> (match: Bool, height: CGFloat) { for item in cellItems { if indexPath.section == item.indexPath_ItemCell.section && indexPath.row == item.indexPath_ItemCell.row { return (match: true, height: item.item.isHidden ? 0.0 : item.desiredHeight) } } return (match: false, height: 0) } func rowSelected(indexPath: IndexPath) -> Bool { var changesMade = false for item in cellItems { if item.indexPath_ToggleCell == indexPath { item.item.isHidden = !item.item.isHidden changesMade = true } else { if item.item.isHidden == false { changesMade = true } item.item.isHidden = true } } return changesMade } }

A continuación, puede crear fácilmente un CellShowHideManager en cualquier clase UITableViewController , agregar los elementos que desea que se puedan alternar:

var showHideManager = CellShowHideManager() override func viewDidLoad() { super.viewDidLoad() let item1ToShowHide = CellShowHideDetail(item: datePicker, indexPath_ToggleCell: IndexPath(row: 0, section: 0), indexPath_ItemCell: IndexPath(row: 1, section: 0), desiredHeight: 232.0) let item2ToShowHide = CellShowHideDetail(item: selection_Picker, indexPath_ToggleCell: IndexPath(row: 0, section: 1), indexPath_ItemCell: IndexPath(row: 1, section: 1), desiredHeight: 90.0) //Add items for the expanding cells: showHideManager.addItem(item: item1ToShowHide) showHideManager.addItem(item: item2ToShowHide) }

Finalmente, solo anule estos dos métodos TableView siguiente manera:

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { let showHideResult = showHideManager.getRowHeight(indexPath: indexPath) if showHideResult.match { return showHideResult.height } else { return super.tableView(tableView, heightForRowAt: indexPath) } } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if showHideManager.rowSelected(indexPath: indexPath) { UIView.animate(withDuration: 0.3, animations: { () -> Void in self.tableView.beginUpdates() // apple bug fix - some TV lines hide after animation //self.tableView.deselectRowAt(indexPath, animated: true) self.tableView.endUpdates() }) } }

¡Y debería funcionar bien!


Supongo que está utilizando el guión gráfico, el ejemplo es con UIPickerView : UIPickerView una tablavista debajo de la celda que contiene el campo de texto que desea llenar y configure la altura de la fila de celdas en 216.0 en el inspector y agregue un UIPickerView a esa celda.

A continuación, conecte el UIPickerView a través de Outlet a su viewcontroller y agregue la siguiente propiedad a su ViewController.h:

@property (weak, nonatomic) IBOutlet UIPickerView *statusPicker; @property BOOL statusPickerVisible;

En su ViewController.m, haga viewWillAppear en viewWillAppear

self.statusPickerVisible = NO; self.statusPicker.hidden = YES; self.statusPicker.translatesAutoresizingMaskIntoConstraints = NO;

Agregue dos métodos:

- (void)showStatusPickerCell { self.statusPickerVisible = YES; [self.tableView beginUpdates]; [self.tableView endUpdates]; self.statusPicker.alpha = 0.0f; [UIView animateWithDuration:0.25 animations:^{ self.statusPicker.alpha = 1.0f; } completion:^(BOOL finished){ self.statusPicker.hidden = NO; }];]; } - (void)hideStatusPickerCell { self.statusPickerVisible = NO; [self.tableView beginUpdates]; [self.tableView endUpdates]; [UIView animateWithDuration:0.25 animations:^{ self.statusPicker.alpha = 0.0f; } completion:^(BOOL finished){ self.statusPicker.hidden = YES; }]; }

En heightForRowAtIndexPath

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { CGFloat height = self.tableView.rowHeight; if (indexPath.row == 1){ height = self.statusPickerVisible ? 216.0f : 0.0f; } return height; }

En didSelectRowAtIndexPath

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.row == 0) { if (self.statusPickerVisible){ [self hideStatusPickerCell]; } else { [self showStatusPickerCell]; } } [self.tableView deselectRowAtIndexPath:indexPath animated:YES]; }