Episode #141

Simulating Rope with UIKit Dynamics

23 minutes
Published on October 16, 2014

This video is only available to subscribers. Get access to this video and 573 others.

In this episode we use UIKit Dynamics to create something fun: Rope! By attaching many segments together with attachments, we can simulate the physics of a rope. We also discover the best way to move an object on the screen in conjunction with existing attachments.

Episode Links

Initial Rope Class

class Rope : UIView {
    var numSegments: Int = 1
    var links: [UIView] = []
    var originalFrame = CGRectZero

    var segmentWidth: CGFloat {
        get {
            return originalFrame.size.width
        }
    }

    var segmentLength: CGFloat {
        get {
            return originalFrame.size.height / CGFloat(numSegments)
        }
    }

    required init(coder: NSCoder) {
        super.init(coder: coder)
    }

    init(frame: CGRect, numSegments: Int, referenceView: UIView) {
        self.numSegments = numSegments
        self.originalFrame = frame

        super.init(frame: referenceView.frame)

        clipsToBounds = false
        backgroundColor = UIColor.clearColor()
        addLinks()
    }

    func addLinks() {
        for var i = 0; i < numSegments; i++ {
            let linkFrame = CGRectMake(
                originalFrame.origin.x + 0,
                originalFrame.origin.y + CGFloat(i) * segmentLength,
                segmentWidth,
                segmentLength
            )
            var link = UIView(frame: linkFrame)
            link.backgroundColor = UIColor.clearColor()
            addSubview(link)
            links.append(link)
        }
    }
}

Here we just add the segments based on our current frame, adding them as subviews.

Adding Attachments Between the Segments

    func addToAnimator(animator: UIDynamicAnimator) {
        for (index, link) in enumerate(links) {
            var attachment: UIAttachmentBehavior!
            if index == 0 {
                attachment = UIAttachmentBehavior(item: link,
                    offsetFromCenter: UIOffsetMake(0, -segmentLength/2.0),
                    attachedToAnchor: originalFrame.origin)
            } else {
                let previousLink = links[index-1]
                attachment = UIAttachmentBehavior(item: link,
                    offsetFromCenter: UIOffsetMake(0, -segmentLength/2.0),
                    attachedToItem: previousLink,
                    offsetFromCenter: UIOffsetMake(0, segmentLength/2.0))
            }

            attachment.length = 1
            attachment.damping = 1
            attachment.frequency = 10
            attachment.action = {
                self.setNeedsDisplay()
            }
            animator.addBehavior(attachment)
        }
    }

We won't own an animator. Instead, a view controller will pass one to this class and we can add our attachments there. Here we add an attachment between each segment, and another one as an anchor for the first attachment.

Drawing the Rope

Right now our rope doesn't look much like rope, it looks like rectangular views held together by attachments. We can make something more realistic by drawing a path between each of the segments.

    override func drawRect(rect: CGRect) {
        var path = UIBezierPath()
        path.lineCapStyle = kCGLineCapRound
        path.lineJoinStyle = kCGLineJoinRound
        path.lineWidth = segmentWidth

        var start = links.first!.center
        start.y -= segmentLength / 2.0
        path.moveToPoint(start)
        for link in links {
            if link == links.first! {
                continue
            }
            path.addLineToPoint(link.center)
        }
        UIColor.blueColor().setStroke()
        path.stroke()
    }