Ios 如何在UITableView的原型单元格中使用UIImageView创建视差效果
我正在用Swift在iOS 8.4中构建一个应用程序 我有一个带有自定义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) -&
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)
}
}