Search code examples
iosswiftios9phasset3dtouch

Calculate preferredContentSize based on PHAsset size for a Preview View Controller


I am implementing a 3D Touch preview (peek) view controller to show upon firmly pressing on a PHAsset. The view controller that I return from previewingContext:viewControllerForLocation: simply shows the photo full screen. I'm currently not setting the preferredContentSize on that view controller, so the system sizes it as desired. Therefore, there is some extra padding above and below the photo when it doesn't fit in the frame exactly (or the photo is cropped if I use ScaleAspectFill instead of Fit). I would like to ensure that does not occur - the photo should fit in the preview frame exactly, without extra padding and without cropping. My question is, how can the correct size be determined?

PHAsset has pixelWidth and pixelHeight that could be very useful. But in Apple's sample code, it states the width provided should be zero when setting the content size, because it's not used in portrait. And I can confirm it does ignore the width in portrait but not in landscape. So I thought I could detect the view's width in the preview view controller, in viewWillAppear for example, and use that to perform a calculation, but this returns the same width as if it were full screen (375 for iPhone 6s).

How can one set the preferredContentSize to fit the aspect ratio of a full-sized image?

//Width should be zero, because it's not used in portrait.
photoViewController.preferredContentSize = CGSize(width: 0.0, height: 0.0)

Note that the Dropbox app behaves as desired - the preview window is the right size for the photo being previewed. And of course the Photos app as well.


Solution

  • Pondering on this some more, I came up with a nice solution. Just a bit of math. :)

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
    
        if self.isShowingAsPreview {
            let photoWidth = self.asset.pixelWidth
            let photoHeight = self.asset.pixelHeight
            let viewWidth = self.view.frame.size.width
            let widthAspectRatio = CGFloat(photoWidth) / viewWidth
    
            self.preferredContentSize = CGSizeMake(0, CGFloat(photoHeight) / widthAspectRatio)
        }
    }
    

    Because the width is ignored in portrait, you'll probably want to set your imageView's contentMode to .ScaleAspectFill when peeked, and .ScaleAspectFit when popped.