UICollectionView 视差效果,图像起点偏移不正确。 Swift
UICollectionView Parallax effect, offset of image start point is incorrect. Swift
我正在尝试为 iPad 创建类似于 TastemadeCity 中的效果。
仅限景观。 collectionView 由细列(屏幕上 7.5)组成,用于动态数量的图像。
这个想法是当 collectionView 水平滚动时,图像本身几乎不会移动,但单元格会移动穿过它并显示更多图像。
我几乎完美地工作了。但是,当创建单元格时,它会将图像置于单元格的中心,而不是整个屏幕(偏移量为单元格编号或 collectionView 内容偏移量)。因此,当集合滚动时,怪异的图像开始时过于靠右,最终在显示边缘之前用完图像。如果我改变它,我试过使用 Aspect fit 和 aspect fill,它只会将图像缩小到单元格的宽度。它必须保持屏幕的height/width。
为了让事情更简单,我尝试将数组中的所有图像自动裁剪为与设备屏幕相同的形状(我认为代码没问题?)
所以基本上,我如何将 ImageView 设置为屏幕大小,但只有一小部分通过单元格可见,但 collectionView 内容有偏移量?
import UIKit
let parallaxCellIdentifier = "parallaxCell"
class HomeViewController: UICollectionViewController {
var images = [UIImage]()
override func viewDidLoad() {
super.viewDidLoad()
setup()
}
func setup() {
for i in 1...14 {
let img = cropToBounds(UIImage(named:"\(i)@2x")!, width: Double(UIScreen.mainScreen().bounds.size.width), height: Double(UIScreen.mainScreen().bounds.size.height))
images.append(img)
}
}
func cropToBounds(image: UIImage, width: Double, height: Double) -> UIImage {
let contextImage: UIImage = UIImage(CGImage: image.CGImage!)
let contextSize: CGSize = UIScreen.mainScreen().bounds.size
var posX: CGFloat = 0.0
var posY: CGFloat = 0.0
var cgwidth: Double = width
var cgheight: Double = height
if Double(image.size.width/image.size.height) > 1 {
posX = ((contextSize.height - contextSize.width) / 2)
posY = 0
cgwidth = Double(contextSize.width)
cgheight = Double(contextSize.height)
} else if Double(image.size.width/image.size.height) <= 1 {
posX = 0
posY = ((contextSize.width - contextSize.height) / 2)
cgwidth = Double(contextSize.width)
cgheight = Double(contextSize.height)
}
let rect: CGRect = CGRectMake(posX, posY, CGFloat(cgwidth), CGFloat(cgheight))
// Create bitmap image from context using the rect
let imageRef: CGImageRef = CGImageCreateWithImageInRect(contextImage.CGImage, rect)!
// Create a new image based on the imageRef and rotate back to the original orientation
let image: UIImage = UIImage(CGImage: imageRef, scale: image.scale, orientation: image.imageOrientation)
return image
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let parallaxCell = collectionView.dequeueReusableCellWithReuseIdentifier(parallaxCellIdentifier, forIndexPath: indexPath) as! ParallaxCollectionViewCell
parallaxCell.image = images[indexPath.row]
parallaxCell.imageView.contentMode = .ScaleAspectFill
let xOffset = (collectionView.contentOffset.x )
parallaxCell.offset(CGPointMake(xOffset, 0.0)) // this doesnt make any difference at this point, i can remove it. I have tried moving the constraints which works here, but breaks them when scrolling
return parallaxCell
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat {
return 0
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
return 0
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let widthOfPoster = collectionView.frame.size.width / 7.5
let heightOfPoster = collectionView.frame.size.height
return CGSizeMake(widthOfPoster, heightOfPoster)
}
override func scrollViewDidScroll(scrollView: UIScrollView) {
if let visibleCells = collectionView!.visibleCells() as? [ParallaxCollectionViewCell] {
for parallaxCell in visibleCells {
let xOffset = (collectionView!.contentOffset.x )
parallaxCell.offset(CGPointMake(xOffset, 0.0)) // this doesnt seem to do anything here, but works in the sr
}
}
}
}
这是单元格内的函数,它只是在滚动过程中改变偏移量
func offset(offset: CGPoint) {
imageView.frame = CGRectOffset(self.imageView.bounds, offset.x, offset.y)
}
所以我解决了。
我将所有图像裁剪成屏幕的形状。
然后在单元格的子视图中创建了 imageView。
这让我可以控制偏移量。
let parallaxCellIdentifier = "parallaxCell"
class HomeViewController: UICollectionViewController {
var images = [UIImage]()
let OffsetSpeed: CGFloat = 1025
let cellsPerScreen: CGFloat = 7.5
let ImageWidth: CGFloat = UIScreen.mainScreen().bounds.size.width
let imageHeight: CGFloat = UIScreen.mainScreen().bounds.size.height
override func viewDidLoad() {
super.viewDidLoad()
setup()
}
func setup() {
for i in 1...22 {
let img = cropToBounds(UIImage(named:"\(i)@2x")!, width: Double(ImageWidth), height: Double(imageHeight))
images.append(img)
}
}
func cropToBounds(image: UIImage, width: Double, height: Double) -> UIImage {
let contextImage: UIImage = UIImage(CGImage: image.CGImage!)
let posX: CGFloat = 0.0
let posY: CGFloat = 0.0
var cgwidth: Double = width
var cgheight: Double = height
let imageRatio = contextImage.size.width / contextImage.size.height
let contextRatio = CGFloat(width) / CGFloat(height)
if imageRatio >= contextRatio {
cgwidth = Double(contextImage.size.height) * Double(contextRatio)
cgheight = Double(contextImage.size.height)
} else {
cgwidth = Double(contextImage.size.width)
cgheight = (Double(contextImage.size.width) / Double(contextRatio))
}
let rect: CGRect = CGRectMake(posX, posY, CGFloat(cgwidth), CGFloat(cgheight))
// Create bitmap image from context using the rect
let imageRef: CGImageRef = CGImageCreateWithImageInRect(contextImage.CGImage, rect)!
// Create a new image based on the imageRef and rotate back to the original orientation
let image: UIImage = UIImage(CGImage: imageRef, scale: image.scale, orientation: image.imageOrientation)
return image
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let parallaxCell = collectionView.dequeueReusableCellWithReuseIdentifier(parallaxCellIdentifier, forIndexPath: indexPath) as! ParallaxCollectionViewCell
let xOffset = -((UIScreen.mainScreen().bounds.size.width / cellsPerScreen) * CGFloat(indexPath.row))
parallaxCell.offsetX = xOffset
parallaxCell.viewDidLayoutSubviews()
parallaxCell.rangeView.image = images[indexPath.row]
parallaxCell.path = indexPath
return parallaxCell
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat {
return 0
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
return 0
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let widthOfPoster = collectionView.frame.size.width / cellsPerScreen
let heightOfPoster = collectionView.frame.size.height
return CGSizeMake(widthOfPoster, heightOfPoster)
}
override func scrollViewDidScroll(scrollView: UIScrollView) {
if let visibleCells = collectionView!.visibleCells() as? [ParallaxCollectionViewCell] {
for parallaxCell in visibleCells {
let xOffset = (((collectionView!.contentOffset.x ) / ImageWidth) * OffsetSpeed)
parallaxCell.offset(CGPointMake(xOffset, 0.0))
}
}
}
}
和单元格代码。
(RangeImage,只是一个空白的 UIImageView class)
class ParallaxCollectionViewCell: UICollectionViewCell {
let rangeView = RangeImage(frame: CGRectZero)
let width = UIScreen.mainScreen().bounds.size.width
let height = UIScreen.mainScreen().bounds.size.height
var offsetX:CGFloat = 0.0
var path = NSIndexPath()
override func awakeFromNib() {
rangeView.backgroundColor = UIColor.redColor()
contentView.addSubview(rangeView)
}
func viewDidLayoutSubviews() {
rangeView.frame = CGRect(x: offsetX, y: 0.0,
width: width, height: height)
}
func offset(offset: CGPoint) {
contentView.frame = CGRectOffset(contentView.bounds, offset.x, offset.y)
}
}
我正在尝试为 iPad 创建类似于 TastemadeCity 中的效果。 仅限景观。 collectionView 由细列(屏幕上 7.5)组成,用于动态数量的图像。 这个想法是当 collectionView 水平滚动时,图像本身几乎不会移动,但单元格会移动穿过它并显示更多图像。
我几乎完美地工作了。但是,当创建单元格时,它会将图像置于单元格的中心,而不是整个屏幕(偏移量为单元格编号或 collectionView 内容偏移量)。因此,当集合滚动时,怪异的图像开始时过于靠右,最终在显示边缘之前用完图像。如果我改变它,我试过使用 Aspect fit 和 aspect fill,它只会将图像缩小到单元格的宽度。它必须保持屏幕的height/width。
为了让事情更简单,我尝试将数组中的所有图像自动裁剪为与设备屏幕相同的形状(我认为代码没问题?)
所以基本上,我如何将 ImageView 设置为屏幕大小,但只有一小部分通过单元格可见,但 collectionView 内容有偏移量?
import UIKit
let parallaxCellIdentifier = "parallaxCell"
class HomeViewController: UICollectionViewController {
var images = [UIImage]()
override func viewDidLoad() {
super.viewDidLoad()
setup()
}
func setup() {
for i in 1...14 {
let img = cropToBounds(UIImage(named:"\(i)@2x")!, width: Double(UIScreen.mainScreen().bounds.size.width), height: Double(UIScreen.mainScreen().bounds.size.height))
images.append(img)
}
}
func cropToBounds(image: UIImage, width: Double, height: Double) -> UIImage {
let contextImage: UIImage = UIImage(CGImage: image.CGImage!)
let contextSize: CGSize = UIScreen.mainScreen().bounds.size
var posX: CGFloat = 0.0
var posY: CGFloat = 0.0
var cgwidth: Double = width
var cgheight: Double = height
if Double(image.size.width/image.size.height) > 1 {
posX = ((contextSize.height - contextSize.width) / 2)
posY = 0
cgwidth = Double(contextSize.width)
cgheight = Double(contextSize.height)
} else if Double(image.size.width/image.size.height) <= 1 {
posX = 0
posY = ((contextSize.width - contextSize.height) / 2)
cgwidth = Double(contextSize.width)
cgheight = Double(contextSize.height)
}
let rect: CGRect = CGRectMake(posX, posY, CGFloat(cgwidth), CGFloat(cgheight))
// Create bitmap image from context using the rect
let imageRef: CGImageRef = CGImageCreateWithImageInRect(contextImage.CGImage, rect)!
// Create a new image based on the imageRef and rotate back to the original orientation
let image: UIImage = UIImage(CGImage: imageRef, scale: image.scale, orientation: image.imageOrientation)
return image
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let parallaxCell = collectionView.dequeueReusableCellWithReuseIdentifier(parallaxCellIdentifier, forIndexPath: indexPath) as! ParallaxCollectionViewCell
parallaxCell.image = images[indexPath.row]
parallaxCell.imageView.contentMode = .ScaleAspectFill
let xOffset = (collectionView.contentOffset.x )
parallaxCell.offset(CGPointMake(xOffset, 0.0)) // this doesnt make any difference at this point, i can remove it. I have tried moving the constraints which works here, but breaks them when scrolling
return parallaxCell
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat {
return 0
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
return 0
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let widthOfPoster = collectionView.frame.size.width / 7.5
let heightOfPoster = collectionView.frame.size.height
return CGSizeMake(widthOfPoster, heightOfPoster)
}
override func scrollViewDidScroll(scrollView: UIScrollView) {
if let visibleCells = collectionView!.visibleCells() as? [ParallaxCollectionViewCell] {
for parallaxCell in visibleCells {
let xOffset = (collectionView!.contentOffset.x )
parallaxCell.offset(CGPointMake(xOffset, 0.0)) // this doesnt seem to do anything here, but works in the sr
}
}
}
}
这是单元格内的函数,它只是在滚动过程中改变偏移量
func offset(offset: CGPoint) {
imageView.frame = CGRectOffset(self.imageView.bounds, offset.x, offset.y)
}
所以我解决了。 我将所有图像裁剪成屏幕的形状。 然后在单元格的子视图中创建了 imageView。 这让我可以控制偏移量。
let parallaxCellIdentifier = "parallaxCell"
class HomeViewController: UICollectionViewController {
var images = [UIImage]()
let OffsetSpeed: CGFloat = 1025
let cellsPerScreen: CGFloat = 7.5
let ImageWidth: CGFloat = UIScreen.mainScreen().bounds.size.width
let imageHeight: CGFloat = UIScreen.mainScreen().bounds.size.height
override func viewDidLoad() {
super.viewDidLoad()
setup()
}
func setup() {
for i in 1...22 {
let img = cropToBounds(UIImage(named:"\(i)@2x")!, width: Double(ImageWidth), height: Double(imageHeight))
images.append(img)
}
}
func cropToBounds(image: UIImage, width: Double, height: Double) -> UIImage {
let contextImage: UIImage = UIImage(CGImage: image.CGImage!)
let posX: CGFloat = 0.0
let posY: CGFloat = 0.0
var cgwidth: Double = width
var cgheight: Double = height
let imageRatio = contextImage.size.width / contextImage.size.height
let contextRatio = CGFloat(width) / CGFloat(height)
if imageRatio >= contextRatio {
cgwidth = Double(contextImage.size.height) * Double(contextRatio)
cgheight = Double(contextImage.size.height)
} else {
cgwidth = Double(contextImage.size.width)
cgheight = (Double(contextImage.size.width) / Double(contextRatio))
}
let rect: CGRect = CGRectMake(posX, posY, CGFloat(cgwidth), CGFloat(cgheight))
// Create bitmap image from context using the rect
let imageRef: CGImageRef = CGImageCreateWithImageInRect(contextImage.CGImage, rect)!
// Create a new image based on the imageRef and rotate back to the original orientation
let image: UIImage = UIImage(CGImage: imageRef, scale: image.scale, orientation: image.imageOrientation)
return image
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let parallaxCell = collectionView.dequeueReusableCellWithReuseIdentifier(parallaxCellIdentifier, forIndexPath: indexPath) as! ParallaxCollectionViewCell
let xOffset = -((UIScreen.mainScreen().bounds.size.width / cellsPerScreen) * CGFloat(indexPath.row))
parallaxCell.offsetX = xOffset
parallaxCell.viewDidLayoutSubviews()
parallaxCell.rangeView.image = images[indexPath.row]
parallaxCell.path = indexPath
return parallaxCell
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat {
return 0
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
return 0
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let widthOfPoster = collectionView.frame.size.width / cellsPerScreen
let heightOfPoster = collectionView.frame.size.height
return CGSizeMake(widthOfPoster, heightOfPoster)
}
override func scrollViewDidScroll(scrollView: UIScrollView) {
if let visibleCells = collectionView!.visibleCells() as? [ParallaxCollectionViewCell] {
for parallaxCell in visibleCells {
let xOffset = (((collectionView!.contentOffset.x ) / ImageWidth) * OffsetSpeed)
parallaxCell.offset(CGPointMake(xOffset, 0.0))
}
}
}
}
和单元格代码。 (RangeImage,只是一个空白的 UIImageView class)
class ParallaxCollectionViewCell: UICollectionViewCell {
let rangeView = RangeImage(frame: CGRectZero)
let width = UIScreen.mainScreen().bounds.size.width
let height = UIScreen.mainScreen().bounds.size.height
var offsetX:CGFloat = 0.0
var path = NSIndexPath()
override func awakeFromNib() {
rangeView.backgroundColor = UIColor.redColor()
contentView.addSubview(rangeView)
}
func viewDidLayoutSubviews() {
rangeView.frame = CGRect(x: offsetX, y: 0.0,
width: width, height: height)
}
func offset(offset: CGPoint) {
contentView.frame = CGRectOffset(contentView.bounds, offset.x, offset.y)
}
}