Portierung vom iPhone auf das Netbook: Über die Grundlagen hinaus

Über die Grundlagen hinaus

Die Entwicklung für das iPhone ist rapide angestiegen, zum Teil aufgrund des App Store, aber auch, weil das Gerät über mehrere spezialisierte Funktionen verfügt und aufgrund der anspruchsvollen Schnittstellen, die Apple zur Verfügung stellt. Mit zunehmender Beliebtheit von Netbooks wird ein großer Teil der Hardware, die bisher den Smartphones vorenthalten war, auch für Netbooks verfügbar. Heutzutage verfügt praktisch jedes verkaufte Netbook über eine Kamera, was vor einigen Jahren bei Windows PCs eine Seltenheit war.

Nachfolgend wird näher auf die Hardware-Unterschiede zwischen dem iPhone und Netbooks eingegangen. Sie sehen auch Code-Beispiele zum Zugang auf diese Funktionen auf den einzelnen Plattformen (wo und falls diese verfügbar sind). Zusätzlich zu diesem Artikel gibt es in der ADP-Artikelsammlung mehrere gute Ressourcen, in denen die weitergehenden Methoden zur Portierung beschrieben werden. In seinem Interview im ADP-Artikel „Von iPhone zu AppUp: Portierung von „Smiles““ erläutert Mike Kasprzack, CEO von Syhkronics, wie er seine iPhone Spiele auf AppUp übertragen hat, und er geht auf die Hardware-Unterschiede ein.

In der Apple iPhone OS Reference Library und der Microsoft MSDN Library finden Sie hervorragendes Referenzmaterial, Tutorials und Community-Diskussionen zu den einzelnen Plattformen.



Bildschirmausrichtung und Beschleunigungsmesser

Kurzübersicht

iPhone API

Windows API

Bildschirmausrichtung

didRotateFromInterfaceOrientation

DisplaySettingsChanged

Beschleunigungssensor

UIAccelerometer

Windows Sensor API

Das iPhone nutzt den integrierten Beschleunigungsmesser, um Ausrichtungsänderungen zu erkennen. Die iPhone API bietet zwei Methoden, um Ausrichtungsänderungen zuzulassen und darauf zu reagieren. shouldAutorotateToInterfaceOrientation teilt dem Framework mit, ob es die Benutzeroberfläche automatisch drehen soll, sobald erkannt wird, dass das Gerät gedreht wird. Um die Drehung zuzulassen, gibt das iPhone SDK didRotateFromInterfaceOrientation aus, damit Ihre App auf die Ausrichtungsänderung reagieren kann.

iPhone: Objective-C

- (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];
}

Laut Kasprzack: „Die Festlegung des Spiels auf eine einzige Ausrichtung auf dem PC ist die gängige Lösung.“ Einige Netbooks (insbesondere Tablet-PCs) unterstützen Ausrichtungsänderungen des Bildschirms und teilen dem System eine Drehung des Bildschirms mit. 

Sie können die Bildschirmdrehung erkennen, indem Sie dem Systemereignis DisplaySettingsChanged einen Ereignishandler in C# (oder einen anderen verwalteten Code) zuweisen, wie in der nachstehenden Windows Beispielanwendung Presentation Foundation (WPF) dargestellt.

Windows: XAML

<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>

Windows: C#

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;
        }
    }

}

In C++ für native Anwendungen warten Sie auf die Nachricht WM_DISPLAYCHANGE und reagieren darauf.

Windows: C++

#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;

Genauere Informationen, einschließlich Beispielcode, finden Sie im MSDN-Artikel „Detecting Screen Orientation and Screen Rotation in Tablet PC Applications.“

Falls Ihre Anwendung einen Beschleunigungsmesser als Input für beispielsweise ein Spiel verwendet, ist sie möglicherweise nicht für die Portierung auf ein Netbook geeignet, da ein Beschleunigungsmesser nur auf wenigen Netbooks integriert ist. Bei den Mitte bis Ende 2010 auf den Markt kommenden Tablet-PCs sind Beschleunigungsmesser jedoch weiter verbreitet. 

Auf dem iPhone können Sie Beschleunigungsmesser-Ereignisse abrufen, indem Sie eine Registrierung mit der UIAccelerometer-Klasse als ein Delegat vornehmen und die Ereignisse einsetzen.

iPhone: Objective-C

-(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
}

Windows 7 unterstützt Beschleunigungsmesser als eine Art „Sensor“ über Sensor API, eine COM-Schnittstelle, auf die über nativen C++ Code zugegriffen wird. Um auf Sensoren über verwalteten Code zuzugreifen, müssen Sie das Windows API Code Pack von MSDN holen. Im Gegensatz zum iPhone bietet Windows keine anspruchsvolle API, die Beschleunigungsmesserdaten aus bestimmten Geräten erfasst. Derzeit sind Beschleunigungsmesser vor allem in Handheld-Geräten integriert, und Windows Mobile unterstützt zwei Geräte von HTC und Samsung, jeweils über direkte Hardwareschnittstellen.1

Um Beschleunigungsmesser auf Tablet-PCs oder Netbooks zu unterstützen, müssen Sie die von den einzelnen Herstellern veröffentlichten SDKs oder Schnittstellen untersuchen.

[1] Weitere Informationen und Quellcode finden Sie in der inoffiziellen Windows Mobile Unified Sensor API von Koushik Dutta.

Vom Touchscreen zur Maus

Außer wenn Sie Gestik verwenden, verhalten sich Bewegungen der Maus im Großen und Ganzen wie Touchscreens, mit dem Vorbehalt, dass Mausaktionen ein Hover-Ereignis liefern, das bei Touchscreens nicht zu finden ist. Die Portierung von einer Touchscreen-Interaktion auf den Einsatz einer Maus sollte also einfach sein. Angesichts der Erwartungen von Windows Benutzern sollten Sie Hover-Effekte in Betracht ziehen, falls Sie angepasste Benutzeroberflächen-Komponenten haben (wie das bei Spielen oft der Fall ist). Wie Hyperlinks in Web-Browsern liefern Windows-Steuerungen heutzutage visuelles Feedback bei einem Hover-Ereignis, um den Benutzer darauf aufmerksam zu machen, dass das Element bei einem Klick-Ereignis reagieren wird. 

Falls Ihre Anwendung Gestik unterstützt oder auf einigen der iPhone-spezifischen Gestiken aufbaut, müssen Sie es sich überlegen, wie Sie dies für Ihre Windows Anwendung umarbeiten können. 

Windows Touch

Tablet-PCs und Touchscreen-Notebooks unterstützen oft Multi-Touch-Bewegungen, und Windows 7 Touch verfügt über integrierte Unterstützung für Ziehen, Bildlauf, Zoom, Durchblättern, Drehen und vieles mehr. Der Artikel „Getting Started with Windows Touch Gestures“ liefert eine gute Diskussion zu diesem Thema. Für Geräte ohne Touchscreen finden Sie nachfolgend einige Ansätze zur Behandlung dieser Ereignisse.

Bildlauf

Kurzübersicht

iPhone API

Windows API

Bildlauf

UIScrollView

CWnd::OnMouseWheel

Control.OnMouseWheel

Die Bildlaufbewegung auf dem iPhone funktioniert ähnlich wie die Mausradbewegung in Windows Anwendungen, Bildlaufleisten und allen Steuerungen mit Bildlaufinhalt. In einer angepassten Benutzeroberfläche müssen Sie möglicherweise das Mausradereignis erfassen, um dies zu replizieren. So reagiert Google Maps in einem Web-Browser beispielsweise auf Mausradbewegungen, um einen Kartenbereich vergrößert oder verkleinert darzustellen. In einer iPhone Anwendung fällt das Bildlaufproblem unter Umständen weg, weil dies von Benutzeroberflächenkomponenten übernommen wird oder Sie eine UIScrollView-Klasse implementiert haben.

Beachten Sie, dass es keine Entsprechung für das Durchblättern-Verhalten in standardmäßigen Windows-Steuerungen gibt. Sie können dieses Verhalten jedoch als einen normalen „Nach oben“- und „Nach unten“-Aufruf auf einem Bildleistenereignis interpretieren, oder Sie können es aus der Beschleunigung des Mausrad-Bewegungsereignisses erschließen. Sie können das Ereignis erfassen, indem Sie die CWnd::OnMouseWheel-Methode in C++/MFC oder die Control.OnMouseWheel-Methode in .NET aufheben.

Windows: C++

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);   
}

Windows: C#

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

Da auf Geräten ohne Touchscreen keine native Unterstützung für das Vergrößern und Verkleinern durch Berühren mit zwei Fingern besteht, müssen Sie entscheiden, ob Sie diese Aktion in Ihrer Anwendung benötigen. Sie könnten eine ähnliche Zoom-Funktion möglicherweise durch Hinzufügen von Steuerungen auf der Benutzeroberfläche anbieten. Viele Windows Anwendungen enthalten ein Menü „Ansicht“ mit Menüoptionen für „Vergrößern“, „Verkleinern“ und „Normale Größe“. Oft sind diese den Tastenkombinationen Strg +, Strg - und Strg 0 zugeordnet. Windows 7 verfügt über die globalen Tastenkombinationen Windows + und Windows -, die Zoomfunktionen für das gesamte Display bieten. 

Drehen

Die vom iPhone erkannte Drehbewegung mit zwei Fingern wird auf Windows Geräten ohne Touchscreen ebenfalls nicht unterstützt. Windows Anwendungen umfassen jedoch auch in diesem Fall ein Menü „Ansicht“ mit den Menüoptionen „Rechtsdrehung“ (oder „Im Uhrzeigersinn drehen“) oder „Linksdrehung“ (oder „Gegen den Uhrzeigersinn drehen“).

Ziehen

Kurzübersicht

iPhone API

Windows API

Ziehen und Ablegen

touchesMoved

DragDrop

Das Ziehen und Ablegen wird zwar nicht nativ auf dem iPhone unterstützt (Sie müssen dies über die touchesMoved-Methode implementieren), Ziehen-Ereignisse werden jedoch in Windows APIs solide unterstützt und sind gut dokumentiert. Es ist ein 30-minütiges Video auf MSDN verfügbar, in dem die Implementierung der Drag&Drop-Funktion in Ihrer MFC-Anwendung erklärt wird. Lesen Sie für .NET den MSDN-Artikel „Performing Drag-and-Drop Operations on Windows“, der den folgenden Beispielcode liefert:

Windows: C#

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();
}

Skalieren von Grafiken

Der Bildschirm eines Netbooks ist größer als der eines iPhone. Bildschirmgrößen von Netbooks sind unterschiedlich, die meisten liegen jedoch bei 1024 x 600 Pixel. Um sich diese größere Bildschirmauflösung und verschiedene Bildschirmauflösungen zunutze zu machen, müssen Sie möglicherweise die Größe und den Standort von Objekten je nach Fenster- oder Bildschirmgröße anpassen. Wenn Sie WPF für Ihre Windows-Anwendung verwenden, können Objekte relativ zueinander automatisch gefunden und zur Anpassung an das Fenster skaliert werden. Für Anwendungen mit vielen Grafiken (wie Spielen) schlägt Kasprzack in seinem Interview vor, die Grafiken zu skalieren, indem Sie mit einer Referenzauflösung beginnen, die größer als alle Ihre Zielauflösungen ist. Indem Sie Ihre Bilder in dieser Größe erstellen, können Sie die Grafiken für jede Plattform entsprechend verkleinern. Zudem kann jede Platzierung oder Bewegung von Objekten (wie in Spielen) von der Referenzauflösung aus für jede Geräteauflösung skaliert werden.

Standortbestimmung/GPS

Kurzübersicht

iPhone API

Windows 7 API

Standortbezogene Dienste

Core Location Framework

Location-API

Für iPhone Apps stellt Apple das Core Location Framework bereit, über das Entwickler die verfügbare Hardware nutzen können, um den Benutzer ausfindig zu machen und Standortänderungen des Benutzers zu verfolgen, ohne Berücksichtigung der eingesetzten Hardware. Die Beispielanwendung „LocateMe“ in der iPhone Referenzbibliothek bietet ein Beispiel für das Erfassen des aktuellen Standorts und der Benachrichtigung bei Standortänderungen. 

Für einen einzigen Standort auf dem iPhone erstellen Sie einen Controller oder einen Delegat, der Folgendes implementiert: CLLocationManagerDelegate

iPhone: Objective-C

#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

Dann definieren Sie die Methoden zum Starten des Standortbestimmungsservices, sammeln die Ergebnisse und bearbeiten mögliche Fehler.

@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

Mit der Windows 7 Plattform für die Ortung ist dies auf standortaktivierten Netbooks jetzt fast genauso einfach. Beachten Sie dabei, dass viele Netbooks derzeit keine Ortung durchführen können, weil sie nicht über eine drahtlose Verbindung per Mobilfunk oder GPS verfügen. Im Komponentenkatalog auf der ADP-Site wird bald eine Komponente verfügbar sein, die den Standort des Netbooks basierend auf einem integrierten GPS (oder einem anderen Ortungsservice) finden kann. Der Standort kann auch über das Internet basierend auf der IP-Adresse des Geräts gefunden werden. Eine weitere Möglichkeit sind Lizenzierungsprodukte von Skyhook Wireless, einem Unternehmen, das einen WiFi-basierten Ortungsservice anbietet, der auch für den iPod Touch verwendet wird.

Falls Sie Ihren Anwendungen selbst Ortungsunterstützung hinzufügen möchten, können Sie die Windows Ortungs-API nutzen, um Standortidentifizierungsgeräte zu erkennen und Daten von ihnen zu erfassen. Nachstehend sehen Sie zum Einstieg ein Codefragment aus der oben erwähnten Komponente.

Windows: C++

#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; 
}

Auf die Ortungs-API kann auch über .NET zugegriffen werden, was viel einfacher ist. Nachstehend sehen Sie eine vollständige Anwendung unter Einsatz von .NET und WPF, die die GeoCoordinateWatcher Klasse einsetzt, um das Display bei Standortänderungen zu aktualisieren.

Windows: XAML

<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>

Windows: C#

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();
        }
    }
}

Datenspeicherung

Fast alle Anwendungen müssen zwischen Sitzungen Stati beibehalten. Ein Teil davon ist in den Anwendungseinstellungen gespeichert (z. B. Sound ein/aus), während andere Datenspeicherungen möglicherweise größer sind (z. B. Mediendateien, Game-Spielstatus, gesammelte Daten). 

Geringe Datenmengen

Kurzübersicht

iPhone API

Windows API

Einstellungen

Voreinstellungen

Anwendungseinstellungen

Das iPhone bietet Voreinstellungen zum Speichern der weniger umfangreichen Konfigurationsdaten. Dazu gehört eine anspruchsvolle API, mit der ein einfacher benannter Wert für den aktuellen Benutzer auf dem aktuellen Host problemlos gespeichert werden kann.

iPhone: Objective-C

@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

Windows bietet eine ähnliche anspruchsvolle Verwaltung der Einstellungen in .NET-Anwendungen über die Anwendungseinstellungen. Sie können dies direkt in Visual Studio über den Designer vornehmen oder wie im folgenden Beispiel dargestellt Einstellungen selber schreiben und kodieren.

Windows: XAML

<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>

Windows: C#

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; }
        }
    }
}

Windows: C#

    
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();
        }
    }
}

Größere Datenmengen

Sie können größere Datenmengen wie Datenbanken oder Dateien lokal im iPhone im für eine iPhone App dedizierten Speicher ablegen. Mit der NSSearchPathForDirectoriesInDomains-Funktion können Sie den Speicherort ausfindig machen und dann standardmäßige I/O-Aufrufe zum Lesen und Schreiben verwenden. Falls Sie schreibgeschützten Zugang zu einer Datei oder Datenbank benötigen, können Sie dies mit der Anwendung als eine Ressource in Xcode bündeln.

Windows verfügt über weniger Einschränkungen zum Speicherort von Dateien (für einige Teile des Systems gilt unter Windows 7 beschränkter Zugriff). Es gilt heutzutage jedoch als bewährte Verfahrensweise, schreibgeschützte Dateien, die mit dem Installationspaket installiert werden, im Programmdateien-Ordner der Anwendung aufzubewahren. Beschreibbare Dateien und vom Benutzer erfasste oder generierte Daten werden üblicherweise im AppData-Ordner der Anwendung gespeichert. Es gibt zahlreiche anspruchsvolle APIs, die Sie bei der Verwaltung verschiedener Fälle der Datenspeicherung unterstützen können. 

Falls Sie Datendateien in Ihre iPhone App aufgenommen haben, dann erfolgt die Analyse und der Zugriff auf die Dateien unter Windows auf ähnliche Art. Eine SQLite-Datenbank würde beispielsweise mit den nativen C-Schnittstellen auf dem iPhone gelesen und kann dann direkt in Ihre Windows App kopiert und als native C-Bibliothek eingesetzt werden. Wenn Sie auf .NET portieren, können Sie die System.Data.SQLite Drittpartei-Bibliothek verwenden, um Ihre Aufrufe an .NET abzubilden. Ab Version 2.6.3 kann die SQLite Engine die Datenbankdateien selbst lesen, wenn Sie auf einem anderen Rechner mit einer anderen Endian erstellt wurden.

Einsatz von Webdiensten

Kurzübersicht

iPhone API

Windows API

HTTP-Services

NSURLConnection

WinHTTP

HttpWebRequest

Viele iPhone Apps stellen eine Verbindung über Remote-Webdienste her und verwenden Daten oder veröffentlichen Updates über diese Dienste. Diese Dienste können auf SOAP basierende Webdienste sein, sind aber mit größerer Wahrscheinlichkeit REST-Schnittstellen oder oft nur HTTP-Verbindungen, die JSON, XML, HTML oder strukturierte Daten zurückgeben und akzeptieren.

Das folgende Codefragment verwendet die NSURLConnection-Klasse, um eine URL zu lesen, und benutzt dann das Drittpartei-json-Framework für Objective-C für die Analyse der Antwort.

iPhone: Objective-C

- (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;
}

Auf SOAP basierende Webdienste werden zwar nicht nativ im iPhone SDK unterstützt, sie können jedoch in einer iPhone App verwendet werden unter Einsatz eines Drittpartei-Tools wie wsdl2objc, das Objective C-Klassen generiert zum Zugriff auf SOAP-basierte Webdienste von einer WSDL-Dienstdefinition aus.

Unter Windows (nativ) liefert die WinHTTP-Bibliothek Zugriff auf einen Client zur Verbindung mit einem Webserver. Da Netzwerkinteraktionen fehleranfällig sind, lohnt es sich, genügend Zeit für die Fehlererfassung, das Testen aller Rückgabewerte und die Integration entsprechender Handlungen einzuplanen, damit das Benutzererlebnis nicht zu stark gestört wird, falls ein Teil der HTTP-Anforderung fehlschlägt. Nachstehend sehen Sie einen Pseudocode zum Einstieg, der von der Fehlerbehandlung eingeschlossen werden sollte.

Windows: C++

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; 
}

Unter .NET ist dies alles viel einfacher mit der HttpWebRequest-Klasse.

Windows: C#

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();

Beachten Sie, dass im Fall von nativem Windows C++ der HTTP-Mechanismus synchron ist. Der Methodenaufruf blockiert die Fortsetzung des Codes, bis eine Antwort empfangen wird oder die Zeitüberschreitung abgelaufen ist. Das oben stehende .NET-Beispiel verwendet zwar auch einen synchronen Ansatz, die Klasse macht jedoch die asynchrone Anfrage/Antwort möglich. Beim iPhone ist der Aufruf asynchron, Threading ist also kein Problem. Für bessere und schnellere Leistung sollten Sie immer asynchrone Aufrufe verwenden oder Netzwerkanfragen in einen separaten Thread setzen (damit der Benutzeroberflächen-Thread nicht blockiert wird). Dies wird im nächsten Abschnitt behandelt.

Threading und Parallelität

Kurzübersicht

iPhone API

Windows API

Threading

Traditionelle Threads

Verbesserte Parallelität

AfxBeginThread

ThreadStart

Viele der Aufgaben mit langer Ausführungsdauer in der iPhone Bibliothek sind asynchron. Sie ernennen einen Delegat, initiieren ein Ereignis und warten auf die Antwort. Das Threading oder die Parallelität werden an die API übergeben, wodurch ein erheblicher Programmieraufwand für die Entwickler entfällt und ein stabilerer Code sichergestellt wird. Die meisten iPhone Entwickler müssen sich daher nicht mit Threading-Problemen befassen, obwohl Apple sowohl traditionelle Threads und verbesserte (und empfohlene) Optionen unterstützt, wie Vorgangsobjekte und Grand Central Dispatch (GCD) auf dem iPhone.

Bei der Portierung Ihrer Anwendung auf Windows müssen Sie möglicherweise Ihre eigenen Threads implementieren, damit einige Ereignisse asynchron behandelt werden. Eine HTTP-Anfrage sollte z. B. auf einem separaten Thread ausgeführt werden, damit die Benutzeroberfläche weiterhin reaktionsfähig ist. Sie können dies von einer MFC-Anwendung aus mit der AfxBeginThread-Funktion starten.

Windows: C++

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);
}

Falls Sie eine .NET-Anwendung erstellen, wird dies ebenfalls einfach über den ThreadStart-Delegat durchgeführt, eines von mehreren verfügbaren Verfahren.

Windows: C#

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
    }
}

Denken Sie daran, dass die Multithread-Programmierung eine komplizierte Angelegenheit ist. Wenn mehrere Threads auf den gleichen Codeteil zugreifen oder Änderungen an gemeinsam verwendeten Daten vornehmen, können Race-Bedingungen und Parallelitätsprobleme auftreten. Threading ist ein leistungsfähiges Tool zur Verbesserung der Reaktionsfähigkeit und der Effizienz Ihres Computers, solange Sie Ihren Code sorgfältig synchronisieren. Falls asynchrone Oberflächen zur Verfügung stehen, nutzen Sie diese zur Verbesserung der Thread-Sicherheit.

Kontaktliste

Kurzübersicht

iPhone API

Windows API

Adressbuch

ABPeoplePickerNavigationController

Windows Live Contacts API

Ihre iPhone Kunden haben ein Adressbuch mit allen ihren Kontakten in ihrem Mobiltelefon. Falls Ihre iPhone App Austauschfunktionen für soziale Netzwerke umfasst, setzen Sie das Adressbuch wahrscheinlich in Ihrer App ein. Mit dem Adressbuch und den entsprechenden Diensten können Sie Kontaktinformationen aufrufen, um E-Mails oder SMS zu verschicken oder andere Informationen auszutauschen. 

Um beispielsweise den Namen eines ausgewählten Kontakts aufzurufen, verwenden Sie die ABPeoplePickerNavigationController-Klasse und implementieren das ABPeoplePickerNavigationControllerDelegate-Protokoll in Ihre Klasse.

iPhone: Objective-C

@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;
}

Im Artikel zum Adressbuch-Benutzeroberflächen-Framework finden Sie weitere Informationen dazu. Für direkten Zugriff auf die Kontaktliste können Sie das Adressbuch-Framework verwenden.

Für Ihre Windows Anwendungen könnten Sie die Windows Kontakte-API verwenden, diese ist jedoch für Windows 7 veraltet und wurde scheinbar durch die Windows Live Contacts API ersetzt. Sie benötigen eine aktive Internetverbindung, um auf das Live Contacts Adressbuch zugreifen zu können. Die API verwendet eine REST-Schnittstelle zum Austausch von XML-Dateien. Auf MSDN ist eine Beispielanwendung zu diesem Thema verfügbar.

Kamera

Kurzübersicht

iPhone API

Windows API

Kamera

UIImagePickerController

DirectShow

Die meisten Netbooks verfügen über eine integrierte Kamera. Das bedeutet, dass iPhone Apps, die die Kamera verwenden, auf Windows portiert werden können. Netbook-Kameras sind jedoch Webcams, d. h. sie sind auf den Benutzer gerichtet. Im Vergleich zu der Kamera an der Hinterseite des iPhones (iPhone, iPhone 3G und iPhone 3GS) bedeutet das, dass Sie das Benutzererlebnis Ihrer App auf der neuen Plattform überdenken müssen. Sie können beispielsweise die nach außen gerichtete Kamera auf einem iPhone nutzen, um einen Strichcode zu scannen, indem das iPhone über den Strichcode gehalten wird. Bei einem Netbook müsste der Strichcode zur Kamera hoch gehalten werden. Die Kamera auf einem Netbook kann jedoch für Videokonferenzen verwendet werden oder für andere Anwendungen, bei denen der Benutzer ein Foto von sich selbst macht. Das ist mit dem iPhone 3G nicht möglich.

Mit dem iPhone können Sie mit der UIImagePickerController-Klasse Bilder von der Kamera und Videos (in 3.0 und neuer) erfassen und in Ihrer Klasse ein UIImagePickerControllerDelegate-Protokoll implementieren.

iPhone: Objective-C

-(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"];
}

Unter Windows können Sie die DirectShow-Bibliothek verwenden, um Videoinhalt mit der Kamera zu erfassen und zu manipulieren. Sie müssen zuerst ein Erfassungsdiagramm erstellen, mit dem Sie den Dateifluss konfigurieren und den Video-Renderprozess starten.

Windows: C++

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();

Eine ausführliche Diskussion zu diesem Thema finden Sie im Abschnitt zur Videoerfassung in der DirectShow-Dokumentation bei MSDN.

Abspielen von Medien

Medien (ob Video oder Audio) sind heutzutage weit verbreitet, und Benutzer können auf ihren Handheld-Geräten über 3G-Netzwerke darauf zugreifen. Das iPhone unterstützt Video- und Audio-Wiedergabe sowie Streaming und verfügt über anspruchsvolle Schnittstellen zur Aktivierung aller dieser Optionen. Bei der Portierung Ihrer Anwendung auf Windows stehen mehrere Optionen für die Medienwiedergabe zur Verfügung, vom anspruchsvollen, integrierten Player bis zum einfachem Zugriff auf Streams. Sie können das Benutzererlebnis also auf verschiedene Art und Weise anpassen. Je nach den Anforderungen Ihrer App und ob Video- oder Audio-Streaming oder -Wiedergabe von kritischer Bedeutung sind, müssen Sie die Technologie entsprechend anpassen. Mit WPF haben Sie schnellen und sofortigen Zugriff zur Integration von Windows Media Player in Ihre Anwendung. Mit C++ können Sie Windows Media Player mit etwas Grundarbeit integrieren. Diese Option bietet eine präzisere Steuerung.

Abspielen von Videos

Kurzübersicht

iPhone API

Windows API

Video-Wiedergabe

UIImagePickerController

MediaElement

Auf dem iPhone ist es extrem einfach, ein Video zu starten und abzuspielen.

iPhone: Objective-C

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];
}

Falls Sie Ihre App in C# mit WPF erstellen können, dann können Sie Ihrem Layout einfach ein MediaElement hinzufügen und die URL für die Medienquelle in der XAML oder im Code-Behind einstellen (wie hier dargestellt).

Windows: XAML

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

Windows: C#

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

Bei Windows läuft dies etwas anders ab, und dies sollte beim Design berücksichtigt werden. Für das iPhone unter OS 3.1 und früheren Versionen wird der MPMoviePlayerController standardmäßig im Vollbildschirm-Modus gestartet. Mit dem MediaElement in WPF wird der Player in Ihre Anwendung mit den Steuerungen rundherum eingebettet.

Falls Ihre Windows App in C++ ist, können Sie eine Windows Media Player ActiveX-Steuerung in Ihre Anwendung einbetten und diese steuern, dazu sind jedoch mehrere Schritte erforderlich. Eine kurze Übersicht der einzelnen Schritte finden Sie im MSDN-Artikel „Hosting the Windows Media Player Control in a Windows Application.“

Abspielen von Audiodateien

Kurzübersicht

iPhone API

Windows API

Audio-Wiedergabe

AV Foundation-Framework

SoundPlayer

PlaySound

Falls Ihre iPhone App über Sound verfügt, dann kennen Sie wahrscheinlich schon die vielen auf dem iPhone verfügbaren Tools für die Arbeit mit Audio-Inhalt. Mit dem Media-Player-Framework kann Inhalt aus der iPod Mediathek des Benutzer wiedergegeben werden. Das Hinzufügen von Sounds zu Ihrer Anwendung kann jedoch auch einfach über das AV Foundation-Framework erzielt werden.

iPhone: Objective-C

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

Falls Sie Audiodateien oder Streams in Ihrer Windows WPF-App abspielen möchten, können Sie (wie oben beim Thema Videowiedergabe besprochen) das MediaElement einbetten. Dadurch wird ein Media-Player angezeigt. Falls Sie Sounds direkt in Ihrer .NET-Anwendung abspielen möchten, können Sie die SoundPlayer-Klasse verwenden, falls Ihre Sound-Dateien das .wav-Format aufweisen.

Windows: C#

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

Für andere Dateiformate in WPF müssen Sie Windows Media Player einbetten, was Sie mit der MediaPlayer-Klasse erzielen. Diese Klasse unterscheidet sich von MediaElement, da keine optische Darstellung vorhanden ist. Die API ist jedoch ähnlich.

Falls Sie Ihre Windows App in C++ schreiben, könnten Sie Windows Media Player einbetten (wie beim Thema Video oben besprochen). Dadurch wird ein optisches Element zur Wiedergabe sichergestellt. Hintergrund-Sound können Sie über die PlaySound-Funktion erzielen.

Windows: C++

#include <msystem.h>

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

Beachten Sie, dass dafür ein Sound benötigt wird, der vom Waveform-Gerät auf dem Netbook abgespielt werden kann. Mit .wav-Dateien gehen Sie also auf Nummer sicher.

0