Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(78)

Side by Side Diff: ios/chrome/browser/ui/qr_scanner/qr_scanner_view.mm

Issue 2589803002: Upstream Chrome on iOS source code [6/11]. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #import "ios/chrome/browser/ui/qr_scanner/qr_scanner_view.h"
6
7 #include "base/logging.h"
8 #include "base/mac/foundation_util.h"
9 #include "base/mac/scoped_nsobject.h"
10 #include "ios/chrome/browser/ui/icons/chrome_icon.h"
11 #include "ios/chrome/browser/ui/ui_util.h"
12 #include "ios/chrome/grit/ios_strings.h"
13 #include "ios/third_party/material_components_ios/src/components/Buttons/src/Mat erialButtons.h"
14 #include "ui/base/l10n/l10n_util.h"
15 #include "ui/base/l10n/l10n_util_mac.h"
16
17 namespace {
18 // TODO(crbug.com/629427): Replace the temporary UI constants with correct
19 // values for all device types.
20
21 // Padding for buttons in the QR scanner UI.
22 const CGFloat kButtonPadding = 16.0;
23
24 // Width and height of the QR scanner viewport.
25 const CGFloat kViewportSize_iPhone = 250.0;
26 const CGFloat kViewportSize_iPad = 300.0;
27
28 // Length of the viewport borders, starting from the corner.
29 const CGFloat kViewportBorderCornerWidth_iPhone = 25.0;
30 const CGFloat kViewportBorderCornerWidth_iPad = 30.0;
31
32 // Opacity of the preview overlay.
33 const CGFloat kPreviewOverlayOpacity = 0.5;
34
35 // Corner radius of the border around the viewport.
36 const CGFloat kViewportBorderCornerRadius = 2.0;
37 // Line width of the viewport border.
38 const CGFloat kViewportBorderLineWidth = 4.0;
39 // Shadow opacity of the viewport border.
40 const CGFloat kViewportBorderShadowOpacity = 1.0;
41 // Shadow radius of the viewport border.
42 const CGFloat kViewportBorderShadowRadius = 10.0;
43 // Padding of the viewport caption, below the viewport.
44 const CGFloat kViewportCaptionPadding = 24.0;
45 // Shadow opacity of the viewport caption.
46 const CGFloat kViewportCaptionShadowOpacity = 1.0;
47 // Shadow radius of the viewport caption.
48 const CGFloat kViewportCaptionShadowRadius = 5.0;
49
50 // Duration of the flash animation played when a code is scanned.
51 const CGFloat kFlashDuration = 0.5;
52
53 // Returns a square of size |rectSize| centered inside |frameSize|.
54 CGRect CenteredRectForViewport(CGSize frameSize, CGFloat rectSize) {
55 CGFloat rectX = AlignValueToPixel((frameSize.width - rectSize) / 2);
56 CGFloat rectY = AlignValueToPixel((frameSize.height - rectSize) / 2);
57 return CGRectMake(rectX, rectY, rectSize, rectSize);
58 }
59
60 // Returns the size of the viewport based on the device type.
61 CGFloat GetViewportSize() {
62 return IsIPadIdiom() ? kViewportSize_iPad : kViewportSize_iPhone;
63 }
64
65 } // namespace
66
67 // A subclass of UIView with the layerClass property set to
68 // AVCaptureVideoPreviewLayer. Contains the video preview for the QR scanner.
69 @interface VideoPreviewView : UIView
70
71 // Returns the VideoPreviewView's layer cast to AVCaptureVideoPreviewLayer.
72 - (AVCaptureVideoPreviewLayer*)previewLayer;
73
74 // Returns the rectangle in camera coordinates in which codes should be
75 // recognized.
76 - (CGRect)viewportRectOfInterest;
77
78 @end
79
80 @implementation VideoPreviewView
81
82 + (Class)layerClass {
83 return [AVCaptureVideoPreviewLayer class];
84 }
85
86 - (AVCaptureVideoPreviewLayer*)previewLayer {
87 return base::mac::ObjCCastStrict<AVCaptureVideoPreviewLayer>([self layer]);
88 }
89
90 - (CGRect)viewportRectOfInterest {
91 DCHECK(CGPointEqualToPoint(self.frame.origin, CGPointZero));
92 CGRect viewportRect =
93 CenteredRectForViewport(self.frame.size, GetViewportSize());
94 AVCaptureVideoPreviewLayer* layer = [self previewLayer];
95 // If the layer does not have a connection,
96 // |metadataOutputRectOfInterestForRect:| does not return the right value.
97 DCHECK(layer.connection);
98 return [layer metadataOutputRectOfInterestForRect:viewportRect];
99 }
100
101 @end
102
103 // A subclass of UIView containing the preview overlay. It is responsible for
104 // redrawing the preview overlay and the viewport border every time the size
105 // of the preview changes. This UIView should always be square, with its width
106 // and height being the maximum of the width and height of its parent.
107 @interface PreviewOverlayView : UIView {
108 // Creates a transparent preview overlay. The overlay is a sublayer of the
109 // PreviewOverlayView's view to keep the opacity of the view's layer 1.0,
110 // otherwise the viewport border would inherit the opacity of the overlay.
111 base::scoped_nsobject<CALayer> _previewOverlay;
112 // A container for the viewport border to draw a shadow under the border.
113 // Sublayer of PreviewOverlayView's layer.
114 base::scoped_nsobject<CALayer> _viewportBorderContainer;
115 // The preview viewport border. Sublayer of |_viewportBorderContainer|.
116 base::scoped_nsobject<CAShapeLayer> _viewportBorder;
117 }
118
119 // Creates a square mask for the overlay to keep the viewport transparent.
120 - (CAShapeLayer*)getViewportMaskWithFrameSize:(CGSize)frameSize
121 viewportSize:(CGFloat)viewportSize;
122 // Creates a mask to only draw the corners of the viewport border.
123 - (CAShapeLayer*)getViewportBorderMaskWithFrameSize:(CGSize)frameSize
124 viewportSize:(CGFloat)viewportSize;
125
126 @end
127
128 @implementation PreviewOverlayView
129
130 - (instancetype)initWithFrame:(CGRect)frame {
131 self = [super initWithFrame:frame];
132 if (!self) {
133 return nil;
134 }
135
136 _previewOverlay.reset([[CALayer alloc] init]);
137 [_previewOverlay setBackgroundColor:[[UIColor blackColor] CGColor]];
138 [_previewOverlay setOpacity:kPreviewOverlayOpacity];
139 [[self layer] addSublayer:_previewOverlay];
140
141 _viewportBorderContainer.reset([[CALayer alloc] init]);
142 [_viewportBorderContainer setShadowColor:[[UIColor blackColor] CGColor]];
143 [_viewportBorderContainer setShadowOffset:CGSizeZero];
144 [_viewportBorderContainer setShadowRadius:kViewportBorderShadowRadius];
145 [_viewportBorderContainer setShadowOpacity:kViewportBorderShadowOpacity];
146 [_viewportBorderContainer setShouldRasterize:YES];
147 [_viewportBorderContainer
148 setRasterizationScale:[[UIScreen mainScreen] scale]];
149
150 _viewportBorder.reset([[CAShapeLayer alloc] init]);
151 [_viewportBorder setStrokeColor:[[UIColor whiteColor] CGColor]];
152 [_viewportBorder setFillColor:nil];
153 [_viewportBorder setOpacity:1.0];
154 [_viewportBorder setLineWidth:kViewportBorderLineWidth];
155 [_viewportBorderContainer addSublayer:_viewportBorder];
156
157 [[self layer] addSublayer:_viewportBorderContainer];
158 return self;
159 }
160
161 - (void)layoutSubviews {
162 [super layoutSubviews];
163 CGSize frameSize = self.frame.size;
164 CGFloat viewportSize = GetViewportSize();
165 [_previewOverlay
166 setFrame:CGRectMake(0, 0, frameSize.width, frameSize.height)];
167 [_previewOverlay setMask:[self getViewportMaskWithFrameSize:frameSize
168 viewportSize:viewportSize]];
169
170 CGRect borderRect = CenteredRectForViewport(
171 frameSize, viewportSize + kViewportBorderLineWidth);
172 UIBezierPath* borderPath =
173 [UIBezierPath bezierPathWithRoundedRect:borderRect
174 cornerRadius:kViewportBorderCornerRadius];
175
176 [_viewportBorder setPath:[borderPath CGPath]];
177 [_viewportBorder
178 setMask:[self getViewportBorderMaskWithFrameSize:frameSize
179 viewportSize:viewportSize]];
180 }
181
182 - (CAShapeLayer*)getViewportMaskWithFrameSize:(CGSize)frameSize
183 viewportSize:(CGFloat)viewportSize {
184 CGRect frameRect = CGRectMake(0, 0, frameSize.width, frameSize.height);
185 CGRect viewportRect = CenteredRectForViewport(frameSize, viewportSize);
186 UIBezierPath* maskPath = [UIBezierPath bezierPathWithRect:frameRect];
187 [maskPath appendPath:[UIBezierPath bezierPathWithRect:viewportRect]];
188
189 CAShapeLayer* mask = [[[CAShapeLayer alloc] init] autorelease];
190 [mask setFillColor:[[UIColor blackColor] CGColor]];
191 [mask setFillRule:kCAFillRuleEvenOdd];
192 [mask setFrame:frameRect];
193 [mask setPath:maskPath.CGPath];
194 return mask;
195 }
196
197 - (CAShapeLayer*)getViewportBorderMaskWithFrameSize:(CGSize)frameSize
198 viewportSize:(CGFloat)viewportSize {
199 CGFloat viewportBorderCornerWidth = IsIPadIdiom()
200 ? kViewportBorderCornerWidth_iPad
201 : kViewportBorderCornerWidth_iPhone;
202 CGRect maskRect = CenteredRectForViewport(
203 frameSize, viewportSize - 2 * viewportBorderCornerWidth);
204 CGFloat sizeX = maskRect.origin.x;
205 CGFloat sizeY = maskRect.origin.y;
206 CGFloat offsetX = sizeX + maskRect.size.width;
207 CGFloat offsetY = sizeY + maskRect.size.height;
208
209 UIBezierPath* path =
210 [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, sizeX, sizeY)];
211 [path appendPath:[UIBezierPath bezierPathWithRect:CGRectMake(0, offsetY,
212 sizeX, sizeY)]];
213 [path appendPath:[UIBezierPath bezierPathWithRect:CGRectMake(offsetY, 0,
214 sizeX, sizeY)]];
215 [path appendPath:[UIBezierPath bezierPathWithRect:CGRectMake(offsetX, offsetY,
216 sizeX, sizeY)]];
217
218 CAShapeLayer* mask = [[[CAShapeLayer alloc] init] autorelease];
219 [mask setFillColor:[[UIColor blackColor] CGColor]];
220 [mask setFrame:CGRectMake(0, 0, frameSize.width, frameSize.height)];
221 [mask setPath:path.CGPath];
222 return mask;
223 }
224
225 @end
226
227 @interface QRScannerView () {
228 // A button to toggle the torch.
229 base::scoped_nsobject<MDCFlatButton> _torchButton;
230 // A view containing the preview layer for camera input.
231 base::scoped_nsobject<VideoPreviewView> _previewView;
232 // A transparent overlay on top of the preview layer.
233 base::scoped_nsobject<PreviewOverlayView> _previewOverlay;
234 // The constraint specifying that the preview overlay should be square.
235 base::scoped_nsobject<NSLayoutConstraint> _overlaySquareConstraint;
236 // The constraint relating the size of the |_previewOverlay| to the width of
237 // the QRScannerView.
238 base::scoped_nsobject<NSLayoutConstraint> _overlayWidthConstraint;
239 // The constraint relating the size of the |_previewOverlay| to the height of
240 // te QRScannerView.
241 base::scoped_nsobject<NSLayoutConstraint> _overlayHeightConstraint;
242 }
243
244 // Creates an image with template rendering mode for use in icons.
245 - (UIImage*)templateImageWithName:(NSString*)name;
246 // Creates an icon for torch turned on.
247 - (UIImage*)torchOnIcon;
248 // Creates an icon for torch turned off.
249 - (UIImage*)torchOffIcon;
250
251 // Sets common configuration properties of a button in the QR scanner UI and
252 // adds it to self.view.
253 - (void)configureButton:(MDCFlatButton*)button
254 withIcon:(UIImage*)icon
255 action:(SEL)action;
256 // Adds a close button.
257 - (void)addCloseButton;
258 // Adds a torch button and stores it in |_torchButton|.
259 - (void)addTorchButton;
260 // Adds a caption to the viewport.
261 - (void)addViewportCaptionLabel;
262 // Adds a preview view to |self| and configures its layout constraints.
263 - (void)setupPreviewView;
264 // Adds a transparent overlay with a viewport border to |self| and configures
265 // its layout constraints.
266 - (void)setupPreviewOverlayView;
267
268 @end
269
270 @implementation QRScannerView
271
272 @synthesize delegate = _delegate;
273
274 #pragma mark lifecycle
275
276 - (instancetype)initWithFrame:(CGRect)frame
277 delegate:(id<QRScannerViewDelegate>)delegate {
278 self = [super initWithFrame:frame];
279 if (!self) {
280 return nil;
281 }
282 DCHECK(delegate);
283 _delegate = delegate;
284 [self setupPreviewView];
285 [self setupPreviewOverlayView];
286 [self addCloseButton];
287 [self addTorchButton];
288 [self addViewportCaptionLabel];
289 return self;
290 }
291
292 - (instancetype)initWithFrame:(CGRect)frame {
293 NOTREACHED();
294 return nil;
295 }
296
297 - (instancetype)initWithCoder:(NSCoder*)coder {
298 NOTREACHED();
299 return nil;
300 }
301
302 #pragma mark UIView
303
304 // TODO(crbug.com/633577): Replace the preview overlay with a UIView which is
305 // not resized.
306 - (void)layoutSubviews {
307 [super layoutSubviews];
308 [self setBackgroundColor:[UIColor blackColor]];
309 if (CGRectEqualToRect([_previewView bounds], CGRectZero)) {
310 [_previewView setBounds:self.bounds];
311 }
312 [_previewView setCenter:CGPointMake(CGRectGetMidX(self.bounds),
313 CGRectGetMidY(self.bounds))];
314 }
315
316 #pragma mark public methods
317
318 - (AVCaptureVideoPreviewLayer*)getPreviewLayer {
319 return [_previewView previewLayer];
320 }
321
322 - (void)enableTorchButton:(BOOL)torchIsAvailable {
323 [_torchButton setEnabled:torchIsAvailable];
324 if (!torchIsAvailable) {
325 [self setTorchButtonTo:NO];
326 }
327 }
328
329 - (void)setTorchButtonTo:(BOOL)torchIsOn {
330 DCHECK(_torchButton);
331 UIImage* icon = nil;
332 NSString* accessibilityValue = nil;
333 if (torchIsOn) {
334 icon = [self torchOnIcon];
335 accessibilityValue =
336 l10n_util::GetNSString(IDS_IOS_QR_SCANNER_TORCH_ON_ACCESSIBILITY_VALUE);
337 } else {
338 icon = [self torchOffIcon];
339 accessibilityValue = l10n_util::GetNSString(
340 IDS_IOS_QR_SCANNER_TORCH_OFF_ACCESSIBILITY_VALUE);
341 }
342 [_torchButton setImage:icon forState:UIControlStateNormal];
343 [_torchButton setAccessibilityValue:accessibilityValue];
344 }
345
346 - (void)resetPreviewFrame:(CGSize)size {
347 [_previewView setTransform:CGAffineTransformIdentity];
348 [_previewView setFrame:CGRectMake(0, 0, size.width, size.height)];
349 }
350
351 - (void)rotatePreviewByAngle:(CGFloat)angle {
352 [_previewView
353 setTransform:CGAffineTransformRotate([_previewView transform], angle)];
354 }
355
356 - (void)finishPreviewRotation {
357 CGAffineTransform rotation = [_previewView transform];
358 // Check that the current transform is either an identity or a 90, -90, or 180
359 // degree rotation.
360 DCHECK(fabs(atan2f(rotation.b, rotation.a)) < 0.001 ||
361 fabs(fabs(atan2f(rotation.b, rotation.a)) - M_PI) < 0.001 ||
362 fabs(fabs(atan2f(rotation.b, rotation.a)) - M_PI / 2) < 0.001);
363 rotation.a = round(rotation.a);
364 rotation.b = round(rotation.b);
365 rotation.c = round(rotation.c);
366 rotation.d = round(rotation.d);
367 [_previewView setTransform:rotation];
368 }
369
370 - (CGRect)viewportRectOfInterest {
371 return [_previewView viewportRectOfInterest];
372 }
373
374 - (void)animateScanningResultWithCompletion:(void (^)(void))completion {
375 UIView* whiteView = [[[UIView alloc] init] autorelease];
376 whiteView.frame = self.bounds;
377 [self addSubview:whiteView];
378 whiteView.backgroundColor = [UIColor whiteColor];
379 [UIView animateWithDuration:kFlashDuration
380 animations:^{
381 whiteView.alpha = 0.0;
382 }
383 completion:^void(BOOL finished) {
384 [whiteView removeFromSuperview];
385 if (completion) {
386 completion();
387 }
388 }];
389 }
390
391 #pragma mark private methods
392
393 - (UIImage*)templateImageWithName:(NSString*)name {
394 UIImage* image = [[UIImage imageNamed:name]
395 imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
396 DCHECK(image);
397 return image;
398 }
399
400 - (UIImage*)torchOnIcon {
401 UIImage* icon = [self templateImageWithName:@"qr_scanner_torch_on"];
402 return icon;
403 }
404
405 - (UIImage*)torchOffIcon {
406 UIImage* icon = [self templateImageWithName:@"qr_scanner_torch_off"];
407 return icon;
408 }
409
410 - (void)configureButton:(MDCFlatButton*)button
411 withIcon:(UIImage*)icon
412 action:(SEL)action {
413 [button setTintColor:[UIColor whiteColor]];
414 [button setImage:icon forState:UIControlStateNormal];
415 [button setInkStyle:MDCInkStyleUnbounded];
416 [button addTarget:_delegate
417 action:action
418 forControlEvents:UIControlEventTouchUpInside];
419 [self addSubview:button];
420 }
421
422 - (void)addCloseButton {
423 MDCFlatButton* closeButton =
424 [[[MDCFlatButton alloc] initWithFrame:CGRectZero] autorelease];
425 UIImage* closeIcon = [ChromeIcon closeIcon];
426 UIImage* closeButtonIcon =
427 [closeIcon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
428 [closeButton setAccessibilityLabel:[closeIcon accessibilityLabel]];
429 [closeButton setAccessibilityIdentifier:[closeIcon accessibilityIdentifier]];
430 [self configureButton:closeButton
431 withIcon:closeButtonIcon
432 action:@selector(dismissQRScannerView:)];
433
434 // Constraints for closeButton.
435 [closeButton setTranslatesAutoresizingMaskIntoConstraints:NO];
436 [NSLayoutConstraint activateConstraints:@[
437 [[closeButton leadingAnchor] constraintEqualToAnchor:[self leadingAnchor]
438 constant:kButtonPadding],
439 [[closeButton bottomAnchor] constraintEqualToAnchor:[self bottomAnchor]
440 constant:-kButtonPadding]
441 ]];
442 }
443
444 - (void)addTorchButton {
445 DCHECK(!_torchButton);
446 _torchButton.reset([[MDCFlatButton alloc] initWithFrame:CGRectZero]);
447 [_torchButton setEnabled:NO];
448 [self configureButton:_torchButton
449 withIcon:[self torchOffIcon]
450 action:@selector(toggleTorch:)];
451 [_torchButton setAccessibilityIdentifier:@"qr_scanner_torch_button"];
452 [_torchButton setAccessibilityLabel:
453 l10n_util::GetNSString(
454 IDS_IOS_QR_SCANNER_TORCH_BUTTON_ACCESSIBILITY_LABEL)];
455 [_torchButton setAccessibilityValue:
456 l10n_util::GetNSString(
457 IDS_IOS_QR_SCANNER_TORCH_OFF_ACCESSIBILITY_VALUE)];
458
459 // Constraints for _torchButton.
460 [_torchButton setTranslatesAutoresizingMaskIntoConstraints:NO];
461 [NSLayoutConstraint activateConstraints:@[
462 [[_torchButton trailingAnchor] constraintEqualToAnchor:[self trailingAnchor]
463 constant:-kButtonPadding],
464 [[_torchButton bottomAnchor] constraintEqualToAnchor:[self bottomAnchor]
465 constant:-kButtonPadding]
466 ]];
467 }
468
469 - (void)addViewportCaptionLabel {
470 UILabel* viewportCaption = [[[UILabel alloc] init] autorelease];
471 NSString* label = l10n_util::GetNSString(IDS_IOS_QR_SCANNER_VIEWPORT_CAPTION);
472 [viewportCaption setText:label];
473 [viewportCaption setAccessibilityLabel:label];
474 [viewportCaption setAccessibilityIdentifier:@"qr_scanner_viewport_caption"];
475 [viewportCaption setTextColor:[UIColor whiteColor]];
476 [viewportCaption.layer setShadowColor:[UIColor blackColor].CGColor];
477 [viewportCaption.layer setShadowOffset:CGSizeZero];
478 [viewportCaption.layer setShadowRadius:kViewportCaptionShadowRadius];
479 [viewportCaption.layer setShadowOpacity:kViewportCaptionShadowOpacity];
480 [viewportCaption.layer setMasksToBounds:NO];
481 [viewportCaption.layer setShouldRasterize:YES];
482 [self addSubview:viewportCaption];
483
484 // Constraints for viewportCaption.
485 [viewportCaption setTranslatesAutoresizingMaskIntoConstraints:NO];
486 [NSLayoutConstraint activateConstraints:@[
487 [[viewportCaption centerXAnchor]
488 constraintEqualToAnchor:[self centerXAnchor]],
489 [[viewportCaption centerYAnchor]
490 constraintEqualToAnchor:[self centerYAnchor]
491 constant:GetViewportSize() / 2 + kViewportCaptionPadding]
492 ]];
493 }
494
495 - (void)setupPreviewView {
496 DCHECK(!_previewView);
497 _previewView.reset([[VideoPreviewView alloc] initWithFrame:self.frame]);
498 [self insertSubview:_previewView atIndex:0];
499 }
500
501 - (void)setupPreviewOverlayView {
502 DCHECK(!_previewOverlay);
503 _previewOverlay.reset([[PreviewOverlayView alloc] initWithFrame:CGRectZero]);
504 [self addSubview:_previewOverlay];
505
506 // Add a multiplier of sqrt(2) to the width and height constraints to make
507 // sure that the overlay covers the whole screen during rotation.
508 _overlayWidthConstraint.reset(
509 [[NSLayoutConstraint constraintWithItem:_previewOverlay
510 attribute:NSLayoutAttributeWidth
511 relatedBy:NSLayoutRelationGreaterThanOrEqual
512 toItem:self
513 attribute:NSLayoutAttributeWidth
514 multiplier:sqrt(2)
515 constant:0.0] retain]);
516
517 _overlayHeightConstraint.reset(
518 [[NSLayoutConstraint constraintWithItem:_previewOverlay
519 attribute:NSLayoutAttributeHeight
520 relatedBy:NSLayoutRelationGreaterThanOrEqual
521 toItem:self
522 attribute:NSLayoutAttributeHeight
523 multiplier:sqrt(2)
524 constant:0.0] retain]);
525
526 _overlaySquareConstraint.reset([[[_previewOverlay heightAnchor]
527 constraintEqualToAnchor:[_previewOverlay widthAnchor]] retain]);
528
529 // Constrains the preview overlay to be square, centered, with both width and
530 // height greater than or equal to the width and height of the QRScannerView.
531 [_previewOverlay setTranslatesAutoresizingMaskIntoConstraints:NO];
532 [NSLayoutConstraint activateConstraints:@[
533 [[_previewOverlay centerXAnchor]
534 constraintEqualToAnchor:[self centerXAnchor]],
535 [[_previewOverlay centerYAnchor]
536 constraintEqualToAnchor:[self centerYAnchor]],
537 _overlaySquareConstraint, _overlayWidthConstraint, _overlayHeightConstraint
538 ]];
539 }
540
541 @end
OLDNEW
« no previous file with comments | « ios/chrome/browser/ui/qr_scanner/qr_scanner_view.h ('k') | ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698