正文
这次分享三个有意思的问题:二维码生成、Xcode8单元测试的问题、添加新字体。
二维码生成
iOS平台上的二维码生成有很多第三方库,也可以使用原生的方法,这里选用的是QREncoder。 二维码的生成非常简单,仅需几行代码,如下:
代码语言:javascript复制 DataMatrix* qrMatrix = [QREncoder encodeWithECLevel:QR_ECLEVEL_H version:QR_VERSION_AUTO string:urlStr];
int qrcodeImageDimension = 256;
UIImage* qrcodeImage = [QREncoder renderDataMatrix:qrMatrix imageDimension:qrcodeImageDimension];
但是,在开发的时候,还是会遇到很多的问题:
1、编译失败的问题
编译的时候,会报无法识别class CQR_Encode{...}
的错误。
从编译的错误来看,是编译器无法识别C 类CQR_Encode
,通过头文件的索引定位到问题:
QREncoder 的头文件用到了QR_LEVEL_H 这些属性需要引入QR_Encode.h,QR_Encode.h里面是c 的类;
解决方案有两个,一个是把代码的文件名后缀改成.mm,第二个是修改文件的Type类型为Objective-C
,如下:
这里如果把
QR_ECLEVEL_H
这几个define改成常量,用extern声明,也是一种解决方案;
2、图片的颜色值异常
QREncoder生成的图片是黑白的,我需要把图片改成下面这样:
设计的同学给出的颜色值是0xFFF04F43,格式是ARGB。 我尝试的解决方案是:遍历像素,把黑色的颜色值改成红色。 在看代码前,介绍几个基本知识点:
kCGBitmapByteOrderDefault
是小端模式; RGBA的模式,内存中的布局是0xAABBGGRR ARGB的模式,内存中的布局是0xBBGGRRAAkCGImageAlphaPremultipliedLast
是先把alpha值乘到RGB值,再放到对应的内存;
- (UIImage *)lyRenderBlackToRedColor
{
// 分配内存
const int imageWidth = self.size.width;
const int imageHeight = self.size.height;
size_t bytesPerRow = imageWidth * 4;
uint32_t* rgbImageBuf = (uint32_t*)malloc(bytesPerRow * imageHeight);
// 创建context
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(rgbImageBuf, imageWidth, imageHeight, 8, bytesPerRow, colorSpace,
kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast);
CGContextDrawImage(context, CGRectMake(0, 0, imageWidth, imageHeight), self.CGImage);
// 遍历像素
int pixelNum = imageWidth * imageHeight;
uint32_t* pCurPtr = rgbImageBuf;
for (int i = 0; i < pixelNum; i , pCurPtr )
{
// RGBA的模式,内存中的布局是0xAABBGGRR
// ARGB的模式,内存中的布局是0xBBGGRRAA
if ((*pCurPtr & 0x00FFFFFF) == 0x00000000 && (*pCurPtr & 0xFF000000) == 0xFF000000) // 将黑色变成红色
{
// *pCurPtr = 0xFFF04F43;
uint8_t* ptr = (uint8_t*)pCurPtr;
ptr[3] = 0xff;
ptr[2] = 0x43;
ptr[1] = 0x4f;
ptr[0] = 0xf0;
}
}
// 将内存转成image
CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, rgbImageBuf, bytesPerRow * imageHeight, lyProviderReleaseData);
CGImageRef imageRef = CGImageCreate(imageWidth, imageHeight, 8, 32, bytesPerRow, colorSpace,
kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast, dataProvider,
NULL, true, kCGRenderingIntentDefault);
CGDataProviderRelease(dataProvider);
UIImage* resultUIImage = [UIImage imageWithCGImage:imageRef];
// 释放
CGImageRelease(imageRef);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
return resultUIImage;
}
void lyProviderReleaseData (void *info, const void *data, size_t size)
{
free((void*)data);
}
问题1:CGBitmapContextCreate 创建失败,bitmap的data信息,我发现在上面的代码中仅支持AlphaPremultiplied的类型; 问题2:QREncoder生成的二维码,会有0x00101010 (ARGB)这样的数据,因为alpha=0,0x101010的颜色值会被忽略; 同样的,有部分透明的边会出现0x00ffffff,如果仅仅以RGB的值=0xffffff进行判断,会导致额外的黑边;
Xcode8单元测试的相关问题
1、单元测试的断点失效
stackoverflow上有关于breakpoints in unit test not getting hit
的解决方案,但没有解决我的问题,原因可能并不一样。
先尝试非单元测试的raget,可以正常断点;
再尝试其他工程的单元测试,也可以正常断点;
猜测是,单元测试的工程设置存在问题;
仔细查找build setting
,找到出问题的设置项:
新建Unit Test的时候,这里的属性值变成了DWARF,没有附带dSYM的符号文件,所以无法断点。
还有其他可能,比如test.m的文件没有加入工程中;(在
Build Phases
的Compile Soucres
可以看到是否添加,也可以通过打Log看是否执行)
2、头文件查找失败
通常是在单元测试中调用了某些第三方库的代码,报.h file not found
的错误;
如果单元测试引入文件A,文件A引入了第三方库C,那么需要在build setting
添加对应的search framework
如果基础工程的项比较多,可以在选中之后(如上图)按
cmd c
再到新的target的cmd v
3、embedded错误
错误描述是:ld: embedded dylibs/frameworks are only supported on iOS 8.0 and later (@rpath/XCTest.framework/XCTest) for architecture x86_64
以前加载动态库的时候,记得iOS8.0以上的版本才支持embedded frameworks
,新的工程的设置deployment target是7.0。
检查Xcode工程,发现embedded frameworks这一项为空!
Xcode的版本是** Version 8.3.1 (8E1000a)** 猜测是Xcode生成的XCTest Framework是动态库,被Xcode
embedded
到项目中。这个版本Xcode能选择最低iOS版本是8.0,所以Xcode可能没有考虑XCTest.framework
兼容iOS8以下的情况。 解决方案就是把deployment target暂时设置成8.0。
4、Undefined symbols for architecture x86_64
注意这里的描述是Undefined symbols
,表示编译器找到变量的声明,但是没有找到变量的实现,于是在linking的阶段就会报错;
还有一种情况是Use of undeclared identifier
,这种是找不到变量的声明,一般是没有引入头文件,或者没有正确设置头文件的search路径。
i386是32位模拟器(5和5s以下的机型) x86_64是64位模拟器(5s后面的机型) armv7/armv7s是32位真机 arm64是64位真机 coocachina上有更详细的介绍。
添加新字体
- 1、把.ttf字体文件导入Xcode工程;
- 2、在plist添加对应的字体文件,如下:
plist
- 3、在代码中通过
[UIFont fontWithName:@“newFontName” size:18]
加载新字体; newFontName 是字体的名字,通过在Finder双击字体文件,红色圈起来的部分就是字体的family Name,再通过UIFont的-fontNamesForFamilyName
方法找到字体名。
字体文件
总结
学而不思则罔,思而不学则殆。 遇到问题,尝试去解决它,并从中得到知识; 思考问题的前因后果,避免再次遇到同样的问题。