Search code examples
iosswiftswift3autolayout

About auto layouts


As I have given auto layout for the collection view but in portrait it was fine:

as I have given auto layout for the collection view but in portrait it was fine

The problem is that where as coming to landscape the gap between the cells are increasing. How to avoid this?

the problem is that where as coming to landscape the gap between the cells are increasing how to avoid this ?


import UIKit

class ViewController: UIViewController,UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout{
    @IBOutlet weak var collectionView: UICollectionView!
    var images = ["apple","banana","cherry","grapes","kiwifruit","mangoes","orange","papaya","passionfruit","peaches","pineapple","strawberry","sugarapple","watermelon"]

    override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(self, selector: #selector(orientationChanged(notification:)), name: Notification.Name.UIDeviceOrientationDidChange, object: nil)
        collectionView.dataSource = self
        collectionView.delegate = self

    }
    func orientationChanged(notification: Notification) {
        collectionView.collectionViewLayout.invalidateLayout()
    }
        func numberOfSections(in collectionView: UICollectionView) -> Int {
            return 1
        }

        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return images.count
        }

        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "stridentifier",for:indexPath) as! FruitsCollectionViewCell


            cell.image.image = UIImage(named: images[indexPath.row])
            cell.nameLabel.text = images[indexPath.row]

            return cell
        }
    class SampleCollectionViewFlowLayout: UICollectionViewFlowLayout {
        override init() {
            super.init()
            setUpLayout()
        }

        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            setUpLayout()
        }

        override var itemSize: CGSize {
            set {}
            get {
                let itemWidth = ((self.collectionView?.bounds.width)! / 3.0) - self.minimumLineSpacing - minimumInteritemSpacing
                return CGSize(width: itemWidth, height: itemWidth)
            }
        }

        func setUpLayout() {
            minimumInteritemSpacing = 0
            minimumLineSpacing = 1.0
            scrollDirection = .vertical
        }

        override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
            return true 
        }
    }
}

Solution

  • Though i am bit late in this thread.But my solution will work in every devices according to your specification. 2 cells in portrait and 3 cells in landscape.But you can increase cell number according to your choice just modify NUMBER_OF_CELLS_IN_PORTRAIT and NUMBER_OF_CELLS_IN_LANDSCAPE

    I have made comment in every function.So it may be understandable.

    At first i made some default value .

    let SCREEN_WIDTH = UIScreen.main.bounds.size.width
    let SCREEN_HEIGHT = UIScreen.main.bounds.size.height
    
    let BASE_SCREEN_HEIGHT:CGFloat = 736.0
    let SCREEN_MAX_LENGTH = max(SCREEN_WIDTH, SCREEN_HEIGHT)
    let ASPECT_RATIO_RESPECT_OF_7P = SCREEN_MAX_LENGTH / BASE_SCREEN_HEIGHT
    
    let MINIMUM_INTERITEM_SPACING:CGFloat = 14.0 //My Default Inter Cell Spacing for iphone 7plus as i have designed in it.
    var ITEM_WIDTH:CGFloat  = 200 //for iPhone 7 plus.initial value
    var ITEM_HEIGHT:CGFloat = 200 //for iphone 7 plus.initial value
    let NUMBER_OF_CELLS_IN_PORTRAIT:CGFloat = 2
    let NUMBER_OF_CELLS_IN_LANDSCAPE:CGFloat = 3 
    

    Then in viewDidLoad i initialize the collection view with the following function

    func initialCollectionData() {
        let orientation = UIApplication.shared.statusBarOrientation
        if orientation == .portrait {
            self.calculateCellSize(screenWidth:SCREEN_WIDTH ,cellItem: NUMBER_OF_CELLS_IN_PORTRAIT)
        }
        else {
            self.calculateCellSize(screenWidth:SCREEN_WIDTH ,cellItem: NUMBER_OF_CELLS_IN_LANDSCAPE)
        }
    }
    

    And there are some helper function. Like this.

    //This method calculate cellsize according to cell number.
    func  calculateCellSize(screenWidth:CGFloat , cellItem:CGFloat) {
        ITEM_WIDTH = (screenWidth - MINIMUM_INTERITEM_SPACING * ASPECT_RATIO_RESPECT_OF_7P  * (cellItem-1)) / cellItem  - 1// This 1 has been subtracted from ITEM_WIDTH to remove mantissa
        ITEM_HEIGHT = ITEM_WIDTH
    }
    
    //This method calculate cell number according to orientation.
    func findCellItem(screenWidth:CGFloat)  {
        let orientation = UIDevice.current.orientation
        if orientation == .portrait {
            self.calculateCellSize(screenWidth:screenWidth ,cellItem: NUMBER_OF_CELLS_IN_PORTRAIT) //You have chosen 2 cells to show in portrait
        }
        else {
            self.calculateCellSize(screenWidth:screenWidth ,cellItem: NUMBER_OF_CELLS_IN_LANDSCAPE) ////You have chosen 3 cells to show in portrait
        }
    }
    
    //During orientation change this method is called everytime.
    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    
        super.viewWillTransition(to: size, with: coordinator)
        coordinator.animate(alongsideTransition: { context in
    
            context.viewController(forKey: UITransitionContextViewControllerKey.from)
            //Below two methods do the trick
            self.findCellItem(screenWidth: size.width)
            self.collectionView.collectionViewLayout.invalidateLayout()
        }, completion: {
            _ in
        })
    
    }
    

    //Collection View delegate methods

    public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    
        let collectionCell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
    
        collectionCell.fruitImage.image = UIImage.init(named: imageArray[indexPath.row])
        return collectionCell
    
    
    }
    
    
     public func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize.init(width: ITEM_WIDTH , height: ITEM_HEIGHT )
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
       return MINIMUM_INTERITEM_SPACING * ASPECT_RATIO_RESPECT_OF_7P
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return MINIMUM_INTERITEM_SPACING * ASPECT_RATIO_RESPECT_OF_7P
    }