ios 使用 DatePicker 展开和折叠 UITableViewCells
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29678471/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Expanding and collapsing UITableViewCells with DatePicker
提问by Erik
I'm building an app that lets the user select dates from a UITableView. The tableView is static and grouped. I've looked through many questions, including this one, trying to figure out how to accomplish this - but nothing seems to work optimal. Apple's calendar app features a very smooth and nice animation that none of the examples I've been through have managed to recreate.
我正在构建一个应用程序,让用户从 UITableView 中选择日期。tableView 是静态的和分组的。我浏览了许多问题,包括这个问题,试图弄清楚如何实现这一点 - 但似乎没有什么是最佳的。Apple 的日历应用程序具有非常流畅和漂亮的动画,我经历过的所有示例都无法重现。
This is my desired result:
这是我想要的结果:
Could someone point me to a tutorial or explain how I can accomplish such a smooth animation with the most concise and straight forward way of doing it, like we see in the calendar app?
有人可以给我指点教程或解释我如何以最简洁直接的方式完成如此流畅的动画,就像我们在日历应用程序中看到的那样?
Thanks alot!
非常感谢!
Erik
埃里克
采纳答案by thorb65
I assume you're using storyboard, the example is with UIPickerView
:
Create a tableviewcell right under the cell that contains the textfield you want to fill and set the cells row height to 216.0 in the inspector and add a UIPickerView to that cell.
我假设您使用的是情节提要,示例如下UIPickerView
:在包含要填充的文本字段的单元格下方创建一个 tableviewcell,并在检查器中将单元格行高设置为 216.0,并向该单元格添加 UIPickerView。
Next connect the UIPickerView via Outlet to your viewcontroller and add the following property to your ViewController.h:
接下来通过 Outlet 将 UIPickerView 连接到您的视图控制器,并将以下属性添加到您的 ViewController.h:
@property (weak, nonatomic) IBOutlet UIPickerView *statusPicker;
@property BOOL statusPickerVisible;
In your ViewController.m do in viewWillAppear
在你的 ViewController.m 中做 viewWillAppear
self.statusPickerVisible = NO;
self.statusPicker.hidden = YES;
self.statusPicker.translatesAutoresizingMaskIntoConstraints = NO;
Add two methods:
添加两个方法:
- (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;
}];
}
In heightForRowAtIndexPath
在 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;
}
In didSelectRowAtIndexPath
在 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];
}
回答by DogCoffee
The 2 answers above enabled me to solve this problem. They deserve the credit, I'm adding this a reminder for myself - summary format.
上面的 2 个答案使我能够解决这个问题。他们应得的荣誉,我正在为自己添加这个提醒 - 摘要格式。
This is my version of the above answers.
这是我对上述答案的版本。
1.As noted above - add picker to a the cell you want to show / hide.
1.如上所述 - 将选择器添加到要显示/隐藏的单元格。
2.Add constraints for the picker in interface builder - center X / center Y / equal height / equal width to the cell's content view
2.在界面构建器中为picker添加约束——居中X/居中Y/等高/等宽到单元格的内容视图
3.Connect the picker to you VC
3.将选择器连接到您的 VC
@IBOutlet weak var dobDatePicker: UIDatePicker!
You might as well control drag and add a method that will register the date changes
您不妨控制拖动并添加一个将注册日期更改的方法
@IBAction func dateChanged(sender: UIDatePicker) {
// updates ur label in the cell above
dobLabel.text = "\(dobDatePicker.date)"
}
4.In viewDidLoad
4.在viewDidLoad中
dobDatePicker.date = NSDate()
dobLabel.text = "\(dobDatePicker.date)" // my label in cell above
dobDatePicker.hidden = true
5.Setting cell heights, in my example the cell I want to expand is section 0, row 3... set this to what you cell you want to expand / hide. If you have many cells with various heights this allows for that.
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.Selecting the cell above to expand the one below, again set this to the cell you will tap to show the cell below.
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()
})
}
}
回答by Nicolas Miari
I implemented @thorb65's answer in Swift, and it works like a charm. Even if I set up two date pickers (e.g., "start" and "end" like in calendar), and set them up so that the open one collapses automatically when expaning the other one (i.e., "one open at a time maximum" policy, just like Calendar), the (concurrent) animations are still smooth.
我在 Swift 中实现了@thorb65 的答案,它的作用就像一个魅力。即使我设置了两个日期选择器(例如,日历中的“开始”和“结束”),并将它们设置为在展开另一个时打开的一个自动折叠(即“一个最多打开一次”)策略,就像日历一样),(并发)动画仍然很流畅。
One thing I struggled with, though, is finding the right autolayout constraints. The following gave me the same behaviour as Calendar.app:
不过,我一直在努力解决的一件事是找到正确的自动布局约束。以下给了我与 Calendar.app 相同的行为:
- Collapse from bottom up (date picker contents do not move)
- 自下而上折叠(日期选择器内容不移动)
Constrains from UIDatePicker towards itself:
从 UIDatePicker 对其自身的约束:
- Height
- 高度
Constrains from UIDatePicker against UITableViewCell's content view:
UIDatePicker 对 UITableViewCell 的内容视图的约束:
- Leading Space to Container Margin
- Trailing Space to Container Margin
- Top Space to Container Margin
- 领先的空间到集装箱保证金
- 到集装箱边距的尾随空间
- 集装箱边缘的顶部空间
"Bottom Space to Container Margin" is explicitly left out, to enforce fixed hight all along the animation (this recreates Calendar.app's behaviour, where the table view cell "slides open" to reveal the unchanging, fixed-height date picker beneath).
“底部空间到容器边距”被明确排除,以在整个动画中强制固定高度(这重新创建了 Calendar.app 的行为,其中表格视图单元格“滑动打开”以显示下方不变的、固定高度的日期选择器)。
- Collapse from bottom up (date picker moves uniformly)
- 自下而上折叠(日期选择器均匀移动)
Constrains from UIDatePicker towards itself:
从 UIDatePicker 对其自身的约束:
- Height
- 高度
Constrains from UIDatePicker against UITableViewCell's content view:
UIDatePicker 对 UITableViewCell 的内容视图的约束:
- Leading Space to Container Margin
- Trailing Space to Container Margin
- Center vertically in outer container
- 领先的空间到集装箱保证金
- 到集装箱边距的尾随空间
- 在外容器中垂直居中
Notice the difference the constraints make in the collapse/expand animation.
请注意约束在折叠/展开动画中的差异。
EDIT:This is the swift code
编辑:这是快速代码
Properties:
特性:
// "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 methods:
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 Methods:
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)
}
Date Picker Control Actions:
日期选择器控制操作:
@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
}
}
Auxiliary Methods:(animation, date formatting)
辅助方法:(动画、日期格式)
@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
}
)
}
回答by Greg
I've been working on this too, and I thought I might share my solution, which is derived from the ones already provided here.
我也一直在研究这个,我想我可以分享我的解决方案,它来自这里已经提供的解决方案。
What I noticed is that there is a lot of code in the other examples specific to individual elements, and so what I did was to create a 'manager' class to deal with it for any item.
我注意到在其他示例中有很多特定于单个元素的代码,所以我所做的是创建一个“管理器”类来处理任何项目。
Here is what I did:
这是我所做的:
The CellShowHideDetail
stores details about the item you want to show or hide. These details include the cell it is in, and also the cell that will be tapped to toggle the showing and hiding:
在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
}
}
Note that UIView
is a parent class of most (all?) UI elements.
请注意,这UIView
是大多数(所有?)UI 元素的父类。
Next we have the manager, which will process as many of these items as you like:
接下来我们有经理,它将根据您的需要处理尽可能多的这些项目:
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
}
}
You can then easily create a CellShowHideManager
on any UITableViewController
class, add in the items you want to be toggle-able:
然后,您可以轻松地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)
}
Finally just override these two TableView
methods as follows:
最后只是重写这两个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()
})
}
}
And it should work nicely!
它应该工作得很好!
回答by dmyma
I am sharing my answer:
我分享我的答案:
I am doing everything without storyboard
我在没有故事板的情况下做所有事情
Swift 3
斯威夫特 3
1.1 add the datePicker
1.1 添加日期选择器
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 and its method
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. then in the loadView display the date in the cell above with format
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. add datePicker to the cell
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. set the height of the cell
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
}
- and finally set the if statement in the didSelectAt
- 最后在 didSelectAt 中设置 if 语句
if(indexPath.section == 1 && indexPath.row == 0) {
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()
})
}
Full code is here with other elements just get a feeling of working app
完整的代码在这里与其他元素只是得到一个工作应用程序的感觉
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()
})
}
}
}
回答by Chris Shields
Just thought I would add my two cents as well. I am actually programming in Xamarin and had to make some small adjustments to get it to work in the framework of Xamarin.
只是想我也会加上我的两美分。我实际上是在 Xamarin 中编程,并且必须进行一些小的调整才能使其在 Xamarin 框架中工作。
All of the principles are the same but as Xamarin uses a separate class for the TableViewSource and as such the management of delegates is different. Of course you can always assign UITableViewDelegates if you want as well in the UIViewController, but I was curious if I could get it to work this way:
所有的原则都是相同的,但 Xamarin 为 TableViewSource 使用一个单独的类,因此委托的管理是不同的。当然,如果你愿意,你也可以在 UIViewController 中分配 UITableViewDelegates,但我很好奇我是否可以让它以这种方式工作:
To start with I subclassed both the Date Picker Cell (datePickerCell) and the selector cell (selectorCell). Side note, I am doing this 100% programmatically without a StoryBoard
首先,我将日期选择器单元格 (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);
}
};
}
}
}
Selector Cell
选择单元
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;
}
}
}
So as you can see I have some methods exposed from each cell to facilitate the needed communication. I then needed to create an instance of these cells in my tableViewSource. Maybe there is a less coupling way of doing this but I couldn't easily figure that out. I believe I am a lot less experienced in iOS programming than my predecessors above :). That said, with the cells available in the scope of the class it makes it very easy to call and access the cells in the RowSelected and GetHeightForRow methods.
如您所见,我从每个单元格中公开了一些方法,以促进所需的通信。然后我需要在我的 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
};
}
}
}
Hope the code is fairly self explanatory. The method toDateTime btw is just an extension method to convert NSDateTime to a .net DateTime object. The reference can be found here: https://forums.xamarin.com/discussion/27184/convert-nsdate-to-datetimeand DefaultiOSDimensions is just a small static class that I use to keep track of typical dimensions such as cellHeight (44pts) or in the case of heightForDatePicker
; 216. It seems to work great for me on my simulator. I have yet to test on it actual devices. Hope it helps someone!
希望代码是不言自明的。顺便说一句, toDateTime 方法只是将 NSDateTime 转换为 .net DateTime 对象的扩展方法。参考可以在这里找到:https: //forums.xamarin.com/discussion/27184/convert-nsdate-to-datetime 和 DefaultiOSDimensions 只是一个小的静态类,我用来跟踪典型的尺寸,如 cellHeight (44pts ) 或在情况下heightForDatePicker
;216. 在我的模拟器上,它似乎对我很有用。我尚未在实际设备上对其进行测试。希望它可以帮助某人!