my Callback in CollectionViewCell doesn't work correctly - swift

I have a simple CollectionViewCell with PhoneNumberTextField and a callback that I want to send to my server
class PhoneNumberCollectionViewCell: UICollectionViewCell, NiBLoadable, UITextFieldDelegate {
#IBOutlet weak var phoneLabel: UILabel!
#IBOutlet weak var PhoneNumberTextField: UITextField!
#IBOutlet weak var SecurityLabel: UILabel!
var returnValue: ((_ value: String) -> ())?
override func awakeFromNib() {
super.awakeFromNib()
Decorator.decorate(self)
}
func setPhoneLabelText(text: String) {
phoneLabel.text = text
}
func setSecurityLabel(text: String) {
SecurityLabel.text = text
}
func textFieldDidEndEditing(_ textField: UITextField) {
returnValue?(PhoneNumberTextField.text ?? "") // Use callback to return data
}
}
and I also have my ViewController with my CollectionView and I cant get my phoneNumber from cell to variable in ViewController. Here is come code
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let models = model[indexPath.row]
switch models {
case .phoneNumber:
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PhoneNumberCollectionViewCell.name, for: indexPath) as? PhoneNumberCollectionViewCell {
cell.setSecurityLabel(text: "_ALLYOURDATAISINSECUREDAREA")
cell.setPhoneLabelText(text: "_YOURPHONENUMBER")
cell.PhoneNumberTextField.text = phoneNumber
cell.returnValue = { value in
self.phoneNumber = value
}
return cell
}
and when I print what I got from textField I got nil
#objc func sendPhoneNumber() {
print("PHONE NUMBER IS - \(String(describing: self.phoneNumber))")
}
what's wrong am I doing???

You need to set the delegate for the textField, or the textField delegates wont work.
override func awakeFromNib() {
super.awakeFromNib()
Decorator.decorate(self)
PhoneNumberTextField.delegate = self
}
Side note:
Don't capitalize variables and constants.
Edit to secondary question:
You either want to (through the Xib) make an IBAction outlet that is called on value changed, or:
textField.addTarget(self, action: #selector(textFieldValueChanged), for: .editingChanged)
both options will call the delegate function every time a letter is typed or removed.

Have you registered your collection view cell and declare the delegate and datasource?
let flowLayout = UICollectionViewFlowLayout()
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
collectionView(PhoneNumberCollectionViewCell.self, forCellWithReuseIdentifier: PhoneNumberCollectionViewCell.name)
collectionView.delegate = self
collectionView.dataSource = self
return collectionView

Related

Why "secim" returns nil to other ViewController in this project

I am trying to make a app that when you click the button on collectionview cell, you can show the detail viewcontroller and gives more information about your choice.But all attributes of my struct shown as nil on other viewController.
Here is my ViewController including CollectionView
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
var ilceDizisi = [Ilceler]()
var secim : Ilceler?
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white, NSAttributedString.Key.font:UIFont(name: "Papyrus", size: 25.0)]
navigationController?.navigationBar.titleTextAttributes = textAttributes as [NSAttributedString.Key : Any]
//Hucre Boyurlandirmasi
let tasarim = UICollectionViewFlowLayout()
let genislik = self.collectionView.frame.size.width
tasarim.minimumInteritemSpacing = 5
tasarim.minimumLineSpacing = 5
tasarim.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
tasarim.itemSize = CGSize(width: (genislik - 30) / 2, height: 322.0)
collectionView.collectionViewLayout = tasarim
ilceDizisi = [Ilceler.adalar, Ilceler.arnavutkoy, Ilceler.atasehir, Ilceler.avcilar, Ilceler.bagciler, Ilceler.bahcelievler, Ilceler.bakirkoy, Ilceler.basaksehir, Ilceler.bayrampasa, Ilceler.besiktas, Ilceler.beykoz, Ilceler.beylikduzu , Ilceler.beyoglu, Ilceler.buyukcekmece, Ilceler.catalca , Ilceler.cekmekoy , Ilceler.esenler , Ilceler.esenyurt , Ilceler.eyupsultan , Ilceler.fatih , Ilceler.gaziosmanpasa , Ilceler.gungoren , Ilceler.kadikoy , Ilceler.kagithane , Ilceler.kartal, Ilceler.kucukcekmece , Ilceler.maltepe , Ilceler.pendik , Ilceler.sancaktepe , Ilceler.sariyer , Ilceler.sile , Ilceler.sultanbeyli , Ilceler.sultangazi , Ilceler.sile, Ilceler.sisli , Ilceler.tuzla , Ilceler.umraniye , Ilceler.uskudar , Ilceler.zeytinburnu]
}
}
extension ViewController : UICollectionViewDelegate , UICollectionViewDataSource, CellButton {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return ilceDizisi.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "hucre", for: indexPath) as! Cell
cell.isimLabel.text = ilceDizisi[indexPath.row].isim!
cell.yakaLabel.text = ilceDizisi[indexPath.row].yaka!
cell.ilceGorsel.image = UIImage(named: ilceDizisi[indexPath.row].gorsel!)
cell.prot = self
cell.indexPath = indexPath
return cell
}
func detail(indexPath: IndexPath) {
performSegue(withIdentifier: "toDetailVC", sender: nil)
secim = ilceDizisi[indexPath.row]
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toDetailVC" {
let destVC = segue.destination as! DetailViewController
destVC.secilenIlce = secim
}
}
}
Here is my detail viewController
import UIKit
class DetailViewController: UIViewController {
#IBOutlet weak var baskan: UILabel!
#IBOutlet weak var yuz: UILabel!
#IBOutlet weak var nufus: UILabel!
#IBOutlet weak var yaka: UILabel!
#IBOutlet weak var gorselView: UIImageView!
#IBOutlet weak var ilceLabel: UILabel!
var secilenIlce : Ilceler?
override func viewDidLoad() {
super.viewDidLoad()
baskan.text = "Belediye Başkanı : \(secilenIlce?.belediyeBaskani)"
yuz.text = "Yüzölçümü(km²) : \(secilenIlce?.yuzolcumu)"
nufus.text = "Nüfus : \(secilenIlce?.nufus)"
yaka.text = "Bulunduğu Yaka : \(secilenIlce?.yaka)"
gorselView.image = UIImage(named: (secilenIlce?.gorsel)!)
ilceLabel.text = secilenIlce?.isim
}
}
I want to see informations like this
but when i runned the project
I tried to get all informations with prepareforsegue func.Actually i needed to delete imageview.image code due to the fact that it returns nil and my project crashs if i unwrapped it.What is my fault and how can i fix it
You need to set secim before performing the segue. Swap the two lines in your detail function.
func detail(indexPath: IndexPath) {
secim = ilceDizisi[indexPath.row]
performSegue(withIdentifier: "toDetailVC", sender: nil)
}
With your original code you were calling performSeque which results in the call to prepare. But at that point you haven't set secim. So you were setting secim after prepare was being called. Most likely, if you tapped on a cell again, it would then show the data of the previously tapped cell.

Updating Realm through a custom button on the UITableViewCell

I have a tableview of "Books" that are stored in Realm. I want to set the "CurrentBook" property to "True" when hitting a button on a custom UITableViewCell.
I believe my error has something to do with getting the correct book value in "func selectCurrentBook", when I use an optional like below nothing happens.
#objc func selectCurrentBook(sender: UIButton) {
try! realm.write {
book?.currentlyReading = true
}
}
When I don't use an optional for book and use book.currentlyReading = true I get the error "Unexpectedly found nil while implicitly unwrapping an Optional value:"
Am I incorrectly passing the book value somewhere? I can't seem to find out how. Maybe I'm delegating wrong?
My TableViewCell is:
import UIKit
import RealmSwift
protocol MyBooksDelegate {
func currentlyReadingButton()
}
class MyBooksTableViewCell: UITableViewCell {
let realm = try! Realm()
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var authorLabel: UILabel!
#IBOutlet weak var smallThumbnailImageView: UIImageView!
#IBOutlet weak var currentlyReadingButton: UIButton!
#IBAction func currentlyReadingButton(_ sender: Any) {
}
private var book: Book!
func loadImage(smallThumbnailURL: String) {
let imageURL = URL(string: smallThumbnailURL ?? "")
smallThumbnailImageView.sd_setImage(with: imageURL)
}
func configureCell(book: Book, delegate: MyBooksDelegate?) {
titleLabel.text = book.bookTitle
authorLabel.text = book.bookAuthor
loadImage(smallThumbnailURL: book.bookSmallThumbnailImageURL)
currentlyReadingButton.addTarget(self, action: #selector(selectCurrentBook(sender:)), for: .touchUpInside)
}
#objc func selectCurrentBook(sender: UIButton) {
try! realm.write {
book?.currentlyReading = true
}
}
}
My View Controller with TableView is :
import SwiftyJSON
import RealmSwift
class BooksViewController: UIViewController, UITextFieldDelegate, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var myBooksTableView: UITableView!
let realm = try! Realm()
var books: Results<Book>?
// Search Bar Properties
var searchParameter = "intitle"
var booksArray: [Book] = []
override func viewDidLoad() {
super.viewDidLoad()
loadBooks()
// Setting up the TableView
self.myBooksTableView.delegate = self
self.myBooksTableView.dataSource = self
self.myBooksTableView.rowHeight = 120.0
// Setup Title
title = "My Books"
// navigationController?.navigationBar.prefersLargeTitles = true
}
override func viewWillAppear(_ animated: Bool) {
navigationController?.navigationBar.barStyle = .black
loadBooks()
}
func loadBooks() {
books = realm.objects(Book.self).sorted(byKeyPath: "DateCreated", ascending: false)
myBooksTableView.reloadData()
}
// TABLEVIEW
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return books?.count ?? 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "MyBooksTableViewCell", for: indexPath) as? MyBooksTableViewCell {
cell.configureCell(book: (books?[indexPath.row])!, delegate: self as? MyBooksDelegate)
// cell.selectionStyle = UITableViewCell.SelectionStyle.none
return cell
} else {
return UITableViewCell()
}
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "myBooksTOMyBooksDetail", sender: self)
myBooksTableView.deselectRow(at: indexPath, animated: true)
}
And my Book Model is:
class Book: Object {
#objc dynamic var bookTitle: String!
#objc dynamic var bookAuthor: String!
#objc dynamic var bookSmallThumbnailImageURL: String!
#objc dynamic var bookThumbnailImageURL: String!
#objc dynamic var bookDescription: String!
#objc dynamic var bookISBN_13: String!
#objc dynamic var currentlyReading = false
#objc dynamic var DateCreated = Date()
#objc dynamic var WordID = UUID().uuidString
// words
let words = List<Word>()
override static func primaryKey() -> String? {
return "WordID"
}
}
The most compatible syntax is
currentlyReadingButton.addTarget(self, action: #selector(selectCurrentBook), for: .touchUpInside)
and
#objc func selectCurrentBook(_ sender: UIButton) {
However as the cell is custom anyway I'd prefer an IBAction over target/action
And the protocol MyBooksDelegate seems to be unused.
Side note:
Force unwrap the cell
let cell = tableView.dequeueReusableCell(withIdentifier: "MyBooksTableViewCell", for: indexPath) as! MyBooksTableViewCell
A crash – with report – reveals a design mistake which can be fixed instantly. With the if let you'll see nothing and have no clue why.
Update:
The crash occurs because you don't set book in the cell, add the first line after the {
func configureCell(book: Book, delegate: MyBooksDelegate?) {
self.book = book
titleLabel.text = book.bookTitle
...

How can I get user to input text in a text field inside a xib cell in swift

I have created a xib cell with two textfields. The textfield are connected as IBoutlets to the xib swift file. The xib cell is registered on the view controller as a Nib. When I run the app on a simulator I cannot get the keyboard to show up. Secondly, the textfields are not editable at all, that is the cursor doesn't even show. I would like to get help with this as I have tried using a label the same thing happens. I'm just not sure how to fix this one. I'm not getting errors on build. I have installed IQKeyboardManager from cocoapods. Thanks in advance.
Heres the code:
import UIKit
class DictionaryCell: UITableViewCell {
#IBOutlet weak var DictBubble: UIView!
#IBOutlet weak var DictSymbolTextfield: UITextField!
#IBOutlet weak var SymbolMeaningTextfield: UITextView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Here is the view controller code:
import UIKit
import Firebase
class PersonalDreamDictionaryViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var dictionaryTextfield: UITextField!
let db = Firestore.firestore()
var dreamDictionary: [DreamDictionary] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
tableView.dataSource = self
tableView.delegate = self
title = K.Dictionary.appName
tableView.register(UINib(nibName: K.Dictionary.cellNibName, bundle: nil), forCellReuseIdentifier:
K.Dictionary.cellIdentifier)
loadDictionaryList()
}
func loadDictionaryList() {
db.collection(K.Fstore1.collectionName)
.order(by: K.Fstore1.date)
.addSnapshotListener{ (QuerySnapshot, error) in
self.dreamDictionary = []
if let e = error {
print("There was an issue retrieving data from Firestore. \(e)")
} else {
if let snapshotDocuments = QuerySnapshot?.documents {
for doc in snapshotDocuments {
//print(doc.data())
let data = doc.data()
if let symbolRetrieved = data[K.Fstore1.symbol] as? String, let meaningRetrieved = data[K.Fstore1.meaning] as? String {
let newDictList = DreamDictionary(mysymbol: symbolRetrieved, mymeaning: meaningRetrieved)
self.dreamDictionary.append(newDictList)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
}
}
}
}
extension PersonalDreamDictionaryViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dreamDictionary.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let dictCell = tableView.dequeueReusableCell(withIdentifier: K.Dictionary.cellIdentifier, for: indexPath)
as! DictionaryCell
if let symbolInput = dictCell.DictSymbolTextfield.text, let meaningInput = dictCell.SymbolMeaningTextfield.text { db.collection(K.Fstore1.collectionName).addDocument(data: [
K.Fstore1.symbol: symbolInput,
K.Fstore1.meaning: meaningInput,
K.Fstore1.date: Date().timeIntervalSince1970
]) { (error) in
if let e = error {
print("There was an issue saving data to firesore, \(e)")
} else {
print("Successfully saved data.")
}
}
}
return dictCell
}
}
extension PersonalDreamDictionaryViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, willBeginEditingRowAt indexPath: IndexPath) {
print(indexPath.row)
}
}

Found Nil Using Protocol in TableViewCell

I'm trying to update my model using data from a textField in a custom cell. I set up a protocol in the cell's class and send the info to my ViewController, however I continually get "Found nil while implicitly unwrapping an Optional value". What am I missing? Thanks!
protocol UpdateDelegate {
func didUpdate (someText: String)
}
class customTableViewCell: UITableViewCell {
var updateDelegate: UpdateDelegate!
#IBOutlet weak var someDescriptionField: UITextField!
#IBAction func someDescriptionField(_ sender: UITextField) {
updateDelegate.didUpdate(someText: sender.text ?? "") //error is here
}
}
extension ViewController : UpdateDelegate {
func didUpdate (someText: String) {
print(someText)
}
}
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate, UIGestureRecognizerDelegate {
//....
viewDidLoad() {
self.tableView.delegate = self
self.tableView.dataSource = self
}
}
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! TableViewCell
cell.someDescriptionField.text = meal.arrayOfPossibleDishes[indexPath.section].arrayOfSteps[indexPath.row-1].stepName
cell.layer.cornerRadius = 10
return cell
}
Very simple answer here: You are never setting the delegate on your cell. Since your ViewController class conforms to your UpdateDelegate protocol you can update your cellForRow method like so:
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! TableViewCell
//set the delegate when setting up the cell
cell.updateDelegate = self
cell.someDescriptionField.text = meal.arrayOfPossibleDishes[indexPath.section].arrayOfSteps[indexPath.row-1].stepName
cell.layer.cornerRadius = 10
return cell

how to get check box functionality in tableview footer view

i have to use checkbox in tableview footer, on check i have to display textfield.
in my ViewController
#IBOutlet weak var resultTable: UITableView!
in FooterCell
import UIKit
class FooterCell: UITableViewCell
{
#IBOutlet weak var textFld: UITextField!
#IBOutlet weak var checkBtn: UIButton!
#IBOutlet weak var submitBtn: UIButton!
override func awakeFromNib()
{
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool)
{
super.setSelected(selected, animated: animated)
}
}
this is my code in class
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView?
{
let footer = resultTable.dequeueReusableCell(withIdentifier: "footer") as? FooterCell
footer!.textFld.isHidden = true
footer!.submitBtn.isHidden = true
footer!.checkBtn.addTarget(self, action: #selector(checkBoxSelection(_:)), for: .touchUpInside)
return footer?.contentView
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat
{
return 55
}
#objc func checkBoxSelection(_ sender:UIButton)
{
let footer = resultTable.dequeueReusableCell(withIdentifier: "footer") as? FooterCell
if askBool
{
footer!.checkBtn.setImage(UIImage(named:"CheckBox"), for: .normal)
footer!.textFld.isHidden = false
footer!.submitBtn.isHidden = false
askBool = false
} else
{
footer!.checkBtn.setImage(UIImage(named:"UnCheckBox"), for: .normal)
footer!.textFld.isHidden = true
footer!.submitBtn.isHidden = true
askBool = true
}
}
If you would like to add functionality to your footerView you need to use protocol to detect user has checked or unchecked button.
1) First, create a protocol
protocol FooterCheckable: class {
func isChecked()
}
2) Then inside your footer view class do following inside checkbox action.
class TableViewFooter: UITableViewHeaderFooterView {
weak var delegate: FooterCheckable?
#IBAction func approveOrRejectBtnPressed(_ sender: UIButton) {
delegate?.isChecked()
}
}
3) Inside your view controller you need to register your tableViewFooter with following code
tableView.register(UINib(nibName: yourFooterViewString, bundle: nil),
forHeaderFooterViewReuseIdentifier: identifier)
4) Then, again inside your View Controller class you need to have following code to use header and give delegate to your view controller
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
if let footer = tableView.dequeueReusableHeaderFooterView(withIdentifier: identifier) as? TableViewFooter {
footer.delegate = self
return footer
}
return nil
}
5) Lastly, you need to be sure you need to conform your protocol inside the view controller and do whatever you need when header button checked.
extension ViewController: FooterCheckable {
func isChecked() {
}
}
That is happy ending hope it will be useful for you.