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)
    }
}