Mobile Development 11 min read

Implementing Multi‑QR Code Detection and Interaction in iOS

This article explains how to detect multiple QR codes in an iOS image using Core Image, mark each code's location, and let users select a specific code either by adding transparent buttons or by handling touch events, covering coordinate conversion, scaling, and offset calculations.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Implementing Multi‑QR Code Detection and Interaction in iOS

Background When scanning breakfast QR codes, Alipay and WeChat codes are often placed together, causing both to be recognized at once. The author adds a multi‑QR handling feature to an iOS app and documents the whole implementation process.

Process Overview The workflow consists of detecting QR codes, marking each code’s position on the image, and then either adding transparent buttons over the codes (Solution 1) or using touch events to determine which code was tapped (Solution 2). The implementation must handle coordinate‑system conversion, scaling according to the displayed image size, and vertical offset when the image is centered.

QR Code Detection

// UIImage + Category
//识别二维码图片
- (NSArray<CIFeature*> *)imageQRFeatures {
    CIImage *ciImage = [[CIImage alloc] initWithCGImage:self.CGImage options:nil];
    CIContext *content = [CIContext contextWithOptions:@{kCIContextUseSoftwareRenderer : @(YES)}];
    CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:content options:@{CIDetectorAccuracy : CIDetectorAccuracyLow}];
    NSArray *features = [detector featuresInImage:ciImage];
    return features;
}

The returned features array contains a CIQRCodeFeature object for each detected QR code, which includes the code’s bounds and message string.

Drawing QR Code Borders

// UIImage + Category
- (UIImage *)drawQRBorder:(UIImage *)targetImage features:(CIQRCodeFeature *)feature {
    CGSize size = targetImage.size;
    UIGraphicsBeginImageContext(size);
    [targetImage drawInRect:CGRectMake(0.0, 0.0, size.width, size.height)];
    //翻转坐标系
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextScaleCTM(context, 1, -1);
    CGContextTranslateCTM(context, 0, size.height);
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:feature.bounds];
    [[UIColor colorWithRed:255.0/255.0 green:59.0/255.0 blue:48.0/255.0 alpha:1.0] setStroke];
    path.lineWidth = 3.0;
    [path stroke];
    UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return resultImage;
}

After drawing the borders, the image is displayed and the next step is to determine which QR code the user selects.

Solution 1 – Transparent Buttons The final position of each QR code is calculated, a clear UIButton is placed over it, and the button’s tag identifies the corresponding QR string.

static NSInteger kTagBeginValue = 1000;
- (void)addAlphaButtons {
    self.messageList = [NSMutableArray array];
    CGAffineTransform transform = CGAffineTransformIdentity;
    transform = CGAffineTransformScale(transform, 1, -1);
    transform = CGAffineTransformTranslate(transform, 0, -self.displayImage.size.height);
    CGFloat scaleX = self.view.bounds.size.width / self.displayImage.size.width;
    CGFloat scaleY = scaleX;
    CGAffineTransform scaleTransform = CGAffineTransformMakeScale(scaleX, scaleY);
    CGFloat offsetY = (zScreenHeight - self.displayImage.size.height * scaleY) / 2.0;
    for (CIQRCodeFeature *feature in self.features) {
        NSInteger index = [self.features indexOfObject:feature];
        if (!IsNilString(feature.messageString) && (index != NSNotFound)) {
            CGRect frame = CGRectApplyAffineTransform(feature.bounds, transform);
            frame = CGRectApplyAffineTransform(frame, scaleTransform);
            frame.origin.y += offsetY;
            UIButton *tempButton = [UIButton buttonWithType:UIButtonTypeCustom];
            tempButton.backgroundColor = [UIColor clearColor];
            tempButton.frame = frame;
            [self.view addSubview:tempButton];
            tempButton.tag = kTagBeginValue + index;
            [tempButton addTarget:self action:@selector(handleBtnAction:) forControlEvents:UIControlEventTouchUpInside];
            [self.messageList addObject:feature.messageString];
        }
    }
}

- (void)handleBtnAction:(UIButton *)sender {
    NSInteger index = sender.tag - kTagBeginValue;
    if (index < self.messageList.count) {
        NSString *scanQRStr = self.messageList[index];
        if (self.selectScanStrBlock) {
            self.selectScanStrBlock(scanQRStr);
            [self dismissViewControllerAnimated:NO completion:nil];
        }
    }
}

Solution 2 – Touch Event Detection Each QR code’s frame and string are stored in a custom object. In touchesBegan:withEvent: the touch point is compared against the stored frames (with a small tolerance) to find the selected code.

// WPSSelectScanImageItem.h
@interface WPSSelectScanImageItem : NSObject
@property (nonatomic, strong) NSString *qrcodeStr;
@property (nonatomic, assign) CGRect qrcodeFrame;
- (BOOL)isPointInQrcodeFrame:(CGPoint)targetPoint;
@end

// WPSSelectScanImageItem.m
@implementation WPSSelectScanImageItem
- (BOOL)isPointInQrcodeFrame:(CGPoint)targetPoint {
    CGFloat offsetValue = 10.0;
    CGFloat minX = self.qrcodeFrame.origin.x - offsetValue;
    CGFloat minY = self.qrcodeFrame.origin.y - offsetValue;
    CGFloat maxX = self.qrcodeFrame.origin.x + self.qrcodeFrame.size.width + offsetValue;
    CGFloat maxY = self.qrcodeFrame.origin.y + self.qrcodeFrame.size.height + offsetValue;
    return (targetPoint.x >= minX && targetPoint.x <= maxX && targetPoint.y >= minY && targetPoint.y <= maxY);
}
@end

// Storing calculated frames
- (void)initData {
    self.qrcodeItemList = [NSMutableArray array];
    CGAffineTransform transform = CGAffineTransformIdentity;
    transform = CGAffineTransformScale(transform, 1, -1);
    transform = CGAffineTransformTranslate(transform, 0, -self.displayImage.size.height);
    CGFloat scaleX = self.view.bounds.size.width / self.displayImage.size.width;
    CGFloat scaleY = scaleX;
    CGAffineTransform scaleTransform = CGAffineTransformMakeScale(scaleX, scaleY);
    CGFloat offsetY = (zScreenHeight - self.displayImage.size.height * scaleY) / 2.0;
    for (CIQRCodeFeature *feature in self.features) {
        NSInteger index = [self.features indexOfObject:feature];
        if (!IsNilString(feature.messageString) && (index != NSNotFound)) {
            CGRect frame = CGRectApplyAffineTransform(feature.bounds, transform);
            frame = CGRectApplyAffineTransform(frame, scaleTransform);
            frame.origin.y += offsetY;
            WPSSelectScanImageItem *item = [WPSSelectScanImageItem new];
            item.qrcodeFrame = frame;
            item.qrcodeStr = feature.messageString;
            [self.qrcodeItemList addObject:item];
        }
    }
}

// Touch handling
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [super touchesBegan:touches withEvent:event];
    UITouch *touch = touches.anyObject;
    CGPoint touchPoint = [touch locationInView:self.view];
    for (WPSSelectScanImageItem *item in self.qrcodeItemList) {
        if ([item isPointInQrcodeFrame:touchPoint]) {
            if (self.selectScanStrBlock) {
                self.selectScanStrBlock(item.qrcodeStr);
                [self dismissViewControllerAnimated:NO completion:nil];
            }
            break;
        }
    }
}

Both solutions correctly handle multiple QR codes, taking into account coordinate‑system differences, image scaling, and vertical offset caused by centering the image on the screen.

Full source code is available on GitHub at https://github.com/mokong/MultipleQRHandle.git .

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

UIiOSQR codeObjective‑CCore ImageTouch handling
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.