First and foremost, thank you very much for taking the time and effort to answer my questions. This is my first project and some things are hard to find an answer for.
It now works beautifully, here is the working code for those who might experience the same problems.
in my ViewController
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//Expandable cell
guard let cell = tableView.cellForRow(at: indexPath) as? AnnotationCell else { return }
cell.toggleFullView()
tableView.beginUpdates()
tableView.endUpdates()
}
and the reworked toggleMethod:
///Expand and collapse the cell
func toggleFullView() {
//Show the full contents
print("ShowFullDetails = \(showFullDetails.description.uppercased())")
if showFullDetails {
print("Show full contents")
if contentView.subviews.contains(previewDetailsView) {
previewDetailsView.removeFromSuperview()
}
if !contentView.subviews.contains(fullDetailsView) {
contentView.addSubview(fullDetailsView)
}
NSLayoutConstraint.activate([
fullDetailsView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: padding),
fullDetailsView.leadingAnchor.constraint(equalTo: checkmarkView.trailingAnchor, constant: padding),
fullDetailsView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -2 * padding),
fullDetailsView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -padding)
])
//Show preview contents
} else {
print("Show preview contents")
if contentView.subviews.contains(fullDetailsView) {
fullDetailsView.removeFromSuperview()
}
if !contentView.subviews.contains(previewDetailsView) {
contentView.addSubview(previewDetailsView)
}
NSLayoutConstraint.activate([
previewDetailsView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: padding),
previewDetailsView.leadingAnchor.constraint(equalTo: checkmarkView.trailingAnchor, constant: padding),
previewDetailsView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -2 * padding),
previewDetailsView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
])
}
showFullDetails.toggle()
//Invalidate current layout &
self.setNeedsLayout()
}
Now only a reusable cell issue remains, but i'll make a new topic about that! Thanks again!
Post
Replies
Boosts
Views
Activity
Ok, now I got some action with .SetNeedslayout() It animates and changes back and forth with one click, BUT ...
The cell does not adjust its size to the content and stays the size of the first view (previewDetailsView). How could I make it so it sizes to fit the content ? (which it does with the reloadData way)
///Expand and collapse the cell
func toggleFullView() {
showFullDetails.toggle()
if showFullDetails {
//show the full version
if contentView.subviews.contains(previewDetailsView) {
previewDetailsView.removeFromSuperview()
}
if !contentView.subviews.contains(fullDetailsView) {
contentView.addSubview(fullDetailsView)
}
} else {
//show the preview version
if contentView.subviews.contains(fullDetailsView) {
fullDetailsView.removeFromSuperview()
}
if !contentView.subviews.contains(previewDetailsView) {
contentView.addSubview(previewDetailsView)
}
}
//Invalidate the layout
contentView.setNeedsLayout()
UIView.animate(withDuration: 1.2) {
self.layoutIfNeeded()
}
}
Thank you for your reply There's indeed a gesturerecognizer attached to the cell/tableview. How would that interfere?
private func configureTableView() {
tableView = UITableView(frame: view.bounds, style: .grouped)
tableView.delegate = self
tableView.dataSource = self
tableView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
tableView.backgroundColor = .clear
view.addSubview(tableView)
tableView.register(AnnotationCell.self, forCellReuseIdentifier: AnnotationCell.reuseIdentifier)
tableView.register(AnnotationHeaderCell.self, forHeaderFooterViewReuseIdentifier: AnnotationHeaderCell.reuseIdentifier)
//Dynamic sizing cells
tableView.sectionHeaderHeight = 40
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 70
//Tap and hold to edit annotation
let tapAndHold = UILongPressGestureRecognizer(target: self, action: #selector(tapAndHoldCell))
tapAndHold.minimumPressDuration = 0.3
tableView.addGestureRecognizer(tapAndHold)
}
@objc private func tapAndHoldCell(recognizer: UILongPressGestureRecognizer) {
if recognizer.state == .ended {
guard let indexPath = tableView.indexPathForRow(at: recognizer.location(in: self.tableView)) else { return }
let annotation = annotationsController.getAnnotationFor(indexPath)
let viewController = AnnotationDetailsViewController(withAnnotation: annotation)
viewController.delegate = self
navigationController?.pushViewController(viewController, animated: true)
}
}
If I don't reload rows or reloadData, nothing happens ...?
Thank you! I'll try reloadItemsWithIdentifier
The warning you are seeing about "UITableView was told to layout its visible cells and other contents without being in the view hierarchy" probably means you are calling apply before UITableView is in a window. The most common case where this happens is if you call apply from inside a view controllers viewDidLoad, where the view was loaded but is not yet in the view hierarchy. About the tableView issue. On first load I do call apply from viewDidLoad and got away with the warning by forcing it on the main queue:
DispatchQueue.main.async {
self.dataSource.apply(snapshot, animatingDifferences: true)
}
Where would be more appropriate to call apply() ?
However, when saving or updating an annotation the. situation is different. The ViewController is already loaded and pushes another ViewController on top to add or edit and existing annotation. When the user taps save, a delegate method is called on the original ViewController (the save method in my post). That's when I get the warning. How do I prevent that warning in this case ?
To summarize: AnnotationsViewController > AnnotationDetailsViewController > Calls save() on AnnotationsViewController (delegate method)
Also, I don't use storyboards, all programmatic UI
Anyone ?
Thank you! Good to know, has been bugging me for days!