2015-08-08 15 views
3

Приложение My To-Do уже добавило и удалило (из основных данных) функциональность. Это сработало отлично. Моя проблема в том, что моя попытка добавить редактирование не работает.Редактирование элемента списка основных данных вместо добавления

Поведение

Я закодирован так, если пользователь вводит задание в таблице, она устанавливает эту задачу в индексе ячейки пути, название и описание к переменным и передает их модальную ViewController для редактирования. Представлен модальный вид, а затем переданные переменные заполняют текстовые поля. Затем пользователь может отредактировать существующий контент и нажать «Сохранить», в котором выполняется некоторый код сохранения (я расскажу ниже). Модальное представление отклоняется, данные таблицы перезагружаются, а ячейка отображается только там, где она была раньше, но с обновленным контентом. ЭТО ВСЕ РАБОТАЕТ. Неисправность происходит при закрытии/полном отключении приложения и повторном его открытии. Внезапно возвращается исходная задача, но копия с изменениями добавляется к нижней части списка.

Вопрос

Почему мой код делает это, и как я могу получить отредактированное название и описание для сохранения и загрузки правильно?

Информация

Мое ядро ​​имя файла данных: CD_Model Мое имя сущности является: TodayTask Мои имена атрибутов являются: 1) "Имя" 2) "по убыванию"

Код

Я включил много своего кода в случае, если ошибка где-то я не ожидаю. Однако я добавил смелые к заголовкам двух фрагментов, которые, как я думаю, вызывают проблемы. Ошибка обнаруживается только при запуске viewDidLoad (первый фрагмент кода ниже). Но ошибка может произойти в последнем фрагменте - функции сохранения.

Импорт & Глобальных переменные:

import UIKit 
import CoreData 

var todayTaskList = [NSManagedObject]() 
var passingEdit = false 

декларация и viewDidLoad основных ОК, который содержит таблицу:

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { 
//***** ----- ***** ------ ***** ----- ***** ----- ***** 
//Initial Setup 
//***** ----- ***** ------ ***** ----- ***** ----- ***** 

@IBOutlet weak var tableView: UITableView! 

override func viewDidLoad() { 
    super.viewDidLoad() 
    // Do any additional setup after loading the view, typically from a nib. 

    //This loads the list from Core Data 
    //1 
    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate 
    let managedContext = appDelegate.managedObjectContext! 

    //2 
    let fetchRequest = NSFetchRequest(entityName:"TodayTask") 

    //3 
    var error: NSError? 
    let fetchedResults = managedContext.executeFetchRequest(fetchRequest, error: &error) as? [NSManagedObject] 

    if let results = fetchedResults { 
     todayTaskList = results 
    } else { 
     println("Could not fetch \(error), \(error!.userInfo)") 
    } 

    //This provides a variable height for each row 
    tableView.rowHeight = UITableViewAutomaticDimension 
    tableView.estimatedRowHeight = 80.0 
} 

код для создания таблицы:

//***** ----- ***** ------ ***** ----- ***** ----- ***** 
//Table View & Cell Setup 
//***** ----- ***** ------ ***** ----- ***** ----- ***** 
@IBOutlet weak var name_Label: UILabel! 
@IBOutlet weak var desc_Label: UILabel! 

//Tells the table how many rows it should render 
//*Looks to the Core Data NSObject to count tasks 
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
    return todayTaskList.count 
} 

//Creates the individual cells. If the above function returns 3, this runs 3 times 
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 

    //Setup variables 
    let cellIdentifier = "BasicCell" 
    let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! CustomTableViewCell 
    let task = todayTaskList[indexPath.row] 

    //Create table cell with values from Core Data attribute lists 
    cell.nameLabel!.text = task.valueForKey("name") as? String 
    cell.descLabel!.text = task.valueForKey("desc") as? String 

    //Make sure the row heights adjust properly 
    tableView.rowHeight = UITableViewAutomaticDimension 
    tableView.estimatedRowHeight = 80.0 

    return cell 
} 

Код запускается, когда exis Задача тин сливают:

//***** ----- ***** ------ ***** ----- ***** ----- ***** 
//Functions 
//***** ----- ***** ------ ***** ----- ***** ----- ***** 

//Action: Edit list item on row tap 
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { 

    passingEdit = true 

    performSegueWithIdentifier("modalToEditor", sender: nil) 
} 

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 
    if (segue.identifier == "modalToEditor") && passingEdit == true { 

     //Assign selection to a variable 'currentCell' 
     let indexPath = tableView.indexPathForSelectedRow(); 
     let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as! CustomTableViewCell; 

     //Set cell text into variables to pass to editor 
     var cellNameForEdit = currentCell.nameLabel!.text 
     var cellDescForEdit = currentCell.descLabel.text 

     //Pass values to EditorView 
     var editorVC = segue.destinationViewController as! EditorView; 
     editorVC.namePassed = cellNameForEdit 
     editorVC.descPassed = cellDescForEdit 
     editorVC.indexOfTap = indexPath 


    } 
} 

Декларация модального редактора ВК, переменные, переданные из основной VC установлены:

class EditorView: UIViewController, UITextFieldDelegate { 

//Declare outlets and vars 
@IBOutlet var txtTask: UITextField! 
@IBOutlet var txtDesc: UITextView! 
@IBOutlet weak var addSave: UIButton! 
@IBOutlet weak var cancel: UIButton! 

var namePassed: String! 
var descPassed: String! 
var indexOfTap: NSIndexPath! 

//Initial Functions 
override func viewDidLoad() { 
    super.viewDidLoad() 

    self.txtTask.becomeFirstResponder() 

    if passingEdit == true { 
     txtTask.text = namePassed 
     txtDesc.text = descPassed 
     addSave.setTitle("Save", forState: UIControlState.Normal) 
    } 
    else { 
     addSave.setTitle("Add", forState: UIControlState.Normal) 
    } 
} 

Запуск функции при нажатии на кнопку сохранения:

func modRec(nameValue: String, descValue: String, indexPos: NSIndexPath) { 

    //1 
    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate 
    let managedContext = appDelegate.managedObjectContext! 

    //2 
    let entity = NSEntityDescription.entityForName("TodayTask", inManagedObjectContext: managedContext) 
    let todayTask = NSManagedObject(entity: entity!, insertIntoManagedObjectContext:managedContext) 

    //3 
    todayTask.setValue(nameValue, forKey: "name") 
    todayTask.setValue(descValue, forKey: "desc") 

    //4 
    var error: NSError? 
    if !managedContext.save(&error) { 
     println("Could not save \(error), \(error?.userInfo)") 
    } 
    //5 
    todayTaskList[indexPos.row] = todayTask 
    managedContext.save(nil) 


} 

ответ

1

Я сделал следующие изменения в коде. Я добавил oldnamevale и olddescvalue в качестве параметра в функции, которая может использоваться для предиката. Вы должны передать эти значения.

func modRec(oldnameValue: String, olddescValue: String,newnameValue: String, newdescValue: String, indexPos: NSIndexPath) { 

    var appDel: AppDelegate = (UIApplication.sharedApplication().delegate as AppDelegate) 
    var context: NSManagedObjectContext = appDel.managedObjectContext! 

    var fetchRequest = NSFetchRequest(entityName: "TodayTask") 
fetchRequest.predicate = NSPredicate(format: "name = %@", oldnameValue) 

    if let fetchResults = appDel.managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as? [NSManagedObject] { 
     if fetchResults.count != 0{ 

     var managedObject = fetchResults[0] 
     managedObject.setValue(newdescValue, forKey: "desc") 
     managedObject.setValue(newnameValue, forKey: "name") 

     context.save(nil) 
     } 
    } 

} 

Если вы хотите выполнить запрос, используя имя и DESC затем внести следующие изменения в коде выше

let Predicate1 = NSPredicate(format: "name = %@", oldnameValue) 
let Predicate2 = NSPredicate(format: "desc = %@", olddescValue) 

var compound = NSCompoundPredicate.andPredicateWithSubpredicates([Predicate1!, Predicate2!]) 
fetchRequest.predicate = compound 

Надежда это может быть полезным.

+0

Это было. В значительной степени просто вырезать/вставить. Спасибо. –

0

Я думаю, проблема связана с функцией modRec. Вы должны изменить значения данных ядра с помощью предиката. Проверьте приведенный ниже пример или This link или This tutorial. Это может быть полезно для вас.

func saveLoginData(accessToken: String, userName: String) { 
    var appDel: AppDelegate = (UIApplication.sharedApplication().delegate as AppDelegate) 
    var context: NSManagedObjectContext = appDel.managedObjectContext! 

    var fetchRequest = NSFetchRequest(entityName: "LoginData") 
    fetchRequest.predicate = NSPredicate(format: "userName = %@", userName) 

    if let fetchResults = appDel.managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as? [NSManagedObject] { 
     if fetchResults.count != 0{ 

      var managedObject = fetchResults[0] 
      managedObject.setValue(accessToken, forKey: "accessToken") 

      context.save(nil) 
     } 
    } 
} 
+0

У меня был этот код прошлой ночью, и я пытался его адаптировать, но не смог. В моем случае, что я должен использовать для формата предиката? То есть, что я должен использовать для «userName =% @». Напоминаем, что мое основное имя Entity данных - «TodayTask», атрибуты - «имя» и «desc», и я устанавливаю var todayTaskList = [NSManagedObject]() –

2

В вашем методе modRec, вы создаете новый TodayTask объект:

let todayTask = NSManagedObject(entity: entity!, insertIntoManagedObjectContext:managedContext) 

, а затем заменить объект в соответствующий индекс в вашем todayTaskList массиве:

todayTaskList[indexPos.row] = todayTask 

Но предыдущий объект, значения которого вы изменяете, все еще существует. Он больше не находится в вашем массиве, но он все еще находится в хранилище Core Data. Когда вы перезагружаете представление таблицы, он использует массив todayTaskList для заполнения строк, и вы видите, что ожидаете. Но когда вы закрываете и снова открываете приложение, массив todayTaskList сам перестраивается, выбирая из хранилища CoreData. Поскольку существуют как старые todayTask, так и новые, они оба добавляются в массив, и, следовательно, ваш табличный вид показывает оба.

Чтобы это исправить, я бы немного перестроить свой код:

  1. Изменить контроллер EditorView вида: вместо того, ВАР для каждого из атрибутов, переданных ему, использовать вар для полного NSManagedObject. Затем вы можете заполнить текстовые поля, используя атрибуты этого NSMO. (Вам необходимо будет внести изменения в код prepareForSegue соответственно. Если вы добавляете новый TodayTask, создайте объект NSManagedObject и добавьте его в свой массив, прежде чем переходить к редактору).
  2. Затем, в вашем методе modRec, вам не нужно вставлять новый объект TodayTask, вы можете просто установить атрибуты NSMO var.
  3. Поскольку вы изменяете существующий NSMO, а не вставляете новый, вам не нужно заменять объект в массиве todayTaskList.

Поскольку вы больше не придется обновлять todayTaskList массив внутри EditorView, он не должен быть глобальным (который, как вопрос хорошей практики, вы должны избегать делать везде, где это возможно). Это может быть просто var в вашем основном контроллере. (Также должно быть исключено, что passingEdit является глобальным).

+1

Я выбрал ответ Карлоса, потому что я не знал, как написать код для вашего ответа, а его вырезать/вставить за две минуты. Ваш ответ - прекрасное объяснение того, как мой массив подделывает меня, полагая, что мой основной хранилище данных изменен. Никто не ответил на эту часть. Вы также заметили мое любопытство относительно того, почему я должен избегать глобальных переменных, а как же это плохая практика? Если вы добавите код, чтобы такой наркотик, как я, мог его реализовать, это определенно сделает ваш лучший пост. Независимо, искренне благодарю. –

+0

@DaveG Globals идут против принципа объектно-ориентированного программирования инкапсуляции. Объектами должны быть «черные ящики», внутренние данные и методы которых скрыты от других объектов, за исключением четко определенного интерфейса. В вашем случае массив 'todayTaskList' является источником данных для представления таблицы и поэтому должен быть действительно скрыт от других объектов. Поскольку он является глобальным, к нему можно получить доступ всеми другими объектами, поэтому ваш редактор EditorView может по необходимости изменить его. Но есть опасность в этом: предположим, вы добавляли новые 'todayTasks', загружая их с сервера ... – pbasdf

+0

@DaveG .... ваш контроллер табличного представления может не знать, что массив обновляется, а таблица представление будет не синхронизировано с вашим массивом. Или, если вы работаете как часть команды, один из ваших коллег может написать еще один контроллер представления в другом месте приложения, которое бесполезно с вашим массивом. Это делает отладку кошмаром. Поэтому моя рекомендация состояла бы в том, чтобы избежать их, где это возможно, - есть другие шаблоны проектирования, которые вы можете использовать (синглтоны, делегаты, ...). Но я не хочу проповедовать - возможно, что глобалы - это самое простое/лучшее решение для вас, по крайней мере, на данный момент. – pbasdf

0

1, вы должны реализовать методы FetchedResultsControllerDelegate в своем представлении таблиц и не пытаться управлять собственным списком - это не правильный способ сделать что-то, и это приведет к тому, что проблемы и записи не будут правильно отображаться в вашем пользовательском интерфейсе.

  1. Вам просто нужно обновить атрибуты исходного объекта, который вы получили. Ваш код сохранения создает новую запись.

Итак, в вашем представлении редактора добавьте свойство, которое устанавливается на исходный объект, а не на каждое свойство.

Теперь в вашем представлении редактирования, когда пользователь нажимает «Сохранить», просто обновите свойства на исходном объекте задачи и позвоните по номеру ManagedObjectContext.Save() - слишком просто.

Передайте значения ManagedObject и ManagedObjectContext к EditorView

var editorVC = segue.destinationViewController as! EditorView; 
editorVC.task = selectedTask 
editorVC.moc = managedContext 

Редактировать вид сохранить - просто обновить первоначальные свойства задачи (а ManagedObject)

 this.task.name = nameField.Text 
     this.task.desc = descField.Text 

     // That's it or commit to disk by calling MOC.Save 
     try { 
      this.moc.Save() 
     } 
+0

Здесь вы можете найти рабочие примеры: http://ossh.com.au/design-and-technology/software-development/sample-library-style-ios-core-data-app-with-icloud-integration/ –

+0

Дункан, спасибо. Я сейчас пытаюсь использовать вашу ссылку и код. Можете ли вы пояснить, что вы подразумеваете под «Не пытайтесь управлять собственным списком»? Я новичок в Core Data, только что начал два дня назад. Что я делаю, это зависит от того, как управлять моим собственным списком? Вы предоставили код для добавления, который, как я полагаю, не нарушает правило №1, но есть ли код, который я должен удалить? –

+0

Хорошо, хорошо, это хорошо, потому что вы избежите тратить много времени. Основные данные предоставляют способ отображения списка данных в виде таблицы, так что добавление, удаление или редактирование любого элемента автоматически обновит представление пользовательского интерфейса, даже если обновления выполняются фоновым потоком. Вот ссылка https://developer.apple.com/library/ios/documentation/CoreData/Reference/NSFetchedResultsControllerDelegate_Protocol/index.html # // apple_ref/occ/intf/NSFetchedResultsControllerDelegate –