Portage d'iPhone vers netbook : Au-delà des fondamentaux

Au-delà des fondamentaux

Le développement sur iPhone a connu une croissance rapide grâce à l'existence de la boutique d'applications, mais également grâce au nombre de fonctionnalités spécialisées offertes par l'appareil et d'interfaces générales proposées par Apple pour utiliser ces fonctionnalités. Maintenant que les netbooks se répandent davantage, bon nombre d'équipements naguère dévolus aux smartphones deviennent disponibles dans le monde des netbooks. En fait, la quasi-totalité des netbooks vendu aujourd'hui inclut une caméra, ce que l'on voyait rarement sur un PC Windows il y a encore quelques années.

Dans les fragments de code qui suivent, j'explorerai en profondeur les différences existant en termes de matériels entre l'iPhone et les netbooks en montrant des exemples de code permettant d'accéder auxdites fonctionnalités sur chaque plate-forme (là ou quand ces fonctionnalités existent). En sus de cet article, il existe plusieurs ressources intéressantes dans la collection d'articles IADP, qui abordent plus largement les techniques utilisées durant le portage. Dans son interview pour l'article « De l'iPhone à AppUp : Portage de “Smiles” », Mike Kasprzack, PDG de Syhkronics, est amené à traiter d'un grand nombre de différences dans le hardware quand il explique comment il a fait passer son jeu d'iPhone sur AppUp.

Les Apple iPhone OS Reference Library et Microsoft’s MSDN Library sont des ressources riches en documents de référence, de tutoriels et de discussions sur chacune des plate-formes.



Rotation et accéléromètre

Référence

API iPhone

API Windows

Rotation de l'écran

didRotateFromInterfaceOrientation

DisplaySettingsChanged

Accéléromètre

UIAccelerometer

Capteur API Windows

L'iPhone utilise l'accéléromètre embarqué pour détecter les changements d'orientation. L'API iPhone fournit deux méthodes pour permettre les changements d'orientation et agir sur eux. shouldAutorotateToInterfaceOrientation indique au framework qu'il doit automatiquement faire pivoter l'interface utilisateur lorsqu'il détecte une rotation de l'appareil. Si vous autorisez la rotation, le SDK iPhone déclenche didRotateFromInterfaceOrientation pour permettre à votre application d'agir sur le changement d'orientation.

Objective-C iPhone

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    return YES;
}


- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
    NSMutableString *newOrientation = [[[NSMutableString alloc] init] autorelease];
    switch ([[UIDevice currentDevice] orientation]) {
        case UIDeviceOrientationLandscapeLeft:
            [newOrientation setString:@"LandscapeLeft"];
            break;
        case UIDeviceOrientationLandscapeRight:
            [newOrientation setString:@"LandscapeRight"];
            break;
        case UIDeviceOrientationPortrait:
            [newOrientation setString:@"Portrait"];
            break;
        default:
            [newOrientation setString:@"(unknown)"];
            break;
    }

    [self setDiagInfo:@"Orientation" value:newOrientation];
}

Comme le dit Kasprzack, « verrouiller le jeu dans une seule orientation est sur PC la solution habituelle ». Certains netbooks (et notamment les tablettes) prennent en charge la rotation de l'écran et aviseront le système lorsque l'écran subit une rotation. 

Vous pouvez détecter la rotation de l'écran en affectant dans C# (ou dans un autre code géré) un gestionnaire d'événements à l'événement système DisplaySettingsChanged, comme dans l'exemple suivant d'application WPF (Windows Presentation Foundation).

XAML Windows

<Window x:Class="Rotation.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Rotation " Height="246" Width="431" Loaded="Window_Loaded" Unloaded="Window_Unloaded">
    <Grid>
        <Label Name="label1" Content="Orientation:" Height="28"  Width="131" 
               HorizontalAlignment="Left" HorizontalContentAlignment="Right" VerticalAlignment="Top" 
               Margin="12,44,0,0" FontSize="14" FontWeight="Bold" />
        <Label Name="labelOrientation" Content="(starting)" Height="28"  Width="230"
               HorizontalAlignment="Right" HorizontalContentAlignment="Left" VerticalAlignment="Top"
               Margin="0,44,30,0" FontSize="14" FontWeight="Bold" />
    </Grid>
</Window>

C# Windows

using System;
using System.Windows;
using Microsoft.Win32;

namespace Rotation
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void UpdateOrientation(object sender, EventArgs e)
        {
            double width = System.Windows.SystemParameters.PrimaryScreenWidth;
            double height = System.Windows.SystemParameters.PrimaryScreenHeight;

            string orientation;
            if (height > width)
                orientation = "Portrait";
            else
                orientation = "Landscape";

            string description = orientation + " (" + width.ToString() + "x" + height.ToString() + ")";
            labelOrientation.Content = description;
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            SystemEvents.DisplaySettingsChanged += new EventHandler(UpdateOrientation);
            UpdateOrientation(sender, e);
        }

        private void Window_Unloaded(object sender, RoutedEventArgs e)
        {
            SystemEvents.DisplaySettingsChanged -= UpdateOrientation;
        }
    }

}

Dans C++, pour les applications natives, vous êtes à l'écoute du message WM_DISPLAYCHANGE et vous y répondez.

C++ Windows

#include <windows.h>

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM 
  lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;
    switch (message) 
    {
//. . .
//. . .
    case WM_DISPLAYCHANGE:
        {
        //Separate the screen width and height and then
        //check to see if screen width is greater than screen height
        if ((LOWORD(lParam) > HIWORD(lParam)) 
            //Run the application in landscape, for example:
            MessageBox(NULL,"Run in landscape.","Landscape",MB_OK);
        else
            //Run the application in portrait, as in:
            MessageBox(NULL,"Run in portrait.","Portrait",MB_OK);
        }
        break;

Vous trouverez tous les détails, y compris cet exemple de code, dans l'article MSDN « Detecting Screen Orientation and Screen Rotation in Tablet PC Applications ».

Si votre application dépend de l'accéléromètre comme source d'entrée (le cas d'un jeu, par exemple), il ne sera peut-être pas indiqué de la porter sur un netbook car la plupart des netbooks ne comportent pas d'accéléromètre. Mais, avec de plus en plus de tablettes sur le marché au cours du second semestre 2010, on peut s'attendre à une plus grande prévalence des accéléromètres. 

Sur l'iPhone, vous pouvez obtenir les événements d'accéléromètre en enregistrant auprès de la classe UIAccelerometer en tant que délégué et consommateur d'événements.

Objective-C iPhone

-(void)viewDidLoad
{
    [super viewDidLoad];

    [[UIAccelerometer sharedAccelerometer] setUpdateInterval:1.0 / 60.0];
    [[UIAccelerometer sharedAccelerometer] setDelegate:self];
}

// UIAccelerometerDelegate method, called when the device accelerates.
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration

{
    // Do something with the data
    // acceleration.x, acceleration.y, acceleration.z
}

Grâce à l'API Sensor, interface COM accessible à partir du code C++ natif, Windows 7 prend en charge les accéléromètres comme type de « capteur ». Pour pouvoir accéder aux capteurs via du code géré, vous devez vous procurer le Windows API Code Pack sur MSDN. À la différence de l'iPhone, Windows ne fournit pas d'API générale qui abstraie les données d'accéléromètre d'appareils distincts. On trouve des accéléromètres la plupart du temps dans des terminaux de poche et Windows Mobile prend en charge deux appareils, un HTC et un Samsung, dans les deux cas via des interfaces matérielles directes1.

Pour pouvoir prendre en charge les accéléromètres sur des tablettes ou des netbooks, il vous faudra effectuer des recherches dans le SDK ou les interfaces publiés par chaque constructeur.

[1] Pour plus de détails et pour le code source, voir l'API non officielle Windows Mobile Unified Sensor API de Koushik Dutta.

Du tactile à la souris

Sauf si vous effectuez des mouvements, les souris se comportent énormément comme les dispositifs tactiles, à ceci près que la souris peut survoler, ce qui n'est pas le cas des dispositifs tactiles. C'est pourquoi passer d'une interaction tactile à une interaction à la souris ne doit pas être compliqué. Compte tenu des attentes des utilisateurs Windows, vous voudrez certainement ajouter l'abilité à survoler si vous avez des composants personnalisés d'interface utilisateur (cas fréquent des jeux). Tout comme les hyperliens dans les navigateurs web, la plupart des contrôles Windows actuels fournissent une rétroaction visuelle de l'effleurement pour notifier l'utilisateur que l'élément va répondre à un clic. 

Si votre application prend en charge les mouvements ou si elle dépend de certaines mouvements spécifiques à l'iPhone, vous aurez sans doute à vous demander comment exploiter cela dans votre application Windows. 

Windows Touch

Les tablettes et les netbooks à écran tactile prennent souvent en charge les interactions tactiles multipoint et Windows 7 Touch intègre la prise en charge des fonctionnalités suivantes : faire glisser, faire défiler, zoomer, faire pivoter et bien plus encore. Vous trouverez une bonne discussion du problème dans l'article MSDN, « Getting Started with Windows Touch Gestures ». Pour les appareils non tactiles, voici quelques approches permettant de gérer ces événements.

Faire défiler

Référence

API iPhone

API Windows

Défilement

UIScrollView

CWnd::OnMouseWheel

Control.OnMouseWheel

Le défilement tactile fonctionne sur l'iPhone de manière semblable à la molette de la souris prise en charge par les applications Windows, par les contrôles de la barre de défilement et par tous les contrôles portant sur le défilement. Dans une situation d'interface utilisateur personnalisée, vous aurez besoin de capturer l'événement scrollwheel (Mollette de défilement) pour répliquer cette prise en charge. Exemple : Google Maps dans un navigateur Web réagit au défilement à l'aide de la molette pour effectuer un zoom rapproché (ou éloigné) sur la carte. Dans une application iPhone, vous n'aurez sans doute pas besoin de vous poser la question du défilement car ce dernier sera géré pour vous par des composants de l'interface utilisateur ou parce que vous aurez mis en œuvre une classe UIScrollView.

Toutefois, il n'existe aucun équivalent du comportement Flick (usage du stylet) dans les contrôles standard de Windows, mais vous pouvez très bien vous le représenter comme un appel page-up et page-down sur un événement barre de défilement ou vous pouvez l'inférer par l'accélération de l'événement Mollette de défilement. Vous pouvez capturer l'événement en substituant la méthode CWnd::OnMouseWheel dans C++/MFC ou la méthode Control.OnMouseWheel dans .NET.

C++ Windows

BOOL CWndEx::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)    
{      
    if( zDelta < 0 )   
    {   
        if( zDelta < -PAGE_DELTA ) {
            // Do action to "scroll" down one "line"
        } else {
            // Do action to "scroll" down one "page"
        }
    }   
    else   
    {   
        if( zDelta < PAGE_DELTA ) {
            // Do action to "scroll" up one "line"
        } else {
            // Do action to "scroll" up one "page"
        }
    }     
   
    return CWnd::OnMouseWheel(nFlags, zDelta, pt);   
}

C# Windows

protected override void OnMouseWheel(MouseWheelEventArgs e)
{ 
    if( e.Delta < 0 ) {
        if( e.Delta < -PAGE_DELTA ) {
            // Do action to "scroll" down one "line"
        } else {
            // Do action to "scroll" down one "page"
        }
    } else {
        if( e.Delta < PAGE_DELTA ) {
            // Do action to "scroll" up one "line"
        } else {
            // Do action to "scroll" up one "page"
        }
    }

}

Zoom

Comme, sur les appareils non tactiles, il n'existe aucune prise en charge native du zoom tactile ou d'une action de zoom, vous devrez décider si vous avez besoin de cette action dans votre application. Il peut y avoir d'autres manières de fournir des fonctionnalités similaires de zoom, peut-être en ajoutant des contrôles à l'interface utilisateur. Un grand nombre d'applications Windows comportent un menu Affichage avec des options Zoom avant, Zoom arrière et Taille normale. Habituellement, ces options sont respectivement mappées aux combinaisons de touches Ctrl +, Ctrl - et Ctrl 0, et Windows 7 dispose des combinaisons globales de touches Windows + et Windows - qui zooment l'ensemble de l'écran. 

Faire pivoter

Le mouvement de rotation avec deux doigts que reconnaît sur l'iPhone, n'est pas prise en charge en natif pour Windows sur les appareils non tactiles. Là aussi, habituellement, les applications Windows qui autorisent la rotation comprendront un menu Affichage avec des éléments de Rotation dans le sens horaire et de Rotation dans le sens inverse.

Faire glisser

Référence

API iPhone

API Windows

Glisser-déposer

touchesMoved

DragDrop

Bien que le glisser-déposer ne soit pas pris en charge en natif par l'iPhone (vous devez le mettre en œuvre via la méthode touchesMoved), les événements « glisser » sont totalement pris en charge dans les API Windows et ils sont bien documentés. Il y a une vidéo de 30 minutes sur MSDN qui explique comment mettre en œuvre le glisser-déposer dans votre application MFC. Pour .NET, voir sur MSDN l'article « Performing Drag-and-Drop Operations on Windows » qui propose l'exemple de code suivant :

C# Windows

private void button1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
   button1.DoDragDrop(button1.Text, DragDropEffects.Copy | 
      DragDropEffects.Move);
}

private void textBox1_DragEnter(object sender, System.Windows.Forms.DragEventArgs e)
{
   if (e.Data.GetDataPresent(DataFormats.Text)) 
      e.Effect = DragDropEffects.Copy;
   else
      e.Effect = DragDropEffects.None;
}

private void textBox1_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
{
   textBox1.Text = e.Data.GetData(DataFormats.Text).ToString();
}

Mise à l'échelle des graphismes

L'écran d'un netbook est plus grand que celui d'un iPhone. Les écrans de netbooks peuvent varier en taille, mais l'immense majorité d'entre eux est de 1024 x 600 pixels. Pour vous adapter à cette résolution plus grande et pour vous adapter aux diverses résolutions, vous voudrez sans doute ajuster la taille et l'emplacement des objets en fonction de la taille de la fenêtre ou de l'écran. Si vous utilisez WPF pour votre application Windows, vous pourrez lui faire repérer automatiquement les objets en rapport mutuel et les mettre à l'échelle en fonction de la fenêtre. Pour les applications riches en graphismes, comme les jeux ; dans son interview, Kasprzack suggérait pour la mise à l'échelle des graphismes de partir avec une résolution de référence plus importante que n'importe laquelle des résolutions cibles. En construisant des images de cette taille, l'on peut alors diminuer l'échelle des graphismes de manière appropriée pour chaque plate-forme. De surcroît, tout placement ou tout déplacement d'objets (comme dans les jeux) peuvent être mis à l'échelle de chaque résolution à partir de la résolution de référence.

Localisation/GPS

Référence

API iPhone

API Windows 7

Services de localisation

L'infrastructure de localisation du noyau - Core Location Framework

API Location

Dans les applications iPhone, Apple fournissait l'infrastructure de localisation du noyau qui permet au développeur d'utiliser le matériel disponible pour repérer l'utilisateur et suivre les modifications de sa position géographique, quel que soit le matériel sous-jacent. L'exemple d'application LocateMe dans la bibliothèque de référence de l'iPhone montre comment récupérer l'emplacement actuel et être notifié quand cette dernière vient à changer. 

Pour obtenir un emplacement sur l'iPhone, vous créez une classe contrôleur ou délégué qui met en oeuvre CLLocationManagerDelegate

Objective-C iPhone

#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>

@interface LocationViewController : UIViewController <CLLocationManagerDelegate> {
    UILabel *labelLatitude;
    UILabel *labelLongitude;
    UILabel *labelStatus;
    CLLocationManager *locationManager;
}

@property(nonatomic,retain) IBOutlet UILabel *labelLatitude;
@property(nonatomic,retain) IBOutlet UILabel *labelLongitude;
@property(nonatomic,retain) IBOutlet UILabel *labelStatus;
@property(nonatomic,retain) CLLocationManager *locationManager;

- (void)locationManager:(CLLocationManager *)manager
    didUpdateToLocation:(CLLocation *)newLocation
           fromLocation:(CLLocation *)oldLocation;

- (void)locationManager:(CLLocationManager *)manager
       didFailWithError:(NSError *)error;

@end

Ensuite, vous définissez les méthodes qui lancent le service de localisation, qui collectent le résultat et qui gèrent les éventuelles erreurs.

@implementation LocationViewController
@synthesize labelLatitude,labelLongitude,labelStatus,locationManager;

- (void)viewDidLoad {
    [super viewDidLoad];
    [labelStatus setText:@"Started"];

    locationManager = [[[CLLocationManager alloc] init] autorelease];
    locationManager.delegate = self;

    [locationManager startUpdatingLocation];
}

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation
           fromLocation:(CLLocation *)oldLocation {
    [labelStatus setText:@"Got location"];
    [labelLatitude setText:[NSString stringWithFormat:@"%.6f",newLocation.coordinate.latitude]];
    [labelLongitude setText:[NSString stringWithFormat:@"%6f",newLocation.coordinate.longitude]];
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
    [labelStatus setText:[error description]];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

- (void)viewDidUnload {
}

- (void)dealloc {
    [super dealloc];
}

@end

Avec Windows 7 Location Platform, il est quasiment aussi facile d'en faire autant sur les netbooks capables de localisation. Remarquez que bon nombre de netbooks n'ont actuellement pas la possibilité de repérer un emplacement car ils ne disposent pas de sans-fil cellulaire ou de GPS. Un composant est annoncé dans le catalogue de composants sur le site IADP, qui détectera l'emplacement d'un netbook à partir du GPS intégré (ou d'un autre service de localisation) ou qui ira sur le web pour essayer de déterminer cette position à partir de l'adresse IP de l'appareil. Vous pouvez également regarder du côté des produits sous licence de Skyhook Wireless qui fournissent un service Wifi de localisation utilisé dans l'iPod Touch.

Si vous souhaitez ajouter par vous-même la prise en charge de la localisation dans vos applications, vous pouvez utiliser l'API Location de Windows pour détecter les appareils à identification de position et en récupérer des données. Voici, pour vous mettre le pied à l'étrier, un fragment de code issu du composant mentionné plus haut.

C++ Windows

#include <LocationApi.h>

GeoLocation::GeoLocation() : Component(id) 
{    
    //Setup COM Location interface 
    HRESULT hr; 
    GeoCOM = false; 
    LOCATION_REPORT_STATUS locationReportStatus = REPORT_NOT_SUPPORTED;
    IID REPORTS[] = { IID_ILatLongReport, IID_ICivicAddressReport };
    hr = CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);

    if(SUCCEEDED(hr))
    {
        hr = pLocation.CoCreateInstance(CLSID_Location);

        if (SUCCEEDED(hr))
        {
            hr = pLocation->RequestPermissions(NULL, REPORTS, ARRAYSIZE(REPORTS), TRUE);
            if (SUCCEEDED(hr)) 
            {
                hr = pLocation->GetReportStatus(IID_ILatLongReport, &locationReportStatus);

                if (SUCCEEDED(hr))
                {
                    switch (locationReportStatus) 
                    {
                        case REPORT_NOT_SUPPORTED: 
                        case REPORT_ERROR:
                        case REPORT_ACCESS_DENIED:
                            GeoCOM = false; 
                        default: 
                            GeoCOM = true; 
                    }
                }
            }
        }
    }


    http = new HTTPRequest(); 
    locationData = new Location(); 

}

/** GeoLocation destructor
    frees the current location data and HTTPRequest class.  
*/ 
GeoLocation::~GeoLocation()
{

    //tear down com interface 
    CoUninitialize();

    if(locationData)
        delete locationData; 
    locationData = NULL; 
}

DWORD GeoLocation::GetCOMLocation()
{
    HRESULT hr = ERROR_SUCCESS; 

    CComPtr<ILocationReport> pLocationReport; 
    CComPtr<ILatLongReport> pLatLongReport; 
    DOUBLE dValue; 

    hr = pLocation->GetReport(IID_ILatLongReport, &pLocationReport);

    if(SUCCEEDED(hr))
    {

        if(pLocationReport)
            hr = pLocationReport->QueryInterface(&pLatLongReport);

        if(SUCCEEDED(hr))
        {
        
            hr = pLatLongReport->GetLongitude(&dValue); 
            if(SUCCEEDED(hr))
                locationData->dLongitude = dValue; 

            hr = pLatLongReport->GetLatitude(&dValue); 
            if(SUCCEEDED(hr))
                locationData->dLatitude = dValue; 

            hr = pLatLongReport->GetAltitude(&dValue); 
            if(SUCCEEDED(hr))
                locationData->dAltitude = dValue; 

            hr = pLatLongReport->GetAltitudeError(&dValue); 
            if(SUCCEEDED(hr))
                locationData->dAltitudeError = dValue; 

            hr = pLatLongReport->GetErrorRadius(&dValue); 
            if(SUCCEEDED(hr))
                locationData->dErrorRadius = dValue; 
        }
    }

    return hr; 
}

L'API Location est également accessible dans .NET et elle est beaucoup plus simple. Voici une application complète pour Windows avec .NET et WPF, qui exploite la classe GeoCoordinateWatcher pour actualiser l'affichage lorsque la position géographique change.

XAML Windows

<Window x:Class="Location.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Location" Height="238" Width="400">
    <Grid Height="177" Width="360">
        <Label Name="label1" Content="Latitude:"
               FontSize="18" FontWeight="Bold"
               Margin="12,23,0,0"
               HorizontalAlignment="Left" HorizontalContentAlignment="Right" VerticalAlignment="Top" Width="124" />

        <Label Name="label2" Content="Longitude:"
               FontSize="18" FontWeight="Bold"
               Margin="12,63,0,0"
               HorizontalAlignment="Left" HorizontalContentAlignment="Right" VerticalAlignment="Top" Width="124" />

        <Label Name="label3" Content="Status:"
               FontSize="18" FontWeight="Bold"
               Margin="12,103,0,0"
               HorizontalAlignment="Left" HorizontalContentAlignment="Right" VerticalAlignment="Top" Width="124" />

        <Label Name="labelLatitude" Content=""
               FontSize="18" FontWeight="Bold"
               Margin="142,23,0,0"
               HorizontalAlignment="Left" VerticalAlignment="Top" Width="206" />

        <Label Name="labelLongitude" Content=""
               FontSize="18" FontWeight="Bold"
               Margin="142,63,0,0"
               HorizontalAlignment="Left" VerticalAlignment="Top" Width="206" />

        <Label Name="labelStatus" Content="(starting)"
               FontSize="18" FontWeight="Bold"
               Margin="142,103,12,0" VerticalAlignment="Top" />


    </Grid>
</Window>

C# Windows

using System;
using System.Windows;
using System.Device.Location;

namespace Location
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            GeoCoordinateWatcher watcher = new GeoCoordinateWatcher();

            watcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(LocationUpdate);
            if (watcher.TryStart(false, TimeSpan.FromMilliseconds(1000)))
                labelStatus.Content = "Started";
            else
                labelStatus.Content = "Couldn't start location";
        }

        public void LocationUpdate(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
        {
            labelStatus.Content = "Got location";
            labelLatitude.Content = e.Position.Location.Latitude.ToString();
            labelLongitude.Content = e.Position.Location.Longitude.ToString();
        }
    }
}

Stockage des données

Presque toutes les applications ont besoin de conserver des données d'état entre leurs sessions. Dans certains cas, la persistance est assurée sous la forme de réglages de l'application (ex.: son activé ou désactivé) tandis que dans d'autres cas les données à conserver sont plus importantes (ex.: fichiers multimédia, état d'un jeu, données collectées). 

Données de petite taille

Référence

API iPhone

API Windows

Paramètres

Préférences

Paramètres d'application

L'iPhone fournit des préférences pour le stockage des données de configuration de petite taille, avec une API générale permettant de stocker facilement une valeur nommée pour l'utilisateur courant sur l'hôte courant.

Objective-C iPhone

@implementation SmallDataViewController
@synthesize binarySetting,stringSetting;

// Load settings from NSUserDefaults and update the display
- (void)viewDidLoad {
    NSUserDefaults *settings = [NSUserDefaults standardUserDefaults];
    [binarySetting setOn:[settings boolForKey:@"binarySetting"]];
    [stringSetting setText:[settings stringForKey:@"stringSetting"]];
    [super viewDidLoad];
}

// When the text box value is changed, update the settings and synchronize
- (IBAction) updateText:(id) sender {
    NSUserDefaults *settings = [NSUserDefaults standardUserDefaults];
    [settings setObject:stringSetting.text forKey:@"stringSetting"];
    [settings synchronize];
    [sender resignFirstResponder];
}

// When the switch is changed, update the settings and synchronize
- (IBAction) switchValueChanged:(id) sender {
    NSUserDefaults *settings = [NSUserDefaults standardUserDefaults];
    [settings setBool:binarySetting.on forKey:@"binarySetting"];
    [settings synchronize];
}

@end

De la même manière, Windows fournit une gestion générale des réglages dans les applications .NET via les paramètres d'application. Vous pouvez le faire directement dans Visual Studio via le designer ou vous pouvez choisir d'écrire vous-même le les paramètres de code comme dans l'exemple suivant.

XAML Windows

<Window x:Class="SmallData.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="SmallData" Height="182" Width="398" 
    >
    <Grid>
        <Label Name="label1" Content="Binary setting:" 
               HorizontalAlignment="Left"  VerticalAlignment="Top" Margin="25,23,0,0" 
               FontSize="16" FontWeight="Bold" />
        <Label Name="label2" Content="String setting:" 
               HorizontalAlignment="Left" VerticalAlignment="Top" Margin="27,78,0,0"
               FontSize="16" FontWeight="Bold" />
        <CheckBox Name="CBBinarySetting" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="164,32,0,0" />
        <TextBox Name="TBStringSetting" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="164,84,0,0" Width="175" />
    </Grid>
</Window>

C# Windows

using System;
using System.Configuration;

namespace SmallData
{
    class MyUserSettings : ApplicationSettingsBase
    {
        [UserScopedSetting()]
        [DefaultSettingValue("False")]
        public bool binarySetting
        {
            get { return ((bool)this["binarySetting"]); }

            set { this["binarySetting"] = (bool)value; }
        }

        [UserScopedSetting()]
        [DefaultSettingValue("")]
        public string stringSetting
        {
            get { return ((string)this["stringSetting"]); }

            set { this["stringSetting"] = (string)value; }
        }
    }
}

C# Windows

    
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace SmallData
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            MyUserSettings settings = new MyUserSettings();

            TBStringSetting.Text = settings.stringSetting;
            CBBinarySetting.IsChecked = settings.binarySetting;

            CBBinarySetting.Checked += CBBinarySetting_Changed;
            CBBinarySetting.Unchecked += CBBinarySetting_Changed;

            TBStringSetting.TextChanged += TBStringSetting_TextChanged;
        }

        private void CBBinarySetting_Changed(object sender, RoutedEventArgs e)
        {
            MyUserSettings settings = new MyUserSettings();
            settings.binarySetting = (bool)CBBinarySetting.IsChecked;
            settings.Save();
        }

        private void TBStringSetting_TextChanged(object sender, TextChangedEventArgs e)
        {
            MyUserSettings settings = new MyUserSettings();
            settings.stringSetting = TBStringSetting.Text;
            settings.Save();
        }
    }
}

Données de taille importante

Pour les données de taille importante, comme les bases de données ou les fichiers, vous pouvez les stocker localement dans le stockage dédié de l'appareil pour une application iPhone. La fonction NSSearchPathForDirectoriesInDomains permet de repérer l'emplacement de stockage afin d'utiliser ensuite des appels standard d'E/S pour la lecture et l'écriture. Si vous avez besoin d'accéder en lecture seule à un fichier ou à une base de données, vous pouvez regrouper ce fichier ou cette base de données avec l'application en tant que ressource dans Xcode.

Windows est moins restrictif sur l'emplacement où peuvent être stockés les fichiers (certaines parties du système sont en accès limité sur Windows 7). Mais de nos jours, il est considéré comme de bonne pratique de conserver les fichiers en lecture seule dans le dossier des fichiers de programme de l'application, qui est installé avec le package de l'application. Pour les fichiers inscriptibles et pour les données collectées ou générées par l'utilisateur, la pratique courante est de les stocker dans le dossier AppData de l'application. Il existe un certain nombre d'API haut niveau qui vous aideront à gérer les différents cas de stockage des données. 

Heureusement, si vous avez inclus des fichiers de données dans votre application iPhone, il y a des chances que vous les analysiez ou y accédiez d'une manière semblable dans Windows. Ainsi, une base de données SQLite pourra être lue sur l'iPhone à l'aide des interfaces C natives, lesquelles pourront être copiées directement dans votre application Windows et utilisées comme bibliothèque C native. Si vous passez à .NET, vous pouvez utiliser la bibliothèque tierce partie System.Data.SQLite pour mapper vos appels à .NET. Depuis sa version 2.6.3, le moteur SQLite sait lire les fichiers de base de données même s'ils ont été créés sur une machine ayant un endian différent.

Utilisation de services web

Référence

API iPhone

API Windows

Services HTTP

NSURLConnection

WinHTTP

HttpWebRequest

Bon nombre d'applications iPhone se connectent à des services web distants par l'intermédiaire desquels elles utilisent des données ou postent des modifications. Ces services peuvent être des services web SOAP, mais la plupart ont plutôt toutes les chances d'être des interfaces RESTful, voire de simples connexions HTTP qui retournent et acceptent des données JSON, XML, HTML ou des données structurées.

Le fragment suivant utilise la classe NSURLConnection pour lire une URL, puis le json-framework for Objective C tierce partie pour analyser la réponse.

Objective-C iPhone

- (void)viewWillAppear:(BOOL)animated {
    NSURL *url = [NSURL URLWithString:urlString];
    NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60];
    [NSURLConnection connectionWithRequest:request delegate:self]
}

...

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [_receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    
    NSString* out = [[NSString alloc] initWithData:_receivedData encoding:NSUTF8StringEncoding];
    
    if ( out ) {
        SBJSON* jsonObject = [[SBJSON alloc] init];
        NSError* error;
        NSDictionary* outputDictionary = [jsonObject objectWithString:out error:&error];
        
        // Do something with the JSON response data
    } else {
        // Give an error or warning message
    }

    
    [_receivedData release];
    _receivedData = nil;
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog([NSString stringWithFormat:@"Request failed. Reason: %@", error.localizedDescription]);

    // Give an error or warning message
    
    [_receivedData release];
    _receivedData = nil;
}

Les sites web SOAP, bien que non pris en charge en natif dans le SDK iPhone, restent utilisables dans une application iPhone, grâce à un outil tierce partie comme wsdl2objc qui génère des classes Objective C permettant d'accéder à des sites web SOAP à partir d'une définition de service WSDL.

Sous Windows natif, la bibliothèque WinHTTP permet d'accéder à un client pour se connecter à un serveur web. Comme les interactions réseau ont facilement tendance à générer des erreurs, vous voudrez certainement faire une place importante pour la capture des erreurs dans votre code, en testant les valeurs retournées et en agissant de manière appropriée et élégante pour éviter que votre utilisateur n'en pâtisse trop en cas de défaillance d'une partie de la requête HTTP. Cela dit, voici du pseudo-code pour vous aider à démarrer ; il devrait être enveloppé dans votre gestion des erreurs.

C++ Windows

void HTTPRequest::Request(const std::wstring wsUrl, 
                const std::wstring wsHeaders, 
                const std::wstring wsContent,
                std::string &sOutBuffer)
{ 
    WinHttpCrackUrl( wsUrl.c_str(), wsUrl.length(), 0, &urlComponents );
    Url.assign(urlComponents.lpszHostName, urlComponents.dwHostNameLength);

    hConnect = WinHttpConnect( hSession, Url.c_str(), urlComponents.nPort, 0 );

    hRequest = WinHttpOpenRequest(
        hConnect, 
        "GET", 
        urlComponents.lpszUrlPath, 
        HTTP_VERSION.c_str(),
        WINHTTP_NO_REFERER, 
        WINHTTP_DEFAULT_ACCEPT_TYPES,
        dwOpenFlags);

    WinHttpSendRequest( hRequest, 
        wsHeaders.c_str(), 
        wsHeaders.length(),    
        WINHTTP_NO_REQUEST_DATA, 
        0, 
        0,
        0 ); 

    WinHttpReceiveResponse( hRequest, NULL );

    GetResponseData( hRequest, sOutBuffer); 
         
    if(hRequest)
        WinHttpCloseHandle(hRequest); 
    hRequest = NULL; 
    if(hConnect)
        WinHttpCloseHandle(hConnect); 
    hConnect = NULL; 
}

DWORD HTTPRequest::GetResponseData( HINTERNET hRequest, string &data) 
{
    DWORD dwSize; 
    DWORD dwStatus = ERROR_SUCCESS; 
    DWORD dwDownloaded;
    LPSTR pszOutBuffer;
        
    dwSize = 0;
    do {
        WinHttpQueryDataAvailable( hRequest, &dwSize); 
        pszOutBuffer = new char[dwSize+1];
        ZeroMemory(pszOutBuffer, dwSize + 1);
        WinHttpReadData( hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded);

        if(pszOutBuffer)
        {
            data.append(pszOutBuffer);
            delete [] pszOutBuffer;
            pszOutBuffer = NULL;
        }
    } while (dwSize > 0);

    
    if(pszOutBuffer)
        delete [] pszOutBuffer; 
    pszOutBuffer = NULL; 

    return dwStatus; 
}

Sous .NET, les choses sont beaucoup plus simples grâce à la classe HttpWebRequest.

C# Windows

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(urlString);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Encoding encoding = Encoding.GetEncoding(1252);  // Windows default Code Page
StreamReader reader = new StreamReader(response.GetResponseStream(),encoding);

string data = reader.ReadToEnd();

response.Close();
reader.Close();

Vous remarquerez que, dans le cas de C++ sur Windows natif, le mécanisme HTTP est synchrone. L'appel de méthode bloque la poursuite du code jusqu'à la réception de la réponse ou jusqu'à l'expiration du délai. Alors que l'exemple .NET ci-dessus utilise une approche synchrone, la classe fournit une demande/réponse asynchrone. Sous iPhone, l'appel est asynchrone, vous n'avez donc pas à vous préoccuper du threading. Le souci des performances et de la réactivité impose de toujours utiliser des appels asynchrones ou de placer les demandes réseau dans un thread distinct (pour qu'elles ne bloquent pas l'unité d'exécution de l'interface utilisateur). C'est ce que nous allons voir dans la prochaine section.

Threading et simultanéité

Référence

API iPhone

API Windows

Threading

Threads traditionnels

Simultanéité renforcée

AfxBeginThread

ThreadStart

Un grand nombre des tâches qu'expose la bibliothèque iPhone et dont l'exécution prend du temps sont asynchrones. Vous devez déclarer un délégué, lancer un événement et attendre la réponse. C'est donc à l'API de gérer le threading ou la simultanéité, ce qui est synonyme de programmation plus rapide et de code plus stable. Il en découle que la plupart des développeurs iPhone n'ont pas à se soucier du threading, même si Apple prend en charge aussi bien les threads classiques que les options améliorées (et recommandées) comme les objets d'opération et GCD (Grand Central Dispatch) sur l'iPhone.

Lorsque vous faites passer vos applications sur Windows, vous risquez d'avoir à mettre en œuvre vos propres threads pour gérer de manière asynchrone un certain nombre d'événements. Ainsi, une requête HTTP devra être gérée dans un thread distinct pour éviter le blocage de l'interface utilisateur. La fonction AfxBeginThread permet cela à partir d'une application MFC.

C++ Windows

typedef struct THREADINFOSTRUCT {
    HWND hWnd;
    bool bPush;
    std::wstring strUrl;
    int nApi;
} THREADINFOSTRUCT;

UINT __cdecl FnThreadedHttpRequest(LPVOID lParam) {
    THREADINFOSTRUCT* tis = (THREADINFOSTRUCT*)lParam;
    // Make the HTTP request and handle response
}

...

void App::CallApi(url) {
    THREADINFOSTRUCT* tis = new THREADINFOSTRUCT;
    tis->hWnd = hWnd;
    tis->bPush = false;
    tis->strUrl = url;
    tis->nApi = API_LOGIN;

    m_pHttpThread = AfxBeginThread(FnThreadedHttpRequest, tis, THREAD_PRIORITY_NORMAL, 0, 0);
}

Dans le cas d'une application .NET, cela est également géré de manière simple : entre autres approches, via le délégué ThreadStart.

C# Windows

static void CallApi(string url) 
{
    ThreadedHttpRequest worker = new ThreadedHttpRequest();
    worker.Push = false;
    worker.Url = url;
    worker.Api = API_LOGIN;

    ThreadStart threadDelegate = new ThreadStart(worker.DoRequest());    
    Thread thread = new Thread(threadDelegate);
    thread.Start();
}

class ThreadedHttpRequest
{
    public boolean Push; 
    public string Url;
    public int Api;

    public void DoRequest() {
        // Make Http request and handle response
    }
}

Ne perdez pas de vue que la programmation multi-thread est compliquée. Lorsque plusieurs threads accèdent à la même portion de code ou modifient des données partagées, vous pouvez vous retrouver avec des conditions de concurrence critique et des problèmes de simultanéité. Le threading est un outil puissant permettant d'améliorer la réactivité et l'efficacité de vos applications tant que vous synchronisez votre code avec soins. N'hésitez pas à utiliser les interfaces asynchrones chaque fois qu'elles vous sont fournies ; la sécurité de vos threads ne s'en portera que mieux.

Liste de contacts

Référence

API iPhone

API Windows

Carnet d'adresses

ABPeoplePickerNavigationController

API Windows Live Contacts

L'iPhone de vos clients comporte un carnet d'adresses qui stocke tous leurs contacts. Si votre application iPhone comprend des fonctionnalités de réseaux sociaux, vous avez certainement été amené à utiliser ce carnet d'adresses. Ce dernier, et les services qui vont avec, permettent d'exploiter les informations sur les contacts pour envoyer des mails et des SMS et pour partager d'autres informations. 

Par exemple, pour obtenir le nom du contact sélectionné, vous utiliserez la classe ABPeoplePickerNavigationController et vous mettrez en œuvre le protocole ABPeoplePickerNavigationControllerDelegate dans votre classe.

Objective-C iPhone

@interface AddressBookQuickStartViewController : UIViewController <ABPeoplePickerNavigationControllerDelegate> {
    IBOutlet UILabel *firstName;
    IBOutlet UILabel *lastName;
}

@property (nonatomic, retain) UILabel *firstName;
@property (nonatomic, retain) UILabel *lastName;


- (IBAction) showPicker:(id) sender;

@end

@implementation AddressBookQuickStartViewController

@synthesize firstName;
@synthesize lastName;

-(IBAction) showPicker: (id) sender {
    ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc] init];
    picker.peoplePickerDelegate = self;
    
    [self presentModalViewController:picker animated:YES];
    [picker    release];
}


 
- (BOOL)peoplePickerNavigationController:
            (ABPeoplePickerNavigationController *)peoplePicker
      shouldContinueAfterSelectingPerson:(ABRecordRef)person {
 
    NSString* name = (NSString *)ABRecordCopyValue(person,
                                             kABPersonFirstNameProperty);
    self.firstName.text = name;
    [name release];
 
    name = (NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);
    self.lastName.text = name;
    [name release];
 
    [self dismissModalViewControllerAnimated:YES];
 
    return NO;
}

Voir l'Address Book UI Framework pour plus de détails. Pour accéder directement à la liste des contacts, vous pouvez utiliser l'Address Book Framework.

Pour vos applications Windows, vous pouvez utiliser l'API Windows Contacts, mais cette dernière est à éviter dans Windows 7, où elle est apparemment remplacée par l'API Windows Live Contacts. Vous aurez besoin d'une connexion Internet active pour pouvoir accéder au carnet d'adresses Live Contacts. L'API utilise une interface REST échangeant des fichiers XML. Vous trouverez sur MSDN un exemple d'application détaillant cela.

Caméra

Référence

API iPhone

API Windows

Caméra

UIImagePickerController

DirectShow

La plupart des netbooks intègrent une caméra. C'est une bonne nouvelle si vous avez décidé de porter sur Windows une application iPhone qui utilise la caméra. Mais, sur les netbooks, la caméra fait face aux utilisateurs (dans les spécifications du produit, elle est souvent mentionnée comme webcam). Il n'en va pas de même avec la caméra des iPhones (à ce jour, l'iPhone 3G et l'iPhone 3GS) qui, elle, est à l'arrière de l'appareil. En d'autres termes, vous devez réfléchir soigneusement à l'emploi que fera l'utilisateur de votre application sur la nouvelle plate-forme. Par exemple, il est possible de scanner un code-barres avec la caméra face arrière d'un iPhone en tenant le téléphone au-dessus du code-barres. Avec un netbook, c'est le code-barres que vous devrez tenir au-dessus de la caméra. La caméra d'un netbook peut, néanmoins, servir à de la vidéoconférence ou à une application dans laquelle l'utilisateur prend une image de lui-même. Ce n'est pas possible avec la génération 3G d'iPhones.

L'iPhone permet de capturer des images avec la caméra et de capturer de la vidéo (dans ses versions 3.0 et ultérieures) grâce à la classe UIImagePickerController avec mise en œuvre d'un protocole UIImagePickerControllerDelegate dans votre classe.

Objective-C iPhone

-(IBAction) recordVideo:(id) sender {
    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
        UIImagePickerController * picker = [[UIImagePickerController alloc] init];
        picker.delegate = self;
        picker.mediaTypes = [NSArray arrayWithObject:(NSString *)kUTTypeMovie];
        picker.sourceType = UIImagePickerControllerSourceTypeCamera;
        [self presentModalViewController:picker animated:YES]
    }
}

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
    [picker dismissModalViewControllerAnimated:YES];
    movieFileUrl = [info objectForKey:@"UIImagePickerControllerMediaURL"];
}

Sous Windows, la bibliothèque DirectShow permet de capturer du contenu vidéo à partir de la caméra et de manipuler ce contenu dans le processus. Vous devez commencer par créer une capture graphique qui vous permet de configurer le flux de fichiers et de démarrer le processus de rendu vidéo.

C++ Windows

IBaseFilter *pMux;
hr = pBuild->SetOutputFileName(&MEDIASUBTYPE_Avi, L"C:\\Example.avi", &pMux, NULL);

hr = pBuild->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pCap, NULL,  pMux);

pMux->Release();

Le sujet est abordé plus en profondeur dans la section Video Capture de la documentation DirectShow sur MSDN.

Lecture multimédia

Le multimédia, vidéo ou son, est désormais très répandu sur les terminaux de poche où il est accessible sur les réseaux 3G. Sur l'iPhone, il est possible de lire de la vidéo et du son à partir d'un fichier ou en diffusion en continu, et l'iPhone dispose d'interfaces générales permettant ces options. Lors du transfert de votre application sur Windows, vous avez un certain nombre de choix à opérer pour la lecture de fichiers multimédia, allant d'un lecteur intégré de haute qualité jusqu'à un accès de basse qualité aux flux, ce qui vous permet de modifier l'expérience de vos utilisateurs. Selon vos besoins, vous devrez envisager quelle technologie choisir, si la diffusion en continu ou la lecture de fichiers sont essentiels dans votre application. WPF vous permet d'incorporer rapidement et immédiatement Windows Media Player dans votre application. Sous C++, vous pouvez également incorporer Windows Media Player avec un peu de génération de code automatique mais vous disposez de contrôle plus fins.

Lire de la vidéo

Référence

API iPhone

API Windows

Lecture de vidéo

UIImagePickerController

MediaElement

L'un des côtés les plus sympathiques du multimédia sur l'iPhone est qu'il est simple comme bonjour de lancer et de lire une vidéo.

Objective-C iPhone

MPMoviePlayerController *mp = [[MPMoviePlayerController alloc] initWithContentURL:movieURL];
if (mp)
{
    // save the movie player object
    self.moviePlayer = mp;
    [mp release];
        
    // Apply the user specified settings to the movie player object
    [self setMoviePlayerUserSettings];
        
    // Play the movie!
    [self.moviePlayer play];
}

Si vous avez la possibilité de développer votre application dans C# à l'aide de WPF, vous pouvez facilement ajouter un MediaElement à votre layout et définir l'URL de la source multimédia dans le XAML ou dans le code sous-jacent (comme le montre l'exemple qui suit).

XAML Windows

1
2
<MediaElement Name="myMediaElement" Width="450" Height="250" LoadedBehavior="Manual" UnloadedBehavior="Stop" Stretch="Fill"
MediaOpened="Element_MediaOpened" MediaEnded="Element_MediaEnded"/>

C# Windows

private void PlayContentUrl( Uri movieURL )
{
    myMediaElement.Source = movieURL;
    myMediaElement.Play();
}

Il n'en va pas tout à fait de même sous Windows, et vous devez prendre cela en compte dans votre conception. Pour l'iPhone OS 3.1 et versions antérieures, le MPMoviePlayerController se lance par défaut en mode plein écran. Avec le MediaElement dans WPF, le lecteur est incorporé dans votre application, en étant encadré par vos contrôles.

Si votre application Windows est écrite en C++, vous pouvez incorporer un contrôle ActiveX Windows Media Player dans votre application et le contrôler, mais cela ne se fait pas en une seule étape. Vous pourrez lire un résumé de chacune de ces étapes dans l'article MSDN « Hosting the Windows Media Player Control in a Windows Application ».

Lire du son

Référence

API iPhone

API Windows

Lecture audio

Infrastructure AV Foundation

SoundPlayer

PlaySound

Si votre application iPhone incorpore du son, vous êtes déjà familier avec toute la gamme d'outils utilisables sur l'iPhone pour exploiter du contenu audio. Même si le Media Player Framework vous permet de lire du contenu provenant de la bibliothèque iPod de l'utilisateur, l'ajout de sons à votre application peut être aussi simple que l'AV Foundation framework.

Objective-C iPhone

self._player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];
if (self._player)
{
    [self._player play]
}

Si vous voulez lire des fichiers ou des flux de son dans votre application Windows WPF, vous pouvez incorporer le MediaElement, de la même manière que nous l'avons vu ci-dessus pour la vidéo, afin d'afficher un lecteur multimédia. Si vous voulez lire des sons directement dans votre application .NET sans éléments visuels, vous pouvez utiliser la classe SoundPlayer si vos fichiers sont au format .wav.

C# Windows

private void playSound() {
    SoundPlayer player = new SoundPlayer();
    player.SoundsLocation = pathToSoundFile;
    player.Play();
}

Pour les autres formats de fichiers, dans WPF vous devrez incorporer Windows Media Player, chose possible grâce à la classe MediaPlayer. Ne pas confondre avec MediaElement car ce dernier est sans représentation visuelle. Cela dit, l'API est similaire.

Si vous écrivez votre application Windows en C++ , vous pouvez incorporer Windows Media Player (voir la discussion ci-dessus sur la vidéo), ce qui vous permet d'avoir un élément visuel pour la lecture du son. La fonction PlaySound vous permet d'utiliser un son d'arrière-fond.

C++ Windows

#include <msystem.h>

void MyClass::playSound() {
    PlaySound(fileUrl, NULL, SND_FILENAME | SND_ASYNC);
}

Comme cela requiert un son pouvant être lu par le périphérique wave-form du netbook, vous aurez tout intérêt à utiliser des fichiers .wav.

0