模仿Sip Color App之自定义相机界面
首先,上篇自定义颜色点视图处可以改进一下,就是像Sip Color App一样在拖动某个颜色点的时候,把它放在视图的最上层,这样体验会好点。修改也很简单,只需要在- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event [self.view bringSubviewToFront:_currentDragDotView];
方法中拿到当前DotView之后,通过````把它放在视图的最上层即可。
这次讲一下自定义相机界面,这块功能也是Sip Color的主要核心功能之一。我猜测Sip Color App在通过摄像头捕获颜色的时候实际上也是通过上一篇讲到的拾取图片上的颜色的。Sip Color应该是在每次手机发生了状态改变时就进行拍照,然后拾取照片上的颜色值。
如果我们项目中需要简单的自定义相机界面,比如拍照时显示一个圆形的框,拍照完成截取圆形框内的部分。这种需求,我们只需要可以给UIImagePickerController的cameraOverlayView赋上一个自定义view即可达到效果。为了使自定义overlayView和系统原本的overlayView一样大,你可以这样写:
YLCameraOverlayerView *overLayer = [[YLCameraOverlayerView alloc] init];
overLayer.frame = imagePicker.cameraOverlayView.frame;
imagePicker.cameraOverlayView = overLayer;
我们也可以通过设置imagePicker.showsCameraControls = NO;
来隐藏系统控件,然后,我们在自定义overlayView上面添加我们想要的各种控件。这种方式实现起来比较快捷,而且基本上保持了和系统相机一致的功能。
但是在实际使用的过程,我发现,如果要几乎全屏拍照效果,而不是像系统相机那样底部固定显示很高一个黑色视图。这个时候两个手指缩放屏幕的时候,屏幕底部类似一个UISlider的控件显示出来了,怎么都移除不掉。如图:
注意看底部如果是刚好我们需要的拍照界面底部和系统相机一样有固定显示的不透明控件,我们可以选择使用这种简单的方式来实现我们想要的效果。但是如果是要全屏拍照的效果,就需要使用AVFoundation框架的AVCaptureSession、AVCaptureDeviceInput和AVCaptureStillImageOutput结合起来来实现自定义相机界面了,这种方式灵活性更高,可以实现高度的自定义。具体实现网上有很多例子了,这里就不贴具体实现了,大家可以自行搜索相关实现代码、关注微信关注号“猿故”索取或者等下篇文章结束后查阅公布的完整代码(代码会在下篇文章结束后整理一下发布在这里YLSipColor)。
这里特别需要注意的一点是,使用AVCaptureStillImageOutput的如下方法获取静态图片时会有快门声音,无法关闭。网上有大神获取苹果系统的快门音频文件,然后自己合成一个对应的降噪音频,在每次拍照时自行播放这个降噪音频,这样子就把系统快门声给抵消掉,给人的感觉就是听不到快门声了,但是听说在某些情形下还是会有快门声,比如在iPad上。
[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:stillImageConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
NSData *jpegData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
CFDictionaryRef attachments = CMCopyDictionaryOfAttachments(kCFAllocatorDefault,
imageDataSampleBuffer,
kCMAttachmentMode_ShouldPropagate);
UIImage *image = [UIImage imageWithData:jpegData];
NSLog(@"%@", image);
// [self savePhoto];
}];
我想实现像Sip Color App那样,通过摄像头拾取颜色时没有快门声。这就需要从CMSampleBufferRef中获取图片了,实现代码如下:
- (UIImage *)imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer {
// 为媒体数据设置一个CMSampleBuffer的Core Video图像缓存对象
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
// 锁定pixel buffer的基地址
CVPixelBufferLockBaseAddress(imageBuffer, 0);
// 得到pixel buffer的基地址
void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);
// 得到pixel buffer的行字节数
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
// 得到pixel buffer的宽和高
size_t width = CVPixelBufferGetWidth(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);
// 创建一个依赖于设备的RGB颜色空间
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
// 用抽样缓存的数据创建一个位图格式的图形上下文(graphics context)对象
CGContextRef context = CGBitmapContextCreate(baseAddress, width, height, 8,
bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
// 根据这个位图context中的像素数据创建一个Quartz image对象
CGImageRef quartzImage = CGBitmapContextCreateImage(context);
// 解锁pixel buffer
CVPixelBufferUnlockBaseAddress(imageBuffer,0);
// 释放context和颜色空间
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
// 用Quartz image创建一个UIImage对象image
UIImage *image = [UIImage imageWithCGImage:quartzImage];
// 释放Quartz image对象
CGImageRelease(quartzImage);
return (image);
}
这样子就达到了没有快门声的拍照,也就是让用户根本感知不到我们在拍照。只要我们拿到当前相机镜头下的照片,就可以轻松拾取照片上的颜色值了。
好了,这篇就到这里了。后面一篇文章会接着讲怎么利用加速计和重力感应来监听手机状态发生了改变,然后拍取新的照片。等下篇文章结束,就可以放出部分代码到Github了。欢迎微信搜索“猿故” or "ApesTalk" or 扫码关注我,谢谢支持!
扫码关注猿故