TableView ContentInset and ContentOffset - ios

I trying to create a stretchy table view header and I saw this code on a post online:
override func viewDidLoad() {
tableView.contentInset = UIEdgeInsets(top: kTableHeaderHeight, left: 0, bottom: 0, right: 0)
tableView.contentOffset = CGPoint(x: 0, , y: -kTableHeaderHeight)
updateHeaderView()
}
I am having a little trouble understanding this code.
So essentially what it is doing is (Assuming the screen is 0 to 500 in height and kTableHeaderHeight = 200):
1) It is first adding padding to the top of the tableView by moving it up by kTableHeaderHeight in the contentInset property (this move is with respect to the frame of the tableView). So now does the tableView exists from -200 to 500?
2) Then it moves its bounds up by -kTableHeaderHeight. So does the contentOffset just make it scrollable in the -200 to 500 region? So is that why we are using contentOffset by -kTableHeaderHeight in this case?

1) No, if inset is positive then it makes table view area smaller, like 200,500
2) contentOffset is state of tableview. so when you set offset to -200 it moves content zero point to 200 from table view zero point, regardless content inset. basically it put current table view content to place where it should be regarding inset
So what that code does is reserves 200point place for custom header, that never overlaps with table view cells content (as table view API headers or footers do)

Related

swift infinite scroll up collectionView

Almost everyone can find examples with infinite collectionView load more data when collectionView scrolls to bottom, but maybe anybody known how infinite scroll collectionView from bottom to top?
Like a chat history load more when user scrolls up.
For example.
I have a list with 100000 items and with one of conditions. The app should show to a user items from 9500 to 9600 and when user scrolls up, the app should add to the collectionView items from 9400 to 9500 at the begin of collectionView, and when user scrolls down - the app should add to the collectionView to the end of collectionView and etc.
I've googled and have tried reverse logic for bottom infinite collectionView, but it unsuccessful.
CollectionView just scrolled up to the first item on the list.
Any ideas or tips?
The result you are describing is expected. Since you scrolled to top the content offset is at zero. And when you added new items the old ones were pushed to the bottom. It will not happen the other way around; when you scroll to bottom and add items beneath them the scroll position stays the same.
To be honest it is all about perspective. The position actually stays the same in both cases (or none) but depends on how you look at it. When you append items at bottom the position should stay the same looking from top (which is a default scenario). But when you add items at top the position stays the same from bottom.
So the solution in your case is that when you add items at the bottom you should change scroll from top:
let distanceFromTop = scrollView.contentOffset.y - 0.0;
// Reload data here
scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: 0.0 + distanceFromTop)
which basically means "do nothing at all".
But when you are appending items at the top you need to compute it from bottom:
let distanceFromBottom = scrollView.contentSize.height - scrollView.contentOffset.y;
// Reload data here
scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: scrollView.contentSize.height - distanceFromBottom)
You should note that you can do this sideways as well. Appending items on the right side would again be transparent. But adding them on the left should be like:
let distanceFromRight = scrollView.contentSize.width - scrollView.contentOffset.x;
// Reload data here
scrollView.contentOffset = CGPoint(x: scrollView.contentSize.width - distanceFromRight, y: scrollView.contentOffset.y)
I am using scrollView as this happens the same to every subclass of it. The same logic can be applied to table view or collection view.
Another note here is that estimated row heights or similar functionalities may cause anomalies when not done correctly. In such cases you may need to compute the offset a bit more smartly.

UICollectionView ScrollToItem With Insets

I have a horizontally scrolling uicollectionview with insets I am setting through the uicollectionviewdelegateflowlayout method insetForSectionAt.
In trying to use the method .scrollToItem the scroll doesn't seem to take into account of the inset and only scrolls a part of the way.
Is there a suggested approach to account for the inset and have the scroll go all the way? It is just one section and I am trying to scroll from item 0 to item 1.
Another approach is to manually modify content offset based on a custom calculation.
let customPoint = CGPoint(x: customX, y: customY)
collectionView.setContentOffset(customPoint, animated: true)
Where customX and customY are calculated based on cell size and cell count + other factors such as insetForSection.

bringSubviewToFront not working with dropdown view (UIView)

I have a dropdown view (it'a UIView with a UITableView fully embedded in it). The top anchor is programmatically constrained to the bottom anchor of a UIButton so that when you touch the button, the dropdown view opens. See code below
However, my problem lies in the fact that the height of the open dropdown view is 150, and the only part shown of the view is the part inside the UITableViewCell (see image), with the bottom part hidden behind the cell.
func genderDropdownViewConfig() {
genderDropdownView.backgroundColor = .red
genderDropdownView.frame = CGRect(x: 0, y: 0, width: 0, height: 0)
genderDropdownView.translatesAutoresizingMaskIntoConstraints = false
genderDropdownButton.addSubview(genderDropdownView)
//genderDropdownButton.bringSubview(toFront: genderDropdownView)
//tableView.cellForRow(at: IndexPath(row: 2, section: 0))?.superview?.addSubview(genderDropdownView)
//tableView.cellForRow(at: IndexPath(row: 2, section: 0))?.superview?.bringSubview(toFront: genderDropdownView)
genderDropdownButton.addSubview(genderDropdownView)
tableView.bringSubview(toFront: genderDropdownButton)
genderDropdownView.topAnchor.constraint(equalTo: genderDropdownButton.bottomAnchor).isActive = true
genderDropdownView.centerXAnchor.constraint(equalTo: genderDropdownButton.centerXAnchor).isActive = true
genderDropdownView.widthAnchor.constraint(equalTo: genderDropdownButton.widthAnchor).isActive = true
//genderDropdownHeight = genderDropdownView.heightAnchor.constraint(equalToConstant: 0)
genderDropdownHeight = genderDropdownView.heightAnchor.constraint(equalToConstant: 0)
for subview in genderDropdownView.subviews {
subview.backgroundColor = .clear
}
}
I guess the problem is with the way you add the genderDropdownView to the layout.
Try to add the genderDropdownView to cell's main view by
cell.view.addSubview(genderDropdownView)
and then ask the cell's view to bring the dropdown view to front.
cell.view.bringSubview(toFront: genderDropdownButton)
Your cell's height size needs to be redefined by your dropDownView. It seems that your cell contentView's height don't have any connection with your dropDownView's height to reform itself.
I think you should update the height after the button tapped.
You can try this code to update your cell's height.
cell.contentView.size.height = dropDownView.frame.origin.y + dropDownView.frame.height
This is probably due to how your layers are defined from the storyboard, it looks like your label "current weight" is forcing your dropdown view to make itself smaller. Maybe define a height constraint for the dropdown section?

add cells in UICollectionView from bottom to top using Swift 4.2

I'm trying to implement a Chat screen using UICollectionView in Swift 4.2. I've done it.
I want to improve it by making the cells grow from bottom to top instead of top to bottom (while keeping the order of messages too). Any help? I've tried searching for it, still unsuccessful.
The easiest way would be to flip the collection view and its cells:-
cv.transform = CGAffineTransform(scaleX: 1, y: -1)
cell.contentView.transform = CGAffineTransform(scaleX: 1, y: -1)
Doing this will just flip your CollectionView's content and won't require you to handle anything, I guess
EDIT:-
For your requirement, you shouldn't be appending elements to your array. You should insert new objects as the first element of the array(the data source):-
myArray.insert(element, at: 0)
In order to get the right order, you can just reverse the array
You have to make collection view height constraint outlet for it and calculate your cells height after that you can set height of collectionView. and one case will come of maximum height, your collection height will equal from screen height with calculate space your navigation and input textview.
Have you tried
let diff = collectionView.frame.height - collectionView.contentSize.height
if diff > 0 {
collectionView.contentInset = UIEdgeInsets(top: diff, left: 0, bottom: 0, right: 0)
}
You can modify the top inset of the collection view as the collectionView reloads.
Call this after calling reloadData():
func updateCollectionContentInset() {
let contentSize = yourCollectionView.collectionViewLayout.collectionViewContentSize
var contentInsetTop = yourCollectionView.bounds.size.height
contentInsetTop -= contentSize.height
if contentInsetTop <= 0 {
contentInsetTop = 0
}
yourCollectionView.contentInset = UIEdgeInsets(top: contentInsetTop,left: 0,bottom: 0,right: 0)
}

Different Table View Content Insets for Table Header View and Section Headers

I have a UITableView with several sections. I am trying to add a view to the table header view, and I want this view to be hidden by default. I accomplish this with this line of code:
tableView.contentInset = UIEdgeInsets(top: -headerView.frame.size.height, left: 0, bottom: 0, right: 0)
I also want this to be hidden when the user scrolls to the top, unless the user intentionally scroll a bit higher to see it (kind of like the behavior of a search bar). I do that with this function:
func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {
let offset = controller.tableView.contentOffset
let barHeight = controller.headerView.frame.size.height
if (offset.y < barHeight / 2.0 ) {
controller.tableView.contentInset = UIEdgeInsetsZero
}
else {
controller.tableView.contentInset = UIEdgeInsetsMake(-barHeight, 0, 0, 0)
}
controller.tableView.contentOffset = offset
}
The problem here is that when I scroll down and my inset is -barHeight, the section headers of my table view also use that content inset. Meaning that they disappear behind the navigation bar instead of sticking underneath it like normal.
My question is, how can achieve this "hidden" table header view while also maintaining the sticky section headers in the simplest way possible?
Thanks in advance!

Resources