Scrollable textview inside tableview cell - ios

I have a UITextView inside a UITableViewCell. UITextView text is appearing only for the first few visible cells and then disappearing on scroll.
If TextView scrolling is disabled, everything is working properly. So this is because of
having a scrollview(textview's) inside another scrollview(tableview's)
Is there a way with scrollable textview with fixed height inside a tableview cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? CustomCell else { return CustomCell() }
cell.myTextView.text = cellData.text
return cell
}

Related

iOS: can VoiceOver swiping work with custom UITableViewCells?

I'm having trouble with swiping between accessibility elements in a UITableView with VoiceOver on. When in a UITableView, the next/previous element in the table is focused when the user swipes right/left. The element is usually a UITableViewCell.
But I have custom cells that use subviews of the cell as accessibility elements, as in:
cell.isAccessibilityElement = false
cell.accessibilityElements = [element1, element2];
This works perfectly fine within the cell, but there's a problem swiping between cells. If the custom cell is at the bottom of the table view, it fails to focus on the next cell in the table view. Normally, using the cells themselves as accessibility elements, the table view will auto-scroll and focus on the next cell. But after the custom cell, it doesn't auto-scroll, as if there are no more cells in the table view.
See screenshot for clarification. When Label 9 is focused, the user can't swipe right to the next cells, even though there are more cells. They can still scroll though using 3 fingers, and then they'll be able to swipe to the next cells as normal.
This problem happens when using the code below. It uses a storyboard that only has a default UITableView with a default prototype cell with some UILabels added to it. So all the accessibility stuff is done here in the code.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
let customRow = 5
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
10
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == self.customRow {
return UITableView.automaticDimension
} else {
return 120
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell
if indexPath.row == self.customRow {
cell = self.tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath)
cell.isAccessibilityElement = false
cell.accessibilityElements = cell.contentView.subviews
} else {
cell = self.tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
var content = cell.defaultContentConfiguration()
content.text = "row \(indexPath.row)"
cell.contentConfiguration = content
}
return cell
}
}

How to center a spinner with cell content view after removing a cell accessory?

I have a tableview in my storyboard where the prototype cell has a disclosure indicator by default.
When I populate my table I want to remove the indicator only from the last cell AND center a spinner on it.
I'm doing it like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CharacterCell", for: indexPath) as! CharacterCell
if indexPath.row == charactersViewModel.charactersCount - 1 {
cell.accessoryType = .none
cell.accessoryView = .none
// Spinner
let spinner = UIActivityIndicatorView(style: .large)
spinner.color = .white
spinner.center = cell.contentView.center
cell.contentView.addSubview(spinner)
spinner.startAnimating()
}
return cell
}
The problem is that the spinner is offcenter, a little bit to the left, just like if the accessory is still there, but hidden.
I feel maybe I'm missing the lifecycle of a table cell, maybe it's getting the center value of the content view when the accessory is still there, so when it's removed it is offcenter?
I tried on willDisplay as well but the same thing happens.
Any tips on this?
As #Paulw11 mentioned, I used a second subclass and created another cell prototype in my tableview.
Then when the last position at the table is reached, we can use the second prototype on cellForRowAt.
Here how it is:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row >= charactersViewModel.charactersCount - 1 {
reloadRows(indexPath: indexPath)
let cell = tableView.dequeueReusableCell(withIdentifier: "LoadingCharacterCell", for: indexPath) as! LoadingCharacterCell
cell.startSpinner()
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "CharacterCell", for: indexPath) as! CharacterCell
cell.configureCell(charactersViewModel: charactersViewModel, cell: cell, index: indexPath.row)
return cell
}
}
private func reloadRows(indexPath: IndexPath) {
var indexPathList = [IndexPath]()
indexPathList.append(indexPath)
charactersTableView.reloadRows(at: indexPathList, with: .automatic)
}
And with the reloadRows function, the last cell is updated and removed when the table receives more data.

reloadData for UITableView changes constraints

I have a UITableView set up with cells: (no conflicting constraints)
Storyboard and Constraints
Looks like:
Pre-refresh
However, whenever I refresh using
self.tableView.reloadData()
the constraints seem to change which changes the cell into:
Post-refresh
Any suggestions as to why this happens?
Code for cellForRowAt:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CaptureCell
let capture = tabBar?.captureArray[indexPath.row]
cell.capsuleImage.image = capture?.image
return cell
}
The cell identifier is "cell" and I have CaptureCell which is a subclass of UITableViewCell.

Add offset to UITableViewCell at time of creation?

I'm struggling to load a cell in cellForRowAtIndexPath in such way so that I can give a custom offset to one cell only - in my case only to Cell 1, Section 1. I'd like to give a custom offset to the cell, as the same cell (xib) is used in other parts of the project where it extends to the edges of the table without any offset. All my cells are designed as *.xib files. I'm willing to set something in awakeFromNib() or other methods, if there's the need for that.
Is there any way to set the cell offset, inset, margins, padding (not sure about the correct wording) at time of the creation or at time of loading of the cell?
I've tried to set margins in this way, but it doesn't work:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (indexPath.section == 0) && (indexPath.row) == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MyCell
cell.layoutMargins = UIEdgeInsetsMake(10, 10, 10, 10)
return cell
}
} else {
// Load other cells
}
I think you're close with your cellForRowAtIndexPath implementation. Try applying the margins to the cell's contentView (the superview for your cell's content, and a subview of the cell). You may also have to set the cell's background colour to clear (the contentView will still be white).
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (indexPath.section == 0) && (indexPath.row) == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MyCell
cell.contentView.layoutMargins = UIEdgeInsetsMake(10, 10, 10, 10)
return cell
}
} else {
// Load other cells
}

detailTextLabel on cell created in storyboard with dynamic prototype appears only after cell is selected?

I am adding cell like below. Before selecting cell, only textLabel is able to be seen, after selection detailTextLabel getting appear too. Why detailTextLabel not on screen from beginning?
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("ooo", forIndexPath: indexPath) as UITableViewCell
cell.textLabel!.textColor = UIColor.whiteColor()
cell.textLabel!.text = "asdf1"
cell.detailTextLabel!.textColor = UIColor.whiteColor()
cell.detailTextLabel!.text = "asdf2"
return cell
}

Resources