Landscape vs. Portrait background image in Asset Catalog
Asked Answered
S

2

1

I have a use case that seems to fall into the cracks between asset catalogs and size classes. My Universal app needs a full screen background image that is different in Landscape and Portrait orientations, and Landscape is only supported for the iPad & iPhone 6.

Since I can't add landscape images to the asset catalog for a new image set, my current solution looks like this (supports iOS 7 & 8):

// permit landscape mode only for iPads & iPhone 6 (would prefer to use size classes, but...?)
override func shouldAutorotate() -> Bool {
    let size = view.frame.size
    let maxv = max(size.width, size.height)
    return ((maxv > 700.0) || (maxv == 512.0)) ? true : false
}

// this will be triggered only for iOS 7, as long as viewWillTransitionToSize:withTransitionCoordinator: is also implemented!
override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {
    adjustToOrientation(toInterfaceOrientation)
}

// this will be triggered only for iOS 8
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {

    let orientation = UIApplication.sharedApplication().statusBarOrientation

    // need to reverse the sense of the orientation here; Application still has the previous orientation
    switch orientation {
        case .Portrait, .PortraitUpsideDown:
            adjustToOrientation(.LandscapeLeft)
        case .LandscapeLeft, .LandscapeRight:
            adjustToOrientation(.Portrait)
        default:
            adjustToOrientation(.Portrait)
    }

}

func adjustToOrientation(newInterfaceOrientation: UIInterfaceOrientation) {

    let size = view.frame.size

    // rotation already permitted only for iPad & iPhone 6, but need to know which one (size classes useless here?)
    switch (max(size.width, size.height)) {
        case 1024.0:
            if UIInterfaceOrientationIsLandscape(newInterfaceOrientation) {
                backgroundImage.image = UIImage(named: "Background-Landscape@2x~ipad.png")
            } else {
                backgroundImage.image = UIImage(named: "Background@2x~ipad.png")
            }
        case 736.0:
            if UIInterfaceOrientationIsLandscape(newInterfaceOrientation) {
                backgroundImage.image = UIImage(named: "Background-Landscape@3x~iphone.png")
            } else {
                backgroundImage.image = UIImage(named: "Background@3x~iphone.png")
            }
        case 512.0:
            if UIInterfaceOrientationIsLandscape(newInterfaceOrientation) {
                backgroundImage.image = UIImage(named: "Background-Landscape~ipad.png")
            } else {
                backgroundImage.image = UIImage(named: "Background~ipad.png")
            }
        default:
            break
    }

}

This works, but seems fragile. Is there a more correct way to identify the device as something that supports a regular size class in Landscape, before actually being in Landscape mode? Or some way that I've missed to specify the Landscape equivalents for an image set?

Swarth answered 24/9, 2014 at 20:43 Comment(0)
U
0

You can use size classes in assets catalog or you can have two image sets: one for portrait and one for landspace mode. Then you can access images by asset name. This will reduce your code a little. But I'd recommend to cosider using size classes everywhere it's possible.

Undergraduate answered 25/9, 2014 at 3:49 Comment(3)
Thanks, Vitaly... I've in fact eliminated most of the code after watching the WWDC14 talk on trait collections (Building Adaptive Apps With UIKit). UIImage does recognise those additional size classes in an image set, and adjusts automatically as the trait collection changes. I set the Width property on the image asset to Any+Regular and added a couple of Landscape images, and that was it.Swarth
So, there's no way to add landscape images in the asset catalog for a new image set?Roughen
@arh Sorry for late reply. You can add different images for different size classes. So, in case your landscape and portrait modes have different size classes you will get different images but when landscape and portrait modes use same size classes (like on iPad) you won't be able to use different images for such modes using size classes. In case you really need to use different images for landscape and portrait modes you have to think on using different images sets for such modes or don't using images sets for such images at all.Undergraduate
S
0

So the final solution was:

-Set the Width property on the image asset to Any+Regular and added a couple of Landscape images for the iPhone 6+ and iPad, and UIImage automatically does the rest

-A more elegant way to permit rotation only for those two devices, in both iOS 7 & iOS 8:

override func shouldAutorotate() -> Bool {
    let scale = UIScreen.mainScreen().scale
    let idiom = UIDevice.currentDevice().userInterfaceIdiom
    return ((idiom == .Pad) || (scale > 2.0)) ? true : false
}

So all of the above down to 5 lines of code!

Swarth answered 25/9, 2014 at 16:38 Comment(1)
Alas, not a perfect solution after all; it seems UIImage's trait collection support can't tell the difference between an iPad in landscape & portrait mode; simulator screenshots reveal that the landscape image gets used for iOS 8 in both orientations, and the portrait image for iOS 7 !?!Swarth

© 2022 - 2024 — McMap. All rights reserved.