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.
338 lines
14 KiB
338 lines
14 KiB
// |
|
// SGScanView.m |
|
// SGQRCodeExample |
|
// |
|
// Created by kingsic on 2017/8/23. |
|
// Copyright © 2017年 kingsic 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
|
|
|