Singleton Class not updating properly - ios

I've got a collectionViewController and a normal viewcontroller. When a cell is tapped it goes to the VC and sets the label to the cell tapped. The variable for this is in a singleton class.
The issue I'm having is that the first time you tap a cell and go to the VC the label doesn't say anything (console prints correct data though). Then you go back to the collectionView and tap a different cell, the label in the view now shows the cell you tapped previously.
I tried cleaning the build folder etc. but didn't do anything. I also tried another method - let CVC = CollectionViewController() then lbl.text = CVC.cellTapped (create var first) but that didnt work either.
SharingManager.swift
class SharingManager {
var cellChoose = String()
static let sharedInstance = SharingManager()
}
CollectionViewController.swift
class CollectionViewController: UICollectionViewController {
let sm = SharingManager.sharedInstance
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
// Configure the cell
let lbl = cell.viewWithTag(1) as! UILabel
lbl.text = String(indexPath.row)
return cell
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
sm.cellChoose = "cell\(indexPath.row)"
print(sm.cellChoose)
}
}
VC2.swift (the viewcontroller tapping a cell takes you to)
class VC2: UIViewController {
let sm2 = SharingManager.sharedInstance
#IBOutlet weak var lbl2: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
self.lbl2.text = sm2.cellChoose
//print(sm2.cellChoose)
}

Since you're navigating via a segue, it's possible your new ViewController is loaded before didSelectItem is called. Moving lbl2.text = sm2.cellChoose to viewWillAppear will fix your issue.
The "correct" way to do this with segues is handle this in prepareForSegue, but what you have now will work.

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!

I really don't know how to make collectionViewCell (didSelected) function work properly

I'm making a Calendar in Swift 5 and now I'm encountering a problem.
First this is my code:
import UIKit
class YearViewController: UIViewController {
#IBOutlet weak var yearName: UILabel!
#IBOutlet weak var yearCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
yearName.text = String(CalenderBrain.init().curruntYear)
yearCollectionView.dataSource = self
yearCollectionView.delegate = self
yearCollectionView.register(UINib(nibName: "ReusableYearCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "MyCell3")
if let layout = self.yearCollectionView.collectionViewLayout as? UICollectionViewFlowLayout{
layout.minimumInteritemSpacing = -10
layout.minimumLineSpacing = 1
}
}
}
extension YearViewController : UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 12
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyCell3", for: indexPath) as! ReusableYearCollectionViewCell
cell.monthName.text = Calendar.current.monthSymbols[indexPath.row]
if cell.monthName.text == CalenderBrain.init().curruntMonthName(){
cell.monthName.textColor = .red
cell.tag = 999
}
cell.month = indexPath.row + 1
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return .init(width: 126.3, height:(570-3)/4)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(withIdentifier: "lastOne") as? ViewController
vc?.whatIGetFromYearViewController = indexPath.row + 1
vc?.curruntMonthNameThatIHaveToPut = Calendar.current.monthSymbols[indexPath.row]
vc?.nextMonthNameThatIHaveToPut = Calendar.current.monthSymbols[indexPath.row + 1]
self.navigationController?.pushViewController(vc!, animated: true)
print("i'm pressed")
}
}
I want if my cells are clicked then a segue occurs.
And this is an image of my cell. (I make collectionView in collectionViewCell.)
The problem is the segue only occurs when I click the left part of the label. If I click collectionView in CollectionViewCell then the segue doesn't occur. I think that's because Swift recognizes collectionView in collectionViewCell another CollectionView. So for now, I want to make collectionViewCell clicked and segue occurred regardless of part that I click. Is that possible?
Try set collectionView.isUserInteractionEnable = false in collectionViewCell.
This code will prevent user action onto collectionView in collectionViewCell, just receive didSelectItem action of main collectionView.

Hint on how to create & pass data via segue when collectionview cell is pressed

I have a UICollectionView embedded in a UITableViewCell & I want to perform a segue when a UICollectionViewCell is pressed & pass the data represented by that cell to the destination ViewController for detailed information.
Here is the code for the embedded UICollectionView inside the UITableViewCell
#IBOutlet weak var EventCollection: UICollectionView!
var events = [Events]()
override func awakeFromNib() {
super.awakeFromNib()
EventCollection.delegate = self
EventCollection.dataSource = self
}
extension PopularCell: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return events.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = EventCollection.dequeueReusableCell(withReuseIdentifier: "EventCell", for: indexPath) as! EventCell
let event = events[indexPath.row]
print("Event Name:\(event.event_name)")
cell.event = event
cell.tag = indexPath.row
return cell
}
How do I perform & prepare a segue in the main ViewController when a UICollectionViewCell is pressed so as to pass the data contained by that cell to the destination ViewController
Here are the steps that you need to do.
As you said, your CollectionView is inside TableView. So your TableView delegates/DataSources binded with the MainViewController. CollectionView delegates/DataSources binded with the TableViewCell.
Now create a protocol to know user has clicked on collectionView.
protocol MyProtocol : class {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
}
In TableViewCell, you need to call delegate like this,
class MyTableCell : UITableViewCell, UICollectionViewDelegate {
weak var delegate : MyProtocol?
:
:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let delegate = delegate {
delegate.collectionView(collectionView, didSelectItemAt: indexPath)
}
}
}
Now your MainViewController must have to conform this protocol,
class MainViewController :UIViewController, MyProtocol {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// Do segue here ....
}
}
Note : Make sure to bind delegate with your MainViewController i.e. in TableviewCellForRow have cell.delegate = self
Add the following code with in a new file named UIView_Ext
extension UIView {
var parentViewController: UIViewController? {
var parentResponder: UIResponder? = self
while parentResponder != nil {
parentResponder = parentResponder!.next
if let viewController = parentResponder as? UIViewController {
return viewController
}
}
return nil
}
}
In func didSelectItem(At indexPath: IndexPath) method , write the following code
self.parentViewController?.performSegue(withIdentifier: "Identifer", sender: "Your Data in place of this string")

UICollectionView not rendering cells - no code only storyboard

I am experimenting with UICollectionView and am only using Storyboard - no code at all. I set the number of items to 3 and gave each cell a different color and reuse identifier in the storyboard. Yet the CollectionView (pinkish background) is shown but no cells are shown. What am I missing?
Thanks in advance
-SR
Short Answer
UICollectionView doesn't support static content layouts.
Discussion
But there is a way to simulate it through code. I recommend this great article.
You don't have any code in your ViewController.swift. You can add something like this:
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
var menuItems = ["Aap","Koe","Vis","Paard", "Leeuw", "Varken"]
#IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return menuItems.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
if let c = cell as? AangepasteCollectionViewCell {
c.cellLabel.text = menuItems[indexPath.row].capitalized
}
return cell
}
}
For this example you have to give the collection cell the name cell in the atribute inspector

Perform Segue from UICollectionViewCell Button different from Cell Click

I have a UICollectionViewCell, with a UIButton. And I have two different actions. The first one, when the user presses the cell, it will segue to another view withdidSelectITemAt; the second one, when the users presses the UIButton inside the cell.
My problem is that on Swift Code of MyCollectionViewCell, I cant perform a segue, When I write the Code:
self.performSegue(withIdentifier: "toStoreFromMyDiscounts", sender: nil)
it says the error:
Value of Type MyCollectionViewCell has no member performSegue.
I also cannot write prepareForSegue, it doesn't auto complete.
How can I create a segue from a cell, that is different from click the cell itself?
You can not call performSegue from your UICollectionViewCell subclass, because there is no interface declared on UICollectionViewCell like that.
The reason why it is working didSelectItemAtIndexPath() is because i suppose the delegate of your UICollectionView is a UIViewController subclass, what has the function called performSegueWithIdentifier:()`.
You need to notify your UIViewController when the button was clicked in your UICollectionViewCell, for what you have various possibilities, like KVO or using delegate.
Here is a little code sniplet, how to use KVO. This solution is great, as long as you do not care, in which cell was the button pressed.
import UIKit
class CollectionViewCell: UICollectionViewCell {
#IBOutlet weak var button: UIButton!
}
class CollectionViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
}
extension CollectionViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: CollectionViewCell = self.collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionViewCell
// Add your `UIViewController` subclass, `CollectionViewController`, as the target of the button
// Check out the documentation of addTarget(:) https://developer.apple.com/reference/uikit/uicontrol/1618259-addtarget
cell.button.addTarget(self, action: #selector(buttonTappedInCollectionViewCell), for: .touchUpInside)
return cell
}
func buttonTappedInCollectionViewCell(sender: UIButton) {
self.performSegue(withIdentifier: "toStoreFromMyDiscounts", sender: nil)
}
}
EDIT:
If you care, in which cell the touch event has happend, use the delegate pattern.
import UIKit
protocol CollectionViewCellDelegate: class {
// Declare a delegate function holding a reference to `UICollectionViewCell` instance
func collectionViewCell(_ cell: UICollectionViewCell, buttonTapped: UIButton)
}
class CollectionViewCell: UICollectionViewCell {
#IBOutlet weak var button: UIButton!
// Add a delegate property to your UICollectionViewCell subclass
weak var delegate: CollectionViewCellDelegate?
#IBAction func buttonTapped(sender: UIButton) {
// Add the resposibility of detecting the button touch to the cell, and call the delegate when it is tapped adding `self` as the `UICollectionViewCell`
self.delegate?.collectionViewCell(self, buttonTapped: button)
}
}
class CollectionViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
}
extension CollectionViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: CollectionViewCell = self.collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionViewCell
// Asssign the delegate to the viewController
cell.delegate = self
return cell
}
}
// Make `CollectionViewController` confrom to the delegate
extension CollectionViewController: CollectionViewCellDelegate {
func collectionViewCell(_ cell: UICollectionViewCell, buttonTapped: UIButton) {
// You have the cell where the touch event happend, you can get the indexPath like the below
let indexPath = self.collectionView.indexPath(for: cell)
// Call `performSegue`
self.performSegue(withIdentifier: "toStoreFromMyDiscounts", sender: nil)
}
}
Here's an elegant solution that only requires a few lines of code:
Create a custom UICollectionViewCell subclass
Using storyboards, define an IBAction for the "Touch Up Inside" event of your button
Define a closure
Call the closure from the IBAction
Swift 4+ code
class MyCustomCell: UICollectionViewCell {
static let reuseIdentifier = "MyCustomCell"
#IBAction func onAddToCartPressed(_ sender: Any) {
addButtonTapAction?()
}
var addButtonTapAction : (()->())?
}
Next, implement the logic you want to execute inside the closure in your
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MyCustomCell.reuseIdentifier, for: indexPath) as? MyCustomCell else {
fatalError("Unexpected Index Path")
}
// Configure the cell
// ...
cell.addButtonTapAction = {
// implement your logic here, e.g. call preformSegue()
self.performSegue(withIdentifier: "your segue", sender: self)
}
return cell
}
You can use this approach also with table view controllers.
Another solution that also works like a charm:
extension YOURViewController : UICollectionViewDataSource
{
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "YOURCell", for: indexPath) as! YOURCollectionViewCell
cell.butTapped = {
[weak self] (YOURCollectionViewCell) -> Void in
// do your actions when button tapped
}
}
return cell
}
class YOURCollectionViewCell: UICollectionViewCell
{
var butQRTapped: ((YOURCollectionViewCell) -> Void)?
#IBAction func deleteButtonTapped(_ sender: AnyObject) {
butTapped?(self)
}
}

Resources