如何在原型单元格中使用UIImageView在UITableView中创建视差效果

ded*_*ded 17 uitableview ios parallax swift

我正在使用Swift在iOS 8.4中构建一个应用程序.

我有一个包含a 和UITableView的自定义.这一切都相当直接,一切都很好.UITableViewCellUILabelUIImageView

我正在尝试创建一个类似于此演示中演示的视差效果.

我目前在我的代码中有这个代码 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
}
Run Code Online (Sandbox Code Playgroud)

该块存在于类似的类中 class HomeController: UIViewController, UITableViewDelegate, UITableViewDataSource { ... }

我也知道我可以通过我的班级听滚动事件func scrollViewDidScroll.

除此之外,感谢帮助!

ded*_*ded 32

我想到了!我们的想法是在不实现任何额外库的情况下执行此操作,特别是考虑到实现的简单性.

首先......在自定义表视图Cell类中,您必须创建一个包装器视图.您可以UIImageView在Prototype单元格中选择,然后选择Editor > Embed in > View.将两个拖动到单元格作为出口,然后设置clipToBounds = true为包含视图.(还记得将约束设置为与图像相同.

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

然后在你的UITableViewController子类(或委托)中,实现scrollViewDidScroll- 从这里你将不断更新UIImageView.frame属性.见下文:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

看到这个在行动.


Nyc*_*cen 25

我对@ded的解决方案不太满意,需要一个包装器视图,所以我想出了另一个使用自动布局的解决方案,并且非常简单.

在故事板中,您只需添加imageView并在ImageView上设置4个约束:

  • 导致ContentView(即Superview)= 0
  • 追踪到ContentView(即Superview)= 0
  • ContentView的顶级空间(即Superview)= 0
  • ImageView高度(此处设置为200,但无论如何都会根据单元格高度重新计算)

在此输入图像描述

最后两个约束(顶部和高度)需要引用自定义UITableViewCell的出口(在上面的图片中,双击最右边列中的约束,然后显示连接检查器 - 图标是圆圈中的箭头)

您的UITableViewCell应该如下所示:

class ParallaxTableViewCell: UITableViewCell {

    @IBOutlet weak var parallaxImageView: UIImageView!

    // MARK: ParallaxCell

    @IBOutlet weak var parallaxHeightConstraint: NSLayoutConstraint!
    @IBOutlet weak var parallaxTopConstraint: NSLayoutConstraint!

    override func awakeFromNib() {
        super.awakeFromNib()

        clipsToBounds = true
        parallaxImageView.contentMode = .ScaleAspectFill
        parallaxImageView.clipsToBounds = false
    }
  }
Run Code Online (Sandbox Code Playgroud)

所以基本上,我们告诉图像尽可能多地占用空间,但我们将其剪切到单元格框架.

现在你的TableViewController看起来像这样:

class ParallaxTableViewController: UITableViewController {
    override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        return cellHeight
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("CellIdentifier", forIndexPath: indexPath) as! ParallaxTableViewCell
        cell.parallaxImageView.image = … // Set your image
        cell.parallaxHeightConstraint.constant = parallaxImageHeight
        cell.parallaxTopConstraint.constant = parallaxOffsetFor(tableView.contentOffset.y, cell: cell)
        return cell
    }

    // Change the ratio or enter a fixed value, whatever you need
    var cellHeight: CGFloat {
        return tableView.frame.width * 9 / 16
    }

    // Just an alias to make the code easier to read
    var imageVisibleHeight: CGFloat {
        return cellHeight
    }

    // Change this value to whatever you like (it sets how "fast" the image moves when you scroll)
    let parallaxOffsetSpeed: CGFloat = 25

    // This just makes sure that whatever the design is, there's enough image to be displayed, I let it up to you to figure out the details, but it's not a magic formula don't worry :)
    var parallaxImageHeight: CGFloat {
        let maxOffset = (sqrt(pow(cellHeight, 2) + 4 * parallaxOffsetSpeed * tableView.frame.height) - cellHeight) / 2
        return imageVisibleHeight + maxOffset
    }

    // Used when the table dequeues a cell, or when it scrolls
    func parallaxOffsetFor(newOffsetY: CGFloat, cell: UITableViewCell) -> CGFloat {
        return ((newOffsetY - cell.frame.origin.y) / parallaxImageHeight) * parallaxOffsetSpeed
    }

    override func scrollViewDidScroll(scrollView: UIScrollView) {
        let offsetY = tableView.contentOffset.y
        for cell in tableView.visibleCells as! [MyCustomTableViewCell] {
            cell.parallaxTopConstraint.constant = parallaxOffsetFor(offsetY, cell: cell)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 使用tableView.dequeueReusableCellWithIdentifier("CellIdentifier",forIndexPath:indexPath)而不是tableView.dequeueReusableCellWithIdentifier("CellIdentifier")非常重要,否则在开始滚动之前图像不会偏移

所以你有它,parallax UITableViewCells应该适用于任何布局,也可以适应CollectionViews.