Can't save data to MVC (Swift) - ios

I can't save my data from textfield to MVC.
When I enter text and click on the button, the data is not added.
how can i fix it?
I delete viewDidLoad() with table.delegate = self, table.dataSource = self from this Question.
This is the ViewController:
class ViewController: UIViewController, UITextFieldDelegate, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var table: UITableView!
#IBOutlet weak var Text: UITextField!
var model = ViewModel()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return model.persons?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! TableViewCell
let person = model.persons?[indexPath.row]
cell.Name?.text = person?.name
return cell
}
func textFieldEditing(_ sender: UITextField) {
model.input = sender.text
}
#IBAction func Add(_ sender: UIButton) {
if let name = model.input, name.count > 0 {
let person = Person()
person.name = name
model.addPerson(person)
model.input = nil
}
table.reloadData()
}
}
Model
class ViewModel {
var persons: [Person]?
var input: String?
func addPerson(_ person: Person) {
if persons == nil { persons = [] }
persons?.append(person)
}
}
class Person {
var name: String?
}

try this
// func textFieldEditing(_ sender: UITextField) {
// model.input = sender.text
// }
func textFieldDidEndEditing(_ textField: UITextField) {
model.input = textField.text
}
#IBAction func Add(_ sender: UIButton) {
self.view.endEditing(true)
if let name = model.input, name.count > 0 {
let person = Person()
person.name = name
model.addPerson(person)
model.input = nil
}
table.reloadData()
}

Related

How can I modify the tableView in the second VC without affecting the related first VC?

the problem is that I have currency flags and currency codes which are used in both of my screens. one is the first screen showing selected currency and the related flag and in the other screen i have same flags and codes next to each other inside tableview. Whichever i click on tableview i get it in the first screen as selected flag and code. what i want is I want to write a description next to the code like "{emoji} - USD - American Dollars" in the tableview but when i click it i dont want to see the American Dollars explanation in the first screen. how can i do that? this is what I want as a result and below that I m adding my codes
The Result I want
First VC
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var amountText: UITextField!
#IBOutlet weak var amountText2: UITextField!
#IBOutlet weak var fromLabel: UILabel!
#IBOutlet weak var fromImage: UIImageView!
#IBOutlet weak var toImage: UIImageView!
#IBOutlet weak var toLabel: UILabel!
var currencyManager = CurrencyManager()
var from: String = "EUR"
var to: String = "TRY"
var amount: String = "0"
override func viewDidLoad() {
super.viewDidLoad()
amountText.delegate = self
currencyManager.delegate = self
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let currencySelectorViewController = segue.destination as? CurrencySelectorViewController {
if let sender = sender as? Int {
if sender == 1 {
currencySelectorViewController.fromCurrencySelection = self
} else {
currencySelectorViewController.toCurrencySelection = self
}
}
}
}
#IBAction func amountChanged(_ sender: UITextField) {
amount = sender.text!
//amountText.endEditing(true)
currencyManager.fetchRates(from: from, to: to, amount: amount )
}
//#IBAction func editingChanged2(_ sender: UITextField) {
// amount = sender.text!
//
// //amountText.endEditing(true)
// currencyManager.fetchRates(from: to, to: from, amount: amount )
// }
#IBAction func didTapView(_ sender: UITapGestureRecognizer) {
performSegue(withIdentifier: "currencySelector", sender: 1)
}
#IBAction func didTapView2(_ sender: UITapGestureRecognizer) {
performSegue(withIdentifier: "currencySelector", sender: 2)
}
}
extension ViewController: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
amountText.endEditing(true)
currencyManager.fetchRates(from: from, to: to, amount: amount )
return true
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
if textField.text != "" {
return true
} else {
amountText.placeholder = "Enter an amount"
return false
}
}
func textFieldDidEndEditing(_ textField: UITextField) {
//amount = amountText.text!
//amount = amountText2.text!
amountText.text = ""
//amountText2.text = ""
}
}
extension ViewController: CurrencyManagerDelegate {
func didFailWithError(error: Error) {
print(error)
}
func didUpdateCurrency(_ currencyManager: String) {
DispatchQueue.main.async {
self.amountText2.text = currencyManager
}
}
}
extension ViewController: FromCurrencySelectorDelegate, ToCurrencySelectorDelegate {
func didGetCurrencyCode(from: String) {
fromLabel.text = from
self.from = from
}
func didGetCurrencyCode(to: String) {
toLabel.text = to
self.to = to
}
}
Second VC
import UIKit
protocol FromCurrencySelectorDelegate: AnyObject {
func didGetCurrencyCode(from: String)
}
protocol ToCurrencySelectorDelegate: AnyObject {
func didGetCurrencyCode(to: String)
}
class CurrencySelectorViewController: UIViewController {
weak var fromCurrencySelection: FromCurrencySelectorDelegate!
weak var toCurrencySelection: ToCurrencySelectorDelegate!
let reusableCell = "ReusableCell"
let currencyArray = ["🇪🇺 EUR", "\u{1F1FA}\u{1F1F8} USD", "\u{1F1F9}\u{1F1F7} TRY", "\u{1F1EC}\u{1F1E7} GBP", "\u{1F1EF}\u{1F1F5} JPY", "\u{1F1E8}\u{1F1E6} CAD", "\u{1F1E6}\u{1F1FA} AUD", "\u{1F1E7}\u{1F1EC} BGN", "\u{1F1F7}\u{1F1FA} RUB", "\u{1F1F3}\u{1F1F4} NOK", "\u{1F1E8}\u{1F1F3} CNY", "\u{1F1E8}\u{1F1ED} CHF", "\u{1F1F2}\u{1F1FD} MXN"]
var filteredData: [String]!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
filteredData = currencyArray
searchBar.delegate = self
tableView.delegate = self
tableView.dataSource = self
}
}
extension CurrencySelectorViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: reusableCell, for: indexPath)
cell.textLabel?.text = filteredData[indexPath.row]
return cell
}
}
extension CurrencySelectorViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if fromCurrencySelection != nil {
fromCurrencySelection.didGetCurrencyCode(from: currencyArray[indexPath.row])
} else {
toCurrencySelection.didGetCurrencyCode(to: currencyArray[indexPath.row])
}
dismiss(animated: true, completion: nil)
}
}
//MARK: - Search Bar Methods
extension CurrencySelectorViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filteredData = []
if searchText == "" {
filteredData = currencyArray
}
for word in currencyArray {
if word.uppercased().contains(searchText.uppercased()) {
filteredData.append(word)
}
}
self.tableView.reloadData()
}
}

How I could save data using realm in tableview?

I have table where I can add some empty cells. Using protocol I can send some data to each cell. When I am looking into the table I can see all the information I sent. My problem happens when try to save date using realm, only save last sent data to cell.
// model:
import UIKit
import RealmSwift
class HITActionModel: Object {
#objc dynamic var seconds: Int = 0
#objc dynamic var color: String = ""
#objc dynamic var name: String = ""
#objc dynamic var id: Int = 0
var parentWorkout = LinkingObjects(fromType: WorkoutModel.self, property: "actionsArray")
}
class WorkoutModel: Object {
#objc dynamic var title: String?
#objc dynamic var rounds: Int = 0
var actionsArray = List<HITActionModel>()
}
//protocol
protocol HITActionCellDelegate {
func action(sec: Int, name: String, id: Int)
}
//custom cell
class HITActionCell: UITableViewCell {
//MARK: - Properties
#IBOutlet weak var changeColorBtn: UIButton!
#IBOutlet weak var actionLabel: UILabel!
#IBOutlet weak var actionNameTextField: UITextField!
var delegate: HITActionCellDelegate?
var numberOfSeconds = 0
var id = 0
//MARK: - Lifecycle
override func awakeFromNib() {
super.awakeFromNib()
selectionStyle = .none
}
//MARK: - Elements setup
func setupElements() {
actionNameTextField.placeholder = "Add name for Action!"
changeColorBtn.backgroundColor = UIColor.neonYellow
actionLabel.text = "Entry seconds for action"
}
#IBAction func addActionSeconds(_ sender: Any) {
numberOfSeconds += 1
actionLabel.text = "\(numberOfSeconds) SECONDS"
delegate?.action(sec: numberOfSeconds, name: actionNameTextField.text ?? "", id: self.id)
}
#IBAction func reduceActionSeconds(_ sender: Any) {
if numberOfSeconds > 0 {
numberOfSeconds -= 1
actionLabel.text = "\(numberOfSeconds) SECONDS"
delegate?.action(sec: numberOfSeconds, name: actionNameTextField.text ?? "", id: self.id)
}
}
}
// From here I send data using Protocol to viewController :
import UIKit
import RealmSwift
class AddActionViewController: UIViewController {
//MARK: - Properties
let workout = WorkoutModel()
let action = HITActionModel()
let realm = try! Realm()
var numberOfRounds = 0
#IBOutlet weak var workoutNameTextField: UITextField!
#IBOutlet weak var workoutNameLabel: UILabel!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var roundsLabel: UILabel!
#IBOutlet weak var addRounds: UIButton!
#IBOutlet weak var reduceRounds: UIButton!
#IBOutlet weak var saveButton: UIButton!
#IBOutlet weak var cancelButton: UIButton!
#IBOutlet weak var addActionButton: UIButton!
//MARK:- Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
print(Realm.Configuration.defaultConfiguration.fileURL)
tableView.delegate = self
tableView.dataSource = self
tableView.separatorStyle = .none
tableView.register(UINib(nibName: Const.UI.NibName.hiActionCell, bundle: nil), forCellReuseIdentifier: Const.UI.CellIdentifier.hiActionCell)
}
//MARK:- Elements setup
func setupUI() {
view.backgroundColor = UIColor.black
title = Const.NameString.workouts
addActionButton.setTitle(Const.NameString.addAction, for: .normal)
addActionButton.tintColor = UIColor.black
addActionButton.titleLabel?.font = UIFont.textStyle9
workoutNameLabel.text = Const.NameString.workoutName
workoutNameLabel.font = UIFont.textStyle7
workoutNameLabel.textColor = UIColor.white
workoutNameTextField.textColor = UIColor.black
workoutNameTextField.font = UIFont.textStyle8
roundsLabel.text = Const.NameString.startingRounds
roundsLabel.font = UIFont.textStyle9
roundsLabel.textColor = UIColor.black
roundsLabel.textAlignment = .center
}
#IBAction func saveButtonAction(_ sender: Any) {
if workoutNameTextField.text != "" {
workout.title = workoutNameTextField.text!
workout.rounds = numberOfRounds
save(workout)
self.dismiss(animated: true, completion: nil)
} else {
let myalert = UIAlertController(title: "Message", message: "You forgot something to add ;) (name, rounds, seconds)", preferredStyle: UIAlertController.Style.alert)
myalert.addAction(UIAlertAction.init(title: "I'm guilty", style: .default, handler: nil))
self.present(myalert, animated: true)
}
}
func save(_ workout: WorkoutModel) {
do {
try realm.write {
realm.add(workout)
}
} catch {
print(error.localizedDescription)
}
}
#IBAction func cancelButtonAction(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
#IBAction func addActionButtonPressed(_ sender: Any) {
workout.actionsArray.append(action)
tableView.beginUpdates()
tableView.insertRows(at: [IndexPath(row: workout.actionsArray.count - 1, section: 0)], with: .fade)
tableView.endUpdates()
}
#IBAction func addRoundsBtn(_ sender: Any) {
numberOfRounds += 1
roundsLabel.text = "\(numberOfRounds) ROUNDS"
}
#IBAction func reduceRoundsBtn(_ sender: Any) {
if numberOfRounds > 0 {
numberOfRounds -= 1
roundsLabel.text = "\(numberOfRounds) ROUNDS"
}
}
}
//MARK:- Setup TextFieldDelegate Method
extension AddActionViewController: UITextFieldDelegate {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
view.endEditing(true)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
workoutNameTextField.resignFirstResponder()
return true
}
}
//Quick guide implementation for showing and deleting cells
extension AddActionViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return workout.actionsArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: Const.UI.CellIdentifier.hiActionCell) as? HITActionCell else {
return UITableViewCell()
}
cell.delegate = self
cell.setupElements()
cell.id = indexPath.row
return cell
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == .delete) {
workout.actionsArray.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
}
if (editingStyle == .insert) {
print("test")
}
}
extension AddActionViewController: HITActionCellDelegate {
func action(sec: Int, name: String, id: Int) {
action.name = name
action.seconds = sec
action.id = id
}
}
If could someone help me, It would be greet for me :)

How to create and save a one to many relationship (Parent Child) using a tableView and Realm

I am trying to create a one to many relationship, otherwise known as a parent child relationship, in realm. I looked at the documentation that realm offers but I am still a little stuck on how to do the actual saving to realm. I have two views, the main view is a view controller that just has a tableview with the numbers 1-7. In this view i can mass select for editing these rows in the table and save them to realm. That part is no big deal.
On the next view I have something very similar where there is a tableview with just some sample data. There is a mass select rows button, that is fine, it is the save button that I am having trouble with. The data in this tableView, which is the same on all of them just for testing purposes, is data i want to have a child relationship with the data on the first view.
For example if I have 4 saved to realm I click the row with 4 on it and it takes me to my next View. The tableview on this has two rows and other data, but i want to be able to mass select these rows and save them as a child to 4. I am a little confused as to what the save to realm function would look like.
this is my first view controller
import UIKit
import Realm
import RealmSwift
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var realm: Realm!
fileprivate var createSaves = SaveClass.createSaves()
var testingBool = false
var values: [String] = []
var valuesTwo: [String] = []
var valuesThree: [String] = []
#IBOutlet weak var itemBtn: UIBarButtonItem!
#IBOutlet weak var saveBtn: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
print(Realm.Configuration.defaultConfiguration.fileURL!)
realm = try! Realm()
self.tableView.delegate = self
self.tableView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return createSaves.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.txtLbl?.text = "\(createSaves[indexPath.row].label)"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if testingBool == true {
values.append(createSaves[indexPath.row].label)
valuesTwo.append(createSaves[indexPath.row].romanNum)
valuesThree.append(createSaves[indexPath.row].txt)
} else if testingBool == false {
performSegue(withIdentifier: "segue", sender: indexPath)
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if testingBool == true {
if let index = values.index(of: createSaves[indexPath.row].label) {
values.remove(at: index)
}
if let index = valuesTwo.index(of: createSaves[indexPath.row].romanNum) {
valuesTwo.remove(at: index)
}
if let index = valuesThree.index(of: createSaves[indexPath.row].txt) {
valuesThree.remove(at: index)
}
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let nxtVC = segue.destination as? TestingViewController
let myIndexPath = self.tableView.indexPathForSelectedRow!
let row = myIndexPath.row
nxtVC?.realmedData = createSaves[row].label
}
#IBAction func btnPressed(_ sender: Any) {
testingBool = !testingBool
if testingBool == true {
tableView.allowsMultipleSelection = true
tableView.allowsMultipleSelectionDuringEditing = true
itemBtn.title = "cancel"
} else if testingBool == false {
tableView.allowsMultipleSelection = false
tableView.allowsMultipleSelectionDuringEditing = false
itemBtn.title = "item"
}
}
#IBAction func saveBtnPressed(_ sender: Any) {
if testingBool == true {
//favorite(label: values)
realmed(label: values, romanNum: valuesTwo, txt: valuesThree)
}
}
func realmed(label: [String], romanNum: [String], txt: [String]) {
try? realm!.write {
for (stringOne, (stringTwo, stringThree)) in zip(label, zip(romanNum, txt)) {
let realmed = Realmed(label: stringOne, romanNum: stringTwo, txt: stringThree)
realm.add(realmed)
}
}
}
}
this is my second view
import UIKit
import Realm
import RealmSwift
class TestingViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var mainLbl: UILabel!
var realm: Realm!
var realmedData = ""
var testingBool = false
var values: [String] = []
var valuesTwo: [String] = []
#IBOutlet weak var testTable: UITableView!
#IBOutlet weak var selectBtn: UIButton!
#IBOutlet weak var saveBtn: UIButton!
let firstSave = OtherSave.otherArrOne()
override func viewDidLoad() {
super.viewDidLoad()
realm = try! Realm()
self.testTable.delegate = self
self.testTable.dataSource = self
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if objectExists(label: realmedData) == true {
self.mainLbl.text = "\(realmedData)"
} else {
self.mainLbl.text = "Don't Know"
}
}
#IBAction func selectBtnPressed(_ sender: Any) {
testingBool = !testingBool
if testingBool == true {
testTable.allowsMultipleSelection = true
testTable.allowsMultipleSelectionDuringEditing = true
} else if testingBool == false {
testTable.allowsMultipleSelection = false
testTable.allowsMultipleSelectionDuringEditing = false
}
}
#IBAction func saveBtnPressed(_ sender: Any) {
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return firstSave.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath) as! TestingTableViewCell
cell.nameLbl.text = "\(firstSave[indexPath.row].name)"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if testingBool == true {
values.append(firstSave[indexPath.row].info)
valuesTwo.append(firstSave[indexPath.row].name)
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if testingBool == true {
if let index = values.index(of: firstSave[indexPath.row].info) {
values.remove(at: index)
}
if let index = valuesTwo.index(of: firstSave[indexPath.row].name) {
valuesTwo.remove(at: index)
}
}
}
func fetchingLabel(label: String) -> Realmed? {
let realm = try? Realm()
return realm?.object(ofType: Realmed.self, forPrimaryKey: label)
}
func objectExists(label: String) -> Bool {
return realm.object(ofType: Realmed.self, forPrimaryKey: label) != nil
}
}
this is the parent's realm object:
import Foundation
import Realm
import RealmSwift
class Realmed: Object {
#objc dynamic var label = ""
#objc dynamic var romanNum = ""
#objc dynamic var txt = ""
let realmTwo = List<RealmTwo>()
override static func primaryKey() -> String {
return "label"
}
convenience init(label: String, romanNum: String, txt: String) {
self.init()
self.label = label
self.romanNum = romanNum
self.txt = txt
}
}
this is the child's realm object
import Foundation
import UIKit
import Realm
import RealmSwift
class RealmTwo: Object {
#objc dynamic var spanish = String()
#objc dynamic var french = String()
let realmed = LinkingObjects(fromType: Realmed.self, property: "realmTwo")
}
I have tried to do a function similar to the first view controllers:
func realmed(label: [String], romanNum: [String], txt: [String]) {
try? realm!.write {
for (stringOne, (stringTwo, stringThree)) in zip(label, zip(romanNum, txt)) {
let realmed = Realmed(label: stringOne, romanNum: stringTwo, txt: stringThree)
realm.add(realmed)
}
}
}
but I don't really understand how to create an instance of the data that i want to save as an array.
If there is anything i can help with, please ask
Thank you
I assume you want to create “RealmTwo” objects from “values” and “valuesTwo” and add them to a Realmed object .
What you want to do is
Fetch a parent object (Realmed)
Create a child object (RealmTwo)
Append the child to the parent object
You can do that with a function like this.
func save() {
let realmed = fetchingLabel(label: realmedData)! // fetch a parent
do {
try realm.write {
for index in 0..<values.count {
let realmTwo = RealmTwo() // create a child
realmTwo.french = values[index]
realmTwo.spanish = valuesTwo[index]
realmed.realmTwo.append(realmTwo) // append
}
}
} catch {
}
}

how to insert data from string to Sqlite in Swift3?

I have a problem that I have tried to insert string data to Sqlite. I want to do is in my Details ViewController if user press Like button, the word and meaning of string pass into Sqlite file that show in Favourite ViewController. Below is my DetailsVC code.
import UIKit
import AVFoundation
class DetailsVC: UIViewController, UITextViewDelegate {
#IBOutlet weak var wordLbl: UILabel!
#IBOutlet weak var meaningLbl: UITextView!
#IBOutlet weak var sysWordLbl: UILabel!
#IBOutlet weak var sysWordsLbl: UILabel!
#IBOutlet weak var anyWordsLbl: UILabel!
#IBOutlet weak var anyWordLbl: UILabel!
#IBOutlet weak var wordImg: UIImageView!
#IBOutlet weak var buSound: UIButton!
#IBOutlet weak var buLike: UIButton!
#IBOutlet weak var menuBtn: UIButton!
//Data from HomeVC
var data:Data?
var fdatas = [FData]()
var favouriteVC = FavouriteVC()
override func viewDidLoad() {
super.viewDidLoad()
meaningLbl.delegate = self
wordLbl.text = "\((data?._word.capitalized)!) \((data?._phonetic)!)"
self.meaningLbl.text = data?._meaning
self.sysWordsLbl.text = data?._meaning
self.anyWordsLbl.text = data?._meaning
self.sysWordLbl.text = "Synonym"
self.anyWordLbl.text = "Antonym"
meaningLbl.font = UIFont(name: FONT_REGULAR, size: 24.0)
}
#IBAction func buMenu(_ sender: Any) {
//menu
dismiss(animated: true, completion: nil)
}
#IBAction func buSound(_ sender: Any) {
//Todo: speak the word
if buSound.isEnabled == true{
buSound.setImage(UIImage(named: "volume-2.png"), for: .normal)
}else{
buSound.setImage(UIImage(named: "volume-un-2.png"), for: .normal)
}
let utTerance = AVSpeechUtterance(string: "\((data?._word)!)")
let synthesize = AVSpeechSynthesizer()
utTerance.voice = AVSpeechSynthesisVoice(language: "en-gb")
synthesize.speak(utTerance)
}
#IBAction func buLike(_ sender: Any) {
//Todo: Click Like
if buLike.isEnabled == true{
buLike.setImage(UIImage(named: "heart.png"), for: .normal)
//Save data to Database
let word = data?._word ?? ""
let meaning = data?._meaning ?? ""
do{
let favData = FData(word: word, meaning: meaning)
self.fdatas.append(favData)
print("\(word), \(meaning)")
}catch{
print(error)
}
}else{
buSound.setImage(UIImage(named: "heart-un.png"), for: .normal)
}
//FavouriteVC.insertRowsAtIndexPaths([NSIndexPath(forRow: fdatas.count-1, inSection: 0)], withRowAnimation: .Fade)
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
self.view.endEditing(true);
return false;
}
}
My FavouriteVC
import UIKit
import SwipeCellKit
import SQLite
class FavouriteVC: UIViewController,UITableViewDelegate,UITableViewDataSource, SwipeTableViewCellDelegate{
//TabelView
#IBOutlet weak var fTableView: UITableView!
//Variables
var fdata = [FData]()
override func viewDidLoad() {
super.viewDidLoad()
// FtableView
fTableView.delegate = self
fTableView.dataSource = self
//reload Data
fTableView.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fdata.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: FAVOURITE_CELL, for: indexPath) as? FavouriteCell{
cell.configureCell(fdata: fdata[indexPath.row])
//cell.delegate = self
return cell
}
return FavouriteCell()
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> [SwipeAction]? {
guard orientation == .right else { return nil }
let deleteAction = SwipeAction(style: .destructive, title: "Delete") { action, indexPath in
// handle action by updating model with deletion
}
// customize the action appearance
deleteAction.image = UIImage(named: "delete")
return [deleteAction]
}
}
FData.swif
import Foundation
class FData {
//let id: Int64?
var word: String
var meaning: String
//var address: String
init(id: Int64) {
// self.id = id
word = ""
meaning = ""
//address = ""
}
init(word: String, meaning: String) {
// self.id = id
self.word = word
self.meaning = meaning
//self.address = address
}
}
FavouriteData Instance
import UIKit
import SQLite
class FavouriteData{
static let instance = FavouriteData()
private let db: Connection?
private let words = Table("words")
private let id = Expression<Int64>("id")
private let wordL = Expression<String?>("word")
private let meaningL = Expression<String?>("shanword_uni")
//private let address = Expression<String>("address")
private init() {
let path = NSSearchPathForDirectoriesInDomains(
.documentDirectory, .userDomainMask, true
).first!
do {
db = try Connection("\(path)/favourite.sqlite")
} catch {
db = nil
print ("Unable to open database")
}
createTable()
}
func createTable() {
do {
try db!.run(words.create(ifNotExists: true) { table in
table.column(id, primaryKey: true)
table.column(wordL)
//table.column(phone, unique: true)
table.column(meaningL)
})
} catch {
print("Unable to create table")
}
}
func addData(word: String, meaning: String) -> Int64? {
do {
let insert = words.insert(wordL <- word, meaningL <- meaning)
let id = try db!.run(insert)
print("Save: \(id)")
return id
} catch {
print("Insert failed")
return -1
}
}
func getData() -> [FData] {
var contacts = [FData]()
do {
for contact in try db!.prepare(self.words) {
contacts.append(FData(
// id: contact[id],
word: contact[wordL]!,
meaning: contact[meaningL]!))
}
} catch {
print("Select failed")
}
return contacts
}
func deleteData(index: Int64) -> Bool {
do {
let contact = words.filter(id == index)
try db!.run(contact.delete())
return true
} catch {
print("Delete failed")
}
return false
}
}
But, there is not cell of row in the FavouriteVC. How to fix this? Please help me.
Best,
Sai Tawng Pha
I have solved my problem by add one line of code. In my FavouriteVC class, just add it under ViewdidLoad function " fdata = FavouriteData.instance.getData() ".
Here is my complete code.
import UIKit
import SwipeCellKit
import SQLite
class FavouriteVC: UIViewController,UITableViewDelegate,UITableViewDataSource, SwipeTableViewCellDelegate{
//TabelView
#IBOutlet weak var fTableView: UITableView!
//Variables
var fdata = [FData]()
override func viewDidLoad() {
super.viewDidLoad()
// FtableView
fTableView.delegate = self
fTableView.dataSource = self
//reload Data
fTableView.reloadData()
//Get Data
fdata = FavouriteData.instance.getData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fdata.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: FAVOURITE_CELL, for: indexPath) as? FavouriteCell{
cell.configureCell(fdata: fdata[indexPath.row])
//cell.delegate = self
return cell
}
return FavouriteCell()
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> [SwipeAction]? {
guard orientation == .right else { return nil }
let deleteAction = SwipeAction(style: .destructive, title: "Delete") { action, indexPath in
// handle action by updating model with deletion
}
// customize the action appearance
deleteAction.image = UIImage(named: "delete")
return [deleteAction]
}
}

iOS development Swift Custom Cells

I'm developing an app in Swift and I have problem with Custom Cells. I have one Custom Cell and when you click on Add button it creates another Custom Cell. Cell has 3 textfields, and 2 buttons. Those textfields are name, price, and amount of the ingredient of meal that I am creating. When I use only one Custom Cell or add one more to make it two, the data is stored properly. But when I add 2 Custom Cell (3 Custom Cells in total) I have problem of wrong price, only last two ingredients are calculated in price. When I add 3 Custom Cells (4 Custom Cells in total) it only recreates first cell with populated data like in first cell.
On finish button tap, I get an fatal error: Found nil while unwrapping Optional value.
View Controller
import UIKit
import CoreData
class AddMealViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {
#IBOutlet weak var mealNameTF: UITextField!
#IBOutlet weak var addMealsCell: UITableViewCell!
#IBOutlet weak var finishButton: UIButton!
#IBOutlet weak var resetButton: UIButton!
#IBOutlet weak var addButton: UIButton!
#IBOutlet weak var addMealTableView: UITableView!
#IBOutlet weak var productPrice: UILabel!
let currency = "$" // this should be determined from settings
var priceTotal = "0"
override func viewDidLoad() {
super.viewDidLoad()
addMealTableView.delegate = self
addMealTableView.dataSource = self
borderToTextfield(textField: mealNameTF)
mealNameTF.delegate = self
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.view.endEditing(true)
return true;
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return counter
}
var counter = 1
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return counter
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier:
"addMealsCell", for: indexPath) as! AddMealsTableViewCell
borderToTextfield(textField: (cell.amountTF)!)
borderToTextfield(textField: (cell.ingredientNameTF)!)
//borderToTextfield(textField: cell.normativeTF)
borderToTextfield(textField: (cell.priceTF)!)
return cell
}
#IBAction func addButton(_ sender: UIButton) {
counter += 1
addMealTableView.register(AddMealsTableViewCell.self, forCellReuseIdentifier: "addMealsCell")
addMealTableView.reloadData()
}
#IBAction func resetButton(_ sender: UIButton) {
mealNameTF.text = ""
for c in 0..<counter{
let indexPath = IndexPath(row: c, section:0)
let cell = addMealTableView.cellForRow(at: indexPath) as! AddMealsTableViewCell
cell.amountTF.text = ""
cell.ingredientNameTF.text = ""
// cell.normativeTF.text = ""
cell.priceTF.text = ""
}
productPrice.text = "\(currency)0.00"
priceTotal = "0"
counter = 1
}
#IBAction func finishButton(_ sender: UIButton) {
for c in (0..<counter){
if let cell = addMealTableView.cellForRow(at: IndexPath(row: c, section: 0)) as? AddMealsTableViewCell {
cell.amountTF.delegate = self
cell.ingredientNameTF.delegate = self
// cell.normativeTF.delegate = self
cell.priceTF.delegate = self
guard cell.priceTF.text?.isEmpty == false && cell.amountTF.text?.isEmpty == false && mealNameTF.text?.isEmpty == false && cell.ingredientNameTF.text?.isEmpty == false
else {
return
}
if cell.priceTF.text?.isEmpty == false{
// if (true) {
let tfp = Double((cell.priceTF.text!))!*Double((cell.amountTF.text!))!
var ttp = Double(priceTotal)
ttp! += tfp
priceTotal = String(ttp!)
// }
}}
}
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let newMeal = NSEntityDescription.insertNewObject(forEntityName: "Meal", into: context)
let mealName = mealNameTF.text
newMeal.setValue(mealName, forKey: "name")
newMeal.setValue(priceTotal, forKey: "price")
do {
try context.save()
print("Spremljeno")
} catch {
print("Neki error")
}
productPrice.text = currency + priceTotal
}
#IBAction func addNewIngredientButton(_ sender: UIButton) {
}
func borderToTextfield(textField: UITextField){
let border = CALayer()
let width = CGFloat(2.0)
border.borderColor = UIColor.white.cgColor
border.frame = CGRect(x: 0, y: textField.frame.size.height - width, width: textField.frame.size.width, height: textField.frame.size.height)
border.borderWidth = width
textField.layer.addSublayer(border)
textField.layer.masksToBounds = true
textField.tintColor = UIColor.white
textField.textColor = UIColor.white
textField.textAlignment = .center
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.view.endEditing(true)
return true;
}
}
Cell
class AddMealsTableViewCell: UITableViewCell, UITextFieldDelegate{
#IBOutlet weak var DropMenuButton: DropMenuButton!
#IBOutlet weak var addNewIngredient: UIButton!
#IBOutlet weak var ingredientNameTF: UITextField!
// #IBOutlet weak var normativeTF: UITextField!
#IBOutlet weak var amountTF: UITextField!
#IBOutlet weak var priceTF: UITextField!
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.endEditing(true)
return true;
}
override func prepareForReuse() {
self.amountTF.text = ""
self.priceTF.text = ""
self.ingredientNameTF.text = ""
}
#IBAction func DropMenuButton(_ sender: DropMenuButton) {
DropMenuButton.initMenu(["kg", "litre", "1/pcs"], actions: [({ () -> (Void) in
print("kg")
sender.titleLabel?.text = "kg"
}), ({ () -> (Void) in
print("litre")
sender.titleLabel?.text = "litre"
}), ({ () -> (Void) in
print("1/pcs")
sender.titleLabel?.text = "1/pcs"
})])
}
#IBAction func addNewIngredient(_ sender: UIButton) {
let name = ingredientNameTF.text
let amount = amountTF.text
let price = priceTF.text
// let normative = normativeTF.text
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let newIngredient = NSEntityDescription.insertNewObject(forEntityName: "Ingredient", into: context)
newIngredient.setValue(name, forKey: "name")
// newIngredient.setValue(normative, forKey: "normative")
newIngredient.setValue(amount, forKey: "amount")
newIngredient.setValue(price, forKey: "price")
do {
try context.save()
print("Spremljeno")
} catch {
print("Neki error")
}
}
}
Your code is very difficult to read, but I suspect the problem may be here:
func numberOfSections(in tableView: UITableView) -> Int {
return counter
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return counter
}
You're returning the same number of rows for both the sections and rows for sections. So if you have 1 ingredient you are saying there is 1 section with 1 row. But if you have 2 ingredients you are saying there are 2 sections, each with 2 cells (4 cells total).
There are many other things to fix with your code, here are a few:
The biggest thing is that you are making this very difficult with the counter variable you have. If instead you have an array of ingredients
var ingredients = [Ingredient]()
You can use that to setup everything for the count of your table. Something like this:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return ingredients.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier:
"addMealsCell", for: indexPath) as! AddMealsTableViewCell
let ingredient = ingredients[indexPath.row]
cell.ingredientNameTF.text = ingredient.name
cell.normativeTF.text = ingredient.normative
cell.amountTF.text = ingredient.amount
cell.priceTF.text = ingredient.price
return cell
}
All of these can be set in interface builder (you dont need lines of code in your view did load for them):
addMealTableView.delegate = self
addMealTableView.dataSource = self
mealNameTF.delegate = self
This line should be in your viewDidLoad function, you only need to register the class once, you're doing it everytime add is pressed.
addMealTableView.register(AddMealsTableViewCell.self, forCellReuseIdentifier: "addMealsCell")
Your action names should be actions
#IBAction func addButtonPressed(_ sender: UIButton)
#IBAction func resetButtonPressed(_ sender: UIButton)
#IBAction func finishButtonPressed(_ sender: UIButton)
Thanks. Now when that ViewController loads I have no cells. My array is now empty so I have to implement some code to addButton which creates another cell and reloads tableView. How can I do that?
You just need to add a new ingredient object to the ingredients array and reload the data of the table view.
#IBAction func addButtonPressed(_ sender: UIButton) {
let newIngredient = Ingredient()
ingredients.append(newIngredient)
tableView.reloadData()
}

Resources