it is my first time using segment control and realm. so currently I am using segue to perform segue to Add/EditVC for modification of data.
when segue performed and took data to Add/EditVC to variable selectedTransaction, I want as following data to to be filled accordingly
/* selectedTransaction print statement Results */
print("selectedTransaction = \(selectedTransaction!)")
selectedTransaction = Transaction {
categoryType = Category {
type = expense;
name = EXPENSE 3;
};
amount = 1000;
date = April 2;
}
amountTF.text = selectedTransaction.amount (done and correct)
categorySCoutlet.selectedSegmentIndex = selectedTransaction.categoryType.type (fail to show segmentindex at selectedTransaction.categoryType.type)
categoryTF.text = selectedTransaction.categoryType.name (name shown correctly, however will return nil if user did not RESELECT again)
I was expecting all data are shown as if it created.
However, I faced 2 issue in getting it done
SegmentControl selected index are always in 0 instead of selectedTransaction.categoryType.name (I want the segment control to be at the index which is as created)
categoryTF.text are showing correctly, but if I did not choose again and leave as it be. It will auto return back to nil in results when I pressed the saveButton. : I wish categoryTF.text will return as created and value will not change even if I did not touch it and clicked saveButton
In gif shown below, I chosen row2 as sample. and the result in Realm Browser showing as it is.
I only changed the amountTF.text information from 1200 to 2000, which in result realm browser will set the result of cateogoryType in segmentcontrol to "income" and category will return to nil
sample of the workflow
//Data Model
//MARK: - Transaction Category Section
enum CategoryType : String, CaseIterable {
case income = "income"
case expense = "expense"
init?(id : Int) {
if id < CategoryType.allCases.count {
self = CategoryType.allCases[id]
} else {
return nil
}
}
}
class Category : Object {
#objc dynamic var type : String = CategoryType.income.rawValue
#objc dynamic var name : String = ""
// let parentCategory = LinkingObjects(fromType: Transaction.self, property: "ofCategory")
convenience init(type:CategoryType, name: String) {
self.init()
self.type = type.rawValue
self.name = name
}
}
/* VC that should read and load all data to required place */
//edit
var selectedTransaction : Transaction!
#IBOutlet weak var amountTF: UITextField!
//PickerView for keyboard
lazy var pickerView : UIPickerView = UIPickerView()
//Segment Control
#IBOutlet weak var categorySCoutlet: UISegmentedControl!
#IBOutlet weak var categoryTF: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
amountTF.text! = selectedTransaction.amount
categoryTF.text! = selectedTransaction.categoryType!.name
setupPicker()
}
#IBAction func categoryTypeSC(_ sender: UISegmentedControl) {
guard let type = CategoryType(id: sender.selectedSegmentIndex) else {
fatalError("error")
}
currentCategories = categories.filter("type == %#", type.rawValue)
categoryTF.text = currentCategories.first?.name
pickerView.reloadAllComponents()
pickerView.selectRow(1, inComponent: 0, animated: true)
}
//MARK:- Add Transaction Btn
#IBAction func addTransButtonTapped(_ sender: UIButton) {
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
if touches.first?.view == view {
categoryTF.resignFirstResponder()
}
}
//MARK:- Picker Helper
func setupPicker() {
currentCategories = categories.filter("type == %#", CategoryType.income.rawValue)
categoryTF.inputView = pickerView
pickerView.delegate = self
pickerView.dataSource = self
categorySCoutlet.setTitle("Income", forSegmentAt: 0)
categorySCoutlet.setTitle("Expense", forSegmentAt: 1)
categorySCoutlet.addTarget(self, action: #selector(categoryTypeSC(_:)), for: .valueChanged)
}
You are calling tableView.deselectRow(at: indexPath, animated: false) in your tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) method.
This results in returning the incorrect indexPath in prepareForSegue.
You should either remove the deselectRow call from didSelectRowAtIndexPath method,
or you should create a property to hold the value of indexPath.
Something like this:
// somewhere in your class
var selectedIndexPath: IndexPath?
then
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedRowForTrans : Transaction = getTransactions[indexPath.row]
selectedIndexPath = indexPath // set the value of selectedIndexPath
tableView.deselectRow(at: indexPath, animated: false)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "editTrans") {
if let indexPath = selectedIndexPath { // check if there is a valid indexPath
let editTransVC = segue.destination as! AddTransactionTableViewController
let selectedRowForTrans : Transaction = getTransactions[indexPath.row]
editTransVC.selectedTransaction = selectedRowForTrans
}
}
}
Hope this helps
Related
I want to save the value of filled button(●) into the array "q.answer[indexPath.row]" about each question "q.question[indexPath.row]).
currentQuizButtonIndex is currently renewed every time when ◯ changes to ● by tapping. However, I have no idea how to save in to variable q which is declared in TableViewController.
View Controller display
Code about QuizCell.swift (TableCell which is about 5 buttons and UIlabel.)
import UIKit
import Foundation
protocol QuizCellDelegate {
func quizCellDidChangeCurrentButtonIndex(_ cell: QuizCell, index: Int)
}
class QuizCell: UITableViewCell {
var currentQuizButtonIndex: Int = 0 {
didSet {
let value = self.currentQuizButtonIndex
self.updateCurrentQuizButton(value)
if let delegate = self.delegate {
delegate.quizCellDidChangeCurrentButtonIndex(self, index: value)
}
}
}
#IBOutlet weak var questionLabel: UILabel!
#IBOutlet var answerButtons: [UIButton]!
var delegate: QuizCellDelegate?
override func awakeFromNib() {
super.awakeFromNib()
//print("ここまできてるか確認")
// Initialization code
}
#IBAction func didTapQuizButton(_ sender: UIButton) {
if let index = self.answerButtons.firstIndex(of: sender){
self.currentQuizButtonIndex = index
delegate?.quizCellDidChangeCurrentButtonIndex(self, index: index)
print(index)
}
}
private func updateCurrentQuizButton(_ currentIndex: Int){
for (index, answerButton) in self.answerButtons.enumerated(){
if index == currentIndex {
answerButton.setTitle("●", for: .normal)
} else {
answerButton.setTitle("○", for: .normal)
}
}
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Code about View Controller
import UIKit
class AnswerQuizViewController: UIViewController, UITableViewDelegate {
var q: QuestionSeries!
#IBOutlet weak var quizTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
quizTableView.dataSource = self
quizTableView.delegate = self
// cell xibファイルを使うときは書く必要があるやつ。
// quizTableView.register(UINib(nibName: K.Cells.QuizCellNibName, bundle: nil), forCellReuseIdentifier: K.Cells.QuizCellIdentifier)
quizTableView.register(UINib(nibName: "QuizCell", bundle: nil), forCellReuseIdentifier: "QuizCellIdentifier")
// Do any additional setup after loading the view.
}
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
// override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// // Get the new view controller using segue.destination.
// // Pass the selected object to the new view controller.
//// if segue.identifier == K.Segue.checkResult {
//// let resultViewController = segue.destination as! ResultViewController
//// answerQuizViewController.q =
//// print(answerQuizViewController.q)
// }
}
// MARK: - quizTableViewのアレンジ
extension AnswerQuizViewController: UITableViewDataSource, QuizCellDelegate {
func quizCellDidChangeCurrentButtonIndex(_ cell: QuizCell, index: Int) {
if let indexPath = self.quizTableView.indexPath(for: cell){
self.q.question[indexPath.row].answer = index
print(index)
}else{
print("ここきてます")
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return q.question.count
//print(q.question.count)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let question = q.question[indexPath.row]
let cell = quizTableView.dequeueReusableCell(withIdentifier: K.Cells.QuizCellIdentifier, for: indexPath) as! QuizCell
cell.questionLabel.text = question.text
// print(question.text)
return cell
}
}
It is also helpful if you have any idea of implementing this by alternative way.
Thanks.
How about you create a static array and store your data into that array.
when the button is tapped you can append it into that static array.
Create a new file. Just a basic "Swift file".
struct structName {
static var qArray: [String] = []
}
Then append data by:
structName.q.append()
Finally get your data trough:
structName.q[index]
Sorry, I'm a noob,
I am a bit stuck. I have been researching this for awhile and cannot find anything to help.
So, my problems is:
I have a Table View controller with a bunch of Cells (Depending on users contact address book). These Cells contain the users contacts information (Name and #) users can select up to 3 cells (Contacts).
That all works fine, I just need to know how to get the name and # labels data from each cell so I can display that information in another View Controller (CAContactsList) when I press the "Done" button (which I'm also stumped with).
My Current Table View Controller Class:
class AddContactsListTableView: UITableViewController {
var contacts = [FetchedContact]()
override func viewDidLoad() {
super.viewDidLoad()
fetchContacts()
}
private func fetchContacts() {
print("Attempting to fetch contacts")
let store = CNContactStore()
store.requestAccess(for: .contacts) { (granted, error) in
if let error = error {
print("failed to request access", error)
return
}
if granted {
print("access granted")
let keys = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey]
let request = CNContactFetchRequest(keysToFetch: keys as [CNKeyDescriptor])
do {
try store.enumerateContacts(with: request, usingBlock: { (contact, stopPointer) in
print(contact.givenName)
self.contacts.append(FetchedContact(firstName: contact.givenName, lastName: contact.familyName, telephone: contact.phoneNumbers.first?.value.stringValue ?? ""))
})
} catch let error {
print("Failed to enumerate contact", error)
}
} else {
print("access denied")
}
}
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// return the number of rows
return contacts.count
}
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
//Max Number of contacts allowed to be selected
let limit = 3
if let selectedRows = tableView.indexPathsForSelectedRows {
if selectedRows.count == limit {
let alertController = UIAlertController(title: "Oops", message: "Sorry, but you are limited to only \(limit) Contacts", preferredStyle: UIAlertController.Style.alert)
alertController.addAction(UIAlertAction(title: "Ok", style: .default, handler: {action in}))
self.present(alertController, animated: true, completion: nil)
return nil
}
}
return indexPath
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "AddContactsCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? AddContactsCell
// Configure the cell...
cell?.NamesLabel.text = contacts[indexPath.row].firstName + " " + contacts[indexPath.row].lastName
cell?.NumberLabel.text = contacts[indexPath.row].telephone
return cell!
}
}
My Current Cell Class:
class AddContactsCell: UITableViewCell {
//Mark Properties
#IBOutlet weak var NamesLabel: UILabel!
#IBOutlet weak var NumberLabel: UILabel!
#IBOutlet weak var ButtonSelector: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// update UI with Checkmark when Selected
accessoryType = selected ? .checkmark : .none
}
}
And my Fetched Contacts Class
struct FetchedContact {
var firstName: String
var lastName: String
var telephone: String
}
Any help would be Greatly Appreciated!
Override the prepare(for segue: UIStoryboardSegue, sender: Any?) in the AddContactsListTableView class where you can pass the selected contacts to the next view controller.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Pass the selected object to the new view controller.
if let selectedRows = tableView.indexPathsForSelectedRows {
let selectedContacts = selectedRows.map{contacts[$0.row]}
let newVC = segue.destination as! NewViewController
newVC.contacts = selectedContacts
}
}
See this tutorial for more.
So basically you are already on the right track querying the table view's indexPathsForSelectedRows since it will contain the indices you need to filter your contacts for. The selected contacts should be:
guard let selectedIndices = tableView.indexPathsForSelectedRows else { return }
let selectedContacts = selectedIndices.map { contacts[$0.item] }
[Edit]: Use a more concise version
first i come from France so sorry for my english.
Second, I'm new in developpement and i have develop a code for add data and after show them with Core Data. it works.ok
but after i want update but i have a problem i don't know why i can't update my value. There is an error : "fatal error: unexpectedly found nil while unwrapping an Optional value"
i have try many solution since 1 week, but can't find the problem. Thanks if someone can help me ! even a little help :)
this is my code (swift 2.3) :
for show in table view :
import UIKit
import CoreData
class ProduitTableViewController: UITableViewController {
#IBOutlet var table: UITableView!
var produits = [NSManagedObject]()
func refreshStories(refreshControl: UIRefreshControl) {
produits.removeAll()
fetchData()
self.table.reloadData()
refreshControl.endRefreshing()
}
override func viewDidLoad() {
super.viewDidLoad()
self.fetchData()
self.table.addSubview(self.refreshControl!)
self.refreshControl?.addTarget(self, action: #selector(ProduitTableViewController.refreshStories(_:)), forControlEvents: UIControlEvents.ValueChanged)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func fetchData() {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
//2
let fetchRequest = NSFetchRequest(entityName: "Produits")
let sort = NSSortDescriptor(key:"dateAjout", ascending:true)
fetchRequest.sortDescriptors = [sort]
//3
do {
let results = try managedContext.executeFetchRequest(fetchRequest)
produits = results as! [NSManagedObject]
} catch let error as NSError {
print("Donnees non recu \(error), \(error.userInfo)")
}
}
// 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 {
// #warning Incomplete implementation, return the number of rows
return self.produits.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell")
let produit = produits[indexPath.row]
cell!.textLabel!.text = produit.valueForKey("nom") as? String
/*
let id = produit.valueForKey("id") as? String
let date = produit.valueForKey("date") as? NSDate
let localNotification = UILocalNotification()
localNotification.userInfo = ["id" : id!]
localNotification.soundName = UILocalNotificationDefaultSoundName
localNotification.alertBody = "expiré"
localNotification.fireDate = date
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
UIApplication.sharedApplication().applicationIconBadgeNumber += 1
*/
return cell!
}
override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
let supprimer = UITableViewRowAction(style: .Normal, title: "Suppr.") { action, index in
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let moc = appDelegate.managedObjectContext
// 3
moc.deleteObject(self.produits[indexPath.row])
appDelegate.saveContext()
// 4
self.produits.removeAtIndex(indexPath.row)
tableView.reloadData()
}
supprimer.backgroundColor = UIColor.redColor()
let update = UITableViewRowAction(style: .Normal, title: "Modifier") { action, index in
}
update.backgroundColor = UIColor.blueColor()
return [supprimer]
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// the cells you would like the actions to appear needs to be editable
return true
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "showDetail" {
if let destination = segue.destinationViewController as? DetailViewController {
let row = table.indexPathForSelectedRow?.row
let produit = produits[row!]
let nom = produit.valueForKey("nom") as? String
let id = produit.valueForKey("id") as? String
let detail = produit.valueForKey("detail") as? String
let date = produit.valueForKey("date") as? NSDate
let time = date
let formatter = NSDateFormatter()
formatter.dateFormat = "dd-MM-YY HH:mm"
let formatteddate = formatter.stringFromDate(time!)
destination.dataNom = nom!
destination.dataId = id!
destination.dataDetail = detail!
destination.dataDate = formatteddate
}
}
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if table.cellForRowAtIndexPath(indexPath) != nil {
self.performSegueWithIdentifier("showDetail", sender: self)
}
}
}
for show detail of the cell :
import CoreData
import UIKit
class DetailViewController: UIViewController {
#IBOutlet var Label: UILabel!
#IBOutlet var Detail: UITextView!
#IBOutlet weak var Date: UILabel!
#IBOutlet weak var Id: UILabel!
var dataNom = ""
var dataDetail = ""
var dataDate = ""
var dataId = ""
override func viewDidLoad() {
super.viewDidLoad()
Label.text = dataNom
Detail.text = dataDetail
Date.text = dataDate
Id.text = dataId
// Do any additional setup after loading the view
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "ModifierDetail" {
if let destination = segue.destinationViewController as? ModifierViewController {
destination.modifierNom = dataNom
destination.modifierId = dataId
destination.modifierDetail = dataDetail
destination.modifierDate = dataDate
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
And the last for update/modify my detail:
import UIKit
import CoreData
class ModifierViewController: UIViewController {
#IBOutlet weak var Nom: UITextField!
#IBOutlet weak var Detail: UITextView!
#IBOutlet weak var Date: UITextField!
var Produits: NSManagedObject!
var managedContext: NSManagedObjectContext!
var modifierNom = ""
var modifierDetail = ""
var modifierDate = ""
var modifierId = ""
override func viewDidLoad() {
super.viewDidLoad()
Nom.text = modifierNom
Detail.text = modifierDetail
Date.text = modifierDate
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func Annuler(sender: UIBarButtonItem) {
navigationController?.popViewControllerAnimated(true)
}
#IBAction func Modifier(sender: UIButton) {
let fetchRequest = NSFetchRequest(entityName:"Produits")
fetchRequest.predicate = NSPredicate(format: "nom = %#", modifierNom)
do {
let list = try managedContext.executeFetchRequest(fetchRequest) as! [Produit]
if list.count == 0 // Check notificationId available then not save
{
let newManagedObject = NSEntityDescription.insertNewObjectForEntityForName("Produits", inManagedObjectContext: managedContext)
newManagedObject.setValue(modifierNom, forKey: "nom")
}
// success ...
} catch let error as NSError {
// failure
print("Fetch failed: \(error.localizedDescription)")
}
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
i have create this other file:
import Foundation
import CoreData
#objc(Produit)
class Produit: NSManagedObject {
#NSManaged var nom:String!
}
As you mentioned in the comment, this line causes the crash:
let list = try managedContext.executeFetchRequest(fetchRequest) as! [Produit]
That line is not safe, because you don't know, if casting to [Prodiut]will always be successful.
In general, you should never force-cast (as!) or force-unwrap (!) something when you don't know 1000%, that it will succeed.
To cast safely, you can use guard:
guard let list = try managedContext.executeFetchRequest(fetchRequest) as! [Produit] else {
//do some error handling here and then return
return
}
After that, you can safely use list.
It's really important to understand, what optionals are and how to handle them safely without crashes.
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.
I'm currently working on an inventory app that will hold information for two entities. The first entity holds two attributes "site name" and "site address". second entity holds "itemName" "itemQuantity" and "item picture". I'm able to save the data to core data. I'm having issues with fetching the data right now. Does any one know how to pass data between the view controllers.
I'm uploading the site view controller, detail view controller, and my new item view controller.
class SiteTableViewController: UIViewController {
var managedContext : NSManagedObjectContext = CoreDataHelper.sharedInstance.myContext
var sites : [Site]? = [Site]()
//variable to hold selected site
var selectedSite : Site?
let myCellIdentifier = "cell"
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
// Do any additional setup after loading the view.
sites = [Site]()
reloadData()
automaticallyAdjustsScrollViewInsets = false
//let barButtonItem = UIBarButtonItem(barButtonSystemItem: .Compose, target: self, action: "addSite:")
//self.navigationItem.leftBarButtonItems = [barButtonItem]
}
//load new data into table
func reloadData() {
let fetchRequest = NSFetchRequest(entityName: "Site")
let sortDescriptor = NSSortDescriptor(key: "siteName", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
do {
sites = try self.managedContext.executeFetchRequest(fetchRequest) as! [Site]
tableView.reloadData()
} catch {
print("Error fetching objects")
}
tableView.reloadData()
}
override func viewWillAppear(animated: Bool) {
reloadData()
}
}
extension SiteTableViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(myCellIdentifier, forIndexPath: indexPath)
let site = sites![indexPath.row]
cell.textLabel?.text = site.valueForKey("siteName") as? String
cell.detailTextLabel?.text = site.valueForKey("siteAddress") as? String
return cell
}
func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
let logItemToDelete = sites![indexPath.row]
sites?.removeAtIndex(indexPath.row)
managedContext.deleteObject(logItemToDelete)
do {
try managedContext.save()
} catch {
print("Could not save or delete object")
}
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sites!.count
}
// func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
//
// selectedSite = sites![indexPath.row]
//
// }
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "detailSegue" {
var nextVC = segue.destinationViewController as! DetailTableViewController
var selectedItem : Site = sites![self.tableView.indexPathForSelectedRow!.row] as Site
fetchSite = selectedItem.valueForKey("siteName") as? NSManagedObject
nextVC.valueToPass = selectedItem.siteName!
}
}
}
here is my detail view controller
import UIKit
import CoreData
public var fetchSite : NSManagedObject?
class DetailTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var holdSiteData: NSMutableArray = NSMutableArray()
var managedContext : NSManagedObjectContext = CoreDataHelper.sharedInstance.myContext
#IBOutlet weak var tableView: UITableView!
var sites : [Site]!
//string to hold fetchResults controll
//open string to hold siteName
var valueToPass = ""
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "\(valueToPass)"
// Do any additional setup after loading the view.
tableView.delegate = self
tableView.dataSource = self
automaticallyAdjustsScrollViewInsets = false
tableView.estimatedRowHeight = 75.0
tableView.rowHeight = UITableViewAutomaticDimension
retrieveItemRelationship()
}
override func viewWillAppear(animated: Bool) {
retrieveItemRelationship()
}
func retrieveItemRelationship() {
// fetchSite.setValue(NSSet(keyCommands), forKey: "test")
let fetchRequest = NSFetchRequest(entityName: "SiteItem")
let sortDescriptors = NSSortDescriptor(key: "itemName", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptors]
do {
let result = try fetchSite?.managedObjectContext?.executeRequest(fetchRequest)
} catch let error as NSError {
print(error)
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! TableViewCell
// let cellInfo = fetchedResultsController.objectAtIndexPath(indexPath) as! SiteInfo
cell.cellName?.text = "Test"
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return holdSiteData.count
}
}
this is my last view to save the data to. I figured i need to pass the data here then associate the two entities so that I can save it correctly. For instance, for site name "Staples Center". I need to pull this over to the final view controller then save the information to this object.
import UIKit
import CoreData
class ItemNewViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
//var managedObjectContext : NSManagedObjectContext!
var managedContext : NSManagedObjectContext = CoreDataHelper.sharedInstance.myContext
//categories array for picker view
var categories = ["Building Materials", "Electrical", "Cleaning Products",
"Tools & Hardware", "Plumbing", "Paint", "Appliances", "Other"]
//textFields
#IBOutlet weak var itemNameTextField: UITextField!
#IBOutlet weak var quantityTextField: UITextField!
#IBOutlet weak var imageView: UIImageView!
//Label properties
#IBOutlet weak var newItemLabel: UILabel!
#IBOutlet weak var quantityLabel: UILabel!
#IBOutlet weak var categoryLabel: UILabel!
//pickerLabel
#IBOutlet weak var pickerListIcon: UIPickerView!
//clicker property label and textlabel for number
#IBOutlet weak var increaseNumberClicker: UIStepper!
#IBOutlet weak var numberFieldClicker: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
//label property customize view code
newItemLabel.layer.masksToBounds = true
newItemLabel.layer.borderWidth = 2.0
newItemLabel.layer.borderColor = UIColor.whiteColor().CGColor
newItemLabel.layer.cornerRadius = 6
//label property customize view code
quantityLabel.layer.masksToBounds = true
quantityLabel.layer.borderWidth = 2.0
quantityLabel.layer.borderColor = UIColor.whiteColor().CGColor
quantityLabel.layer.cornerRadius = 6
//lable property customize view code
categoryLabel.layer.masksToBounds = true
categoryLabel.layer.borderWidth = 3.0
categoryLabel.layer.borderColor = UIColor.whiteColor().CGColor
categoryLabel.layer.cornerRadius = 6
//label picker customize view
pickerListIcon.layer.masksToBounds = true
pickerListIcon.layer.borderWidth = 3.0
pickerListIcon.layer.borderColor = UIColor.whiteColor().CGColor
pickerListIcon.layer.cornerRadius = 6
pickerListIcon.layer.backgroundColor = UIColor.blackColor().CGColor
self.pickerListIcon.dataSource = self
self.pickerListIcon.delegate = self
numberFieldClicker.enabled = false
//UIStepper value
increaseNumberClicker.wraps = true
increaseNumberClicker.autorepeat = true
increaseNumberClicker.layer.masksToBounds = true
increaseNumberClicker.layer.borderWidth = 3.0
increaseNumberClicker.layer.borderColor = UIColor.whiteColor().CGColor
increaseNumberClicker.layer.cornerRadius = 6
increaseNumberClicker.layer.backgroundColor = UIColor.blackColor().CGColor
//below code declares a variable that inherits from UITapGesture
//this allows the image view to be an action
//*Make sure you enable user interaction in storyBoard very IMPORTANT!
let imageTapRecognizer = UITapGestureRecognizer(target: self, action: "enableImage")
self.imageView.addGestureRecognizer(imageTapRecognizer)
}
// below function allows the camera view to be accessible
func enableImage(){
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera) {
let cameraView = UIImagePickerController()
cameraView.sourceType = UIImagePickerControllerSourceType.Camera
cameraView.delegate = self
self.presentViewController(cameraView, animated: true, completion: nil)
}
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) {
self.imageView.image = image
picker.dismissViewControllerAnimated(true, completion: nil)
}
//function used to increase UIStepper. increment by integers of one
#IBAction func clickerIncreaser(sender: UIStepper) {
numberFieldClicker.text = Int(sender.value).description
}
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return categories.count
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return categories[row]
}
func pickerView(pickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? {
//passing the categorories array into the new constant myCategoriesArray
let myCategoriesArray = categories[row]
let myTextColor = NSAttributedString(string: myCategoriesArray, attributes: [NSForegroundColorAttributeName: UIColor.whiteColor()])
return myTextColor
}
//save data to core data
#IBAction func saveButton(sender: AnyObject) {
if (itemNameTextField.text?.isEmpty)! {
let displayAlertController = UIAlertController(title: "Form Incomplete", message: "Please provide item with a name", preferredStyle: .Alert)
let okButton : UIAlertAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
displayAlertController.addAction(okButton)
self.presentViewController(displayAlertController, animated: true, completion: nil)
} else {
//declare site object and object
let siteInfoEntity = NSEntityDescription.entityForName("SiteItem", inManagedObjectContext: self.managedContext)
//declare item and item object
let siteObject = SiteItem(entity: siteInfoEntity!, insertIntoManagedObjectContext: self.managedContext)
//convert String to Integer
let quantityNumber: Int? = Int(quantityTextField.text!)
siteObject.setValue(itemNameTextField.text?.capitalizedString, forKey: "itemName")
siteObject.setValue(quantityNumber, forKey: "itemQnty")
fetchSite?.setValue(NSSet(object: siteObject), forKey: "reverseItems")
self.managedContext.saveOrLogError()
self.navigationController?.popViewControllerAnimated(true)
}
}
}
Once again if someone can point me in the right direction for saving my relationships to core data and retrieving these relationships would be great. Still new to core data so need all the help i can get!
Thank you
Your detail controller can have a property Site. In prepareForSegue you pass the selected Site object to the detail controller.
Both the site list and the list of items should be backed by NSFetchedResultsController, not in-memory arrays.