I have a custom UICollectionReusableView with a single label inside: the label has leading, trailing, top and bottom constraints set to 8.
On the controller, I register the cell and then:
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return self.sectionSizeForIndex(section)
}
func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
let cell = self.collectionView.dequeueReusableSupplementaryViewOfKind(UICollectionElementKindSectionHeader, withReuseIdentifier: "cellID", forIndexPath: indexPath) as! FooCollectionViewCell
cell.setupLabelWithText("Lorem ipsum")
return cell
}
For debugging reasons, I show both the borders of the cell and the borders of the inner label, with different colors.
Here's what happens: The cell got the right frame, but the inner label seems not update constraints according to its parent view (cell).
On "Debug View Hierarchy", I see that also cell.contentView doesn't update itself -> I suppose that is this the reason why the label doesn't update.
In cell awake from nib:
override func awakeFromNib() {
super.awakeFromNib()
self.label.numberOfLines = 0
self.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
self.translatesAutoresizingMaskIntoConstraints = true
}
add this inside viewForSuplementary
switch kind {
case UICollectionElementKindSectionHeader:
let cell = self.collectionView.dequeueReusableSupplementaryViewOfKind(UICollectionElementKindSectionHeader, withReuseIdentifier: "cellID", forIndexPath: indexPath) as! FooCollectionViewCell
cell.setupLabelWithText("Lorem ipsum")
return cell
default: break
}
this will let you collectionView know that you actually are using a header and not a footer or other type of view kind
The problem may be that you are using an instance of UICollectionViewCell for a supplementary view instead of the usual custom subclass of UICollectionReusableView.
UICollectionReusableView does not have a contentView property, unlike UICollectionViewCell. The latter creates its own view for that property by manipulating your view hierarchy and constraints. It manages the frame of the content view, and gives it a gesture recognizer, amongst other things. The relationship between the cell (which is itself a view) and the content view (as a subview of the cell) is somewhat opaque and has led to developer grief in previous versions of iOS.
There is nothing in the documentation expressly forbidding the use of UICollectionViewCell as a supplementary view. But there are implicit indications that the expected practice is to the contrary.
The purpose of UICollectionViewCell is to "present the main content" of your collection view, or "your main data items". It "presents the content for a single data item". Supplementary views, on the other hand, "are separate from the collection view's cells". "Like cells, these views are provided by the data source object, but their purpose is to enhance the main content of your app."
When configuring supplementary views in a storyboard, the "Collection View Programming Guide for iOS" provides the following guidance:
For supplementary views, drag a Collection Reusable View from the object library and drop it on to your collection view. Set the custom class and the collection reusable view identifier of your view to appropriate values.
There is no express guidance about configuring supplementary views programmatically, but there is also no reason to think the expected practice would be any different.
In testing your scenario (iOS 10.2 and iOS 9.3, Xcode 8), I could not reproduce the problem. I found that the frames of the content view and label were both set correctly.
But the easiest solution to your problem is likely to be to adopt the expected practice:
Change FooCollectionViewCell (and any associated nib file) from a subclass of UICollectionViewCell to a subclass of UICollectionReusableView.
Remove the autoresizing mask lines from awakeFromNib.
Related
I have a view HeaderView that is subclassing UICollectionReusableView and which is a UIStackView arranging some subviews with labels. The number and texts of those labels depend of the information requested to a service.
Such view is registered to a collectionView this way:
collectionView.register(HeaderView.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: HeaderView.reuseIdentifier)
Then I've implemented collectionView(_:viewForSupplementaryElementOfKind:at:) method like this:
public func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionElementKindSectionHeader:
if let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind,
withReuseIdentifier: HeaderView.reuseIdentifier,
for: indexPath) as? HeaderView {
// Provide the headerView with the texts it needs to show
return headerView
}
fatalError("no view")
default:
fatalError("no view")
}
}
and also implemented collectionView(_:layout:referenceSizeForHeaderInSection:) method.
My issue is that collectionView(_:layout:referenceSizeForHeaderInSection:) is called first, and at that moment the headerView has not been instantiated, so I can't get its actual height. I can't provide a fix height either because, as I said, the height of such headerView will depend on the information I get from a service and it's going to be variable.
I'm not using either storyboard or xib files, I'm creating all the views and setting the layout in code.
I should also add that actually I need this HeaderView to be the header of just one section in my collection view, so I may not need to dequeue and reuse it?
I have 2 views on top of my CollectionView and just want to scroll on all these 3 views. I understand the best approach is to place these 2 top views in my collectionView header. How can I achieve this in storyboard (interface builder) without any code?
(I use this way in my another tableView but I have can't do this with CollectionoView)
I drag those 2 views to my collectionView's reusableView(headerView) and I've faced with this error:
enter image description here
There is no way you can drag and drop in interface builder and get header view for collection view. You have to additionally implement viewForSupplementaryElementOfKind method which returns UICollectionReusableView. While using collection reusable view, we have to treat that view in different way similar to stuffs we do for reusable cell.
Steps to follow.
Create a class (HeaderViewExample) for that header view.
Assign class (HeaderViewExample) to the reusable view you have just added in interface builder.
Give a reusable identifier (HeaderViewExample) to that reusable view.
Now you add label or buttons to the reusable view and create outlets for those outlets in the Class HeaderViewExample.
(Note: While using reusable views, don't create outlets directly in the controller.)
Now Update your CollectionViewController with the below code.
override func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath:
NSIndexPath) -> UICollectionReusableView {
var reusableView = UICollectionReusableView()
if kind == UICollectionElementKindSectionHeader {
guard let view = collectionView?.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: String(HeaderViewExample), forIndexPath: indexPath) as? HeaderViewExample else { return reusableView }
view.label = "Test String"
view.backgroundColor = UIColor.redColor()
} else {
assert(false, "Unexpected element kind")
}
return reusableView
}
I am working on an iOS app where I want to show a UIScrollView with two UILabels an UIImageView and a UICollectionView at the bottom. Now I don't know how to do this in auto layout, who could help me out?
I've tried just adding everything and setting the constraints to each other, but I do remember that the UIScrollView needs to calculate the intrinsic content height. How can I create this scroll view?
I would avoid embedding the UICollectionView into a UIScrollView in this case. It will be easier to add the UILabels and UIImageView into a section header using a UICollectionReusableView subclass.
Here are the steps:
Add a section header to the UICollectionView:
Create your section header view class by subclassing UICollectionReusableView. Set custom class and reuse identifier of the header reusable view:
Lay out your header view. Connect your outlets.
Implement the viewForSupplementaryElementOfKind method.
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
// If you also use footers:
// use a switch statement on the 'kind' argument to
// decide which view to dequeue.
let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "Header", for: indexPath)
// set up your header view
return view
}
I'm a newbie of IOS app develop. Now a day , I want to make a screen likes below picture.
How to make it?
I'm thinking about using UICollectionView but I don't know how to customize it?
You have implemented method named: "sizeForItemAt", it is used to set the size of every cell of your collection view. And your condition is satisfied because of bidirectional scrolling property of collection view. If you change the size of cell, than this may be worst for your UI.
Follow the steps:-
Select collection view from storyboard (if using).
Than go to Attributes Inspector.
And set the Scroll Direction Vertical to Horizontal.
(Vertical is default)
I can do that now by below workaround
class ViewController: UIViewController, UICollectionViewDelegateFlowLayout{
...
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 40.0, height: 500.0)
}
}
You can do it after seeing tutorial of content offset and content size of collection view. This two parameter help you to check for overlapped cell.
Check content offset of every cell with the x origin of green view.
create cocoa touch file.
step 1
step 2
Now you will have two files created.
use xib to design your UI and CollectionViewCell class to connect xib's outlets.
Now in the main view controller where you are confirming UIcollectionViewDelegate and UIcollectionViewDataSource register the xib for an identifier.
collectionView.register(UINib(nibName: "CollectionViewCell", bundle: Bundle.main), forCellWithReuseIdentifier: "yourIdentifier")
you can use this yourIdentifer to deque item for collection view in ItemForRowAtIndexPath.
Now to Change the Scroll Direction Property. Below is the image you can refer
Or programatically you can change it by following piece of code.
if let layout = self.yourCollectionView.collectionViewLayout as? UICollectionViewFlowLayout {
layout.scrollDirection = .horizontal
}
I have this function inside my UICollectionView Class:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("MoreCell", forIndexPath: indexPath) as! UICollectionViewCell
if let icLab = cell.viewWithTag(491) as? UILabel{
icLab.text = moreitems[indexPath.section][indexPath.row].title
}else{
println("Couldnt find label")
}
// Same code for imageView
The rest of the UICollectionView is fine because it does display the cells properly on the screen, as well as the initially set label text ("Label")
But I just can't seem to access the subviews inside. Here is how they're set up:
And I do have the Tags and the Cell ID set up correctly.
Has anyone any idea why it keeps printing Couldn't find Label
Screen looks like this:
Obviously the placeholder image and label is not being edited.
From apples's document
Installed views are added to the view hierarchy. Uninstalled views are not added.
Choose the label and select 'Installed'
From the UICollectionViewCell documentation :
var contentView: UIView { get }
When configuring a cell, you add any custom views
representing your cell’s content to this view. The cell object places
the content in this view in front of any background views.
So you must look for your views inside cell's contentView and not cell itself
if let icLab = cell.contentView.viewWithTag(491) as? UILabel{
icLab.text = moreitems[indexPath.section][indexPath.row].title
}else{
println("Couldnt find label")
}