You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

339 lines
14 KiB

//
// SGScanView.m
// SGQRCodeExample
//
// Created by kingsic on 2017/8/23.
// Copyright © 2017kingsic All rights reserved.
//
#import "SGScanView.h"
#import "SGScanViewConfigure.h"
#import "SGWeakProxy.h"
#import "SGQRCodeLog.h"
@interface SGScanView ()
@property (nonatomic, strong) SGScanViewConfigure *configure;
@property (nonatomic, strong) UIView *contentView;
@property (nonatomic, strong) UIImageView *scanlineImgView;
@property (nonatomic, strong) CADisplayLink *link;
@property (nonatomic, assign) BOOL isTop;
@property (nonatomic, assign) BOOL isSelected;
@end
@implementation SGScanView
- (void)dealloc {
if ([SGQRCodeLog sharedQRCodeLog].log) {
NSLog(@"SGScanView - - dealloc");
}
}
- (instancetype)initWithFrame:(CGRect)frame configure:(SGScanViewConfigure *)configure {
if (self = [super initWithFrame:frame]) {
self.configure = configure;
self.backgroundColor = [UIColor clearColor];
[self initialization];
[self addSubview:self.contentView];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap_action)];
tap.numberOfTapsRequired = 2;
[self addGestureRecognizer:tap];
}
return self;
}
+ (instancetype)scanViewWithFrame:(CGRect)frame configure:(SGScanViewConfigure *)configure {
return [[SGScanView alloc] initWithFrame:frame configure:configure];
}
- (void)initialization {
CGFloat w = 0.7 * self.frame.size.width;
CGFloat h = w;
CGFloat x = 0.5 * (self.frame.size.width - w);
CGFloat y = 0.5 * (self.frame.size.height - h);
_borderFrame = CGRectMake(x, y, w, h);
_scanFrame = CGRectMake(x, y, w, h);
self.isTop = YES;
}
- (UIView *)contentView {
if (!_contentView) {
CGFloat x = _scanFrame.origin.x;
CGFloat y = _scanFrame.origin.y;
CGFloat w = _scanFrame.size.width;
CGFloat h = _scanFrame.size.height;
_contentView = [[UIView alloc] initWithFrame:CGRectMake(x, y, w, h)];
_contentView.backgroundColor = [UIColor clearColor];
_contentView.clipsToBounds = YES;
}
return _contentView;
}
- (UIImageView *)scanlineImgView {
if (!_scanlineImgView) {
_scanlineImgView = [[UIImageView alloc] init];
/// 静态库 url 的获取
NSURL *url = [[NSBundle mainBundle] URLForResource:@"SGQRCode" withExtension:@"bundle"];
if (!url) {
/// 动态库 url 的获取
url = [[NSBundle bundleForClass:[self class]] URLForResource:@"SGQRCode" withExtension:@"bundle"];
}
NSBundle *bundle = [NSBundle bundleWithURL:url];
UIImage *image = [UIImage imageNamed:self.configure.scanline inBundle:bundle compatibleWithTraitCollection:nil];
if (!image) {
image = [UIImage imageNamed:self.configure.scanline];
}
_scanlineImgView.image = image;
if (image) {
[self updateScanLineFrame];
}
}
return _scanlineImgView;
}
- (void)tap_action {
if (self.isSelected) {
self.isSelected = NO;
} else {
self.isSelected = YES;
}
if (self.doubleTapBlock) {
self.doubleTapBlock(self.isSelected);
}
}
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
if (self.configure.isShowBorder == NO) {
return;
}
/// 边框 frame
CGFloat borderW = self.borderFrame.size.width;
CGFloat borderH = self.borderFrame.size.height;
CGFloat borderX = self.borderFrame.origin.x;
CGFloat borderY = self.borderFrame.origin.y;
CGFloat borderLineW = self.configure.borderWidth;
/// 空白区域设置
[self.configure.color setFill];
UIRectFill(rect);
// 获取上下文,并设置混合模式 -> kCGBlendModeDestinationOut
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetBlendMode(context, kCGBlendModeDestinationOut);
// 设置空白区
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRect:CGRectMake(borderX + 0.5 * borderLineW, borderY + 0.5 *borderLineW, borderW - borderLineW, borderH - borderLineW)];
[bezierPath fill];
// 执行混合模式
CGContextSetBlendMode(context, kCGBlendModeNormal);
/// 边框设置
UIBezierPath *borderPath = [UIBezierPath bezierPathWithRect:CGRectMake(borderX, borderY, borderW, borderH)];
borderPath.lineCapStyle = kCGLineCapButt;
borderPath.lineWidth = borderLineW;
[self.configure.borderColor set];
[borderPath stroke];
CGFloat cornerLength = self.configure.cornerLength;
CGFloat insideExcess = fabs(0.5 * (self.configure.cornerWidth - borderLineW));
CGFloat outsideExcess = 0.5 * (borderLineW + self.configure.cornerWidth);
/// 左上角小图标
[self leftTop:borderX borderY:borderY cornerLength:cornerLength insideExcess:insideExcess outsideExcess:outsideExcess];
/// 左下角小图标
[self leftBottom:borderX borderY:borderY borderH:borderH cornerLength:cornerLength insideExcess:insideExcess outsideExcess:outsideExcess];
/// 右上角小图标
[self rightTop:borderX borderY:borderY borderW:borderW cornerLength:cornerLength insideExcess:insideExcess outsideExcess:outsideExcess];
/// 右下角小图标
[self rightBottom:borderX borderY:borderY borderW:borderW borderH:borderH cornerLength:cornerLength insideExcess:insideExcess outsideExcess:outsideExcess];
}
- (void)leftTop:(CGFloat)borderX borderY:(CGFloat)borderY cornerLength:(CGFloat)cornerLength insideExcess:(CGFloat) insideExcess outsideExcess:(CGFloat)outsideExcess {
UIBezierPath *leftTopPath = [UIBezierPath bezierPath];
leftTopPath.lineWidth = self.configure.cornerWidth;
[self.configure.cornerColor set];
if (self.configure.cornerLocation == SGCornerLoactionInside) {
[leftTopPath moveToPoint:CGPointMake(borderX + insideExcess, borderY + cornerLength + insideExcess)];
[leftTopPath addLineToPoint:CGPointMake(borderX + insideExcess, borderY + insideExcess)];
[leftTopPath addLineToPoint:CGPointMake(borderX + cornerLength + insideExcess, borderY + insideExcess)];
} else if (self.configure.cornerLocation == SGCornerLoactionOutside) {
[leftTopPath moveToPoint:CGPointMake(borderX - outsideExcess, borderY + cornerLength - outsideExcess)];
[leftTopPath addLineToPoint:CGPointMake(borderX - outsideExcess, borderY - outsideExcess)];
[leftTopPath addLineToPoint:CGPointMake(borderX + cornerLength - outsideExcess, borderY - outsideExcess)];
} else {
[leftTopPath moveToPoint:CGPointMake(borderX, borderY + cornerLength)];
[leftTopPath addLineToPoint:CGPointMake(borderX, borderY)];
[leftTopPath addLineToPoint:CGPointMake(borderX + cornerLength, borderY)];
}
[leftTopPath stroke];
}
- (void)rightTop:(CGFloat)borderX borderY:(CGFloat)borderY borderW:(CGFloat)borderW cornerLength:(CGFloat)cornerLength insideExcess:(CGFloat) insideExcess outsideExcess:(CGFloat)outsideExcess {
UIBezierPath *rightTopPath = [UIBezierPath bezierPath];
rightTopPath.lineWidth = self.configure.cornerWidth;
[self.configure.cornerColor set];
if (self.configure.cornerLocation == SGCornerLoactionInside) {
[rightTopPath moveToPoint:CGPointMake(borderX + borderW - cornerLength - insideExcess, borderY + insideExcess)];
[rightTopPath addLineToPoint:CGPointMake(borderX + borderW - insideExcess, borderY + insideExcess)];
[rightTopPath addLineToPoint:CGPointMake(borderX + borderW - insideExcess, borderY + cornerLength + insideExcess)];
} else if (self.configure.cornerLocation == SGCornerLoactionOutside) {
[rightTopPath moveToPoint:CGPointMake(borderX + borderW - cornerLength + outsideExcess, borderY - outsideExcess)];
[rightTopPath addLineToPoint:CGPointMake(borderX + borderW + outsideExcess, borderY - outsideExcess)];
[rightTopPath addLineToPoint:CGPointMake(borderX + borderW + outsideExcess, borderY + cornerLength - outsideExcess)];
} else {
[rightTopPath moveToPoint:CGPointMake(borderX + borderW - cornerLength, borderY)];
[rightTopPath addLineToPoint:CGPointMake(borderX + borderW, borderY)];
[rightTopPath addLineToPoint:CGPointMake(borderX + borderW, borderY + cornerLength)];
}
[rightTopPath stroke];
}
- (void)leftBottom:(CGFloat)borderX borderY:(CGFloat)borderY borderH:(CGFloat)borderH cornerLength:(CGFloat)cornerLength insideExcess:(CGFloat) insideExcess outsideExcess:(CGFloat)outsideExcess {
UIBezierPath *leftBottomPath = [UIBezierPath bezierPath];
leftBottomPath.lineWidth = self.configure.cornerWidth;
[self.configure.cornerColor set];
if (self.configure.cornerLocation == SGCornerLoactionInside) {
[leftBottomPath moveToPoint:CGPointMake(borderX + cornerLength + insideExcess, borderY + borderH - insideExcess)];
[leftBottomPath addLineToPoint:CGPointMake(borderX + insideExcess, borderY + borderH - insideExcess)];
[leftBottomPath addLineToPoint:CGPointMake(borderX + insideExcess, borderY + borderH - cornerLength - insideExcess)];
} else if (self.configure.cornerLocation == SGCornerLoactionOutside) {
[leftBottomPath moveToPoint:CGPointMake(borderX + cornerLength - outsideExcess, borderY + borderH + outsideExcess)];
[leftBottomPath addLineToPoint:CGPointMake(borderX - outsideExcess, borderY + borderH + outsideExcess)];
[leftBottomPath addLineToPoint:CGPointMake(borderX - outsideExcess, borderY + borderH - cornerLength + outsideExcess)];
} else {
[leftBottomPath moveToPoint:CGPointMake(borderX + cornerLength, borderY + borderH)];
[leftBottomPath addLineToPoint:CGPointMake(borderX, borderY + borderH)];
[leftBottomPath addLineToPoint:CGPointMake(borderX, borderY + borderH - cornerLength)];
}
[leftBottomPath stroke];
}
- (void)rightBottom:(CGFloat)borderX borderY:(CGFloat)borderY borderW:(CGFloat)borderW borderH:(CGFloat)borderH cornerLength:(CGFloat)cornerLength insideExcess:(CGFloat) insideExcess outsideExcess:(CGFloat)outsideExcess {
UIBezierPath *rightBottomPath = [UIBezierPath bezierPath];
rightBottomPath.lineWidth = self.configure.cornerWidth;
[self.configure.cornerColor set];
if (self.configure.cornerLocation == SGCornerLoactionInside) {
[rightBottomPath moveToPoint:CGPointMake(borderX + borderW - insideExcess, borderY + borderH - cornerLength - insideExcess)];
[rightBottomPath addLineToPoint:CGPointMake(borderX + borderW - insideExcess, borderY + borderH - insideExcess)];
[rightBottomPath addLineToPoint:CGPointMake(borderX + borderW - cornerLength - insideExcess, borderY + borderH - insideExcess)];
} else if (self.configure.cornerLocation == SGCornerLoactionOutside) {
[rightBottomPath moveToPoint:CGPointMake(borderX + borderW + outsideExcess, borderY + borderH - cornerLength + outsideExcess)];
[rightBottomPath addLineToPoint:CGPointMake(borderX + borderW + outsideExcess, borderY + borderH + outsideExcess)];
[rightBottomPath addLineToPoint:CGPointMake(borderX + borderW - cornerLength + outsideExcess, borderY + borderH + outsideExcess)];
} else {
[rightBottomPath moveToPoint:CGPointMake(borderX + borderW, borderY + borderH - cornerLength)];
[rightBottomPath addLineToPoint:CGPointMake(borderX + borderW, borderY + borderH)];
[rightBottomPath addLineToPoint:CGPointMake(borderX + borderW - cornerLength, borderY + borderH)];
}
[rightBottomPath stroke];
}
- (void)setBorderFrame:(CGRect)borderFrame {
_borderFrame = borderFrame;
}
- (void)setScanFrame:(CGRect)scanFrame {
_scanFrame = scanFrame;
self.contentView.frame = scanFrame;
if (self.scanlineImgView.image) {
[self updateScanLineFrame];
}
}
- (void)updateScanLineFrame {
CGFloat w = _contentView.frame.size.width;
CGFloat h = (w * self.scanlineImgView.image.size.height) / self.scanlineImgView.image.size.width;
CGFloat x = 0;
CGFloat y = self.configure.isFromTop ? -h : 0;
self.scanlineImgView.frame = CGRectMake(x, y, w, h);
}
- (void)startScanning {
if (self.scanlineImgView.image == nil) {
return;
}
[self.contentView addSubview:self.scanlineImgView];
if (self.link == nil) {
self.link = [CADisplayLink displayLinkWithTarget:[SGWeakProxy weakProxyWithTarget:self] selector:@selector(updateUI)];
[self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
}
- (void)stopScanning {
if (self.scanlineImgView.image == nil) {
return;
}
// 此代码防止由于外界逻辑,可能会导致多次停止
if (self.link == nil) {
return;
}
[self.scanlineImgView removeFromSuperview];
self.scanlineImgView = nil;
[self.link invalidate];
self.link = nil;
}
- (void)updateUI {
CGRect frame = self.scanlineImgView.frame;
CGFloat contentViewHeight = CGRectGetHeight(self.contentView.frame);
CGFloat scanlineY = self.scanlineImgView.frame.origin.y + (self.configure.isFromTop ? 0 : self.scanlineImgView.frame.size.height);
if (self.configure.autoreverses) {
if (self.isTop) {
frame.origin.y += self.configure.scanlineStep;
self.scanlineImgView.frame = frame;
if (contentViewHeight <= scanlineY) {
self.isTop = NO;
}
} else {
frame.origin.y -= self.configure.scanlineStep;
self.scanlineImgView.frame = frame;
if (scanlineY <= self.scanlineImgView.frame.size.height) {
self.isTop = YES;
}
}
} else {
if (contentViewHeight <= scanlineY) {
CGFloat scanlineH = self.scanlineImgView.frame.size.height;
frame.origin.y = -scanlineH + (self.configure.isFromTop ? 0 : scanlineH);
self.scanlineImgView.frame = frame;
} else {
frame.origin.y += self.configure.scanlineStep;
self.scanlineImgView.frame = frame;
}
}
}
@end