Get UITextField Data from Separate .XIB - Swift/XCode - ios

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

Related

ios swift tableview not showing custom cells

I am trying to create a table view with custom cells from Storyboard layout in an iOS app.
But for some reason the table cells are not being shown. When I tried to set debug breakpoints I found that the debugger is reaching this function
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
but it never reaches this function -
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
Here is my viewcontroller code -
extension NavigationViewController: UITableViewDataSource, UITableViewDelegate, SideMenuControllerDelegate {
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SideMenuTableItem", for: indexPath as IndexPath) as! SideMenuTableItem
cell.setItemData(items[indexPath.row])
return cell
}
public func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func setupTableViews() {
menuTable.register(SideMenuTableItem.self, forCellReuseIdentifier: "SideMenuTableItem")
}
}
class SideMenuTableItem: UITableViewCell {
#IBOutlet weak var menuImage: UIImageView!
#IBOutlet weak var menuLabel: UILabel!
var data: MenuItem?
override func awakeFromNib() {
super.awakeFromNib()
}
func setItemData(_ item: MenuItem) {
data = item
menuLabel.text = data?.title
if data?.icon_res != nil {
menuImage.image = UIImage(named: (data?.icon_res)!)
}
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
I have checked in the storyboard that I have set the reusable identifier to the table prototype cell and also connected the datasource and the delegate properties to the tableview
and I am calling the setupTableViews() method inside my viewDidLoad() function after creating the items array
But still I am not able to get the cells to appear in my view at all.
Can anyone suggest what am I missing here or what's wrong with my code, or how can I further debug this issue
import UIKit
import SideMenuSwift
class NavigationViewController: UIViewController {
#IBOutlet weak var navigationContainer: UIView!
#IBOutlet weak var emailButton: UIButton!
#IBOutlet weak var phoneButton: UIButton!
#IBOutlet weak var userAvatar: UIImageView!
#IBOutlet weak var userProfile: UIButton!
#IBOutlet weak var userName: UILabel!
#IBOutlet weak var menuTable: UITableView!
var service: AuthenticationService!
var cdc: CoreDataController!
var items: [MenuItem] = []
var currentUser: User?
override func viewDidLoad() {
super.viewDidLoad()
setupSidebar()
initSidebarData()
setupUserHeader()
setupTableViews()
}
func setupUserHeader() {
if currentUser != nil {
if currentUser?.name != nil {
userName.text = currentUser?.name
} else if currentUser?.role != nil {
userName.text = "urTutors " + (currentUser?.role ?? "")
}
if currentUser?.avatarUrl != nil {
userAvatar.downloaded(from: (currentUser?.avatarUrl)!)
}
}
}
func initSidebarData() {
service = AuthenticationServiceProvider()
cdc = CoreDataController()
items = cdc.getNavigationData()
currentUser = cdc.getUserData()
}
func setupSidebar() {
self.view.backgroundColor = UIColor.hexColor("#fff")
navigationContainer.backgroundColor = UIColor.hexColor("#2a2a2a")
SideMenuController.preferences.basic.statusBarBehavior = .hideOnMenu
SideMenuController.preferences.basic.position = .above
SideMenuController.preferences.basic.direction = .left
SideMenuController.preferences.basic.enablePanGesture = true
SideMenuController.preferences.basic.menuWidth = 275
sideMenuController?.delegate = self
}
static func createViewController() -> NavigationViewController {
let sb = UIStoryboard(name: "StudentHomeModuleStoryboard", bundle: nil)
let vc = sb.instantiateViewController(withIdentifier: "NavigationViewController")
return vc as! NavigationViewController
}
}
--UPDATE--
updated setupTableLayout function -
func setupTableViews() {
let bundle = Bundle(for: type(of: self))
let cellNib = UINib(nibName: "SideMenuTableItem", bundle: bundle)
menuTable.register(cellNib, forCellReuseIdentifier: "SideMenuTableItem")
menuTable.register(SideMenuTableItem.self, forCellReuseIdentifier: "SideMenuTableItem")
menuTable.reloadData()
}
After breaking into chat on this, we found that there were two issues.
The first issue was the missing reloadData call mentioned above. That was causing cellForRow to not be called. Adding reloadData corrected that issue, but then the custom cell class's outlets were nil, causing a crash in setItemData.
The second issue was that register(_:forCellReuseIdentifier:) was being called in code, but the custom cell was already setup as part of the Interface Builder UITableView declaration. Calling register again on the custom class re-registered the reuseIdentifier, disconnecting the outlets set up in the storyboard.
Removing the register call and adding reloadData solved all issues.
You are never calling setupTableViews(). You'r code should look like this:
class NavigationViewController: UIViewController, SideMenuControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
setupTableViews()
}
func setupTableViews() {
menuTable.reloadData()
}
}
extension NavigationViewController: UITableViewDataSource, UITableViewDelegate {
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SideMenuTableItem", for: indexPath as IndexPath) as! SideMenuTableItem
cell.setItemData(items[indexPath.row])
return cell
}
public func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
}
You are never calling the function, nor calling viewDidLoad. This should help. Also, where is the rest of your view controller code (is this all of it? It should not be!).
You don't need to register your cell because you requested it and make sure you reloadData().
Hope this helps!

Deleting a UITableView cell in a specific section

There is a task. Each cell contains a button by clicking which you want to delete this cell. The problem is that sections are used to delineate the entire list by category. The data I take from Realm DB. removal must occur under two conditions because the name is repeated, so you need to consider the name from the label and the name of the section. I will be very grateful for the sample code with comments.
import UIKit
import RealmSwift
class PurchesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var purchesTableView: UITableView!
let manage = ManagerData()
override func viewDidLoad() {
super.viewDidLoad()
purchesTableView.delegate = self
purchesTableView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
purchesTableView.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return manage.loadPurchases().0.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return manage.loadPurchases().0[section]
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return manage.loadPurchases().1[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "purchesCell", for: indexPath) as! CustomPurchesTableViewCell
cell.productLabel.text = manage.loadPurchases().1[indexPath.section][indexPath.row]
cell.weightProductLabel.text = manage.loadPurchases().2[indexPath.section][indexPath.row]
cell.weightNameLabel.text = manage.loadPurchases().3[indexPath.section][indexPath.row]
// cell.boughtButton.addTarget(self, action: #selector(removeProduct), for: .touchUpInside)
return cell
}
}
class CustomPurchesTableViewCell: UITableViewCell {
#IBOutlet weak var boughtButton: UIButton!
#IBOutlet weak var productLabel: UILabel!
#IBOutlet weak var weightProductLabel: UILabel!
#IBOutlet weak var weightNameLabel: UILabel!
#IBAction func removePurches(_ sender: Any) {
print("remove")
}
}
method for get data
func loadPurchases() -> ([String], Array<Array<String>>, Array<Array<String>>, Array<Array<String>>) {
var sections: [String] = []
var product = Array<Array<String>>()
var weight = Array<Array<String>>()
var nameWeight = Array<Array<String>>()
let realm = try! Realm()
let data = realm.objects(Purches.self)
for item in data {
if sections.contains(item.nameDish) == false {
sections.append(item.nameDish)
}
}
for a in sections {
var productArr = Array<String>()
var weightArr = Array<String>()
var nameWeightArr = Array<String>()
for prod in data {
if a == prod.nameDish {
productArr.append(prod.product)
weightArr.append(prod.weight)
nameWeightArr.append(prod.nameWeigh)
}
}
product.append(productArr)
weight.append(weightArr)
nameWeight.append(nameWeightArr)
}
return (sections, product, weight, nameWeight)
}
Index path you will get in cell class
Index path have two property section and row for table view
Now you can create on more method in Controller class and assign to a variable to every cell or you can use editAction provided by table view for delete
in order to get number section and row you need create IBOutlet in custom cell and on ViewController class is created addTarget for your button.
Example code at the bottom.
import UIKit
import RealmSwift
class PurchesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var purchesTableView: UITableView!
let manage = ManagerData()
//... more code ...
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "purchesCell", for: indexPath) as! CustomPurchesTableViewCell
cell.productLabel.text = manage.loadPurchases().1[indexPath.section][indexPath.row]
cell.weightProductLabel.text = manage.loadPurchases().2[indexPath.section][indexPath.row]
cell.weightNameLabel.text = manage.loadPurchases().3[indexPath.section][indexPath.row]
cell.boughtButton.addTarget(self, action: #selector(removePurches(_:)), for: .touchUpInside)
return cell
}
#objc func removePurches(_ sender: UIButton) {
let position: CGPoint = sender.convert(CGPoint.zero, to: purchesTableView)
let indexPath: IndexPath! = self.purchesTableView.indexPathForRow(at: position)
print("indexPath.row is = \(indexPath.row) && indexPath.section is = \(indexPath.section)")
purchesTableView.deleteRows(at: [indexPath], with: .fade)
}
}
and custom class CustomPurchesTableViewCell for cell
class CustomPurchesTableViewCell: UITableViewCell {
#IBOutlet weak var boughtButton: UIButton! // you button for press
#IBOutlet weak var productLabel: UILabel!
#IBOutlet weak var weightProductLabel: UILabel!
#IBOutlet weak var weightNameLabel: UILabel!
}

Initialization of UITableViewCell Fails (Swift)

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.

Update label.text at runtime

I try to make a label.text value get updated in a row of a table. The update is supposed to be triggered by a user entering a number in another textfield in this row:
My code looks like this:
Swift 3: ViewController
import UIKit
class RiskPlan: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var probability1 = String()
var impact1 = String()
var riskFactor = String()
var result = Int()
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView!.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CellCustomized
impact1 = (cell.impact?.text)!
probability1 = (cell.probability?.text)!
result = Int(impact1)! * Int(probability1)!
cell.riskFactor?.text = String(result)
self.tableView.reloadRows(at: [indexPath], with: UITableViewRowAnimation.top)
return cell
}
}
Swift 3: CellCustomized
import UIKit
class CellCustomized: UITableViewCell {
#IBOutlet weak var probability: UITextField!
#IBOutlet weak var impact: UITextField!
#IBOutlet weak var riskFactor: UILabel!
}
My problem is that
self.tableView.reloadRows(at: [indexPath], with:
UITableViewRowAnimation.top) does not do the update and
I get a "fatal error: unexpectedly found nil while unwrapping an Optional
value" for result = Int(impact1)! * Int(probability1)!
If you want to know when changes are done in the textFields, func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell is not the place you want to put your calculation code.
Instead you should listen to the event .editingChanged on text fields from your CellCustomized class.
class RiskPlan: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView!.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CellCustomized
return cell
}
}
class CellCustomized: UITableViewCell {
#IBOutlet weak var probability: UITextField!
#IBOutlet weak var impact: UITextField!
#IBOutlet weak var riskFactor: UILabel!
var probability1 = 0
var impact1 = 0
override func awakeFromNib() {
super.awakeFromNib()
probability.addTarget(self, action: #selector(textOnTextFieldDidChange(textField:)), for: .editingChanged)
impact.addTarget(self, action: #selector(textOnTextFieldDidChange(textField:)), for: .editingChanged)
}
func textOnTextFieldDidChange(textField: UITextField) {
if textField === probability {
probability1 = Int(textField.text!) ?? 0
} else if textField === impact {
impact1 = Int(textField.text!) ?? 0
}
riskFactor.text = String(probability1 * impact1)
}
}
If I'm not mistaking, you want to update the label's text depending on what you are inserting in the textfield(s).
In your CellCustomized you can do this:
class CellCustomized: UITableViewCell {
// assuming that the outlets has been renamed...
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var label: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
textField.addTarget(self, action: #selector(editing(sender:)), for: .editingChanged)
}
func editing(sender: UITextField) {
// checking if the input is convertible to a number, and then add 5 for it (you can do your own operations)
if let string = sender.text, Int(sender.text!) != nil {
let int = Int(string)
label.text = "\(int! + 5)"
}
}
}
hope that helped.

Update model through UIButton within a UITableViewCell

In MainVC.swift I'm capturing the tag of my custom "PlayerCell". I want to press theincreaseBtn (UIButton) which will increment the playerLbl.text (UILabel) by one but also update my model (PlayerStore.player.playerScore: Int)
Main.swift:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "PlayerCell", for: indexPath) as? PlayerCell {
let player = players[indexPath.row]
cell.updateUI(player: player)
cell.increaseBtn.tag = indexPath.row
cell.decreaseBtn.tag = indexPath.row
return cell
} else {
return UITableViewCell()
}
}
PlayerCell.swift
class PlayerCell: UITableViewCell {
#IBOutlet weak var playerLbl: UILabel!
#IBOutlet weak var increaseBtn: UIButton!
#IBOutlet weak var decreaseBtn: UIButton!
#IBOutlet weak var scoreLbl: UILabel!
#IBOutlet weak var cellContentView: UIView!
func updateUI(player: Player){
playerLbl.text = player.playerName
scoreLbl.text = "\(player.playerScore)"
cellContentView.backgroundColor = player.playerColor.color
}
#IBAction func increaseBtnPressed(_ sender: AnyObject) {
let tag = sender.tag
// TODO: send this tag back to MainVC?
}
I would use the delegate pattern in this case. Create a protocol that Main.swift implements, and that PlayerCell.swift uses as an optional property. So for example:
protocol PlayerIncrementor {
func increment(by: Int)
func decrement(by: Int)
}
Then use an extension on Main.swift to implement this protocol
extension Main: PlayerIncrementor {
func increment(by: int) {
//not 100% what you wanted to do with the value here, but this is where you would do something - in this case incrementing what was identified as your model
PlayerStore.player.playerScore += by
}
}
Inside of PlayerCell.swift, add a delegate property and call the delegate increment method in your #IBAction
class PlayerCell: UITableViewCell {
var delegate: PlayerIncrementor?
#IBOutlet weak var increaseBtn: UIButton!
#IBAction func increaseBtnPressed(_ sender: AnyObject) {
let tag = sender.tag
//call the delegate method with the amount you want to increment
delegate?.increment(by: tag)
}
Lastly - to make it all work, assign Main as the delegate to the PlayerCell UITableViewCell.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "PlayerCell", for: indexPath) as? PlayerCell {
//self, in this case is Main - which now implements PlayerIncrementor
cell.delegate = self
//etc

Resources