Quantcast
Channel: FlexMonkey
Viewing all articles
Browse latest Browse all 257

Swift: When removeFromSuperview() Is Not Enough

$
0
0

I've just added a little menu button with pop out UIAlertController to my Swift node based user interface demonstration. Along with options to change the selected node's type, it also allows the user to delete the current node.

At first glance, I thought this is simple: all I need to do is filter out the deleted node from presentation model's array of node value objects and invoke removeFromSuperview() on the widget itself. 

Aah, would that it were, would that it were.

There are a few issues that need to be resolved: simply removing the UI widget from its superview doesn't nullify it, so any observers it has on the presentation model are still active and, of course, it's still loitering around in memory wasting precious resources.

It takes a little additional work to be able to do this...

            nodeWidget.removeFromSuperview()
            nodeWidget = nil

...the first step being to make my NodeWidget class implement NilLiteralConvertible. Implementing this protocol means that my class has to have a convertFromNilLiteral() method that returns a nulled version of itself. My version looks like this:

    classfunc convertFromNilLiteral() -> Self
    {
        returnself(frame: CGRectZero, node: NodeVO(name: "NULL_NODE", position: CGPointZero))

    }

Now, when I set an instance of NodeWidget to nil, it's properly deinitialised. As part of that process, its deinit is invoked and I can also remove any observers:

    deinit
    {
        NodesPM.removeObserver(self) // invokes notificationCentre.removeObserver(observer)

    }

I've also used UIView.animateWithDuration to make the deleted node fade out rather than suddenly vanish. This requires a temporary variable, nodeWidgetPendingDelete, that holds a reference to the deleted widget which is removed from its superview and nulled once the fade is completed:

    UIView.animateWithDuration(NodeConstants.animationDuration, animations: {self.nodeWidgetPendingDelete!.alpha = 0}, completion: deleteAnimationComplete)

    [...]

    func deleteAnimationComplete(value: Bool)
    {
        if (value && nodeWidgetPendingDelete != nil)
        {
            nodeWidgetPendingDelete?.removeFromSuperview()
            nodeWidgetPendingDelete = nil
        }

    }

...and there we go: now the node isn't just removed from the view and hidden from the user, it's properly deinitialised and all its observers removed.

Of course, the related value object needs to be removed and any references to it in the inputs array of the other nodes need to be removed too. This is done in the presentation model using filters on the arrays:

    staticfunc 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

    }

My source code is now updated and available at my GitHub repository here.

Viewing all articles
Browse latest Browse all 257

Trending Articles