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

时间:2015-07-13 00:02:49

标签: ios swift uitableview parallax

我正在使用Swift在iOS 8.4中构建应用程序。

我有UITableView个自定义UITableViewCell,其中包含UILabelUIImageView。这一切都很直接,一切都很好。

我试图创建一个类似于demonstrated in this demo的视差效果。

我目前在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
}

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

的类中

我也知道我可以通过func scrollViewDidScroll收听课堂上的滚动事件。

除此之外,感谢帮助!

4 个答案:

答案 0 :(得分:27)

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

首先......在自定义表视图Cell类中,您必须创建一个包装器视图。您可以在原型单元格中选择UIImageView,然后选择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
    }
}

然后在UITableViewController子类(或委托)中实施scrollViewDidScroll - 从此处您将不断更新UIImageView的{​​{1}}属性。见下文:

.frame

See this in action

答案 1 :(得分:25)

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

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

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

enter image description here

最后两个约束(顶部和高度)需要引用自定义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
    }
  }

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

现在你的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)
        }
    }
}

注意:

  • 使用tableView.dequeueReusableCellWithIdentifier(" CellIdentifier",forIndexPath:indexPath)而不是tableView.dequeueReusableCellWithIdentifier(" CellIdentifier")非常重要,否则图片不会是偏移,直到你开始滚动

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

答案 2 :(得分:2)

此方法适用于表格视图和集合视图。

  
      
  • 首先为tableview创建单元格并将图像视图放入其中。

  •   
  • 将图像高度设置为略高于单元格高度。如果单元格高度= 160,则让图像高度为200(以产生视差效果,您可以相应地更改它)

  •   
  • 将这两个变量放在viewController或扩展tableView委托的任何类中

  •   
let imageHeight:CGFloat = 150.0
let OffsetSpeed: CGFloat = 25.0
  
      
  • 在同一个类中添加以下代码
  •   
 func scrollViewDidScroll(scrollView: UIScrollView) {
    //  print("inside scroll")

    if let visibleCells = seriesTabelView.visibleCells as? [SeriesTableViewCell] {
        for parallaxCell in visibleCells {
            var yOffset = ((seriesTabelView.contentOffset.y - parallaxCell.frame.origin.y) / imageHeight) * OffsetSpeedTwo
            parallaxCell.offset(CGPointMake(0.0, yOffset))
        }
    }
}
  
      
  • 其中seriesTabelView是我的UItableview

  •   
  • 现在让我们转到这个tableView的单元格并添加以下代码

  •   
func offset(offset: CGPoint) {
        posterImage.frame = CGRectOffset(self.posterImage.bounds, offset.x, offset.y)
    }
  
      
  • 是posterImage是我的UIImageView
  •   

如果你想将它实现到collectionView,只需将tableView vairable更改为collectionView变量

就是这样。我不确定这是不是最好的方法。但它对我有用。希望它也适合你。并告诉我是否有任何问题

答案 3 :(得分:0)

结合@ded和@Nycen的答案后,我来到了此解决方案,该解决方案使用嵌入式视图,但更改了布局约束(仅其中之一):

  1. 在Interface Builder中,将图像视图嵌入到UIView中。对于该视图,请在视图>绘图中选中[√] Clips to bounds

  2. 从图像中添加以下约束以进行查看:左右,垂直居中,高度

  3. 调整高度限制,以使图像略高于视图

enter image description here

  1. 对于Align Center Y约束,在您的UITableViewCell中插入一个插座

  2. 将此功能添加到视图控制器(可以是UITableViewController或UITableViewControllerDelegate)

    private static let screenMid = UIScreen.main.bounds.height / 2
    private func adjustParallax(for cell: MyTableCell) {
        cell.imageCenterYConstraint.constant = -(cell.frame.origin.y - MyViewController.screenMid - self.tableView.contentOffset.y) / 10
    }
    

注意:通过编辑幻数10,您可以更改效果的应用程度,并从等式中删除-符号,可以更改效果的方向

  1. 从重用单元格开始调用函数:

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "myCellId", for: indexPath) as! MyTableCell
        adjustParallax(for: cell)
        return cell
    }
    
  2. 以及发生滚动时:

    override func scrollViewDidScroll(_ scrollView: UIScrollView) {
        (self.tableView.visibleCells as! [MyTableCell]).forEach { cell in
            adjustParallax(for: cell)
        }
    }