zoukankan      html  css  js  c++  java
  • iOS开发——高级篇——换肤、静态库

    一、换肤


    1、思路
    1> 解决方案1,使用颜色作为图片素材的命名关键字
    问题1:要保证每套图片的文件名 颜色+ 名称.png的格式比较麻烦
    问题2:如果要将某一个图片应用到其他皮肤不方便
    2> 解决方案2,利用Bundle,将图片文件保存在不同的Bundle中
    问题:平面设计师维护不方便
    3> 解决方案3,利用文件夹(蓝色),将图片文件保存在不同文件夹中
    好处:便于平面设计师针对不同的文件夹维护图片素材
    扩展,将用户上次使用的皮肤保存在用户偏好中

    2、可能遇到的问题
    问题一:默认进来是没有皮肤颜色
    解决方案:手动设置

    // 判断skinColor是否为空,如果为空,表示用户之前没有选中皮肤,那么显示蓝色皮肤
        if (_skinColor == nil) {
            _skinColor = @"blue";
        }
    }


    问题二:当用户选中某一个皮肤,下次打开程序,应该依然显示之前选中的皮肤
    解决方案:将用户上次使用的皮肤保存在用户偏好中

    /**
     *  设置当前选中的皮肤
     */
    + (void)setSkinColor:(NSString *)skinColor
    {
        _skinColor = skinColor;
        
        // 记录用户选中的皮肤
        [[NSUserDefaults standardUserDefaults] setObject:skinColor forKey:skinColor];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }
         // 先从偏好设置中取出用户之前选中的皮肤 再判断skinColor是否为空
    _skinColor = [[NSUserDefaults standardUserDefaults] objectForKey:skinColor];

    问题三:美工给我们的图片,可能不是像这种:red_face,颜色不同的相同图片命名完全一样
    解决方案:拷贝图片时用真实的文件夹(蓝色的),每一种颜色的文件夹名用颜色名来命名

     /**
     *  根据用户选择皮肤后设置对应的背景图片
     */
    + (UIImage *)skinToolWithImageName:(NSString *)imageName
    {
        NSString *imageNamePath = [NSString stringWithFormat:@"skin/%@/%@", _skinColor, imageName];
        
        return [UIImage imageNamed:imageNamePath];
    }

    问题四:多控制器换肤功能(切换皮肤之后,多个控制器都实现换肤)
    解决方案:在viewWillAppear:方法中写换肤相关的代码

    3、工具类(具体代码)

    .h

    #import <UIKit/UIKit.h>
    
    @interface SkinTool : NSObject
    
    /**
     *  设置当前选中的皮肤
     */
    + (void)setSkinColor:(NSString *)skinColor;
    
    /**
     *  根据用户选择皮肤后设置对应的背景图片
     */
    + (UIImage *)skinToolWithImageName:(NSString *)imageName;
    
    /**
     *  根据用户选择皮肤后设置label的背景颜色
     */
    + (UIColor *)skinToolWithLabelBgColor;
    @end

    .m

    #import "SkinTool.h"
    
    NSString *const skinColorKey = @"skinColor";
    
    @implementation SkinTool
    
    static NSString *_skinColor;
    
    /**
     *  当类第一次使用的时候会调用该方法,该方法只会调用一次
     */
    + (void)initialize
    {
        // 1.先从偏好设置中取出用户之前选中的皮肤
        _skinColor = [[NSUserDefaults standardUserDefaults] objectForKey:skinColorKey];
        
        // 2.判断skinColor是否为空,如果为空,表示用户之前没有选中皮肤,那么显示蓝色皮肤
        if (_skinColor == nil) {
            _skinColor = @"blue";
        }
    }
    
    /**
     *  设置当前选中的皮肤
     */
    + (void)setSkinColor:(NSString *)skinColor
    {
        _skinColor = skinColor;
        
        // 记录用户选中的皮肤
        [[NSUserDefaults standardUserDefaults] setObject:skinColor forKey:skinColorKey];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }
    
    /**
     *  根据用户选择皮肤后设置对应的背景图片
     */
    + (UIImage *)skinToolWithImageName:(NSString *)imageName
    {
        NSString *imageNamePath = [NSString stringWithFormat:@"skin/%@/%@", _skinColor, imageName];
        
        return [UIImage imageNamed:imageNamePath];
    }
    
    /**
     *  根据用户选择皮肤后设置label的背景颜色
     */
    + (UIColor *)skinToolWithLabelBgColor
    {
        // 1.找到对应背景的plist文件
        NSString *bgColorFileName = [NSString stringWithFormat:@"skin/%@/BgColor.plist", _skinColor];
        NSString *bgColorFilePath = [[NSBundle mainBundle] pathForResource:bgColorFileName ofType:nil];
        
        // 2.加载背景颜色
        // 2.1.加载背景字典
        NSDictionary *bgColorDict = [NSDictionary dictionaryWithContentsOfFile:bgColorFilePath];
        
        // 2.2.取出背景颜色的字符串
        NSString *bgColorString = bgColorDict[@"LabelBgColor"];
        
        // 2.3.取出背景颜色的数组(0,255,0)
        NSArray *bgColorArray = [bgColorString componentsSeparatedByString:@","];
        
        // 2.4.取出对应的RGB值
        NSInteger red = [bgColorArray[0] integerValue];
        NSInteger green = [bgColorArray[1] integerValue];
        NSInteger blue = [bgColorArray[2] integerValue];
        
        return [UIColor colorWithRed:red / 255.0 green:green / 255.0 blue:blue / 255.0 alpha:1.0];
    }
    
    @end

    4、具体使用
    比如说设置橘色的

    - (IBAction)switchOrangeSkin {
        // 1.告诉工具类当前皮肤的颜色
        [SkinTool setSkinColor:@"orange"];
        
        // 2.切换对应皮肤的图片
        [self switchSkinImages];
    }
    
    - (void)switchSkinImages
    {
        self.faceImageView.image = [SkinTool skinToolWithImageName:@"face"];
        self.heartImageView.image = [SkinTool skinToolWithImageName:@"heart"];
        self.rectImageView.image = [SkinTool skinToolWithImageName:@"rect"];
        
        // 切换Label的背景
        self.testLabel.backgroundColor = [SkinTool skinToolWithLabelBgColor];
    }

    那么在其他控制器中只要在viewWillAppear方法中如下使用,注意凡是涉及到了换肤的代码都写这里

    - (void)viewWillAppear:(BOOL)animated
    {
        [super viewWillAppear:animated];
        
        // 注意:换肤的内容(背景图片/背景颜色/字体的大小),写在viewWillAppear方法
        self.faceImageView.image = [SkinTool skinToolWithImageName:@"face"];
        self.heartImageView.image = [SkinTool skinToolWithImageName:@"heart"];
        
        self.label.backgroundColor = [SkinTool skinToolWithLabelBgColor];
    }

    二、静态库


    1、简介
    什么是库?
    库是程序代码的集合,是共享程序代码的一种方式

    根据源代码的公开情况,库可以分为2种类型
    开源库
    公开源代码,能看到具体实现
    比如SDWebImage、AFNetworking

    闭源库
    不公开源代码,是经过编译后的二进制文件,看不到具体实现
    主要分为:静态库、动态库

    2、静态库和动态库
    静态库和动态库的存在形式
    静态库:.a 和 .framework
    动态库:.dylib 和 .framework

    静态库和动态库在使用上的区别
    静态库:链接时,静态库会被完整地复制到可执行文件中,被多次使用就有多份冗余拷贝(PPT左图所示)
    动态库:链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存(PPT右图所示)

    需要注意的是:
    项目中如果使用了自制的动态库,不能被上传到AppStore


    三、还是静态库


    01. 为什么要做静态库
    1> 国内的企业,掌握有核心技术,同时是又希望更多的程序员来使用其技术,因此采用"闭源"的方式开发使用
    例如:百度地图,友盟,JPush等

    2> 在企业开发中,一些核心技术或者常用框架,出于安全性和稳定性的考虑,也会提供静态库给程序员使用

    02. 静态库的特点
    .a + .h

    03. 静态库简单演练
    1> 新建Tools项目
    2> 创建一个类方法,实现一个简单的加法
    3> 编译
    # 注意,在编译静态库时,需要编译两个版本 -> 真机(arm) | 模拟器(i386)
    4> 新建项目将编译生成的.a + .h拖到项目中使用

    04. 静态库中的资源包的使用
    问题:有些第三方库会使用到一些图片素材,例如公司的logo等。

    但是由于Xcode默认在编译时会把所有的素材文件导入到mainBundle中,为了避免与使用静态库的程序冲突。
    在静态库中如果要使用图片素材,会利用bundle的手段

    1> 建立bundle,并且向其中添加图片
    2> 创建一个类方法,返回图片
    3> 编译
    4> 调用方如果需要使用,需要导入 .h + .a + XXX.bundle

    05. 静态库中的自定义视图
    问题:程序需要测试
    静态库如何测试呢?
    创建复合项目
    1> 复习块代码传值

    06. 静态库的使用
    新建项目将编译生成的.a + .h拖到项目中使用

    07. 静态库的种类
    静态库文件的版本(4种)
    1.真机-Debug版本
    2.真机-Release版本
    3.模拟器-Debug版本
    4.模拟器-Release版本

    调试版本(Debug版本) VS 发布版本(Release版本)
    - 调试版本会包含完整的符号信息,以方便调试
    - 调试版本不会对代码进行优化

    - 发布版本不会包含完整的符号信息
    - 发布版本的执行代码是进行过优化的
    - 发布版本的大小会比调试版本的略小
    - 在执行速度方面,发布版本会更快些,但不意味着会有显著的提升

    08. 静态库的合并
    # 检测.a的类型
    $ lipo -info libCZTools.a

    # 合并.a
    lipo -create Debug-iphoneos/libTools.a Debug-iphonesimulator/libTools.a -output libTools.a

    # 合并.a的好处,开发过程中既可以在真机上调试,也可以在模拟器上调试
    # 合并.a的坏处,如果静态库太大,合并打包后,会非常大,因此很多第三方的静态库的.a是区分版本的
    # 今后在使用.a时一定注意版本


    四、CPU架构


    1、常见报错
    如果发现使用静态库是报一下错误:
    Undefined symbols for architecture i386: OBJC_CLASS_$_MathTool
    说明你的静态库不支持 i386 这种CPU架构

    2、CPU架构
    每一个手机都有属于自己的CPU,而每一个CPU都有属于自己的CPU架构(指令集)

    每一个静态库都有自己可以支持的架构

    手机和模拟器使用的CPU架构
    模拟器CPU架构
    iPhone4s-->iphone5 : i386
    iphone5s-->iphone6s plus : x86_64

    真机CPU架构
    iPhone3gs-->iphone4s : armv7
    iphone5-->iphone5c : armv7s(如果一个静态库支持armv7架构,那么也可以跑在架构是armv7s的设备,没有对代码进行优化)
    iPhone5s-->iPhone6s plus : arm64

    查看静态库支持的架构:lipo -info 静态库名称

    合并静态库:
    lipo -create 静态库1 静态库2 -output 新的静态库

    五、创建和使用静态库时的注意点


    1.制作.a的静态库(Framework&Library) 需包含头⽂件(项目—>build Phase—>copy File—>选中头⽂文件)

    2.边开发边制作静态库

    添加一个静态库的targets—>点击项目—>targets—>+

    测试静态库: 项目—>项目的target—>general—>linked framework—>添加静 态库

    打包静态库—>选中项目静态库—>编译(头⽂件)

    3.使⽤静态库的注意点:

    静态库有真机和模拟器(测试阶段可以使⽤模拟器,发布阶段使⽤真机).好处:资源包变⼩

    静态库有Debug版和release版本(测试阶段使用Debug,发布阶段使用release).好处:资源包变小/运行速度变快/代码进⾏优化

    默认情况下资源打包上传到app 默认就是release版本的

    编译出来的静态库,所有设备CPU的架构都能支持(选择NO,代表编译出来的静态库不仅仅只支持当前选中的设备CPU的构架):

    4.制作.framework库

    1>动态库(默认) 注意:如果项目中⽤到了动态库要先进⾏设置:项目—>项目的target—> general— >Embedded—>添加动态库

    2>静态库 制作静态库的修改配置:项目—>静态库的target—>build setting—>搜索mach —>static library

    3>注意点 查看⼀个.framework静态库⽀持哪些架构:lipo -info framework⽂件下面的二 进制 合并静态库:合并framework⽂件下面的二进制

    将来的你会感谢今天如此努力的你! 版权声明:本文为博主原创文章,未经博主允许不得转载。
  • 相关阅读:
    POJ 1018 Communication System
    POJ 1017 Packets
    Codeforces 725B Food on the Plane
    Lessons learned from manually classifying CIFAR-10
    CCF推荐国际学术期刊
    局部数组过大导致编译栈区溢出问题
    自己动手写处理器之第一阶段(3)——MIPS32指令集架构简单介绍
    C++学习笔记29,引用变量(1)
    Android视频应用去广告学习实践
    sublime编辑器怎样高速输入PHP头部版本号声明
  • 原文地址:https://www.cnblogs.com/chglog/p/4844804.html
Copyright © 2011-2022 走看看