Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scale image to fit - iOS

I have a UICollectionView that displays 12 images. The images are not fitting in the cells, they are being cropped, but I want them to fit in the cells without being cropped.

The code that should make the image scale to fit is:

 cell.imageView.contentMode = UIViewContentMode.ScaleAspectFit
 cell.imageView.image = UIImage(named: name)

Here is my code for the whole viewController class and screenshot:

import UIKit

class DressingRoomViewController:
        UIViewController,
        UICollectionViewDelegateFlowLayout,
        UICollectionViewDataSource {

    @IBOutlet weak var collectionView: UICollectionView!

    let identifier = "cellIdentifier"
    let dataSource = DataSource()

    override func viewDidLoad() {
        super.viewDidLoad()
        collectionView.dataSource = self
    }

    override func viewDidAppear(animated: Bool) {
        //  Notes on the equation to get the cell size:
        //  cells per row = 6
        //  cell spacing = 10
        //  collectionView.layout.inset = 20 (10 left, 10 right)
        let cellSize = (collectionView.collectionViewLayout
            .collectionViewContentSize().width - 20) / 6 - 10

        let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()

        layout.sectionInset = UIEdgeInsets( top: 20,
                                            left: 10,
                                            bottom: 10,
                                            right: 10)

        layout.itemSize = CGSize(width: cellSize, height: cellSize)

        collectionView.collectionViewLayout = layout
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    override func prepareForSegue(  segue: UIStoryboardSegue,
                                    sender: AnyObject?) {
        if (segue.identifier == "dressingRoom2MyOutfits") {
            let myOutfitsViewController = segue.destinationViewController
                as! MyOutfitsViewController
        }
    }
}



// MARK:- UICollectionViewDataSource Delegate
extension DressingRoomViewController : UICollectionViewDataSource {

    func numberOfSectionsInCollectionView(
        collectionView: UICollectionView) -> Int {
            return 1
    }

    func collectionView(collectionView: UICollectionView,
        numberOfItemsInSection section: Int) -> Int {
            return 12
    }

    func collectionView(collectionView: UICollectionView,
        cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

        let cell = collectionView.dequeueReusableCellWithReuseIdentifier(
            identifier,forIndexPath:indexPath) as! FruitCell

        let fruits: [Fruit] = dataSource.fruits
        let fruit = fruits[indexPath.row]

        let name = fruit.name!

        cell.imageView.contentMode = UIViewContentMode.ScaleAspectFit
        cell.imageView.image = UIImage(named: name)

        return cell
    }
}

EDIT: Here is the FruitCell class, just in case you were wondering.

    class FruitCell: UICollectionViewCell {

    @IBOutlet weak var imageView: UIImageView!

}

enter image description here

like image 354
BeniaminoBaggins Avatar asked Nov 18 '25 07:11

BeniaminoBaggins


1 Answers

A UIImageView that has been placed in a UICollectionViewCell from the interface builder, will not have any knowledge of a UICollectionViewFlowLayout that has been initialised programmatically. So the layout.sectionInset that I had set, was making the UICollectionViewCells smaller, and even when the UIImageView created in interface builder was constrained to the margins of the UICollectionViewCell, the UIImageView was not resizing to go smaller.

The solution was to initialise the UIImageView programmatically and set the size to be the size of the cell taking into account the cell spacing.

Here is the code:

import UIKit

class DressingRoomViewController:
        UIViewController,
        UICollectionViewDelegateFlowLayout,
        UICollectionViewDataSource {

    @IBOutlet weak var collectionView: UICollectionView!

    let identifier = "cellIdentifier"
    let dataSource = DataSource()
    let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
    let cellSpacing: CGFloat = 2
    let cellsPerRow: CGFloat = 6


    override func viewDidLoad() {
        super.viewDidLoad()
        collectionView.dataSource = self
    }

    override func viewDidAppear(animated: Bool) {
        let cellSize = (collectionView.collectionViewLayout
            .collectionViewContentSize().width / cellsPerRow) - (cellSpacing)

        layout.itemSize = CGSize(width: cellSize, height: cellSize)
        layout.minimumInteritemSpacing = cellSpacing
        layout.minimumLineSpacing = cellSpacing
        collectionView.collectionViewLayout = layout
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    override func prepareForSegue(  segue: UIStoryboardSegue,
                                    sender: AnyObject?) {
        if (segue.identifier == "dressingRoom2MyOutfits") {
            let myOutfitsViewController = segue.destinationViewController
                as! MyOutfitsViewController
        }
    }
}



// MARK:- UICollectionViewDataSource Delegate
extension DressingRoomViewController : UICollectionViewDataSource {

    func numberOfSectionsInCollectionView(
        collectionView: UICollectionView) -> Int {
            return 1
    }

    func collectionView(collectionView: UICollectionView,
        numberOfItemsInSection section: Int) -> Int {
            return 12
    }

    func collectionView(collectionView: UICollectionView,
        cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

        let cell = collectionView.dequeueReusableCellWithReuseIdentifier(
            identifier,forIndexPath:indexPath) as! FruitCell

        let fruits: [Fruit] = dataSource.fruits
        let fruit = fruits[indexPath.row]

        let name = fruit.name!

        var imageView :UIImageView
        imageView = UIImageView(frame:CGRectMake(   0,
                                                    0,
            (collectionView.collectionViewLayout
                .collectionViewContentSize().width / cellsPerRow)
                - (cellSpacing),
            (collectionView.collectionViewLayout
                .collectionViewContentSize().width / cellsPerRow)
                - (cellSpacing)))

        imageView.contentMode = UIViewContentMode.ScaleAspectFit
        imageView.image = UIImage(named: name)
        imageView.backgroundColor = UIColor.redColor()
        cell.addSubview(imageView)

        return cell
    }
}

EDIT: I was able to crop off the empty space at the bottom of the UICollectionView by creating an outlet for the height constraint ( control + drag height constraint in interface builder onto viewController swift file) calling it heightConstraint, and then at the bottom of viewDidAppear added these two lines of code:

self.heightConstraint.constant = collectionView.contentSize.height
self.view.layoutIfNeeded()

So to show you the result with a white background on the UICollectionView and a red background on the UIImageViews, here is the result (also works on all other device sizes):

enter image description here

like image 173
BeniaminoBaggins Avatar answered Nov 20 '25 19:11

BeniaminoBaggins



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!