Я только что добавил маленькую кнопку меню со всплывающим UIAlertController для демонстрации пользовательского интерфейса на основе узла Swift . Наряду с опциями для изменения типа выбранного узла, он также позволяет пользователю удалить текущий узел.
На первый взгляд, я подумал, что это просто: все, что мне нужно сделать, это отфильтровать удаленный узел из массива объектов значений узла модели представления и вызвать r emoveFromSuperview () для самого виджета.
Ааа, если бы это было, если бы это было .
Есть несколько проблем, которые необходимо решить: простое удаление виджета пользовательского интерфейса из его суперпредставления не отменяет его, поэтому все наблюдатели, которые у него есть в модели представления, все еще активны и, конечно, они все еще слоняются без дела в памяти, тратящей драгоценное Ресурсы.
Требуется немного дополнительной работы, чтобы сделать это …
nodeWidget.removeFromSuperview()
nodeWidget = nil
…
: [см. Приложение ниже]
первый шаг в том , чтобы сделать мой
NodeWidget класс реализации
NilLiteralConvertible . Реализация этого протокола означает, что мой класс должен иметь
метод convertFromNilLiteral (), который возвращает его собственную версию. Моя версия выглядит следующим образом
class func convertFromNilLiteral() -> Self
{
return self(frame: CGRectZero, node: NodeVO(name: "NULL_NODE", position: CGPointZero))
}
Теперь, когда я установил экземпляр NodeWidget на ноль , он правильно деинициализирован. В рамках этого процесса вызывается его deinit, и я также могу удалить любых наблюдателей:
deinit
{
NodesPM.removeObserver(self) // invokes notificationCentre.removeObserver(observer)
}
Я также использовал UIView.animateWithDuration, чтобы удаленный узел исчезал , а не внезапно исчезал. Для этого требуется временная переменная, nodeWidgetPendingDelete , которая содержит ссылку на удаленный виджет, который удаляется из его суперпредставления и обнуляется после завершения замирания:
UIView.animateWithDuration(NodeConstants.animationDuration, animations: {self.nodeWidgetPendingDelete!.alpha = 0}, completion: deleteAnimationComplete)
[...]
func deleteAnimationComplete(value: Bool)
{
if (value && nodeWidgetPendingDelete != nil)
{
nodeWidgetPendingDelete?.removeFromSuperview()
nodeWidgetPendingDelete = nil
}
}
… и все: теперь узел не просто удален из вида и скрыт от пользователя, он должным образом деинициализирован и все его наблюдатели удалены.
Конечно, связанный объект значения должен быть удален, и любые ссылки на него во входном массиве других узлов также должны быть удалены. Это делается в модели презентации с использованием фильтров на массивах:
static func deleteSelectedNode()
{
for node in nodes
{
node.inputNodes = node.inputNodes.filter({!($0 == NodesPM.selectedNode!)})
}
nodes = nodes.filter({!($0 == NodesPM.selectedNode!)})
postNotification(.NodeDeleted, payload: selectedNode)
postNotification(.RelationshipsChanged, payload: nil)
selectedNode = nil
}
Мой исходный код теперь обновлен и доступен в моем репозитории GitHub здесь .
Добавление Спасибо @ChromophoreApp за указание на то, что реализация NilLiteralConvertable является ненужным шагом. Мой первый фрагмент кода случайно имел ссылку на удаленный необязательный NodeWidget (то есть, нет ? ), Поэтому я не смог установить его на ноль — ошибка школьника! Теперь, когда nodeWidgetPendingDelete является необязательным, я могу установить его на ноль, не перепрыгивая через обручи!