UICollectionView sticky header not working iOS 10 - ios10

I am having an issue with a sticky header in my collection view not working in iOS 10. I have a collectionView that has a header in the second section only - implemented by setting the size in the first section as CGSize.zero and the size in the second section as the appropriate size:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
if section == 0 {
return CGSize.zero
}
return CGSize(width: collectionView.width, height: 80.0)
}
I make the sticky header sticky with these lines in viewDidLoad:
theCollectionView.delegate = self
theCollectionView.dataSource = self
if let flowLayout = theCollectionView.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.sectionHeadersPinToVisibleBounds = true
}
Everything works great in iOS 11. When you scroll the collection view up, the header sticks to the top.
However, in iOS 10, this does not work. The header sticks - but not to the top of the screen - it sticks to its initial location and the collection view cells in the second section can be seen scrolling underneath it. There is a gap at the top of the screen. I've attached two screenshots illustrating my problem. Any help would be much appreciated!

Fixed by changing the top auto layout constraint in my storyboard. It was set from the Collection View top to the Safe Area top. Changed from the Collection View top to the Superview top.

Related

Header view of UICollectionView keeps duplicating on scroll - How to create fixed (non-sticky), single collection view header?

I wanted to implement a view hierarchy like the following so that the ENTIRE view would be scrollable:
UIScrollView
Image View
Collection view
But a lot of people on here have said that it is better to use the header that comes along with the collection view. I've done that but now I have a new problem: as I scroll the collection view, any configurations I've done to the header cell in the viewForSupplementaryElementOfKind function is duplicating (Eg: If I programmatically create a new view in viewForSupplementaryElementOfKind function, this view will keep creating as I scroll)
I kind of get that this is happening because I'm dequeuing the header using dequeueReusableSupplementaryView. But I've tried searching on Apple docs and there are no other codes I can use to instantiate the header view without making it reusable.
Is there any way I can create a view controller as described above without using UICollectionView?
I've tried setting the number of sections to 1 hoping that it would only be reused ONCE but it doesn't work.
Edit: Also tried setting header size using UICollectionViewDelegateFlowLayouta and using UICollectionView instead of UIViewController and UICollectionViewDataSource etc.
func collectionView(_ collectionView: UICollectionView,
viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView
{
switch kind {
case UICollectionView.elementKindSectionHeader:
guard
let headerView = collectionView.dequeueReusableSupplementaryView(
ofKind: kind,
withReuseIdentifier: "DiscoverHeader",
for: indexPath) as? DiscoverHeader
else {
fatalError("Invalid view type")
}
// Rotating arrow image
headerView.arrowImg.transform = headerView.arrowImg.transform.rotated(by: CGFloat.pi/0.67)
return headerView
default:
assert(false, "Invalid element type")
}
}
Why not UIImageView and UICollectionView inside UIScrollView?
You can definitely create a UIScrollView and add an UIImageView and a UICollectionView in it.
But that won't work as expected. This is because you're embedding a scrollView(UICollectionView) inside another scrollView. Scrolling the collectionView vertically will hamper the scrolling of the outer scrollView.
Solution:
Try giving the height of the header view using UICollectionViewDelegateFlowLayout's method,
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: collectionView.bounds.width, height: 100.0) //give the height as per your requirement...
}
Also, set the contentMode of imageView as Aspect Fit / Aspect Fill.

How to align collectionview cells right next to each other?

I am creating a profile page screen for my application. The screen displays all the user's recent posts. I used storyboard to create two UICollectionViewCells in a UICollectionView, one that displays your profile info and the other that displays your posts. See this for how I designed it in storyboard: https://i.stack.imgur.com/6TzwK.png
When I run the application, I get the following result: https://i.stack.imgur.com/XmvY3.jpg
However, I desire the cells placed in a way that it looks like the following: https://i.stack.imgur.com/nWQ14.jpg
How do I force the cells the align so it looks like the image above? Thanks!
Found an answer to the question. Implement UICollectionViewDelegateFlowLayout delegate to your class. Then, include the following method into the class:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = UIScreen.main.bounds.width // get the width of the screen
let scale = (width / 3) // get the width (and height) of each cell
if indexPath.row != 0 { // check to see if the cell is the profile header
return CGSize(width: scale, height: scale) // if not, then return the cell size
}
return CGSize(width: width, height: ((238/414) * width)) // if it is the profile header, return the size for it.
}
Make sure "Min Spacing" for the UICollectionView's storyboard setting to 0,0. The cells will then align into a grid view.

CollectionView: HeaderView Bounce

pretty new to CollectionView so hope to have a direction here.
I have a CollectionView with an header enabled. All works fine but I want the HeaderView stay still while scrolling the main section.
Question:
Is that possible in iOS given a collectionview? Or I have to create a dedicated still view for the header?
I set the below in the UICollectionViewFlowLayout
if let layout: UICollectionViewFlowLayout = self.collectionViewLayout as? UICollectionViewFlowLayout {
layout.scrollDirection = .vertical
layout.sectionHeadersPinToVisibleBounds = true
}
which makes the header behave as below:
When scrolling down the main collection, the header goes up until it's lower margin reaches the top of the screen, then bunches in the middle of the sceen
When going up the header follows the main collection and become hided
I am pretty sure I am not the only one which has had this issue. Maybe I am doing something wrong here?
layout.sectionHeadersPinToVisibleBounds = true
that makes your header:
stick to the cells, when you scroll down
begin to hide after bottom of cells reaches bottom of the header
I see that you want the header to be still at all times. Of course, as you said, you could add a UIView on top of the collectionView, which would do the job. A different solution may be this:
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "header", for: indexPath)
headerContainer.backgroundColor = .yellow
headerContainer.frame = header.frame
header.layer.masksToBounds = false // that's important
header.addSubview(self.headerContainer)
return header
}
So basically, we're adding a subview inside our header (header needs to be transparent then). Now, we're using the fact that UICollectionView is scrollable:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let offset = scrollView.contentOffset.y
if offset < 0 {
headerContainer.transform = CGAffineTransform(translationX: 0, y: offset)
}
}
So if we are scrolling down by offset value, we transform the container by adding offset value to its Y position.
Unfortunately, I haven't come up with the case of scrolling up (it still begins to hide after some time) - I guess it's doable.
I based on this topic:
TableView header bouncing
I think I'd rather make a UIView outside of UICollectionView anyway.

UICollectionView Cell with a thick border

I am attempting to create a round collection view cell.
In the cell I have a red UIView that is 0 from top, leading, trailing, and bottom. Inside of that cell I have another view which is white and it is 4 from the top, leading, trailing, and bottom. Within that view is a UILabel and a UIImageView.
The goal would be to have 2 cells per column and it is has a red ring around a white circle and text and an image.
To create the round UIViews I have an extension for UIView like this
extension UIView {
func createRoundView() {
layer.cornerRadius = frame.size.width/2
clipsToBounds = true
}
}
Inside of my cellForItemAt I say
cell.whiteBackgroundView.createRoundView()
cell.colorStatusView.createRoundView()
The goal is on the left, but what is happening is on the right.
Here is my Storyboard
The constraints are all blue, nothing red.
And to get the 2 cell per column I use this delegate method
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let padding: CGFloat = 25
let collectionCellSize = collectionView.frame.size.width - padding
return CGSize(width: collectionCellSize/2, height: collectionCellSize/2)
}
Any idea what I might be doing wrong?
In the end it doesn't matter if I have a view within a view. I change the ring or border color based on other data....so I just need to be able to easily change that border color.
This code is not enough, to tell what's going on there... try to set all
backgroundColors to .clear
then set those properties to your view
customView.layer.cornerRadius = half of your width
customView.layer.borderWidth = 5
customView.layer.borderColor = UIColor.red.cgColor
Using #blinkmeoff answer I figured it out mostly (although follow up question to come)
Inside the cell class I added the following
self.backgroundViewContainer.layoutIfNeeded()
self.backgroundViewContainer.layer.cornerRadius = min(backgroundViewContainer.frame.size.width, backgroundViewContainer.frame.size.height)/2
self.backgroundViewContainer.clipsToBounds = true
I kept getting weird shapes by just doing the /2 so I added the min(ba...
and that got me perfect circles

How to make a 2 row multicolumn UICollectionView that scrolls horizontally

I want to make a UICollectionView that looks like this:
[ - - - - - 0 - - - - - ] -> Scrolls Horizontally
[1][2][3][4][5][6][7][8] -> Scrolls Horizontally
How is it done?
I'd prefer not to place UICollectionView in a UITableViewCell as well.
Make your subclass inherit the CollectionViewFlowLayoutDelegate protocol
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let size = CGSize(width: defaultWidth, height: collectionView.height / 2)
//The important thing here is the height of your item is half the height of collectionView and then your flowlayout will start to put them on top of eachother.
// Also make sure that height accounts for section and content insets
return size
}
Then just make sure in viewDidLoad() you set the scrolling properties of collectionView to only horizontal scrolling.