1,ios app主题切换的原理就是为具有相同名称的图片创建不同的路径,在载入这些图片的时候,我们需要根据不同的主题找到不同的路径,然后根据不的路径加上图片的名称,从而找到不同的图片。
2,根据上述原理,首先我们需要创建一个plist文件,存储不同主题对应的图片路径。创建themes.plist文件,如下图:
此文件中保存了两种主题blue和pink,还有他们所对应的路径,分别是主目录下的blue文件夹和pink文件夹。这个路径是根据图片的在项目中的位置来赋值的。因此创建一个group名称为Theme,然后将themes.plist加入到这个group当中来。
3,在你的硬盘上创建3个文件夹,分别是imges,blue,pink。在这每一个文件夹中加入你们需要的图片,3个文件夹中的图片不同但是图片的名称相同,即不同风格或颜色的同名图片。而且在这些文件夹中都加入一个fontColor.plist文件,用于保存在各个主题下面,字体的颜色,内容如下图:
其中“kThemeColor”值不变,它的value将根据不同主题而变化,“0,0,105”是RGB三色值。
首先将images加入到group “Theme”当中来,注意下图中的选项:
此时我们在 Folders的选项是 “create groups for any added foldeers”,意思是以group的形式加入这些文件夹,这些加入的文件在项目中实际上是存储在项目主目录下面的。文件路径:主目录/1.png
接着我们将加入blue和pink文件夹,此时我们在 Folders的选项是 “create folder reference for any added foldeers”,意思是以文件夹的引用形式加入这些文件夹,这些加入的文件在项目中实际上是存储在项目主目录下面自己的文件夹下面,这样文件的路径就不同了,就会多了一层路径: 主目录/blue/1.png , 主目录/pink/1.png。
当完成这些步骤之后,我们的项目将是这样的:
4,接下来我们将要创建主题的管理类了,我们在“Theme”group中创建ThemeManager类。内容如下:
//
// ThemeManager.h
// ThemeDemo
//
// Created by 陈 海涛 on 13-8-21.
// Copyright (c) 2013年 陈 海涛. All rights reserved.
//
#import <Foundation/Foundation.h>
//主题发生改变通知名称
#define kThemeChangedNotification @"themeChanged"
@interface ThemeManager : NSObject
//主题名称
@property (nonatomic,strong) NSString *themeName;
//themes.plist文件中存储的字典数据,是主题名称与主题路径
@property (nonatomic,strong) NSDictionary *themesPlist;
//根据主题名称找到相应的主题路径,在各个主题路径对应的文件夹中存储的是主题字体颜色fontColor.plist
@property (nonatomic,strong) NSDictionary *themeColorPlist;
//单例获取ThemeManager对象实例
+ (id)shareInstance;
//获取主题图片
- (UIImage *)themeImageWithName:(NSString *)imageName;
//获取主题字体颜色,name为fontColor.plist文件中的key
- (UIColor *)themeColorWithName:(NSString *)name;
@end
//
// ThemeManager.m
// ThemeDemo
//
// Created by 陈 海涛 on 13-8-21.
// Copyright (c) 2013年 陈 海涛. All rights reserved.
//
#import "ThemeManager.h"
static ThemeManager *shareInstance;
@implementation ThemeManager
+ (id)shareInstance
{
if (shareInstance == nil) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shareInstance = [[ThemeManager alloc] init];
});
}
return shareInstance;
}
#pragma mark -重写初始化方法
- (id)init
{
self = [super init];
if (self) {
NSString *themesPlistPath = [[NSBundle mainBundle] pathForResource:@"themes" ofType:@"plist"];
self.themesPlist = [NSDictionary dictionaryWithContentsOfFile:themesPlistPath];
self.themeName = nil;
self.themeColorPlist = nil;
}
return self;
}
//重写setThemeName方法
- (void)setThemeName:(NSString *)themeName
{
if (_themeName != themeName) {
_themeName = themeName;
}
//主题路径
NSString *themePath = [self _themePathWithThemeName];
self.themeColorPlist = [NSDictionary dictionaryWithContentsOfFile:[themePath stringByAppendingPathComponent:@"fontColor.plist"]];
[[NSNotificationCenter defaultCenter] postNotificationName:kThemeChangedNotification object:self];
}
#pragma mark -
//根据主题名称获取相应的主题路径
- (NSString *)_themePathWithThemeName
{
NSString *themePath = nil;
//默认情况self.themeName为空
if (self.themeName == nil) {
themePath = [[NSBundle mainBundle] resourcePath];
}else{
//根据themeName在themes.plist文件中获取相应的主题路径
themePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:self.themesPlist[self.themeName]];
}
return themePath;
}
//获取主题图片
- (UIImage *)themeImageWithName:(NSString *)imageName
{
//判断imageName是否为nil,或者是否具有有效数据
if (imageName == nil || [[imageName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] isEqualToString:@""]) {
return nil;
}
//主题路径
NSString *themePath = [self _themePathWithThemeName];
//图片路径
NSString *imagePath = [themePath stringByAppendingPathComponent:imageName];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:imagePath];
return image;
}
//获取主题字体颜色,name为fontColor.plist文件中的key
- (UIColor *)themeColorWithName:(NSString *)name
{
//判断name是否为nil,或者是否具有有效数据
if (name == nil || [[name stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] isEqualToString:@""]) {
return nil;
}
NSString *colorStr = self.themeColorPlist[name];
NSArray *colors = [colorStr componentsSeparatedByString:@","];
if (colors.count == 3) {
UIColor *color = [UIColor colorWithRed:[colors[0] floatValue]/255.0f green:[colors[1] floatValue]/255.0f blue:[colors[2] floatValue]/255.0f alpha:1.0f];
return color;
}
return nil;
}
@end
根据代码注释内容应该不难懂的。
5,下面我们将要自定义我们的主题控件了,我们自定义一个ThemeTabbarItem,和一个ThemeLabel,这些自定义代码的目的是为了给我们的控件加上对上诉代码中
[[NSNotificationCenter defaultCenter] postNotificationName:kThemeChangedNotification object:self];
这个通知的监听,当主题发生改变时,我们将会获得通知并且重现想ThemeManager去获取相应的图片或者字体颜色。因此我们在自己的控件时,我们在初始化方法(init...............)方法或者nib文件创建的时awakeFromNib方法中加入我们的监听通知:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(themeChanged:) name:kThemeChangedNotification object:nil];
具体的代码如下:
//
// ThemeLabel.h
// ThemeDemo
//
// Created by 陈 海涛 on 13-8-21.
// Copyright (c) 2013年 陈 海涛. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface ThemeLabel : UILabel
@end
//
// ThemeLabel.m
// ThemeDemo
//
// Created by 陈 海涛 on 13-8-21.
// Copyright (c) 2013年 陈 海涛. All rights reserved.
//
#import "ThemeLabel.h"
#import "ThemeManager.h"
@implementation ThemeLabel
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(themeChanged:) name:kThemeChangedNotification object:nil];
[self customTextColor];
}
return self;
}
- (void)awakeFromNib
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(themeChanged:) name:kThemeChangedNotification object:nil];
[self customTextColor];
}
- (void)themeChanged:(NSNotification*)notification
{
[self customTextColor];
}
- (void)customTextColor
{
self.textColor = [[ThemeManager shareInstance] themeColorWithName:@"kThemeColor"];
}
@end
//
// ThemeTabbarItem.h
// ThemeDemo
//
// Created by 陈 海涛 on 13-8-21.
// Copyright (c) 2013年 陈 海涛. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface ThemeTabbarItem : UITabBarItem
@property (nonatomic,strong) NSString *imageName;
- (id)initWithTitle:(NSString *)title imageName:(NSString *)imageName tag:(NSInteger)tag;
@end
//
// ThemeTabbarItem.m
// ThemeDemo
//
// Created by 陈 海涛 on 13-8-21.
// Copyright (c) 2013年 陈 海涛. All rights reserved.
//
#import "ThemeTabbarItem.h"
#import "ThemeManager.h"
@implementation ThemeTabbarItem
- (id)initWithTitle:(NSString *)title imageName:(NSString *)imageName tag:(NSInteger)tag
{
UIImage *image = [[ThemeManager shareInstance] themeImageWithName:imageName];
self = [super initWithTitle:title image:image tag:tag];
if (self) {
self.imageName = imageName;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(themeChanged:) name:kThemeChangedNotification object:nil];
}
return self;
}
- (void)themeChanged:(NSNotification *)notification
{
[self customTabbarItem];
}
- (void)customTabbarItem
{
self.image = [[ThemeManager shareInstance] themeImageWithName:self.imageName];
}
@end
6,接下来我们将要使用这些主题控件和主题管理器了。
首先,我们自定义一个BaseNavigationController用于自定义navigationBar,并添加对主题改变的事件通知的监听,主要代码如下:
//
// BaseNavigationController.m
// ThemeDemo
//
// Created by 陈 海涛 on 13-8-21.
// Copyright (c) 2013年 陈 海涛. All rights reserved.
//
#import "BaseNavigationController.h"
#import "ThemeManager.h"
@interface BaseNavigationController ()
@end
@implementation BaseNavigationController
- (void)viewDidLoad
{
[super viewDidLoad];
[self customNavigationbar];
//注册主题改变通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_themeChanged:) name:kThemeChangedNotification object:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - 主题发生改变的处理
- (void)_themeChanged:(NSNotification *)notification
{
[self customNavigationbar];
}
//根据主题来自定义Navigationbar
- (void)customNavigationbar
{
UIImage *backgroundImage = [[ThemeManager shareInstance] themeImageWithName:@"navigationbar_background.png"];
[self.navigationBar setBackgroundImage:backgroundImage forBarMetrics:UIBarMetricsDefault];
}
- (void)dealloc
{
//注销主题改变通知
[[NSNotificationCenter defaultCenter] removeObserver:self name:kThemeChangedNotification object:nil];
}
@end
然后,自定义一个ThemeTabbarController,来自定义Tabbar,并且监听主题发生改变并且重新去请求图片,具体代码如下:
//
// BaseTabBarController.m
// ThemeDemo
//
// Created by 陈 海涛 on 13-8-21.
// Copyright (c) 2013年 陈 海涛. All rights reserved.
//
#import "BaseTabBarController.h"
#import "ThemeManager.h"
@interface BaseTabBarController ()
@end
@implementation BaseTabBarController
- (void)viewDidLoad
{
[super viewDidLoad];
[self customTabbar];
//注册主题改变的监听
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(themeChanged:) name:kThemeChangedNotification object:nil];
}
-(void)themeChanged:(NSNotification *)notification
{
[self customTabbar];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
//自定义tabbar
- (void)customTabbar
{
UIImage *image = [[ThemeManager shareInstance] themeImageWithName:@"tabbar_background.png"];
[self.tabBar setBackgroundImage:image];
}
-(void)dealloc
{
//移除监听
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end
我们创建ViewController和SettingViewController和相应的xib文件,然后 我们将初始化我们的项目,使其能够运行起来,我们修改appDelegate类,具体代码如下:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
BaseTabBarController *tabVC = [[BaseTabBarController alloc] init];
ViewController *vc = [[ViewController alloc] init];
vc.tabBarItem = [[ThemeTabbarItem alloc] initWithTitle:@"主题展示" imageName:@"tabbar_home.png" tag:1];
SettingViewController *sVC = [[SettingViewController alloc] init];
sVC.tabBarItem = [[ThemeTabbarItem alloc] initWithTitle:@"主题设置" imageName:@"tabbar_more.png" tag:200];
BaseNavigationController *baseNav1 = [[BaseNavigationController alloc] initWithRootViewController:vc];
BaseNavigationController *baseNav2 = [[BaseNavigationController alloc] initWithRootViewController:sVC];
tabVC.viewControllers = @[baseNav1,baseNav2];
self.window.rootViewController = tabVC;
[self.window makeKeyAndVisible];
return YES;
}
7,接下来我们将要实现ViewController和SettingViewController了,我们实现SettingViewController,它提供了主题选择,并且将选择的结果保存到UserDefaults中,具体的代码:
#import <UIKit/UIKit.h>
#define kThemeFlagKey @"ThemeFlagKey"
@interface SettingViewController : UIViewController <UITableViewDataSource,UITableViewDelegate>
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@end
//
// SettingViewController.m
// ThemeDemo
//
// Created by 陈 海涛 on 13-8-21.
// Copyright (c) 2013年 陈 海涛. All rights reserved.
//
#import "SettingViewController.h"
#import "ThemeManager.h"
#import "ThemeTabbarItem.h"
@interface SettingViewController ()
@end
@implementation SettingViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = @"主题设置";
self.tableView.delegate = self;
self.tableView.dataSource = self;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - UITableView delegate and dataSource method
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 3;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = @"MyCell";
UITableViewCell *cell = nil;
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
switch (indexPath.row) {
case 0:
cell.textLabel.text = @"默认";
break;
case 1:
cell.textLabel.text = @"blue";
break;
case 2:
cell.textLabel.text = @"pink";
break;
default:
break;
}
NSInteger selectedRow = [[NSUserDefaults standardUserDefaults] integerForKey:kThemeFlagKey];
if (selectedRow == indexPath.row) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}else{
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[[NSUserDefaults standardUserDefaults] setInteger:indexPath.row forKey:kThemeFlagKey];
[[NSUserDefaults standardUserDefaults] synchronize];
ThemeManager *themeManager = [ThemeManager shareInstance];
switch (indexPath.row) {
case 0:
themeManager.themeName = nil;
break;
case 1:
themeManager.themeName = @"blue";
break;
case 2:
themeManager.themeName = @"pink";
break;
default:
break;
}
[tableView reloadData];
}
@end
当我们在SettingViewController中选择了主题,将会被存储到UserDefaults当中,当下次进入系统的时候我们将从UserDfaults当中读取相应的主题,因此在AppDeledate当中我们将加入如下代码:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
BaseTabBarController *tabVC = [[BaseTabBarController alloc] init];
ViewController *vc = [[ViewController alloc] init];
vc.tabBarItem = [[ThemeTabbarItem alloc] initWithTitle:@"主题展示" imageName:@"tabbar_home.png" tag:1];
SettingViewController *sVC = [[SettingViewController alloc] init];
sVC.tabBarItem = [[ThemeTabbarItem alloc] initWithTitle:@"主题设置" imageName:@"tabbar_more.png" tag:200];
BaseNavigationController *baseNav1 = [[BaseNavigationController alloc] initWithRootViewController:vc];
BaseNavigationController *baseNav2 = [[BaseNavigationController alloc] initWithRootViewController:sVC];
tabVC.viewControllers = @[baseNav1,baseNav2];
self.window.rootViewController = tabVC;
//初始化主题管理器
[self _initThemeManager];
[self.window makeKeyAndVisible];
return YES;
}
//初始化主题管理器
- (void)_initThemeManager
{
NSInteger selectedRow = [[NSUserDefaults standardUserDefaults] integerForKey:kThemeFlagKey];
NSString *themeName = nil;
switch (selectedRow) {
case 0:
themeName = nil;
break;
case 1:
themeName = @"blue";
break;
case 2:
themeName = @"pink";
break;
default:
break;
}
[[ThemeManager shareInstance] setThemeName:themeName];
}
现在运行程序就可以实现主题切换选择了。
最后我们还要测试一下我们的ThemeLabel,在ViewController的xib文件中加入一个label,然后将其改为ThemeLabel类型,并设置相应的text值,然后运行程序你就可以看到ThemeLabel上文字的颜色随着主题的变换而变化。
下面有这个项目的源代码,大家共同探讨。