4

I want to use barcode reader with scanning indicator. can anyone tell me which barcode sdk should I need to use. currently I am using zbar sdk. In zbar we don't have scanning indicator. Please see the screen shot which I want to implement

enter image description here

Bhupesh
  • 2,310
  • 13
  • 33

5 Answers5

4

I did an enhanced version of @rgkobashi answer in attempt to create the square shape and animating the indicator line inside the square.

Scanning indicator and animation code as below:

 func createScanningIndicator() {
    
    let height: CGFloat = 15
    let opacity: Float = 0.4
    let topColor = UIColor.green.withAlphaComponent(0)
    let bottomColor = UIColor.green

    let layer = CAGradientLayer()
    layer.colors = [topColor.cgColor, bottomColor.cgColor]
    layer.opacity = opacity
    
    let squareWidth = view.frame.width * 0.6
    let xOffset = view.frame.width * 0.2
    let yOffset = view.frame.midY - (squareWidth / 2)
    layer.frame = CGRect(x: xOffset, y: yOffset, width: squareWidth, height: height)
    
    self.view.layer.insertSublayer(layer, at: 0)

    let initialYPosition = layer.position.y
    let finalYPosition = initialYPosition + squareWidth - height
    let duration: CFTimeInterval = 2

    let animation = CABasicAnimation(keyPath: "position.y")
    animation.fromValue = initialYPosition as NSNumber
    animation.toValue = finalYPosition as NSNumber
    animation.duration = duration
    animation.repeatCount = .infinity
    animation.isRemovedOnCompletion = false
    
    layer.add(animation, forKey: nil)
}

Scanning Square shape as below:

func createScanningFrame() {
            
    let lineLength: CGFloat = 15
    let squareWidth = view.frame.width * 0.6
    let topLeftPosX = view.frame.width * 0.2
    let topLeftPosY = view.frame.midY - (squareWidth / 2)
    let btmLeftPosY = view.frame.midY + (squareWidth / 2)
    let btmRightPosX = view.frame.midX + (squareWidth / 2)
    let topRightPosX = view.frame.width * 0.8
    
    let path = UIBezierPath()
    
    //top left
    path.move(to: CGPoint(x: topLeftPosX, y: topLeftPosY + lineLength))
    path.addLine(to: CGPoint(x: topLeftPosX, y: topLeftPosY))
    path.addLine(to: CGPoint(x: topLeftPosX + lineLength, y: topLeftPosY))

    //bottom left
    path.move(to: CGPoint(x: topLeftPosX, y: btmLeftPosY - lineLength))
    path.addLine(to: CGPoint(x: topLeftPosX, y: btmLeftPosY))
    path.addLine(to: CGPoint(x: topLeftPosX + lineLength, y: btmLeftPosY))

    //bottom right
    path.move(to: CGPoint(x: btmRightPosX - lineLength, y: btmLeftPosY))
    path.addLine(to: CGPoint(x: btmRightPosX, y: btmLeftPosY))
    path.addLine(to: CGPoint(x: btmRightPosX, y: btmLeftPosY - lineLength))

    //top right
    path.move(to: CGPoint(x: topRightPosX, y: topLeftPosY + lineLength))
    path.addLine(to: CGPoint(x: topRightPosX, y: topLeftPosY))
    path.addLine(to: CGPoint(x: topRightPosX - lineLength, y: topLeftPosY))
    
    let shape = CAShapeLayer()
    shape.path = path.cgPath
    shape.strokeColor = UIColor.white.cgColor
    shape.lineWidth = 3
    shape.fillColor = UIColor.clear.cgColor
    
    self.view.layer.insertSublayer(shape, at: 0)
}

Result as below images: enter image description here

titan
  • 524
  • 1
  • 6
  • 19
  • its not showing anything on the screen , How to call this func ? Inside viewdidload ? – Hanzala Raza Jan 11 '21 at 10:51
  • @HanzalaRaza yes, just call these 2 function in viewDidLoad will do – titan Jan 11 '21 at 19:17
  • but i didn't work for me :( , does it something to do with bringSubviewToFront func ? It may be the possibility that view may rendering behind camera view layer ? – Hanzala Raza Jan 12 '21 at 07:14
  • @HanzalaRaza as u can see in the code, i did add the indicator view on top of the view itself using the insert layer function. On my side, im actually adding this indicator view to my AR scene view and work no problem. I think you can try inspect all your view layer during runtime and check whether it's overlapped by other view layer. – titan Jan 14 '21 at 11:51
2

You could add an image as "indicator"

// Create the reader
self.reader = [ZBarReaderViewController new];
self.reader.readerDelegate = self;

// Create image for adding an indicator :)
UIImage *image = [UIImage imageNamed:@"scan_indicator.png"];
UIImageView *imageLogo = [[UIImageView alloc] initWithImage:image];
imageLogo.frame = CGRectMake(0, self.view.frame.size.height / 2, image.size.width, image.size.height);

// Configure reader
self.reader.cameraOverlayView = imageLogo;
LoVo
  • 1,856
  • 19
  • 21
2

This was my solution for this problem, without using any third party library, on Swift and using MVVM, so it is testable and avoid adding stuff to your view layer:

On viewModel:

    func createScannerGradientLayer(for view: UIView) -> CAGradientLayer {
        let height: CGFloat = 50
        let opacity: Float = 0.5
        let topColor = {your color}
        let bottomColor = topColor.withAlphaComponent(0)

        let layer = CAGradientLayer()
        layer.colors = [topColor.cgColor, bottomColor.cgColor]
        layer.opacity = opacity
        layer.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: height)
        return layer
    }

    func createAnimation(for layer: CAGradientLayer) -> CABasicAnimation {
        guard let superLayer = layer.superlayer else {
            fatalError("Unable to create animation, layer should have superlayer")
        }
        let superLayerHeight = superLayer.frame.height
        let layerHeight = layer.frame.height
        let value = superLayerHeight - layerHeight

        let initialYPosition = layer.position.y
        let finalYPosition = initialYPosition + value
        let duration: CFTimeInterval = 1

        let animation = CABasicAnimation(keyPath: "position.y")
        animation.fromValue = initialYPosition as NSNumber
        animation.toValue = finalYPosition as NSNumber
        animation.duration = duration
        animation.repeatCount = .infinity
        return animation
    }

And on caller site (view/viewController):

let layer = viewModel.createScannerGradientLayer(for: scannerView)
scannerView.layer.insertSublayer(layer, at: 0)
let animation = viewModel.createAnimation(for: layer)
layer.add(animation, forKey: nil)
rgkobashi
  • 2,551
  • 19
  • 25
1

Here's an overlay view that you can place into your view that also contains a ZBar reader view. It contains:

  • Camera view finder edges
  • Scanning laser

(incorporate your own graphics)

It would work equally well using native iOS scanning.

@implementation ScanModeOverlay
{
    UIImageView* _viewFinder;
    UIImageView* _laser;

    BOOL _jiggled;
    NSTimer* _laserJiggler;
    BOOL _animating;
}

//-------------------------------------------------------------------------------------------
#pragma mark - Initialization & Destruction
//-------------------------------------------------------------------------------------------

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {
        [self initViewFinder];
    }
    return self;
}

- (void)dealloc
{
    [_laserJiggler invalidate];
}


//-------------------------------------------------------------------------------------------
#pragma mark - Interface Methods
//-------------------------------------------------------------------------------------------

- (void)setAnimating:(BOOL)animating
{
    _animating = animating;
    if (_animating)
    {
        if (_laser == nil)
        {
            _laser = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"redlaser.png"]];
            [_laser setFrame:CGRectMake(0, (self.frame.size.height / 2) - 130, 320, 30)];
            [self addSubview:_laser];
            _laserJiggler =
                    [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(jiggleLaser) userInfo:nil repeats:YES];
        }
        [self scanDownwards];
    }
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    [_viewFinder setFrame:self.bounds];
}


//-------------------------------------------------------------------------------------------
#pragma mark - Private Methods
//-------------------------------------------------------------------------------------------

- (void)initViewFinder
{
    _viewFinder = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"camera-overlay.png"]];
    [_viewFinder setContentMode:UIViewContentModeCenter];
    [self addSubview:_viewFinder];
}

- (void)jiggleLaser
{
    if (_jiggled)
    {
        [_laser performSelectorOnMainThread:@selector(setImage:) withObject:[UIImage imageNamed:@"redlaser2"] waitUntilDone:NO];
        _jiggled = NO;
    }
    else
    {
        [_laser performSelectorOnMainThread:@selector(setImage:) withObject:[UIImage imageNamed:@"redlaser"] waitUntilDone:NO];
        _jiggled = YES;
    }
}

- (void)scanDownwards
{
    [UIView transitionWithView:self duration:1 options:UIViewAnimationOptionCurveEaseInOut animations:^
    {
        CGRect newFrame = _laser.frame;
        newFrame.origin.y = (self.frame.size.height / 2) - 130;
        _laser.frame = newFrame;
    } completion:^(BOOL complete)
    {
        if (_animating)
        {
            [self scanUpwards];
        }
    }];
}

- (void)scanUpwards
{
    [UIView transitionWithView:self duration:1 options:UIViewAnimationOptionCurveEaseInOut animations:^
    {
        CGRect newFrame = _laser.frame;
        newFrame.origin.y = (self.frame.size.height / 2) + 125;
        _laser.frame = newFrame;
    } completion:^(BOOL complete)
    {
        if (_animating)
        {
            [self scanDownwards];
        }
    }];
}

@end
Jasper Blues
  • 28,258
  • 22
  • 102
  • 185
0

Add a CABasicAnimation to the view's layer to animate its position. The below code assumes that the view's layer's initial position y is 0. Add it in viewDidAppear:.

UIView *movingView = "Green line";

CABasicAnimation *animation = [CABasicAnimation
                               animationWithKeyPath:@"position"];

animation.toValue = [NSValue valueWithCGPoint:CGPointMake(movingView.center.x, movingView.superview.bounds.size.height)];
animation.duration = 4.0;
animation.repeatCount = HUGE_VAL;
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
[movingView.layer addAnimation:animation forKey:@"position"];
gabbler
  • 13,626
  • 4
  • 32
  • 44
  • but I am presenting zbar view on some button action. so do I need to add this code on that button action ? – Bhupesh Mar 09 '15 at 10:19
  • I don't quite understand the question, how is the green box added? is it a subview of the zbar view? In the UIViewController that you add the green box as subview of self.view, add the above code in viewDidAppear method of the view controller. – gabbler Mar 09 '15 at 16:15
  • no its not subView of zbar view. I have shown the reference image. I want to make like that. – Bhupesh Mar 10 '15 at 06:58
  • https://www.youtube.com/watch?v=DjKlSTnpVtY see this video you will get more clear. hows the green line is moving in this video. – Bhupesh Mar 10 '15 at 07:14
  • You can add 4 blur views to self.view, top, left, bottom, right. Then add a green box view in the center. Add a imageView with a green line image as subview of the green box, when the green box is showed, you animate the imageView's layer as showed in the code. Also, if barcode is not inside the green box, there will be no scanning result, this can be achieved by some frame intersection computing. – gabbler Mar 10 '15 at 07:33