Как удалить строку в UITableViewController, представленную как всплывающее окно - PullRequest
0 голосов
/ 02 августа 2020

У меня есть собственный UITableViewController, который я представляю как всплывающее окно в моем приложении. В некоторых ячейках есть кнопка удаления (tra sh can) для удаления этого элемента. Все работает как надо, за исключением того, что при нажатии кнопки удаления пользовательский интерфейс не обновляется. То есть данные очищаются, и я вызываю self.tableView.reloadData(), но ячейка остается видимой в пользовательском интерфейсе. (Повторное нажатие кнопки удаления приводит к тому, что в моем коде C ++ приложение испорчено sh из-за утверждения). У меня нет раскадровки или xib, так как они мне не нужны. Я хочу, чтобы это было только в коде.

Что мне не хватает? Это может быть что-то простое, но я не могу понять почему. Я пробовал:

  • Реализация отдельного источника данных.
  • Вызов reloadData() как syn c, так и asyn c.
  • Установка делегата на себя.
  • Различные другие хаки.

Вот реализация UITableViewController:

import Foundation

class IngredientInfoPopoverViewController : UITableViewController
{
    var slViewController: ShoppingListViewController?;
    var ingredientName: String = "Ingrediens";
    @IBOutlet var uniqueIngredients: [Ingredient] = []; // Unique per *recipe* so that we can list all the recipes for the ingredients
    var clickedCellIndexPath: IndexPath? = nil;

    enum SECTIONS : Int
    {
        case HEADER = 0;
        case RECIPE = 1;
    }

    static let ROW_HEIGHT = 44;

    override func viewDidLoad()
    {
        super.viewDidLoad();
        tableView.register(UINib(nibName: "OpenIngredientInfoCell", bundle: nil), forCellReuseIdentifier: "OpenIngredientInfoCell");
        tableView.register(UINib(nibName: "OpenRecipeCell", bundle: nil), forCellReuseIdentifier: "OpenRecipeCell");

        tableView.separatorStyle = .singleLine;
        tableView.bounces = false; // "Static" table view
        updateSize();
    }

    func updateSize()
    {
        let totalCount = min(uniqueIngredients.count + 1, 6); // + 1: header row. min: Allow max 5 recipes in list (enables scrolling)
        self.preferredContentSize = CGSize(width: 300, height: totalCount * IngredientInfoPopoverViewController.ROW_HEIGHT);
    }

    func setup(slvc: ShoppingListViewController?, ingredients: [Ingredient], clickedCellIndexPath: IndexPath)
    {
        self.slViewController = slvc;
        self.clickedCellIndexPath = clickedCellIndexPath;

        if (ingredients.count > 0)
        {
            let first = ingredients[0];
            for i in ingredients
            {
                assert(i.getId() == first.getId());
            }

            ingredientName = first.getName();
            var uniqueRecipeNames: Set<String> = [];
            for i in ingredients
            {
                uniqueRecipeNames.insert(i.getRecipeName());
            }

            let sorted = uniqueRecipeNames.sorted();
            uniqueIngredients.removeAll();
            for s in sorted
            {
                for i in ingredients
                {
                    if (i.getRecipeName() == s)
                    {
                        uniqueIngredients.append(i);
                        break;
                    }
                }
            }
        }
    }

    override func numberOfSections(in tableView: UITableView) -> Int
    {
        return 2;
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        switch section
        {
        case SECTIONS.HEADER.rawValue:
            return 1;
        case SECTIONS.RECIPE.rawValue:
            return uniqueIngredients.count;
        default:
            assert(false);
            return 0;
        }
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
    {
        switch (indexPath.section)
        {
        case SECTIONS.HEADER.rawValue:
            assert(indexPath.row == 0);
            if (uniqueIngredients.count > 0)
            {
                let ingredient = uniqueIngredients[0]; // All are the same ingredient
                self.dismiss(animated: true, completion: nil);
                slViewController?.onIngredientInfoButtonClicked(ingredient);
            }
            break;

        case SECTIONS.RECIPE.rawValue:
            if (indexPath.row < uniqueIngredients.count)
            {
                let ingredient = uniqueIngredients[indexPath.row];
                self.dismiss(animated: true, completion: nil);
                slViewController?.onRecipeInfoButtonClicked(ingredient);
            }
            break;

        default:
            break;
        }
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
    {
        let cell = UITableViewCell();

        switch (indexPath.section)
        {
        case SECTIONS.HEADER.rawValue:
            if (uniqueIngredients.count > 0)
            {
                let ingredient = uniqueIngredients[0];
                let cell = tableView.dequeueReusableCell(withIdentifier: "OpenIngredientInfoCell", for: indexPath) as! OpenIngredientInfoCell;
                cell.setup(ingredient);
            }
            break;

        case SECTIONS.RECIPE.rawValue:
            if (indexPath.row < uniqueIngredients.count)
            {
                cell.selectionStyle = .none; // Without this the cell contents become gray and disappear when long pressing! FML
                let ingredient = uniqueIngredients[indexPath.row];
                let cell = tableView.dequeueReusableCell(withIdentifier: "OpenRecipeCell", for: indexPath) as! OpenRecipeCell;
                cell.setup(self, ingredient, clickedCellIndexPath);
            }
            break;

        default:
            break;
        }

        return cell;
    }

    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
    {
        return CGFloat(IngredientInfoPopoverViewController.ROW_HEIGHT);
    }

    func ingredientRemoved(_ ingredient: Ingredient)
    {
        for i in 0..<uniqueIngredients.count
        {
            if (uniqueIngredients[i].getRecipeId() == ingredient.getRecipeId())
            {
                uniqueIngredients.remove(at: i);
//              let indexPath = IndexPath(row: i, section: SECTIONS.RECIPE.rawValue);
//              self.tableView.deleteRows(at: [indexPath], with: .fade);
                DispatchQueue.main.async {
                    self.tableView.reloadData();
                }
                break;
            }
        }

        if (uniqueIngredients.count == 0)
        {
            self.dismiss(animated: true, completion: nil);
        }
        else
        {
            DispatchQueue.main.async {
                self.tableView.reloadData();
            }
        }
    }
}

Вот как я представляю IngredientInfoPopoverViewController:

@objc func ingredientInfoClicked(_ sender: UITapGestureRecognizer)
{
    let tapLocation = sender.location(in: self.tableView)
    let indexPath = self.tableView.indexPathForRow(at: tapLocation)!

    let ingredients = CppInterface.shoppingList.getIngredients(UInt(indexPath.section), position: UInt(indexPath.row));

    let controller = IngredientInfoPopoverViewController();
    controller.setup(slvc: self, ingredients: ingredients!, clickedCellIndexPath: indexPath);
    controller.modalPresentationStyle = .popover;
    controller.popoverPresentationController!.delegate = self;
    self.present(controller, animated: true, completion: {
        self.tableView.reloadData();
    });
}

Вот как выглядит контроллер представления в представленном виде. Если я нажму кнопку tra sh на одном из элементов, данные будут очищены, но ячейка не будет удалена из пользовательского интерфейса, чего я и пытаюсь достичь.

Как представлен контроллер представления

Ответы [ 2 ]

1 голос
/ 03 августа 2020

Я действительно удивлен, что ваш tableView вообще показывает какие-либо данные. Поскольку вы объявляете cell как let в cellForRowAt, когда вы делаете let cell = UITableViewCell();, это делает его неизменным, а первый cell (за пределами switch) - это тот, который технически должен быть возвращен. Следовательно, почему данные не должны отображаться. И, вероятно, причина, по которой ваш tableView не обновляется правильно.

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

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    if indexPath.section == SECTIONS.HEADER.rawValue, let cell = tableView.dequeueReusableCell(withIdentifier: "OpenIngredientInfoCell", for: indexPath) as? OpenIngredientInfoCell {
        // not sure this check is necessary, but I'm adding it because it was in your original code
        guard uniqueIngredients.count > 0 else { return UITableViewCell() }
        let ingredient = uniqueIngredients[0]
        cell.setup(ingredient)
        return cell

    } else if indexPath.section == SECTIONS.RECIPE.rawValue, let cell = tableView.dequeueReusableCell(withIdentifier: "OpenRecipeCell", for: indexPath) as? OpenRecipeCell {
        // it shouldn't be possible for the indexPath to ever be greater than the dataSource items count, but I'll keep the check
        guard indexPath.row < uniqueIngredients.count else { return UITableViewCell() }
        cell.selectionStyle = .none
        let ingredient = uniqueIngredients[indexPath.row]
        cell.setup(self, ingredient, clickedCellIndexPath)
        return cell
    }

    return UITableViewCell()
}
  • Я удалил semi-colons, поскольку они не нужны в Swift.
  • Для указания идентификаторов повторного использования ячеек таблицы, используя класс имена, наверное, были бы лучше. Таким образом, вы должны использовать "\(OpenRecipeCell.self)" вместо "OpenRecipeCell"
0 голосов
/ 02 августа 2020

Если вы используете способ редактирования UITableView по умолчанию (прокрутка или переход в режим редактирования), то вот мой код делегата, который отлично работает:

func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
    let movedStep = appState.recipe.steps[sourceIndexPath.row]
    appState.recipe.steps.remove(at: sourceIndexPath.row)
    appState.recipe.steps.insert(movedStep, at: destinationIndexPath.row)
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
    if editingStyle == .delete {
        appState.recipe.steps.remove(at: indexPath.row)
        tblSteps.deleteRows(at: [indexPath], with: .automatic)
    }
}

Примечания:

  • Я вручную помещаю это табличное представление в режим редактирования с помощью UIBarButtonItem, и ячейку можно и перемещать или удалять.
  • Мой источник данных находится в моей модели по адресу appState.recipe.steps. Структура не имеет значения, просто обрабатывается массив.
  • Я устанавливаю Notification каждый раз, когда этот массив изменяется, что вызывает reloadData() в этом табличном представлении.

I не вижу ни одного из этих методов делегата в списке, поэтому я отправляю этот ответ. Если случайно это не поможет, я с радостью удалю это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...