The top of tableView has some empty area - ios

The issue occurs as the pic .
at the top of tableView has some empty area
The interface is that viewController(embed in navigationController).I put one tableView on it.And construct its constraints.But run the code ,and the issue occurs .I wanna try to use two methods to solve it ,but no any changes.
self.automaticallyAdjustsScrollViewInsets = true
I adjust the constraints of tableView to superView,I set the constraint of top to -100,it works well.
The code as below:
import UIKit
import CoreData
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var people:[NSManagedObject] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.title = "My book"
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName:"Person")
do {
people = try managedContext.fetch(fetchRequest)
}catch let error as NSError {
print("Cannot fetch \(error),\(error.userInfo)")
}
}
#IBAction func addName(_ sender: AnyObject) {
let alert = UIAlertController(title: "New name", message: "Add a name", preferredStyle: .alert)
let saveAction = UIAlertAction(title: "Save", style: .default) { [unowned self] action in
guard let textField = alert.textFields?.first,
let nameToSave = textField.text else {return
}
self.save(name: nameToSave)
self.tableView.reloadData()
}
let cancelAction = UIAlertAction(title: "Cancel", style: .default)
alert.addTextField()
alert.addAction(saveAction)
alert.addAction(cancelAction)
present(alert,animated: true)
}
func save(name:String) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "Person", in: managedContext)
let person = NSManagedObject(entity: entity!, insertInto: managedContext)
person.setValue(name, forKey: "name")
do {
try managedContext.save()
people.append(person)
}catch let error as NSError {
print("Could not save\(error),\(error.userInfo)")
}
}
}
extension ViewController :UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return people.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let person = people[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = person.value(forKey: "name") as? String
return cell
}
}

You should put constraint for the tableView from storyboard and set top space = 0

You can also try setting the edgesForExtendedLayout in your viewDidLoad method:
self.edgesForExtendedLayout = []

try to implement
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return .min
}

Related

How to update Core Data assigned to indexpath in Custom cell via button

I have an app of type "to do list" where I want to check my item on the list with custom button checkmark. Structure of app looks like that: main list uses UITableViewController which has custom TableViewCell with UIButton "checkmarkButton" and Label "cellLabel". Their values are saved to core data after creating, with checkmarkButton default state as false, and are assigned to a cell in cellForRowAtindexPath using value(forKeyPath:). In TableViewCell class I have a checkmarkBtnTapped function which changes displayed image for a button (check/uncheck), tint this image to a specified color, and update button "state" as bool in CoreData attribute for its key path, fetch CoreData and reloadTableView. Some functions that use my list array and other stuff from core data, or table view come from UITableViewController so I implemented delegate for them.
The problem is when I tap the checkmarkButton and it uses updateBtnState new row is created with changed state (i.e. i have tapped row with button in state "false", my actual row with label is still on "false" and new row is added with empty label and button in state "true") i guess this is due to updateBtnState() method that reference only to managedObjectContext and not to indexPath. But when I try to reference item as a point of indexPath and not NSManagedObject i cannot pass this function to TableViewCell class due to IndexPath parameter. Below in TableViewController.swift i left updateBtnState2() that i think could solve my problem but is unusable in TableViewCell checkmarkBtnTapped() function
TableViewController.swift
import UIKit
import CoreData
class TableViewController: UITableViewController, ButtonSelectionDelegate {
var list: [NSManagedObject] = []
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "List"
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addTapped))
let cellNib = UINib(nibName: "TableViewCell", bundle: nil)
self.tableView.register(cellNib, forCellReuseIdentifier: "cell")
}
override func viewWillAppear(_ animated: Bool) {
UIApplication.shared.statusBarStyle = .lightContent
fetch()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
UIApplication.shared.statusBarStyle = UIStatusBarStyle.default
}
func save(name: String, state: Bool) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedObjectContext = appDelegate.persistentContainer.viewContext
let entity =
NSEntityDescription.entity(forEntityName: "Item", in: managedObjectContext)!
let Item = NSManagedObject(entity: entity, insertInto: managedObjectContext)
Item.setValue(name, forKeyPath: "name")
Item.setValue(state, forKeyPath: "isChecked")
do{
try managedObjectContext.save()
list.append(Item)
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
func fetch(){
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedObjectContext = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Item")
do{
list = try managedObjectContext.fetch(fetchRequest)
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
}
func updateBtnState(state: Bool){
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedObjectContext = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "Item", in: managedObjectContext)!
let Item = NSManagedObject(entity: entity, insertInto: managedObjectContext)
Item.setValue(state, forKeyPath: "isChecked")
do{
try managedObjectContext.save()
} catch let error as NSError {
print("Couldnt update. \(error)")
}
}
func updateBtnState2(indexPath: IndexPath, state: Bool){
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedObjectContext = appDelegate.persistentContainer.viewContext
let item = list[indexPath.row]
item.setValue(state, forKeyPath: "isChecked")
do{
try managedObjectContext.save()
list[indexPath.row] = item
} catch let error as NSError {
print("Couldnt update. \(error)")
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return(list.count)
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
let item = list[indexPath.row]
cell.selectionDelegate = self
cell.cellLabel.text = item.value(forKeyPath: "name") as? String
cell.checkmarkButton.isSelected = item.value(forKeyPath: "isChecked") as! Bool
return cell
}
func updateTableView(){
tableView.reloadData()
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
#objc func addTapped(){
let alert = UIAlertController(title: "New Name", message: "Add a new name", preferredStyle: .alert)
let saveAction = UIAlertAction(title: "Save", style: .default) {
[unowned self] action in
guard let textField = alert.textFields?.first,
let nameToSave = textField.text else {
return
}
self.save(name: nameToSave, state: false)
self.tableView.reloadData()
}
let cancelAction = UIAlertAction(title: "Cancel", style: .default)
alert.addTextField()
alert.addAction(cancelAction)
alert.addAction(saveAction)
present(alert, animated: true)
}
TableViewCell.swift
import UIKit
protocol ButtonSelectionDelegate: class {
func fetch()
func updateTableView()
func updateBtnState(state: Bool)
}
class TableViewCell: UITableViewCell {
weak var selectionDelegate: ButtonSelectionDelegate!
#IBOutlet var checkmarkButton: UIButton!
#IBOutlet var cellLabel: UILabel!
#IBAction func checkmarkBtnTapped(_ sender: UIButton) {
sender.isSelected = !sender.isSelected
if sender.isSelected {
selectionDelegate?.updateBtnState(state: true)
let image: UIImage? = UIImage(named: "done_icon.png")?.withRenderingMode(.alwaysTemplate)
checkmarkButton.setImage(image, for: .normal)
checkmarkButton.tintColor = UIColor( red: CGFloat(21/255.0), green: CGFloat(126/255.0), blue: CGFloat(251/255.0), alpha: CGFloat(1.0))
selectionDelegate?.fetch()
selectionDelegate?.updateTableView()
print("checkmarkButton pressed to done")
} else {
selectionDelegate?.updateBtnState(state: false)
let image: UIImage? = UIImage(named: "undone_icon.png")?.withRenderingMode(.alwaysTemplate)
checkmarkButton.setImage(image, for: .normal)
checkmarkButton.tintColor = UIColor.gray
selectionDelegate?.fetch()
selectionDelegate?.updateTableView()
print("checkmarkButton pressed to undone")
}
}
override func layoutSubviews() {
super.layoutSubviews()
let image: UIImage? = UIImage(named: "undone_icon.png")?.withRenderingMode(.alwaysTemplate)
checkmarkButton.setImage(image, for: .normal)
checkmarkButton.tintColor = UIColor.gray
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
}
extension UIButton {
func hasImage(named imageName: String, for state: UIControlState) -> Bool {
guard let buttonImage = image(for: state), let namedImage = UIImage(named: imageName) else {
return false
}
return UIImagePNGRepresentation(buttonImage) == UIImagePNGRepresentation(namedImage)
}
}
The best way to work with table and collection view cells is to have the cell take care of all configuration of its UI. To do this you pass the data in to the cell and it then puts the right data in the right data formatted as desired. You already have a custom UITableViewCell so this will be pretty easy to do…
// Make the to-do item's property names into strings so you can't mistype them later.
// The other option would be to create the subclass of NSManagedObject so the properties are directly accessible.
let isChecked = "isChecked"
let name = "name"
class TableViewCell: UITableViewCell {
// Add a property to hold the actual to-do item
var item: NSManagedObject? {
didSet {
updateCell()
}
}
// Make all outlets private so you aren't tempted to touch them from outside
#IBOutlet private var checkmarkButton: UIButton!
#IBOutlet private var cellLabel: UILabel!
// Create both images once for each cell rather than every time the image changes
let doneImage = UIImage(named: "done_icon.png")?.withRenderingMode(.alwaysTemplate)
let notDoneImage = UIImage(named: "undone_icon.png")?.withRenderingMode(.alwaysTemplate)
let doneColor = UIColor( red: CGFloat(21/255.0), green: CGFloat(126/255.0), blue: CGFloat(251/255.0), alpha: CGFloat(1.0))
let notDoneColor = UIColor.gray
private func updateCell() {
guard let item = item else { return }
cellLabel?.text = item.value(forKeyPath: name) as? String
checkmarksButton?.isSelected = item.value(forKeyPath: isChecked) as! Bool
}
#IBAction private func checkmarkBtnTapped(_ sender: UIButton) {
// Safely unwrap the to-do item
guard let item = item else { return }
sender.isSelected = !sender.isSelected
let selected = sender.isSelected
item.setValue(selected, forKeyPath: "isChecked")
checkmarkButton.setImage(selected ? doneImage : notDoneImage, for: .normal)
checkmarkButton.tintColor = selected ? doneColor : notDoneColor
print("checkmarkButton pressed to \(selected ? "done" : "undone")")
}
…
}
This way the cell can update the managed object directly rather than trying to reconnect to it through the view controller.
Also, the code in layoutSubviews shouldn't really be needed but if it is awakeFromNib is the better place to put it.
Once you cell is done you can get rid of those update button functions and change cellForRowAt to…
class TableViewController: UITableViewController {
…
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
let item = list[indexPath.row]
cell.item = item
return cell
}
…
}

Value of type 'Item' has no member 'parentCategory'

I have 2 entities
I'm using XCode 10 right now, I am not sure if I did something wrong or XCode bug.
I added 1 line into these lines
let newItem = Item(context: self.context)
newItem.title = textField.text!
newItem.done = false
newItem.parentCategory = self.selectedCategory <--------- ADD HERE
self.itemArray.append(newItem)
self.saveItems()
Any hints for me on why this is happening ?
TodoListVC
//
// TodoListVC
// ListHue
// Copyright © 2018 LR Web Design. All rights reserved.
//
import UIKit
import CoreData
class TodoListVC: UITableViewController {
var itemArray = [Item]()
var selectedCategory : Category? {
didSet {
loadItems()
}
}
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
// ---------------------------------------------------------------------------------------------------------
//MARK - viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
print(FileManager.default.urls(for: .documentDirectory, in: .userDomainMask))
}
// ---------------------------------------------------------------------------------------------------------
//MARK - Datasource
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return itemArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "listItemCell", for: indexPath)
let item = itemArray[indexPath.row]
cell.textLabel?.text = item.title
cell.accessoryType = item.done == true ? .checkmark : .none
return cell
}
// ---------------------------------------------------------------------------------------------------------
//MARK - Delegate
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
context.delete(itemArray[indexPath.row])
itemArray.remove(at: indexPath.row)
itemArray[indexPath.row].done = !itemArray[indexPath.row].done
self.saveItems()
tableView.deselectRow(at: indexPath, animated: true)
}
// ---------------------------------------------------------------------------------------------------------
//MARK - Add new item
#IBAction func addButtonPressed(_ sender: UIBarButtonItem) {
var textField = UITextField()
let alert = UIAlertController(title: "Add New Item", message: "", preferredStyle: .alert)
//action
let action = UIAlertAction(title: "Add Item", style: .default) { (action) in
let newItem = Item(context: self.context)
newItem.title = textField.text!
newItem.done = false
newItem.parentCategory = self.selectedCategory
self.itemArray.append(newItem)
self.saveItems()
}
alert.addTextField { (alertTextField) in
alertTextField.placeholder = "Create new item"
textField = alertTextField
}
alert.addAction(action)
present(alert, animated: true, completion: nil)
}
// ---------------------------------------------------------------------------------------------------------
//MARK - Model Manipulation Methods
func saveItems() {
do {
try context.save()
} catch {
print("Error saving context, \(error)")
}
self.tableView.reloadData()
}
func loadItems(with request: NSFetchRequest<Item> = Item.fetchRequest()) {
let predicate = NSPredicate(format: "parentCategory.name MATCHES %#", selectedCategory?.name!)
request.predicate = predicate
do {
itemArray = try context.fetch(request)
} catch {
print("Error fetching data from the context, \(error)")
}
self.tableView.reloadData()
}
}
//MARK: - Search bar methods
extension TodoListVC : UISearchBarDelegate {
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
let request : NSFetchRequest<Item> = Item.fetchRequest()
request.predicate = NSPredicate(format: "title CONTAINS[cd] %#", searchBar.text!)
request.sortDescriptors = [NSSortDescriptor(key: "title", ascending: true)]
loadItems(with: request)
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text?.count == 0 {
loadItems()
DispatchQueue.main.async {
searchBar.resignFirstResponder()
}
}
}
}
CategoryVC
//
// CategoryVC.swift
// ListHue
// Copyright © 2018 LR Web Design. All rights reserved.
//
import UIKit
import CoreData
class CategoryVC: UITableViewController {
var categories = [Category]()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
override func viewDidLoad() {
super.viewDidLoad()
loadCategories()
}
// ---------------------------------------------------------------------------------------------------------
//MARK - Table View Datasource
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return categories.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CategoryCell", for: indexPath)
let category = categories[indexPath.row]
cell.textLabel?.text = category.name
return cell
}
// ---------------------------------------------------------------------------------------------------------
//MARK - Table View Delegate Methods
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
performSegue(withIdentifier: "goToItems", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destinationVC = segue.destination as! TodoListVC
//get the category of the selected cell
if let indexPath = tableView.indexPathForSelectedRow {
//set the property
destinationVC.selectedCategory = categories[indexPath.row]
}
}
// ---------------------------------------------------------------------------------------------------------
//MARK - Data Manipulation Methods
#IBAction func addButtonPressed(_ sender: UIBarButtonItem) {
print("click")
var textField = UITextField()
let alert = UIAlertController(title: "Add New Category", message: "", preferredStyle: .alert)
//action
let action = UIAlertAction(title: "Add Category", style: .default) { (action) in
let newCategory = Category(context: self.context)
newCategory.name = textField.text!
self.categories.append(newCategory)
self.saveCategories()
}
alert.addTextField { (alertTextField) in
alertTextField.placeholder = "Create new item"
textField = alertTextField
}
alert.addAction(action)
present(alert, animated: true, completion: nil)
}
// ---------------------------------------------------------------------------------------------------------
//MARK - Add New Categories
func saveCategories() {
do {
try context.save()
} catch {
print("Error saving context, \(error)")
}
self.tableView.reloadData()
}
func loadCategories(with request: NSFetchRequest<Category> = Category.fetchRequest()) {
do {
categories = try context.fetch(request)
} catch {
print("Error fetching data from the context, \(error)")
}
self.tableView.reloadData()
}
}
I got the diagram backward, thanks to #Larme, and #Ladislav helped me to know that.

Edit the contents list of TableViewCell in Swift

I have a TableViewController with lists of data entry using CoreData. I also have ViewController with UITextView as linked by Segue to TableViewController. I have completed operations like add, save and delete on items.
Now I am working on edit by linking the selected TableViewCell to UITextView on next ViewController.I have created an action DONE on ViewController.I am able to pass data from selected Cell to TextView of next ViewController using Segue.
Now, I need to update the edited text on UITextView by clicking DONE button to back to Cells of TableViewController and also save data to CoreData. Plzz give the genuine advices and suggestions....I am new to Swift and I have enclosed my code here.
import UIKit
import CoreData
class ToDoTableViewController: UITableViewController {
var listItems = [NSManagedObject]()
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.add, target: self , action: #selector(ToDoTableViewController.addItem))
}
func addItem(){
let alertController = UIAlertController(title: "To Do Tasks Lists!!!!", message: "Write Down...", preferredStyle: .alert)
let confirmAction = UIAlertAction(title: "Confirm", style: UIAlertActionStyle.default, handler: ({
(_) in
if let field = alertController.textFields![0] as? UITextField {
self.saveItem(itemToSave: (field.text!))
self.tableView.reloadData()
}
}
))
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: nil)
alertController.addTextField(configurationHandler: ({
(textField) in
textField.placeholder = "Type in Something!!!!"
}))
alertController.addAction(confirmAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}
func saveItem(itemToSave : String){
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let entity = NSEntityDescription.entity(forEntityName: "ListEntity", in: managedContext)
let item = NSManagedObject(entity: entity!, insertInto: managedContext)
item.setValue(itemToSave, forKey: "item")
do {
try managedContext.save()
listItems.append(item)
}
catch {
print("Error")
}
}
override func viewWillAppear(_ animated: Bool) {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "ListEntity")
do{
let results = try managedContext.fetch(fetchRequest)
listItems = results as! [NSManagedObject]
}
catch {
print("Error")
}
}
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let objectToDelete = listItems[indexPath.row]
listItems.remove(at: indexPath.row)
managedContext.delete(objectToDelete)
tableView.deleteRows(at: [indexPath], with: .fade)
do {
try managedContext.save()
}
catch {
print("Error")
}
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return listItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")! as UITableViewCell
let item = listItems[indexPath.row]
cell.textLabel?.text = item.value(forKey: "item") as! String?
cell.backgroundColor = UIColor.clear
return cell
}
func getIndexPathForSelectedCell() -> IndexPath?
{
var indexPath2:IndexPath?
if tableView.indexPathsForSelectedRows!.count > 0 {
indexPath2 = tableView.indexPathsForSelectedRows![0]
}
return indexPath2
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "detailView")
{
if let indexPath2 = getIndexPathForSelectedCell()
{
// here write code for move to next controller.
let vc = segue.destination as! TextEditViewController
//vc.FirstString = listItems[(indexPath2 as NSIndexPath).row] as String
let item = listItems[(indexPath2 as NSIndexPath).row]
vc.FirstString = (item.value(forKey: "item") as! String?)!
}
}
}
}
//TextEditViewController
import UIKit
import CoreData
class TextEditViewController: UIViewController {
#IBOutlet weak var textEdit: UITextView!
var FirstString = String()
override func viewDidLoad() {
super.viewDidLoad()
textEdit.text = FirstString
print(self.FirstString)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Pass your CoreData model reference to next screen:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "detailView")
{
if let indexPath2 = getIndexPathForSelectedCell()
{
// here write code for move to next controller.
let vc = segue.destination as! TextEditViewController
//vc.FirstString = listItems[(indexPath2 as NSIndexPath).row] as String
let item = listItems[(indexPath2 as NSIndexPath).row]
vc.FirstString = (item.value(forKey: "item") as! String?)!
vc.recentItem = item; //Like this. Create a recentItem refrence to your next controller of NSManagedObject or your custom core data modal.
}
}
}
When you are done with your editing in TextEditViewController:
recentItem.item = "String" //Your updated string
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
do {
try managedContext.save()
}
catch {
print("Error")
}
Hope this will work...!!

Segue not going on Table Cell click

I am having a problem with my TableCell Segue.
I have setup a Segue with the identifier ShowAssesment on the prototype cell to Show a navigation controller connected to a table view, but when i click a cell it just highlights it and nothing happens.
class ViewController: UIViewController, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var subjects = [NSManagedObject]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.registerClass(UITableViewCell.self,
forCellReuseIdentifier: "Cell")
}
func tableView(tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return subjects.count
}
func tableView(tableView: UITableView,
cellForRowAtIndexPath
indexPath: NSIndexPath) -> UITableViewCell {
let cell =
tableView.dequeueReusableCellWithIdentifier("Cell")
let check = subjects[indexPath.row]
cell!.textLabel!.text =
check.valueForKey("name") as? String
return cell!
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func addName(sender: AnyObject) {
let alert = UIAlertController(title: "New Subject", message: "Add a new Subject", preferredStyle: .Alert)
let saveAction = UIAlertAction(title: "Save",
style: .Default,
handler: { (action:UIAlertAction) -> Void in
let textField = alert.textFields!.first
self.saveName(textField!.text!)
self.tableView.reloadData()
})
let cancelAction = UIAlertAction(title: "Cancel",
style: .Default) { (action: UIAlertAction) -> Void in
}
alert.addTextFieldWithConfigurationHandler {
(textField: UITextField) -> Void in
}
alert.addAction(saveAction)
alert.addAction(cancelAction)
presentViewController(alert,
animated: true,
completion: nil)
}
func saveName(name: String) {
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let entity = NSEntityDescription.entityForName("Subjects",
inManagedObjectContext:managedContext)
let check = NSManagedObject(entity: entity!,
insertIntoManagedObjectContext: managedContext)
check.setValue(name, forKey: "name")
do {
try managedContext.save()
subjects.append(check)
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
}
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "Subjects")
do {
let results =
try managedContext.executeFetchRequest(fetchRequest)
subjects = results as! [NSManagedObject]
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle,forRowAtIndexPath indexPath: NSIndexPath) {
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "ShowAssesment") {
let indexPath = tableView.indexPathForCell((sender as? UITableViewCell)!)
let listVC = segue.destinationViewController as? AssesmentViewController
let subject = subjects[indexPath!.row]
listVC?.assesments = ["Death"]
}
}
}
I don't know if you have setup your segue properly and connect it to a button in cell or so but if you wan't to be on cell tap you need to implement didselectrow and perform segue from code like
override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("ShowAssesment", sender: self);
}
Check to make sure you set it up correctly. You also might try something like
func tableView(tableView:UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
self.performSegueWithIdentifier("ShowAssesment", sender: tableView.cellForRowAtIndexPath(indexPath))
}

Can't pass variable into prepareForSegue in Swift?

I'm trying to pass selectedName to VC #2 (BrandTableViewController). If I println(selectedName) in viewDidDisappear I get the value, however, the value is nil when it's in prepareForSegue? Can anyone see why?
import UIKit
import CoreData
class NameTableViewController: UITableViewController, UITableViewDelegate {
//Changes [String] to [NSManagedObject]
var people = [NSManagedObject]()
#IBOutlet var nameTableView: UITableView!
var selectedName: Person?
override func viewDidLoad() {
super.viewDidLoad()
title = "\"People\""
nameTableView.delegate = self
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
// Retreive the managedObjectContext from AppDelegate
let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
// Print it to the console
println(managedObjectContext)
}
// Mark: - Add Button Alert
#IBAction func addButton(sender: AnyObject) {
var alert = UIAlertController(title: "New name",
message: "Add a new name",
preferredStyle: .Alert)
let saveAction = UIAlertAction(title: "Save",
style: .Default) { (action: UIAlertAction!) -> Void in
let textField = alert.textFields![0] as! UITextField
self.saveName(textField.text)
self.tableView.reloadData()
}
let cancelAction = UIAlertAction(title: "Cancel",
style: .Default) { (action: UIAlertAction!) -> Void in
}
alert.addTextFieldWithConfigurationHandler {
(textField: UITextField!) -> Void in
}
alert.addAction(saveAction)
alert.addAction(cancelAction)
presentViewController(alert,
animated: true,
completion: nil)
}
//function to save a name (code from: http://www.raywenderlich.com/85578/first-core-data-app-using-swift)
func saveName(name: String) {
//1
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
//2
let entity = NSEntityDescription.entityForName("Person",
inManagedObjectContext:
managedContext)
let person = NSManagedObject(entity: entity!,
insertIntoManagedObjectContext:managedContext)
//3
person.setValue(name, forKey: "name")
//4
var error: NSError?
if !managedContext.save(&error) {
println("Could not save \(error), \(error?.userInfo)")
}
//5
people.append(person)
}
// MARK: - Table view data source
// override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// // #warning Potentially incomplete method implementation.
// // Return the number of sections.
// return 1
// }
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return people.count
}
private struct Storyboard {
static let CellReuseIdentifier = "Name"
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(Storyboard.CellReuseIdentifier, forIndexPath: indexPath) as! UITableViewCell
let person = people[indexPath.row]
cell.textLabel!.text = person.valueForKey("name") as? String
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let row = self.tableView.indexPathForSelectedRow()!.row
println("row \(row) was selected")
selectedName = people[indexPath.row] as? Person
println(selectedName)
// if let unWrappedSelectedName = selectedName {
// // println(unWrappedSelectedName)
// }
// else {
// println("no person was selected in didSelectRowAtIndexPath")
// }
}
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return NO if you do not want the specified item to be editable.
return true
}
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
let personToDelete = people[indexPath.row]
//abstract this into helper function later
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
managedContext.deleteObject(personToDelete)
self.fetchCoreData()
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
/*
// Override to support rearranging the table view.
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return NO if you do not want the item to be re-orderable.
return true
}
*/
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
fetchCoreData()
}
//Helper Function to Fetch Core Data
func fetchCoreData() {
//1
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
//2
let fetchRequest = NSFetchRequest(entityName:"Person")
//3
var error: NSError?
let fetchedResults = managedContext.executeFetchRequest(fetchRequest,
error: &error) as? [NSManagedObject]
if let results = fetchedResults {
people = results
} else {
println("Could not fetch \(error), \(error!.userInfo)")
}
}
// MARK: - Navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let btvc = segue.destinationViewController as? BrandsTableTableViewController {
if let identifer = segue.identifier {
if identifer == "segueToBrands" {
btvc.selectedName = selectedName
println(selectedName)
}
}
}
else {
println("we have a segue problem")
}
}
The prepareForSegue: method is called before tableView:didSelectRowAtIndexPath: method.And I saw that you only assign selectedName in method didSelectRowAtIndexPath.So selectedName will always be nil in prepareForSegue.
You can assign selectedName in tableView:willSelectRowAtIndexPath:
override func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) {
let row = self.tableView.indexPathForSelectedRow()!.row
println("row \(row) was selected")
selectedName = people[indexPath.row] as? Person
println(selectedName)
}
In your preprareForSegue() you can access the selected cell (or cells) as such:
override func prepareForSegue (segue: UIStoryboardSegue, sender: AnyObject?) {
switch segue.identifier ?? "" {
case "theSegue":
if let indexPath = tableView.indexPathForSelectedRow () {
// get what you need from the cell or the DataSource object
let controller = segue.destinationViewController as! PersonController
controller.person = people[indexPath.row]
}
// ...
}
}

Resources