如何为iPhone应用程序创建多个主题/皮肤?

时间:2012-01-19 00:01:18

标签: objective-c ios cocoa-touch themes

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

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

非常感谢!

1 个答案:

答案 0 :(得分:129)

由于应用程序没有相应的css样式表,因此非常困难。

首先,您需要弄清楚要涂抹的应用程序的哪些部分,以及何时允许用户交换皮肤。

我将假设您想要更改图像和字体颜色,并且如果用户必须重新启动应用程序以更改皮肤(这会使事情变得更简单),这没关系

创建包含所有可换肤图像和颜色的plist。 plist将是一个字典,具有合理的,主题中立的图像和颜色的关键名称(例如,不要有颜色称为"红色",称之为" primaryHeadingColor")。图像将是文件名,颜色可以是十六进制字符串,例如FF0000表示红色。

每个主题都有一个plist。

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

+ (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.plist主题文件(或者将代码中的@"默认"更改为您实际调用的默认主题plist)。

现在您已经加载了主题,您需要使用它;我假设您的应用有各种图片和文字标签。如果您正在加载并将其放入代码中,那么这部分很容易。如果你是用笔尖做的,那么它有点棘手,但我会解释如何在以后处理它。

现在通常你会通过说:

来加载图像
UIImage *image = [UIImage imageNamed:@"myImage.png"];

但是如果你想让那个图像成为可能的,你现在需要通过说

来加载它。
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:" - 您必须使用类别添加。你可以谷歌使用十六进制字符串" UIColor"找到代码来实现这一目标的解决方案,或者我已经编写了一个方便的类别,可以在此处进行更多操作:https://github.com/nicklockwood/ColorUtils

如果你一直在关注你,你也会想到不是一遍又一遍地写这三行,为什么不向UIColor添加一个叫做的方法:

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

就像我们使用UIImage一样?好主意!

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

我们已经忘记了一件小事......

如果您已将大部分视图构建为笔尖(并且我认为没有理由不这样做)那么这些技术无法正常工作,因为您的图像名称和字体颜色已被隐藏在难以理解的nib数据中,并且没有在源代码中设置。

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

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

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

请注意我使用视图控制器的类名作为键的巧妙技巧 - 这将为您节省一些输入,因为您可以使用该方法创建一个基本ThemeViewController并让所有可见的视图控制器继承它。

这种方法确实意味着要保留每个笔尖的多个副本,如果您以后需要更改任何屏幕,这是一个维护噩梦。

2)你可以为你的笔尖中的所有imageViews和标签制作IBOutlets,然后在viewDidLoad方法中的代码中设置它们的图像和颜色。这可能是最麻烦的方法,但至少你没有重复的笔尖需要维护(这与本地化nibs btw和几乎相同的解决方案选项基本相同)。

3)你可以创建一个名为ThemeLabel的UILabel自定义子类,它在实例化标签时使用上面的代码自动设置字体颜色,然后通过设置标签的类在你的nib文件中使用那些ThemeLabel而不是常规的UILabel到Interface Builder中的ThemeLabel。不幸的是,如果您有多种字体或字体颜色,则需要为每种不同的样式创建不同的UILabel子类。

或者你可能是狡猾的并且使用类似view标签或accessibilityLabel属性的东西作为样式字典键,这样你就可以拥有一个ThemeLabel类并在Interface Builder中设置辅助功能标签来选择样式。

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

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

无论如何,你有一个完整的iOS app主题解决方案。你很受欢迎!

更新:

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

更新2:

从iOS 6开始,现在iOS中内置了一个有限的皮肤系统,允许您使用名为 UIAppearance proxy 的属性来同时为给定控件类的所有实例设置外观(其中&# 39;关于UIAppearance API here)的一个很好的教程。值得检查一下这是否足以满足您的皮肤需求,但如果没有,我上面概述的解决方案仍然有效,可以替代使用,或与UIAppearance结合使用。