Swift ios 9: Section header change position after reload data
I know it's an old thread but uncheck the Adjust Scroll View Insets
doesn't help. I had this problem and solved it by reload table twice.
UITableview: Section header does not automatically adjusts the position on fullscreen tap
The change could be due to change in safe area constraints with iPhone X. So, instead of reloading the whole data, I suggest adding a reference for the frame.
A workaround that worked perfectly for me even in iPhone X.
Add the following lines in the respective functions.
func hideNavbar() and hideTabbar ()
tableView.frame = CGRect(x: 0, y: 0.01, width: view.bounds.width, height: view.bounds.height)
func showNavbar() and hideTabbar ()
tableView.frame = CGRect(x: 0, y: 0, width: view.bounds.width, height: view.bounds.height)
Reload only one section header in UITableView
I'm a little unclear as to what's going on here, but it sounds like there is a UITableView
concepts worth explaining here:
UITableView
has its own concept of a cell, implemented as UITableViewCell
, and its own concept of a header/footer, implemented as UITableViewHeaderFooterView
.
Depending on which of these two you meant, there are a few things you can do to get the intended effect:
The UITableViewCell
Approach:
If you're using a UITableViewCell
as the first row of a section to act like a "header," and you just want to reload that row to the exclusion of the rest of the section, you can call yourTableViewInstance.reloadRows(at:with:)
(Apple Documentation) This method takes an array of IndexPath
s, and an animation style. You can pass in the indexPath of the one you want to reload.
The UITableViewHeaderFooterView
Approach:
If you're using a proper UITableViewHeaderFooterView
then you need to make sure that you're providing the appropriate view when reloading the section. Zack Shapiro outlines the steps you need to take in this answer:
- Create a class that's a subclass of
UITableViewHeaderFooterView
. - Register it with your
UITableView
instance. - Then in
viewForHeaderInSection
, you dolet header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "HeaderView") as! YourHeaderViewSubclass
The last thing he points out is this:
The deceptive thing is the function calls for a return of
UIView?
when it really needs adequeuedReusableHeaderFooterView
orreloadData
will cause it to disappear.
It depends on which of these two implementation paths you're taking, but this should be enough information to point you in the right direction.
Edit:
Based on the code you added, it looks like you're calling yourTableViewInstance.dequeueReusableCell(withIdentifier:for:)
instead of yourTableViewInstance.dequeueReusableHeaderFooterView(withIdentifier:)
inside of viewForHeaderInSection
.
You need to have a subclass of UITableViewHeaderFooterView
and then call it correctly. Create that new subclass, then change this:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cellHeader = tableViewHome.dequeueReusableCell(withIdentifier: "header") as! HeaderTableViewCell
// ...
to this:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cellHeader = tableViewHome.dequeueReusableHeaderFooterView(withIdentifier: "header") as! HeaderTableView
// ...
You need to follow two steps here:
- Create a new class, subclassing
UITableViewHeaderFooterView
instead ofUITableViewCell
. - Then use the appropriate class as outlined above.
Reload section of UICollectionView and preserve scroll position
If you are OK with doing it without any animation I would do as follows:
- start with disabling animations
UIView.setAnimationsEnabled(false)
- before inserting a new item (cell), store the value of visible rect for the selected cell
let selectedCell = collectionView.cellForItem(at: indexPath)
let visibleRect = collectionView.convert(collectionView.bounds, to: selectedCell)
- perform the insertion and when it's finished, check what is the new visible rect for the selected cell, compare it to the old value and add their difference to collectionView's contentOffset.
selectedObjects.insert(allObjects[indexPath.item], at: 0)
collectionView.performBatchUpdates({
// INSERTING NEW ITEM
let indexPathForNewItem = IndexPath(item: 0, section: 1)
collectionView.insertItems(at: [indexPathForNewItem])
}) { (finished) in
// GETTING NEW VISIBLE RECT FOR SELECTED CELL
let updatedVisibleRect = collectionView.convert(collectionView.bounds, to: selectedCell)
// UPDATING COLLECTION VIEW CONTENT OFFSET
var contentOffset = collectionView.contentOffset
contentOffset.x = contentOffset.x + (visibleRect.origin.x - updatedVisibleRect.origin.x)
collectionView.contentOffset = contentOffset
}
- Finish by enabling animations back
UIView.setAnimationsEnabled(true)
I tried it on a simple collection view adjusted to the behaviour you described.
Here's the whole implementation (collecionView is in the storyboard, so if you want to give my solution a test, don't forget to connect the outlet.)
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
let reuseIdentifier = "cell.reuseIdentifier"
var allObjects: [UIColor] = [.red, .yellow, .orange, .purple, .blue]
var selectedObjects: [UIColor] = []
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView.delegate = self
self.collectionView.dataSource = self
self.collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: self.reuseIdentifier)
}
}
extension ViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
switch section {
case 0: return 1
case 1: return selectedObjects.count
case 2: return allObjects.count
default: return 0
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: self.reuseIdentifier, for: indexPath)
switch indexPath.section {
case 0: cell.contentView.backgroundColor = .black
case 1: cell.contentView.backgroundColor = selectedObjects[indexPath.item]
case 2: cell.contentView.backgroundColor = allObjects[indexPath.item]
default: break
}
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 150, height: collectionView.frame.size.height)
}
}
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionView.deselectItem(at: indexPath, animated: false)
switch indexPath.section {
case 0:
self.selectedObjects.removeAll()
collectionView.reloadData()
case 2:
if selectedObjects.contains(allObjects[indexPath.item]) {
break
} else {
// SOLUTION //
UIView.setAnimationsEnabled(false)
let selectedCell = collectionView.cellForItem(at: indexPath)
let visibleRect = collectionView.convert(collectionView.bounds, to: selectedCell)
selectedObjects.insert(allObjects[indexPath.item], at: 0)
collectionView.performBatchUpdates({
let indexPathForNewItem = IndexPath(item: 0, section: 1)
collectionView.insertItems(at: [indexPathForNewItem])
}) { (finished) in
let updatedVisibleRect = collectionView.convert(collectionView.bounds, to: selectedCell)
var contentOffset = collectionView.contentOffset
contentOffset.x = contentOffset.x + (visibleRect.origin.x - updatedVisibleRect.origin.x)
collectionView.contentOffset = contentOffset
}
UIView.setAnimationsEnabled(true)
// END OF SOLUTION //
}
default:
break
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 0)
}
}
EDIT
I just also tried replacing
let indexPathForNewItem = IndexPath(item: 0, section: 1)
collectionView.insertItems(at: [indexPathForNewItem])
with
collectionView.reloadSections(IndexSet(integer: 1))
and it also works just fine, without any flickering, so it's up to you which is more convenient for you.
Reload tableView section without reloading header section - Swift
You could use UITableView
's reloadSections
method instead.
tableView.reloadSections(IndexSet(integer: 2), with: .none)
Related Topics
Swiftui - Show the Data Fetched from Firebase in View
Crash When Running on Device After Second Launch
How, Exactly, Do I Render Metal on a Background Thread
Way to Purge All But One Object Types (Models) in a Realm
Swift Firebase Storage How to Retrieve Image with Unknow Name(Nsuuid)
Fetch Coredata with One to Many Relationship in Swift
Simple Observable Struct with Rxswift
Expression Pattern of Type 'String' Cannot Match Values of Type 'Nsstoryboardsegue.Identifier
Instance Member Cannot Be Used on Type | Closures
Searchbar Problem While Trying to Search Firestore and Reload the Tableview
Differences Generic Protocol Type Parameter VS Direct Protocol Type
Shows the Alert When Uitextfield's Are Full or Empty Swift
Crop/Mask Circular Image Node in Sprite Kit Gives Jagged Edges
How How to Perform Multiple Alamofire Requests That Are Finished One After Another
Swift: Dictionaries Inside Array
Using Codable to Encode/Decode from Strings to Ints with a Function in Between
[_Nscftimer Copywithzone:]: Unrecognized Selector Sent to Instance