iOS开发笔记(七)

2018-04-27 18:07:50 浏览数 (1)

正文

这次分享三个有意思的问题:二维码生成、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的模式,内存中的布局是0xBBGGRRAA
  • kCGImageAlphaPremultipliedLast 是先把alpha值乘到RGB值,再放到对应的内存;
代码语言:javascript复制
- (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 PhasesCompile 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是动态库,被Xcodeembedded到项目中。这个版本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方法找到字体名。

字体文件

总结

学而不思则罔,思而不学则殆。 遇到问题,尝试去解决它,并从中得到知识; 思考问题的前因后果,避免再次遇到同样的问题。

0 人点赞