How do I create a parallax effect in UITableView with UIImageView in their prototype cells

ded picture ded · Jul 13, 2015 · Viewed 18.6k times · Source

I'm building an app in iOS 8.4 with Swift.

I have a UITableView with a custom UITableViewCell that includes a UILabel and UIImageView. This is all fairly straight forward and everything renders fine.

I'm trying to create a parallax effect similar to the one demonstrated in this demo.

I currently have this code in my tableView.cellForRowAtIndexPath

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    var cell = self.tableView.dequeueReusableCellWithIdentifier("myitem", forIndexPath: indexPath) as! MixTableViewCell
    cell.img.backgroundColor = UIColor.blackColor()
    cell.title.text = self.items[indexPath.row]["title"]

    cell.img.image = UIImage(named: "Example.png")

    // ideally it would be cool to have an extension allowing the following
    // cell.img.addParallax(50) // or some other configurable offset

    return cell
}

That block exists inside a class that looks like class HomeController: UIViewController, UITableViewDelegate, UITableViewDataSource { ... }

I am also aware that I can listen to scroll events in my class via func scrollViewDidScroll.

Other than that, help is appreciated!

Answer

ded picture ded · Jul 13, 2015

I figured it out! The idea was to do this without implementing any extra libraries especially given the simplicity of the implementation.

First... in the custom table view Cell class, you have to create an wrapper view. You can select your UIImageView in the Prototype cell, then choose Editor > Embed in > View. Drag the two into your Cell as outlets, then set clipToBounds = true for the containing view. (also remember to set the constraints to the same as your image.

class MyCustomCell: UITableViewCell {
    @IBOutlet weak var img: UIImageView!
    @IBOutlet weak var imgWrapper: UIView!
    override func awakeFromNib() {
        self.imgWrapper.clipsToBounds = true
    }
}

Then in your UITableViewController subclass (or delegate), implement the scrollViewDidScroll — from here you'll continually update the UIImageView's .frame property. See below:

override func scrollViewDidScroll(scrollView: UIScrollView) {
    let offsetY = self.tableView.contentOffset.y
    for cell in self.tableView.visibleCells as! [MyCustomCell] {
        let x = cell.img.frame.origin.x
        let w = cell.img.bounds.width
        let h = cell.img.bounds.height
        let y = ((offsetY - cell.frame.origin.y) / h) * 25
        cell.img.frame = CGRectMake(x, y, w, h)
    }
}

See this in action.