【问题标题】:Appending Values Consistently in Swift在 Swift 中一致地附加值
【发布时间】:2018-11-03 01:18:51
【问题描述】:

编辑:只需更改代码以显示我对 Singleton 建议的尝试。

所以我正在尝试创建一个应用程序,该应用程序将从 UITableView 中选择用户并将其传递给另一个 UITableView。然后,它将被附加到该视图中的数组并显示为新表。这个想法是用户可以从多个列表中选择并创建一个包含所有选择的列表。

但是,我对 iOS 开发非常陌生,虽然我可以让它让我获取选定的值并在新的 UITableView 中显示它,但它只发送一个值而不保留或附加它。这意味着我永远无法在列表中显示多个添加内容。

所以,我得到的 atm 是选择一个单元格的能力,比如说“Kevin Smith”,该值被发送到新的 UITableView 并显示给用户。但如果我选择另一个值“John Smith”,那么只有 John 出现,而​​ Kevin 消失了。

这是我涉及的三个控制器:

第一个UITableView控制器

    class PlayerViewController: UITableViewController {

var resultsController: NSFetchedResultsController<Player>!
let CDSPlayer = coreDataStackPlayer()

override func viewDidLoad() {
    super.viewDidLoad()
    tableView.allowsMultipleSelection = true

    let request: NSFetchRequest<Player> = Player.fetchRequest()
    let sortDescriptor = NSSortDescriptor(key: "level", ascending: true)
    request.sortDescriptors = [sortDescriptor]
    resultsController = NSFetchedResultsController(
        fetchRequest: request,
        managedObjectContext: CDSPlayer.managedContext,
        sectionNameKeyPath: nil,
        cacheName: nil
    )

    resultsController.delegate = self

    do{
        try resultsController.performFetch()
    } catch {
        print("Perform Fetch Error: \(error)")
    }
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // #warning Incomplete implementation, return the number of rows
    return resultsController.sections?[section].numberOfObjects ?? 0
}

override func tableView(_ _tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
    let cell = tableView.dequeueReusableCell(withIdentifier: "PlayerCell", for: indexPath)
    let selectedIndexPaths = tableView.indexPathsForSelectedRows
    let rowIsSelected = selectedIndexPaths != nil && selectedIndexPaths!.contains(indexPath)
    cell.accessoryType = rowIsSelected ? .checkmark : .none
    let player = resultsController.object(at: indexPath)
    cell.textLabel?.text = player.name

    return cell
}

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    performSegue(withIdentifier: "player2form", sender: tableView.cellForRow(at: indexPath))
}

override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
    let action = UIContextualAction(style:.destructive, title: "Delete"){(action, view, completion) in
        let player = self.resultsController.object(at: indexPath)
        self.resultsController.managedObjectContext.delete(player)
        do {
            try self.resultsController.managedObjectContext.save()
            self.errorMessage(message: "Player Character Deleted.");
            completion(true)
        } catch {
            print("Delete Failed: \(error)")
            self.errorMessage(message: "Failed to Delete Player Character.");
            completion(false)
        }
    }
    action.image = UIImage(named: "trash")
    action.backgroundColor = .red
    return UISwipeActionsConfiguration(actions: [action])
}

    override func prepare(for segue: UIStoryboardSegue, sender: Any?){

    if let _ = sender as? UIBarButtonItem, let vc = segue.destination as? AddPlayerViewController{
        vc.managedContext = resultsController.managedObjectContext
    }

    if let cell = sender as? UITableViewCell, let vc = segue.destination as? AddPlayerViewController{
        vc.managedContext = resultsController.managedObjectContext
        if let indexPath = tableView.indexPath(for: cell){
            let player = resultsController.object(at: indexPath)
            Service.shared.allPlayers.append(player) 
            vc.player = player
        }
    }
}

//Error Function
func errorMessage(message:String){
    let alert = UIAlertController(title: "Alert", message: message, preferredStyle:UIAlertControllerStyle.alert);
    let okAction = UIAlertAction(title:"Ok", style:UIAlertActionStyle.default, handler:nil);

    alert.addAction(okAction);
    self.present(alert, animated: true, completion:nil);
}
}

extension PlayerViewController: NSFetchedResultsControllerDelegate{

func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    tableView.beginUpdates()
}

func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    tableView.endUpdates()
}

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
    switch type{
    case .insert:
        if let indexPath = newIndexPath{
            tableView.insertRows(at: [indexPath], with: .automatic)
        }
    case .delete:
        if let indexPath = indexPath{
            tableView.deleteRows(at: [indexPath], with: .automatic)
        }
    case .update:
        if let indexPath = indexPath, let cell = tableView.cellForRow(at: indexPath){
            let player = resultsController.object(at: indexPath)
            cell.textLabel?.text = player.name
        }
    default:
        break
    }
}

传递数据的选择视图:

    class AddPlayerViewController: UIViewController {

var managedContext: NSManagedObjectContext!
var player: Player?
var selectedItems = [String]()

@IBOutlet weak var done_btn: UIButton!

@IBOutlet weak var playerInput: UITextField!

@IBOutlet weak var cancel_btn: UIButton!

@IBAction func add2combat(_ sender: UIButton) {
    self.performSegue(withIdentifier: "player2combat", sender: self)
}
@IBAction func done(_ sender: UIButton) {
    guard let name = playerInput.text, !name.isEmpty else {
        return //Add notice user cannot save empty items
    }

    if let player = self.player {
        player.name = name
    } else {
        //Set values from input to the cell
        let player = Player(context: managedContext)
        player.name = name
    }

    do {
        try managedContext.save()
        playerInput.resignFirstResponder()
        dismiss(animated: true)
    } catch {
        print("Error Saving Player Character: \(error)")
    }
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?){
    if segue.identifier == "player2combat" {
        let selectedItems : [String] = [playerInput.text!]
        let otherVc = segue.destination as! CombatSceneViewController
        otherVc.selectedItems = selectedItems
        print(selectedItems)
    }
}

@IBAction func cancel(_ sender: UIButton) {
    playerInput.resignFirstResponder()
    dismiss(animated: true)
}


override func viewDidLoad() {
    super.viewDidLoad()
    playerInput.becomeFirstResponder()

    if let player = player {
        playerInput.text = player.name
        playerInput.text = player.name
    }

}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

以及获取数据并尝试将其附加到现有数组的 UITableView。

    class CombatSceneViewController: UITableViewController{

var selectedItems =  Service.shared.allSelectedItems

override func viewDidLoad() {
    super.viewDidLoad()

    let otherVC = AddPlayerViewController()
    selectedItems.append(contentsOf: otherVC.selectedItems)

    saveData()
    loadData()
    print(selectedItems)

}


override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return selectedItems.count
}

override func tableView(_ _tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
    let cell = tableView.dequeueReusableCell(withIdentifier: "CombatCell", for: indexPath as IndexPath)
    cell.textLabel?.text = selectedItems[indexPath.item]
    return cell
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    tableView.reloadData()
}

func saveData() {
    let data = NSMutableData()

    // 1
    let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
    let path = paths[0]
    let file = (path as NSString).appendingPathComponent("Persistent.plist")

    //2
    let archiver = NSKeyedArchiver(forWritingWith: data)
    archiver.encode(selectedItems, forKey: "Agents")
    archiver.finishEncoding()
    data.write(toFile: file, atomically: true)
}

func loadData() {
    let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
    let path = paths[0]
    let file = (path as NSString).appendingPathComponent("Persistent.plist")

    // 1
    if FileManager.default.fileExists(atPath: file) {
        if let data = NSData(contentsOfFile: file) {
            let unarchiver = NSKeyedUnarchiver(forReadingWith: data as Data)
            selectedItems = unarchiver.decodeObject(forKey: "Agents") as! [String]

            unarchiver.finishDecoding()
        }
    }
}

单例类:

    class Service {

static let shared = Service()

var allPlayers = [Player]()

var allSelectedItems = [String]()
}

我希望我的格式正确...这是我在这里的第一篇文章,请温柔^^;。

【问题讨论】:

    标签: ios arrays swift uitableview append


    【解决方案1】:

    问题是当你回去时,老玩家因为你没有保留它而消失了,你可以尝试为它创建一个单例

    class Service {
    
       static let shared = Service()
    
       var allPlayers = [Player]()
    
       var allSelectedItems = [String]()
    
    }
    

    // 留在这里

      if let cell = sender as? UITableViewCell, let vc = segue.destination as? AddPlayerViewController{
        vc.managedContext = resultsController.managedObjectContext
        if let indexPath = tableView.indexPath(for: cell){
            let player = resultsController.object(at: indexPath)
            Service.shared.allPlayers.append(player)  // add this line
            vc.player = player
        }
    
     }
    

    执行相同的逻辑来附加选定的项目,当你到达最终的 tableView 时访问它们

    Service.shared.allPlayers
    

    或者

    Service.shared.selectedItems
    

    这样你就有了一个持久化的容器,可以在应用内的任何地方访问所有选定的玩家和物品

    【讨论】:

    • 我实施了你的建议,记住我对 iOS 的幼稚本性,它仍然做着和以前一样的事情。我不能直接附加 Service.shared.allSelectedItems 或 Service.shared.allPlayers 因为我的容器是一个字符串数组,但 allSelectedItems 也是一个字符串数组,它希望它是字符串类型?同时, allPlayers 如果是 Player 类型,那么它也不会进入。我也尝试像您建议的那样放入 Service.shared.selectedItems 但在 Service 中没有该名称的变量 - 仅在 UITableView 中。
    • 我确实让 Singleton 工作并被识别,但它仍然只向数组附加 1 个值(现在奇怪的是列表中的两个相同元素。也许我的附加逻辑很奇怪?
    • @DemicaFatali 检查数组是否包含插入前的项
    • 我有一个打印命令测试它。它每次都获取 1 元素,但从未将其添加到现有列表中。它只是不断向我显示我当时选择的 1 个名称...我应该用新代码更新我的帖子吗?
    猜你喜欢
    • 2023-03-11
    • 2021-04-24
    • 1970-01-01
    • 2012-08-22
    • 2020-05-02
    • 1970-01-01
    • 2016-01-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多