Why Collection Reusable View not shown? - ios

I would like to create UICollectionView with header. I set Collection Reusable View on mainStoryBoard but, nothing is shown on device. I tried to search but could not find out why it is not appearing. I
Main Story Board
On device
import UIKit
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var collectionView: UICollectionView!
var images = ["medal1","medal2","medal3","medal4","medal5","medal6","medal7","medal8","medal9","medal10","medal1","medal2","medal3","medal14"]
var texts = ["hi","yes","hoo","such","hi","yes","hoo","such","hi","yes","hoo","such","hi","yes"]
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
}
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell
cell.myImage.image = UIImage(named: images[indexPath.row])
cell.achievementLabel.text = texts[indexPath.row]
return cell
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
}
import UIKit
Class for collection View
class CustomCell: UICollectionViewCell {
#IBOutlet weak var myImage: UIImageView!
#IBOutlet weak var achievementLabel: UILabel!
}
class for Collection Reusable View
import UIKit
class CollectionReusableView: UICollectionReusableView {
#IBOutlet weak var reuseableVimage: UIImageView!
}
> import UIKit
class ViewController: UIViewController, UICollectionViewDelegate {
#IBOutlet weak var collectionView: UICollectionView!
var images = ["medal1","medal2","medal3","medal4","medal5","medal6","medal7","medal8","medal9","medal10","medal1","medal2","medal3","medal14"]
var texts = ["hi","yes","hoo","such","hi","yes","hoo","such","hi","yes","hoo","such","hi","yes"]
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
if kind == UICollectionElementKindSectionHeader {
let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "HeaderView", for: indexPath)
// do any programmatic customization, if any, here
return view
}
fatalError("Unexpected kind")
}
}

You have to implement viewForSupplementaryElementOfKind:
Implement collectionView(_:viewForSupplementaryElementOfKind:at:), for UICollectionElementKindSectionHeader or UICollectionElementKindSectionFooter, as appropriate.
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
if kind == UICollectionElementKindSectionHeader {
let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "CollectionReusableView", for: indexPath) as! CollectionReusableView
// do any programmatic customization, if any, here
return view
}
fatalError("Unexpected kind")
}
Make sure the header reusable view in IB has
the appropriate base class; and
the appropriate "reuse identifier"
In IB, make sure the collection view's "Accessories" have checkmarks next "Section Header" and "Section Footer", as appropriate.

Try to implement this. There's an example in RayWenderlich that could be useful for you: https://www.raywenderlich.com/136161/uicollectionview-tutorial-reusable-views-selection-reordering
override func collectionView(_ collectionView: UICollectionView,
viewForSupplementaryElementOfKind kind: String,
at indexPath: IndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionElementKindSectionHeader:
let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind,
withReuseIdentifier: "CollectionReusableView",
for: indexPath) as! CollectionReusableView
headerView.reuseableVimage ....
return headerView
default:
assert(false, "Unexpected element kind")
}
}

Related

How to change label text place in collectionveiw header at runtime?

this is screenshot(image) of my viewcontrollerI'm using collectionview and placed label in header, header and label created in storyboard
I want to change label text at runtime.
I know I can do it in viewForSupplementaryElementOfKind of collectionview but I want it in viewdidload method
my code is as below
Controller code
class DeleteViewController: UIViewController,UICollectionViewDelegate,UICollectionViewDataSource {
#IBOutlet weak var collectionView: UICollectionView!
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)
cell.backgroundColor = .red
return cell
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "headerId", for: indexPath) as! TestCollectionReusableView
headerView.labelText.text = "dummy" // this line shows dummy
return headerView
}
let testCollectionReusableView = TestCollectionReusableView()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.register(TestCollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "headerId")
testCollectionReusableView.labelText.text = "Test"
// above line Xcode 12.4 shows error - **Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value**
}
}
Header Class File
class TestCollectionReusableView: UICollectionReusableView {
#IBOutlet weak var labelText: UILabel!
}
Probably the best place to do this is in your DeleteViewController assuming that this class holds your collection view and is a data source for it.
You can simply add a new property such as headerText: String which may then be user in your data source method. Whenever you change the text you should reload your collection view (or just the headers) for change to take effect.
So for instance
class DeleteViewController: UIViewController, UICollectionViewDataSource {
#IBOutlet private var collectionView: UICollectionView?
private var currentHeaderText: String = "Dummy"
override func viewDidLoad() {
super.viewDidLoad()
changeHeaderText(to: "Some other text")
}
private func changeHeaderText(to text: String) {
currentHeaderText = text
collectionView?.reloadData()
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "headerId", for: indexPath) as! TestCollectionReusableView
headerView.labelText.text = currentHeaderText
return headerView
}
}
A more optimized approach may be to detect which index paths need reloading and only reload those index paths. It would even be possible to use methods like collectionView?.visibleSupplementaryViews(ofKind: <#T##String#>) and loop through all visible views that correspond to your class to apply the change. But this is all up to you.

Why is my UICollectionView not showing at all?

I am using storyboards, and my UICollectionView with a basic custom cell is not showing at all? My simulator is constantly running a version of my storyboard when my "Next" button was at the middle of the screen, obviously now it is at the bottom as shown here along with all the identities and links created in the storyboard: https://imgur.com/a/R8iTm9n
import UIKit
class CategoryViewController: UIViewController{
#IBOutlet weak var categoryCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
categoryCollectionView.delegate = self
categoryCollectionView.dataSource = self
NetworkingClient.fetchRecipeCategories{ (recipeCategories) in
//print(recipeCategories)
}
categoryCollectionView.reloadData()
}
}
extension CategoryViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionView.deselectItem(at: indexPath, animated: true)
print("Taptaptap")
}
}
extension CategoryViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 12
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
var cell = UICollectionViewCell()
if let testCell = collectionView.dequeueReusableCell(withReuseIdentifier: "categoryCell", for: indexPath) as? CategoryCollectionViewCell{
testCell.configure(with: indexPath.row)
cell = testCell
print("test1")
}
print("test2")
return cell
}
}
/*
extension CategoryViewController: UICollectionViewDelegateFlowLayout {
}
*/
And my my custom view cell :
class CategoryCollectionViewCell: UICollectionViewCell {
static let identifier: String = "CategoryCollectionViewCell"
#IBOutlet weak var categoryImageView: UIImageView!
#IBOutlet private weak var testLabel: UILabel!
func configure(with id: Int){
testLabel.text = String(id)
categoryImageView.clipsToBounds = true
categoryImageView.contentMode = .scaleAspectFit
}
}
Sorry for the formatting...
Register your custom collectionview cell named "categoryCell" in viewDidLoad()
categoryCollectionView.register(UINib(nibName: "categoryCell", bundle:
nil),forCellWithReuseIdentifier: "categoryCell")
categoryCollectionView.delegate = self
categoryCollectionView.dataSource = self
categoryCollectionView.reloadData()

The currencyLabel outlet from the ViewController to the UILabel is invalid. Outlets cannot be connected to repeating content

I inserted Label into CollectionView cell but when I made outlet this error appeared.
error: The currencyLabel outlet from the ViewController to the UILabel is invalid. Outlets cannot be connected to repeating content
class ViewController: UIViewController {
var currency = Currency.getCurrency()
#IBOutlet var currencyLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return currency.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "currencyCell", for: indexPath) as! CurrencyCollectionViewCell
let currencyList = currency[indexPath.row]
currencyLabel.text = "\(currencyList.currency) \n \(currencyList.cash)"
return cell
}
}
You need to create IBOutlet of your currencyLabel in CurrencyCollectionViewCell class, and use it like
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "currencyCell", for: indexPath) as! CurrencyCollectionViewCell
let currencyList = currency[indexPath.row]
cell.currencyLabel.text = "\(currencyList.currency) \n \(currencyList.cash)"
return cell
}
You have made a simple mistake.
You should make below outlet in CollectionViewCell not in your ViewController.
#IBOutlet var currencyLabel: UILabel!

Creating a child view controller from a collection view

I am trying to create a child view for my collection view, but whenever I run the app and select a cell from the collection view, nothing appears. How do I fix this so that the tableView is then displayed once a cell is selected?
Here is the code for my collectionView.
import UIKit
class ContentViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
let imageArray = [UIImage(named: "image 1"), UIImage(named: "image 2"), UIImage(named: "image 3")]
override func viewDidLoad() {
super.viewDidLoad()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell
cell.mapIconImage.image = imageArray[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let child = MapItemsViewController()
addChild(child)
view.addSubview(child.view)
child.didMove(toParent: self)
}
class CustomCell: UICollectionViewCell {
#IBOutlet weak var mapIconImage: UIImageView!
}

How to Fix the error: Generic parameter 'T' could not be inferred

I am doing a tutorial, and have one problem with error:
Generic parameter 'T' could not be inferred.
****>> I just update the question follow your advice.
And added 2 line code:
let cell: TacoCell = collectionView.dequeReusableCell(forIndexPath: indexPath)
cell.configureCell(taco: ds.tacoArray[indexPath.row])
return cell
And i also modify this code:
func dequeReusableCell to dequeReusableCell
In my UICollectionView+Ex.swift file.
import UIKit
extension UICollectionView {
func register<T: UICollectionViewCell>(_: T.Type) where T: ReusableView, T: NibLoadableView {
let nib = UINib(nibName: T.nibName, bundle: nil)
register(nib, forCellWithReuseIdentifier: T.reuseIdentifier)
}
func dequeReusableCell<T: UICollectionView>(forIndexPath indexPath: IndexPath) -> T where T: ReusableView{
guard let cell = dequeueReusableCell(withReuseIdentifier: T.reuseIdentifier, for: indexPath as IndexPath) as? T else {
fatalError("Coud not deque cell with Identifier: \(T.reuseIdentifier)")
}
return cell
}
}
extension UICollectionViewCell: ReusableView {}
and in my MainVC.swift, I have the following code:
import UIKit
class MainVC: UIViewController, DataServiceDelegate {
#IBOutlet weak var headerView: HeaderView!
#IBOutlet weak var collectionView: UICollectionView!
var ds: DataService = DataService.instance
override func viewDidLoad() {
super.viewDidLoad()
ds.delegate = self
ds.loadDeliciousTacoData()
collectionView.dataSource = self
collectionView.delegate = self
headerView.addDropShadow()
/*
let nib = UINib(nibName: "TacoCell", bundle: nil)
collectionView.register(nib, forCellWithReuseIdentifier: "TacoCell")
*/
collectionView.register(TacoCell.self)
}
func deliciousTacoDataLoaded() {
print("Delicious Taco Data Loaded!")
}
}
extension MainVC: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return ds.tacoArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TacoCell", for: indexPath) as? TacoCell {
// cell.configureCell(taco: ds.tacoArray[indexPath.row])
// return cell
// }
// return UICollectionViewCell()
let cell: TacoCell = collectionView.dequeReusableCell(forIndexPath: indexPath)
cell.configureCell(taco: ds.tacoArray[indexPath.row])
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 95, height: 95)
}
}
At this line:
let cell = collectionView.dequeReusableCell(forIndexPath: indexPath) as TacoCell
the compiler complains.
Why this error was showed, and how to fix it? Thank in advance.
==> After do like your advice, now i can build success.
My problem is soved. I still implement something, if have more any issue, i will update again. Many thanks!
You should let the generic constraint for dequeReusableCell to be: <T: UICollectionViewCell> instead of <T: UICollectionView>:
func dequeReusableCell<T: UICollectionViewCell>(forIndexPath indexPath: IndexPath) -> T where T: ReusableView{
Obviously, I would assume that TacoCell is type of ReusableView.
Fix the typo(s)
func dequeueReusableCell<T: UICollectionViewCell>( ...
Annotate the type and remove the type cast
let cell : TacoCell = collectionView.dequeueReusableCell(forIndexPath: indexPath)

Resources