Over a million developers have joined DZone.

Swift: When removeFromSuperview() Is Not Enough

Here are some great tips for your Swift UI.

· Mobile Zone



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[See addendum below]

class func convertFromNilLiteral() -> Self
    {
        return self(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:

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

    }

My source code is now updated and available at my GitHub repository here.
Addendum Thanks to @ChromophoreApp for pointing out that the NilLiteralConvertable implementation is an unnecessary step. My first cut of the code accidentally had the reference to the deleted NodeWidget non-optional (i.e. no ?) which is why I couldn't set it to nil - a schoolboy error! Now that nodeWidgetPendingDelete is optional, I can set it to nil without jumping through hoops!

Topics:
java ,mobile ,tutorial ,ios ,swift

Published at DZone with permission of Simon Gladman, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}