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

pat*_*pat 55 cocoa-touch themes objective-c ios

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

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

非常感谢!

Nic*_*ood 130

这很难,因为应用程序没有相应的css样式表.

首先,您需要确定要皮肤的应用程序的哪些部分,以及何时允许用户交换皮肤.

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

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

每个主题你都有一个plist.

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

+ (ThemeManager *)sharedManager
{
    static ThemeManager *sharedManager = nil;
    if (sharedManager == nil)
    {
        sharedManager = [[ThemeManager alloc] init];
    }
    return sharedManager;
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

(注意:有些人不喜欢在init方法中做很多工作.我从来没有发现它是一个问题,但如果你愿意,可以创建一个单独的方法来加载主题词典并从你的应用程序调用它设置代码).

请注意我是如何从用户默认值中获取主题plist的名称的.这意味着用户可以在您的首选项中选择一个主题并保存它,该应用程序将在下次启动时加载该主题.如果没有选择主题,我会输入默认主题名称"default",因此请确保您有一个default.plist主题文件(或者将代码中的@"default"更改为您的默认主题plist实际调用的内容).

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

现在通常你会通过说:

UIImage *image = [UIImage imageNamed:@"myImage.png"];
Run Code Online (Sandbox Code Playgroud)

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

NSDictionary *styles = [ThemeManager sharedManager].styles;
NSString *imageName = [styles objectForKey:@"myImageKey"];
UIImage *image = [UIImage imageNamed:imageName];
Run Code Online (Sandbox Code Playgroud)

这将在您的主题文件中查找与主题"myImageKey"匹配的主题图像,并将加载它.根据您加载的主题文件,您将获得不同的风格.

你会经常使用这三行,所以你可能想把它们包装在一个函数中.一个好主意是在UIImage上创建一个类别,声明一个名为的方法:

+ (UIImage *)themeImageNamed:(NSString *)key;
Run Code Online (Sandbox Code Playgroud)

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

好的,这就是为你的图像添加它.要设置标签颜色的主题,假设您当前正在设置标签颜色,请说:

 someLabel.color = [UIColor redColor];
Run Code Online (Sandbox Code Playgroud)

您现在将其替换为:

NSDictionary *styles = [ThemeManager sharedManager].styles;
NSString *labelColor = [styles objectForKey:@"myLabelColor"];
someLabel.color = [UIColor colorWithHexString:labelColor];
Run Code Online (Sandbox Code Playgroud)

现在您可能已经注意到UIColor没有方法"colorWithHexString:" - 您必须使用类别添加它.您可以使用Google的"UIColor with hex string"解决方案来查找代码来执行此操作,或者我已经编写了一个方便的类别,可以在此处执行此操作:https://github.com/nicklockwood/ColorUtils

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

+ (UIColor *)themeColorNamed:(NSString *)key;
Run Code Online (Sandbox Code Playgroud)

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

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

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

如果你已经将大部分视图构建为nib(并且我没有理由不这样做),那么这些技术将不起作用,因为您的图像名称和字体颜色被隐藏在难以穿透的nib数据中而不是在源代码中设置.

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

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

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

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

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

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

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

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

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

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

无论如何,你有一个完整的iOS应用程序主题解决方案.别客气!

更新:

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

更新2:

作为iOS 6后,现在有内置的iOS有限换肤系统,使您可以使用一个属性叫做UIAppearance代理对皮肤有给定的控制类的所有实例一次(有关于UIAppearance API的一个很好的教程在这里).值得检查这是否足以满足您的皮肤需求,但如果没有,我上面概述的解决方案仍然可以正常使用,可以替代使用,或与UIAppearance结合使用.

  • 哦,我的上帝!我认为这是我读过的最好,最好,最好的指南/答案/帮助/教程.尚未到达终点,但对于如何花时间以简单的方式很好地解释事物而感到惊讶.真的很棒!我希望IRC频道的人们也很好.非常感谢. (6认同)
  • 为什么这样的问题在SO上关闭?没有任何意义.这是该网站的失败.Thanx的帖子@NickLockwood! (4认同)
  • 非常棒的感谢nik (3认同)