How to specify size for iPhone 6/7 customised edge-to-edge image?
Asked Answered
S

12

60

Say I want a bundled image to take up all available screen width in an iPhone app - for example a banner. I'd create my_banner.png with width 320px, [email protected] with width 640px and [email protected] for iPhone 6 plus with width 1242px. But the resolution of iPhone 6 is 750×1334 pixels. Still it shares the @2x suffix with iPhone 4 and 5 that have 640px width.

What's the recommended way or a good way to specify an image file that has been optimised for the 750px width of iPhone 6? Seems like it cannot be done in an asset catalog? Should it be done programatically? Is there some other suffix that can be used for iPhone 6?

iPhone 4,5,6 screen sizes (Image extracted from http://www.iphoneresolution.com)

Sushi answered 17/9, 2014 at 13:39 Comment(18)
Didn't Apple say that i6+ images are @3x?Doubly
iPhone 5 and iPhone 6 has same screen ratio. You should prepare your 2x images for the iPhone 6 resolution and it will be down sampled for the iPhone 5 without a problem.Albuminate
@Albuminate Screen ratio isn't the issue. (The 6+ has the same ratio as the 6 and the 5.) The resolution is what matters here. While the best solution given current knowledge is to re-create all current 2x images using the iPhone 6 resolution and let iOS downsample them for the iPhone 5, it doesn't directly answer the question of how (if possible) to specify separate x640 or x750 image sizes for iPhone 5/6.Feudatory
@CraigOtis You can create multiple images in the assets catalog and use them after you check the screen bounds. But it would be unneccecary overkill in my opinion.Albuminate
@Albuminate Ah - I think that may be the answer OP is looking for.Feudatory
@CraigOtis Yes, maybe. But he really shouldn't do that.Albuminate
@Albuminate Agreed. I was hoping (for OP) that there would be some similar bonus prefix like when Apple introduced the iPhone 5. Something like: [email protected] and [email protected]Feudatory
what about img-667h@2x, have you tried it?Pairoar
what did you end up using? i have this problem too :SHyaline
@joãonunes how you solved it?Gloomy
@MárioCarvalho I just added code for it. 2 assets in the catalog and switch between them.Hyaline
Forgetting iPhone 6 Plus will bite.Sheepdip
@Sheepdip there's the @3x suffix for iPhone 6 Plus. The problem with iPhone 6 is that it shares the @2x suffix with older models, even though it has higher screen resolution than that.Sushi
I think you are mixing points and pixels up. iPhone 6 has the same resolution as iPhone 5, but a large display area.Sheepdip
@Sheepdip no, iPhone 6 has the same pixel density as iPhone 5 but not the same resolution. If you want an image to take upp the full width for iPhone 5 and 6 and optimize it for both devices then you'd make it 640px wide for iPhone 5, 750px wide for iPhone 6. This might help you see the what the issue is. There's a great answer by Jef below.Sushi
In my world, pixel density is the same as resolution, so I guess we are talking about the same thing. I recommend this page: paintcodeapp.com/news/ultimate-guide-to-iphone-resolutions You can see that iPhoen 6 and iPhone 5 has the same PPI, pixels per inch, ie pixel density ie resolution.Sheepdip
@Sheepdip it's not the same thing though. I'm not confused about the resolutions(nor PPI) of different devices. I think what's throwing you off is rendered pixels vs physical pixels in that table. I'm not a fan of the 1.171875 upsampling, and want to avoid it by using images optimised for the device's actual screen resolution. For iPhone 6 that is 750x1334. If you know that iPhone 6 is incapable of displaying image resources at this resolution then that would be new information and make for a good answer to this question. But I don't think that's the case.Sushi
iPhone 6 Plus does downsampling from @3x though. So you're right, one might want to consider the plus version as well and provide device specific images for it.Sushi
C
41

It seems to me that a lot of these answers want to address how to constrain the imageView, where I think you are concerned with loading the correct media file? I would come up with my own future extensible solution, something like this:

"UIImage+DeviceSpecificMedia.h" - (a category on UIImage)

Interface:

#import <UIKit/UIKit.h>

typedef NS_ENUM(NSInteger, thisDeviceClass) {

    thisDeviceClass_iPhone,
    thisDeviceClass_iPhoneRetina,
    thisDeviceClass_iPhone5,
    thisDeviceClass_iPhone6,
    thisDeviceClass_iPhone6plus,

    // we can add new devices when we become aware of them

    thisDeviceClass_iPad,
    thisDeviceClass_iPadRetina,


    thisDeviceClass_unknown
};

thisDeviceClass currentDeviceClass();

@interface UIImage (DeviceSpecificMedia)

+ (instancetype )imageForDeviceWithName:(NSString *)fileName;

@end

Implementation:

#import "UIImage+DeviceSpecificMedia.h"

thisDeviceClass currentDeviceClass() {

    CGFloat greaterPixelDimension = (CGFloat) fmaxf(((float)[[UIScreen mainScreen]bounds].size.height),
                                                    ((float)[[UIScreen mainScreen]bounds].size.width));

    switch ((NSInteger)greaterPixelDimension) {
        case 480:
            return (( [[UIScreen mainScreen]scale] > 1.0) ? thisDeviceClass_iPhoneRetina : thisDeviceClass_iPhone );
            break;
        case 568:
            return thisDeviceClass_iPhone5;
            break;
        case 667:
            return thisDeviceClass_iPhone6;
            break;
        case 736:
            return thisDeviceClass_iPhone6plus;
            break;
        case 1024:
            return (( [[UIScreen mainScreen]scale] > 1.0) ? thisDeviceClass_iPadRetina : thisDeviceClass_iPad );
            break;
        default:
            return thisDeviceClass_unknown;
            break;
    }
}

@implementation UIImage (deviceSpecificMedia)

+ (NSString *)magicSuffixForDevice
{
    switch (currentDeviceClass()) {
        case thisDeviceClass_iPhone:
            return @"";
            break;
        case thisDeviceClass_iPhoneRetina:
            return @"@2x";
            break;
        case thisDeviceClass_iPhone5:
            return @"-568h@2x";
            break;
        case thisDeviceClass_iPhone6:
            return @"-667h@2x"; //or some other arbitrary string..
            break;
        case thisDeviceClass_iPhone6plus:
            return @"-736h@3x";
            break;

        case thisDeviceClass_iPad:
            return @"~ipad";
            break;
        case thisDeviceClass_iPadRetina:
            return @"~ipad@2x";
            break;

        case thisDeviceClass_unknown:
        default:
            return @"";
            break;
    }
}

+ (instancetype )imageForDeviceWithName:(NSString *)fileName
{
    UIImage *result = nil;
    NSString *nameWithSuffix = [fileName stringByAppendingString:[UIImage magicSuffixForDevice]];

    result = [UIImage imageNamed:nameWithSuffix];
    if (!result) {
        result = [UIImage imageNamed:fileName];
    }
    return result;
}

@end
Cribble answered 31/10, 2014 at 22:48 Comment(12)
Exactly! Seems like there's no better way to do it currently. This is a nice way to do it programatically.Sushi
Thanks :) One thing to note about this example is I've exposed the thisDeviceClass enum and the c function to get it in the .h file, probably on the assumption that there would be another use/need for it. Some might prefer to squirrel these things away inside the .m file, need-to-know, encapsulation, etc. Glad to be of service. May I have the bounty ?? :DCribble
I have still issue with setting image with the above code if i have set the background image size for tabbar with the iPhone6 size and used above code to load the image it gets the image but not showing proper in background image of tabbar image name is footer_iPhone4New-667@2x and size is 750*110, but still image is not set proper, need help.Amur
Sorry I think that is beyond the scope of this question/answer, it sounds like you ARE getting the file you are expecting to. Perhaps UITabBar isn't prepared to handle an image of that size or it may have a bug. I suggest you ask a new question and link back to this page as something you've tried. Good luck :) ps quick suggestion, is your size {750, 110} double the size of the bar in BOTh dimensions (to account for the scale)... ? I suspect you might need to adjust the dimensions of the image you're including for this bar.. ;)Cribble
You are great :) you saved my life and it's really stupidness from Apple to leave us without support to specific devices if we want :( we face this problem in every app with background imagesVenter
Do you use Image Assets with this? How do you setup all your images files with this technique?Catalano
No mate sorry, only the launch image and icon sets in the AssetCatalog have device specific slots like that, and those don't respond to +[imageNamed: ] properly (hence the question). So all this answer attempts to do is specify a 'magic-suffix' for each specific screen footprint for use with that method there, +[imageForDeviceWithName:(NSString *)fileName] So unfortunately it comes down to the file names, no nice GUI with a box for each screen. ....you could easily write that in cocoa with a NSImageWell tho, go for it ;)Cribble
@Cribble i could not get this to run :/ where do i put this code and how do I actually call it?Toile
It is a category on UIImage. So make a new file, objectiveC category paste that stuff in the header and main, add the media with the appropriate suffixes for each device and call the method thereCribble
can you please let me know to how to use this category.. @CribbleDetoxicate
@bLacKhoLE well let us say we have an image called productImage We prepare files with the 'magicSuffix' for each screen footprint, productImage.png (first iPhones 320x480) [email protected] , [email protected] etc... Then we load our image with the only method which is exposed in the header here - UIImage *appropriateProductImage = [UIImage imageForDeviceWithName: @"productImage"]Cribble
@bLacKhoLE nb- these may not neccesarily be full screen sized images, could be for any layout at all. If this seems like a cumbersome and labour intensive design then that's because it is... It's how things were done when iPad was introduced, then retina screens, then the tallboys... Obviously at some point we have too many screen footprints to support individually. The devices are fast enough now to just put a large image file in the bundle and create and cache the appropriate size on the device at runtime (like how launch images have gone with the xib for that..)Cribble
H
4

I am using the following trick as some stuff actually works:

  • Asset Catalog for specific devices
  • Specify images for 1x, 2x on the base of 320x640
  • Specify images for 4 2x and 3x on the base of 320x568 (iPhone 5)
  • Create a new Images set for the iPhone 6 specifically (as this is the only device that makes trouble with edge to edge bindings)
  • Only provide 2x image for iPhone 6 in full resolution (750x1334)

Declare a constant

#define IS_IPHONE_6 [[UIScreen mainScreen]nativeBounds].size.width == 750.0 ? true : false

and use it like this:

UIImage *image = [UIImage imageNamed:@"Default_Image_Name"];
if(IS_IPHONE_^) {
    image = [UIImage imageNamed:@"Iphone6_Image_Name"];
}

this might be not the most beautiful solution, but it works, at least as long as apple does not provide a better API for edge to edge bindings.

Helve answered 22/4, 2015 at 6:29 Comment(0)
L
2

Auto Layout is supposed to help with this situation..

Now tell me @Nicklas Berglund what would you do if the device rotates? Lets say you are in landscape mode now.. How would you fill the Horizontal space which is not in the image assets any more?

Just food for thoughts.. Auto Layout supposed to take care of your screen no matter which orientation, or which device you are running your app on..

Maybe Apple should start targeting device orientations in image assets in future?

Lets go back to your question.. The solution is to replace your @2x images with 750px wide images and then have Auto Layout do its job. Oh yea, this is the tricky part..

If you just add constraints to fit it, it will squeeze it horizontally when displayed in 4" screen, but you can use multipliers to scale the image appropriately. Here's how you can do it:

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[imageFooterView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(imageFooterView)]];

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[imageFooterView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(imageFooterView)]];


float aspectRatio = imageFooterView.frame.size.height/imageFooterView.frame.size.width;

[imageFooterView addConstraint:[NSLayoutConstraint constraintWithItem:imageFooterView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:imageFooterView attribute:NSLayoutAttributeWidth multiplier:aspectRatio constant:0.0f]];
Laws answered 15/10, 2014 at 14:51 Comment(1)
Is this possible using storyboard?Ataraxia
C
1

I couldn't find a way to do it either, as I had a background Image that was perfectly sized with the Asset Catalog on every device except the iPhone 6. My fix (I did this in SpriteKit)?

if (bgNode.size.width != self.frame.size.width) {
        bgNode.texture = [SKTexture textureWithImageNamed:@"i6bg.png"];
        [bgNode runAction:[SKAction scaleXTo:self.frame.size.width/bgNode.size.width y:self.frame.size.width/bgNode.size.height duration:.1]];
    }

bgNode is the background image that is pulled up by the device. If it's an iPhone 6, it won't fit the screen and so the background image width wont be the same as the screen width. When the device is recognized as an iPhone 6, I change the texture to the R4 texture (the @2x for retina) and scale it to the correct dimensions.

I tried doing the same with the regular @2x image, but the scaled image looked very bad (it was too stretched out and noticable). With the R4 texture scaled, the proportions of width/height are a bit better and so the change isn't even noticeable. I hope this gives you some idea as to what you can do before Apple adds an iPhone 6 Asset.

Collage answered 22/9, 2014 at 4:37 Comment(2)
Thanks. There are various workarounds. I'd probably check the width with UIScreen: [UIScreen mainScreen] bounds].size.width and wrap it in a function to check whether it's running on iPhone 6. Having a hard time believing Apple haven't already created a beautiful solution for this, such as iPhone 6 assets.Sushi
Exactly. I was wondering whether my asset images had something wrong, as the iPhone 6 background image that I had wouldn't fit the whole screen. I came about the other post you made while searching google and realized I needed to apply a workaround. I'm going to change the check method to [UIScreen mainScreen]..., thanks for the advice!Collage
B
1

Hope this will solve all your issues related to customised edge to edge image.
Xcode 6 - xcassets for universal image support

Make sure if you are using auto layout then check pin is set to zero for all edges and constraints to margin is un checked.

You can also visit this links for launch screen images:
http://www.paintcodeapp.com/news/iphone-6-screens-demystified
http://www.paintcodeapp.com/news/ultimate-guide-to-iphone-resolutions

enter image description here

Bernardinabernardine answered 31/10, 2014 at 15:5 Comment(0)
D
1

I raised the same question to apple technical support and they confirm that for fullscreen image it can't be done in asset catalog: "Currently, there is no way for the Asset Catalog to load device specific images. If your app needs to support device specific images you will need to implement your own code to detect the screen size and choose the appropriate image. You can file an enhancement request by using the following link. Be sure to explain your use case for this feature. "

Drumlin answered 13/11, 2015 at 2:16 Comment(0)
F
0

I checked the naming convention of a launch image generated from an asset catalog via Xcode 6 and the landscape version for iPhone 6+, for example, had: LaunchImage-Landscape-736h@3x.png

Based on that, I'd presume it would be as follows, for retina devices, assuming a base file desert.png:

  • desert@2x : iPhone 4s (320 x 420)
  • desert-568h@2x : iPhones 5, 5C and 5S (320 x 568)
  • desert-667h@2x : iPhone 6 (375 x 667)
  • desert-736h@3x : iPhone 6+ (414 x 736)
  • desert@2x~ipad : iPad (1024 x 768)
Fossa answered 10/10, 2014 at 12:20 Comment(5)
But there's nothing magic about the -667h@2x suffix or is there? If there's an image [email protected] it wouldn't automatically be used when specifying image my-image.png right?Sushi
You shouldn't specify the .png part to let the SDK choose the correct image.Sacci
I was sloppy. Still I'm quite sure there's nothing magical about the -667h@2x suffix.Sushi
@NiklasBerglund I did some magic here :) https://mcmap.net/q/326829/-how-to-specify-size-for-iphone-6-7-customised-edge-to-edge-imageCylindrical
this doesn't appear to work ... were you able to get this to work, or is this naming convention purely a guess?Davita
S
0

There is no native Assets support for this case, so I think it would be better to do it manually as working with undocumented file names may break easily in the future.

Sacci answered 27/10, 2014 at 2:54 Comment(0)
P
0

Just measure the device dimensions and call the image that you want. ie Do it programatically

So in your appdelegate have globals

deviceHeight = self.window.frame.size.height;
deviceWidth = self.window.frame.size.width;

that you can call repeatedly. Then check them and call the appropriate image

if (deviceWidth == 640){
image = IPHONE4IMAGE;
deviceString = @"iPhone4";
}
else...
Parasympathetic answered 31/10, 2014 at 16:47 Comment(0)
G
0

In my case, I was interested in making my base view controller subclass have the same background image as my launch image.

NOTE: This approach will not work unless this is your specific requirement.

Also, even when I tried creating a background image that was the correct size for the iPhone 6 (750x1334), loading that image as a pattern image into a background color for a view ended up scaling the image up in an undesirable way.

This answer gave me the code that I needed to figure out a good solution for me.

Here's the code I got working to have my launch image match my UIViewController's background image (or vice versa):

- (void)viewDidLoad {
    [super viewDidLoad];
    UIImage *background         = [UIImage imageNamed:[self splashImageName]];
    UIColor *backgroundColor    = [UIColor colorWithPatternImage:background];
    self.view.backgroundColor   = backgroundColor;
}
- (NSString *)splashImageName {
    UIInterfaceOrientation orientation  = [[UIApplication sharedApplication] statusBarOrientation];
    CGSize viewSize                     = self.view.bounds.size;
    NSString *viewOrientation           = @"Portrait";
    if (UIDeviceOrientationIsLandscape(orientation)) {
        viewSize                        = CGSizeMake(viewSize.height, viewSize.width);
        viewOrientation                 = @"Landscape";
    }
    NSArray *imagesDict                 = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"UILaunchImages"];
    for (NSDictionary *dict in imagesDict) {
        CGSize imageSize                = CGSizeFromString(dict[@"UILaunchImageSize"]);
        if (CGSizeEqualToSize(imageSize, viewSize) && [viewOrientation isEqualToString:dict[@"UILaunchImageOrientation"]])
            return dict[@"UILaunchImageName"];
    }
    return nil;
}
Ginni answered 24/1, 2015 at 2:9 Comment(0)
B
0

Please try this class to change the image name programmatically.

import UIKit

class AGTools: NSObject {
    class func fullWidthImage(imageName: String!) -> String!{
        let screenWidth = UIScreen.mainScreen().bounds.size.width
        switch (screenWidth){
        case 320:
            // scale 2x or 1x
            return (UIScreen.mainScreen().scale > 1.0) ? "\(imageName)@2x" : imageName
        case 375:
            return "\(imageName)-375w@2x"
        case 414:
            return "\(imageName)-414w@3x"
        default:
            return imageName
        }
    }
}

use this method like this.

_imgTest.image = UIImage(named: AGTools.fullWidthImage("imageName"))
Bullhorn answered 29/9, 2015 at 15:58 Comment(0)
I
-2

FIRST of all, you need to configure your imageView to cover all the screen, Autolayout will help a lot for this job, take a look on the link below and find how to Pin the constraints (Leading Space, Trailing Space, Top Space and Bottom Space) using Storyboards:

http://www.thinkandbuild.it/learn-to-love-auto-layout/

enter image description here

SECOND step is create device specific image sets on your image assets (image below), to display different images according to device.

enter image description here

Check out this infographic: http://www.paintcodeapp.com/news/ultimate-guide-to-iphone-resolutions

It explains the differences between old iPhones, iPhone 6 and iPhone 6 Plus. You can see comparison of screen sizes in points, rendered pixels and physical pixels

That's all

enter image description here

enter image description here

Please, give a feedback if you have any trouble.

Isolationism answered 30/10, 2014 at 18:7 Comment(1)
Thanks, but the question isn't related to layout constrains. It's about the pixel ratio between different devices. Apple haven't provided a convenient way(similar to @2x, @3x) to specify images optimized for iPhone 6.Sushi

© 2022 - 2024 — McMap. All rights reserved.