【问题标题】:How to switch skins (or design themes) in iOS app?如何在 iOS 应用中切换皮肤(或设计主题)?
【发布时间】:2011-10-29 16:03:51
【问题描述】:

我想让我的 iPhone 应用程序能够在皮肤之间切换(或设计主题,或外观和感觉,如木质、金属、大地色、男士、女孩等......)。

我将准备一些包含按钮和背景图像、声音和文本颜色的皮肤集,并让用户通过应用程序设置来决定他们想要使用的皮肤集。

实现这一点的最佳做法是什么?

条件是:

  • 我想使用界面生成器
  • 我需要支持 iOS 3.1.3 及更高版本
  • 我想让这组皮肤可以从互联网上下载(我不能在应用程序中捆绑所有皮肤,因为一组皮肤需要大量图像,如果我这样做,应用程序文件可能会变得很大。 .. 我也不想硬编码有关特定皮肤的任何信息。)
  • 如果自定义皮肤不包含一个或一些元素(例如图像或声音文件),我希望它使用默认皮肤集中缺少的元素。
  • 我不想为每个皮肤创建 Nib 文件。一个屏幕的 Nib 文件应该是主包中唯一的一个,以便于维护。

我正在考虑在我的应用程序中创建一个所有 UIViewControllers 的超类并覆盖它加载 Nib 文件的部分,而不是从主包加载,而是从保存在 Document 目录中的皮肤加载资源...但我不知道该怎么做... Nib加载方法的默认行为总是从主包中加载资源,并且在阅读后有关资源文件名的信息会丢失... :(

提前感谢您的帮助。

【问题讨论】:

标签: iphone ios themes skin


【解决方案1】:

我不确定最佳实践.. 但是,如果您的应用程序不够大,那么结构良好的 plist 就是您的朋友。

最初,您可以选择:金属主题。以下应该成立:

您要么有一个单例ThemeManager,要么在适当的情况下将NSDictionary 粘贴到您的一个单例上。

ThemeManager 背后的重点是资产和主题之间的映射..

一些示例代码(直接写在 SOF 上.. 不要介意语法错误):

#define kThemeMap(__x__) [[ThemeManager sharedManager] assetForCurrentTheme:__x__]

...

-(void)doUselessStuff {
    UIImage* backgroundImage = [UIImage imageNamed:kThemeMap(@"FirstViewBG")];

    ...

}

//in the ThemeManager:
//returns the appropriate name of the asset based on current theme
-(NSString*)assetForCurrentTheme:(NSString*)asset {
    //_currentTheme is an NSDictionary initialized from a plist. Plist can be downloaded, too.
    NSString* newAsset = [_currentTheme objectForKey:asset];
    if(newAsset == nil) {
        newAsset = [_defaultTheme objectForKey:asset];
    }
    return asset;
}

//Let us assume the user selects Metal Theme somewhere .. Still coding ThemeManager:
-(void)selectedNewTheme:(NSString*)newTheme {
    //First, get the full path of the resource .. Either The main bundle, or documents directory or elsewhere..
    NSString* fullPath = ...;
    self.currentTheme = [NSDictionary dictionaryWithContentsOfFile:fullPath];
}

plist 文件只是一个带有字符串到字符串映射的字典......像这样:

//Default.plist
@"FirstViewBG"  : @"FirstViewBG_Default.png"
@"SecondViewBG" : @"SecondViewBG_Default.png"
@"WinSound"     : @"WinSound_Default.aiff"

//Metal.plist
@"FirstViewBG"  : @"FirstViewBG_Metal.png"
@"SecondViewBG" : @"SecondViewBG_Metal.png"
@"WinSound"     : @"WinSound_Metal.aiff"

或者,你可以只保存后缀,如果这对你来说足够好的话。但是,它需要字符串操作,通过切片扩展 -> 添加后缀 -> 添加扩展..

或者也许让它成为一个前缀?

【讨论】:

  • 非常感谢您的回答和非常描述性的代码!这可能是一个可能的解决方案。 :)
  • 我犹豫采用这种方法的唯一原因是,这样我必须在 Nib 文件和 Plist 中保存有关资源文件名的冗余信息。不过它不会花费太多...我仍然希望有一种方法可以在加载 Nib 期间读取资源文件名,并在此过程中将特定于主题的后缀或前缀添加到文件名中...任何主意?如果这不容易或不可能,我会采取这种方法。谢谢。 ;)
  • aah .. 我真的想不出办法。您必须将它们链接为 IBOutlets 并在 viewDidLoad 中自定义它们;)
  • 感谢 Mazyod!看起来没有简单的方法可以从 Nib 读取资源文件名,所以我决定采用您的方法。我没有为所有 UIView 元素创建 IBOutlets,而是在标记属性中设置整数 ID,并在 PList 中将其用作资源文件的键,然后为我的所有应用程序 UIViewController 子类创建一个通用 UIViewController 父类并覆盖 viewWillAppear 以进行搜索在视图层次结构中递归地使用 ID 设置图像资源。尽管这会留下一些冗余,但仍然可以很好地工作。谢谢! ;)
  • Np,虽然我觉得你自己弄清楚了主要部分:)
【解决方案2】:

您可以使用方法化的 imageNamed: 在 UIImage 上进行分类,使用自定义的 imageNamed: 而不是默认的。 在自定义方法中,按主题选择图片。

【讨论】:

  • 感谢您的回复。好吧,它会起作用,但我认为使用 Objective-C 类别来覆盖方法不是一个好习惯。 (在stackoverflow.com/questions/5272451/… 中讨论过)在这种情况下,我们可以继承 UIImage 来覆盖 imageNamed:,但是这样我们就不能有效地使用 Interface Builder。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-10
  • 2018-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多