使用DatePicker扩展和折叠UITableViewCells

时间:2015-04-16 14:54:05

标签: ios objective-c uitableview datepicker tableviewcell

我正在构建一个应用程序,让用户从UITableView中选择日期。 tableView是静态的并且是分组的。我已经查看了许多问题,包括this one,试图找出如何实现这一目标 - 但似乎没有任何工作是最佳的。 Apple的日历应用程序具有非常流畅和漂亮的动画,我所经历的所有示例都没有设法重现。

这是我想要的结果:

In-place date picker

有人能指点我一个教程或解释如何以最简洁直接的方式完成这样一个流畅的动画,就像我们在日历应用中看到的那样?

非常感谢!

埃里克

6 个答案:

答案 0 :(得分:36)

我假设你正在使用故事板,例子是UIPickerView: 在包含要填充的文本字段的单元格下创建一个tableviewcell,并在检查器中将单元格行高设置为216.0,并将UIPickerView添加到该单元格。

see here

接下来,通过Outlet将UIPickerView连接到viewcontroller,并将以下属性添加到ViewController.h中:

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

在ViewController.m中viewWillAppear

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

添加两种方法:

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

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

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

答案 1 :(得分:30)

上面的2个答案使我能够解决这个问题。他们值得赞扬,我为自己添加了一个提醒 - 摘要格式。

这是我上述答案的版本。

1。如上所述 - 将选择器添加到要显示/隐藏的单元格中。

2. 在界面构建器中为选择器添加约束 - 中心X /中心Y /等高/等宽到单元格的内容视图

3. 将选择器连接到VC

@IBOutlet weak var dobDatePicker: UIDatePicker!

您也可以控制拖动并添加一个将注册日期更改的方法

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

4. 在viewDidLoad

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

5. 设置单元格高度,在我的示例中,我想要展开的单元格是第0部分,第3行...将此设置为您想要展开/隐藏的单元格。如果你有许多不同高度的细胞,那就可以了。

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. :选择上面的单元格以展开下面的单元格,再次将其设置为您将点击以显示下方单元格的单元格。

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

enter image description here

答案 2 :(得分:18)

我在Swift中实现了@ thorb65的答案,它就像一个魅力。即使我设置了两个日期选择器(例如,像日历中的“开始”和“结束”),并将它们设置为使得打开的日期选择器在显示另一个日期选择器时自动折叠(即,“一次打开一个”政策,就像日历一样,(并发)动画仍然很流畅。

但是,我努力解决的一件事是找到正确的自动布局限制。以下给了我与Calendar.app相同的行为:

  1. 自下而上折叠(日期选择器内容不移动)
  2. 从UIDatePicker到自身的约束:

    • 高度

    UIDatePicker对UITableViewCell内容视图的约束:

    • 领先空间到集装箱保证金
    • 尾随空间到容器保证金
    • 容器保证金的最高空间

    Resulting animation

    明确地省略了“容器余量的底部空间”,以在整个动画中强制执行固定高度(这会重新创建Calendar.app的行为,其中表格视图单元格“滑开”以显示不变的固定高度日期选择器下面)。

    1. 从下往上折叠(日期选择器均匀移动)
    2. 从UIDatePicker到自身的约束:

      • 高度

      UIDatePicker对UITableViewCell内容视图的约束:

      • 领先空间到集装箱保证金
      • 尾随空间到容器保证金
      • 在外部容器中垂直居中

      Resulting animation

      注意约束在折叠/展开动画中所产生的差异。

      编辑:这是快速代码

      <强>属性:

      // "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!
      

      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方法:

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

      日期选择器控件操作:

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

      辅助方法:(动画,日期格式化)

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

答案 3 :(得分:2)

我也一直在研究这个问题,我想我可能会分享我的解决方案,这个解决方案源于此处已经提供的解决方案。

我注意到在其他示例中有很多代码专门针对单个元素,所以我所做的就是创建一个&#39;经理&#39;为任何项目处理它的类。

这是我做的:

CellShowHideDetail存储有关您要显示或隐藏的项目的详细信息。这些细节包括它所在的单元格,以及用于切换显示和隐藏的单元格:

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

请注意,UIView是大多数(所有?)UI元素的父类。

接下来我们有经理,他会根据你的喜好处理这些项目:

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

然后,您可以在任何CellShowHideManager课程中轻松创建UITableViewController,添加您想要切换的项目:

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

最后,只需覆盖以下两个TableView方法:

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

它应该很好用!

答案 4 :(得分:1)

我正在分享我的回答:

我正在做所有没有故事板的事情

Swift 3

1.1添加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及其方法

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。然后在loadView中以格式

显示上面单元格中的日期
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd"
        let dateString = dateFormatter.string(from: travelDatePicker.date)
        self.shareCell.textLabel?.text = "\(dateString)"
        travelDatePicker.isHidden = true

3。将datePicker添加到单元格

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。设置单元格的高度

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. 最后在didSelectAt
  2. 中设置if语句

    if(indexPath.section == 1&amp;&amp; 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()
        })
    }
    

    完整代码在这里与其他元素只是感觉工作的应用程序

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

答案 5 :(得分:0)

我想我也会加两分钱。我实际上是在Xamarin中进行编程,并且必须进行一些小的调整才能使它在Xamarin的框架中工作。

所有原则都是相同的,但是Xamarin为TableViewSource使用了一个单独的类,因此委托的管理是不同的。当然,你可以随时在UIViewController中分配UITableViewDelegates,但我很好奇我是否能以这种方式工作:

首先,我将日期选择器单元格(datePickerCell)和选择器单元格(selectorCell)子类化。 旁注,我是在没有StoryBoard

的情况下以编程方式100%完成的

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

选择器单元

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

因此,您可以看到我从每个单元格中暴露了一些方法以促进所需的通信。然后我需要在tableViewSource中创建这些单元格的实例。也许有一种较少的耦合方式,但我不能轻易搞清楚。我相信我在iOS编程方面的经验比我上面的前辈少得多:)。也就是说,通过类的范围内的单元格,可以很容易地调用和访问RowSelected和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
            };
        }
    }
}

希望代码是相当自我解释的。 toDateTime btw的方法只是将NSDateTime转换为.net DateTime对象的扩展方法。可以在这里找到引用:https://forums.xamarin.com/discussion/27184/convert-nsdate-to-datetime和DefaultiOSDimensions只是一个小的静态类,用于跟踪典型的维度,如cellHeight(44pts)或heightForDatePicker;在我的模拟器上,它似乎对我很有用。我还没有测试它的实际设备。希望它可以帮到某人!