Initialization of UITableViewCell Fails (Swift) - ios

I'm using a tableView and try to create my cells.. in vain.
I set all the things we have to do (created a prototype cell, gave an identifier ("CustomerCell"), set the delegate and dataSource in the storyBoard and inside the ViewDidLoad both, set the good class in the StoryBoard for the tableView, the cells, etc).
Here's my code:
override func viewDidLoad() {
self.tableView.delegate = self
self.tableView.dataSource = self
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = items[indexPath.section]
switch item.type {
case .customer:
if let cell = tableView.dequeueReusableCell(withIdentifier: "CustomerCell", for: indexPath) as? CustomerCellSetter {
cell.item = item as? Customer // THIS is never called, the cell return nil all the time
return cell
}
return UITableViewCell()
}
Is there an other parameter in the equation to get my cell?
Thanks in advance for your precious help !
EDIT:
here's my UITableViewCell classes:
class CustomerCellSetter: CustomerTableViewCell {
var item: Customer? {
didSet {
guard let item = item else {
return }
if let firstName = item.firstName {
fisrtName?.text = firstName
}
if let theLastName = item.lastName {
lastName.text = theLastName
}
if let theGsm = item.GSM {
gsm.text = theGsm
}
if let theMail = item.mail {
mail.text = theMail
}
if let theAdress = item.adress {
adress.text = theAdress
}
if let theNote = item.notes {
notes.text = theNote
}
}
}
}
class CustomerTableViewCell: UITableViewCell {
#IBOutlet var fisrtName : UILabel!
#IBOutlet var lastName : UILabel!
#IBOutlet var gsm : UILabel!
#IBOutlet var mail : UILabel!
#IBOutlet var adress : UILabel!
#IBOutlet var notes : UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
EDIT 2:

did you set the class for the cell as "CustomerCellSetter"?
This can be done in the identity inspector.

Did you have add "UITableViewDelegate, UITableViewDataSource" at class name ?
class ClassName: UITableViewDelegate, UITableViewDataSource {
}
and also you need to add more method of tableview
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1 // number of your rows
}

I needed to set CustomCellSetter, not CustomerTableViewCell in identity inspector. Thanks #OOPer for this simple answer.

Related

UITableViewCell data not showing up in UITableViewController

I am having trouble debugging why my UITableview cell data isn't showing up in the UITableview. The UITableview currently displays blank when the user navigates to it. Data is correctly going into the cellForRowAt and into the function that sets the cell data.
Setting the cell data
class EventInboxTableViewCell: UITableViewCell {
#IBOutlet weak var eventNameLabel: UILabel!
#IBOutlet weak var eventCoverImageView: UIImageView!
#IBOutlet weak var eventStartLabel: UILabel!
#IBOutlet weak var eventEndLabel: UILabel!
var eventStartString = String()
var eventEndString = String()
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
}
func setEvent(_ event:Event) {
eventNameLabel?.text = event.eventName
if event.eventStart != nil {
let eventStartTS = event.eventStart
let eventStartDate = eventStartTS?.dateValue()
self.eventStartString = AppWideService.dateToStringShort(date: eventStartDate!)
}
if event.eventEnd != nil {
let eventEndTS = event.eventEnd
let eventEndDate = eventEndTS?.dateValue()
self.eventEndString = AppWideService.dateToStringShort(date: eventEndDate!)
}
print("Event inbox event \(eventStartString)")
print("Event inbox event \(eventEndString)")
eventStartLabel?.text = self.eventStartString
eventEndLabel?.text = self.eventEndString
guard let urlString = event.eventCoverUrl as? String else { return }
let url = URL(string: urlString)
guard url != nil else {
//Couldn't create url object
return
}
eventCoverImageView?.sd_setImage(with: url) { (image, error, cacheType, url) in
self.eventCoverImageView?.image = image
}}}
For some reason when I remove the ? from setting the label text it says the values like eventName or eventStartString etc are nil, but I have print statements that ensure they are not.
UITableView Datasource
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return retrievedEvents.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "EventInboxTableViewCell", for: indexPath) as! EventInboxTableViewCell
let event = retrievedEvents[indexPath.row]
cell.setEvent(event)
return cell
}
Registered the cell in viewDidLoad
tableView.register(EventInboxTableViewCell.self, forCellReuseIdentifier: "EventInboxTableViewCell")
The problem is the way the table view controller was being used.
If you design a View Controller (of any type) in Storyboard, and you want to use it, you cannot simply say:
let vc = EventInboxTableViewController()
you have to instantiate it from the storyboard:
if let vc = storyboard?.instantiateViewController(withIdentifier: "EventInboxTableViewController") as? EventInboxTableViewController {
navigationController?.pushViewControllerFromLeft(controller: vc)
}
So, in Storyboard, assign your custom class to your UITableViewController, and make sure to fill in the Storyboard ID field (with the string you are using in code as the Identifier).

change variables from an other view controller when a textField is changed

I'm trying to change the values of a variable in two different view controllers from the value of a textField but I don't understand how to use the delegate so that it works.
My Storyboard:
My Code:
MainView:
class GameCreatingViewController: UIViewController {
var newGame = Game()
override func viewDidLoad() {
super.viewDidLoad()
newGame = Game()
newGame.playerBook.NumberOfPlayers = 2
if let vc = self.children.first(where: { $0 is PlayersTableViewController }) as? PlayersTableViewController {
vc.currentGame = self.newGame
vc.tableView.reloadData()
}
if let vc = self.children.first(where: { $0 is GameViewController }) as? GameViewController {
vc.currentGame = self.newGame
}
}
func changeName(name: String, number: Int) {
self.newGame.playerBook.players[number].name = name
}
}
tableViewController:
class PlayersTableViewController: UITableViewController, UITextFieldDelegate {
var currentGame = Game()
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "playerCell", for: indexPath) as? PlayerNameTableViewCell else {fatalError("Wrong type of cell")}
// Configure the cell...
cell.playerName.delegate = self
let row = indexPath[1]+1
cell.numberOfPlayer = row
return cell
}
func changeName(name: String, number: Int) {
self.currentGame.playerBook.players[number].name = name
}
}
The Cell:
protocol changeNameDelegate: class {
func changeName(name: String, number: Int)
}
class PlayerNameTableViewCell: UITableViewCell, UITextFieldDelegate {
weak var delegate: changeNameDelegate? = nil
#IBOutlet weak var playerName: UITextField!
var numberOfPlayer: Int = Int()
#IBAction func changeName(_ sender: UITextField) {
delegate?.changeName(name: sender.text!, number: numberOfPlayer)
}
}
It seems like the action from the button executes but the fonctions from the other viewcontrollers don't.
Use the delegate to notify the other viewController.
Make sure isn't nil.
Usually protocols name the first letter is capitalized.
A good practice is to implement protocols in extensions.
Implement the changeNameDelegate protocol.
class PlayersTableViewController: UITableViewController, UITextFieldDelegate, changeNameDelegate {
And in the cell configuration set the delegate.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "playerCell", for: indexPath) as? PlayerNameTableViewCell else {fatalError("Wrong type of cell")}
// Configure the cell...
cell.playerName.delegate = self
cell.delegate = self // This line is missing.
let row = indexPath[1]+1
cell.numberOfPlayer = row
return cell
}

How can I reload the following viewcontroller with a tableview?

I have a tableview inside a viewcontroller. When pressing a button in the navigation bar I would like the table view to reload.
The view controller is called FirstViewController, the Tableview is called listTableView and the refresh button is called refreshButton
In the code below I have linked the refreshButton action but cannot seem to figure out which function to put inside it to trigger the refresh when pressed.
The following is my code:
import UIKit
class FirstViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, FeedModelProtocol {
var feedItems: NSArray = NSArray()
var selectedStock : StockModel = StockModel()
let tableView = UITableView()
#IBOutlet weak var listTableView: UITableView!
#IBOutlet weak var refreshButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
//set delegates and initialize FeedModel
self.listTableView.delegate = self
self.listTableView.dataSource = self
let feedModel = FeedModel()
feedModel.delegate = self
feedModel.downloadItems()
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
#IBAction func reloadData(_ sender: Any) {
}
func itemsDownloaded(items: NSArray) {
feedItems = items
self.listTableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of feed items
return feedItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Retrieve cell
let cellIdentifier: String = "stockCell"
let myCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
myCell.textLabel?.textAlignment = .center
// Get the stock to be shown
let item: StockModel = feedItems[indexPath.row] as! StockModel
// Configure our cell title made up of name and price
let titleStr = [item.customer].compactMap { $0 }.joined(separator: "-")
print(titleStr)
// Get references to labels of cell
myCell.textLabel!.text = titleStr
return myCell
}
}
Use yourUITableView.reloadData() for this. Check Apple's Developer page for more info.

Get UITextField Data from Separate .XIB - Swift/XCode

Here are the binaries:
FieldStyle1.swift:
import UIKit
protocol FieldStyle1Delegate {
func textChange(text: String, tag: NSInteger)
}
class FieldStyle1: UITableViewCell, UITextFieldDelegate {
var delegate: FieldStyle1Delegate?
#IBOutlet var fullnameField: UITextField!
#IBOutlet var usernameField: UITextField!
#IBOutlet var emailField: UITextField!
#IBOutlet var passwordField: UITextField!
#IBOutlet var confirmPasswordField: UITextField!
override func awakeFromNib() {
fullnameField.delegate = self
usernameField.delegate = self
emailField.delegate = self
passwordField.delegate = self
confirmPasswordField.delegate = self
fullnameField.tag = 0
usernameField.tag = 1
emailField.tag = 2
passwordField.tag = 3
confirmPasswordField.tag = 4
}
func textFieldDidEndEditing(_ textField: UITextField) {
delegate?.textChange(text: textField.text!, tag: textField.tag)
}
}
I need to pull the #IBOutlet var passwordField: UITextField! and #IBOutlet var fullnameField: UITextField! from FieldStyle1.swift, into the ViewController.swift create a user function, where
user.username = usernameField.text, but im getting the "use of unresolved identifier "usernameField" error.
Ive tried multiple methods found on stackoverflow but all have been unsuccessful. Please help!
You can't access directly from textFields from table cell So you need to implement protocols as you implemented:
First of all, you need to give FieldStyle1Delegate from the controller to table cell like this way:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier:"identifier") as? FieldStyle1 else {
fatalError("Nil")
}
cell.delegate = self
return cell
}
Then you need to handle it in view controller and get data from table cell:
class ViewController: UIViewController, FieldStyle1Delegate {
func textChange(text: String, tag: NSInteger) {
if tag == 0 {
user.fullname = text
} else if tag == 1 {
user.username = text
} else if tag == 2 {
user.email = text
} else if tag == 3 {
user.password = text
}
}
}
1) When configuring a cell you must assign object that conforms to your FieldStyle1Delegate delegate. If you are configuring cell in you view controller you can assign self:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = ...
cell.delegate = self
return cell
}
2) You must conform to that delegate:
class ViewController: FieldStyle1Delegate, UITableViewDataSource {
func textChange(text: String, tag: NSInteger) {
// now you have those values
}
}
Full example:
class ViewController: FieldStyle1Delegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return ...
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = ...
cell.delegate = self
return cell
}
func textChange(text: String, tag: NSInteger) {
// now you have those values
}
}
Hold reference of FieldStyle1 class in viewController.swift
Class ViewController: UIViewController {
var xibView: FieldStyle1?
func loadNib() {
self.xibView = //..load nib
}
func accessOutlets() {
User.name = xibView?.textField.text
}
}

NSManagedObject Array becomes nil when UITableView is scrolled

I have a ViewController in my app where I have to show Settings to the user and user can turn the Settings on or off using UISwitch. I have to store the settings in the local db and based on that display data to user in app.
I am using SugarRecord for Core Data Management. Initially all the settings are turned on.
SugarRecordManager.swift
import Foundation
import SugarRecord
import CoreData
class SugarRecordManager
{
static let sharedInstance = SugarRecordManager()
private init(){
}
// Initializing CoreDataDefaultStorage
func coreDataStorage() -> CoreDataDefaultStorage {
let store = CoreDataStore.named("db")
let bundle = Bundle(for: type(of: self))
let model = CoreDataObjectModel.merged([bundle])
let defaultStorage = try! CoreDataDefaultStorage(store: store, model: model)
return defaultStorage
}
//MARK:- User Settings methods
//update local settings
func updateSettingsModel(userSettings: [UserSetting]){
let db = self.coreDataStorage()
for localSetting in userSettings{
try! db.operation { (context, save) -> Void in
if let settingObjectToUpdate = try! context.request(UserSetting.self).filtered(with: "groupName", equalTo: localSetting.groupName!).fetch().first{
settingObjectToUpdate.groupId = localSetting.groupId! as String
settingObjectToUpdate.groupName = localSetting.groupName! as String
settingObjectToUpdate.isGroupActive = localSetting.isGroupActive
try! context.insert(settingObjectToUpdate)
save()
}
}
}
}
//retrieve settings from storage
func getAllSettings() -> [UserSetting] {
let db = self.coreDataStorage()
var userSettings : [UserSetting]
do {
userSettings = try db.fetch(FetchRequest<UserSetting>())
} catch {
userSettings = []
}
return userSettings
}
//initialise settings for the first time
func initialiseUserSettings(){
let db = self.coreDataStorage()
var groupNameArray = UserDefaults.standard.value(forKey: "groupNamesArrayKey") as? [String]
var groupIdArray = UserDefaults.standard.value(forKey: "groupIdsArrayKey") as? [String]
for i in 0 ..< groupIdArray!.count {
try! db.operation { (context, save) -> Void in
let settingObject: UserSetting = try! context.new()
settingObject.groupId = groupIdArray?[i];
settingObject.groupName = groupNameArray?[i];
settingObject.isGroupActive = true;
try! context.insert(settingObject)
save()
}
}
}
}
SettingsViewController.swift
class SettingsViewController: BaseViewController, UITableViewDataSource, UITableViewDelegate, SettingsCellDelegate {
#IBOutlet weak var btnSideNav: UIBarButtonItem!
#IBOutlet weak var settingsTable: UITableView!
var userSetting = [UserSetting]() //array to hold settings from storage
override func viewDidLoad() {
super.viewDidLoad()
self.automaticallyAdjustsScrollViewInsets = false;
btnSideNav.target = revealViewController();
btnSideNav.action = #selector(SWRevealViewController.revealToggle(_:));
userSetting = SugarRecordManager.sharedInstance.getAllSettings() //here userSetting contains data and I have checked it
self.settingsTable.reloadData()
self.settingsTable.dataSource = self;
self.settingsTable.delegate = self;
// Do any additional setup after loading the view.
}
//MARK:- Table View Methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("Count of cells = \(self.userSetting.count)") //prints 18 which is good
return self.userSetting.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 60;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let settingsCell : SettingsCell? = tableView.dequeueReusableCell(withIdentifier: "SettingsCell") as? SettingsCell;
settingsCell?.setUpWithModel(model: self.userSetting[indexPath.row], cell: settingsCell!)
settingsCell?.delegate = self as SettingsCellDelegate;
return settingsCell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
func didTappedSwitch(cell: SettingsCell) {
let indexPath = settingsTable.indexPath(for: cell);
userSetting[(indexPath?.row)!].isGroupActive? = cell.isGroupActive.isOn as NSNumber
}
#IBAction func btnSaveTapped(_ sender: UIButton) {
// code to save settings
}
}
SettingsCell.swift
protocol SettingsCellDelegate {
func didTappedSwitch(cell: SettingsCell)
}
class SettingsCell: UITableViewCell {
#IBOutlet weak var groupName: UILabel!
#IBOutlet weak var lblGroupId: UILabel!
#IBOutlet weak var isGroupActive: UISwitch!
var delegate: SettingsCellDelegate!
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
}
func setUpWithModel(model: UserSetting, cell: SettingsCell)
{
cell.groupName.text = model.groupName;
cell.lblGroupId.text = model.groupId;
isGroupActive.setOn((model.isGroupActive?.boolValue)!, animated: false)
}
#IBAction func isGroupActiveValueChanged(_ sender: UISwitch) {
delegate.didTappedSwitch(cell: self)
}
}
Now, initally the TableView is populated and all arrays are working fine but as soon as I scroll the TableView all data is gone. Even the userSetting array is nill. I know it's something to do with context but can't figure out what. Any help would be greatly appreciated.
Change your func coreDataStorage() -> CoreDataDefaultStorage like this
// Initializing CoreDataDefaultStorage
lazy var coreDataStorage: CoreDataDefaultStorage = {
let store = CoreDataStore.named("db")
let bundle = Bundle(for: type(of: self))
let model = CoreDataObjectModel.merged([bundle])
let defaultStorage = try! CoreDataDefaultStorage(store: store, model: model)
return defaultStorage
}()
you have this problem because you re-init CoreDataDefaultStorage each time when you do any request.
After you made it lazy - you will have only one CoreDataDefaultStorage for all app life
Basically, it will be good to make coreDataStorage as singleton

Resources