Search code examples
iosswiftcgsize

Swift -how to make a smaller version of a 16:9 video thumbnail but keep the aspect ratio the same


The aspect ratio for a 1080p hd video is 16:9 and to set a frame for the video in a cell so that it would fill it up completely I would use view.frame.width * 9 / 16:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

    // the collection view is pinned to both sides of the vc with no spacing
    let width = collectionView.frame.width

    let videoHeight: CGFloat = width * 9 / 16

    return CGSize(width: width, height: videoHeight)
}

// inside the cell itself:

let videoHeight: CGFloat = self.frame.width * 9 / 16

contentView.addSubview(thumbnailImageView)
thumbnailImageView.topAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
thumbnailImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
thumbnailImageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true

// here is where I set the thumbnail's height
thumbnailImageView.heightAnchor.constraint(equalToConstant: videoHeight).isActive = true

This perfectly gives me:

enter image description here

The thing is I want a smaller version of the thumbnailImageView similar to youtube. I want to add the smaller thumbnailImageView on the left side of the screen but keep the aspect ratio the same.

enter image description here

The problem is when I attempted to do so I got a square instead of a rectangle. I divided the width of the cell by 3 and then multiplied it by 9 /16 but that isn't working. I used (width / 3) because I though it would keep the aspect ratio the same but reduce the size of the thumbnailImageView but it didn't work.

enter image description here

Where am I going wrong at?

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

    // the collection view is pinned to both sides of the vc with no spacing
    let width = collectionView.frame.width

    let videoHeight: CGFloat = (width / 3) * 9 / 16

    return CGSize(width: width, height: videoHeight)
}

// inside the cell itself
let videoHeight = (self.frame.width / 3) * 9 / 16

contentView.addSubview(thumbnailImageView)
thumbnailImageView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8).isActive = true
thumbnailImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8).isActive = true

// here is where I set the thumbnail's height and width
thumbnailImageView.heightAnchor.constraint(equalToConstant: videoHeight).isActive = true
thumbnailImageView.widthAnchor.constraint(equalToConstant: videoHeight).isActive = true

Solution

  • You can use AVMakeRect(aspectRatio:insideRect:) function to get a scaled rectangle that maintains the specified aspect ratio within a bounding rectangle.

    You can refer the API here.

    By using the above API, your videoHeight value would be populated like below.

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        // the collection view is pinned to both sides of the vc with no spacing
        let width = collectionView.frame.width
        let rect = AVMakeRect(aspectRatio: CGSize(width: 16, height: 9), insideRect: CGRect(origin: .zero, size: CGSize(width: width / 3, height: CGFloat.infinity)))
        let videoHeight: CGFloat = rect.size.height
        return CGSize(width: width, height: videoHeight)
    }
    

    And in cell, you can simply have height as follows.

    let videoHeight = self.frame.height