如何为iphone应用程序创建多个主题/皮肤? [英] How to create Multiple Themes/Skins for iphone apps?

查看:169
本文介绍了如何为iphone应用程序创建多个主题/皮肤?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个iphone应用程序准备和批准的应用商店。现在我想为我的应用程序创建不同的主题。有人可以帮助我,有关如何为我的应用程序创建主题的信息/链接/步骤。



我想为男孩和粉红色创建一个金属主题女孩的主题。再次通过主题我的意思是,整个应用程序(功能和功能)将保持不变,但根据用户是谁(男孩或女孩),他/她可以选择他们希望看到的主题。当主题改变时,只有图片/背景/音乐会根据应用的主题而改变。



非常感谢!



首先你需要一个简单的应用程序。找出你想要皮肤的应用程序的哪些部分,以及何时允许用户交换皮肤。



我要假设你想要更改图像和字体颜色,如果用户必须重新启动应用程序来更改皮肤(这将使现在更简单)。



创建plist包含所有你的skinnable图像和颜色。 plist将是一个具有合理的主题中性键名称的图像和颜色的字典(例如,没有称为红色的颜色,称为primaryHeadingColor)。图像将是文件名,颜色可以是十六进制字符串,例如。 FF0000为红色。



每个主题都有一个plist。



创建一个新类并通过添加以下方法使其成为单例:

  +(ThemeManager *)sharedManager 
{
static ThemeManager * sharedManager = nil;
if(sharedManager == nil)
{
sharedManager = [[ThemeManager alloc] init];
}
return sharedManager;
}

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

(注意:有些人不喜欢在init方法,我从来没有发现它是一个问题,但如果你喜欢,创建一个单独的方法来加载主题字典,并从应用程序的设置代码调用它。)



请注意我从用户默认值获取主题plist的名称。这意味着用户可以在首选项中选择一个主题并保存,应用程序将在下次启动时加载该主题。如果没有选择主题,我将默认主题名称设置为default,所以确保你有一个default.plist主题文件(或者将代码中的@default更改为默认主题plist的名称) )。



现在你已经加载了你的主题,你需要使用它;我假设你的应用程序有各种图像和文本标签。如果你加载和放置在代码中,那么这部分很容易。如果你在nibs这样做有点棘手,但我会解释如何处理这个后来。



现在通常你会加载一个图像说: p>

  UIImage * image = [UIImage imageNamed:@myImage.png]; 

但是如果你想让这个图像适用,你现在需要加载它, / p>

  NSDictionary * styles = [ThemeManager sharedManager] .styles; 
NSString * imageName = [styles objectForKey:@myImageKey];
UIImage * image = [UIImage imageNamed:imageName];

这将在主题文件中查找与键myImageKey匹配的主题图像,它。根据你加载的主题文件,你会得到不同的风格。



你会使用这三行,所以你可能想包装他们在函数中。一个伟大的想法是在UIImage上创建一个类别,声明一个类似如下的方法:

  +(UIImage *)themeImageNamed: (NSString *)key; 

然后使用它,你可以替换任何调用[UIImage imageNamed:@foo.png ];与[UIImage themeImageNamed:@foo];其中foo现在是主题键,而不是实际的图片名称。



好的,这是为了你的图片。要标记您的标签颜色,假设您目前正在设置标签颜色:

  someLabel.color = [UIColor redColor] ; 

您现在可以用以下代替:

  NSDictionary * styles = [ThemeManager sharedManager] .styles; 
NSString * labelColor = [styles objectForKey:@myLabelColor];
someLabel.color = [UIColor colorWithHexString:labelColor];

现在你可能已经注意到UIColor没有方法colorWithHexString: - 必须添加使用类别。你可以在Google上找到UIColor with hex string解决方案来找到代码,或者我已经写了一个方便的类别,这里有更多: https://github.com/nicklockwood/ColorUtils



如果你一直注意,你也会认为,而不是写为什么不添加一个方法UIColor调用:

  +(UIColor *)themeColorNamed:(NSString * )键; 

就像我们用UIImage做的一样吗?太棒了!



就是这样。现在,您可以为应用程式中的任何图片或标签设定主题。您可以使用相同的技巧来设置字体名称,或任何其他可能适合的视觉属性。



只有一个微小的东西我们忘记了...



如果你已经把你的大部分视图作为nibs(我没有看到没有理由你不会),那么这些技术不会工作,因为你的图像名称



有几种方法可以解决这个问题:



1)您可以制作重复的主题副本,然后将nib名称放入您的主题plist,并从您的主题管理器加载它们。这不是太糟糕,只是实现您的视图控制器的nibName方法,如下:

   - (NSString *)nibName 
{
NSDictionary * styles = [ThemeManager sharedManager] .styles;
return [styles objectForKey:NSStringFromClass([self class])];
}

注意我使用视图控制器的类名作为关键 - 这将保存你一些打字,因为你可以只使用一个基础ThemeViewController与该方法,并让所有的可控制的视图控制器继承自它。



这种方法的意思是维护多个



2)您可以为所有的imageView和标签创建IBOutlets nibs,然后在viewDidLoad方法的代码中设置它们的图像和颜色。这可能是最麻烦的方法,但至少你没有重复的nibs维护(这本质上是与localizing nibs btw相同的问题,和几乎相同的解决方案选项)。



3)你可以创建一个名为ThemeLabel的UILabel的自定义子类,当标签被实例化时,使用上面的代码自动设置字体颜色,然后在你的nib文件中使用这些ThemeLabels而不是普通的UILabels,通过设置类在Interface Builder中将标签添加到ThemeLabel。不幸的是,如果你有多种字体或字体颜色,你需要为每种不同的风格创建一个不同的UILabel子类。



或者你可以狡猾,像视图标签或accessibilityLabel属性作为样式字典键,以便您可以有一个ThemeLabel类,并在Interface Builder中设置辅助功能选项标签来选择样式。



同样的技巧可以为ImageViews工作 - 创建一个名为ThemeImageView的UIImageView子类,在awakeFromNib方法替换图像与基于标记或accessibilityLabel属性的主题图像。



我喜欢选项3最好,因为它节省了编码。选项3的另一个优点是,如果你想在运行时交换主题,你可以实现一个机制,你的主题管理器重新加载主题字典,然后广播一个NSNotification到所有的ThemeLabels和ThemeImageViews告诉他们重新绘制自己。这可能只需要额外的15行代码。



无论如何,你有一个完整的iOS应用程序的主题解决方案。欢迎!



更新:



从iOS 5开始,现在可以设置自定义属性keyPath在Interface Builder,意味着它不再需要为每个themable属性创建一个视图子类,或滥用标签或accessibilityLabel来选择样式。只需给你的UILabel或UIImageView子类一个字符串属性来指示它应该使用的主题键,然后在IB中设置该值。



UPDATE 2:



从iOS 6开始,iOS中内置了一个有限的皮肤系统,允许您使用一个名为 UIAppearance proxy (有关UIAppearance API的一个很好的教程这里)。这是值得检查,如果这是足够你的皮肤需要,但如果不是,我上面列出的解决方案仍然工作良好,可以改用或与UIAppearance结合使用。


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?

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!

解决方案

This is quite difficult as apps don't have the equivalent of a css stylesheet.

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.

You'll have one plist for each theme.

Create a new class called ThemeManager and make it a singleton by adding the following method:

+ (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:

- (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).

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).

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.

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 *)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.

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

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 *)themeColorNamed:(NSString *)key;

Just like we did with UIImage? Great idea!

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:

- (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.

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).

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.

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.

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.

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.

Anyway, there you have a complete iOS app theming solution. You're welcome!

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.

UPDATE 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 proxy to 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.

这篇关于如何为iphone应用程序创建多个主题/皮肤?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆