Categories
english

Reading Barcodes on iOS 7

iOS 7 introduced APIs to read bar codes using the camera. I could have easily overlooked it without this excellent post from doubleencore.

NSHipster provided sample code, but it missed some details to work. Here is a sample which does.

Sample code

@import AVFoundation;

@interface CEViewController () <AVCaptureMetadataOutputObjectsDelegate>

@property (strong) AVCaptureSession *captureSession;

@end

@implementation CEViewController

- (void)viewDidLoad
{
	[super viewDidLoad];

	self.captureSession = [[AVCaptureSession alloc] init];
	AVCaptureDevice *videoCaptureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
	NSError *error = nil;
	AVCaptureDeviceInput *videoInput = [AVCaptureDeviceInput deviceInputWithDevice:videoCaptureDevice error:&error];
	if(videoInput)
		[self.captureSession addInput:videoInput];
	else
		NSLog(@"Error: %@", error);

	AVCaptureMetadataOutput *metadataOutput = [[AVCaptureMetadataOutput alloc] init];
	[self.captureSession addOutput:metadataOutput];
	[metadataOutput setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
	[metadataOutput setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code]];

	AVCaptureVideoPreviewLayer *previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession];
	previewLayer.frame = self.view.layer.bounds;
	[self.view.layer addSublayer:previewLayer];

	[self.captureSession startRunning];
}

#pragma mark AVCaptureMetadataOutputObjectsDelegate

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
	for(AVMetadataObject *metadataObject in metadataObjects)
	{
		AVMetadataMachineReadableCodeObject *readableObject = (AVMetadataMachineReadableCodeObject *)metadataObject;
		if([metadataObject.type isEqualToString:AVMetadataObjectTypeQRCode])
		{
			NSLog(@"QR Code = %@", readableObject.stringValue);
		}
		else if ([metadataObject.type isEqualToString:AVMetadataObjectTypeEAN13Code])
		{
			NSLog(@"EAN 13 = %@", readableObject.stringValue);
		}
	}
}

@end

Pitfalls

The issue I had with NSHipster’s sample code is the delegate method was not called at all. I quickly understood this was because the AVCaptureMetadataOutput must be configured to tell which metadata to recognise.

But, and this was not obvious to me, -[AVCaptureMetadataOuput setMetadataObjectTypes:] must be called after -[AVCaptureSession addOutput:]. Otherwise, the following message shows in the console:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[AVCaptureMetadataOutput setMetadataObjectTypes:] - unsupported type found.  Use -availableMetadataObjectTypes.'

I did try to look at the output of -availableMetadataObjectTypes, and it returns an empty array.

Therefore, -addOuput: must be called before setMetadataObjectTypes. In retrospect, it makes sense: the output object must know it is linked to a video session to know which metadata it may provide.

Categories
français

Une fenêtre qui change de view controller

Ce billet intéressera les programmeurs débutants sur le Mac. Je vais ici vous montrer comment passer d’un panneau à un autre, chacun défini dans son propre xib et géré par une sous-classe de NSViewController.

Capture d’écran 2013-09-16 à 10.09.24

Création du projet et mise en place de la toolbar

J’utilise Xcode 5. Commencez par créer un projet d’application pour Mac.

J’ajoute deux images de 32 x 32 pixels, qui serviront d’icônes pour la toolbar:

Square Circle

 

 

Xcode a créé un premier MainMenu.xib pour votre appli, comprenant une barre des menus et surtout une fenêtre.

Nous allons ajouter une NSToolbar à cette fenêtre. Pour cela, il suffit de glisser une toolbar depuis la rubrique Object Library (dans le coin inférieur droit de la fenêtre de Xcode) vers la fenêtre. La toolbar comporte déjà des icônes. Configurez-la pour qu’elle présente deux icônes en son centre:

Capture d’écran 2013-09-16 à 09.33.27

Pour cela, supprimez tous les NSToolbarItems présents,  glissez de nouveaux items, puis configurez-les.

Maintenant, tirez des actions allant de chaque icône vers l’AppDelegate.

@interface CEAppDelegate : NSObject <NSApplicationDelegate>

@property (assign) IBOutlet NSWindow *window;
- (IBAction)item0Pushed:(NSToolbarItem *)sender;
- (IBAction)item1Pushed:(NSToolbarItem *)sender;

@end

 Création des panneaux

Chaque panneau sera géré par un view controller et sera contenu dans un xib.

Commencez par créer une sous-classe de NSViewController (que j’appelle CECustom0ViewController):

Capture d’écran 2013-09-16 à 09.42.16

Éditez CECustom0ViewController.xib. Ajoutez simplement une NSBox et fixez son titre pour savoir de quel xib il s’agit:

Capture d’écran 2013-09-16 à 09.43.50

Créez une deuxième sous-classe de NSViewController sur le même principe (CECustom1ViewController).

Passage d’un view controller à l’autre

Pour gérer le changement de view controller, je créé une classe CEViewControllerSwitcher, qui hérite de NSObject:

CEViewControllerSwitcher.h

#import <Foundation/Foundation.h>
@interface CEViewControllerSwitcher : NSObject

- (id) initWithParentView:(NSView *)parentView viewControllers:(NSArray *)viewControllers;
@property (nonatomic, assign) NSUInteger viewControllerIndex;

@end

CEViewControllerSwitcher.m

#import "CEViewControllerSwitcher.h"

@interface CEViewControllerSwitcher ()

@property (strong) NSView *parentView;
@property (strong) NSArray *viewControllers;
@property (weak) NSViewController *currentViewController;

@end

@implementation CEViewControllerSwitcher

- (id) initWithParentView:(NSView *)parentView viewControllers:(NSArray *)viewControllers
{
	self = [super init];
	if (self) {
		_parentView = parentView;
		_viewControllers = viewControllers;
		self.viewControllerIndex = 0;  // Afficher le premier VC au départ
	}
	return self;
}

- (void) setViewControllerIndex:(NSUInteger)viewControllerIndex
{
	_viewControllerIndex = viewControllerIndex; 
	// Retirer le VC précédent
	if(self.currentViewController)
	{
		[self.currentViewController.view removeFromSuperview];
	} 

	// Ajouter le nouveau VC
	self.currentViewController = [self.viewControllers objectAtIndex:viewControllerIndex];
	[self.parentView addSubview:self.currentViewController.view];
}

@end

Liaison avec l’AppDelegate

CEAppDelegate.m

#import "CEAppDelegate.h"
#import "CEViewControllerSwitcher.h"
#import "CECustom0ViewController.h"
#import "CECustom1ViewController.h"

@interface CEAppDelegate ()

@property (strong, nonatomic) CEViewControllerSwitcher *viewControllerSwitcher;

@end

@implementation CEAppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
	NSView *contentView = self.window.contentView;
	CECustom0ViewController *viewController0 = [[CECustom0ViewController alloc] initWithNibName:@"CECustom0ViewController" bundle:nil];
	CECustom1ViewController *viewController1 = [[CECustom1ViewController alloc] initWithNibName:@"CECustom1ViewController" bundle:nil];
	self.viewControllerSwitcher = [[CEViewControllerSwitcher alloc] initWithParentView:contentView viewControllers:[NSArray arrayWithObjects:viewController0, viewController1, nil]];
}

- (IBAction)item0Pushed:(NSToolbarItem *)sender {
	self.viewControllerSwitcher.viewControllerIndex = 0;
}

- (IBAction)item1Pushed:(NSToolbarItem *)sender {
	self.viewControllerSwitcher.viewControllerIndex = 1;
}

@end

C’est terminé !

Voilà, ça fonctionne, on passe bien d’un View Controller à l’autre. Il s’agissait de la technique de base, je vous laisse régler l’autolayout des vues pour qu’elles remplissent bien la contentView de la fenêtre comme on le souhaite.

Le projet Xcode complet: ChangeVues

 

Categories
Non classé

dopop

Je me suis lancé depuis quelques semaines dans un nouveau projet. Il s’agit d’une application iPhone qui regroupera plusieurs jeux en rapport avec l’apprentissage du solfège, mais réalisés comme des jeux d’arcade pour ne pas être ennuyeux.

Je vous laisse jeter un œil au site consacré au jeu: dopop.net.

Categories
Non classé

Open Street Map: édition

Comme je vous le disais dans le billet d’introduction à OpenStreetMap, nous disposons aujourd’hui de nombreux outils pour éditer les plans. Aussi, il ne tient qu’à vous de commencer à compléter votre ville. Voyons trois de ces outils.

Il vous faudra un compte

Quel que soit l’outil choisi, il vous faudra créer un compte pour pouvoir éditer les cartes. S’agissant d’un outil collaboratif, vous comprendrez qu’il est nécessaire d’attribuer à chacun ses modifications, ne serait-ce que pour se prémunir des vandales. Vous disposerez également d’un historique de vos modifications et pourrez téléverser les traces GPS sur le site.

L’éditeur intégré à OpenStreetMap

Un tout nouvel éditeur intégré a fait son apparition. L’ancien outil était assez limité (il convenait pour renommer une rue, mais pas guère plus), et utilisait Flash, donc était inaccessible aux appareils mobiles.

Le nouvel éditeur propose les outils de base: ajout de points, de lignes et polygones, et balisage de ces éléments. Comme le fond de carte de Bing apparait pendant l’édition, nous disposons à présent d’un outil qui répond à la plupart des besoins.

Pour l’utiliser, rendez-vous sur la page d’accueil d’OpenStreetMap, et cliquez sur l’onglet Modifier. Un petit tutoriel est même présent pour vous expliquer les principes.

JOSM

Cet outil servira aux cartographes plus avertis. Par exemple, vous pourrez importer les données du cadastre français et changer le type de projection. Il est aussi utile si vous cartographiez massivement, puisqu’il dispose, par exemple, d’outils qui permettent d’importer des photos (pour noter comment s’appellent les rues ou les magasins), puis de synchroniser leur heure avec une trace GPS, ou un fichier audio.

Vous trouverez JOSM ici.  L’outil est écrit en Java, et est lourd. Par ailleurs, il est très mal adapté à la souris du Mac.

Pour finir, il y a un concept à comprendre: pour commencer l’édition, JOSM demande de délimiter une zone, qui ne doit pas être trop grande, de l’ordre de 3 x 3 km.  Il va ensuite charger tous les éléments (points, polygones…) qui se trouvent dans cette zone. On travaille alors hors-ligne, et c’est quand on a fini notre travail que JOSM vérifie qu’il n’y a pas d’incohérences, et qu’on peut enfin envoyer les données à OpenStreetMap.

Go Map!!

Pour finir, une application pour iPhone et surtout iPad, bien fichue et qui présente les outils essentiels. En pratique, travailler avec cette appli me semble plus lent qu’avec un ordinateur de bureau, mais la géolocalisation est bien pratique pour pouvoir éditer les cartes sur place.