我们先来看需求
我们的需求是,通过调用相机,获取图像资源,根据手势控制截取范围,获取图片
让我们一步一步来实现吧
话不多说,直接上代码
引入相机需要的库
#import <AVFoundation/AVFoundation.h>
一些必要的属性
@property (nonatomic, strong) AVCaptureDevice *device;
@property (nonatomic, strong) AVCaptureSession *session;
@property (nonatomic, strong) AVCaptureDeviceInput *input;
@property (nonatomic, strong) AVCaptureMetadataOutput *output;
@property (nonatomic, strong) AVCapturePhotoOutput *ImageOutPut;
初始化相机
- (void)initCamera {
self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
self.input = [[AVCaptureDeviceInput alloc]initWithDevice:self.device error:nil];
self.output = [[AVCaptureMetadataOutput alloc] init];
self.ImageOutPut = [[AVCapturePhotoOutput alloc] init];
self.session = [[AVCaptureSession alloc]init];
self.session.sessionPreset = AVCaptureSessionPreset1920x1080;
if ([self.session canSetSessionPreset:AVCaptureSessionPresetInputPriority]) {
self.session.sessionPreset = AVCaptureSessionPresetInputPriority;
}
if([self.sessioncanAddInput:self.input]) {
[self.sessionaddInput:self.input];
}
if ([self.session canAddOutput:self.ImageOutPut]) {
[self.session addOutput:self.ImageOutPut];
}
self.previewLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:self.session];
self.previewLayer.frame = CGRectMake(0, 0, ScreenWidth, ScreenHeight - k_bottomControlHeight);
self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
[self.view.layer addSublayer:self.previewLayer];
self.frameView = [[ARTRecPicFrameView alloc] initWithFrame:self.previewLayer.frame];
[self.view addSubview:self.frameView];
self.frameView.frame = self.previewLayer.frame;
if ([_device lockForConfiguration:nil]) {
//自动白平衡
if ([_device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeAutoWhiteBalance]) {
[_device setWhiteBalanceMode:AVCaptureWhiteBalanceModeAutoWhiteBalance];
}
[_device unlockForConfiguration];
}
}
开始相机的图像采集
-(void)sessionStartRunning{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self.session startRunning];
});
}
当然相机权限的判断可以自己写一下啊,网上一大堆,这里不再贴代码。
采集图像的硬件初始化了,接下来我们要去实现一下这个覆盖层
我命名为
@property (nonatomic, strong) RecPicFrameView * frameView;
这个view的会覆盖在相机采集区域的上方,并且根据手势调整采集区域大小,输出采集区域的frame
触点的枚举
解释一下,采集框有八个点,手指触碰到这八个点,计算方式有所不同,因此区分开来,根据实际触碰的点来进行相应的采集框frame调整
typedef NS_ENUM(NSUInteger, DotRegion) {
DR_Center,
DR_LeftTop,
DR_LeftMid,
DR_LeftBottom,
DR_RightTop,
DR_RightMid,
DR_RightBottom,
DR_TopMid,
DR_BottomMid
};
@interface ARTRecPicFrameView : UIView
@property (nonatomic, assign) CGRect resizerFrame;
@property (nonatomic, assign) BOOL pointInside;
@property (nonatomic, assign) ARTDotRegion currDR;
@property (nonatomic, assign) CGRect contentFrame;
@property (nonatomic, assign) CGFloat maxResizeX;
@property (nonatomic, assign) CGFloat maxResizeY;
@property (nonatomic, assign) CGFloat minImageWH;
@property (nonatomic, weak) UIPanGestureRecognizer *panGR;
@property (nonatomic, weak) ARTFrameResizerBlurView *blurView;
@property (nonatomic, weak) CAShapeLayer *maskLayer;
// 线框图层
@property (nonatomic, weak) CAShapeLayer *frameLayer;
// 左右上中下点图层
@property (nonatomic, weak) CAShapeLayer *dotsLayer;
@end
交互、计算、绘制的代码如下
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.contentFrame = frame;
self.maxResizeX = frame.origin.x*2 + frame.size.width;
self.maxResizeY = frame.origin.y*2 + frame.size.height;
self.minImageWH = 70.0f;
_scopeWH = 50.0;
_halfScopeWH = _scopeWH * 0.5;
[self layoutWithFrame:frame];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
CGRect frame = self.frame;
self.contentFrame = frame;
NSLog(@"x=%.2f y=%.2f",self.frame.size.width,self.frame.size.height);
self.maxResizeX = frame.origin.x*2 + frame.size.width;
self.maxResizeY = frame.origin.y*2 + frame.size.height;
self.minImageWH = 70.0f;
[self layoutWithFrame:frame];
}
- (void)layoutWithFrame:(CGRect)frame {
CGFloat H_distance = 60;
CGFloat V_distance = 80;
CGRect initialFrame = CGRectMake(H_distance, V_distance, frame.size.width-2*H_distance, frame.size.height-2*V_distance);
self.resizerFrame = initialFrame;
for (ARTFrameResizerBlurView *blur in self.subviews) {
[blur removeFromSuperview];
}
ARTFrameResizerBlurView *blurView = [[ARTFrameResizerBlurView alloc] initWithFrame:frame blurEffect:nil bgColor:UIColor.blackColor maskAlpha:0.5];
blurView.userInteractionEnabled = NO;
[self addSubview:blurView];
self.blurView = blurView;
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = blurView.bounds;
maskLayer.fillColor = [UIColor blackColor].CGColor;
maskLayer.fillRule = kCAFillRuleEvenOdd;
blurView.layer.mask = maskLayer;
self.maskLayer = maskLayer;
self.frameLayer.lineWidth = 3.0f;
self.frameLayer.opacity = 1;
UIBezierPath *framePath = [UIBezierPath bezierPathWithRoundedRect:initialFrame cornerRadius:2];
self.frameLayer.path = framePath.CGPath;
self.dotsLayer.lineWidth = 3.0f;
self.dotsLayer.fillColor = UIColor.clearColor.CGColor;
self.dotsLayer.strokeColor = UIColor.whiteColor.CGColor;
self.dotsLayer.opacity = 1;
UIBezierPath *dotsPath = [self __classicDotsPathWithFrame:initialFrame];
self.dotsLayer.path = dotsPath.CGPath;
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRect:self.blurView.bounds];
[maskPath appendPath:framePath];
self.maskLayer.path = maskPath.CGPath;
UIPanGestureRecognizer *panGR = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panHandle:)];
[self addGestureRecognizer:panGR];
self.panGR = panGR;
}
- (void)panHandle:(UIPanGestureRecognizer *)panGR {
switch (panGR.state) {
case UIGestureRecognizerStateBegan: {
[self startImageresizer];
} break;
case UIGestureRecognizerStateChanged: {
[self __panChangedHandleWithTranslation:[panGR translationInView:self]];
} break;
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateFailed: {
[self endedImageresizer];
} break;
default:
break;
}
[panGR setTranslation:CGPointZero inView:self];
}
#pragma mark 开始/结束拖拽
- (void)startImageresizer {
if (_pointInside) {
}
}
- (void)endedImageresizer {
if (!_pointInside) {
return;
}
_pointInside = NO;
}
- (CAShapeLayer *)frameLayer {
if (!_frameLayer) {
_frameLayer = [self __createShapeLayer:1.0];
_frameLayer.fillColor = UIColor.clearColor.CGColor;
}
return _frameLayer;
}
- (CAShapeLayer *)dotsLayer {
if (!_dotsLayer) _dotsLayer = [self __createShapeLayer:0];
return _dotsLayer;
}
- (CAShapeLayer *)__createShapeLayer:(CGFloat)lineWidth {
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.lineWidth = lineWidth;
[self.layer addSublayer:shapeLayer];
return shapeLayer;
}
- (UIBezierPath *)__classicDotsPathWithFrame:(CGRect)frame {
UIBezierPath *path = [UIBezierPath bezierPath];
void (^appendPathBlock)(CGPoint point, CGPoint startPoint, CGPoint endPoint) = ^(CGPoint point, CGPoint startPoint, CGPoint endPoint) {
[path moveToPoint:startPoint];
[path addLineToPoint:point];
[path addLineToPoint:endPoint];
};
CGPoint originPoint = frame.origin;
CGPoint midPoint = CGPointMake(CGRectGetMidX(frame), CGRectGetMidY(frame));
CGPoint maxPoint = CGPointMake(CGRectGetMaxX(frame), CGRectGetMaxY(frame));
CGFloat halfArrLineW = 0.0f;
CGFloat arrLength = 20.0f;
CGFloat minLength = MIN(midPoint.x - originPoint.x, midPoint.y - originPoint.y);
if (arrLength > minLength) arrLength = minLength;
CGPoint point;
CGPoint startPoint;
CGPoint endPoint;
point = CGPointMake(originPoint.x - halfArrLineW, originPoint.y - halfArrLineW);
startPoint = CGPointMake(point.x, point.y + arrLength);
endPoint = CGPointMake(point.x + arrLength, point.y);
appendPathBlock(point, startPoint, endPoint);
point = CGPointMake(originPoint.x - halfArrLineW, maxPoint.y + halfArrLineW);
startPoint = CGPointMake(point.x, point.y - arrLength);
endPoint = CGPointMake(point.x + arrLength, point.y);
appendPathBlock(point, startPoint, endPoint);
point = CGPointMake(maxPoint.x + halfArrLineW, originPoint.y - halfArrLineW);
startPoint = CGPointMake(point.x - arrLength, point.y);
endPoint = CGPointMake(point.x, point.y + arrLength);
appendPathBlock(point, startPoint, endPoint);
point = CGPointMake(maxPoint.x + halfArrLineW, maxPoint.y + halfArrLineW);
startPoint = CGPointMake(point.x - arrLength, point.y);
endPoint = CGPointMake(point.x, point.y - arrLength);
appendPathBlock(point, startPoint, endPoint);
if (arrLength > minLength) arrLength = minLength;
point = CGPointMake(originPoint.x - halfArrLineW, midPoint.y);
startPoint = CGPointMake(point.x, point.y - arrLength);
endPoint = CGPointMake(point.x, point.y + arrLength);
appendPathBlock(point, startPoint, endPoint);
point = CGPointMake(maxPoint.x + halfArrLineW, midPoint.y);
startPoint = CGPointMake(point.x, point.y - arrLength);
endPoint = CGPointMake(point.x, point.y + arrLength);
appendPathBlock(point, startPoint, endPoint);
point = CGPointMake(midPoint.x, originPoint.y - halfArrLineW);
startPoint = CGPointMake(point.x - arrLength, point.y);
endPoint = CGPointMake(point.x + arrLength, point.y);
appendPathBlock(point, startPoint, endPoint);
point = CGPointMake(midPoint.x, maxPoint.y + halfArrLineW);
startPoint = CGPointMake(point.x - arrLength, point.y);
endPoint = CGPointMake(point.x + arrLength, point.y);
appendPathBlock(point, startPoint, endPoint);
return path;
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
if (![super pointInside:point withEvent:event]) {
_pointInside = NO;
return NO;
}
if (_pointInside) return YES;
_currDR = ARTDR_Center;
if (!_panGR.enabled) {
_pointInside = NO;
return NO;
}
CGRect frame = self.resizerFrame;
CGFloat scopeWH = _scopeWH;
CGFloat halfScopeWH = _halfScopeWH;
CGFloat x = frame.origin.x;
CGFloat y = frame.origin.y;
CGFloat w = frame.size.width;
CGFloat h = frame.size.height;
CGFloat maxX = CGRectGetMaxX(frame);
CGFloat maxY = CGRectGetMaxY(frame);
CGRect leftTopRect = CGRectMake(x - halfScopeWH, y - halfScopeWH, scopeWH, scopeWH);
CGRect leftBotRect = CGRectMake(x - halfScopeWH, maxY - halfScopeWH, scopeWH, scopeWH);
CGRect rightTopRect = CGRectMake(maxX - halfScopeWH, y - halfScopeWH, scopeWH, scopeWH);
CGRect rightBotRect = CGRectMake(maxX - halfScopeWH, maxY - halfScopeWH, scopeWH, scopeWH);
if (CGRectContainsPoint(leftTopRect, point)) {
_currDR = ARTDR_LeftTop;
} else if (CGRectContainsPoint(leftBotRect, point)) {
_currDR = ARTDR_LeftBottom;
} else if (CGRectContainsPoint(rightTopRect, point)) {
_currDR = ARTDR_RightTop;
} else if (CGRectContainsPoint(rightBotRect, point)) {
_currDR = ARTDR_RightBottom;
}
if (_currDR == ARTDR_Center) {
CGRect leftMidRect = CGRectNull;
CGRect rightMidRect = CGRectNull;
CGRect topMidRect = CGRectNull;
CGRect botMidRect = CGRectNull;
leftMidRect = CGRectMake(x - halfScopeWH, y + halfScopeWH, scopeWH, h - scopeWH);
rightMidRect = CGRectMake(maxX - halfScopeWH, y + halfScopeWH, scopeWH, h - scopeWH);
topMidRect = CGRectMake(x + halfScopeWH, y - halfScopeWH, w - scopeWH, scopeWH);
botMidRect = CGRectMake(x + halfScopeWH, maxY - halfScopeWH, w - scopeWH, scopeWH);
if (CGRectContainsPoint(leftMidRect, point)) {
_currDR = ARTDR_LeftMid;
} else if (CGRectContainsPoint(rightMidRect, point)) {
_currDR = ARTDR_RightMid;
} else if (CGRectContainsPoint(topMidRect, point)) {
_currDR = ARTDR_TopMid;
} else if (CGRectContainsPoint(botMidRect, point)) {
_currDR = ARTDR_BottomMid;
} else {
_pointInside = NO;
return NO;
}
}
_pointInside = YES;
return YES;
}
//更新ui
- (void)setResizerFrame:(CGRect)resizerFrame {
[self __updateImageresizerFrame:resizerFrame animateDuration:-1.0];
}
- (void)__updateImageresizerFrame:(CGRect)resizerFrame animateDuration:(NSTimeInterval)duration {
_resizerFrame = resizerFrame;
CGFloat radius = 0.1;
UIBezierPath *framePath = [UIBezierPath bezierPathWithRoundedRect:resizerFrame cornerRadius:radius];
UIBezierPath *dotsPath;
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRect:self.blurView.bounds];
[maskPath appendPath:framePath];
dotsPath = [self __classicDotsPathWithFrame:resizerFrame];
[CATransaction begin];
[CATransaction setDisableActions:YES];
_maskLayer.path = maskPath.CGPath;
_frameLayer.path = framePath.CGPath;
_dotsLayer.path = dotsPath.CGPath;
[CATransaction commit];
}
- (void)__panChangedHandleWithTranslation:(CGPoint)translation {
CGFloat x = self.resizerFrame.origin.x;
CGFloat y = self.resizerFrame.origin.y;
CGFloat w = self.resizerFrame.size.width;
CGFloat h = self.resizerFrame.size.height;
switch (_currDR) {
case ARTDR_LeftTop: {
w += -2*translation.x;
h += -2*translation.y;
break;
}
case ARTDR_LeftBottom: {
w += -2*translation.x;
h += 2*translation.y;
break;
}
case ARTDR_RightTop: {
w += 2*translation.x;
h += -2*translation.y;
break;
}
case ARTDR_RightBottom: {
w += 2*translation.x;
h += 2*translation.y;
break;
}
case ARTDR_LeftMid: {
w += -2*translation.x;
break;
}
case ARTDR_RightMid: {
w += 2*translation.x;
break;
}
case ARTDR_TopMid: {
h += -2*translation.y;
break;
}
case ARTDR_BottomMid: {
h += 2*translation.y;
break;
}
default:
break;
}
if (w < _minImageWH) {
w = _minImageWH;
} else if (w > self.contentFrame.size.width) {
w = self.contentFrame.size.width - 2;
}
if (h < _minImageWH) {
h = _minImageWH;
} else if (h > self.contentFrame.size.height) {
h = self.contentFrame.size.height - 2;
}
x = (self.frame.size.width - w)/2;
y = (self.frame.size.height - h)/2;
CGRect imageresizerFrame = CGRectMake(x, y, w, h);
self.resizerFrame = imageresizerFrame;
}
差不多就这些,有问题的可以私聊我
关联词:iOS可拖拽扫描框,iOS动态扫描框、iOS可以控制大小的扫描框