I made an application, this application have a couple of functions. It has Table View, Labels, Buttons etc. I made a Picker View in a class called SettingsViewController that is useful for choosing language. I made it and it's working good but I don't know how to change text of Table View and Labels of other classes. I'm not to experienced in calling classes.
This is my class SettingsViewController:
import Foundation
import UIKit
class SettingsViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
var languages = ["English", "German", "French"]
var selectedLanguage = 0
#IBOutlet var languageLabel: UILabel!
#IBOutlet var topPartView: UIView!
#IBOutlet weak var languagePicker: UIPickerView!
#IBOutlet var SubmitLanguageButton: UIButton!
#IBAction func pickLanguageButton(sender: AnyObject) {
//Show Picker
topPartView.hidden = false
languagePicker.hidden = false
SubmitLanguageButton.hidden = false
}
#IBAction func submitLanguageButton(sender: AnyObject) {
//Hidde Picker
topPartView.hidden = true
languagePicker.hidden = true
SubmitLanguageButton.hidden = true
if (selectedLanguage == 0) {
languageLabel.text = "English"
}
else if (selectedLanguage == 1) {
languageLabel.text = "German"
}
else if (selectedLanguage == 2) {
languageLabel.text = "French"
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
topPartView.hidden = true
languagePicker.hidden = true
SubmitLanguageButton.hidden = true
languagePicker.delegate = self
languagePicker.dataSource = self
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return languages[row]
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return languages.count
}
// returns the number of 'columns' to display.
public func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
selectedLanguage = row
}
}
Another random ViewController with Table View and Label:
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let foodList:[String] = ["Apple", "Bread", "Phineapple", "Water", "Other"]
#IBOutlet weak var myTableView: UITableView!
#IBOutlet var foodTitleLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
foodTitleLabel.text = "Food"
myTableView.reloadData()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var returnValue = 0
return returnValue
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCellWithIdentifier("myCells", forIndexPath: indexPath)
myCell.textLabel!.text = foodList[indexPath.row]
myCell.textLabel!.numberOfLines = 0
return myCell
}
}
So how I'm gonna change Labels and Table View from another class, because every text have to be on language that is chose in SettingsViewController ?
For ex: When user choose English, the text in all ViewControllers have to be in English.
When user choose French, the text in all ViewControllers have to be in French.
If this question was not clear, please inform me, I wil try to show more info. Thanks for your contribution.
We can accomplish this in a cleaner way with less code like :
enum names{
case English, German, French
func food() -> String {
switch self {
case English: return "food"
case German: return "Lebensmittel"
case French: return "Aliments"
}
}
func foodList() -> [] {
switch self {
case English: return ["Apple", "Bread", "Pineapple", "Water", "Other"]
case German: return [ "Apfel", "Brot", "Pineapple", "Wasser","Andere"]
case French: return ["Pomme", "Pain", "Ananas", "Eau", "Autre"]
}
}
}
import UIKit
var lang = names.English
class SettingsViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
foodTitleLabel.text = lang.food()
foodList = lang.foodList()
So you could address by different approachs the problem, but for my point of view the easiest one is to implement a protocol that handles events in a viewcontroller and has an impact in another one for examlple
protocol ChangeLanguagesProtocol
{
func returnNewLanguages(newlanguages:[String])
}
then you you will need to implement the protocol and its method in your viewcontroller like
class SettingsViewController: UIViewController,
UIPickerViewDelegate, UIPickerViewDataSource , ChangeLanguagesProtocol {
var languages = ["English", "German", "French"]
func returnNewLanguages(newlanguages:[String]){
self.languages = newlanguages
}
Then in you second ViewController class you need to have a var of the type of your protocol and trigger its method when your need it
//variable
var delegate:ChangeLanguagesProtocol!
//for example when clicking a button
#IBAction func SampleButtonPress(sender:UIButton){
let newlanguages:[String] = ["Spanish", "Chinesse", "German", "French", "Other"]
delegate.returnNewLanguages(newlanguages)
}
last step we need to assign the delegate variable when we are going to push to the desired viewcontroller, in the case where viewcontrollers are not next to each other you can use singleton or a shared instance to maintain your delegate property until you need it, but it will look something like
// this should be in your first ViewController
#IBAction func gotoNextViewController(sender:UIButton){
let _localization = Localization(nibName: "Localization", bundle:nil)
_localization .delegate = self
self.navigationController?.pushViewController(_localization , animated: true)
}
You are trying to internationalize your app. Apple has some built in stuff for this that you should check out: https://developer.apple.com/internationalization/
If that doesn't suit your needs, to answer your question, your views should set their text when they either load (viewDidLoad) or right before they appear (viewWillAppear) as a result of the user's language choice, which could possibly be stored in NSUserDefaults. Your settings class shouldn't have to force any change in other views, your other views should set their own text to the right language before they are displayed.
struct Localization {
static func stringsForScreen(screenName:String, forLanguage language:String) -> [String:String] {
//parse a file that has the strings for the given screen in the given language, and return it
}
}
class HomeViewController:UIViewController {
#IBOutlet var titleLabel:UILabel?
#IBOutlet var bodyLabel:UILabel?
override func viewDidLoad() { //or possibly viewDidAppear, depends on your needs
super.viewDidLoad()
let language = NSUserDefaults.standardUserDefaults().stringForKey("currentLanguage") ?? "English"
let textDict = Localization.stringsForScreen("HomeViewController" forLanguage:language)
titleLabel.text = textDict["titleLabelText"] ?? ""
bodyLabel.text = textDict["bodyLabelText"] ?? ""
}
}
class PickLanguageViewController:UIViewController, UIPickerViewDelegate {
var languages = ["English","French","Spanish"]
//Whatever method the picker uses
func pickerView(pickerView:UIPickerView didSelectRow row:Int inComponent component:Int) {
NSUserDefaults.standardUserDefaults().setObject(languages[row] forKey:"currentLanguage")
}
}
skeleton example of how this might be structured.
I found solution. First of All the variable selectedLanguage I declared it out of the class SettingsViewController.
import UIKit
var selectedLanguage = 0 //<<< Here
class SettingsViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
var languages = ["English", "German", "French"]
After that I did this in method viewDidLoad to every ViewController (I will show just one ViewController).
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if (selectedLanguage == 0) { //English
foodTitleLabel.text = "Food"
foodList = ["Apple", "Bread", "Pineapple", "Water", "Other"]
}
else if (selectedLanguage == 1) { //German
foodTitleLabel.text = "Lebensmittel"
foodList = [ "Apfel", "Brot", "Pineapple", "Wasser","Andere"]
}
if (selectedLanguage == 2) { //French
foodTitleLabel.text = "Aliments"
foodList = ["Pomme", "Pain", "Ananas", "Eau", "Autre"]
}
I hope this will be helpful for other users, because It worked perfectly for me.
Related
I have a ViewController which uses multiple Subviews (HomeViewController, etc.) which can be selected via a Custom Tab Bar at the bottom of my app. Inside the HomeViewController there is a UIView containing a UITableView containing a Prototype Custom Cell with name and image.
import UIKit
class HomeViewController: UIViewController {
#IBOutlet weak var friendView: UITableView!
let friends = ["batman", "harsh", "ava", "sasha", "fatima", "alfred"]
override func viewDidLoad() {
super.viewDidLoad()
friendView.delegate = self
friendView.dataSource = self
friendView.allowsSelection = false
}
}
extension HomeViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 120
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return friends.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = friendView.dequeueReusableCell(withIdentifier: "customCell") as! CustomCell
let friend = friends[indexPath.row]
cell.avatarImg.image = UIImage(named: friend)
cell.nameLbl.text = friend
return cell
}
}
Custom cell:
import UIKit
class CustomCell: UITableViewCell {
#IBOutlet weak var friendView: UIView!
#IBOutlet weak var nameLbl: UILabel!
#IBOutlet weak var avatarImg: UIImageView!
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
}
}
When I start the app, everything looks just fine. However, when I start scrolling inside the table, all data suddenly disappears. All relations between storyboard and code should be just fine. I think it might have got something to do with my need of using a Subview.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var tabBarView: UIView!
#IBOutlet weak var contentView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
Design.makeCornersRound(view: tabBarView, radius: 10.0)
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { (timer) in
self.switchToHomeViewController()
}
}
#IBAction func onClickTabBar(_ sender: UIButton) {
let tag = sender.tag
if tag == 1 {
switchToIncomingsViewController()
}
else if tag == 2 {
switchToSpendingsViewController()
}
else if tag == 3 {
switchToHomeViewController()
}
else if tag == 4 {
switchToSavingsViewController()
}
else if tag == 5 {
switchToSettingsViewController()
}
}
func switchToHomeViewController() {
guard let Home = self.storyboard?.instantiateViewController(withIdentifier: "HomeViewController") as? HomeViewController else { return }
contentView.addSubview(Home.view)
Home.didMove(toParent: self)
}
...
}
Reference to the tutorial I have been trying to implement: https://www.youtube.com/watch?v=ON3Z0PXSoVk
In this function:
func switchToHomeViewController() {
// 1
guard let Home = self.storyboard?.instantiateViewController(withIdentifier: "HomeViewController") as? HomeViewController else { return }
// 2
contentView.addSubview(Home.view)
// 3
Home.didMove(toParent: self)
// 4
}
At 1 you create an instance of HomeViewController
at 2 you add its view to cotentView
at 3 you call didMove() ... but that doesn't do anything because you haven't added the controller to your hierarchy
at 4 your Home instance goes away, so the code in that controller no longer exists
You need to add the controller as a child controller.
As a side note, use lowerCase for variable names:
func switchToHomeViewController() {
// create an instance of HomeViewController
guard let homeVC = self.storyboard?.instantiateViewController(withIdentifier: "HomeViewController") as? HomeViewController else { return }
// add it as a child view controller
self.addChild(homeVC)
// add its view
contentView.addSubview(homeVC.view)
// here you should either set the view's frame or add constraints
// such as:
homeVC.view.frame = contentView.bounds
// inform the controller that it moved to a parent controller
homeVC.didMove(toParent: self)
}
I am trying to add an option to add additional student fields inside table so that user can add more than one student name.
But I am confused how to do it using table view.
I am not interested in hiding view with specific number of fields.
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
struct listItems{
var title : String
var isExpandable:Bool
var maxFields :Int
init(title:String,isExp:Bool,mxF:Int) {
self.title = title
self.isExpandable = isExp
self.maxFields = mxF
}
}
#IBOutlet weak var tblListTable: UITableView!
let data : [listItems] = [listItems(title: "Name", isExp: false, mxF: 1), listItems(title: "Student Name", isExp: true, mxF: 20), listItems(title: "Email", isExp: false, mxF: 1)]
override func viewDidLoad() {
super.viewDidLoad()
tblListTable.delegate = self
tblListTable.dataSource = self
self.tblListTable.reloadData()
print("isLoaded")
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("cellForRow")
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! ListCell
cell.lblName.text = data[indexPath.row].title
if data[indexPath.row].isExpandable == true {
cell.btnAddField.isHidden = false
print("ishidden")
}
else {
cell.btnAddField.isHidden = true
}
return cell
}
}
List Cell Class
import UIKit
protocol AddFieldDelegate : class {
func addField( _ tag : Int)
}
class ListCell: UITableViewCell {
#IBOutlet weak var btnAddField: UIButton!
#IBOutlet weak var lblName: UILabel!
#IBOutlet weak var txtField: UITextField!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func addField( _ tag : Int){
}
}
You are on the right track creating the AddFieldDelegate. However, rather than implementing the method inside the ListCell class you need to implement it in the ViewController.
First, change the view controller class definition line to:
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource, AddFieldDelegate {
This will allow you to call the delegate method from the view controller. Next, when you are creating your table view cells add the line:
cell.delegate = self
After that, move the method definition of the method addField to the view controller.
So inside of your view controller add:
func addField(titleOfTextFieldToAdd: String, numberAssociatedWithTextFieldToAdd: Int) {
data.append(listItems(title: titleOfTextFieldToAdd, isExp: false, mxF: numberAssociatedWithTextFieldToAdd))
self.tableView.reloadData()
}
I used an example definition of the addField method but you can change it to anything that you would like, just make sure that you change the data array and reload the table view data.
Lastly, we must define the delegate in the ListCell class. So add this line to the ListCell class:
weak var delegate: MyCustomCellDelegate?
You can then add the text field by running the following anywhere in your ListCell class:
delegate?.addField(titleOfTextFieldToAdd: "a name", numberAssociatedWithTextFieldToAdd: 50)
For more information on delegation, look at the answer to this question.
You have to append another item in your data array on button click and reload the tableview.
This would be part # 2 of my question How to prevent cells from mirroring button pressed action in another cell?
What im trying to do is have my buttons have a button pressed turn red while a previously selected button deselects to back to blue, and also preventing it from mirroring the pressed button action in another cell, I have achieved that in a previous question I posted
what Im trying to do is integrate this with classes that pass data from Firebase Firestore. since I don't know where to go to convert this prevent the cells from mirroring the same button select action in another and changes the button selected to red and automatically deselects previous button back to blue
I have been stuck trying to make this work and just not getting the right luck to make it happen, I have been getting error codes in 3 different areas in ViewController preventing my code from compiling and making it work so that it works with my cells that pass data to labels from my cloud Firestore
any help would be appreciated and thank you for your time
import Foundation
import UIKit
class Labels {
var id: String
var lbl1: String
var lbl2: String
var lbl3: String
init(id: String,
lbl1: String,
lbl2: String,
lbl3: String) {
self.id = id
self. lbl1 = lbl1
self. lbl2 = lbl2
self. lbl3 = lbl3
}
convenience init(dictionary: [String : Any]) {
let id = dictionary["id"] as? String ?? ""
let lbl1 = dictionary["lbl1"] as? String ?? ""
let lbl2 = dictionary["lbl2"] as? String ?? ""
let lbl3 = dictionary["lbl3"] as? String ?? ""
self.init(id: id,
lbl1: lbl1,
lbl2: lbl2,
lbl3: lbl3)
}
}
enum ButtonSelectionIdentity {
case first
case second
case third
}
struct CellModel {
let buttonSelectionIdentity: ButtonSelectionIdentity
let labels: Labels
}
import UIKit
import SDWebImage
import Firebase
protocol OptionSelectDelegate: class {
func onCellModelChange(cell: Cell, model: ButtonSelectionIdentity)
}
class Cell: UITableViewCell {
weak var labels: Labels!
private var elements: [ButtonSelectionIdentity] = []
weak var optionSelectDelegate: OptionSelectDelegate?
#IBOutlet weak var lbl1: UILabel!
#IBOutlet weak var lbl2: UILabel!
#IBOutlet weak var lbl3: UILabel!
#IBOutlet weak var btnOne: RoundButton!
#IBOutlet weak var btnTwo: RoundButton!
#IBOutlet weak var btnThree: RoundButton!
func configure(withLabels labels: Labels) {
lbl1.text = labels.lbl1
lbl2.text = labels.lbl2
lbl3.text = labels.lbl3
}
override func layoutSubviews() {
super.layoutSubviews()
}
func update(with model: ButtonSelectionIdentity) {
btnOne.backgroundColor = UIColor.blue
btnTwo.backgroundColor = UIColor.blue
btnThree.backgroundColor = UIColor.blue
switch model {
case .first:
btnOne.backgroundColor = UIColor.red
case .second:
btnTwo.backgroundColor = UIColor.red
case .third:
btnThree.backgroundColor = UIColor.red
}
}
#IBAction func optionSelectOne(_ sender: RoundButton!) {
optionSelectDelegate?.onCellModelChange(cell: self, model: .first)
}
#IBAction func optionSelectTwo(_ sender: RoundButton!) {
optionSelectDelegate?.onCellModelChange(cell: self, model: .second)
}
#IBAction func optionSelectThree(_ sender: RoundButton!) {
optionSelectDelegate?.onCellModelChange(cell: self, model: .third)
}
}
import UIKit
import Firebase
import FirebaseFirestore
class ViewController: UIViewController {
private var elements: [CellModel] = []
#IBOutlet weak var tableView: UITableView!
var labelSetup: [Labels] = []
override func viewDidLoad() {
super.viewDidLoad()
//▼ Cannot convert value of type 'ButtonSelectionIdentity' to expected argument type 'CellModel'
elements.append(ButtonSelectionIdentity.first) // error one
tableView.dataSource = self
tableView.delegate = self
fetchLabels { (labels) in
self.labelSetup = labels.sorted(by:
self.tableView.reloadData()
}
}
func fetchLabels(_ completion: #escaping ([Labels]) -> Void) {
let ref = Firestore.firestore().collection("labels")
ref.addSnapshotListener { (snapshot, error) in
guard error == nil, let snapshot = snapshot, !snapshot.isEmpty else {
return
}
completion(snapshot.documents.compactMap( {Labels(dictionary: $0.data())} ))
}
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return labelSetup.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as? Cell else { return UITableViewCell() }
cell.configure(withLabels: labelSetup[indexPath.row])
cell.optionSelectDelegate = self
let model = elements[indexPath.row]
//▼ Cannot convert value of type 'CellModel' to expected argument type 'ButtonSelectionIdentity'
cell.update (with: CellModel) //error 2
return cell
}
}
extension ViewController: OptionSelectDelegate {
func onCellModelChange(cell: Cell, model: ButtonSelectionIdentity) {
guard let indexPath = productListTableView.indexPath(for: cell) else {
return
}
let index = indexPath.row
elements[index] = model
//▼ Cannot assign value of type 'ButtonSelectionIdentity' to type 'CellModel'
cell.update(with: model) //error 3
}
}
I have made a simple demo using TableView here: https://github.com/deadcoder0904/TableViewDemo
I have used Defaults module as a dependency
My project looks like
All the code is in ViewController.swift as follows -
import Cocoa
import Defaults
extension Defaults.Keys {
static let dreams = Defaults.Key<Array<String>>("dreams", default: [
"Hit the gym",
"Run daily",
"Become a millionaire",
"Become a better programmer",
"Achieve your dreams"
])
}
class ViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate {
#IBOutlet weak var table: NSTableView!
var dreams = defaults[.dreams]
var selectedRow:Int = 0
override func viewDidLoad() {
super.viewDidLoad()
table.dataSource = self
table.delegate = self
}
override var acceptsFirstResponder : Bool {
return true
}
override func keyDown(with theEvent: NSEvent) {
if theEvent.keyCode == 51 {
removeDream()
}
}
func tableViewSelectionDidChange(_ notification: Notification) {
let table = notification.object as! NSTableView
selectedRow = table.selectedRow
}
func numberOfRows(in tableView: NSTableView) -> Int {
return dreams.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let dream = table.makeView(withIdentifier: tableColumn!.identifier, owner: self) as! NSTableCellView
dream.textField?.stringValue = dreams[row]
return dream
}
#IBAction func addTableRow(_ sender: Any) {
addNewDream()
}
#IBAction func removeTableRow(_ sender: Any) {
removeDream()
}
func addNewDream() {
dreams.append("Double Click or Press Enter to Add Item")
table.beginUpdates()
let last = dreams.count - 1
table.insertRows(at: IndexSet(integer: last), withAnimation: .effectFade)
table.scrollRowToVisible(last)
table.selectRowIndexes([last], byExtendingSelection: false)
table.endUpdates()
saveDreams()
}
func removeDream() {
if selectedRow >= dreams.count {
selectedRow = dreams.count - 1
}
if selectedRow != -1 {
dreams.remove(at: selectedRow)
table.removeRows(at: IndexSet(integer: selectedRow), withAnimation: .effectFade)
}
saveDreams()
}
func saveDreams() {
defaults[.dreams] = dreams
}
}
I want to do 2 things -
Get notified after Text Cell is edited so that I can save the changed data using Defaults module
After adding new Data by Clicking on the plus sign it adds Double Click or Press Enter to Add Item but what I want is I want to add Empty String which I can do with "" but I also want it to be focused & be editable so user can start entering text in it without having to Double Click or Press Enter.
I also want a solution in Swift 4 & not Objective-C. How to achieve this?
Use Cocoa Bindings, it's very powerful and saves a lot of boilerplate code.
Short tutorial:
Edit: To take full advantage of KVC the data source must be an NSObject subclass with dynamic properties
Create a simple class Dream (the description property is optional)
class Dream : NSObject {
#objc dynamic var name : String
init(name : String) { self.name = name }
override var description : String { return "Dream " + name }
}
In the view controller declare the data source array
var dreams = [Dream]()
and replace var selectedRow:Int = 0 with
#objc dynamic var selectedIndexes = IndexSet()
Go to Interface Builder
Select the table view, press ⌥⌘7 to go to the Bindings Inspector.
Bind Selection Indexes to View Controller Model Key Path selectedIndexes.
Press ⌥⌘6 and connect the dataSource (by drag&drop) to the view controller () .
Select the text field File 1 in Table Cell View in the table column. The easiest way is to ⌃⇧click in the text field area.
Press ⌥⌘7 and bind Value to Table Cell View Model Key Path objectValue.name (!)
In the view controller populate the data source array in viewDidLoad ( I don't know that framework so I leave it out) and reload the table view.
override func viewDidLoad() {
super.viewDidLoad()
let dreamNames = ["Hit the gym", "Run daily", "Become a millionaire", "Become a better programmer", "Achieve your dreams"]
dreams = dreamNames.map{Dream(name: $0)}
table.reloadData()
}
Delete acceptsFirstResponder
Delete tableViewSelectionDidChange
Delete tableView:viewFor:row:
Add
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
return dreams[row]
}
Replace addNewDream with
func addNewDream() {
let last = dreams.count
dreams.append(Dream(name: "Double Click or Press Enter to Add Item"))
table.insertRows(at: IndexSet(integer: last), withAnimation: .effectGap)
table.scrollRowToVisible(last)
table.selectRowIndexes([last], byExtendingSelection: false)
saveDreams()
}
Replace removeDream() with
func removeDream() {
guard let selectedRow = selectedIndexes.first else { return }
dreams.remove(at: selectedRow)
table.removeRows(at: IndexSet(integer: selectedRow), withAnimation: .effectFade)
saveDreams()
}
To save the array when the text was edited afterwards you have to implement the delegate method controlTextDidEndEditing(_:)
override func controlTextDidEndEditing(_ obj: Notification) {
saveDreams()
}
and in Interface Builder connect the delegate of the text field in the table view to the view controller.
Novice coder and I'm stuck. I have 2 UITextField on my Viewcontroller that I want passed to a UITableView when "save" is tapped. I've set both textFields to delegate.self, the problem is that the data entered in the textfields only shows up on the UITableView IF I remove 1 of the UItextFields.
I'm thinking in order to use 2 (or more) UITextFields on the same VC I must need a way besides the outlets to differentiate between them. I've seen responses about tags but I don't understand.
import UIKit
class BudgetViewController: UIViewController, UITextFieldDelegate, UINavigationControllerDelegate {
// Properties:
#IBOutlet weak var datePicker: UIDatePicker!
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var amountTextField: UITextField!
#IBOutlet weak var dateDisplay: UILabel!
#IBOutlet weak var saveButton: UIBarButtonItem!
var budget: Budget?
// Date picker:
let dateFormatter = NSDateFormatter()
func setDate() {
dateFormatter.dateStyle = NSDateFormatterStyle.MediumStyle
dateDisplay.text = dateFormatter.stringFromDate(datePicker.date)
}
// Navigation
// This method lets you configure a view controller before it's presented
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if saveButton === sender {
let name = nameTextField.text ?? ""
let date = dateDisplay.text ?? ""
let amount = amountTextField.text ?? ""
// set the budget to be passed to the Controller, this code configures the meal prperty with the appropriate values before the segue executes
budget = Budget(date: date, name: name, amount: amount)
}
}
// Actions:
#IBAction func datePickerChanger(sender: AnyObject) {
setDate()
}
override func viewDidLoad() {
super.viewDidLoad()
// Handle the text field
nameTextField.delegate = self
amountTextField.delegate = self
}
// UITextFieldDelegate
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
func textFieldDidEndEditing(textField: UITextField) {
}
}
import UIKit
class BudgetTableViewController: UITableViewController {
//Properties
var budgets = [Budget]()
override func viewDidLoad() {
super.viewDidLoad()
loadSampleBudgets()
}
func loadSampleBudgets() {
let budget1 = Budget(date: "8/16/2016", name: "Eyebrows", amount: "15")!
let budget2 = Budget(date: "8/28/2016", name: "Acme", amount: "59")!
let budget3 = Budget(date: "9/10/2016", name: "Wildwood", amount: "199")!
budgets += [budget1, budget2, budget3]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return budgets.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "BudgetTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! BudgetTableViewCell
let budget = budgets[indexPath.row]
cell.dateLabel.text = budget.date
cell.nameLabel.text = budget.name
cell.amountLabel.text = budget.amount
return cell
}
#IBAction func unwindToMealList(sender: UIStoryboardSegue) {
if let sourceViewController = sender.sourceViewController as? BudgetViewController, budget = sourceViewController.budget {
//Add a new meal
let newIndexPath = NSIndexPath(forRow: budgets.count, inSection: 0)
budgets.append(budget)
tableView.insertRowsAtIndexPaths([indexPath], withRowanimation: .Bottom)
}
}
Check to see if your text fields outlets are properly linked to your storyboard. There should be a filled circle next to each IBOutlet. If you have both the code and storyboard open in XCode and hover your mouse over the circle next to each outlet, the text field should highlight on the storyboard.
If you copied and pasted the original name text field and changed it to the amount text field, it actually still might have a reference to the name outlet. You can check this by right clicking on the text field in the storyboard and see what outlets it refers to.
override func viewDidLoad() {
super.viewDidLoad()
nameTextField.delegate = self
streetTextField.delegate = self
cityTextField.delegate = self
stateTextField.delegate = self
countryTextField.delegate = self
phoneTextField.delegate = self
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
if textField == nameTextField {
streetTextField.becomeFirstResponder()
}
else if textField == streetTextField {
cityTextField.becomeFirstResponder()
}
else if textField == cityTextField {
stateTextField.becomeFirstResponder()
}
else if textField == stateTextField {
countryTextField.becomeFirstResponder()
}
else if textField == countryTextField {
phoneTextField.becomeFirstResponder()
}
else if textField == phoneTextField {
nameTextField.becomeFirstResponder()
}
return true
}
This is some code I wrote to allow a user to fill in a form (pressing return to move from one field to the next), so you can definitely have multiple textFields with the same delegate.