前言

iOS 实现主题切换,相信在未来的app里也是会频繁出现的,尽管现在只是出现在主流的APP,如(QQ、新浪微博、酷狗音乐、网易云音乐等),但是现在是看颜值、追求个性的年代,所以根据用户喜好自定义/切换主题也是未来app的必备功能了。

实现思路

为了降低耦合度,决定采用的方案是使用NSObject的分类来实现主题设置,有些读者可能会想为何不使用UIView的分类而是使用NSObject的分类?建议这部分读者看一下UIBarItem父类,然后仔细思考一下,就会理解了。

设置主题色

PYThemeColor.png

  1. 创建主题色池

  2. 将需要设置主题色的控件及其对应属性/方法添加到主题色池中

  3. 调用设置主题色方法时,遍历主题色池中的控件,使用KVC设置对应属性或调用对应的方法来实现主题色的设置

代码实现

建议读者在理解思路以后先下载源码大概看一下(纵观全局)再阅读以下内容:

源码地址:https://github.com/iphone5solo/PYTheme

1. 创建主题色池

由于是在NSObject的分类里面创建,为了方便管理,设置全局变量_themeColorPool,并通过懒加载完成_themeColorPool的实例化。数组中的对象采用为NSDictionary的原因见下一步就会理解了

1
2
3
4
5
6
7
8
9
10
11
/** 主题颜色池 */
static NSMutableArray *_themeColorPool;
 
#pragma mark - 懒加载
- (NSMutableArray *)themeColorPool
{
    if (!_themeColorPool) {
        _themeColorPool = [NSMutableArray array];
    }
    return _themeColorPool;
}

2. 添加控件到主题色池中

由于颜色设置有的可以直接通过属性设置也有的需要通过调用方法才可设置。以UIButton为例,设置背景色可通过属性button.backgroundColor设置,设置选中状态时的字体颜色则要调用setTitleColor:forState:方法才可设置,于是,就得提供两个方法供使用者调用,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
 * 添加到主题色池
 * selector : 执行方法
 * objects : 方法参数数组
 * 注意:方法参数必须按顺序一一对应,如果涉及到的主题色设置使用 PYTHEME_THEME_COLOR 宏定义代替
 * 如果数组中某个参数为nil,需包装为 [NSNull null] 对象再添加到数组中
 */
- (void)py_addToThemeColorPoolWithSelector:(SEL)selector objects:(NSArray *)objects;
/** 
 * 添加到主题色池
 * propertyName : 属性名
 */
- (void)py_addToThemeColorPool:(NSString *)propertyName;

实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#pragma mark - Theme Color
/**
 * 添加到主题色池
 * selector : 执行方法
 * objects : 方法参数数组
 * 注意:方法参数必须按顺序一一对应,如果涉及到的主题色设置使用 PYTHEME_THEME_COLOR 宏定义代替
 * 如果数组中某个参数为nil,需包装为 [NSNull null] 对象再添加到数组中
 */
- (void)py_addToThemeColorPoolWithSelector:(SEL)selector objects:(NSArray *)objects
{
    // 判断参数是否为空
    if (!objects) return;
    Class appearanceClass = NSClassFromString(@"_UIAppearance");
    // 如果对象为_UIAppearance,直接返回
    if ([self isMemberOfClass:appearanceClass]) return;
    // 键:对象地址+方法名 值:对象
    NSString *pointSelectorString = [NSString stringWithFormat:@"%p%@", self, NSStringFromSelector(selector)];
    NSDictionary *dic = @{ pointSelectorString      : self,
                           PYTHEME_COLOR_ARGS_KEY   : objects };
    // 判断是否已经在主题色池中
    if (![[self themeColorPool] containsObject:dic]) { // 不在主题色池中
        [[self themeColorPool] addObject:dic];
        if (_currentThemeColor) { // 已经设置主题色,直接设置
            [self py_performSelector:selector withObjects:objects];
        }
    }
}
 
/**
 * 添加到主题色池
 * propertyName : 属性名
 */
- (void)py_addToThemeColorPool:(NSString *)propertyName
{
    // 如果对象为_UIAppearance,直接返回
    Class appearanceClass = NSClassFromString(@"_UIAppearance");
    if ([self isMemberOfClass:appearanceClass]) return;
    // 键:对象地址+属性名 值:对象
    NSString *pointString = [NSString stringWithFormat:@"%p%@", self, propertyName];
    NSDictionary *dic = @{ pointString : self };
    // 判断是否已经在主题色中
    if (![[self themeColorPool] containsObject:dic]) { // 不在主题色池中
        [[self themeColorPool] addObject:dic];
        if (_currentThemeColor) { // 已经设置主题色,直接设置
            [self setValue:_currentThemeColor forKey:propertyName];
        }
    }
    // 遍历主题色池(移除应该被回收的对象)
    for (NSDictionary *dict in [[self themeColorPool] copy]) {
        // 取出key
        NSString *objectKey = nil;
        for (NSString *key in [dict allKeys]) {
            if (![key isEqualToString:PYTHEME_COLOR_ARGS_KEY]) {
                objectKey = key;
                break;
            }
        }
        // 取出对象
        id object = [dict valueForKey:objectKey];
        // 取出对象的引用计数
        NSInteger retainCount = [[object valueForKey:@"retainCount"] integerValue];
        if (retainCount == 2) { // 对象应该被回收了
            [[self themeColorPool] removeObject:dict];
        }
    }
}

为了满足个别需求,所以还是提供一下从主题色池中移除控件的方法

1
2
3
4
5
6
7
8
9
10
/**
 * 从主题色池移除
 * propertyName : 属性名
 */
- (void)py_removeFromThemeColorPool:(NSString *)propertyName;
/** 
 * 从主题色池移除
 * selector : 方法选择器
 */
- (void)py_removeFromThemeColorPoolWithSelector:(SEL)selector;

实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
 * 从主题色池移除
 * propertyName : 属性名
 */
- (void)py_removeFromThemeColorPool:(NSString *)propertyName
{
    // 如果对象为_UIAppearance,直接返回
    Class appearanceClass = NSClassFromString(@"_UIAppearance");
    if ([self isMemberOfClass:appearanceClass]) return;
    // 键:对象地址+属性名 值:对象
    NSString *pointString = [NSString stringWithFormat:@"%p%@", self, propertyName];
    NSDictionary *dic = @{ pointString : self };
    // 判断是否已经在主题色池中
    if ([[self themeColorPool] containsObject:dic]) { // 在主题色池中
        [[self themeColorPool] removeObject:dic];
    }
}
 
/**
 * 从主题色池移除
 * selector : 方法选择器
 */
- (void)py_removeFromThemeColorPoolWithSelector:(SEL)selector
{
    // 如果对象为_UIAppearance,直接返回
    Class appearanceClass = NSClassFromString(@"_UIAppearance");
    if ([self isMemberOfClass:appearanceClass]) return;
    // 键:对象地址+方法名
    NSString *pointSelectorString = [NSString stringWithFormat:@"%p%@", self, NSStringFromSelector(selector)];
    // 遍历主题色池(移除应该被回收的对象)
    for (NSDictionary *dict in [[self themeColorPool] copy]) {
        for (NSString *key in [dict allKeys]) {
            if ([key isEqualToString:pointSelectorString]) { // 存在,移除
                [[self themeColorPool] removeObject:dict];
                return;
            }
        }
    }
}

3. 设置主题色

实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
 * 设置主题色
 * color : 主题色
 */
- (void)py_setThemeColor:(UIColor *)color
{
    _currentThemeColor = color;
    // 遍历缓主题池,设置统一主题色
    for (NSDictionary *dict in [_themeColorPool copy]) {
        // 取出key
        NSString *objectKey = nil;
        for (NSString *key in [dict allKeys]) {
            if (![key isEqualToString:PYTHEME_COLOR_ARGS_KEY]) {
                objectKey = key;
                break;
            }
        }
        // 取出对象
        id object = [dict valueForKey:objectKey];
        if ([objectKey containsString:@":"]) { // 方法
            // 取出参数
            NSArray *args = dict[PYTHEME_COLOR_ARGS_KEY];
            // 取出方法
            NSString *selectorName = [objectKey substringFromIndex:[[NSString stringWithFormat:@"%p", object] length]];
            SEL selector = NSSelectorFromString(selectorName);
            // 调用方法,设置属性
            [object py_performSelector:selector withObjects:args];
        else // 成员属性
            // 取出属性值
            NSString *propertyName = [objectKey substringFromIndex:[[NSString stringWithFormat:@"%p", object] length]];
            // 给对象的对应属性赋值(使用KVC)
            [object setValue:color forKeyPath:propertyName];
        }
    }
}

使用

假设有个需求:UINavigationBar的背景颜色和UIButton选中时的字体颜色会随着主题颜色的变化而变化,实现如下:

将navigationBar的background和UIButton的setTitleColor:forState:方法添加到主题池中,方法参数中如果是设置为主题色的参数则用PYTHEME_THEME_COLOR占位,如果参数为nil,则使用[NSNull null]代替

1
2
3
4
5
6
7
8
9
// 创建导航栏
UINavigationBar *navigationBar = [[UINavigationBar alloc] init];
// 添加到主题色池中
[navigationBar py_addToThemeColorPool:@"barTintColor"];
 
// 创建按钮
UIButton *button = [[UIButton alloc] init];
// 添加到主题色中
[button py_addToThemeColorPoolWithSelector:@selector(setTitleColor:forState:) objects:@[PYTHEME_THEME_COLOR, @(UIControlStateSelected)]];

设置主题色

1
2
// 设置主题色为红色
[self py_setThemeColor:[UIColor redColor]];

这里有一点注意的是[object py_performSelector:selector withObjects:args];这是自己实现的performSelector 多参调用关于这方面的网上已经有很多教程了,这里就不多介绍了。直接附上的我实现(内部方法,主要考虑到自己的使用):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#pragma mark - performSelector 多参调用
- (id)py_performSelector:(SEL)selector withObjects:(const NSArray *)objects
{
    // 1. 创建方法签名
    // 根据方法来初始化NSMethodSignature
    NSMethodSignature *methodSignate = [[self class] instanceMethodSignatureForSelector:selector];
    if (!methodSignate) { // 没有该方法
        return self;
    }
    // 2. 创建invocation对象(包装方法)
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignate];
    // 3. 设置相关属性
    // 调用者
    invocation.target = self;
    // 调用方法
    invocation.selector = selector;
    // 获取除self、_cmd的参数个数
    NSInteger paramsCount = methodSignate.numberOfArguments - 2;
    // 取最少的,防止越界
    NSInteger count = MIN(paramsCount, objects.count);
    // 用于dictionary的拷贝(用于保住objCopy,避免非法内存访问)
    NSMutableDictionary *objCopy = nil;
    // 设置参数
    for (int i = 0; i < count; i++) {
        // 取出参数对象
        id obj = objects[i];
        // 如果是主题颜色参数颜色,则设置
        if ([obj isKindOfClass:[NSString class]] && [obj isEqualToString:PYTHEME_THEME_COLOR]) {
            obj = _currentThemeColor;
        }
        // 判断需要设置的参数是否是NSNull, 如果是就设置为nil
        if ([obj isKindOfClass:[NSNull class]]) {
            obj = nil;
        }
        // 获取参数类型
        const char *argumentType = [methodSignate getArgumentTypeAtIndex:i + 2];
        // 判断参数类型 根据类型转化数据类型(如果有必要)
        NSString *argumentTypeString = [NSString stringWithUTF8String:argumentType];
        if ([argumentTypeString isEqualToString:@"@"]) { // id
            // 如果是dictionary,可能存在 PYTHEME_THEME_COLOR
            if ([obj isKindOfClass:[NSDictionary class]]) { // NSDictionary
                objCopy = [obj mutableCopy];
                // 取出所有键
                NSArray *keys = [objCopy allKeys];
                for (NSString *key in keys) {
                    // 取出值
                    id value = objCopy[key];
                    if ([value isKindOfClass:[NSString class]] && [value isEqualToString:PYTHEME_THEME_COLOR]) {
                        // 替换成颜色
                        [objCopy setValue:_currentThemeColor forKey:key];
                    }
                }
                [invocation setArgument:&objCopy atIndex:i + 2];
            else // 其他
                [invocation setArgument:&obj atIndex:i + 2];
            }
        }  else if ([argumentTypeString isEqualToString:@"B"]) { // bool
            bool objVaule = [obj boolValue];
            [invocation setArgument:&objVaule atIndex:i + 2];
        else if ([argumentTypeString isEqualToString:@"f"]) { // float
            float objVaule = [obj floatValue];
            [invocation setArgument:&objVaule atIndex:i + 2];
        else if ([argumentTypeString isEqualToString:@"d"]) { // double
            double objVaule = [obj doubleValue];
            [invocation setArgument:&objVaule atIndex:i + 2];
        else if ([argumentTypeString isEqualToString:@"c"]) { // char
            char objVaule = [obj charValue];
            [invocation setArgument:&objVaule atIndex:i + 2];
        else if ([argumentTypeString isEqualToString:@"i"]) { // int
            int objVaule = [obj intValue];
            [invocation setArgument:&objVaule atIndex:i + 2];
        else if ([argumentTypeString isEqualToString:@"I"]) { // unsigned int
            unsigned int objVaule = [obj unsignedIntValue];
            [invocation setArgument:&objVaule atIndex:i + 2];
        else if ([argumentTypeString isEqualToString:@"S"]) { // unsigned short
            unsigned short objVaule = [obj unsignedShortValue];
            [invocation setArgument:&objVaule atIndex:i + 2];
        else if ([argumentTypeString isEqualToString:@"L"]) { // unsigned long
            unsigned long objVaule = [obj unsignedLongValue];
            [invocation setArgument:&objVaule atIndex:i + 2];
        else if ([argumentTypeString isEqualToString:@"s"]) { // shrot
            short objVaule = [obj shortValue];
            [invocation setArgument:&objVaule atIndex:i + 2];
        else if ([argumentTypeString isEqualToString:@"l"]) { // long
            long objVaule = [obj longValue];
            [invocation setArgument:&objVaule atIndex:i + 2];
        else if ([argumentTypeString isEqualToString:@"q"]) { // long long
            long long objVaule = [obj longLongValue];
            [invocation setArgument:&objVaule atIndex:i + 2];
        else if ([argumentTypeString isEqualToString:@"C"]) { // unsigned char
            unsigned char objVaule = [obj unsignedCharValue];
            [invocation setArgument:&objVaule atIndex:i + 2];
        else if ([argumentTypeString isEqualToString:@"Q"]) { // unsigned long long
            unsigned long long objVaule = [obj unsignedLongLongValue];
            [invocation setArgument:&objVaule atIndex:i + 2];
        else if ([argumentTypeString isEqualToString:@"{CGRect={CGPoint=dd}{CGSize=dd}}"]) { // CGRect
            CGRect objVaule = [obj CGRectValue];
            [invocation setArgument:&objVaule atIndex:i + 2];
        else if ([argumentTypeString isEqualToString:@"{UIEdgeInsets=dddd}"]) { // UIEdgeInsets
            UIEdgeInsets objVaule = [obj UIEdgeInsetsValue];
            [invocation setArgument:&objVaule atIndex:i + 2];
        }
    }
    // 4.调用方法
    [invocation invoke];
    // 5. 设置返回值
    id returnValue = nil;
    if (methodSignate.methodReturnLength != 0) { // 有返回值
        // 将返回值赋值给returnValue
        [invocation getReturnValue:&returnValue];
    }
    return returnValue;
}

细节处理

1. 设置主题色的方式

  • 通过属性直接设置主题色

  • 通过调用方法并以主题色为参数来设置主题色

  • 通过调用方法但主题色被封装后(如:NSDictionary)作为参数设置主题色

2. 自动管理内存管理

当对象应该被释放后,下一次当主题色池有新元素添加时,会遍历主题色池,根据对象的引用计数来决定是否移除对象(实现自动管理内存),因此:主题色池中最多可能会残留一个对象,这对内存几乎没有任何影响,如果要及时释放对象本人认为可以采用KVO监听对象的引用计数(未尝试),但是耗能高,不建议这么做!

3. 当对象为_UIAppearance类时,不添加到主题色池

了解UIAppearance的读者应该可以理解,而且使用UIAppearance的目的也为为了设置全局色,所以为了避免冲突,如果使用了该“技术”就不添加到主题色池

设置主题图片

观察了新浪微博、酷狗音乐等app,发现设置主题图片还是很有必要的,而且发现每套主题皮肤/图片都有对应的主题色,所以在设计接口的时候都考虑了这方面的需求。先看一下设置主题图片的基本原理,如下:

  • 创建一个主题图片池(使用懒加载)

  • 将相关控件对象直接添加到主题图片池中

  • 设置主题图片时,通过block把主题图片池中的所有对象传递给用户,用户实现block,在block中获得对象,并根据需求设置相关属性完成主题图片的设置

代码实现:

1. 创建一个主题图片池(使用懒加载)

1
2
3
4
5
6
7
8
9
10
/** 主题图片池 */
static NSMutableArray(id) *_themeImagePool;(此处圆括号代替尖括号)
 
- (NSMutableArray *)themeImagePool
{
    if (!_themeImagePool) {
        _themeImagePool = [NSMutableArray array];
    }
    return _themeImagePool;
}

2. 添加相关控件到主题图片池中

因为在设置图片是,比较复杂,如UITabBar上面的UIBarItem的图片、字体颜色等,所以为了满足大部分用户的需求,决定采用的是直接存储控件对象

1
2
3
4
5
/** 添加到主题图片池 */
- (void)py_addToThemeImagePool;
 
/** 从主题图片池中移除 */
- (void)py_removeFromThemeImagePoo

实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#pragma mark - Theme Image
/** 添加到主题图片池 */
- (void)py_addToThemeImagePool
{
    // 如果对象为_UIAppearance,直接返回
    Class appearanceClass = NSClassFromString(@"_UIAppearance");
    if ([self isMemberOfClass:appearanceClass]) return;
 
    if ([self isKindOfClass:[UITabBarItem class]]) { // 如果是UITabBarItem,判断是否有设置图片
        UITabBarItem *item = (UITabBarItem *)self;
        if (!item.image) { // 没有设置图片
            item.image = [[UIImage alloc] init];
        }
        if (!item.selectedImage) { // 没有设置图片
            item.selectedImage = [[UIImage alloc] init];
        }
    }
    // 判断是否已经在主题图片池中
    if (![[self themeImagePool] containsObject:self]) { // 不在主题图片池中
        [[self themeImagePool] addObject:self];
    }
    // 遍历主题图片池(移除应该被回收的对象)
    for (id object in [self themeImagePool]) {
        NSInteger retainCount = [[object valueForKey:@"retainCount"] integerValue];
        if (retainCount == 2) { // 对象应该被回收了
            [[self themeImagePool] removeObject:self];
        }
    }
}
 
/** 从主题图片池中移除 */
- (void)py_removeFromThemeImagePool
{
    // 如果对象为_UIAppearance,直接返回
    Class appearanceClass = NSClassFromString(@"_UIAppearance");
    if ([self isMemberOfClass:appearanceClass]) return;
 
    // 判断是否已经在图片池中
    if ([[self themeImagePool] containsObject:self]) { // 在主题图片池中
        [[self themeImagePool] removeObject:self];
    }
}

3. 设置主题图片和相关配色

当设置图片时,会通过block将主题图片池里面的所有控件传递给用户,用户根据需求进行相关设置,如果提供了配色,就会采用上面设置主题色功能来设置主题色

1
2
3
4
5
6
/** 
 * 重新加载主题图片
 * themeColor : 主题色
 * block : 设置主题图片时调用的block
 */
- (void)py_reloadThemeImageWithThemeColor:(UIColor *)themeColor setting:(PYThemeImageSettingBlock)block;

实现如下:

1
2
3
4
5
6
7
8
9
10
11
/** 重新加载主题图片 */
- (void)py_reloadThemeImageWithThemeColor:(UIColor *)themeColor setting:(PYThemeImageSettingBlock)block
{
    if (themeColor) { // 有主题色,设置主题色
        [self py_setThemeColor:themeColor];
    }
     
    if (block) { // 存在block,直接调用
        block([self themeImagePool]);
    }
}

使用

假设现在有这么一个需求:更换主题图片时,更换UITabBarItem的图片

1.将UITabBarItem添加到图片池

1
2
// UITabBarItem
[childController.tabBarItem py_addToThemeImagePool];

2.切换主题图片并设置配色为红色

1
2
3
4
// 重新加载主题图片,并设置主题色为红色
[self py_reloadThemeImageWithThemeColor:[UIColor redColor] setting:^(const NSArray *objects) {    
    // 根据控件类型完成相关设置
}

总结

篇幅可能有点大,能耐心读到这里的读者相信会有不少收获的,希望读者在阅读此教程的时候,千万不要学习代码实现,而是要多思考:为什么要这样实现?那样实现有什么不好?多学学接口为什么要这样设计,那样设计是不是更合理?当你带着这些问题再回过头来去看看源码时,希望你会有更多的收货!当然,这里只是提供了一种思路,你也可以在此基础上实现夜间模式的切换等。期待你们的实现!

期望

当然如果您有更多的想法想表达或者交流的话,欢迎到留言/评论!因为本人比较喜欢活跃在GitHub社区,所以,如果您有什么想反馈的也可以issuse me,在这也鼓励大家去多多发现优秀源码,并且共享给大家。毕竟分享是双方获利的,何乐而不为?

源码地址:https://github.com/iphone5solo/PYTheme

iOS 实现快速切换主题详细教程(附上源码)的更多相关文章

  1. PHP PC端微信扫码支付【模式二】详细教程-附带源码(转)

    博主写这破玩意儿的时候花了大概快两天时间才整体的弄懂逻辑,考虑了一下~还是把所有代码都放出来给大家~抱着开源大无私的精神!谁叫我擅长拍黄片呢?同时也感谢我刚入行时候那些无私帮过我的程序员们! 首先还是 ...

  2. easyUI整合富文本编辑器KindEditor详细教程(附源码)

    原因 在今年4月份的时候写过一篇关于easyui整合UEditor的文章Spring+SpringMVC+MyBatis+easyUI整合优化篇(六)easyUI与富文本编辑器UEditor整合,从那 ...

  3. 史上最详细的IDEA优雅整合Maven+SSM框架(详细思路+附带源码)

    目录 前言: 1. 搭建整合环境 2.Spring框架代码的编写 3.SpringMVC框架代码的编写 4. Spring整合SpringMVC的框架 5.MyBatis框架代码的编写 6. Spri ...

  4. LIRE教程之源码分析 | LIRE Tutorial of Analysis of the Source Code

    LIRE教程之源码分析 |LIRE Tutorial of Analysis of the Source Code 最近在做地理图像识别和检索的研究,发现了一个很好用的框架LIRE,遂研究了一通.网上 ...

  5. 使用 IDEA 创建 SpringBoot 项目(详细介绍)+ 源码案例实现

    使用 IDEA 创建 SpringBoot 项目 一.SpringBoot 案例实现源码 二.SpringBoot 相关配置 1. 快速创建 SpringBoot 项目 1.1 新建项目 1.2 填写 ...

  6. PHP简单的长文章分页教程 附源码

    PHP简单的长文章分页教程 附源码.本文将content.txt里的内容分割成3页,这样浏览起来用户体验很好. 根据分页参数ipage,获取对应文章内容 include('page.class.php ...

  7. GEF入门实例_总结_01_教程、源码、开发环境准备

    一.前言 最近在学Eclipse插件开发,发现了一个比较好的GEF入门教程,并且按照教程上的操作,一步步实现了一个入门Demo,在此感谢作者的贡献. 好记性不如烂笔头,故决定总结一下这段时间的学习心得 ...

  8. SpringBoot 项目搭建(详细介绍+案例源码)

    SpringBoot 项目搭建 SpringBoot 项目整合源码 SpringBoot 项目整合 一.项目准备 1.1 快速创建 SpringBoot 项目 1.2 标准项目结构图如下 1.3 添加 ...

  9. vim一个快速切换主题的插件(change-colorscheme,原创)

    概述 有时候我们想快速浏览主题并找到一款合适的主题,change-colorscheme将会满足我们的要求. 安装 git https://github.com/chxuan/change-color ...

随机推荐

  1. Troubleshoot Refused VNC Connection in CentOS 7

    Troubleshoot Refused VNC Connection in CentOS 7 Posted on March 15, 2015 by Istvan Szarka — 2 Commen ...

  2. Orchard Platform v1.8 发布

    发布说明: 1. 添加Json格式数据文件支持.2. 彻底删除了Settings, Modules, Themes模块.3. 删除了默认的ContentType,Site和User.4. 支持空库(无 ...

  3. 换个角度说工作单元(Unit Of Work):创建、持有与API调用

    看到一些工作单元的介绍,有两种感觉,第一种是很学院,说了等于没说,我估计很多都是没有自己引入到实际的项目中去,第二种是告诉我一种结果,说这就是工作单元,但是没说为什么要这么使用.所以,本篇想要探讨的是 ...

  4. MMM互助金融/理财源码

    1.1.1MMM互助金融配比系统源码销售 (3mphp.com/mmm-office.com) 联系QQ: 3375551869,全套源码,包含: 1 源码:安装.开发文档 2 数据库:含演示数据,自 ...

  5. [WinAPI] API 10 [创建、打开、读写文件,获取文件大小]

    在Windows系统中,创建和打开文件都是使用API函数CreateFile,CreateFile通过指定不同的参数来表示是新建一个文件,打开已经存在的文件,还是重新建立文件等.读写文件最为直接的方式 ...

  6. We are doomed, and RPC does not help

    第一种死法:Big ball of Mud 架构里最常用的反面案例是 big ball of mud.很大程度上可以说打格子,把复杂的系统拆解成小格子是架构师最重要的工作.这个小格子有很多种名字,比如 ...

  7. XML相关知识全接触(一)

    XML文件格式已经出来很久了.他的风头如今在JSON.YAML等新兴文件格式的冲击下已经显的不那么强劲.但是XML仍然是当今世界上使用最广泛的文件格式.围绕着它也有一大堆的概念和知识点.所以我们还是很 ...

  8. 如何试用Office 365 及 SharePoint Online(美版)

    Office 365已经在国外运营一段时间了,本文主要帮助大家注册一个试用账户.废话少说按步骤来: 进入注册页面,链接地址 . 1. 对于不同的企业,提供了不同的套餐,这里我们试用这个中等企业的套餐, ...

  9. java继承与多态-3个小题

    1.(1)编写一个接口ShapePara,要求: 接口中的方法: int getArea():获得图形的面积.int getCircumference():获得图形的周长 (2)编写一个圆类Circl ...

  10. 移动页面HTML5自适应手机屏幕宽度

    标签: 网上关于这方面的文章有很多,重复的东西本文不再赘述,仅提供思路,并解释一些其他文章讲述模糊的地方. 1.使用meta标签,这也是普遍使用的方法,理论上讲使用这个标签是可以适应所有尺寸的屏幕的, ...