在Xcode中使用Asset Catalogs大致效果如下图
代码调用
UIColor(named: "greenx")
UIImage(named: "Image")
与我们平时调用的代码没有什么区别。
这样调用代码里面会出现很对的硬编码,会导致代码不易维护,资源管理也极其不方便。
为了减少硬编码的对代码管理带来的不便可以统一管理资源的硬编码,创建管理类来维护这些资源。
颜色管理类
import UIKit
extension UIColor {
//start sync tag
// R: 0.717, G: 0.200 , B: 0.300, A: 1.000
// R: 182 , G: 51 , B: 76 , A: 255
// R: B6 , G: 33 , B: 4C , A: FF
public static var asdw: UIColor? {
_ = #colorLiteral(red: 0.717, green: 0.200, blue: 0.300, alpha: 1.000)
return UIColor(named: "asdw")
}
........
//end sync tag
}
代码效果
图片管理类
import UIKit
extension UIImage {
//start sync tag
public static var folder: UIImage? {
_ = #imageLiteral(resourceName: "folder")
return UIImage(named: "folder")
}
//end sync tag
}
图片效果
资源的使用就不会出现硬编码了
let color = UIColor.asdw
let image = UIImage.folder
但是还是需要我们手动去维护这些个管理类,也是一件比较麻烦的事。
我们可以使用Xcode的Run Script来帮助我门来完成这个管理类的维护工作,这样就能帮助我们省去很多的工作量
我们可以使用脚本来帮助我们完成管理类与Assets Catalogs的同步工作,每次编译时都会进行一次同步(Run Script的位置决定了脚本的执行顺序,建议放在Compile Sources的上面),同步后在进行代码的编译。
这样就可以直接知道资源的使用错误。
下面直接上脚本,这里我用的是Python脚本
颜色同步脚本
import sys
import os
import json
def sync_color_file():
temp_file = None
if os.path.exists(color_file_path):
os.rename(color_file_name, temp_file_name)
temp_file = open(temp_file_path, 'r')
color_file = open(color_file_path, "w+")
while True:
line = temp_file.readline()
color_file.write(line)
if start_tag in line:
break
else:
color_file = open(color_file_path, "w+")
color_file.write('//\
\n// {}\
\n// {}\
\n//\
\n// Created by XXXX on 9999/99/99.\
\n//\
\n\
\nimport UIKit\
\n\
\nextension UIColor {{\
\n{}\n'.format(color_file_name, project_name, start_tag))
color_file.write(analysis_assets())
start_write_tail = False
if temp_file:
while True:
line = temp_file.readline()
if end_tag in line:
start_write_tail = True
if start_write_tail:
color_file.write(line)
if line == '':
break
if not start_write_tail:
color_file.write('\n{}\n}}'.format(end_tag))
temp_file.close()
os.remove(temp_file_path)
else:
color_file.write('\n{}\n}}'.format(end_tag))
color_file.close()
def analysis_assets():
sync_result_str = ''
for folder, subFolders, files in os.walk("Assets.xcassets"):
color_name = os.path.basename(folder)
color_suffix = '.colorset'
if not color_name.endswith(color_suffix):
continue
param_name = color_name.replace(color_suffix, '')
if param_name in system_color_names:
print('!!!!!color name is same as system color name <<<{}>>>!!!!! '.format(param_name))
continue
if 'Contents.json' not in files:
continue
with open(os.path.join(folder, 'Contents.json'), 'r') as load_f:
load_dict = json.load(load_f)
for color_item in load_dict['colors']:
# appearances = color_item.get('appearances', None)
# idiom = color_item.get('idiom', None)
color = color_item.get('color', None)
if not color:
continue
color_components = color['components']
sync_result_str += '\
\n\t// R: {R}, G: {G} , B: {B}, A: {A}\
\n\t// R: {R8}, G: {G8} , B: {B8}, A: {A8}\
\n\t// R: {RH}, G: {GH} , B: {BH}, A: {AH}\
\n\tpublic static var {name}: UIColor? {{\
\n\t\t_ = #colorLiteral(red: {R}, green: {G}, blue: {B}, alpha: {A})\
\n\t\treturn UIColor(named: "{name}")\
\n\t}}\n'.format(name=param_name,
R=color_to_float(color_components['red']),
G=color_to_float(color_components['green']),
B=color_to_float(color_components['blue']),
A=color_to_float(color_components['alpha']),
R8=color_to_8_bit(color_components['red']),
G8=color_to_8_bit(color_components['green']),
B8=color_to_8_bit(color_components['blue']),
A8=color_to_8_bit(color_components['alpha']),
RH=color_to_bit_h(color_components['red']),
GH=color_to_bit_h(color_components['green']),
BH=color_to_bit_h(color_components['blue']),
AH=color_to_bit_h(color_components['alpha']))
break
return sync_result_str
def color_to_float(value):
if '.' in value:
return value
elif 'X' in value or 'x' in value:
return '{:0.3f}'.format(float(int(value[-2:], 16)) / 255)
else:
return '{:0.3f}'.format(float(value) / 255)
def color_to_8_bit(value):
if '.' in value:
return '{: <5}'.format(int(float(value) * 255))
elif 'X' in value or 'x' in value:
return '{: <5}'.format(int(value, 16))
else:
return '{: <5}'.format(value)
def color_to_bit_h(value):
if '.' in value:
return '{: <5X}'.format(int(float(value) * 255))
elif 'X' in value or 'x' in value:
return '{: <5}'.format(value[-2:])
else:
return '{: <5X}'.format(int(value))
if __name__ == '__main__':
project_path = sys.argv[1]
project_name = sys.argv[2]
os.chdir(project_path)
system_color_names = ['black', 'darkGray', 'lightGray', 'white', 'gray', 'red', 'green', 'blue', 'cyan', 'yellow', 'magenta', 'orange', 'purple', 'brown', 'clear']
color_file_name = 'Colors.swift'
color_file_path = os.path.join(project_path, color_file_name)
temp_file_name = 'Colors_temp.swift'
temp_file_path = os.path.join(project_path, temp_file_name)
start_tag = '//start sync tag'
end_tag = '//end sync tag'
sync_color_file()
exit(0)
图片同步脚本
import sys
import os
import json
project_path = sys.argv[1]
project_name = sys.argv[2]
os.chdir(project_path)
image_file_name = 'Images.swift'
image_file_path = os.path.join(project_path, image_file_name)
temp_file_name = 'Images_temp.swift'
temp_file_path = os.path.join(project_path, temp_file_name)
start_tag = '//start sync tag'
end_tag = '//end sync tag'
temp_file = None
if os.path.exists(image_file_path):
os.rename(image_file_name, temp_file_name)
temp_file = open(temp_file_path, 'r')
image_file = open(image_file_path, "w+")
while True:
line = temp_file.readline()
image_file.write(line)
if start_tag in line:
break
else:
image_file = open(image_file_path, "w+")
image_file.write('//\
\n// {}\
\n// {}\
\n//\
\n// Created by XXXX on 9999/99/99.\
\n//\
\n\
\nimport UIKit\
\n\
\nextension UIImage {{\
\n{}\n'.format(image_file_name, project_name, start_tag))
print('current project path: {}'.format(os.getcwd()))
for folder, subFolders, files in os.walk("Assets.xcassets"):
image_name = os.path.basename(folder)
image_suffix = '.imageset'
if not image_name.endswith(image_suffix):
continue
param_name = image_name.replace(image_suffix, '')
image_file.write('\n\tpublic static var {0}: UIImage? {{\
\n\t\t_ = #imageLiteral(resourceName: "{0}")\
\n\t\treturn UIImage(named: "{0}")\
\n\t}}\n'.format(param_name))
start_write_tail = False
if temp_file:
while True:
line = temp_file.readline()
if end_tag in line:
start_write_tail = True
if start_write_tail:
image_file.write(line)
if line == '':
break
if not start_write_tail:
image_file.write('\n{}\n}}'.format(end_tag))
temp_file.close()
os.remove(temp_file_path)
else:
image_file.write('\n{}\n}}'.format(end_tag))
image_file.close()
exit(0)
将脚本放到py为后缀的文件中就可以了(示例中用的是color.py和image.py),
然后在Xcode的Run Script里面写入
python3 path/for/script/image.py ${SRCROOT}/${TARGET_NAME} ${PROJECT_NAME}
python3 path/for/script/image.py ${SRCROOT}/${TARGET_NAME} ${PROJECT_NAME}
直接在shell脚本中调用python脚本,将脚本路径换成自己的存放路径,建议在放在项目根目录下 color.py,这样所有人都可以使用。
这样,每次在编译之前都会进行一次同步,不需要手动去维护这些硬编码了,在编码时也会有相应的提示。
资源的管理也会变得简单,删除后管理类也会同时删除,还能有编译错误来提示删除的资源对代码的影响
可以使用 literal 对资源的代码可视化,在代码中就可以看到颜色效果,和图片的缩略图,如上面的截图,color_code.png和image_code.png