Swift iOS 9: Section Header Change Position After Reload Data

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 IndexPaths, 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:

  1. Create a class that's a subclass of UITableViewHeaderFooterView.
  2. Register it with your UITableView instance.
  3. Then in viewForHeaderInSection, you do let 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 a dequeuedReusableHeaderFooterView or reloadData 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:

  1. Create a new class, subclassing UITableViewHeaderFooterView instead of UITableViewCell.
  2. 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



Leave a reply



Submit