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

Ios 如何在UITableView的原型单元格中使用UIImageView创建视差效果,ios,swift,uitableview,parallax,Ios,Swift,Uitableview,Parallax,我正在用Swift在iOS 8.4中构建一个应用程序 我有一个带有自定义UITableView单元格的UITableView,其中包括UILabel和UIImageView。这一切都相当直截了当,一切都很好 我正在尝试创建一个类似的视差效果 我当前在我的tableView.cellforrowatinexpath中有此代码 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -&

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

我有一个带有自定义
UITableView单元格的
UITableView
,其中包括
UILabel
UIImageView
。这一切都相当直截了当,一切都很好

我正在尝试创建一个类似的视差效果

我当前在我的
tableView.cellforrowatinexpath中有此代码

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
}
该块存在于一个类中,该类看起来像
类HomeController:UIViewController、UITableViewDelegate、UITableViewDataSource{…}

我还知道,我可以通过
func scrollviewdidcoll
收听课堂上的滚动事件


除此之外,感谢您的帮助

我想出来了!这样做的想法是在不实现任何额外库的情况下完成的,特别是考虑到实现的简单性

首先。。。在自定义表视图
单元格
类中,必须创建包装器视图。您可以在Prototype单元格中选择您的
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
子类(或委托)中,实现
scrollviewdidcoll
-从这里您将不断更新
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)
    }
}

.

我对@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
    }
  }
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)
        }
    }
}
所以基本上,我们告诉图像尽可能多地占据空间,但是我们把它剪辑到细胞框架上

现在,TableViewController应该如下所示:

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
    }
  }
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”)非常重要,否则在开始滚动之前图像不会偏移

因此,您可以使用视差UITableViewCells,它可以与任何布局一起使用,也可以适应CollectionView。

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

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

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

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

  • 在同一类中添加以下代码
  • 其中seriestableview是我的UItableview

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

  • 我们的后期图像是我的UIImageView
如果要对collectionView实现此功能,只需将tableView变量更改为collectionView变量


就是这样。我不确定这是否是最好的方法。但它对我有效。希望它也对你有效。结合@ded和@Nycen的答案后,让我知道是否有任何问题。

我找到了这个解决方案,它使用嵌入式视图,但改变了布局约束(只有一个):

  • 在Interface Builder中,将图像视图嵌入到UIView中。对于该视图,请生成
    [√] 剪辑到边界
    在视图>图形中选中

  • 将图像中的以下约束添加到视图:左侧和右侧、垂直居中、高度

  • 调整高度约束,使图像略高于视图

  • 对于“对齐中心Y”约束,在UITableViewCell中创建一个插座

  • 将此函数添加到视图控制器(即UITableViewController或UITableViewControllerDelegate)中

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

  • 从重新使用单元格时调用函数:

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

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

  • 这是一个完美的解决方案。感谢@JohnTravolta,很高兴它帮助了你,感谢编辑建议,尽管它被审查者拒绝了。这当然是我想要的,但是你如何在ImageView上设置这些限制?在我的版本中,我想在图片的顶部添加一个标签title@Ragnar只需按住ctrl键并单击图像查看,按住并拖动容器,然后选择所需的约束。对于标签,只需添加标签,然后在标签和图像之间的标签上定义约束。@FredA。这是一个二次方程的结果(另一个结果为负)表示图片溢出和tableview可见高度之间的关系。基本上,您要解决的问题是:如何确保无论单元格位于何处,单元格f中始终存在内容
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "myCellId", for: indexPath) as! MyTableCell
        adjustParallax(for: cell)
        return cell
    }
    
    override func scrollViewDidScroll(_ scrollView: UIScrollView) {
        (self.tableView.visibleCells as! [MyTableCell]).forEach { cell in
            adjustParallax(for: cell)
        }
    }