I'm trying to put a UIDatePicker into a UITableViewCell which is opened/closed by clicking on the cell above it. The picker seems to be rendering all wrong. Firstly, here is the related code from the UITableView class:
// Determines if the date picker should be shown
var editingDate: Bool = false
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.section == 1 && indexPath.row == 1 { // Picker cell
if !self.editingDate {
return 0
}
}
return super.tableView(tableView, heightForRowAtIndexPath: indexPath)
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.section == 1 && indexPath.row == 0 { // Date cell
self.editingDate = !self.editingDate
tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: 1, inSection: 1)], withRowAnimation: .Fade)
}
}
The table in the storyboard:
I have set the Table Cell to a custom height of 219 in the storyboard. Below is an animated GIF of what happens when I currently click the date field. Sorry for the terrible quality.
I noticed that we can see from the animated gif above that the cell background becomes transparent (not white) which means the Content View of the cell is not stretching to the full height. I wonder why that could be?
Edit: Here is the constraints used for the DatePicker:
If you use static cells, you can specify the height of the cell holding the picker in the storyboard editor and your code could look like this:
if (indexPath.section == 1 && indexPath.row == 1 && !editingDate) { // Picker cell
return 0
}
return super.tableView(tableView, heightForRowAtIndexPath: indexPath)
Update
Replace:
tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: 1, inSection: 1)], withRowAnimation: .Fade)
With:
tableView.beginUpdates()
tableView.endUpdates()
Are you using AutoLayout?
If so try pinning your DatePicker by the cell's edges in IB by CTRL-dragging from it to the cell. After all 4 edges have been pinned give it another try.
Related
I have UITableView with static cells. I have set equal Content View height constraint equals to UIPicker view height. I have also implemented didSelectRowAt method with changing UITableViewCell content view.
https://youtu.be/lJuTLQ1oTaE
Magic happens, changing height of TableViewCell is animated. But the following change of UIPickerView height is happening simultaneously, with no animation. I have tried to put UIView.animate with layoutIfNeeded(), but it did not help.
How to animate such auto change of UIPickerView height?
// TableView functions
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
let reuseIdentifier = cell.reuseIdentifier
guard reuseIdentifier != nil else {return}
if reuseIdentifier == "currencyPickerTableViewCell" && self.selectedCellIndexPath == nil {
self.skipCellsFromGestureRecognition.append(indexPath)
self.selectedCellIndexPath = indexPath
print(currencyPicker.constraints)
tableView.beginUpdates()
tableView.endUpdates()
} else if reuseIdentifier == "currencyPickerTableViewCell" && self.selectedCellIndexPath != nil {
self.selectedCellIndexPath = nil
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
}
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if self.selectedCellIndexPath == indexPath {
return self.selectedCellHeight
} else {
return self.unselectedCellHeight
}
}
The animation actually happens but as the font of the UIPicker stays the same you can't see it , so change the font inside UIView.animate , also note that your width of the picker is static so, number of trimmed characters will be increased as the font becomes larger . . .
View Setup:
My TableView has 3 sections with 4 or 9 cell each. Each Cell has a Label and TextField.
On Starting to edit a cell at index 2 of each section, I reload the section which will now consist of 9 cells(update model to dequeueCell so that 5 more cells will be added).
Problem:
The tableView scrolls as expected(brings textfield to visible part of the screen) for the unexpanded state of the section. But after I add cells by beginning to edit the textfield of cell at index 2 of any section, the tableView scrolls such that it hides the textfield. The weird scrolling occurs for any cells in the tableview once any section has expanded numbers of cells. Also, while weird scroll is happening, the tableView is reloaded(which is leading to lose the focus away from textfield). I have included tableView.reloadSection(_:) in the didBeginEditing(:_) custom delegate of the cell.
I have seen this problem in iOS 9 and 10
Sorry for poor explanation. Thanks
Heres the Github Repo
And Problem is here
P.S. I am using Swift 3 and Xcode 8.3.3 with deployment target iOS 10
Please do not suggest answer in Swift 4 and Xcode 9
You can try another approach: change the height of cells instead of insert / delete.
Change number of cells to always return all items:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
guard let sectionEnum = Sections(rawValue: section) else { return 0 }
return sectionEnum.getRows(forExpanded: true).count
}
Set height of 'hidden' items to 0:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
guard let sectionEnum = Sections(rawValue: indexPath.section) else { return 0 }
let isExpanded = expandedSectionData[indexPath.section]
if (!isExpanded) {
let object = sectionEnum.getRows(forExpanded: true)[indexPath.row]
if (!sectionEnum.getRows(forExpanded: false).contains(object)) {
return 0;
}
}
return self.tableView.estimatedRowHeight
}
Set cell to clip subviews to its bounds:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
....
cell.clipsToBounds = true;
return cell
}
And change updating code to (remove tableView.reloadSections, change indexPath):
func didBeginEditing(textField: UITextField, cell: UITableViewCell) {
guard let indexPath = tableView.indexPath(for: cell), let section = Sections(rawValue: indexPath.section) else { return }
if indexPath.row == 7 && !expandedSectionData[indexPath.section] {
expandedSectionData[indexPath.section] = true
tableView.beginUpdates()
tableView.endUpdates()
tableView.scrollToRow(at: indexPath, at: UITableViewScrollPosition.none, animated: true)
textField.becomeFirstResponder()
}
}
You need to make textfield as first responder again, after reloading section text field no longer remains first responder.
You might need to change something like -
func didBeginEditing(textField: UITextField, cell: UITableViewCell) {
guard let indexPath = tableView.indexPath(for: cell) else { return }
if indexPath.row == 2 && !expandedSectionData[indexPath.section] {
tableView.beginUpdates()
expandedSectionData[indexPath.section] = true
tableView.reloadSections(IndexSet(integer: indexPath.section), with: .automatic)
tableView.endUpdates()
// after tableview is reloaded, get cell again
let cell = tableView.cellForRow(at: IndexPath(row: 2, section: indexPath.section)) as? TestCell
cell?.textField.becomeFirstResponder()
}
}
I have tried running this, kind of looks fine to me.
This issue has to do with your use of self-sizing tableview cells. To fix the issue, comment out these two lines in your viewDidLoad and consider defining the height of your cells with tableView:heightForRowAtIndexPath:.
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 100
Since the self-sizing tableview documentation states,
To define the cell’s height, you need an unbroken chain of constraints
and views (with defined heights) to fill the area between the content
view’s top edge and its bottom edge
I also tried changing the bottomMargin = textField.bottom constraint from priority 750 to 1000, but this did not fix the issue.
No article explains it clearly regarding my query, I have three cells in a static table and I want to hide second cell when users taps on first cell. Any kind of help is appreciated.
Although you cannot stop the static table from trying to show your cells, you can set their height to zero, making them effectively invisible:
Add this method to your table view controller delegate class:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
let cell = super.tableView(tableView, cellForRowAtIndexPath: indexPath)
return cell == myHiddenCell ? 0 : super.tableView(tableView, heightForRowAtIndexPath:indexPath)
}
In the didSelectCellAtIndexPath method, you can set the height to 0 to hide it :
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == 0 {
let indexPath = NSIndexPath(forItem: 1, inSection : 0)
let secondCell = tableview.cellForRowAtIndexPath(indexPath)
secondCell.frame.size.height = 0;
self.view.layoutSubviews()
}
}
If you want an animation, just put self.view.layoutSubviews() in an UIView animation method UIView.animateWithDuration... etc
For me, setting the height to 0 for some cells and another height for other cells wasn't an option, as all my cells have different height.
I created another cell in Storyboard, and set row height of 0 (in size inspector). Then in the code, I show the cell with height = 0 if I want to hide it, if not, I show the other cell:
if (hideCell) {
let hiddenCell = tableView.dequeueReusableCell(withIdentifier: "hiddenCell",for: indexPath) as! TheWallTableViewCell
return hiddenCell
}
else {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell",for: indexPath) as! TheWallTableViewCell
return cell
}
I have added a feature to my app that allows users to add notes which will appear in table view cells when they are tapped as they expand to show more content.
My question is - How do I layout the content that is to be shown when the cell is expanded? I have tried adding the textView to the prototype cell, but I just get the textView overlapping on the other cells in the table view rather than being hidden before being tapped.
Here is my code for when a cell is tapped:
var selectedRowIndex: NSIndexPath = NSIndexPath(forRow: -1, inSection: 0)
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedRowIndex = indexPath
tableView.beginUpdates()
tableView.endUpdates()
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.row == selectedRowIndex.row {
if cellTapped == false {
cellTapped = true
return 141
} else {
cellTapped = false
return 70
}
}
return 70
}
You need to set the cell's clipToBounds property to YES (or select the "Clip Subviews" check box in IB for the cell), so none of its subviews will show outside the bounds of the cell. The text view should have constraints to the sides of the contentView as well as a height constraint. It should also have a constraint to something above it, and that view should have a constraint to the top of the contentView. The constraints should look something like this,
I have a feature in my app that allows users to tap on a table view cell and it will expand to show more information. This works great but there are a few issues:
1) When I segue away from the table view, the top cell becomes expanded.
2) When I tap to expand a cell and then tap again to shrink the cell, it loses the dividing line above it until I tap a different cell.
3) When I slide to delete, the content in the expanded version of the cell overlaps the cell below it like this:
Here is my code for expanding the cell:
var selectedRowIndex: NSIndexPath = NSIndexPath(forRow: -1, inSection: 0)
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedRowIndex = indexPath
tableView.beginUpdates()
tableView.endUpdates()
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if tableView != self.searchDisplayController?.searchResultsTableView {
if indexPath.row == selectedRowIndex.row {
if cellTapped == false {
cellTapped = true
return 141
} else {
cellTapped = false
return 68
}
}
}
return 68
}