如何为iPhone 6/7定制的边缘到边缘图像指定尺寸?

60
假设我想在 iPhone 应用中使用一张捆绑图片,并让它占用全部可用屏幕宽度,例如横幅。我会创建一张宽为 320pxmy_banner.png,一张宽为 640pxmy_banner@2x.png 和一张为 iPhone 6 plus 宽度 1242pxmy_banner@3x.png。但是,iPhone 6 的分辨率为 750×1334 像素。它仍然与拥有 640px 宽度的 iPhone 4 和 5 共享 @2x 后缀。

有没有推荐的方法或良好的方法来指定已针对 iPhone 6750px 宽度进行优化的图像文件?看起来不能在 资产目录 中完成吗?是否应该通过编程来完成?还有其他后缀可以用于 iPhone 6 吗?

iPhone 4,5,6 screen sizes (图片来源:http://www.iphoneresolution.com


苹果不是说 i6+ 的图片要使用 @3x 吗? - Lior Pollak
4
iPhone 5和iPhone 6的屏幕比例相同。你需要为iPhone 6分辨率准备2倍图像,它将在不影响iPhone 5的情况下被降采样。 - Desdenova
1
@CraigOtis 你可以在资源目录中创建多个图像,并在检查屏幕边界后使用它们。但在我看来,这是不必要的过度设计。 - Desdenova
2
@Desdenova 同意。我希望(对于OP)会有一些类似的奖励前缀,就像苹果推出iPhone 5时一样。例如:background@2x.pngbackground-750w@2x.png - Craig Otis
1
@Jonny,这两件事情并不一样。我对不同设备的分辨率(也不是PPI)没有困惑。我认为让你感到困惑的是表格中的“渲染像素”与“物理像素”。我不喜欢1.171875倍的上采样,并希望通过使用针对设备的“实际屏幕分辨率”进行优化的图像来避免它。对于iPhone 6而言,它是750x1334。如果您知道iPhone 6无法以此分辨率显示图像资源,那么这将是新信息,并且可以成为回答此问题的好方法。但我认为情况并非如此。 - Niklas Berglund
显示剩余13条评论
12个回答

41

在我看来,很多答案似乎想要解决如何限制imageView的问题,而我认为你关心的是如何加载正确的媒体文件?我会提出自己未来可扩展的解决方案,类似于:

“UIImage+DeviceSpecificMedia.h” -(UIImage的一个分类)

接口:

#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

实现:

#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

1
没错!目前似乎没有更好的方法。这是一种很好的编程方式。 - Niklas Berglund
1
你做得很好!谢谢! - emotality
请问您能告诉我如何使用这个类别吗?@Jef - bLacK hoLE
@bLacKhoLE 好吧,假设我们有一个名为productImage的图像。我们为每个屏幕分辨率准备带有“magicSuffix”的文件,例如productImage.png(第一代iPhone 320x480)、productImage-568h@2x.pngproductImage-667@2x.png等。然后,我们使用此处唯一公开的方法加载我们的图像- UIImage * appropriateProductImage = [UIImage imageForDeviceWithName:@"productImage"]。 - Jef
@bLacKhoLE nb- 这些图片不一定是全屏大小的,可以适用于任何布局。如果这看起来像是一个繁琐和劳动密集型的设计,那是因为它确实如此... 当iPad推出时,然后是视网膜屏幕,再然后是高大的屏幕... 显然,在某个点上,我们有太多的屏幕尺寸需要单独支持。现在设备足够快了,只需将大型图像文件放入包中,并在运行时创建和缓存适当的大小(就像启动图像已经使用xib完成了...)。 - Jef
显示剩余8条评论

4
我正在使用以下技巧,因为一些东西实际上可以起作用:
  • 针对特定设备的资源目录
  • 以320x640为基础指定1x2x图像
  • 以320x568(iPhone 5)为基础指定4 2x3x图像
  • 专门为iPhone 6创建一个新的图像集(因为这是唯一一个在边缘绑定方面有问题的设备)
  • 仅为iPhone 6提供完整分辨率(750x1334)的2x图像

声明一个常量

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

并像这样使用它:

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

这可能不是最美观的解决方案,但它有效,至少在苹果没有为边缘到边缘绑定提供更好的API之前是这样。


2

自动布局旨在帮助处理这种情况...

现在告诉我,@Nicklas Berglund,如果设备旋转了,你会怎么做?比如说,你现在处于横屏模式。如何填补不再存在于图像资源中的水平空间?

只是思考一下... 自动布局应该能够照顾好你的屏幕,无论你在哪个方向上或者使用哪个设备运行应用程序。

也许苹果将来应该开始针对图像资源中的设备方向进行定位?

让我们回到你的问题... 解决方法是用750px宽的图像替换你的@2x图像,然后让自动布局完成其工作。哦,是的,这是棘手的部分...

如果你只是添加约束来适应它,当在4英寸的屏幕上显示时,它会被水平挤压,但是你可以使用乘数来适当地缩放图像。以下是如何实现:

[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]];

使用Storyboard能实现这个吗? - Arun Gupta

1

我也找不到方法,因为我有一个背景图片,使用资源目录在每个设备上都是完美大小,除了iPhone 6。我的解决方法(我在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是设备拉起的背景图像。如果是iPhone 6,它将不适合屏幕,因此背景图像的宽度将与屏幕宽度不同。当设备被识别为iPhone 6时,我将纹理改变为R4纹理(@2x用于视网膜)并将其缩放到正确的尺寸。

我尝试使用常规的@2x图像做同样的事情,但缩放后的图像看起来非常糟糕(过度拉伸且易被发现)。随着R4纹理的缩放,宽度/高度的比例稍微好一些,所以变化甚至不会被注意到。我希望这能让您想到在Apple添加iPhone 6资产之前可以做些什么。


1
谢谢。有各种变通方法。我可能会使用UIScreen来检查宽度:[UIScreen mainScreen] bounds].size.width并将其封装在一个函数中,以检查是否在iPhone 6上运行。我很难相信苹果还没有为此创建美观的解决方案,例如iPhone 6资源。 - Niklas Berglund
1
没错。我一直在想我的资源图片是否有问题,因为我使用的 iPhone 6 背景图片无法适应整个屏幕。在谷歌搜索时,我看到了你发布的另一篇帖子,意识到我需要采用一个解决方法。我将把检查方法更改为 [UIScreen mainScreen]...,感谢你的建议! - Andriko13

1

1

我向苹果技术支持提出了同样的问题,他们确认全屏图像无法在资源目录中完成:"目前,资源目录无法加载特定于设备的图像。如果您的应用程序需要支持特定于设备的图像,则需要实现自己的代码来检测屏幕大小并选择适当的图像。您可以使用以下链接提交增强请求。一定要解释您使用此功能的用例。"


0

在我的情况下,我希望使我的基本视图控制器子类具有与启动图像相同的背景图像。

注意:除非这是您特定的要求,否则此方法将无法正常工作。

而且,即使我尝试创建一个正确大小的iPhone 6背景图像(750x1334),将该图像作为模式图像加载到视图的背景颜色中最终以不可取的方式缩放了该图像。

这个答案 给了我需要找到好的解决方案的代码。

以下是我成功实现启动图像与我的 UIViewController 的背景图像匹配(或者反过来)所需的代码:

- (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;
}

0
请尝试使用这个类来通过编程方式更改图像名称。
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
        }
    }
}

使用这个方法就像这样。

_imgTest.image = UIImage(named: AGTools.fullWidthImage("imageName"))

0
只需测量设备尺寸并调用所需的图像。即以编程方式实现。
因此,在您的appdelegate中拥有全局变量。
deviceHeight = self.window.frame.size.height;
deviceWidth = self.window.frame.size.width;

您可以重复调用它们。 然后检查并调用相应的图像。

if (deviceWidth == 640){
image = IPHONE4IMAGE;
deviceString = @"iPhone4";
}
else...

0

针对此情况,没有本地化的资产支持,所以我认为最好手动操作,因为使用未经记录的文件名称可能很容易在将来出现问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接