Swift 3 warning func collectionView instance method nearly matches optional requirement - ios

I am converting my project to Swift 3 in Xcode 8.1 and one of my collection view functions is returning a warning message indicating that the footprint has changed. The code is
func collectionView(_ collectionView: UICollectionView, cellForItemAtIndexPath indexPath: IndexPath) -> UICollectionViewCell {
and the warning is
Instance method CollectionView(:CellForItemAtIndexPath:) nearly matches optional requirement collectionView(:canFocusItemAt:) of protocol UICollectionViewDelegate
I believe the warning is a red herring here. I looked up the API reference https://developer.apple.com/reference/uikit/uicollectionview/1618088-cellforitem and I see
func cellForItem(at indexPath: IndexPath) -> UICollectionViewCell?
However, I am unable to find a declaration along these lines which won't result in a compiler error with a red dot.
edit:
After the answer below I added the datasource to my ViewController class as follows:
class MyController: UIViewController,
UICollectionViewDelegateFlowLayout,
UICollectionViewDataSource,
then in ViewDidLoad() I added this myCollectionView.dataSource = self.
I now have this
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
This View Controller is entirely constructed in code and had not implemented datasource although there was code for numberOfSectionsInCollectionView which produced a compiler red dot after the datasource was added. This was updated to
func numberOfSections(in collectionView: UICollectionView) -> Int {

I believe the warning is a red herring here
Well, it isn't. Typically the way to treat the compiler is with respect; it usually knows considerably more than you do. Your https://developer.apple.com/reference/uikit/uicollectionview/1618088-cellforitem is the red herring.
This is the function you want:
https://developer.apple.com/reference/uikit/uicollectionviewdatasource/1618029-collectionview
As you can see, the signature is:
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
... which is subtly different from what you wrote originally.
Another thing to watch out for is that you must make this declaration inside a class declaration that explicitly adopts UICollectionViewDataSource. The only exception is if this is a UICollectionViewController, in which case it already adopts UICollectionViewDataSource — and in that case, the compiler will tell you to add override to your declaration (and you should obey).

Related

How to use custom UICollectionReusableView as section header of collection view?

I've been driven insane for hours as I can't get around with the issue.
I have a collection view which can have different section with different no. of items in each. For each section I need to use a section header of different type. So for this, I'm going to use UICollectionReusableView. But I can't seem to succeed in using a custom subclass of UICollectionReusableView by means of UINib registration.
A crash happens when I downcast the reusable view to my subclass. Like:
let friendHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind,
withReuseIdentifier: "FriendHeaderView",
for: indexPath) as! FriendHeaderView
Below is the code snippet:
class ViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
private let viewModel = ProfileViewModel()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
// more code
collectionView.register(UINib(nibName: "FriendHeaderView", bundle: nil),
forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader,
withReuseIdentifier: "FriendHeaderView")
}
}
Now here is the data source implementation:
extension ViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
// valid implementation
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// valid implementation
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// valid implementation
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionView.elementKindSectionHeader:
let friendHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "FriendHeaderView", for: indexPath) as! FriendHeaderView
// *** Crash happens here *** //
return friendHeader
default:
assert(false, "Invalid element type")
}
}
}
And I don't know why the collectionView(_:layout:referenceSizeForHeaderInSection:) needs to be also implemented. So here it is:
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
let size = CGSize(width: collectionView.bounds.width, height: 100)
return size
}
}
Okay, now come to the point: The above mentioned crash doesn't happen
at all if I don't downcast with as! operator. Well, if I use section
header from the storyboard instead of UINib registration, there is
no crash.
If I'm going to need multiple type header, then I can't also use storyboard approach or without down-casting approach as I need to feed data to those headers.
What can I do to have multiple type headers with view built from interface builder?
I've made a demo project with what I've said above. If anyone is interested please check out that.
Once you assign proper class and identifier in your Xib file, then it will work without crashes.
Well, after some more investigation and the input from #good4pc in the accepted answer (actually I found out that by myself before looking at the answer) it seems that the issue is actually happening for some unwanted issue with Xcode.
When we create any view (preferably, UITableViewCell or UICollectionViewCell) with .xib, the class identity is provided automatically for that .xib in the identity inspector. But this was not the case for UICollectionReusableView. See the attached screenshot below for easy understanding.
This is a UICollectionViewCell subclassed with .xib:
This is a UICollectionReusableView subclassed with .xib:
So the key is to provide the class identity of the .xib file which
is done from the attributes inspector.

Unable to dequeue single cell in collection view

I have come across a very disturbing issue in which I am not able to dequeue only a single cell in collectionView. When there are more than one item in collectionView, it works fine but soon as I have an array with a single item, it doesn't show any cell. I am not able to figure it out what’s wrong with my code.
extension collectionview: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! UICollectionViewCell
return cell
}
}
The number returned for numberOfItemsInSection should be the total number of objects you want to show, so if you have an array of objects for example, you would want to return the count of that array. Since it's currently set to one, only one item will be shown. If you change that to match the number of items you're displaying, then the collectionView should cooperate.
First:
the returned of numberOfItemsInSectionwill be array.count for Example
var array = [model]()
Then in cellForItemAt you should configure your cell with data so
Add this block in your cell class
func configureCell(_ data: model) {
// code for cell view
}
Finally cellForItemAt will be
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! UICollectionViewCell
cell.configureCell(array[indexPath.item])
return cell
}
I hope this be helpful for you.
Needless to say, collection views work fine with only one cell. Your code is insufficient to manifest the problem you describe.
But here are a few observations/suggestions:
Add breakpoint or debugging log statement in numberOfItemsInSection and make sure it’s getting called. If not, then you likely have one of a variety of different possible problems:
Make sure you set the “data source” for the collection view (either in IB or programmatically). Merely conforming to UICollectionViewDataSource is not sufficient: You actually have to set the data source of the collection view.
Make sure you set the base class for your storyboard scene to be the view controller in question.
Make sure you put these UICollectionViewDataSource methods in the right object.
I notice that you've put these methods as an extension to collectionview.
Class names should, as a matter of convention, always start with upper case letter.
Usually the UICollectionViewDataSource methods are put in the view controller (or, less common, a dedicated object that you'll vend and to which keep your own strong reference). I'm not sure what to make of your class named collectionview.
But, in short, there is no problem having collection views with only one cell. Your problem rests elsewhere.

numberOfItemsInSection is called but cellForItemAt is not called UICollectionViewController

This maybe a duplicate post. But I haven't found any solution in the answers given in previous questions.
Basic checks from my side that I have done.
No errors on the constraint that I have put on CollectionView and CollectionViewCell.
When numberofitemsinsection is called, it always returns more than 0.
Reusable identifier is tagged for both CollectionView and CollectionViewCell
All links for ui objects are at place.
CollectionViewCell size is set as default.
Some more information on my code.
class StickerGridCollectionViewController: UICollectionViewController{
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return stickerPackList.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
/*some code*/
return cell
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
/*some code*/
}
}
I checked all the answers here, but didn't find my answer yet.
Any clue why I might be getting this issue?
Update
When I open debug view hierarchy, I find out that collectionview is taking correct area, but views which were supposed to be inside collectionview are flowing outside and acting abnormally.
Moreover I see UICollectionView inside UiCollectionView.
Any reason, why this abnormal behaviour might occur?

CollectionViewDelegate method doesn't get called after Swift 3 migration

I have a simple collection view in my app, and I use the delegate to get notified when a cell was tapped, so I implement didSelectItemAtIndexPath: method. In Swift 2 this method's signature looked like this:
#objc func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
After running thru migration process this signature changed to this:
#objc func collectionView(collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
The compiler was happy and I moved on, but now this method doesn't get triggered when I tap any cell. I found a way to fix it:
#objc(collectionView:didSelectItemAtIndexPath:) func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
Obviously, the problem was in the first parameter name, but I still don't understand why it doesn't work without specifying obj-c selector explicitly.
no need to write #objc,
Here is a working code,
extension YourViewController:UICollectionViewDelegate{
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("didSelect")
}
}
add delegate in viewdidload also add uicollectionviewdelegae,uicollectionviewdatasource also in storyboard,
this helps me.

Type 'Custom TableViewController' does not conform to protocol 'UICollectionViewDataSource'

Type 'Custom TableViewController' does not conform to protocol 'UICollectionViewDataSource'.
I have created a project using Swift and have a UITableViewcontroller. and in UITableFooterView i added collection but i can't add UICollectionViewDataSource.
Type 'TableViewController' does not conform to protocol 'UICollectionViewDataSource'
Check once your datasource method is delecared or not
func collectionView(collectionView: UICollectionView, numberOfItemInSection section: Int) -> Int;
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell;
if it is already declared , Check for types in your code Like - use numberOfItemsInSection instead numberOfItemInSection.
It should work if you have implemented the following methods
- collectionView:numberOfItemsInSection:
- collectionView:cellForItemAtIndexPath:

Resources