ios 如何为 iphone 应用程序创建多个主题/皮肤?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8919334/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to create Multiple Themes/Skins for iphone apps?
提问by pat
I have an iphone app ready and approved by the app store. Now I want to create different themes for my app. Can someone please help me out, with info/links/steps on how to create themes for my app?
我有一个 iphone 应用程序准备好并得到应用程序商店的批准。现在我想为我的应用程序创建不同的主题。有人可以帮助我,提供有关如何为我的应用创建主题的信息/链接/步骤吗?
I want to create a Metal theme for the Boys and a Pink theme for the Girls. Again by theme I mean, the whole app(features and functionality) is gonna stay the same, but depending on who the user is(boy or girl), he/she can choose the theme they wish to see. And when the theme changes, only the images/Background/music will change according to the applied theme.
我想为男孩创建一个金属主题,为女孩创建一个粉红色主题。再次通过主题,我的意思是,整个应用程序(特性和功能)将保持不变,但取决于用户是谁(男孩或女孩),他/她可以选择他们希望看到的主题。并且当主题发生变化时,只有图像/背景/音乐会根据应用的主题发生变化。
Thanks a lot!
非常感谢!
回答by Nick Lockwood
This is quite difficult as apps don't have the equivalent of a css stylesheet.
这非常困难,因为应用程序没有等效的 css 样式表。
First you need to work out what parts of the app you want to skin, and when you want to allow the user to swap skins.
首先,您需要确定要为应用程序的哪些部分设置皮肤,以及何时允许用户交换皮肤。
I'm going to assume that you want to change images and font colours, and that it's okay if the user has to relaunch the app to change the skin (that will make things simpler for now).
我将假设您想要更改图像和字体颜色,并且如果用户必须重新启动应用程序来更改皮肤(这将使现在的事情变得更简单),那也没关系。
Create a plist containing all your skinnable images and colours. The plist will be a dictionary with sensible, theme neutral key names for the images and colours (e.g. don't have a colour called "red", call it "primaryHeadingColor"). Images will be file names, and colours can be hex strings, e.g. FF0000 for red.
创建一个包含所有可换肤图像和颜色的 plist。plist 将是一个字典,其中包含图像和颜色的合理、主题中性键名称(例如,没有称为“红色”的颜色,将其称为“primaryHeadingColor”)。图像将是文件名,颜色可以是十六进制字符串,例如 FF0000 代表红色。
You'll have one plist for each theme.
每个主题都有一个 plist。
Create a new class called ThemeManager and make it a singleton by adding the following method:
创建一个名为 ThemeManager 的新类,并通过添加以下方法使其成为单例:
+ (ThemeManager *)sharedManager
{
static ThemeManager *sharedManager = nil;
if (sharedManager == nil)
{
sharedManager = [[ThemeManager alloc] init];
}
return sharedManager;
}
The ThemeManager class will have an NSDictionary property called "styles", and in the init method you will load the theme into your styles dictionary like this:
ThemeManager 类将有一个名为“styles”的 NSDictionary 属性,在 init 方法中,您将像这样将主题加载到样式字典中:
- (id)init
{
if ((self = [super init]))
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *themeName = [defaults objectForKey:@"theme"] ?: @"default";
NSString *path = [[NSBundle mainBundle] pathForResource:themeName ofType:@"plist"];
self.styles = [NSDictionary dictionaryWithContentsOfFile:path];
}
return self;
}
(Note: some people don't like doing a lot of work inside an init method. I've never found it to be an issue, but if you prefer, create a separate method to load the themes dictionary and call it from your app's setup code).
(注意:有些人不喜欢在 init 方法中做很多工作。我从来没有发现这是一个问题,但如果你愿意,可以创建一个单独的方法来加载主题字典并从你的应用程序的设置代码)。
Notice how I'm getting the name for the theme plist from user defaults. That means the user can select a theme in your preferences and save it and the app will load that theme next time it is launched. I've put in a default theme name of "default" if no theme is selected, so make sure you have a default.plist theme file (or change the @"default" in the code to whatever your default theme plist is actually called).
请注意我如何从用户默认值中获取主题 plist 的名称。这意味着用户可以在您的首选项中选择一个主题并保存它,应用程序将在下次启动时加载该主题。如果没有选择主题,我已经输入了默认主题名称“default”,因此请确保您有一个 default.plist 主题文件(或将代码中的 @"default" 更改为您的默认主题 plist 实际调用的任何内容)。
Now that you've loaded your theme you need to use it; I'm assuming your app has various images and text labels. If you're loading and laying those out in code then this part is easy. If you are doing it in nibs then it's a bit trickier but I'll explain how to handle that later.
现在您已经加载了您的主题,您需要使用它;我假设您的应用程序具有各种图像和文本标签。如果您在代码中加载和布置这些内容,那么这部分很容易。如果你用笔尖来做,那么它有点棘手,但我稍后会解释如何处理。
Now normally you would load an image by saying:
现在通常你会说:
UIImage *image = [UIImage imageNamed:@"myImage.png"];
But if you want that image to be themable, you'll now need to load it by saying
但是,如果您希望该图像可以主题化,则现在需要通过说来加载它
NSDictionary *styles = [ThemeManager sharedManager].styles;
NSString *imageName = [styles objectForKey:@"myImageKey"];
UIImage *image = [UIImage imageNamed:imageName];
That will look in your theme file for the themed image that matches the key "myImageKey" and will load it. Depending on which theme file you've loaded you'll get a different style.
这将在您的主题文件中查找与键“myImageKey”匹配的主题图像并加载它。根据您加载的主题文件,您将获得不同的样式。
You'll be using those three lines a lot so you may want to wrap them up in a function. A great idea would be to create a category on UIImage that declares a method called something like:
您将经常使用这三行,因此您可能希望将它们包含在一个函数中。一个好主意是在 UIImage 上创建一个类别,声明一个名为的方法,如下所示:
+ (UIImage *)themeImageNamed:(NSString *)key;
Then to use it you can just replace any calls to [UIImage imageNamed:@"foo.png"]; with [UIImage themeImageNamed:@"foo"]; where foo is now the theme key instead of the actual image name.
然后要使用它,您只需替换对 [UIImage imageNamed:@"foo.png"] 的任何调用;与 [UIImage themeImageNamed:@"foo"]; 其中 foo 现在是主题键而不是实际的图像名称。
Okay, so that's it for theming your images. To theme your label colours, suppose you're currently setting your label colours by saying:
好的,这就是为您的图像设置主题。要主题化标签颜色,假设您当前正在通过以下方式设置标签颜色:
someLabel.color = [UIColor redColor];
You would now replace that with:
您现在将其替换为:
NSDictionary *styles = [ThemeManager sharedManager].styles;
NSString *labelColor = [styles objectForKey:@"myLabelColor"];
someLabel.color = [UIColor colorWithHexString:labelColor];
Now you may have noticed that UIColor doesn't have a method "colorWithHexString:" - you'll have to add that using a category. You can Google for "UIColor with hex string" solutions to find code to do that, or I've written a handy category that does that and a bit more here: https://github.com/nicklockwood/ColorUtils
现在您可能已经注意到 UIColor 没有方法“colorWithHexString:”——您必须使用类别添加它。您可以在 Google 上搜索“带有十六进制字符串的 UIColor”解决方案来查找代码来执行此操作,或者我已经编写了一个方便的类别来执行此操作以及更多内容:https: //github.com/nicklockwood/ColorUtils
If you've been paying attention you'll also be thinking that instead of writing those three lines over and over, why not add a method to UIColor called:
如果您一直在关注,您还会想到与其一遍又一遍地编写这三行,为什么不向 UIColor 添加一个名为的方法:
+ (UIColor *)themeColorNamed:(NSString *)key;
Just like we did with UIImage? Great idea!
就像我们对 UIImage 所做的那样?好点子!
So that's it. Now you can theme any image or label in your app. You could use the same trick to set the font name, or any number of other potentially themable visual properties.
就是这样了。现在,您可以为应用中的任何图像或标签设置主题。您可以使用相同的技巧来设置字体名称或任意数量的其他潜在主题视觉属性。
There's just one tiny thing we've forgotten...
我们忘记了一件小事......
If you've built most of your views as nibs (and I see no reason why you wouldn't) then these techniques aren't going to work because your image names and font colours are buried inside impenetrable nib data and aren't being set in your source code.
如果您已将大部分视图构建为笔尖(而且我认为您没有理由不这样做),那么这些技术将不起作用,因为您的图像名称和字体颜色被隐藏在难以理解的笔尖数据中并且没有被在你的源代码中设置。
There are a few approaches to solve this:
有几种方法可以解决这个问题:
1) You could make duplicate themed copies of your nibs and then put the nib names in your theme plist and load them from your theme manager. That's not too bad, just implement the nibName method of your view controllers like this:
1)您可以制作笔尖的重复主题副本,然后将笔尖名称放入主题 plist 并从主题管理器加载它们。这还不错,只需像这样实现视图控制器的 nibName 方法:
- (NSString *)nibName
{
NSDictionary *styles = [ThemeManager sharedManager].styles;
return [styles objectForKey:NSStringFromClass([self class])];
}
Notice my neat trick of using the class name of the view controller as the key - that will save you some typing because you can just make a base ThemeViewController with that method and have all your themable view controllers inherit from it.
请注意我使用视图控制器的类名作为键的巧妙技巧 - 这将为您节省一些输入,因为您可以使用该方法创建一个基本的 ThemeViewController 并让所有可主题化的视图控制器从它继承。
This approach does mean maintaining multiple copies of each nib though, which is a maintenance nightmare if you need to change any screens later.
但是,这种方法确实意味着维护每个笔尖的多个副本,如果您以后需要更改任何屏幕,这将是一场维护噩梦。
2) You could make IBOutlets for all of the imageViews and labels in your nibs, then set their images and colors in code in your viewDidLoad method. That's probably the most cumbersome approach, but at least you don't have duplicate nibs to maintain (this is essentially the same problem as localising nibs btw, and pretty much the same solution options).
2)您可以为笔尖中的所有图像视图和标签制作 IBOutlets,然后在您的 viewDidLoad 方法中的代码中设置它们的图像和颜色。这可能是最麻烦的方法,但至少你不需要维护重复的笔尖(这与本地化笔尖 btw 本质上是相同的问题,并且几乎相同的解决方案选项)。
3) You could create a custom subclass of UILabel called ThemeLabel that automatically sets the font color using the code above when the label is instantiated, then use those ThemeLabels in your nib files instead of regular UILabels by setting the class of the label to ThemeLabel in Interface Builder. Unfortunately if you have more than one font or font colour, you'll need to create a different UILabel subclass for each different style.
3)您可以创建一个名为 ThemeLabel 的 UILabel 的自定义子类,它会在实例化标签时使用上面的代码自动设置字体颜色,然后通过将标签的类设置为 ThemeLabel 在您的 nib 文件中使用这些 ThemeLabels 而不是常规的 UILabels界面生成器。不幸的是,如果您有不止一种字体或字体颜色,则需要为每种不同的样式创建不同的 UILabel 子类。
Or you could be devious and use something like the view tag or accessibilityLabel property as the style dictionary key so that you can have a single ThemeLabel class and set the accessibility label in Interface Builder to select the style.
或者,您可以狡猾地使用诸如 view 标签或 accessibilityLabel 属性之类的东西作为样式字典键,这样您就可以拥有一个 ThemeLabel 类并在 Interface Builder 中设置可访问性标签以选择样式。
The same trick could work for ImageViews - create a UIImageView subclass called ThemeImageView that, in the awakeFromNib method replaces the image with a theme image based on the tag or accessibilityLabel property.
同样的技巧也适用于 ImageViews - 创建一个名为 ThemeImageView 的 UIImageView 子类,在awakeFromNib 方法中,根据标签或accessibilityLabel 属性用主题图像替换图像。
Personally I like option 3 best because it saves on coding. Another advantage of option 3 is that if you wanted to be able to swap themes at runtime, you could implement a mechanism where your theme manager reloads the theme dictionary, then broadcasts an NSNotification to all the ThemeLabels and ThemeImageViews telling them to redraw themselves. That would probably only take about an extra 15 lines of code.
我个人最喜欢选项 3,因为它可以节省编码。选项 3 的另一个优点是,如果您希望能够在运行时交换主题,您可以实现一种机制,让主题管理器重新加载主题字典,然后向所有 ThemeLabels 和 ThemeImageViews 广播一个 NSNotification,告诉它们重新绘制自己。这可能只需要额外的 15 行代码。
Anyway, there you have a complete iOS app theming solution. You're welcome!
无论如何,您有一个完整的 iOS 应用程序主题解决方案。别客气!
UPDATE:
更新:
As of iOS 5, it's now possible to set custom attributes by keyPath in Interface Builder, meaning that it's no longer necessary to create a view subclass for each themable property, or abuse the tag or accessibilityLabel for selecting styles. Just give your UILabel or UIImageView subclass a string property to indicate which theme key it should use from the plist, and then set that value in IB.
从 iOS 5 开始,现在可以通过 Interface Builder 中的 keyPath 设置自定义属性,这意味着不再需要为每个可主题化的属性创建视图子类,或者滥用标签或 accessibilityLabel 来选择样式。只需给你的 UILabel 或 UIImageView 子类一个字符串属性来指示它应该从 plist 使用哪个主题键,然后在 IB 中设置该值。
UPDATE 2:
更新 2:
As of iOS 6, there is now a limited skinning system built into iOS that allows you to use a property called the UIAppearance proxyto skin all instances of a given control class at once (there's a good tutorial about the UIAppearance APIs here). It's worth checking if this is sufficient for your skinning needs, but if not, the solution I outlined above still works well, and can be used instead, or in combination with UIAppearance.
作为iOS 6后,现在有内置的iOS有限换肤系统,让你使用一个叫物业UIAppearance代理一次(有关于UIAppearance API的一个很好的教程皮肤给定的控制类的所有实例在这里)。值得检查这是否足以满足您的蒙皮需求,但如果不是,我上面概述的解决方案仍然有效,并且可以替代使用,或与 UIAppearance 结合使用。