How to interact with custom UITableViewCell class? - ios

i created table view with custom UITableViewCell class. So my app should set UIStepper hidden when button in navigation bar is tapped. Than it should update count Label by Stepper's value
My app's mission to see count and list of medicines at your home. So update each medical's count when stepper at that row is tapped.
import UIKit
class MedicinesListPage: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
struct Medicine {
var name: String
var count: Int
}
var medicines = [
Medicine(name: "Парацетамол", count: 1),
Medicine(name: "Ибуфен", count: 2),
Medicine(name: "Цитрамон", count: 1),
Medicine(name: "Смекта", count: 1),
Medicine(name: "Мезин", count: 1),
Medicine(name: "Терафлю", count: 1),
Medicine(name: "Спирт", count: 1),
Medicine(name: "Бинт", count: 1),
Medicine(name: "Мукалтин", count: 1),
Medicine(name: "Стрепсилс", count: 1)
]
override func viewDidLoad() {
super.viewDidLoad()
let rightButton = UIBarButtonItem(title: "Add", style: UIBarButtonItem.Style.plain, target: self, action:(#selector(showEditing)))
navigationItem.rightBarButtonItem = rightButton
tableView.delegate = self
tableView.dataSource = self
}
#objc func showEditing(){
// should unhide stepper
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 65
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return medicines.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "medicalTableViewCell") as? medicalTableViewCell
cell?.configureCell(name: medicines[indexPath.row].name, count: medicines[indexPath.row].count)
return cell!
}
}
UITableViewCell class
import UIKit
final class medicalTableViewCell: UITableViewCell{
#IBOutlet weak var medicalName: UILabel!
#IBOutlet weak var medicalCount: UILabel!
#IBOutlet weak var stepper: UIStepper!
override func awakeFromNib() {
super.awakeFromNib()
}
#IBAction func stepperTapped(_ sender: UIStepper) {
// should update count of medicals
}
public func configureCell(name: String, count: Int){
medicalName.text = name
medicalCount.text = String(count)
}
}

As I understood, you have a TableViewController with customs UITableView cells.
Each cell contains medicine name, its number and the UIStepper (that allows to change the number of that medicine).
You goal is to update medicalCountLabel every time the UIStepper is tapped.
(from the conversation in the comments, this is what I implemented)
Custom UITableViewCell Class
import UIKit
class TableViewCell: UITableViewCell {
//MARK: - Properties
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var countLabel: UILabel!
#IBOutlet weak var stepper: UIStepper!
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
}
//MARK: - Actions
#IBAction func stepperTapped(_ sender: UIStepper) {
countLabel.text = Int(sender.value).description
} //this is where you update `medicalCountLabel`
}
TableViewController Class:
import UIKit
class TableViewController: UITableViewController {
//MARK: - Properties
var medicines = [Medicine(name: "Парацетамол", count: 4), Medicine(name: "Ибуфен", count: 7),Medicine(name: "Цитрамон", count: 4),Medicine(name: "Смекта", count: 9), Medicine(name: "Мезин", count: 2)]
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return medicines.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? TableViewCell else{
fatalError("The dequeued cell is not an instance of TableViewCell.")
}
// Configure the cell
let medicine = medicines[indexPath.row]
cell.nameLabel.text = medicine.name
cell.countLabel.text = String(medicine.count)
cell.stepper.value = Double(medicine.count)
return cell
}
}
Medicine Class (in your case you just did it using struct which is also fine; but I created a separate swift file):
import UIKit
class Medicine{
var name: String
var count: Int
init(name: String, count: Int){
self.name = name
self.count = count
}
}

var medicalNames: [String] = ["item 1", "item 2", "item 3"]
var medicalCount: [Int] = [5,2,7]
This is bad design - make this an array of structs instead:
struct Medicine {
let name: String
var count: Int
}
let medicines = [
Medicine(name: "foo", count: 1), Medicine(name: "bar", count: 2)
]
Once you've done that, you can easily add a Bool flag like stepperHidden which controls whether the stepper is visible in your configureCell method. If you want all steppers to be hidden, set all flags to true and call tableView.reloadData()
Your tableView method would look like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "medicalTableViewCell") as! medicalTableViewCell
let medicine = medicines[indexPath.row]
cell?.configureCell(with: medicine)
return cell
}
and correspondingly,
public func configureCell(_ medicine: Medicine){
medicalName.text = medicine.name
medicalCount.text = "\(medicine.count)"
// stepper.isHidden = medicine.stepperHidden
}

Related

How to pass stepper value to ViewController?

I have a custom cell that has 2 labels, myLabel and numLabel, and a stepper. I have my custom cell in a Swift file and XIB file. I want when I click + or - button on the stepper, my numLabel change with the value of the stepper. I don't know how to pass the stepper value to the viewController where I have my tableView. Later want to save the stepper value to CoreDate how can I do that?. I'm just a beginner. Thank you for helping.
MyCell.swift
import UIKit
class MyCell: UITableViewCell {
static let identifier = "MyCell"
static func nib() -> UINib {
return UINib(nibName: "MyCell", bundle: nil)
}
public func configure(with name: String, number: String) {
myLabel.text = name
numLabel.text = number
}
#IBOutlet var myLabel: UILabel!
#IBOutlet var numLabel: UILabel!
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
}
}
ViewController.swift
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
table.register(MyCell.nib(), forCellReuseIdentifier: MyCell.identifier)
table.delegate = self
table.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: MyCell.identifier, for: indexPath) as! MyCell
cell.configure(with: "Item 1", number: "1")
return cell
}
}
My Screen Shot
You can do this easily with a "callback" closure:
class MyCell: UITableViewCell {
static let identifier: String = "MyCell"
#IBOutlet var myStepper: UIStepper!
#IBOutlet var numLabel: UILabel!
#IBOutlet var myLabel: UILabel!
// "callback" closure - set my controller in cellForRowAt
var callback: ((Int) -> ())?
public func configure(with name: String, number: String) {
myLabel.text = name
numLabel.text = number
}
#IBAction func stepperChanged(_ sender: UIStepper) {
let val = Int(sender.value)
numLabel.text = "\(val)"
// send value back to controller via closure
callback?(val)
}
static func nib() -> UINib {
return UINib(nibName: "MyCell", bundle: nil)
}
}
Then, in cellForRowAt:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: MyCell.identifier, for: indexPath) as! MyCell
cell.configure(with: "Item 1", number: "1")
// set the "callback' closure
cell.callback = { (val) in
print("Stepper in cell at \(indexPath) changed to: \(val)")
// do what you want when the stepper value was changed
// such as updating your data array
}
return cell
}
Use a delegate for a generic approach. This allows flexibility in how your cell interacts with the tableview, and enables type checking as you would expect from Swift.
Typically, for a UITableView, you would have an array of data that drives the content of the cells. In your case, let's assume that it's MyStruct (inside your view controller):
struct MyStruct {
let name: String
var value: Int
}
var myStructs: [ MyStruct ] = [
MyStruct( name: "Name 1", value: 1 ),
MyStruct( name: "Name 2", value: 2 ),
MyStruct( name: "Name 3", value: 3 ) ]
Create MyCellDelegate, and place in it whatever methods that you require to communicate changes from the cell to the view controller. For example:
protocol MyCellDelegate: class {
func didSet( value: Int, for myStructIndex: Int )
}
class MyCell: UITableViewCell {
weak var delegate: MyCellDelegate!
var myStructIndex: Int!
...
}
For your table view, assign the delegate when dequeuing the cell, and implement the protocol.
class ViewController: MyCellDelegate, UITableViewDelegate, UITableViewDataSource {
func tableView( _ tableView: UITableView, cellForRowAt indexPath: IndexPath ) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: MyCell.identifier, for: indexPath) as! MyCell
let myStruct = myStructs[indexPath.row] // You may want to ensure that you are in bounds
cell.delegate = self
cell.myStructIndex = indexPath.row
cell.configure( with: myStruct.name, number: myStruct.value )
return cell
}
func didSet( value: Int, for myStructIndex: Int ) {
// Now MyViewController sees the change.
myStructs[myStructIndex].value = value
}
}
Lastly, in your MyCell, whenever the value changes, for example in your stepper, invoke:
#IBAction func stepperChanged( _ sender: UIStepper ) {
let integerValue = Int( sender.value.round() )
numLabel.text = "\(integerValue)"
// Tell the view controller about the change: what happened, and to what cell.
self.delegate.didSet( value: integerValue, for: self.myStructIndex )
}

How to pass data on button clicked in cell to another tableview?

First: How would I be able to pass data from the ItemVC Cell to the CartVC on button Clicked (Add To Cart Button(ATC)) in the selected cell, since I am no trying to use didSelectRowAt to pass the data to the CartVC. but the ATC btn to pass the cells data to the CartVC
my Segue from the ItemVC to the CartVC is in the BarButtonItem(cartBtn) so I dont want to jump to the CartVc when pressing the ATC button but only pass selected items data to CartVC when the ATC is pressed
Second how would I be able to pass the increment/decrement value in lblQty to to the CartVC when the ATC is pressed as well to be able to present a more accurate Subtotal
import UIKit
import SDWebImage
import Firebase
class ItemCell: UITableViewCell {
weak var items: Items!
#IBOutlet weak var name: UILabel!
#IBOutlet weak var category: UILabel!
#IBOutlet weak var productImage: UIImageView!
#IBOutlet weak var weight: UILabel!
#IBOutlet weak var price: UILabel!
#IBOutlet weak var lblQty: UILabel!
#IBOutlet weak var addToCart: RoundButton!
#IBOutlet weak var plusBtn: RoundButton!
#IBOutlet weak var minusBtn: RoundButton!
func configure(withItems : Items) {
name.text = product.name
category.text = items.category
image.sd_setImage(with: URL(string: items.image))
price.text = items.price
weight.text = items.weight
}
}
import UIKit
import Firebase
import FirebaseFirestore
class ItemViewController: UITableViewController {
#IBOutlet weak var cartBtn: BarButtonItem!!
#IBOutlet weak var tableView: UITableView!
var itemSetup: [Items] = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
fetchItems { (items) in
self.itemSetup = items.sorted
self.tableView.reloadData()
}
}
func fetchItems(_ completion: #escaping ([Items]) -> Void) {
// -** FireStore Code **-
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as? CartViewController {
vc.items = self.itemSetup
}
}
#objc func plusItem(_ sender: UIButton) {
// -** increase Qty Code **-
}
//Function to decrement item count
#objc func minusItem(_ sender: UIButton) {
// -** decrease Qty Code **-
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return itemSetup.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "ItemCell") as? ItemCell else { return UITableViewCell() }
cell.configure(withItem: itemSetup[indexPath.row])
cell.lblQty.text = "\(self.imageSetup[indexPath.row].count)"
cell.plusBtn.tag = indexPath.row
cell.plusBtn.addTarget(self, action: #selector(self.plusItem(_:)), for: .touchUpInside)
cell.minusBtn.tag = indexPath.row
cell.minusBtn.addTarget(self, action: #selector(self.minusItem(_:)), for: .touchUpInside)
return cell
}
}
class CartViewController: UIViewController {
var items: Items!
#IBOutlet weak var cartTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
cartTableView.dataSource = self
cartTableView.delegate = self
}
}
extension CartViewController: UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Cart.currentCart.cartItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CartCell", for: indexPath) as! CartCell
let cart = Cart.currentCart.CartItems[indexPath.row]
cell.qty.text = "\(cart.qty)"
cell.lblMealName.text = "\(cart.items.category): \(cart.items.name)"
cell.lblSubTotal.text = "$\(cart.items.price1 * Float(cart.qty))"
cell.imageUrl // can't figure out how to pass image
return cell
}
}
import Foundation
class CartItem {
var items: Items
var qty: Int
init(items: Items, qty: Int) {
self.items = items
self.qty = qty
}
}
class Cart {
static let currentCart = Cart()
var cartItems = [CartItem]()
}
Just create a protocol to create a delegate and assign it to your cart class when the cell is initialized
protocol ItemCellDelegate {
func itemCell(didTapButton button: UIButton)
}
Then have a delegate property in the ItemCell
class ItemCell {
weak var delegate: ItemCellDelegate?
...
// Then call the delegate method when the buttons is tapped.
func buttonTapped() {
delegate?.itemCell(didTapButton: theCellsButton)
}
}
Then make your Cart conform to the delegate
extension Cart: ItemCellDelegate {
func itemCell(didTapButton button: UIButton) {
// Do you stuff with button, or any other data you want to pass in
}
}
Then set the delegate before the cell is returned.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CartCell", for: indexPath) as! CartCell
let cart = Cart.currentCart.CartItems[indexPath.row]
cell.qty.text = "\(cart.qty)"
cell.lblMealName.text = "\(cart.items.category): \(cart.items.name)"
cell.lblSubTotal.text = "$\(cart.items.price1 * Float(cart.qty))"
cell.delegate = Cart.currentCart //You should rename this static var to just 'current'
return cell
}

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

How to have each UITableViewOption have its own data

I am trying to have each choice in my UITableView to have its own unique set of data. For example, in my table view I have a list of states, then when I click on a state, I want each state to have a list of cities that correspond specifically to it. I have attached my code below, the code is strictly for the UITableView only.
I'm new to Xcode/Swift.
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
let textCellIdentifier = "TextCell"
var states = ["Illinois", "Indiana", "Kentucky", "Michigan", "Ohio", "Pennsylvania", "Wisconsin"]
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return states.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: textCellIdentifier, for: indexPath)
let row = indexPath.row
cell.textLabel?.text = states[row]
return cell
}
private func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: IndexPath) {
tableView.deselectRow(at: indexPath as IndexPath, animated: true)
let row = indexPath.row
print(states[row])
}
You can construct array model like this
struct MainItem {
var name:String
var cities:[String]
init(name:String,cities:[String]) {
self.name = name
self.cities = cities
}
}
//
let item1 = MainItem(name:"Illinois",cities:["city1","city2"])
let item2 = MainItem(name:"Indiana",cities:["city3","city4"])
var states = [item1,item2]
//
in cellForRowAt
cell.textLabel?.text = states[row].name
//
in didSelectRowAtIndexPath
let cities = states[row].cities
I recently did this by creating separate classes for each of the delegates I wanted to have. Move all of the table functions into a new class and create an instance of the class in your new controller. In the view did load function set the delegate for the first table. Whenever you switch tables with a button or whatever, do nextTable.delegate = xxxx.
View controller code:
let eventLogTableController = EventLogTableController()
let missedEventLogController = MissedEventTableController()
#IBOutlet weak var emptyTableLabel: UILabel!
#IBOutlet weak var missedEventLog: UITableView!
override func viewDidLoad() {
self.eventLog.delegate = eventLogTableController

Sort cell input from customized cell in viewController

I have a ViewController in which a tableView and a button "sort" is integrated.
The cells of this tableView are customized in another class called "customizedCell".
When the ViewController is loaded, a label (riskTitle: UITextView! in CellCustomized) in the tableView is filled with the items stored in the array (RiskTitles_Plan = String in ViewController). My code below has some values hard coded for this array.
What I am trying to do now is to store the numbers generated by two pickerViews in the label "riskFactor: UILabel!" in an array (RiskFactor_Plan -> RiskFactor_Int). When the user clicks on the button sort, the values in my array have to be sorted and the rows in the tableView will be loaded in a new order (smallest to biggest number or vise versa).
This is my code (unnecessary code is deleted).
Swift 3 - ViewController:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var RiskTitles_Plan = [String]()
var RiskFactor_Plan = [String]()
var RiskFactor_Plan_Int = [Int]()
var sortBtn = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
RiskTitles_Plan = ["my cat","my dog","my sheep","my cow","my fish"]
sortBtn.setImage(UIImage(named: "Sort"), for: UIControlState.normal)
sortBtn.addTarget(self, action: #selector(RiskPlan.sortAction(_:)), for: .touchUpInside)
self.view.addSubview(sortBtn)
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return RiskTitles_Plan.count
}
/////// Here comes the interesting part ///////
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView!.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CellCustomized
RiskFactor_Plan.append(cell.riskFactor.text!)
cell.riskTitle?.text = RiskTitles_Plan[indexPath.row]
cell.backgroundColor = myColorsClass.darkCyan()
for i in RiskFactor_Plan_Int {
let stringItem: String = String(i)
RiskFactor_Plan.append(stringItem)
}
cell.riskFactor.text! = RiskFactor_Plan[indexPath.row]
return cell
}
func sortAction(_ sender:UIButton!) {
for i in RiskFactor_Plan {
let intItem: Int = Int(i)!
RiskFactor_Plan_Int.append(intItem)
}
RiskFactor_Plan_Int = RiskFactor_Plan_Int.sorted{ $0 < $1 }
tableView.reloadData()
}
}
Swift 3 - CellCustomized:
import UIKit
class CellCustomized: UITableViewCell {
var myColorsClass = myColors()
var myStylesClass = myStyles()
#IBOutlet weak var riskTitle: UITextView!
#IBOutlet weak var riskFactor: UILabel!
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) // the common code is executed in this super call
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
} // nib end
}
The code above results in the error fatal error: unexpectedly found nil while unwrapping an Optional value for code line: let intItem: Int = Int(i)! in ViewController when the sort button in clicked.
My problem is to figure out
how to save the generated values in riskFactor: UILabel! in an array (-> RiskTitles_Plan = String) during runtime. I guess the array needs to be appended whenever the label in the table is updated with a generated value
how to convert the string array (-> RiskTitles_Plan = String) to an integer array (-> RiskTitles_Plan_Int = Int)
Based on the context of your problem I tried to replicate in a project so this is how I do it, hopefully it can help you.
Result:
Code:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
#IBOutlet weak var tableView: UITableView!
var risks = Array<(title: String, factor: Int)>()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
risks.append(("my cat", 0))
risks.append(("my dog", 0))
risks.append(("my sheep", 0))
risks.append(("my cow", 0))
risks.append(("my fish", 0))
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return risks.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
let riskTitle = risks[indexPath.row].title
let riskFactor = risks[indexPath.row].factor
cell.titleLabel.text = riskTitle
cell.factorLabel.text = String(riskFactor)
cell.index = indexPath.row
cell.onFactorValueChanged = { [unowned self] newFactorValue, atIndex in
self.risks[atIndex].factor = newFactorValue
}
return cell
}
#IBAction func sortRisksBasedOnFactor(_ sender: AnyObject) {
risks.sort { (riskA, riskB) -> Bool in
return riskA.factor > riskB.factor
}
tableView.reloadData()
}
}
class TableViewCell: UITableViewCell {
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var factorLabel: UILabel!
var index = 0
var onFactorValueChanged: (Int, Int) -> Void = { _ in }
#IBAction func generateFactor(_ sender: AnyObject) {
factorLabel.text = String(random(min: 1, max: 10))
onFactorValueChanged(Int(factorLabel.text!)!, index)
}
func random(min: Int, max: Int) -> Int {
guard min < max else {return min}
return Int(arc4random_uniform(UInt32(1 + max - min))) + min
}
}

Resources