首页 > 解决方案 > 苹果自定义布局算法如何保持纵横比?

问题描述

我正在尝试创建一个自定义布局,如下所示。目前,我的单元格正在通过硬编码它们的尺寸并将单元格内部设置contentmode为和来调整大小。图像显示为缩放。如下所示,如何实现在不同单元格尺寸下保持纵横比的布局。在下面的布局中,高度和宽度似乎是前缀。 UIImageView.scaleAspectFillclipsToBounds = true

[ 自定义布局 1] 1 [ 自定义布局 2]

我对自定义布局代码的尝试

import UIKit

    class gridLayouts{
        static var screenHeight = UIScreen.main.bounds.height
        static var screenWidth = UIScreen.main.bounds.width
        var xOffsets:[CGFloat]
        var yOffsets:[CGFloat]
        var itemWidths:[CGFloat]
        var itemHeights:[CGFloat]
        var blockHeight:CGFloat?
        init(xOffsets:[CGFloat],yOffsets:[CGFloat],itemWidths:[CGFloat], itemHeights:[CGFloat],blockHeight:CGFloat) {
            self.xOffsets = xOffsets
            self.yOffsets = yOffsets
            self.itemWidths = itemWidths
            self.itemHeights = itemHeights
            self.blockHeight = blockHeight
        }
        func calcBlockHeight()->CGFloat{
            var blockHeight:CGFloat = 0
            for item in 0...itemWidths.count-1{
                if self.xOffsets[item] + self.itemWidths[item] == 
                UIScreen.main.bounds.width{ ((self.itemWidths[item]+xOffsets[item])/UIScreen.main.bounds.width)")
                    blockHeight += self.itemHeights[item]
                }

            }
            return blockHeight
        }

        func updateYoffSets(previousBlockHeight:CGFloat)->[CGFloat]{
            var updatedYoffsets = self.yOffsets
            for item in 0...self.yOffsets.count - 1{
                updatedYoffsets[item] += previousBlockHeight
            }
            return updatedYoffsets
        }
    }



    class customCollectionViewLayout:UICollectionViewLayout{

        // 2
        fileprivate var numberOfColumns = 2
        fileprivate var cellPadding: CGFloat = 0

        // 3
        fileprivate var cache = [UICollectionViewLayoutAttributes]()

        // 4
        fileprivate var contentHeight: CGFloat = 0

        fileprivate var contentWidth: CGFloat {
            guard let collectionView = collectionView else {
                return 0
            }
            let insets = collectionView.contentInset
            return collectionView.bounds.width - (insets.left + insets.right)
        }

        // 5
        override var collectionViewContentSize: CGSize {
            return CGSize(width: contentWidth, height: contentHeight)
        }



        override func prepare() {
            // 1
            guard cache.isEmpty == true, let collectionView = collectionView else {
                return
            }
            setUpLayout()
            let screenWidth = UIScreen.main.bounds.width
            let screenHeight = UIScreen.main.bounds.height

            // 3
            var totalBlockHeight:CGFloat = 0
            var currentgridLayout:gridLayouts = appleLayout
            var updatedYoffSets:[CGFloat] = [CGFloat]()
            for item in 0 ..< collectionView.numberOfItems(inSection: 0) {

                let indexPath = IndexPath(item: item, section: 0)

                if item%8 == 0{
                    print("\(item): Im divisible by 8")


                    if (collectionView.numberOfItems(inSection: 0) - item < 8){
                        print("Im inside the if \(item)")
                        switch (collectionView.numberOfItems(inSection: 0) - item )%8{
                        case 0:
                            print("Im in case 0")
                            currentgridLayout = NextLayout()
                        case 1:
                            print("Im in case 1")
                            currentgridLayout = gridLayouts(xOffsets: [0], yOffsets: [0], itemWidths: [screenWidth], itemHeights: [screenHeight/3], blockHeight: screenHeight/3)
                        case 2:
                            print("Im in case 2")
                            currentgridLayout = gridLayouts(xOffsets: [0,screenWidth/2], yOffsets: [0,0], itemWidths: [screenWidth/2,screenWidth/2], itemHeights: [screenHeight/3], blockHeight: screenHeight/3)
                        case 3:
                            print("Im in case 3")
                            currentgridLayout = gridLayouts(xOffsets: [0,screenWidth/3,2*screenWidth/3], yOffsets: [0,0,0], itemWidths: [screenWidth/3,screenWidth/3,screenWidth/3], itemHeights: [screenHeight/3,screenHeight/3,screenHeight/3], blockHeight: screenHeight/3)
                        case 4:
                            print("Im in case 4")
                            currentgridLayout = gridLayouts(xOffsets: [0,2*screenWidth/3,0,screenWidth/3], yOffsets: [0,0,screenHeight/3,screenHeight/3], itemWidths: [2*screenWidth/3,screenWidth/3,screenWidth/3,2*screenWidth/3], itemHeights: [screenHeight/3,screenHeight/3,screenHeight/3,screenHeight/3], blockHeight: 2*screenHeight/3)
                        case 5:
                            print("Im in case 5")
                            currentgridLayout = gridLayouts(xOffsets: Array(appleLayout.xOffsets.prefix(through: 4)), yOffsets: Array(appleLayout.yOffsets.prefix(through: 4)), itemWidths: Array(appleLayout.itemWidths.prefix(through: 4)), itemHeights: Array(appleLayout.itemHeights.prefix(through: 4)), blockHeight: appleLayout.blockHeight!)
                            print("currentgridLayout.xOffsets.count: \(currentgridLayout.xOffsets.count)")
                        case 6:
                            print("Im in case 6")
                            currentgridLayout = gridLayouts(xOffsets: [0,2*screenWidth/3,2*screenWidth/3,0,screenWidth/3,2*screenWidth/3], yOffsets: [0,0,0,2*screenHeight/3,2*screenHeight/3,2*screenHeight/3], itemWidths: [2*screenWidth/3,screenWidth/3,screenWidth/3,screenWidth/3,screenWidth/3,screenWidth/3], itemHeights: [2*screenHeight/3,screenHeight/3,screenHeight/3,screenHeight/3,screenHeight/3,screenHeight/3], blockHeight: screenHeight)
                        case 7:
                            print("Im in case 7")
                            currentgridLayout = gridLayouts(xOffsets: [0,2*screenWidth/3,2*screenWidth/3,0,screenWidth/3,2*screenWidth/3,0], yOffsets: [0,0,0,2*screenHeight/3,2*screenHeight/3,2*screenHeight/3,screenHeight], itemWidths: [2*screenWidth/3,screenWidth/3,screenWidth/3,screenWidth/3,screenWidth/3,screenWidth/3,screenWidth], itemHeights: [2*screenHeight/3,screenHeight/3,screenHeight/3,screenHeight/3,screenHeight/3,screenHeight/3,2*screenHeight/3], blockHeight: screenHeight+2*screenHeight/3)
                        default:
                            print("Im in the default case")
                            currentgridLayout = NextLayout()
                        }


                    }


                    updatedYoffSets = currentgridLayout.updateYoffSets(previousBlockHeight: totalBlockHeight)
                    totalBlockHeight += currentgridLayout.calcBlockHeight()


                }

                let frame = CGRect(x: currentgridLayout.xOffsets[item%8], y: updatedYoffSets[item%8], width: currentgridLayout.itemWidths[item%8], height: currentgridLayout.itemHeights[item%8])
                //

                var insetFrame = frame.insetBy(dx: cellPadding, dy: cellPadding)
                //changed
                insetFrame = frame.insetBy(dx: 2, dy: 2)
                // 5
                let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
                attributes.frame = insetFrame
                cache.append(attributes)

                // 6
                contentHeight = max(contentHeight, frame.maxY)
                //yOffset[column] = yOffset[column] + blockHeight
                //push the y insets down
            }
            print("The block height is \(currentgridLayout.calcBlockHeight())")
            print("The block height is \(currentgridLayout.calcBlockHeight()/UIScreen.main.bounds.height) screens")
        }

        func NextYOffSet( yOffset:[CGFloat],previousBlockHeight:CGFloat)->[CGFloat]{
            var offSet = yOffset
            for item in 0...offSet.count{
                offSet[item] += previousBlockHeight
            }
            return offSet
        }

        func NextLayout()->gridLayouts{
            let rand = Int(arc4random_uniform(UInt32(2)))

            print("rand num \(rand)")
            switch rand {
            case 0:
                print("Apple Layout Returned")
                return appleLayout
            default:
                print("My Layout Returned")
                return myLayout
            }
        }


        var myLayout:gridLayouts!
        var appleLayout:gridLayouts!
        func setUpLayout(){
            let screenWidth = gridLayouts.screenWidth
            let screenHeight = gridLayouts.screenHeight
            myLayout = gridLayouts(
                xOffsets: [0,screenWidth/4,screenWidth/2,0,screenWidth/4,0,screenWidth/2,0],
                yOffsets: [0,0,0,screenHeight/4,screenHeight/4,screenHeight/2,screenHeight/2,screenHeight],
                itemWidths: [screenWidth/4,screenWidth/4,screenWidth/2,screenWidth/4,screenWidth/4,screenWidth/2,screenWidth/2,screenWidth],
                itemHeights: [screenHeight/4,screenHeight/4,screenHeight/2,screenHeight/4,screenHeight/4,screenHeight/2,screenHeight/2,screenHeight],
                blockHeight: 2*screenHeight)

            appleLayout = gridLayouts(xOffsets: [0,screenWidth/3,0,screenWidth/3,2*screenWidth/3,0,2*screenWidth/3,2*screenWidth/3], yOffsets: [0,0,screenHeight/3,screenHeight/3,screenHeight/3,2*screenHeight/3,2*screenHeight/3,screenHeight], itemWidths: [screenWidth/3,2*screenWidth/3,screenWidth/3,screenWidth/3,screenWidth/3,2*screenWidth/3,screenWidth/3,screenWidth/3], itemHeights: [screenHeight/3,screenHeight/3,screenHeight/3,screenHeight/3,screenHeight/3,2*screenHeight/3,screenHeight/3,screenHeight/3], blockHeight: 4*screenHeight/3)

            print("myLayout blockHeight = \(myLayout.calcBlockHeight()/screenHeight)")
            print("appleLayout blockHeight = \(appleLayout.calcBlockHeight()/screenHeight)")
        }

        override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {

            var visibleLayoutAttributes = [UICollectionViewLayoutAttributes]()

            // Loop through the cache and look for items in the rect
            for attributes in cache {
                if attributes.frame.intersects(rect) {
                    visibleLayoutAttributes.append(attributes)
                }
            }
            return visibleLayoutAttributes
        }

        override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
            return cache[indexPath.item]
        }

    }

    extension Array where Iterator.Element == CGFloat{
        mutating func incrementBy(_ amount:CGFloat){

            for element in 0...self.count-1{
                self[element] += amount
            }
        }

        func incrementedArray(_ amount:CGFloat)->[CGFloat]{
            var incrementedArray = self
            for element in 0...incrementedArray.count-1{
                incrementedArray[element] += amount
            }
            return incrementedArray
        }
    }

标签: iosswiftxcodeuicollectionviewuiimageview

解决方案


推荐阅读