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

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.

Related

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!

How to set my UIButton tittle inside of uicollection view

so I want to set my button tittle like this with my array, but the result the button title didn't show my array
//viewcontroller
let menus = ["Topokki","Sundubu","Galbitang"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return menus.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionViewCell
cell.menusButton.titleLabel?.text = menus[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print(indexPath.item)
}
//collectionviewcell class
class CollectionViewCell: UICollectionViewCell {
#IBOutlet weak var menusButton: UIButton!
}
Try:
cell.menusButton.setTitle(menus[indexPath.item], forState: .normal)
From docs about titleLabel of UIButton
To set the actual text of the label, use setTitle(_:for:) (button.titleLabel.text does not let you set the text)
So... you need to set title of your button by calling setTitle(_:for:)
cell.menusButton.setTitle(menus[indexPath.item], for: .normal)

UICollectionViewCell not updating view after selection

Have an issue where:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
is called when an item is selected, but I cannot change any cell properties from this method.
I created a new project with a stripped down UICollectionViewController to change the background color of a cell when selected. It also doesn't work. Here it is:
import UIKit
private let reuseIdentifier = "Cell"
class CollectionViewController: UICollectionViewController {
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
cell.backgroundColor = UIColor.blue
return cell
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = self.collectionView(self.collectionView, cellForItemAt: indexPath)
cell.backgroundColor = UIColor.green
}
}
The only thing I did in Storyboard is delete the standard View Controller and replace it with a UICollectionViewController, create a subclass of UICollectionViewController and set the controller in the storyboard as that class.
Also, I can confirm that the cell's index path is returned from this method when called from inside the didSelectItemAt method:
self.collectionView.indexPathsForSelectedItems
You are using the wrong API. Never call the delegate method collectionView(_ cellForItemAt:, use cellForItem(at:
if let cell = collectionView.cellForItem(at: indexPath) {
cell.backgroundColor = UIColor.green
}
But be aware that this change is not persistent. When the user scrolls the color will change back to blue.
You can achieve that as below,
ViewController
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ColoursViewCell", for: indexPath) as! ColoursViewCell
return cell
}
UICollectionViewCell
class ColoursViewCell: UICollectionViewCell {
#IBOutlet var photoImageView: UIImageView?
override var bounds: CGRect {
didSet {
self.layoutIfNeeded()
}
}
override var isSelected: Bool{
didSet{
if self.isSelected{
self.photoImageView?.backgroundColor = UIColor.random
}else{
self.photoImageView?.backgroundColor = UIColor.lightGray
}
}
}
}
you can have sample projects from this link I have in GitHub
https://github.com/hadanischal/RandomColors/tree/develop

How to reload collection view inside UICollectionReusableView in swift

I have a UICollectionView (let name aCollection) inside a View controller and i use a UICollectionReusableView to show header at the top of collection view
I also have a another UICollectionView (let name bCollection) in UICollectionReusableView. I need to show here top user list. but when i am trying to connect an outlet from storyboard i am getting an error
I know how to reload data in collection view
self.aCollection.reloadData()
My issue is how to connect bCollection outlet and how to reload bCollection to show user list coming from web services?
To take the outlet of bCollection, you need to create a subclass of UICollectionReusableView.
Example:
UIViewController containing aCollection:
class ViewController: UIViewController, UICollectionViewDataSource
{
#IBOutlet weak var aCollectionView: UICollectionView!
override func viewDidLoad()
{
super.viewDidLoad()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
return collectionView.dequeueReusableCell(withReuseIdentifier: "aCell", for: indexPath)
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView
{
return (collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "reusableView", for: indexPath) as! ReusableView)
}
}
UICollectionReusableView containing bCollection:
class ReusableView: UICollectionReusableView, UICollectionViewDataSource
{
#IBOutlet weak var bCollectionView: UICollectionView!
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
return collectionView.dequeueReusableCell(withReuseIdentifier: "bCell", for: indexPath)
}
}
Interface Screenshot
Edit:
To reload bCollection:
You need a reference to the reusableView that you are using. The way you are using it ReusableView() is not right.
Use it like this:
var reusableView: ReusableView?
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView
{
self.reusableView = (collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "reusableView", for: indexPath) as! ReusableView) //Storing the reference to reusableView
return self.reusableView!
}
Now, to reload bCollection,
self.reusableView?.bCollectionView.reloadData()
Since your UICollectionView (bCollection) is inside the UICollectionReusableView you can connect outlet to UICollectionReusableView's class only. So I believe you may have to create a custom class for UICollectionReusableView and assign it in the storyboard and connect bCollection 's outlet to that custom class

Why Collection Reusable View not shown?

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")
}
}

Resources