Smartphones and tablets are, without any doubt, the kings of the consumer market. Traditional
computers won’t go away anytime soon, especially in the business world, but in many scenarios
mobile devices have replaced the old “mouse and keyboard” approach with a more modern and
intuitive one, based on touch and natural interfaces. For sure, the iPhone by Apple was the
product that forced all the other companies to rethink the mobile experience: first, with a new
phone concept, and second, with the idea of applications and app stores. These days, one of
the key considerations when choosing a new phone is the availability and quality of the apps,
rather than just the features offered by the platform. Developers play an important role in this.
Microsoft might have joined the party a little late, but it did so with a fresh and new approach.
Microsoft was developing Windows Mobile 7 when it realized that the phone wouldn’t be an
appealing product for consumers who were starting to get used to iPhone or Android devices.
So its developers dropped the project and started from scratch to build a totally new platform:
Windows Phone 7. The result was really different from the other competitors: a new user
interface, based on a flat design style called Microsoft Design style (once known as Metro); and
deep integration with social networks and all the Microsoft services, like Office, SkyDrive, and
Xbox.
The current version of the platform (which will be covered in this series) is Windows Phone 8; in
the middle, Microsoft released an update called Windows Phone 7.5 that added many new
consumer features but, most of all, improved the developer experience by adding many new
APIs.
Windows Phone 8 is a fresh start for the platform: Microsoft has abandoned the old stack of
technologies used in Windows Phone 7 (the Windows Mobile kernel, Silverlight, XNA) to
embrace the new features introduced in Windows 8, like the new kernel, the Windows Runtime,
and the native code (C++) support.
For this reason, Windows Phone 8 isn’t available as an update for old Windows Phone 7
devices. To help users in the transition, Microsoft has released an intermediate update called
Windows Phone 7.8, which has ported some of the new Windows Phone 8 features (like the
new Tile formats) to the old devices.
Since the release of Windows Phone 8, Microsoft has released three updates:
Update 1 (or GDR1), which added some improvements in Internet Explorer, Wi-Fi
connectivity, and messaging experience
Update 2 (or GDR2), which improved support for Google accounts, Xbox Music, and
Skype, added FM radio support, and expanded the availability of the Data Sense
application to keep track of the data traffic
Update 3 (or GDR3), which added support for a new resolution (1080p), driving mode,
screen lock orientation, and better storage management, and improved the Bluetooth
and Wi-Fi stack
The Hardware
Talking about the hardware is important because it’s strictly connected to the development experience and the features you can use while developing an application. With Windows Phone, Microsoft has introduced an approach that is a hybrid between Apple’s and Google’s.
Like Android, Windows Phone can run on a wide range of devices, with different form factors and hardware capabilities. However, Microsoft has defined a set of hardware guidelines that all manufacturers need to follow to build a Windows Phone device. In addition, vendors can’t customize the user interface or the operating system; all the phones, regardless of the producer, offer the same familiar user experience.
This way, Windows Phone can take the best from both worlds: a wide range of devices means more opportunities, because Windows Phone can run well on cheap and small devices in the same way it works well on high-resolution, powerful phones. A more controlled hardware, instead, makes the lives of developers much easier, because they can always count on features like sensors or GPS.
Here are the key features of a Windows Phone 8 device:
multi-core processor support (dual core and quad core processors)
at least 512 MB of RAM (usually 1 GB or 2 GB on high-end devices)
at least 4 GB of storage (that can be expanded with a Micro SD)
four supported resolutions: WVGA (480 × 800), WXGA (768 × 1280), 720p (720 × 1280), and 1080p (1080 × 1920)
three hardware buttons: Back, Start, and Search
The Windows Runtime
The Windows Runtime is the new API layer that Microsoft introduced in Windows 8, and it’s the foundation of a new and more modern approach to developing applications. In fact, unlike the .NET framework, it’s a native layer, which means better performance. Plus, it supports a wide range of APIs that cover many of the new scenarios that have been introduced in recent years: geolocation, movement sensors, NFC, and much more. In the end, it’s well suited for asynchronous and multi-threading scenarios that are one of the key requirements of mobile applications; the user interface needs to be always responsive, no matter which operation the application is performing.
Under the hood of the operating system, Microsoft has introduced the Windows Phone Runtime. Compared to the original Windows Runtime, it lacks some features (specifically, all the APIs that don’t make much sense on a phone, like printing APIs), but it adds several new ones specific to the platform (like hub integration, contacts and appointments access, etc.).
Microsoft faced a challenge during the Windows Phone 8 development: there was already a great number of applications published on the Windows Phone Store that were based on the “old” technologies like Silverlight and XNA. To avoid forcing developers to write their apps from scratch, Microsoft introduced three features:
The XAML stack has been ported directly from Windows Phone 7 instead of from Windows 8. This means that the XAML is still managed and not native, but it's completely aligned with the previous one so that, for example, features like behaviors, for which support has been added only in Windows 8.1, are still available). This way, you’ll be able to reuse all the XAML written for Windows Phone 7 applications without having to change it or fix it.
Thanks to a feature called quirks mode, applications written for Windows Phone 7 are able to run on Windows Phone 8 devices without having to apply any change in most cases. This mode is able to translate Windows Phone 7 API calls to the related Windows Runtime ones.
The Windows Phone Runtime includes a layer called .NET for Windows Phone, which is the subset of APIs that were available in Windows Phone 7. Thanks to this layer, you’ll be able to use the old APIs in a Windows Phone 8 application, even if they’ve been replaced by new APIs in the Windows Runtime. This way, you’ll be able to migrate your old applications to the new platform without having to rewrite all the code.
Like the full Windows Runtime, Windows Phone 8 has added support for C++ as a programming language, while the WinJS layer, which is a library that allows developers to create Windows Store apps using HTML and JavaScript, is missing. If you want to develop Windows Phone applications using web technologies, you’ll have to rely on the WebBrowser control (which embeds a web view in the application) and make use of features provided by frameworks like PhoneGap.
This series will cover the development using C# as a programming language and XAML as a user interface language. We won’t talk about C++ or VB.NET (the available APIs are the same, so it will be easy to reuse the knowledge acquired by reading this series). Plus, since this series is about Windows Phone 8, I will cover just the Windows Runtime APIs. In the areas where APIs are duplicated (meaning that there are both Windows Runtime and .NET for Windows Phone APIs to accomplish the same task, like storage or sensors), I will cover only the Windows Runtime ones.
The Development Tools
The official platform to develop Windows Phone applications is Visual Studio 2012, although support has also been added to the Visual Studio 2013 commercial versions. The major difference is that while Visual Studio 2012 still allows you to open and create Windows Phone 7 projects, Visual Studio 2013 can only be used to develop Windows Phone 8 applications.
There are no differences between the two versions when we talk about Windows Phone development: since the SDK is the same, you’ll get the same features in both environments, so we’ll use Visual Studio 2012 as a reference for this series.
To start, you’ll need to download the Windows Phone 8 SDK from the official developer portal. This download is suitable for both new developers and Microsoft developers who already have a commercial version of Visual Studio 2012. If you don’t already have Visual Studio installed, the setup will install the free Express version; otherwise, it will simply install the SDK and the emulator and add them to your existing Visual Studio installation.
The setup will also install Blend for Windows Phone, a tool made by Microsoft specifically for designers. It’s a XAML visual editor that makes it easier to create a user interface for a Windows Phone application. If you’re a developer, you’ll probably spend most of the time manually writing XAML in the Visual Studio editor, but it can be a valid companion when it comes to more complex things like creating animations or managing the visual states of a control.
To install the Windows Phone 8 SDK you’ll need a computer with Windows 8 Pro or Windows 8 Enterprise 64-bit. This is required since the emulator is based on Hyper-V, which is the Microsoft virtualization technology that is available only in professional versions of Windows. In addition, there’s a hardware requirement: your CPU needs to support the Second Level Address Translation (SLAT), which is a CPU feature needed for Hyper-V to properly run. If you have a newer computer, you don’t have to worry; basically all architectures from Intel i5 and further support it. Otherwise, you’ll still be able to install and the use the SDK, but you’ll need a real device for testing and debugging.
You can use a free tool called Machine SLAT Status Check to find out if your CPU satisfies the SLAT requirement.
The Emulator
Testing and debugging a Windows Phone app on a device before submitting it to the Windows Phone Store is a requirement; only on a real phone will you be able to test the true performance of the application. During daily development, using the device can slow you down. This is when the emulator is useful, especially because you’ll easily be able to test different conditions (like different resolutions, the loss of connectivity, etc.).
The emulator is a virtual machine powered by Hyper-V that is able to interact with the hardware of your computer. If you have a touch monitor, you can simulate the phone touch screen; if you have a microphone, you can simulate the phone microphone, etc. In addition, the emulator comes with a set of additional tools that are helpful for testing some scenarios that would require a physical device, like using the accelerometer or the GPS sensor.
You’ll be able to launch the emulator directly from Visual Studio. There are different versions of the emulator to match the different resolutions and memory sizes available on the market.
The Developer Experience
Windows Phone applications are published on the Windows Phone Store, which is the primary way for developers to distribute their applications. However, there are two exceptions: enterprise companies and developers for testing purposes.
To start publishing applications, you’ll need a developer account, which can be purchased from the official portal. The fee is $19 per year and allows you to publish an unlimited number of paid apps and a maximum of 100 free apps. Recently, Microsoft has merged the developer experience for its two main platforms. This means that with the developer account, you’ll also be able to publish Windows Store apps for Windows 8 on the Windows Store.
The developer account is also connected to testing. In fact, by default, phones are locked and the only way to install third-party apps is through the Windows Phone Store. All developers can unlock phones for free, even if they don’t have a paid account; the difference is that with a free account, only one phone can be unlocked and only two applications can be loaded on the phone. With a paid account, developers are able to unlock up to three phones and can load up to 10 applications on each.
The app deployment can be performed directly from Visual Studio or by using a tool installed with the SDK called Application Deployment.
To unlock your phone, you’ll need another tool that comes with the SDK called Windows Phone Developer Registration. You’ll have to connect your phone to the computer and sign in with the same Microsoft account you’ve used to register the developer account.
The application to be published on the Windows Phone Store needs to be certified. The certification process (in which both automatic and manual tests are executed) makes sure that your app is acceptable from a technical (the app doesn’t crash, the user experience isn’t confusing, etc.) and content (no pornography, no excessive violence) point of view.
We’ll cover more details about the publishing process in the last article of this series.
Preview for Developers
Microsoft has introduced a new program for developers to grant early access to new Windows Phone updates. This way, developers are able to test their apps against the latest OS releases before they are distributed to users.
To subscribe to the program you have to:
own a developer unlocked phone
have a paid developer account or a free account on App Studio, the web tool created by Microsoft for easily creating Windows Phone apps without programming skills
Once you’ve met these requirements, you can download the Preview for Developers application from the Store.
After installing it, you’ll have to run it and enable the preview program by accepting the terms and conditions. Once you’ve completed the process, preview updates will be delivered like regular updates: you’ll have to go to the Updates section of the Settings page and check for new updates. At the time of writing, Microsoft is delivering GDR3 with this preview program.
Just keep in mind that depending on the manufacturer, installing a preview version can break the phone’s warranty. It’s a safe procedure, but if something goes wrong, your manufacturer may not be able to support you.
The First Project
The starting point of every Windows Phone application is Visual Studio 2012. Let’s see how to create a project and how it is structured.
The first step is to open Visual Studio 2012 and click on New project. In the available installed templates, you’ll find the Windows Phone section that contains all the templates related to Windows Phone development.
We’re going to use the simplest one, Windows Phone app, which contains only a starting page. Give it a name and click OK. You’ll be asked which platform you’re going to support. Since we’re covering Windows Phone 8 development, choose Windows Phone 8.0. The designer will automatically load the starting page and, in the Solution Explorer window, you’ll see the structure of the project. Let’s look at it in detail:
App.xaml
The App.xaml file is the starting point of every Windows Phone application: it takes care of initializing the application and the frame that will contain the pages. Plus, since an instance of the App class (which is defined in the App.xaml.cs file) is always kept alive during the application execution, it’s also used to define all the global settings. For example, you can intercept the life-cycle event we’ll cover later in this series, or you can define global XAML styles and resources that will be used across the application.
MainPage.xaml
This is the main page of the application that is launched after the app is initialized. It’s the default one included in a project, but you can add as many pages as you want in your project. Every page has the same structure: it’s composed of a file with the extension .xaml, which defines the user interface, and a file with extension .cs, which is the code behind that defines the logic for interacting with the user interface. Every page inherits from a class called PhoneApplicationPage that offers built-in support for properties and events that are connected to the page life cycle, such as navigation events, orientation, system tray management, and more.
Assets
The Assets folder contains the graphic assets of the application. The standard project includes some default icons and images for the various Tile sizes and templates.
Resources
The Resources folder contains all the files needed to manage localization. By default, you’ll find just one file called AppResources.resx, which contains the base language (usually English, but you can select another). Every other language will be supported by adding a new AppResources file. The LocalizedStrings.cs file is added by default in the project’s root, and it is the class that we will use to manage localization. We’ll cover this more deeply later in this series.
The Manifest File
Inside the Properties folder you’ll find a file called WMAppManifest.xml. This is a very important file. It is called manifest because it’s used to declare all the capabilities and features of the application. Its role is crucial during the certification process; thanks to this file, the automatic process is able to extract all the needed information about the application, like its title, supported resolutions, features that are used, etc.
Visual Studio 2012 provides a visual editor for the manifest file; simply double-click on the file to open it. It’s important to note that not all use-case scenarios are supported by the visual editor. Sometimes we’ll have to manually edit the XML to extend our application.
The editor is split into four different sections:
Application UI: Features all the information about the look of the application once it is installed on the phone, like the title, supported resolutions, template, and standard images to use as the main Tile.
Capabilities: Lists all the hardware and software features the application can use, like the camera, geolocalization services, networking, etc. In most cases, using a feature for which the specific capabilities have not been declared will lead to an exception when the application is executed. In this series I will note every time we use a feature that requires a specific capability.
Requirements: Lists specific hardware features that can be used by your app, like the camera or NFC. If you set requirements, people that own phones without these specific hardware features won’t be able to download the app.
Packaging: Defines the features of the package that will be published in the store, like the author, publisher, default language, and supported languages.
The Splash Screen
If you’ve already developed apps for Windows Phone 7, you should be familiar with the splash screen. It’s a static image that is immediately displayed when the app is opened and disappears when the app is fully loaded and ready to be used.
The splash screen was part of the standard Visual Studio template for Windows Phone 7 projects, but it has been removed in Windows Phone 8 projects. Due to the performance improvements introduced in the Windows Runtime, apps now start much faster, so typically a splash screen is not needed.
If you do need a splash screen, you’ll have to manually add it to the project following a specific naming convention: it has to be in JPG format, the file name has to be SplashScreenImage.jpg, and the required resolution is 768 × 1280. This way, the image will be automatically adapted to the resolution of the device. If you want full control over the device’s resolution, you can add three different images, one for each supported resolution. In this case, the naming convention to follow is:
SplashScreenImage.screen-WVGA.jpg for 480 × 800 devices.
SplashScreenImage.screen-WXGA.jpg for 768 × 1280 devices.
SplashScreenImage.screen-720p.jpg for 720 × 1280 devices.
The 1080p resolution, from an aspect ratio point of view, behaves like 720p: if your application is launched on a 1080p device, the 720p splash screen will be used if one exists.
Testing the Application
When you’re ready to test your application, you can deploy it on a device or in the emulator directly from Visual Studio. In the toolbar area you will find what looks like a play button, together with a description of a deployment target. You can choose between five different targets: a real device, two versions of the WVGA emulator (one with 512 MB of RAM and one with 1 GB of RAM), a WXGA emulator, and a 720p emulator. From time to time, the list may be longer because Microsoft periodically releases SDK updates to add new emulators that match the new release of the operating system. For example, Microsoft has already released an SDK update that adds the emulator images aligned with the GDR2 release.
When the application is running in debug mode, two features are automatically enabled to help you work:
The right side of the screen displays performance counters like the number of frames per second or the memory usage. They will help you identify potential performance issues with your app (see image below for more detailed information).
The phone or emulator will never be suspended—the “auto lock” feature that turns the screen off after not being used is usually enabled on devices, but in debug mode, this feature is disabled.
A Quick Recap
In this first article we’ve started to get comfortable with some important concepts that every Windows Phone developer should be familiar with:
We’ve learned the main software and hardware features of the platform that developers can take advantage of.
We discussed the Windows Runtime that is the core of the newest Microsoft technologies, like Windows 8 and Windows Phone 8.
We’ve seen how to start working with Windows Phone: which tools to download and install, how to create the first project, and the structure of a Windows Phone application.
This tutorial represents a chapter from Windows Phone 8 Succinctly, a free eBook from the team at Syncfusion.
Smartphones and tablets are, without any doubt, the kings of the consumer market. Traditional
computers won’t go away anytime soon, especially in the business world, but in many scenarios
mobile devices have replaced the old “mouse and keyboard” approach with a more modern and
intuitive one, based on touch and natural interfaces. For sure, the iPhone by Apple was the
product that forced all the other companies to rethink the mobile experience: first, with a new
phone concept, and second, with the idea of applications and app stores. These days, one of
the key considerations when choosing a new phone is the availability and quality of the apps,
rather than just the features offered by the platform. Developers play an important role in this.
Microsoft might have joined the party a little late, but it did so with a fresh and new approach.
Microsoft was developing Windows Mobile 7 when it realized that the phone wouldn’t be an
appealing product for consumers who were starting to get used to iPhone or Android devices.
So its developers dropped the project and started from scratch to build a totally new platform:
Windows Phone 7. The result was really different from the other competitors: a new user
interface, based on a flat design style called Microsoft Design style (once known as Metro); and
deep integration with social networks and all the Microsoft services, like Office, SkyDrive, and
Xbox.
The current version of the platform (which will be covered in this series) is Windows Phone 8; in
the middle, Microsoft released an update called Windows Phone 7.5 that added many new
consumer features but, most of all, improved the developer experience by adding many new
APIs.
Windows Phone 8 is a fresh start for the platform: Microsoft has abandoned the old stack of
technologies used in Windows Phone 7 (the Windows Mobile kernel, Silverlight, XNA) to
embrace the new features introduced in Windows 8, like the new kernel, the Windows Runtime,
and the native code (C++) support.
For this reason, Windows Phone 8 isn’t available as an update for old Windows Phone 7
devices. To help users in the transition, Microsoft has released an intermediate update called
Windows Phone 7.8, which has ported some of the new Windows Phone 8 features (like the
new Tile formats) to the old devices.
Since the release of Windows Phone 8, Microsoft has released three updates:
Update 1 (or GDR1), which added some improvements in Internet Explorer, Wi-Fi
connectivity, and messaging experience
Update 2 (or GDR2), which improved support for Google accounts, Xbox Music, and
Skype, added FM radio support, and expanded the availability of the Data Sense
application to keep track of the data traffic
Update 3 (or GDR3), which added support for a new resolution (1080p), driving mode,
screen lock orientation, and better storage management, and improved the Bluetooth
and Wi-Fi stack
The Hardware
Talking about the hardware is important because it’s strictly connected to the development experience and the features you can use while developing an application. With Windows Phone, Microsoft has introduced an approach that is a hybrid between Apple’s and Google’s.
Like Android, Windows Phone can run on a wide range of devices, with different form factors and hardware capabilities. However, Microsoft has defined a set of hardware guidelines that all manufacturers need to follow to build a Windows Phone device. In addition, vendors can’t customize the user interface or the operating system; all the phones, regardless of the producer, offer the same familiar user experience.
This way, Windows Phone can take the best from both worlds: a wide range of devices means more opportunities, because Windows Phone can run well on cheap and small devices in the same way it works well on high-resolution, powerful phones. A more controlled hardware, instead, makes the lives of developers much easier, because they can always count on features like sensors or GPS.
Here are the key features of a Windows Phone 8 device:
multi-core processor support (dual core and quad core processors)
at least 512 MB of RAM (usually 1 GB or 2 GB on high-end devices)
at least 4 GB of storage (that can be expanded with a Micro SD)
four supported resolutions: WVGA (480 × 800), WXGA (768 × 1280), 720p (720 × 1280), and 1080p (1080 × 1920)
three hardware buttons: Back, Start, and Search
The Windows Runtime
The Windows Runtime is the new API layer that Microsoft introduced in Windows 8, and it’s the foundation of a new and more modern approach to developing applications. In fact, unlike the .NET framework, it’s a native layer, which means better performance. Plus, it supports a wide range of APIs that cover many of the new scenarios that have been introduced in recent years: geolocation, movement sensors, NFC, and much more. In the end, it’s well suited for asynchronous and multi-threading scenarios that are one of the key requirements of mobile applications; the user interface needs to be always responsive, no matter which operation the application is performing.
Under the hood of the operating system, Microsoft has introduced the Windows Phone Runtime. Compared to the original Windows Runtime, it lacks some features (specifically, all the APIs that don’t make much sense on a phone, like printing APIs), but it adds several new ones specific to the platform (like hub integration, contacts and appointments access, etc.).
Microsoft faced a challenge during the Windows Phone 8 development: there was already a great number of applications published on the Windows Phone Store that were based on the “old” technologies like Silverlight and XNA. To avoid forcing developers to write their apps from scratch, Microsoft introduced three features:
The XAML stack has been ported directly from Windows Phone 7 instead of from Windows 8. This means that the XAML is still managed and not native, but it's completely aligned with the previous one so that, for example, features like behaviors, for which support has been added only in Windows 8.1, are still available). This way, you’ll be able to reuse all the XAML written for Windows Phone 7 applications without having to change it or fix it.
Thanks to a feature called quirks mode, applications written for Windows Phone 7 are able to run on Windows Phone 8 devices without having to apply any change in most cases. This mode is able to translate Windows Phone 7 API calls to the related Windows Runtime ones.
The Windows Phone Runtime includes a layer called .NET for Windows Phone, which is the subset of APIs that were available in Windows Phone 7. Thanks to this layer, you’ll be able to use the old APIs in a Windows Phone 8 application, even if they’ve been replaced by new APIs in the Windows Runtime. This way, you’ll be able to migrate your old applications to the new platform without having to rewrite all the code.
Like the full Windows Runtime, Windows Phone 8 has added support for C++ as a programming language, while the WinJS layer, which is a library that allows developers to create Windows Store apps using HTML and JavaScript, is missing. If you want to develop Windows Phone applications using web technologies, you’ll have to rely on the WebBrowser control (which embeds a web view in the application) and make use of features provided by frameworks like PhoneGap.
This series will cover the development using C# as a programming language and XAML as a user interface language. We won’t talk about C++ or VB.NET (the available APIs are the same, so it will be easy to reuse the knowledge acquired by reading this series). Plus, since this series is about Windows Phone 8, I will cover just the Windows Runtime APIs. In the areas where APIs are duplicated (meaning that there are both Windows Runtime and .NET for Windows Phone APIs to accomplish the same task, like storage or sensors), I will cover only the Windows Runtime ones.
The Development Tools
The official platform to develop Windows Phone applications is Visual Studio 2012, although support has also been added to the Visual Studio 2013 commercial versions. The major difference is that while Visual Studio 2012 still allows you to open and create Windows Phone 7 projects, Visual Studio 2013 can only be used to develop Windows Phone 8 applications.
There are no differences between the two versions when we talk about Windows Phone development: since the SDK is the same, you’ll get the same features in both environments, so we’ll use Visual Studio 2012 as a reference for this series.
To start, you’ll need to download the Windows Phone 8 SDK from the official developer portal. This download is suitable for both new developers and Microsoft developers who already have a commercial version of Visual Studio 2012. If you don’t already have Visual Studio installed, the setup will install the free Express version; otherwise, it will simply install the SDK and the emulator and add them to your existing Visual Studio installation.
The setup will also install Blend for Windows Phone, a tool made by Microsoft specifically for designers. It’s a XAML visual editor that makes it easier to create a user interface for a Windows Phone application. If you’re a developer, you’ll probably spend most of the time manually writing XAML in the Visual Studio editor, but it can be a valid companion when it comes to more complex things like creating animations or managing the visual states of a control.
To install the Windows Phone 8 SDK you’ll need a computer with Windows 8 Pro or Windows 8 Enterprise 64-bit. This is required since the emulator is based on Hyper-V, which is the Microsoft virtualization technology that is available only in professional versions of Windows. In addition, there’s a hardware requirement: your CPU needs to support the Second Level Address Translation (SLAT), which is a CPU feature needed for Hyper-V to properly run. If you have a newer computer, you don’t have to worry; basically all architectures from Intel i5 and further support it. Otherwise, you’ll still be able to install and the use the SDK, but you’ll need a real device for testing and debugging.
You can use a free tool called Machine SLAT Status Check to find out if your CPU satisfies the SLAT requirement.
The Emulator
Testing and debugging a Windows Phone app on a device before submitting it to the Windows Phone Store is a requirement; only on a real phone will you be able to test the true performance of the application. During daily development, using the device can slow you down. This is when the emulator is useful, especially because you’ll easily be able to test different conditions (like different resolutions, the loss of connectivity, etc.).
The emulator is a virtual machine powered by Hyper-V that is able to interact with the hardware of your computer. If you have a touch monitor, you can simulate the phone touch screen; if you have a microphone, you can simulate the phone microphone, etc. In addition, the emulator comes with a set of additional tools that are helpful for testing some scenarios that would require a physical device, like using the accelerometer or the GPS sensor.
You’ll be able to launch the emulator directly from Visual Studio. There are different versions of the emulator to match the different resolutions and memory sizes available on the market.
The Developer Experience
Windows Phone applications are published on the Windows Phone Store, which is the primary way for developers to distribute their applications. However, there are two exceptions: enterprise companies and developers for testing purposes.
To start publishing applications, you’ll need a developer account, which can be purchased from the official portal. The fee is $19 per year and allows you to publish an unlimited number of paid apps and a maximum of 100 free apps. Recently, Microsoft has merged the developer experience for its two main platforms. This means that with the developer account, you’ll also be able to publish Windows Store apps for Windows 8 on the Windows Store.
The developer account is also connected to testing. In fact, by default, phones are locked and the only way to install third-party apps is through the Windows Phone Store. All developers can unlock phones for free, even if they don’t have a paid account; the difference is that with a free account, only one phone can be unlocked and only two applications can be loaded on the phone. With a paid account, developers are able to unlock up to three phones and can load up to 10 applications on each.
The app deployment can be performed directly from Visual Studio or by using a tool installed with the SDK called Application Deployment.
To unlock your phone, you’ll need another tool that comes with the SDK called Windows Phone Developer Registration. You’ll have to connect your phone to the computer and sign in with the same Microsoft account you’ve used to register the developer account.
The application to be published on the Windows Phone Store needs to be certified. The certification process (in which both automatic and manual tests are executed) makes sure that your app is acceptable from a technical (the app doesn’t crash, the user experience isn’t confusing, etc.) and content (no pornography, no excessive violence) point of view.
We’ll cover more details about the publishing process in the last article of this series.
Preview for Developers
Microsoft has introduced a new program for developers to grant early access to new Windows Phone updates. This way, developers are able to test their apps against the latest OS releases before they are distributed to users.
To subscribe to the program you have to:
own a developer unlocked phone
have a paid developer account or a free account on App Studio, the web tool created by Microsoft for easily creating Windows Phone apps without programming skills
Once you’ve met these requirements, you can download the Preview for Developers application from the Store.
After installing it, you’ll have to run it and enable the preview program by accepting the terms and conditions. Once you’ve completed the process, preview updates will be delivered like regular updates: you’ll have to go to the Updates section of the Settings page and check for new updates. At the time of writing, Microsoft is delivering GDR3 with this preview program.
Just keep in mind that depending on the manufacturer, installing a preview version can break the phone’s warranty. It’s a safe procedure, but if something goes wrong, your manufacturer may not be able to support you.
The First Project
The starting point of every Windows Phone application is Visual Studio 2012. Let’s see how to create a project and how it is structured.
The first step is to open Visual Studio 2012 and click on New project. In the available installed templates, you’ll find the Windows Phone section that contains all the templates related to Windows Phone development.
We’re going to use the simplest one, Windows Phone app, which contains only a starting page. Give it a name and click OK. You’ll be asked which platform you’re going to support. Since we’re covering Windows Phone 8 development, choose Windows Phone 8.0. The designer will automatically load the starting page and, in the Solution Explorer window, you’ll see the structure of the project. Let’s look at it in detail:
App.xaml
The App.xaml file is the starting point of every Windows Phone application: it takes care of initializing the application and the frame that will contain the pages. Plus, since an instance of the App class (which is defined in the App.xaml.cs file) is always kept alive during the application execution, it’s also used to define all the global settings. For example, you can intercept the life-cycle event we’ll cover later in this series, or you can define global XAML styles and resources that will be used across the application.
MainPage.xaml
This is the main page of the application that is launched after the app is initialized. It’s the default one included in a project, but you can add as many pages as you want in your project. Every page has the same structure: it’s composed of a file with the extension .xaml, which defines the user interface, and a file with extension .cs, which is the code behind that defines the logic for interacting with the user interface. Every page inherits from a class called PhoneApplicationPage that offers built-in support for properties and events that are connected to the page life cycle, such as navigation events, orientation, system tray management, and more.
Assets
The Assets folder contains the graphic assets of the application. The standard project includes some default icons and images for the various Tile sizes and templates.
Resources
The Resources folder contains all the files needed to manage localization. By default, you’ll find just one file called AppResources.resx, which contains the base language (usually English, but you can select another). Every other language will be supported by adding a new AppResources file. The LocalizedStrings.cs file is added by default in the project’s root, and it is the class that we will use to manage localization. We’ll cover this more deeply later in this series.
The Manifest File
Inside the Properties folder you’ll find a file called WMAppManifest.xml. This is a very important file. It is called manifest because it’s used to declare all the capabilities and features of the application. Its role is crucial during the certification process; thanks to this file, the automatic process is able to extract all the needed information about the application, like its title, supported resolutions, features that are used, etc.
Visual Studio 2012 provides a visual editor for the manifest file; simply double-click on the file to open it. It’s important to note that not all use-case scenarios are supported by the visual editor. Sometimes we’ll have to manually edit the XML to extend our application.
The editor is split into four different sections:
Application UI: Features all the information about the look of the application once it is installed on the phone, like the title, supported resolutions, template, and standard images to use as the main Tile.
Capabilities: Lists all the hardware and software features the application can use, like the camera, geolocalization services, networking, etc. In most cases, using a feature for which the specific capabilities have not been declared will lead to an exception when the application is executed. In this series I will note every time we use a feature that requires a specific capability.
Requirements: Lists specific hardware features that can be used by your app, like the camera or NFC. If you set requirements, people that own phones without these specific hardware features won’t be able to download the app.
Packaging: Defines the features of the package that will be published in the store, like the author, publisher, default language, and supported languages.
The Splash Screen
If you’ve already developed apps for Windows Phone 7, you should be familiar with the splash screen. It’s a static image that is immediately displayed when the app is opened and disappears when the app is fully loaded and ready to be used.
The splash screen was part of the standard Visual Studio template for Windows Phone 7 projects, but it has been removed in Windows Phone 8 projects. Due to the performance improvements introduced in the Windows Runtime, apps now start much faster, so typically a splash screen is not needed.
If you do need a splash screen, you’ll have to manually add it to the project following a specific naming convention: it has to be in JPG format, the file name has to be SplashScreenImage.jpg, and the required resolution is 768 × 1280. This way, the image will be automatically adapted to the resolution of the device. If you want full control over the device’s resolution, you can add three different images, one for each supported resolution. In this case, the naming convention to follow is:
SplashScreenImage.screen-WVGA.jpg for 480 × 800 devices.
SplashScreenImage.screen-WXGA.jpg for 768 × 1280 devices.
SplashScreenImage.screen-720p.jpg for 720 × 1280 devices.
The 1080p resolution, from an aspect ratio point of view, behaves like 720p: if your application is launched on a 1080p device, the 720p splash screen will be used if one exists.
Testing the Application
When you’re ready to test your application, you can deploy it on a device or in the emulator directly from Visual Studio. In the toolbar area you will find what looks like a play button, together with a description of a deployment target. You can choose between five different targets: a real device, two versions of the WVGA emulator (one with 512 MB of RAM and one with 1 GB of RAM), a WXGA emulator, and a 720p emulator. From time to time, the list may be longer because Microsoft periodically releases SDK updates to add new emulators that match the new release of the operating system. For example, Microsoft has already released an SDK update that adds the emulator images aligned with the GDR2 release.
When the application is running in debug mode, two features are automatically enabled to help you work:
The right side of the screen displays performance counters like the number of frames per second or the memory usage. They will help you identify potential performance issues with your app (see image below for more detailed information).
The phone or emulator will never be suspended—the “auto lock” feature that turns the screen off after not being used is usually enabled on devices, but in debug mode, this feature is disabled.
A Quick Recap
In this first article we’ve started to get comfortable with some important concepts that every Windows Phone developer should be familiar with:
We’ve learned the main software and hardware features of the platform that developers can take advantage of.
We discussed the Windows Runtime that is the core of the newest Microsoft technologies, like Windows 8 and Windows Phone 8.
We’ve seen how to start working with Windows Phone: which tools to download and install, how to create the first project, and the structure of a Windows Phone application.
This tutorial represents a chapter from Windows Phone 8 Succinctly, a free eBook from the team at Syncfusion.
In this two-part series, I will be showing you how to create the classic game of hangman. This version comes with a surprise ending, however. If you win the game the hangman does a happy dance. Along the way, you will learn about Corona's drawing API, scene management, dispatching custom events, how to utilize a sprite sheet, and how to use modules to emulate classes in the Lua programming language.
In the second part of this series, we will make the hangman dance if the user wins the round. During the hangman's dance, we play some music that was created by knarmahfox, licensed under the Creative Commons Attribution License.
1. New Project
Open the Corona Simulator, click New Project, and configure the project as shown below.
Select a location to save your project and click OK. This will create a folder with a number of icons and three files that are of interest to us, main.lua, config.lua, and build.settings. We'll take a look at each file in the next few steps.
2. Build Settings
The build.settings file is responsible for the build time properties of the project. Open this file, remove its contents, and populate it with the following configuration.
In build.settings, we are setting the default orientation and restricting the application to only support a portrait orientation. You can learn which other settings you can include in build.settings by exploring the Corona documentation.
3. Application Configuration
The config.lua file handles the application's configuration. As we did with build.settings, open this file, remove its contents, and add the following configuration.
This sets the default width and height of the screen, uses letterbox to scale the images, and sets the frame rate to 30. Visit the Corona documentation to learn more about the other properties you can set in config.lua.
4. Entry Point
The main.lua file is the file that the application loads first and uses to bootstrap the application. We will be using main.lua to set a few default settings for the application and the Composer library to load the first screen.
If you're not familiar with Corona's Composer library, then I recommend giving the documentation
a quick read. In short, Composer is the built-in solution to scene
(screen) creation and management in Corona. The library provides
developers with an easy way to create and transition between scenes.
The newer Composer module replaces the older and now deprecated StoryBoard module. A migration guide is available to help convert your old projects over to use Composer.
Step 1: Hide Status Bar
We don't want the status bar showing in our application. Add the following code snippet to main.lua to hide the status bar.
display.setStatusBar(display.HiddenStatusBar)
Step 2: Set Default Anchor Points
To set the default anchor or registration points, add the following code block to main.lua.
The anchorX and anchorY properties specify where you want the registration point of your display objects to be. Note that the value ranges from 0.0 to 1.0. For example, if you want the registration point to be the top left of the display object, then you'd set both properties to 0.0.
Step 3: Seed Random Generator
Our game will be using Lua's math.random
function to generate random numbers. To make sure that the numbers are
truly random each time the application runs, you must provide a seed
value. If you don't provide a seed value, the application will generate
the same randomness every time.
A good seed value is Lua's os.time function since it will be different each time the application is run. Add the following code snippet to main.lua.
math.randomseed( os.time() )
Step 4: Require Composer
Before we can use the Composer module, we must first require it. Add the following to main.lua.
local composer = require( "composer" )
Step 5: Load the GameScreen Scene
Add the following code snippet to main.lua. This will make the application go to the scene named gamescreen, which is also a Lua file, gamescreen.lua. You don't need to append the file extension when calling the gotoScene function.
composer.gotoScene( "gamescreen" )
5. GameScreen
Create a new Lua file named gamescreen.lua
in the project's main directory. This will be a composer file, which
means we need to require the Composer module and create a composer
scene. Add the following snippet to gamescreen.lua.
local composer = require( "composer" )
local scene = composer.newScene()
return scene
The call to newScene makes gamescreen.lua part of composer's scene hierarchy. This means that it becomes a screen within the game, which we can call composer methods on.
From here on out, the code added to gamescreen.lua should be placed above the return statement.
Step 1: Local Variables
The following snippet includes the local variables we will need for the gamescreen scene.
local widget = require ("widget") -- Needed for the button widget
local growText = require("growtext") -- A module providing a growing text effect
local words5 = {} -- Holds words 5 characters or less in length
local words9 = {} -- Holds words 9 characters or less in length
local words13 = {} -- Holds words 13 characters or less in length
local hangmanGroup
local alphabetArray = {"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"}
local guessWordText
local theWord -- The actual word
local guessWord -- The word the player has guessed
local numWrong = 0
local gameButtons = {} -- holds all the buttons
local winLoseText
local wonGame = false
It's important to understand that local variables in the main chunk only get called once, when the scene is loaded for the first time. When navigating through the composer scenes, for example, by invoking methods like gotoScence, the local variables will already be initialized.
This is important to remember if you want the local variables to be reinitialized when navigating
back to a particular scene. The easiest way to do this is to remove the
scene from the composer hierarchy by calling the removeScence
method. The next time you navigate to that scene, the scene will automatically be reloaded. That's the approach we'll be taking in this
tutorial.
To be able to use Corona's ButtonWidget, we must require the Widget library. ThegrowText module is a custom module we will create to provide a growing text effect. Create a new file in your project folder named growtext.lua.
Step 2: Composer Events
If you've taken the time to read the documentation of the Composer library, which I linked to earlier, you will have noticed the documentation includes a template that contains every possible composer
event. The comments are very useful as they indicate which events to
leverage for initializing assets, timers, etc. We are interested in the scene:create, scene:show, and scene:hide methods for this tutorial.
scene:create
Add the following code snippet to gamescreen.lua.
function scene:create( event )
local group = self.view
end
This method is called when the scene's view doesn't exist yet. This is
where you should initialize the display objects and add them to the
scene. The group variable is pointing to self.view, which is a GroupObject for the entire scene.
scene:show
Composer's scene:show method has two phases. The will phase is called when the scene is still off-screen, but is about to come on-screen. The did
phase is called when the scene is on-screen. This is where you want to
add code to make the scene come alive, start timers, add event
listeners, play audio, etc.
In this tutorial, we are only interested in the did phase. Add the following code snippet to gamescreen.lua.
function scene:show( event )
local phase = event.phase
local previousScene = composer.getSceneName( "previous" )
if(previousScene~=nil) then
composer.removeScene(previousScene)
end
if ( phase == "did" ) then
end
end
We declare a local variable phase, which we use to check which phase the show method is in. Since we will be coming back to this scene later in the game, we
check to see if there is a previous scene and, if true, remove it.
scene:hide
Composer's scene:hide method also has two phases. The will
phase is called when the scene is on-screen, but is about to go
off-screen. Here you will want to stop any timers, remove event
listeners, stop audio, etc. The did phase is called once the scene has gone off-screen.
In this tutorial, we are only interested in the will phase.
function scene:hide( event )
local phase = event.phase
if ( phase == "will" ) then
end
end
Step 3: Add Scene Listeners
We need to add scene listeners for the create, show, and hide methods. Add the following code to gamescreen.lua.
scene:addEventListener( "create", scene )
scene:addEventListener( "show", scene )
scene:addEventListener( "hide", scene )
scene:addEventListener( "destroy", scene )
Step 4: Test Progress
You should now test the project to make sure there are no errors. An easy way to confirm that the project is working as it should is by putting a print statement in the scene:show method as shown below.
function scene:show( event )
local phase = event.phase
--SNIP--
if ( phase == "did" ) then
print("Working") -- Temporary print statement
end
end
If you test the app and look in the console you should see "Working" has been printed out. You can remove the print statement now that you have verified the project is working as it should.
6. Drawing the Chalkboard
We will draw a "chalkboard" that will be used as the background for the game. Enter the following code in gamescreen.lua.
function drawChalkBoard(r,g,b)
local chalkBoard = display.newRect( 0, 0, display.contentWidth, display.contentHeight )
chalkBoard:setFillColor(r,g,b)
chalkBoard.anchorX = 0
chalkBoard.anchorY = 0
scene.view:insert(chalkBoard)
end
The newRect method draws a rectangle onto the screen. It takes as parameters the x and y positions of the rectangle, and the width and height. We use display.contentWidth and display.contentHeight, which represent the original width and height of the content in pixels. These default to the screen's width and height.
The setFillColor takes as parameters the R, G, and B values as a percentage, ranging from 0 to 1. By passing in the R,G, B values as parameters, we can easily change the color of the chalkboard.
Earlier in this tutorial, we have set the default anchorX and anchorY to 0.5. We set them to 0 here so the rectangle will be drawn from the top left of the display. We insert the chalkboard into the scene's view so that it will be removed when the scene itself is removed.
Next, we need to invoke this method in the scene:create method as shown in the following code snippet.
function scene:create( event )
local group = self.view
drawChalkBoard(1,1,1)
end
7. Drawing API
The Corona SDK provides a drawing API through the Display class. We will only be using the drawing methods newCircle and newLine in this tutorial. We used the newRect drawing method in the previous step. The Display class also provides a method for drawing polygons. Check out the documentation for more info.
Step 1: Adding the hangmanGroup
The game will draw the hangman and place it into a GroupObject, hangmanGroup. This will allow us to operate on the drawn pieces as a group. Add the following to the scene:create method.
function scene:create( event )
--SNIP--
hangmanGroup = display.newGroup()
group:insert(hangmanGroup)
hangmanGroup.x = 180
hangmanGroup.y = 180
end
Step 2: Drawing the Hangman
drawGallows
Add the following code to gamescreen.lua.
function drawGallows()
local gallows = display.newLine( hangmanGroup,20,380,360, 380)
gallows:append(290,380,290,50,180,50,180,90)
gallows:setStrokeColor(0, 0, 0)
gallows.strokeWidth = 3
end
Here we use the newLine method to draw the gallows. The parameters to newLine are the group in which to draw the line, the the starting x and y positions of the line, and the ending x and y positions of the line.
The append method lets us add extra x and y pairs to the original line. The method setStrokeColor sets the color of the line and the strokeWidth property sets the width of the line.
Invoke this method in scene:create.
function scene:create( event )
--SNIP--
hangmanGroup.x = 180
hangmanGroup.y = 180
drawGallows()
end
If you test the game at this point, you should see the gallows drawn to the screen.
drawHead
The following code draws the head.
function drawHead()
local head = display.newCircle(hangmanGroup,150,90,30)
head:setStrokeColor(0,0,0)
head:setFillColor(0,0,0,0)
head.strokeWidth = 3
head.anchorX = 0
head.anchorY = 0
end
The newCircle method takes as parameters the group into which to draw the circle, the x and y positions, and the radius of the circle. With the setFillColor method we pass in an extra 0, which is the alpha property. We don't want a filled circle, so by passing in 0 the fill will be completely transparent. The other options should be familiar if you have been following along.
drawBody
The drawBody function draws a vertical line representing the hangman's body.
function drawBody()
local body = display.newLine(hangmanGroup,180,150,180,300)
body:setStrokeColor(0, 0, 0)
body.strokeWidth = 3
end
drawArm1 and drawArm2
The drawArm1 and drawArm2 functions draw the hangman's arms.
function drawArm1()
local arm = display.newLine(hangmanGroup,180,200,130,190)
arm:setStrokeColor(0, 0, 0)
arm.strokeWidth = 3
end
function drawArm2()
local arm= display.newLine(hangmanGroup,180,200,230,190)
arm:setStrokeColor(0, 0, 0)
arm.strokeWidth = 3
end
drawLeg1 and drawLeg2
The drawLeg1 and drawLeg2 functions draw the hangman's legs.
function drawLeg1()
local leg = display.newLine(hangmanGroup,180,300,130,330)
leg:setStrokeColor(0, 0, 0)
leg.strokeWidth = 3
end
function drawLeg2()
local leg = display.newLine(hangmanGroup,180,300,230,330)
leg:setStrokeColor(0, 0, 0)
leg.strokeWidth = 3
end
8. Testing Progress
Now that we have all the methods in place to draw the hangman, we can test and make sure everything is working as it should. All we need to do is invoke each of the previous methods in the scene:create method.
function scene:create( event )
--SNIP--
drawGallows()
drawHead()
drawBody()
drawArm1()
drawArm2()
drawLeg1()
drawLeg2()
end
You can now remove all the function calls we just added. We do not want the hangman being drawn as soon as the game starts. Make sure you do leave the drawGallows method though.
Conclusion
This brings the first of this two-part series to a close. In the second part, we will implement the gameplay and get the hangman dancing to some happy music. Thanks for reading and I hope to see you in the second part.
In this two-part series, I will be showing you how to create the classic game of hangman. This version comes with a surprise ending, however. If you win the game the hangman does a happy dance. Along the way, you will learn about Corona's drawing API, scene management, dispatching custom events, how to utilize a sprite sheet, and how to use modules to emulate classes in the Lua programming language.
In the second part of this series, we will make the hangman dance if the user wins the round. During the hangman's dance, we play some music that was created by knarmahfox, licensed under the Creative Commons Attribution License.
1. New Project
Open the Corona Simulator, click New Project, and configure the project as shown below.
Select a location to save your project and click OK. This will create a folder with a number of icons and three files that are of interest to us, main.lua, config.lua, and build.settings. We'll take a look at each file in the next few steps.
2. Build Settings
The build.settings file is responsible for the build time properties of the project. Open this file, remove its contents, and populate it with the following configuration.
In build.settings, we are setting the default orientation and restricting the application to only support a portrait orientation. You can learn which other settings you can include in build.settings by exploring the Corona documentation.
3. Application Configuration
The config.lua file handles the application's configuration. As we did with build.settings, open this file, remove its contents, and add the following configuration.
This sets the default width and height of the screen, uses letterbox to scale the images, and sets the frame rate to 30. Visit the Corona documentation to learn more about the other properties you can set in config.lua.
4. Entry Point
The main.lua file is the file that the application loads first and uses to bootstrap the application. We will be using main.lua to set a few default settings for the application and the Composer library to load the first screen.
If you're not familiar with Corona's Composer library, then I recommend giving the documentation
a quick read. In short, Composer is the built-in solution to scene
(screen) creation and management in Corona. The library provides
developers with an easy way to create and transition between scenes.
The newer Composer module replaces the older and now deprecated StoryBoard module. A migration guide is available to help convert your old projects over to use Composer.
Step 1: Hide Status Bar
We don't want the status bar showing in our application. Add the following code snippet to main.lua to hide the status bar.
display.setStatusBar(display.HiddenStatusBar)
Step 2: Set Default Anchor Points
To set the default anchor or registration points, add the following code block to main.lua.
The anchorX and anchorY properties specify where you want the registration point of your display objects to be. Note that the value ranges from 0.0 to 1.0. For example, if you want the registration point to be the top left of the display object, then you'd set both properties to 0.0.
Step 3: Seed Random Generator
Our game will be using Lua's math.random
function to generate random numbers. To make sure that the numbers are
truly random each time the application runs, you must provide a seed
value. If you don't provide a seed value, the application will generate
the same randomness every time.
A good seed value is Lua's os.time function since it will be different each time the application is run. Add the following code snippet to main.lua.
math.randomseed( os.time() )
Step 4: Require Composer
Before we can use the Composer module, we must first require it. Add the following to main.lua.
local composer = require( "composer" )
Step 5: Load the GameScreen Scene
Add the following code snippet to main.lua. This will make the application go to the scene named gamescreen, which is also a Lua file, gamescreen.lua. You don't need to append the file extension when calling the gotoScene function.
composer.gotoScene( "gamescreen" )
5. GameScreen
Create a new Lua file named gamescreen.lua
in the project's main directory. This will be a composer file, which
means we need to require the Composer module and create a composer
scene. Add the following snippet to gamescreen.lua.
local composer = require( "composer" )
local scene = composer.newScene()
return scene
The call to newScene makes gamescreen.lua part of composer's scene hierarchy. This means that it becomes a screen within the game, which we can call composer methods on.
From here on out, the code added to gamescreen.lua should be placed above the return statement.
Step 1: Local Variables
The following snippet includes the local variables we will need for the gamescreen scene.
local widget = require ("widget") -- Needed for the button widget
local growText = require("growtext") -- A module providing a growing text effect
local words5 = {} -- Holds words 5 characters or less in length
local words9 = {} -- Holds words 9 characters or less in length
local words13 = {} -- Holds words 13 characters or less in length
local hangmanGroup
local alphabetArray = {"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"}
local guessWordText
local theWord -- The actual word
local guessWord -- The word the player has guessed
local numWrong = 0
local gameButtons = {} -- holds all the buttons
local winLoseText
local wonGame = false
It's important to understand that local variables in the main chunk only get called once, when the scene is loaded for the first time. When navigating through the composer scenes, for example, by invoking methods like gotoScence, the local variables will already be initialized.
This is important to remember if you want the local variables to be reinitialized when navigating
back to a particular scene. The easiest way to do this is to remove the
scene from the composer hierarchy by calling the removeScence
method. The next time you navigate to that scene, the scene will automatically be reloaded. That's the approach we'll be taking in this
tutorial.
To be able to use Corona's ButtonWidget, we must require the Widget library. ThegrowText module is a custom module we will create to provide a growing text effect. Create a new file in your project folder named growtext.lua.
Step 2: Composer Events
If you've taken the time to read the documentation of the Composer library, which I linked to earlier, you will have noticed the documentation includes a template that contains every possible composer
event. The comments are very useful as they indicate which events to
leverage for initializing assets, timers, etc. We are interested in the scene:create, scene:show, and scene:hide methods for this tutorial.
scene:create
Add the following code snippet to gamescreen.lua.
function scene:create( event )
local group = self.view
end
This method is called when the scene's view doesn't exist yet. This is
where you should initialize the display objects and add them to the
scene. The group variable is pointing to self.view, which is a GroupObject for the entire scene.
scene:show
Composer's scene:show method has two phases. The will phase is called when the scene is still off-screen, but is about to come on-screen. The did
phase is called when the scene is on-screen. This is where you want to
add code to make the scene come alive, start timers, add event
listeners, play audio, etc.
In this tutorial, we are only interested in the did phase. Add the following code snippet to gamescreen.lua.
function scene:show( event )
local phase = event.phase
local previousScene = composer.getSceneName( "previous" )
if(previousScene~=nil) then
composer.removeScene(previousScene)
end
if ( phase == "did" ) then
end
end
We declare a local variable phase, which we use to check which phase the show method is in. Since we will be coming back to this scene later in the game, we
check to see if there is a previous scene and, if true, remove it.
scene:hide
Composer's scene:hide method also has two phases. The will
phase is called when the scene is on-screen, but is about to go
off-screen. Here you will want to stop any timers, remove event
listeners, stop audio, etc. The did phase is called once the scene has gone off-screen.
In this tutorial, we are only interested in the will phase.
function scene:hide( event )
local phase = event.phase
if ( phase == "will" ) then
end
end
Step 3: Add Scene Listeners
We need to add scene listeners for the create, show, and hide methods. Add the following code to gamescreen.lua.
scene:addEventListener( "create", scene )
scene:addEventListener( "show", scene )
scene:addEventListener( "hide", scene )
scene:addEventListener( "destroy", scene )
Step 4: Test Progress
You should now test the project to make sure there are no errors. An easy way to confirm that the project is working as it should is by putting a print statement in the scene:show method as shown below.
function scene:show( event )
local phase = event.phase
--SNIP--
if ( phase == "did" ) then
print("Working") -- Temporary print statement
end
end
If you test the app and look in the console you should see "Working" has been printed out. You can remove the print statement now that you have verified the project is working as it should.
6. Drawing the Chalkboard
We will draw a "chalkboard" that will be used as the background for the game. Enter the following code in gamescreen.lua.
function drawChalkBoard(r,g,b)
local chalkBoard = display.newRect( 0, 0, display.contentWidth, display.contentHeight )
chalkBoard:setFillColor(r,g,b)
chalkBoard.anchorX = 0
chalkBoard.anchorY = 0
scene.view:insert(chalkBoard)
end
The newRect method draws a rectangle onto the screen. It takes as parameters the x and y positions of the rectangle, and the width and height. We use display.contentWidth and display.contentHeight, which represent the original width and height of the content in pixels. These default to the screen's width and height.
The setFillColor takes as parameters the R, G, and B values as a percentage, ranging from 0 to 1. By passing in the R,G, B values as parameters, we can easily change the color of the chalkboard.
Earlier in this tutorial, we have set the default anchorX and anchorY to 0.5. We set them to 0 here so the rectangle will be drawn from the top left of the display. We insert the chalkboard into the scene's view so that it will be removed when the scene itself is removed.
Next, we need to invoke this method in the scene:create method as shown in the following code snippet.
function scene:create( event )
local group = self.view
drawChalkBoard(1,1,1)
end
7. Drawing API
The Corona SDK provides a drawing API through the Display class. We will only be using the drawing methods newCircle and newLine in this tutorial. We used the newRect drawing method in the previous step. The Display class also provides a method for drawing polygons. Check out the documentation for more info.
Step 1: Adding the hangmanGroup
The game will draw the hangman and place it into a GroupObject, hangmanGroup. This will allow us to operate on the drawn pieces as a group. Add the following to the scene:create method.
function scene:create( event )
--SNIP--
hangmanGroup = display.newGroup()
group:insert(hangmanGroup)
hangmanGroup.x = 180
hangmanGroup.y = 180
end
Step 2: Drawing the Hangman
drawGallows
Add the following code to gamescreen.lua.
function drawGallows()
local gallows = display.newLine( hangmanGroup,20,380,360, 380)
gallows:append(290,380,290,50,180,50,180,90)
gallows:setStrokeColor(0, 0, 0)
gallows.strokeWidth = 3
end
Here we use the newLine method to draw the gallows. The parameters to newLine are the group in which to draw the line, the the starting x and y positions of the line, and the ending x and y positions of the line.
The append method lets us add extra x and y pairs to the original line. The method setStrokeColor sets the color of the line and the strokeWidth property sets the width of the line.
Invoke this method in scene:create.
function scene:create( event )
--SNIP--
hangmanGroup.x = 180
hangmanGroup.y = 180
drawGallows()
end
If you test the game at this point, you should see the gallows drawn to the screen.
drawHead
The following code draws the head.
function drawHead()
local head = display.newCircle(hangmanGroup,150,90,30)
head:setStrokeColor(0,0,0)
head:setFillColor(0,0,0,0)
head.strokeWidth = 3
head.anchorX = 0
head.anchorY = 0
end
The newCircle method takes as parameters the group into which to draw the circle, the x and y positions, and the radius of the circle. With the setFillColor method we pass in an extra 0, which is the alpha property. We don't want a filled circle, so by passing in 0 the fill will be completely transparent. The other options should be familiar if you have been following along.
drawBody
The drawBody function draws a vertical line representing the hangman's body.
function drawBody()
local body = display.newLine(hangmanGroup,180,150,180,300)
body:setStrokeColor(0, 0, 0)
body.strokeWidth = 3
end
drawArm1 and drawArm2
The drawArm1 and drawArm2 functions draw the hangman's arms.
function drawArm1()
local arm = display.newLine(hangmanGroup,180,200,130,190)
arm:setStrokeColor(0, 0, 0)
arm.strokeWidth = 3
end
function drawArm2()
local arm= display.newLine(hangmanGroup,180,200,230,190)
arm:setStrokeColor(0, 0, 0)
arm.strokeWidth = 3
end
drawLeg1 and drawLeg2
The drawLeg1 and drawLeg2 functions draw the hangman's legs.
function drawLeg1()
local leg = display.newLine(hangmanGroup,180,300,130,330)
leg:setStrokeColor(0, 0, 0)
leg.strokeWidth = 3
end
function drawLeg2()
local leg = display.newLine(hangmanGroup,180,300,230,330)
leg:setStrokeColor(0, 0, 0)
leg.strokeWidth = 3
end
8. Testing Progress
Now that we have all the methods in place to draw the hangman, we can test and make sure everything is working as it should. All we need to do is invoke each of the previous methods in the scene:create method.
function scene:create( event )
--SNIP--
drawGallows()
drawHead()
drawBody()
drawArm1()
drawArm2()
drawLeg1()
drawLeg2()
end
You can now remove all the function calls we just added. We do not want the hangman being drawn as soon as the game starts. Make sure you do leave the drawGallows method though.
Conclusion
This brings the first of this two-part series to a close. In the second part, we will implement the gameplay and get the hangman dancing to some happy music. Thanks for reading and I hope to see you in the second part.
If you've read the previous articles of this series, you should have a good grasp of the fundamentals of the Swift programming language by now. We talked about variables, constants, and functions, and in the previous article we covered the basics of object-oriented programming in Swift.
While playgrounds are a great tool to play with Swift and learn the language, it's time to move on and create our first Swift project in Xcode. In this article, we're going to create the foundation of a simple to-do application using Xcode and Swift. Along the way, we'll learn more about object-oriented programming and we'll also take a closer look at the integration between Swift and Objective-C.
Prerequisites
If you'd like to follow along with me, then make sure that you have Xcode 6.3 or higher installed on your machine. At the time of writing, Xcode 6.3 is in beta and available from Apple's iOS Dev Center to registered iOS developers.
The reason for requiring Xcode 6.3 or higher is to be able to take advantage of Swift 1.2, which Apple introduced a few days ago. Swift 1.2 introduces a number of great additions that we'll take advantage of in the rest of this series.
1. Project Setup
Step 1: Choose a Template
Launch Xcode 6.3 or higher and select New > Project... from the File menu. From the list of iOS Application templates, choose the Single View Application template and click Next.
Step 2: Configure the Project
Name the project ToDo and set Language to Swift. Make sure that Devices is set to iPhone and the checkbox labeled Use Core Data is unchecked. Click Next to continue.
Tell Xcode where you'd like to store your project and click Create in the bottom right to create your first Swift project.
2. Project Anatomy
Before we continue our journey into Swift, let's take a moment to see what Xcode has created for us. If you're new to Xcode and iOS development, then most of this will be new to you. By working with a Swift project, however, you will get a better feeling of how classes and structures look like and behave in Swift.
The project template doesn't differ much from a project created with Objective-C as the programming language. The most important differences are related to the AppDelegate and ViewController classes.
If you've worked with Objective-C in the past, then you'll notice that the project template contains less files. There are no header (.h) or implementation (.m) files to be found in our project. In Swift, classes have no separate header file in which the interface is declared. Instead, a class is declared and implemented in a single .swift file.
Our project currently contains two Swift files, one for the AppDelegate class, AppDelegate.swift, and another one for the ViewController class, ViewController.swift. The project also includes a storyboard, Main.storyboard, and a XIB file for the launch screen, LaunchScreen.xib. We'll work with the storyboard a bit later in this article. It currently only contains the scene for the ViewController class.
There are a number of other files and folders included in the project, but we're going to ignore those for now. They play no important role in the scope of this article.
3. Inheritance
The first thing we'll touch upon in this article is inheritance, a common paradigm in object-oriented programming. In Swift, only classes can inherit from another class. In other words, structures and enumerations do not support inheritance. This is one of the key differences between classes and structures.
Open ViewController.swift to see inheritance in action. The interface and implementation of the ViewController class is pretty bare-bones, which will make it easier for us to focus on the essentials.
UIKit
At the top of ViewController.swift, you should see an import statement for the UIKit framework. Remember that the UIKit framework provides the infrastructure for creating a functional iOS application. The import statement at the top makes this infrastructure available to us in ViewController.swift.
import UIKit
Superclass
Below the import statement, we define a new class named ViewController. The colon after the class name doesn't translate to is of type as we saw earlier in this series. Instead, the class after the colon is the superclass of the ViewController class. In other words, the following snippet could be read as we define a class named ViewController that inherits from UIViewController.
class ViewController: UIViewController {
}
This also explains the presence of the import statement at the top of ViewController.swift since the UIViewController class is defined in the UIKit framework.
Overrides
The ViewController class currently includes two methods, but notice that each method is prefixed with the override keyword. This indicates that the methods are defined in the class's superclass—or higher up the inheritance tree—and are overridden by the ViewController class.
The override construct doesn't exist in Objective-C. In Objective-C, you implement an overridden method in a subclass without explicitly indicating that it overrides a method higher up the inheritance tree. The Objective-C runtime takes care of the rest.
The same is true in Swift, but the override keywords adds safety to method overriding. Because we've prefixed the viewDidLoad method with the override keyword, Swift expects this method in the class's superclass or higher up the inheritance tree. Simply put, if you override a method that doesn't exist in the inheritance tree, Swift will throw an error. You can test this by misspelling the viewDidLoad method as shown below.
4. User Interface
Declaring Outlets
Let's add a table view to the view controller to display a list of to-do items. Before we do that, we need to create an outlet for the table view. An outlet is nothing more than a property that is visible to and can be set in Interface Builder. To declare an outlet in the ViewController class, we prefix the property, a variable, with the @IBOutlet attribute.
Note that the outlet is an implicitly unwrapped optional. A what? Let me start by saying that an outlet always needs to be an optional type. The reason is simple. Every property of the ViewController class needs to have a value after initialization. An outlet, however, is only connected to the user interface at runtime after the ViewController instance has been initialized hence the optional type.
Wait a minute. The tableView outlet is declared as an implicitly unwrapped optional, not an optional. No problem. We can declare the tableView outlet as an optional by replacing the exclamation mark in the above snippet with a question mark. That would compile just fine. However, this would also mean that we would need to explicitly unwrap the property every time we want to access the value stored in the optional. This will quickly become cumbersome and verbose.
Instead, we declare the tableView outlet as an implicitly unwrapped optional, which means that we don't need to explicitly unwrap the optional if we need to access its value. In short, an implicitly unwrapped optional is an optional, but we can access the value stored in the optional like a regular variable. The important thing to keep in mind is that your application will crash if you try to access its value if no value has been set. That's the gotcha. If the outlet is properly connected, however, we can be sure that the outlet is set when we first try to access it.
Connecting Outlets
With the outlet declared, it's time to connect it in Interface Builder. Open Main.storyboard, and select the view controller. Choose Embed In > Navigation Controller from the Editor menu. This will set the view controller as the root view controller of a navigation controller. Don't worry about this for now.
Drag a UITableView instance from the Object Library to the view controller's view, and add the necessary layout constraints. With the table view selected, open the Connections Inspector and set the table view's dataSource and delegate outlets to the view controller.
With the Connections Inspector still open, select the view controller and connect the tableView outlet to the table view we just added. This will connect the tableView outlet of the ViewController class to the table view.
5. Protocols
Before we can build and run the application, we need to implement the UITableViewDataSource and UITableViewDelegate protocols in the ViewController class. This involves several things.
Step 1: Conforming to Protocols
We need to tell the compiler that the ViewController class conforms to the UITableViewDataSource and UITableViewDelegate protocols. The syntax looks similar to that in Objective-C.
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
...
}
The protocols the class conforms to are listed after the superclass, UIViewController in the above example. If a class doesn't have a superclass, which isn't uncommon in Swift, then the protocols are listed immediately after the class name and colon.
Step 2: Implementing the UITableViewDataSource Protocol
Because the UITableViewDelegate protocol doesn't define required methods, we're only going to implement the UITableViewDataSource protocol for now. Before we do, let's create a variable property to store the to-do items we're going to list in the table view.
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
@IBOutlet var tableView: UITableView!
var items: [String] = []
...
}
We declare a variable property items of type [String] and set an empty array, [], as the initial value. This should look familiar by now. Next, let's implement the two required methods of the UITableViewDataSource protocol.
The first required method, numberOfRowsInSection(_:), is easy. We simply return the number of items stored in the items property.
The second required method, cellForRowAtIndexPath(_:), needs a bit more explaining. Using subscript syntax, we ask for the correct item from the array of to-do items.
We then ask the table view for a cell with identifier "TableViewCell", passing in the index path for the current row. Note that we store the cell in a constant named tableViewCell. There's no need to declare tableViewCell as a variable.
Another important detail is that we downcast the return value of dequeueReusableCellWithIdentifier(_:forIndexPath:) to UITableViewCell since it returns an object of type AnyObject, which is equivalent to id in Objective-C. To downcast AnyObject to UITableViewCell, we use the as keyword.
We could use the as? variant to downcast to an optional type, but since we can be sure that dequeueReusableCellWithIdentifier(_:forIndexPath:) always returns a properly initialized instance, we can use the as! keyword, implicitly unwrapping the result of the method call.
In the next line of code, we configure the table view cell, setting the text label's text with the value of item. Note that in Swift the textLabel property of UITableViewCell is declared as an optional type hence the question mark. This line of code could be read as set the text label's text property to item if textLabel is not equal to nil. In other words, the text label's text property is only set if textLabel is not nil. This is a very convenient safety construct in Swift known as optional chaining.
Step 3: Cell Reuse
There are two things we need to sort out before building the application. First, we need to tell the table view that it needs to use the UITableViewCell class to create new table view cells. We do this by invoking registerClass(_:forCellReuseIdentifier:), passing in the UITableViewCell class and the reuse identifier we used earlier, "TableViewCell". Update the viewDidLoad method as shown below.
override func viewDidLoad() {
super.viewDidLoad()
// Register Class for Cell Reuse
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "TableViewCell")
}
Step 4: Adding Items
We currently don't have any items to show in the table view. This is easily solved by populating the items property with a few to-do items. There are several ways to accomplish this. I've chosen to populate the items property in the viewDidLoad method as shown below.
It's time to take our application for a spin. Select Run from Xcode's Product menu or hit Command-R. If you've followed along and you're using Swift 1.2, you should end up with the following result.
Note that I've added a title, To Do, at the top of the view in the navigation bar. You can do the same by setting the title property of the ViewController instance in the viewDidLoad method.
override func viewDidLoad() {
super.viewDidLoad()
// Set Title
self.title = "To Do"
// Populate Items
self.items = ["Buy Milk", "Finish Tutorial", "Play Minecraft"]
// Register Class for Cell Reuse
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "TableViewCell")
}
Even though we've created a simple application, you've learned quite a few new things. We've explored inheritance and overriding methods. We also covered protocols and how to adopt the UITableViewDataSource protocol in the ViewController class. The most important thing you've learned, however, is how to interact with Objective-C APIs.
It's important to understand that the APIs of the iOS SDK are written in Objective-C. Swift was designed to be compatible with these APIs. Based on past failures, Apple understood that Swift needed to be able to hook into the iOS SDK without having to rewrite every single API in Swift.
Combining Objective-C and Swift is possible, but there are some caveats that we'll explore in more detail as we move forward. Because of Swift's relentless focus on safety, we need to take a few hurdles from time to time. However, none of these hurdles are too great to take as we'll find out in the next article in which we continue working on our to-do application.
If you've read the previous articles of this series, you should have a good grasp of the fundamentals of the Swift programming language by now. We talked about variables, constants, and functions, and in the previous article we covered the basics of object-oriented programming in Swift.
While playgrounds are a great tool to play with Swift and learn the language, it's time to move on and create our first Swift project in Xcode. In this article, we're going to create the foundation of a simple to-do application using Xcode and Swift. Along the way, we'll learn more about object-oriented programming and we'll also take a closer look at the integration between Swift and Objective-C.
Prerequisites
If you'd like to follow along with me, then make sure that you have Xcode 6.3 or higher installed on your machine. At the time of writing, Xcode 6.3 is in beta and available from Apple's iOS Dev Center to registered iOS developers.
The reason for requiring Xcode 6.3 or higher is to be able to take advantage of Swift 1.2, which Apple introduced a few days ago. Swift 1.2 introduces a number of great additions that we'll take advantage of in the rest of this series.
1. Project Setup
Step 1: Choose a Template
Launch Xcode 6.3 or higher and select New > Project... from the File menu. From the list of iOS Application templates, choose the Single View Application template and click Next.
Step 2: Configure the Project
Name the project ToDo and set Language to Swift. Make sure that Devices is set to iPhone and the checkbox labeled Use Core Data is unchecked. Click Next to continue.
Tell Xcode where you'd like to store your project and click Create in the bottom right to create your first Swift project.
2. Project Anatomy
Before we continue our journey into Swift, let's take a moment to see what Xcode has created for us. If you're new to Xcode and iOS development, then most of this will be new to you. By working with a Swift project, however, you will get a better feeling of how classes and structures look like and behave in Swift.
The project template doesn't differ much from a project created with Objective-C as the programming language. The most important differences are related to the AppDelegate and ViewController classes.
If you've worked with Objective-C in the past, then you'll notice that the project template contains less files. There are no header (.h) or implementation (.m) files to be found in our project. In Swift, classes have no separate header file in which the interface is declared. Instead, a class is declared and implemented in a single .swift file.
Our project currently contains two Swift files, one for the AppDelegate class, AppDelegate.swift, and another one for the ViewController class, ViewController.swift. The project also includes a storyboard, Main.storyboard, and a XIB file for the launch screen, LaunchScreen.xib. We'll work with the storyboard a bit later in this article. It currently only contains the scene for the ViewController class.
There are a number of other files and folders included in the project, but we're going to ignore those for now. They play no important role in the scope of this article.
3. Inheritance
The first thing we'll touch upon in this article is inheritance, a common paradigm in object-oriented programming. In Swift, only classes can inherit from another class. In other words, structures and enumerations do not support inheritance. This is one of the key differences between classes and structures.
Open ViewController.swift to see inheritance in action. The interface and implementation of the ViewController class is pretty bare-bones, which will make it easier for us to focus on the essentials.
UIKit
At the top of ViewController.swift, you should see an import statement for the UIKit framework. Remember that the UIKit framework provides the infrastructure for creating a functional iOS application. The import statement at the top makes this infrastructure available to us in ViewController.swift.
import UIKit
Superclass
Below the import statement, we define a new class named ViewController. The colon after the class name doesn't translate to is of type as we saw earlier in this series. Instead, the class after the colon is the superclass of the ViewController class. In other words, the following snippet could be read as we define a class named ViewController that inherits from UIViewController.
class ViewController: UIViewController {
}
This also explains the presence of the import statement at the top of ViewController.swift since the UIViewController class is defined in the UIKit framework.
Overrides
The ViewController class currently includes two methods, but notice that each method is prefixed with the override keyword. This indicates that the methods are defined in the class's superclass—or higher up the inheritance tree—and are overridden by the ViewController class.
The override construct doesn't exist in Objective-C. In Objective-C, you implement an overridden method in a subclass without explicitly indicating that it overrides a method higher up the inheritance tree. The Objective-C runtime takes care of the rest.
The same is true in Swift, but the override keywords adds safety to method overriding. Because we've prefixed the viewDidLoad method with the override keyword, Swift expects this method in the class's superclass or higher up the inheritance tree. Simply put, if you override a method that doesn't exist in the inheritance tree, Swift will throw an error. You can test this by misspelling the viewDidLoad method as shown below.
4. User Interface
Declaring Outlets
Let's add a table view to the view controller to display a list of to-do items. Before we do that, we need to create an outlet for the table view. An outlet is nothing more than a property that is visible to and can be set in Interface Builder. To declare an outlet in the ViewController class, we prefix the property, a variable, with the @IBOutlet attribute.
Note that the outlet is an implicitly unwrapped optional. A what? Let me start by saying that an outlet always needs to be an optional type. The reason is simple. Every property of the ViewController class needs to have a value after initialization. An outlet, however, is only connected to the user interface at runtime after the ViewController instance has been initialized hence the optional type.
Wait a minute. The tableView outlet is declared as an implicitly unwrapped optional, not an optional. No problem. We can declare the tableView outlet as an optional by replacing the exclamation mark in the above snippet with a question mark. That would compile just fine. However, this would also mean that we would need to explicitly unwrap the property every time we want to access the value stored in the optional. This will quickly become cumbersome and verbose.
Instead, we declare the tableView outlet as an implicitly unwrapped optional, which means that we don't need to explicitly unwrap the optional if we need to access its value. In short, an implicitly unwrapped optional is an optional, but we can access the value stored in the optional like a regular variable. The important thing to keep in mind is that your application will crash if you try to access its value if no value has been set. That's the gotcha. If the outlet is properly connected, however, we can be sure that the outlet is set when we first try to access it.
Connecting Outlets
With the outlet declared, it's time to connect it in Interface Builder. Open Main.storyboard, and select the view controller. Choose Embed In > Navigation Controller from the Editor menu. This will set the view controller as the root view controller of a navigation controller. Don't worry about this for now.
Drag a UITableView instance from the Object Library to the view controller's view, and add the necessary layout constraints. With the table view selected, open the Connections Inspector and set the table view's dataSource and delegate outlets to the view controller.
With the Connections Inspector still open, select the view controller and connect the tableView outlet to the table view we just added. This will connect the tableView outlet of the ViewController class to the table view.
5. Protocols
Before we can build and run the application, we need to implement the UITableViewDataSource and UITableViewDelegate protocols in the ViewController class. This involves several things.
Step 1: Conforming to Protocols
We need to tell the compiler that the ViewController class conforms to the UITableViewDataSource and UITableViewDelegate protocols. The syntax looks similar to that in Objective-C.
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
...
}
The protocols the class conforms to are listed after the superclass, UIViewController in the above example. If a class doesn't have a superclass, which isn't uncommon in Swift, then the protocols are listed immediately after the class name and colon.
Step 2: Implementing the UITableViewDataSource Protocol
Because the UITableViewDelegate protocol doesn't define required methods, we're only going to implement the UITableViewDataSource protocol for now. Before we do, let's create a variable property to store the to-do items we're going to list in the table view.
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
@IBOutlet var tableView: UITableView!
var items: [String] = []
...
}
We declare a variable property items of type [String] and set an empty array, [], as the initial value. This should look familiar by now. Next, let's implement the two required methods of the UITableViewDataSource protocol.
The first required method, numberOfRowsInSection(_:), is easy. We simply return the number of items stored in the items property.
The second required method, cellForRowAtIndexPath(_:), needs a bit more explaining. Using subscript syntax, we ask for the correct item from the array of to-do items.
We then ask the table view for a cell with identifier "TableViewCell", passing in the index path for the current row. Note that we store the cell in a constant named tableViewCell. There's no need to declare tableViewCell as a variable.
Another important detail is that we downcast the return value of dequeueReusableCellWithIdentifier(_:forIndexPath:) to UITableViewCell since it returns an object of type AnyObject, which is equivalent to id in Objective-C. To downcast AnyObject to UITableViewCell, we use the as keyword.
We could use the as? variant to downcast to an optional type, but since we can be sure that dequeueReusableCellWithIdentifier(_:forIndexPath:) always returns a properly initialized instance, we can use the as! keyword, implicitly unwrapping the result of the method call.
In the next line of code, we configure the table view cell, setting the text label's text with the value of item. Note that in Swift the textLabel property of UITableViewCell is declared as an optional type hence the question mark. This line of code could be read as set the text label's text property to item if textLabel is not equal to nil. In other words, the text label's text property is only set if textLabel is not nil. This is a very convenient safety construct in Swift known as optional chaining.
Step 3: Cell Reuse
There are two things we need to sort out before building the application. First, we need to tell the table view that it needs to use the UITableViewCell class to create new table view cells. We do this by invoking registerClass(_:forCellReuseIdentifier:), passing in the UITableViewCell class and the reuse identifier we used earlier, "TableViewCell". Update the viewDidLoad method as shown below.
override func viewDidLoad() {
super.viewDidLoad()
// Register Class for Cell Reuse
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "TableViewCell")
}
Step 4: Adding Items
We currently don't have any items to show in the table view. This is easily solved by populating the items property with a few to-do items. There are several ways to accomplish this. I've chosen to populate the items property in the viewDidLoad method as shown below.
It's time to take our application for a spin. Select Run from Xcode's Product menu or hit Command-R. If you've followed along and you're using Swift 1.2, you should end up with the following result.
Note that I've added a title, To Do, at the top of the view in the navigation bar. You can do the same by setting the title property of the ViewController instance in the viewDidLoad method.
override func viewDidLoad() {
super.viewDidLoad()
// Set Title
self.title = "To Do"
// Populate Items
self.items = ["Buy Milk", "Finish Tutorial", "Play Minecraft"]
// Register Class for Cell Reuse
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "TableViewCell")
}
Even though we've created a simple application, you've learned quite a few new things. We've explored inheritance and overriding methods. We also covered protocols and how to adopt the UITableViewDataSource protocol in the ViewController class. The most important thing you've learned, however, is how to interact with Objective-C APIs.
It's important to understand that the APIs of the iOS SDK are written in Objective-C. Swift was designed to be compatible with these APIs. Based on past failures, Apple understood that Swift needed to be able to hook into the iOS SDK without having to rewrite every single API in Swift.
Combining Objective-C and Swift is possible, but there are some caveats that we'll explore in more detail as we move forward. Because of Swift's relentless focus on safety, we need to take a few hurdles from time to time. However, none of these hurdles are too great to take as we'll find out in the next article in which we continue working on our to-do application.
Have you ever seen a beautiful animated GIF that loops seamlessly and wondered if you could use it as a live wallpaper on your Android device? Well, you can, and in this tutorial I am going to show you how.
Introduction
Creating an interesting and beautiful live wallpaper from scratch using only math and code to generate the graphics can be tedious and time-consuming. It also requires lots of creativity. On the other hand, creating an animated GIF or finding one online is a lot easier. In this tutorial, you are going to learn how to convert any animated GIF into a live wallpaper.
Prerequisites
Ensure that you have the latest version of Android Studio set up. You can get it from the Android Developer website.
Even though any animated GIF will do, I suggest that you download a good cinemagraph. A cinemagraph is nothing but an animated GIF—usually created from a video—that loops seamlessly. You can find lots of good ones on Flickr.
For this tutorial, I am using a cinemagraph created by Flickr user djandyw.com as it is available under a Creative Commons license.
1. Create a New Project
Start Android Studio, create a new project, and name the project GIFWallpaper. Pick a unique package name if you plan to publish this app on Google Play.
Set the minimum SDK to API 8: Android 2.2 (Froyo).
Our app is not going to have an Activity, so choose Add No Activity and click Finish.
2. Describe the Wallpaper
A live wallpaper needs a file that describes it. Create a new XML file named res/xml/wallpaper.xml and replace its contents with the following XML:
The label and thumbnail are particularly important as they will be used when the wallpaper shows up in the list of the wallpapers available on your device.
3. Edit the Manifest
To run as a live wallpaper, our app needs only one permission, android.permission.BIND_WALLPAPER.
A live wallpaper runs as a Service that can receive the android.service.wallpaper.WallpaperService intent action. Name the ServiceGIFWallpaperService and add it to the project's manifest, AndroidManifest.xml.
Copy the animated GIF you downloaded from Flickr to the assets folder of the project. I've named the GIF girl.gif.
5. Create the Service
Create a new Java class and name it GIFWallpaperService.java. This class should extend the WallpaperService class.
public class GIFWallpaperService extends WallpaperService {
}
Because WallpaperService is an abstract class, you have to override its onCreateEngine method and return an instance of your own Engine, which can render the frames of the GIF.
To use the animated GIF, you first have to convert it into a Movie object. You can use the Movie class's decodeStream method to do so. Once the Movie object has been created, pass it as a parameter to the constructor of the custom Engine.
This is what the onCreateEngine method should look like:
@Override
public WallpaperService.Engine onCreateEngine() {
try {
Movie movie = Movie.decodeStream(
getResources().getAssets().open("girl.gif"));
return new GIFWallpaperEngine(movie);
}catch(IOException e){
Log.d("GIF", "Could not load asset");
return null;
}
}
6. Create the Engine
Let's start working on the Engine now. Create a class named GIFWallpaperEngine inside the GIFWallpaperService class and make it extend WallpaperService.Engine.
Add the following fields to this new class:
frameDuration: This integer represents the delay between re-draw operations. A value of 20 gives you 50 frames per second.
visible: This boolean lets the engine know if the live wallpaper is currently visible on the screen. This is important, because we should not be drawing the wallpaper when it isn't visible.
movie: This is the animated GIF in the form of a Movie object.
holder: This refers to the SurfaceHolder object available to the engine. It has to be initialized by overriding the onCreate method.
handler: This is a Handler object that will be used to start a Runnable that is responsible for actually drawing the wallpaper.
Your class should now look like this:
private class GIFWallpaperEngine extends WallpaperService.Engine {
private final int frameDuration = 20;
private SurfaceHolder holder;
private Movie movie;
private boolean visible;
private Handler handler;
public GIFWallpaperEngine(Movie movie) {
this.movie = movie;
handler = new Handler();
}
@Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
this.holder = surfaceHolder;
}
}
Next, create a method named draw that draws the contents of the animated GIF. Let's break this method down:
We first check if the visible variable is set to true. We only continue if it is.
Use the SurfaceHolder's lockCanvas method to get a Canvas to draw on.
Draw a frame of the animated GIF on the Canvas after scaling and positioning it.
Once all the drawing is done, pass the Canvas back to the SurfaceHolder.
Update the current frame of the animated GIF using the Movie object's setTime method.
Call the method again using the handler after waiting for frameDuration milliseconds.
The draw method is never called directly. It is always called using a Handler and a Runnable object. Therefore, let's make the Runnable object a field of the class and call it drawGIF.
Add the following code to the GIFWallpaperService class:
private Runnable drawGIF = new Runnable() {
public void run() {
draw();
}
};
private void draw() {
if (visible) {
Canvas canvas = holder.lockCanvas();
canvas.save();
// Adjust size and position so that
// the image looks good on your screen
canvas.scale(3f, 3f);
movie.draw(canvas, -100, 0);
canvas.restore();
holder.unlockCanvasAndPost(canvas);
movie.setTime((int) (System.currentTimeMillis() % movie.duration()));
handler.removeCallbacks(drawGIF);
handler.postDelayed(drawGIF, frameDuration);
}
}
The onVisibilityChanged method is automatically called whenever the visibility of the wallpaper changes. We need to override it and, based on the value of the visible argument, either start or stop drawGIF. The removeCallbacks method of the Handler is used to stop any pending drawGIF runs.
@Override
public void onVisibilityChanged(boolean visible) {
this.visible = visible;
if (visible) {
handler.post(drawGIF);
} else {
handler.removeCallbacks(drawGIF);
}
}
Finally, override the onDestroy method of the Engine to stop any pending drawGIF runs if the wallpaper is deactivated.
@Override
public void onDestroy() {
super.onDestroy();
handler.removeCallbacks(drawGIF);
}
7. Compile and Install
Your live wallpaper is now ready. Compile it and install it on your Android device. Once installed, you should be able to find the wallpaper in the list of available wallpapers.
Most launchers give you an option to change the wallpaper after a long tap gesture. Alternatively, you can go to the display settings to change the wallpaper.
If the GIF looks too small or it isn't positioned correctly, then go back to the draw method and adjust the scale and position.
Conclusion
You now know how to use an animated GIF to create a live wallpaper. Feel free to experiment with more GIFs. If you plan to publish your live wallpaper on Google Play, make sure you have the creator's permission to use the animated GIF commercially. Visit the Android Developer website to learn more about the WallpaperService class.
Have you ever seen a beautiful animated GIF that loops seamlessly and wondered if you could use it as a live wallpaper on your Android device? Well, you can, and in this tutorial I am going to show you how.
Introduction
Creating an interesting and beautiful live wallpaper from scratch using only math and code to generate the graphics can be tedious and time-consuming. It also requires lots of creativity. On the other hand, creating an animated GIF or finding one online is a lot easier. In this tutorial, you are going to learn how to convert any animated GIF into a live wallpaper.
Prerequisites
Ensure that you have the latest version of Android Studio set up. You can get it from the Android Developer website.
Even though any animated GIF will do, I suggest that you download a good cinemagraph. A cinemagraph is nothing but an animated GIF—usually created from a video—that loops seamlessly. You can find lots of good ones on Flickr.
For this tutorial, I am using a cinemagraph created by Flickr user djandyw.com as it is available under a Creative Commons license.
1. Create a New Project
Start Android Studio, create a new project, and name the project GIFWallpaper. Pick a unique package name if you plan to publish this app on Google Play.
Set the minimum SDK to API 8: Android 2.2 (Froyo).
Our app is not going to have an Activity, so choose Add No Activity and click Finish.
2. Describe the Wallpaper
A live wallpaper needs a file that describes it. Create a new XML file named res/xml/wallpaper.xml and replace its contents with the following XML:
The label and thumbnail are particularly important as they will be used when the wallpaper shows up in the list of the wallpapers available on your device.
3. Edit the Manifest
To run as a live wallpaper, our app needs only one permission, android.permission.BIND_WALLPAPER.
A live wallpaper runs as a Service that can receive the android.service.wallpaper.WallpaperService intent action. Name the ServiceGIFWallpaperService and add it to the project's manifest, AndroidManifest.xml.
Copy the animated GIF you downloaded from Flickr to the assets folder of the project. I've named the GIF girl.gif.
5. Create the Service
Create a new Java class and name it GIFWallpaperService.java. This class should extend the WallpaperService class.
public class GIFWallpaperService extends WallpaperService {
}
Because WallpaperService is an abstract class, you have to override its onCreateEngine method and return an instance of your own Engine, which can render the frames of the GIF.
To use the animated GIF, you first have to convert it into a Movie object. You can use the Movie class's decodeStream method to do so. Once the Movie object has been created, pass it as a parameter to the constructor of the custom Engine.
This is what the onCreateEngine method should look like:
@Override
public WallpaperService.Engine onCreateEngine() {
try {
Movie movie = Movie.decodeStream(
getResources().getAssets().open("girl.gif"));
return new GIFWallpaperEngine(movie);
}catch(IOException e){
Log.d("GIF", "Could not load asset");
return null;
}
}
6. Create the Engine
Let's start working on the Engine now. Create a class named GIFWallpaperEngine inside the GIFWallpaperService class and make it extend WallpaperService.Engine.
Add the following fields to this new class:
frameDuration: This integer represents the delay between re-draw operations. A value of 20 gives you 50 frames per second.
visible: This boolean lets the engine know if the live wallpaper is currently visible on the screen. This is important, because we should not be drawing the wallpaper when it isn't visible.
movie: This is the animated GIF in the form of a Movie object.
holder: This refers to the SurfaceHolder object available to the engine. It has to be initialized by overriding the onCreate method.
handler: This is a Handler object that will be used to start a Runnable that is responsible for actually drawing the wallpaper.
Your class should now look like this:
private class GIFWallpaperEngine extends WallpaperService.Engine {
private final int frameDuration = 20;
private SurfaceHolder holder;
private Movie movie;
private boolean visible;
private Handler handler;
public GIFWallpaperEngine(Movie movie) {
this.movie = movie;
handler = new Handler();
}
@Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
this.holder = surfaceHolder;
}
}
Next, create a method named draw that draws the contents of the animated GIF. Let's break this method down:
We first check if the visible variable is set to true. We only continue if it is.
Use the SurfaceHolder's lockCanvas method to get a Canvas to draw on.
Draw a frame of the animated GIF on the Canvas after scaling and positioning it.
Once all the drawing is done, pass the Canvas back to the SurfaceHolder.
Update the current frame of the animated GIF using the Movie object's setTime method.
Call the method again using the handler after waiting for frameDuration milliseconds.
The draw method is never called directly. It is always called using a Handler and a Runnable object. Therefore, let's make the Runnable object a field of the class and call it drawGIF.
Add the following code to the GIFWallpaperService class:
private Runnable drawGIF = new Runnable() {
public void run() {
draw();
}
};
private void draw() {
if (visible) {
Canvas canvas = holder.lockCanvas();
canvas.save();
// Adjust size and position so that
// the image looks good on your screen
canvas.scale(3f, 3f);
movie.draw(canvas, -100, 0);
canvas.restore();
holder.unlockCanvasAndPost(canvas);
movie.setTime((int) (System.currentTimeMillis() % movie.duration()));
handler.removeCallbacks(drawGIF);
handler.postDelayed(drawGIF, frameDuration);
}
}
The onVisibilityChanged method is automatically called whenever the visibility of the wallpaper changes. We need to override it and, based on the value of the visible argument, either start or stop drawGIF. The removeCallbacks method of the Handler is used to stop any pending drawGIF runs.
@Override
public void onVisibilityChanged(boolean visible) {
this.visible = visible;
if (visible) {
handler.post(drawGIF);
} else {
handler.removeCallbacks(drawGIF);
}
}
Finally, override the onDestroy method of the Engine to stop any pending drawGIF runs if the wallpaper is deactivated.
@Override
public void onDestroy() {
super.onDestroy();
handler.removeCallbacks(drawGIF);
}
7. Compile and Install
Your live wallpaper is now ready. Compile it and install it on your Android device. Once installed, you should be able to find the wallpaper in the list of available wallpapers.
Most launchers give you an option to change the wallpaper after a long tap gesture. Alternatively, you can go to the display settings to change the wallpaper.
If the GIF looks too small or it isn't positioned correctly, then go back to the draw method and adjust the scale and position.
Conclusion
You now know how to use an animated GIF to create a live wallpaper. Feel free to experiment with more GIFs. If you plan to publish your live wallpaper on Google Play, make sure you have the creator's permission to use the animated GIF commercially. Visit the Android Developer website to learn more about the WallpaperService class.
In the first part of this two-part series, we laid the foundation for transitioning between screens and for drawing the hangman using Corona's drawing API. In the second and final part of this series, we will implement the game logic, and transition to a win screen where the hangman will do a dance along with some music. Lets get started.
1. Creating the Word to Guess
Step 1: Reading a Text File
The game reads in words from a text file that contains thousands of words. We will filter the words based on their length and add them to one of three lists, words that are 5 characters long or less, words that are 9 characters long or less, and words that are 13 characters long or less.
By having three separate word lists, we could integrate a difficulty level based on word length. We will not be doing that in this tutorial, but the words will already be separated into separate lists should you decide to pursue that option on your own.
Add the following code to gamescreen.lua.
function readTextFile()
local path = system.pathForFile( "wordlist.txt", system.ResourceDirectory)
local file = io.open( path, "r" )
for line in file:lines() do
--If targeting Windows Operating System comment the following line out
-- line = string.sub(line, 1, #line - 1)
if(#line >=3 and #line <=5)then
table.insert(words5,line)
elseif (#line >=3 and #line<=9)then
table.insert(words9,line)
elseif (#line >=3 and #line<=13)then
table.insert(words13,line)
end
end
io.close( file )
file = nil
end
The readTextFile function reads in the text file wordlist.txt. We loop through each line of wordlist.txt and, depending on the word's length, insert it into one of three tables, words5 , words9, or words13.
Windows and Unix-based systems handle line endings differently. On Windows, there will be an extra character, which we can remove using the string.sub method. If you are using a Windows machine, you need to add that line of code by removing the -- preceding it.
Whenever you read a file, it's important to remember that once you're done, you should close and nil out the file as shown in the above implementation.
Invoke this function in scene:create. I placed it at the very top, above the other function calls.
function scene:create( event )
local group = self.view
readTextFile()
drawChalkBoard(1,1,1)
--SNIP--
end
Step 2: CreateGuessWord
The createGuessWord function is responsible for getting a random word from the list and returning it. Add the following code to gamescreen.lua.
function createGuessWord()
guessWord = {}
local randomIndex = math.random(#words5)
theWord = words5[randomIndex];
print(theWord)
for i=1, #theWord do
local character= theWord:sub(i,i)
if(character == "'")then
guessWord[i] ="'";
elseif(character=="-")then
guessWord[i] = "-"
else
guessWord[i]="?";
end
end
local newGuessWord = table.concat(guessWord)
return newGuessWord;
end
We use a table, guessWord, to store each letter of the word. The reason for this is that strings are immutable in Lua, meaning that we cannot change a character of the string.
We first generate a random number randomIndex, which will be a number from 1 to however many items are in the table. We then use that number to get a word from the table.
We loop over the word and get a reference to the current character by using the string.sub method. If the current character is an apostrophe or a dash, we put that into the table guessword, otherwise we put a question mark into the table.
We then transform the table guessWord into a string newGuessWord using the table.concat method. Lastly, we return newGuessWord.
Step 3: CreateGuessWordText
The createGuessWordText function creates the Text at the top of the game area that will change depending on the player's guess.
function createGuessWordText()
local options =
{
text = createGuessWord(),
x = 384,
y = 70,
width = 700, --required for multi-line and alignment
font = native.systemFontBold,
fontSize = 50,
align = "center" --new alignment parameter
}
guessWordText = display.newText(options)
guessWordText:setFillColor(0,0,0)
scene.view:insert(guessWordText)
end
The options table holds the various configuration options for the Text. Because the createGuessWord function returns a word as a string, we can just invoke it when setting the text property.
We create the Text by invoking the newText method on Display, passing in the options table. We then set its color and insert it into the scene's view.
Invoke this function in scene:create as shown below.
function scene:create( event )
--SNIP--
drawGallows()
createGuessWordText()
end
A Word about Metatables
The Lua programming language does not have a class system built in. However, by using Lua's metatable construct we can emulate a class system. There is a good example on the Corona website, showing how to implement this.
An important thing to note is that Corona's Display
objects cannot be set as the metatable. This has to do with how the
underlying C language interfaces with them. A simple way to get around
this is to set the Display object as a key on a new table and then set that table as the metatable. This is the approach that we'll take in this tutorial.
If you read the above article on the Corona website, you will have noticed that the __Index metamethod was being used on the metatable. The way the __Index metamethod works, is that when you try to access an absent field in a table, it triggers the interpreter to look for an __Index metamethod. If the __Index is there, it will look for the field and provide the result, otherwise it will result in nil.
2. Implementing the GrowText Class
The game has Text that will grow from small to large over a short period of time, when the user wins or loses the game. We will create this functionality as a module. By having this code as a module, we can reuse it in any project that requires this functionality.
Add the following to growtext.lua, which you created in the first part of this series.
local growText = {}
local growText_mt = {__index = growText}
function growText.new(theText,positionX,positionY,theFont,theFontSize,theGroup)
local theTextField = display.newText(theText,positionX,positionY,theFont,theFontSize)
local newGrowText = {
theTextField = theTextField}
if(theGroup ~=nil)then
theGroup:insert(theTextField)
end
return setmetatable(newGrowText,growText_mt)
end
function growText:setColor(r,b,g)
self.theTextField:setFillColor(r,g,b)
end
function growText:grow()
transition.to( self.theTextField, { xScale=4.0, yScale=4.0, time=2000, iterations = 1,onComplete=function()
local event = {
name = "gameOverEvent",
}
self.theTextField.xScale = 1
self.theTextField.yScale = 1
Runtime:dispatchEvent( event )
end
} )
end
function growText:setVisibility(visible)
if(visible == true)then
self.theTextField.isVisible = true
else
self.theTextField.isVisible = false
end
self.theTextField.xScale = 1
self.theTextField.yScale = 1
end
function growText:setText(theText)
self.theTextField.text = theText
end
return growText
We create the main table growText and the table to be used as the metatable, growText_mt. In the new method, we create the Text object and add it to the table newGrowText that will be set as the metatable. We then add the Text object to the group that was passed in as a parameter, which will be the scene's group in which we instantiate an instance of GrowText.
It's important to make sure that we add it to the scene's group so it
will be removed when the scene is removed. Finally, we set the
metatable.
We have four methods that access the Text object and perform operations on its properties.
setColor
The setColor method sets the color on the Text by invoking the setFillColor method, which takes as parameters the R, G, and B values numbers from 0 to 1.
grow
The grow method uses the Transition library to make the text grow. It enlarges the text by using the xScale and yScale properties. The onComplete function gets invoked once the transition is complete.
In this onComplete function, we reset the Text's xScale and yScale properties to 1 and dispatch an event. The reason we dispatch an event here is to inform the gamescreen that the Text has finished its transition, and therefore the game round is over.
This will all become clear soon, but you may want to read up on dispatchEvent in the documentation.
setVisibility
The setVisibility method simply sets the visibility of the Text, depending on whether true or false was passed in as a parameter. We also reset the xScale and yScale property to 1.
setText
The setText method is used to set the actual text property, depending on whatever string was passed in as the parameter.
Lastly, we return the growText object.
3.createWinLoseText
The createWinLoseText function creates a GrowText object that will show either "YOU WIN!" or "YOU LOSE!", depending on whether the user wins or loses a round. Add the following code to gamescreen.lua.
function createWinLoseText()
winLoseText = growText.new( "YOU WIN",display.contentCenterX,display.contentCenterY-100, native.systemFontBold, 20,scene.view)
winLoseText:setVisibility(false)
end
We invoke this function in scene:create as shown below.
function scene:create( event )
--SNIP--
drawGallows()
createWinLoseText()
end
4.setupButtons
The setupButtons function sets up the buttons, draws them to the screen, and adds an event listener that will call the function checkLetter. The checkLetter function is where the game's logic takes place.
function setupButtons()
local xPos=150
local yPos = 600
for i=1, #alphabetArray do
if (i == 9 or i == 17) then
yPos = yPos + 65
xPos = 150
end
if (i == 25) then
yPos = yPos + 65
xPos = 330
end
local tempButton = widget.newButton{
label = alphabetArray[i],
labelColor = { default ={ 1,1,1}},
onPress = checkLetter,
shape="roundedRect",
width = 40,
height = 40,
cornerRadius = 2,
fillColor = { default={0, 0, 0, 1 }, over={ 0.5, 0.5, 0.5, 0.4 } },
strokeColor = { default={ 0.5, 0.5, 0.5, 0.4 }, over={ 0, 0, 0, 1 } },
strokeWidth = 5
}
tempButton.x = xPos
tempButton.y = yPos
xPos = xPos + 60
table.insert(gameButtons,tempButton)
end
end
We first set the initial x and y positions of the buttons. We then run a for loop over the alphabetArray, which in turn creates a button for every letter of alphabetArray. We want eight buttons per row so we check if i is equal to 9 or 17, and, if true, we increment the yPos variable to create a new row and reset the xPos to the beginning position. If i is equal to 25, we are on the last row and we center the last two buttons.
We create a tempButton by using the newButton method of the widget class, which takes a table of options. There are several ways to affect the visual appearance of the buttons. For this game, we are using the Shape Construction option. I highly suggest you read the documentation on the Button object to learn more about these options.
We set the label by indexing into alphabetArray and set the onPress property to call checkLetter. The rest of the options have to do with the visual appearance and are better explained by reading the documentation as mentioned earlier. Lastly, we insert the tempButton into the table gameButtons so we can reference it later.
If you now invoke this method from scene:create, you should see that the buttons are drawn to the screen. We cannot tap them yet though, because we have not created the checkLetter function. We'll do that in the next step.
function scene:create( event )
--SNIP--
createGuessWordText()
createWinLoseText()
setupButtons()
end
5.checkLetter
The game's logic lives in the checkLetter function. Add the following code to gamescreen.lua.
function checkLetter(event)
local tempButton = event.target
local theLetter = tempButton:getLabel()
theLetter = string.lower(theLetter)
local correctGuess = false
local newGuessWord = ""
tempButton.isVisible = false
for i =1 ,#theWord do
local character= theWord:sub(i,i)
if(character == theLetter)then
guessWord[i] = theLetter
correctGuess = true
end
end
newGuessWord = table.concat(guessWord)
guessWordText.text = newGuessWord
if(correctGuess == false)then
numWrong = numWrong +1
drawHangman(numWrong);
end
if(newGuessWord == theWord)then
wonGame = true
didWinGame(true)
end
if(numWrong == 6) then
for i =1 , #theWord do
guessWord[i] = theWord:sub(i,i)
newGuessWord = table.concat(guessWord)
guessWordText.text = newGuessWord;
end
didWinGame(false)
end
end
The first thing we do is, get the letter the user has guessed by invoking the getLabel method on the button. This returns the button's label, a capitalized letter. We convert this letter to lowercase by invoking the lower method on string, which takes as its parameter the string to be lowercased.
We then set correctGuess to false, newGuessWord to an empty string, and hide the button the user has tapped, because we don't want the user to be able to tap the button more than once per round.
Next, we loop over theWord, get the current character by using the string.sub method, and compare that character to theLetter. If they are equal, then the user has made a correct guess and we update that particular letter in guessWord, setting correctGuess totrue.
We create newGuessWord by using the table.concat method, and update the guessWordText to reflect any changes.
If correctGuess is still false, it means the user has made an incorrect guess. As a result, we increment the numWrong variable and invoke the drawHangman function, passing in numWrong. Depending on how many wrong guesses the user has made, the drawHangman function will draw the hangman as appropriate.
If newGuessWord is equal to theWord, it means the user has guessed the word and we update wonGame totrue, calling the didWinGame function and passing in true.
If numWrong is equal to 6, it means the user has used up all their guesses and the hangman has been fully drawn. We loop through theWord and set every character in guessWord equal to the characters in theWord. We then show the user the correct word.
This bit of code should make sense by now as we have done something similar a couple of times before. Lastly, we call didWinGame and pass in false.
6. Winning and Losing
Step 1: didWinGame
The didWinGame function is called when the use either wins of loses a round.
function didWinGame(gameWon)
hideButtons()
winLoseText:setVisibility(true)
if(gameWon == true)then
winLoseText:setText("YOU WIN!!")
winLoseText:setColor(0,0,1)
else
winLoseText:setText("YOU LOSE!!")
winLoseText:setColor(1,0,0)
end
winLoseText:grow()
end
The first thing we do is invoke hideButtons, which, as the name suggests, hides all of the buttons. We set the winLoseText to be visible, and, depending on whether the user won or lost the round, set its text and color as appropriate. Lastly, we invoke the grow method on the winLoseText.
As we saw earlier in this tutorial, once the text has finished growing, it dispatches an event. We need to use the Runtime to listen for that event. We will be coding this functionality in the upcoming steps.
Step 2: Showing and Hiding the Buttons
The showButtons and hideButtons functions show and hide the buttons by looping through the gameButtons table, setting each of the button's visibility.
function hideButtons()
for i=1, #gameButtons do
gameButtons[i].isVisible = false
end
end
function showButtons()
for i=1, #gameButtons do
gameButtons[i].isVisible = true
end
end
Step 3: drawHangman
The drawHangman function takes a number as a parameter. Depending on what that number is, it draws a certain part of the hangman.
function drawHangman(drawNum)
if(drawNum== 0) then
drawGallows();
elseif(drawNum ==1)then
drawHead();
elseif(drawNum == 2) then
drawBody();
elseif(drawNum == 3) then
drawArm1();
elseif(drawNum == 4) then
drawArm2();
elseif(drawNum == 5) then
drawLeg1();
elseif(drawNum == 6) then
drawLeg2();
end
end
Step 4: Test Progress
It has been quite a while since we have checked our progress, but if you test now you should be able to play a few rounds. To reset the game go to File > Relaunch in the Corona Simulator. Remember, the correct word is being printed to the console so that should help you test everything is working as it should.
When the winLoseText is finished growing, we will start a new round. If the user has won the round, we will go to a new scene where the hangman will do a happy dance. If the user has lost, we will reset everything in gamescreen.lua and begin a new round.
Before we do any of that, however, we need to listen for the gameOverEvent that is being dispatched from the growText class.
7. Game Over
Step 1: Listening for gameOverEvent
Add the following to the scene:show method.
function scene:show( event )
--SNIP--
if ( phase == "did" ) then
Runtime:addEventListener( "gameOverEvent", gameOver )
end
end
We pass the gameOverEvent as the first argument of the addEventListener method. When a gameOverEvent is dispatched, the gameOver function is called.
We should also remove the event listener at some point. We do this in the scene:hide method as shown below.
function scene:hide( event )
local phase = event.phase
if ( phase == "will" ) then
Runtime:removeEventListener( "gameOverEvent", gameOver )
end
end
Step 2: gameOver
Add the following code to gamescreen.lua.
function gameOver()
winLoseText:setVisibility(false)
if(wonGame == true)then
composer.gotoScene("gameoverscreen")
else
newGame()
end
end
If the user has won the game, we invoke the gotoScene method on the composer object and transition to the gameoverscreen. If not, we call the newGame method, which resets the game and creates a new word.
Step 3: newGame
The newGame function resets some variables, sets the buttons to be visible, clears the hangmanGroup, and creates a new word.
function newGame()
clearHangmanGroup()
drawHangman(0)
numWrong = 0
guessWordText.text = createGuessWord()
showButtons()
end
Most of this code should look familiar to you. The clearHangmanGroup function is the only thing new here and we will look at that function in the next step.
Step 4: clearHangmanGroup
The clearHangmanGroup simply loops through the hangmanGroup's numChildren and removes them. Basically, we are clearing everything out so we can start drawing afresh.
function clearHangmanGroup()
for i = hangmanGroup.numChildren, 1 ,-1 do
hangmanGroup[i]:removeSelf()
hangmanGroup[i]=nil
end
Step 5: Test Progress
We are at a point where we can test the progress once again. If you test the game, you can lose a game and a new game should start. You can do this for as long as you wish. In the next step we will get the gameoverscreen wired up.
8. Game Over Screen
Create a new file gameoverscreen.lua and add the following code to it.
local composer = require( "composer" )
local scene = composer.newScene()
local hangmanSprite
local hangmanAudio
function scene:create( event )
local group = self.view
drawChalkBoard()
local options = { width = 164,height = 264,numFrames = 86}
local hangmanSheet = graphics.newImageSheet( "hangmanSheet.png", options )
local sequenceData = {
{ start=1, count=86, time=8000, loopCount=1 }
}
hangmanSprite = display.newSprite( hangmanSheet, sequenceData )
hangmanSprite.x = display.contentCenterX
hangmanSprite.y = display.contentCenterY
hangmanSprite.xScale = 1.5
hangmanSprite.yScale = 1.5
group:insert(hangmanSprite)
end
function scene:show( event )
local phase = event.phase
local previousScene = composer.getSceneName("previous")
composer.removeScene(previousScene)
if ( phase == "did" ) then
hangmanSprite:addEventListener( "sprite", hangmanListener )
hangmanSprite:play()
hangmanAudio = audio.loadSound( "danceMusic.mp3" )
audio.play(hangmanAudio)
end
end
function scene:hide( event )
local phase = event.phase
if ( phase == "will" ) then
hangmanSprite:removeEventListener( "sprite", hangmanListener )
audio.stop(hangmanAudio)
audio.dispose(hangmanAudio)
end
end
function drawChalkBoard()
local chalkBoard = display.newRect( 0, 0, display.contentWidth, display.contentHeight )
chalkBoard:setFillColor(1,1,1 )
chalkBoard.anchorX = 0
chalkBoard.anchorY = 0
scene.view:insert(chalkBoard)
end
function hangmanListener( event )
if ( event.phase == "ended" ) then
timer.performWithDelay(1000,newGame,1)
end
end
function newGame()
composer.gotoScene("gamescreen")
end
scene:addEventListener( "create", scene )
scene:addEventListener( "show", scene )
scene:addEventListener( "hide", scene )
return scene
The hangmanSprite is a SpriteObject that will be used for the dancing animation. The hangmanAudio is an AudioObject that will be used to play some music while the hangman does its dance.
Step 1: Animating the Hangman
As I mentioned, the hangmanSprite is a SpriteObject instance and by having the hangmanSprite be a sprite instead of a regular image, we can animate it. The hangmanSprite has 86 separate images, each one being a different frame for the animation. You can see this by opening hangmanSheet.png in an image editor.
The options table holds the width, height, and numFrames of the individual images in the larger image. The numFrames variable contains the value of the number of smaller images. The hangmanSheet is an instance of the ImageSheet object, which takes as its parameters the image and the options table.
The sequenceData variable is used by the SpriteObject instance, the start key is the image you wish to start the sequence or animation with, and the count key is how many total images there are in the animation. The time key is how long it will take the animation to play and the loopCount key is how many times you wish the animation to play or repeat.
Lastly, you create the SpriteObject instance by passing in the ImageSheet instance and sequenceData.
We set the hangmanSprite's x and y positions, and scale it up to 1.5 its normal size by using the xScale and yScale properties. We insert it into the group to make sure it is removed whenever the scene is removed.
Step 2: Play Audio
Inside scene:show, we remove the previous scene, add an event listener to the hangmanSprite and invoke its play method. We instantiate the hangmanAudio by invoking the loadSound method, passing in danceMusic.mp3. Lastly we call the play method to start the sound playing.
In the scene:hide method, we remove the event listener from the hangmanSprite, invoke stop on the audio instance, and invoke the dispose method. The dispose method ensures that the memory that was allocated to the audio instance is released.
Step 3: Cleaning Up
In the hangmanListener, we check if it is in the end phase, and, if true, it means the animation has finished playing. We then invoke timer.performWithDelay. The timer fires after one second, invoking the newGame method, which uses composer to transition back to the gamescreen to begin a new game.
Conclusion
This was quite a long tutorial, but you now have a functional hangman game with a nice twist. As mentioned at the beginning of this tutorial, try to incorporate difficulty levels. One option would be to have an options screen and implement a SegmentedControl where the user could choose between the lists of 5, 9, and 13 letterwords.
I hope you found this tutorial useful and have learned something new. Thanks for reading.
XAML is the acronym for Extensible Application Markup Language. It’s a markup language based on XML, and its purpose and philosophy are very similar to HTML. Every control that can be placed on a page, whether a button, text box, or custom controls, is identified by a specific XML tag. Like XML, the structure is hierarchical; you can place tags inside other tags. For example, this hierarchical structure is how you can define the layout of a page, thanks to some controls that act as a container for other controls, like Grid or StackPanel.
The following is a sample of the XAML that defines a Windows Phone page:
PhoneApplicationPage is the base class of a Windows Phone page. As you can see, every other control is placed inside it. Notice also the x:Class attribute; it identifies which is the code-behind class that is connected to this page. In this sample, the code that is able to interact with the page will be stored in a class called MainPage that is part of the FirstApp namespace. We can see this class simply by clicking on the black arrow near the XAML file in Solution Explorer. You’ll see another file with the same name of the XAML one plus the .cs extension.
Let’s begin analyzing this simple XAML to introduce some key concepts, like namespaces and resources.
Namespaces
You should already be familiar with namespaces; they’re a way to structure your code by assigning a logical path to your class.
By default, Visual Studio assigns namespaces using the same folder structure of the project. This means that if, for example, you have a class called MyClass stored inside a file in the Classes folder, the default full namespace of your class will be Classes.MyClass.
Namespaces in XAML work exactly the same way. The XAML controls are, in the end, classes that are part of your project, so you have to tell the page where it can find them. In the standard page you can see many examples of namespace declarations:
Every namespace starts with the xmlns prefix, which is the standard XML namespace, followed by a custom prefix (in this sample it’s phone). This prefix is very important, because it’s the one that we’re going to use in the rest of the page to add the controls. Then, we define the full namespace that contains the controls. If the class is part of our project, it’s enough to specify just the namespace; otherwise, we also need to define which assembly (that is the DLL’s name) contains the class.
In the previous example, we want to include controls and resources in our page that are defined inside the Microsoft.Phone.Controls namespace, which is included in the Microsoft.Phone.dll library.
The PhoneApplicationPage class gives you an example of how to use a namespace. Since the PhoneApplicationPage class is part of the Microsoft.Phone.Controls namespace, we have to add the prefix phone to the tag to use it:
<phone:PhoneApplicationPage />
It’s very important to understand how namespaces in XAML work, because we’ll need to declare them every time we use third-party controls (that we created on our own or are part of an external library) or resources, like converters.
Properties and Events
Every control can be customized in two ways: by setting properties and actions. Both are identified by attributes of the XAML tag, but they have two different purposes.
Properties are used to change the look or the behavior of the control. Usually, a property is simply set by assigning a value to the specific attribute. For example, if we want to assign a value to the Text property of a TextBlock control, we can do it in the following way:
<TextBlock Text="This is a text block" />
There’s also an extended syntax that can be used in the case of a complex property that can’t be defined with a plain string. For example, if we need to set an image as a control’s background, we need to use the following code:
Complex properties are set by using a nested tag with the name of the control plus the name of the property, separated by a dot (to set the Background property of the Grid control, we use the Grid.Background syntax).
One important property that is shared by every control is x:Name, which is a string that univocally identifies the control in the page. You can’t have two controls with the same name in a single page. Setting this property is very important if you need to interact with the control in the code behind—you’ll be able to refer to it by using its name.
Events are a way to manage user interactions with the control. One of the most used is Tap, which is triggered when users tap the control.
<Button Tap="OnButtonClicked" />
When you define an action, Visual Studio will automatically prompt you to create an event handler, which is the method (declared in the code behind) that is executed when the event is triggered.
In the previous example, we display the classic “Hello world” message to users when the button is pressed.
Resources
As in HTML, we are able to define CSS styles that can be reused in different parts of the website or the page. XAML has introduced the concept of resources that can be applied to different controls in an application.
Basically every XAML control supports the Resources tag: thanks to the hierarchy structure, every other nested control will be able to use it. In the real world, there are two common places to define a resource: at page and application level.
Page resources are defined in a single page and are available to all the controls that are part of that page. They are placed in a specific property called Resources of the PhoneApplicationPage class.
<phone:PhoneApplicationPage.Resources><!-- you can place resources here --></phone:PhoneApplicationPage.Resources>
Application resources, instead, are globally available and they can be used inside any page of the application. They are defined in the App.xaml file, and the standard template already includes the needed definition.
<Application.Resources><!-- here you can place global resources --></Application.Resources>
Every resource is univocally identified by a name that is assigned using the x:Key property. To apply a resource to a control, we need to introduce the concept of markup extensions. These are special extensions that allow us to apply different behaviors that would otherwise need some code to properly work. There are many markup extensions in the XAML world, and the one needed to apply a resource is called StaticResource. Here is an example of how to use it:
In this sample, the resource is applied to the Style property by including the StaticResource keyword inside braces, followed by the resource name which is the value of the x:Key property.
Resources can be also defined in an external file called ResourceDictionary if you want to better organize your project. To do this, right-click on your project in Visual Studio, click Add> New Item, and choose XML file. Give the file a name that ends with the .xaml extension and include the following definition:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"><!-- put resources here --></ResourceDictionary>
Now you can add it to your project by declaring it in the App.xaml:
Notice the MergedDictionaries property: all the external resource files should be declared here. This way, they will be automatically merged and every resource declared in each external file will be available to every page, as if they were declared inline.
Let’s see now, in detail, which are the most important kind of resources available.
Styles
XAML styles work the same way as CSS styles: you can define the values of different properties together in a single style that can be applied to multiple controls so that they all use the same layout. Here is how a style definition looks:
A style is defined by a Style tag, which has two important attributes: x:Key, the name of the style, and TargetType, the type of controls that will be suitable for this style.
Inside the Style tag you can place as many Setter tags as you want. Each one identifies a control’s property you want to change. Every Setter tag needs two attributes: Property is the control’s property you want to change, and Value is the value you want to assign to the property.
The style defined in the previous example can be applied to any TextBlock control. Its purpose is to change the size of the font to 24 and to apply a bold style to the text.
There are also special types of styles called implicit styles. They are defined in the same way as the styles in the earlier example, except that the x:Key attribute is missing. In this case, the style is automatically applied to every control that is compliant with the value of the TargetType property, according to the scope where the style has been defined. If the style is set as a page resource, it will be applied only to the controls of the page; if the style is set as an application resource, it will be applied to every control in the application.
Data Templates
Data templates are a special type of resource that can be applied to a control to define its appearance. Data templates are often used with controls that are able to display collections of elements, like ListBox or LongListSelector.
A data template simply contains the XAML that will be used to render the specific item. If, for example, we apply this data template to the ItemTemplate property of a ListBox control, the result will be that the defined XAML will be repeated for every item in the collection (for the moment, just ignore the Binding markup extension; we’ll deal with it later when we talk about data binding).
As for every other resource, data templates can be assigned to a property using the StaticResource markup extension.
XAML is a powerful language because it allows us to do more than just create the layout of applications. One of the most interesting features is the animation feature, which can be created using the Storyboard control.
The Storyboard control can be used to define different types of animations:
DoubleAnimation, which can be used to change the numeric value of a property (for example, Width or FontSize).
ColorAnimation, which can be used to interact with properties that define a color (like inside a SolidColorBrush).
PointAnimation, which can be applied to properties that define a point coordinate.
The first two properties are inherited from the Storyboard control: Storyboard.TargetName is used to set the name of the control we’re going to animate, while Storyboard.TargetProperty is the property whose value we’re going to change during the animation.
Next, we define the animation’s behavior: the initial value (the From property), the ending value (the To property) and the duration (the Duration property). The behavior defined in the previous sample animates a Rectangle control by increasing its width from 200 to 400 over 4 seconds.
We can also control the animation more deeply by using the UsingKeyFrames variant available for every animation type:
This way you’re able to control the animation’s timing. In the previous sample, the animation’s type is the same (it’s a DoubleAnimation), but we’re able to set, for a specific time, which is the value to apply using the LinearDoubleKeyFrame tag.
In the previous sample, the Width of the Rectangle control is set to 200 at the beginning. Then, after two seconds, it increases to 250 and after four seconds, it is set to 500.
Easing Animations
Another way to create animations is to use mathematical formulas that are able to apply a realistic behavior to an object, like bouncing, or acceleration and deceleration. You could achieve the same result by using key frames, but it would require a lot of work. For this reason, the animation framework offers a set of predefined easing functions that can be easily applied to an animation.
To add an easing function, you just need to set the EasingFunction property of an animation, as shown in the following sample:
After you’ve defined a regular animation (in the example, it’s a PointAnimation that moves an Ellipse object from the coordinates (0, 0) to (0, 200)), you can set the EasingFunction property with one of the available easing functions. This example shows how to use the BounceEase function, which can be used to apply a bouncing effect to the object (the number of bounces performed is specified with the Bounces property).
Other available easing functions are:
BackEase, which retracts the motion of the animation slightly before it starts.
CircleEase, which applies a circular function to the acceleration animation.
ElasticEase, which creates an animation that resembles an oscillating spring.
Animations are treated like resources. They can be defined as local resources, page resources, or application resources. Unlike traditional resources, Storyboard controls are identified by the x:Name property, like a regular control.
The following sample shows an animation that is set as a page resource:
Thanks to the unique identifier, you’ll be able to control the animation in the code behind. Every Storyboard object offers many methods to control it, like Begin(), Stop(), or Resume(). In the following code you can see the event handlers assigned to two buttons that are used to start and stop the animation:
Data binding is one of the most powerful features provided by XAML. With data binding, you’ll be able to create a communication channel between a UI element and various data sources, which can be another control or a property in one of your classes. Moreover, data binding is heavily connected to the XAML notification system (which we’ll detail later) so that every time you change something in your object, the control displaying it will be automatically updated to reflect the changes and display the new value.
When you create a communication channel using data binding, you define a source (which contains the data to display) and a target (which takes care of displaying the value). By default, the binding channel is set to OneWay mode. This means that when the source changes, the target is updated to display the new value, but not vice versa. If we need to create a two-way communication channel (for example, because the target is a TextBox control and we need to intercept a new value inserted by the user), we can set the Mode property of the binding to TwoWay.
Almost every control in the XAML can participate in data binding. Most of the properties available for a control, in fact, are dependency properties. Beyond offering basic read and write capabilities, these are special properties that support notifications, so that they can notify the other side of the channel that something has changed.
The following example shows how data binding can be used to create a channel between two XAML controls:
The first thing to notice is that to apply binding, we need to use another markup extension, called Binding. With this expression, we connect the Text property of a TextBlock control (the target) to the Value property of a Slider control named Volume (the source).
Since both Text and Value are dependent properties, every time you move the slider, the selected value will be automatically displayed on the screen in the TextBlock control.
Data Binding With Objects
One of the most powerful data binding features is the ability to connect controls with objects that are part of your code. However, first, we need to introduce the DataContext concept. DataContext is a property that is available for almost every control and can be used to define its binding context, which is also automatically inherited by every nested control. When you define an object as DataContext, the control and all its children will have access to all its properties.
Let’s see an example that will help you better understand how it works. Let’s say that you have a class that represents a person:
public class Person
{
public string Name { get; set; }
public string Surname { get; set; }
}
Our goal is to display information about a person using this class. Here is how we can do it using data binding. First, let’s take a look at the code behind:
public MainPage()
{
InitializeComponent();
Person person = new Person();
person.Name = "Matteo";
person.Surname = "Pagani";
Author.DataContext = person;
}
When the page is initialized, we create a new Person object and set a value for the Name and Surname properties. Then, we set this new object as DataContext of the Author control. Let’s see in the XAML page the Author control and how the Name and Surname properties are displayed:
Author is the name assigned to a StackPanel control, which is the container we’ve placed inside different TextBlock controls. In the previous sample we can see the Binding markup extension in action again, this time with a different attribute: Path. We’re using it to tell the XAML which property of the current DataContext to display. Since the DataContext is inherited from the StackPanel control, every TextBlock has access to the properties of the Person object we’ve created in the code behind. Notice that the Path attribute is optional. The two following statements are exactly the same:
The previous code has a flaw. Everything works fine, but if you change the value of one of the Name or Surname properties during the execution, the user interface won’t be updated to display the new value. The reason is that Name and Surname are simple properties, so they aren’t able to notify the user interface that something has changed, unlike dependency properties. For this scenario, the XAML framework has introduced the INotifyPropertyChanged interface that can be implemented by objects that need to satisfy this notification requirement. Here is how the Person class can be changed to implement this interface:
public class Person: INotifyPropertyChanged
{
private string _name;
private string _surname;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged();
}
}
public string Surname
{
get { return _surname; }
set
{
_surname = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
The class now implements the INotifyPropertyChanged interface, allowing us to support an event handler (called PropertyChangedEventHandler) that is triggered every time a property’s value changes. The class also implements a method called OnPropertyChanged() that acts as a wrapper of the event handler and needs to be invoked when a property changes.
We also need a change in our properties. Every time the set of the property is called (meaning that a new value has been assigned) we raise the OnPropertyChanged() method. The result will be that every control bound with the property will be notified of the change and will update its visual status accordingly.
Data Binding and Collections
Data binding is especially helpful when you have to deal with collections of objects like arrays or lists (basically, every framework’s collection types that implement the IEnumerable interface). Almost every control that supports collections, which inherit from the ItemsControl class (like ListBox or LongListSelector), has a property called ItemsSource, which can be directly assigned to a list.
You can control how every object of the collection will be rendered by using the ItemTemplate property. As we’ve seen when we talked about data templates, this property allows us to set which XAML to use to display the object.
Now that we’ve talked about data binding, there’s another important piece to add. In the sample code we used to show data templates, we included some binding expressions to display the name and surname of a person.
When you set a collection as ItemSource, every object that is part of it becomes the DataContext of the ItemTemplate. If, for example, the ItemsSource property of a ListBox is connected to a collection whose type is List<Person>, the controls included in the ItemTemplate will be able to access all the properties of the Person class.
This is the real meaning of the previous sample code: for every Person object that is part of the collection, we’re going to display the values of the Name and Surname properties.
Another important piece of the puzzle when you deal with collections is the ObservableCollection<T> class. It acts like a regular collection, so you can easily add, remove, and move objects. Under the hood, it implements the INotifyPropertyChanged interface so that every time the collection is changed, the UI receives a notification. This way, every time we manipulate the collection (for example, we add a new item), the control that is connected to it will automatically be updated to reflect the changes.
Converters
Converters play an important role in data binding. Sometimes, in fact, you need to modify the source data before it is sent to the target. A common example is when you have to deal with DateTime properties. The DateTime class contains a full representation of a date, including hours, minutes, seconds, and milliseconds. Most of the time, however, you don’t need to display the full representation—often the date is just enough.
This is where converters come in handy. You are able to change the data (or, as shown in the following example, apply different formatting) before sending it to the control that is going to display it using data binding.
To create a converter, you need to add a new class to your project (right-click in Visual Studio, choose Add > Class), and it has to inherit from the IValueConverter interface. The following is a converter sample:
public class DateTimeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
DateTime date = (DateTime)value;
return date.ToShortDateString();
}
return string.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
DateTime date = DateTime.Parse(value.ToString());
return date;
}
return DateTime.Now;
}
}
When you support the IValueConverter interface, you are forced to implement two methods:
Convert() is the method invoked when data from the source is sent to the target.
ConvertBack() does the opposite—it is invoked when data from the target is sent back to the source.
Most of the time it’s enough to implement the Convert() method that is supported by every binding. The ConvertBack() method, instead, is supported only when you have TwoWay binding.
Both methods receive some important information as input parameters:
The value returned from the binding source (which is the one you need to manipulate).
The property that the binding has been applied to.
An optional parameter that can be set in XAML using the ConverterParameter property. This parameter can be used to apply a different behavior in the converter logic.
The current culture.
The previous code sample shows the DateTime example mentioned before. In the Convert() method we get the original value and, after we’ve converted it into a DateTime object, we return a string with the short formatting.
In the ConvertBack() method, we get the string returned from the control and convert it into a DateTime object before sending it back to the code.
Converters are treated like resources—you need to declare them and include them in your binding expression using the StaticResource keyword.
It’s important to highlight that converters can have a negative impact on performance if you use them too heavily, since the binding operation needs to be reapplied every time the data changes. In this case, it’s better to find a way to directly modify the source data or to add a new property in your class with the modified value.
Controls
The Windows Phone 8 SDK includes many built-in controls that can be used to define the user interface of the application. There are so many controls that it’s almost impossible to analyze all of them in this series, so we’ll take a closer look at just the most important ones.
Layout Controls
Some controls simply act as containers for other controls and define the layout of the page. Let’s discuss the most important ones.
StackPanel
The StackPanel control can be used to simply align the nested controls one below the other. It is able to automatically adapt to the size of the child controls.
The Grid control can be used to create table layouts, which fill the entire parent container’s size. It supports rows and columns in which you can place the different controls. The following code sample demonstrates its use:
You define the grid layout by using the RowDefinitions and ColumnDefinitions properties. You can add a RowDefinition tag for every row that the table will have, while the ColumnDefinition tag can be used to set the number of columns. For every row and column you can set the width and height, or you can simply omit them so that they automatically adapt to the nested controls.
To define a control’s position inside the grid, we’re going to use two attached properties, which are special dependency properties that are inherited from the Grid control and can be used with every control. With the Grid.Row property, we can set the row’s number, and with Grid.Column, we can set the column’s number.
The previous sample code is used to display a TextBlock in the cell that is placed in the first row, second column of the grid, as you can see in the following figure:
ScrollViewer
The ScrollViewer control is a container, but it doesn’t define a layout. If you want to arrange nested controls, you still have to use another container like a StackPanel or a Grid. The purpose of this control is to create a layout that is bigger than the size of the screen. This way, users will be able to scroll down to see the rest of the user interface.
This control is useful, for example, when you have to display text that doesn’t fit the size of the page.
<ScrollViewer><StackPanel><TextBlock TextWrapping="Wrap" Text="This can be long text" /></StackPanel></ScrollViewer>
Border
The Border control’s purpose is to display a border. This is useful because it’s able to contain a child control that will be wrapped into the border.
The following sample shows how to wrap an image inside a red border:
There are some key properties of the Border control. The first one is BorderThickness, which specifies the border’s thickness. You can specify a single value, as we did in the previous sample. In this case, the same thickness is applied to every side. You can also specify multiple values to give every border a different size, as in the following sample:
The second important property is BorderBrush, which is used to set the brush that is applied to the border. It can use any of the available XAML brushes. By default, it accepts a SolidColorBrush, so you can simply specify the color you want to apply.
Another useful property is Padding, which can be used to specify the distance between the border and the child control, as shown in the following sample:
The purpose of these controls is to display something to users, such as text, an image, etc.
TextBlock
TextBlock is one of the basic XAML controls and is used to display text on the screen. Its most important property is Text, which, of course, contains the text to display. You have many properties to choose from to modify the text’s appearance, such as FontSize, FontWeight, and FontStyle, and you can automatically wrap the text in multiple lines in case the text is too long by setting the TextWrapping property to true.
<TextBlock Text="This is long and bold text" TextWrapping="Wrap" FontWeight="Bold" />
You can also apply different formatting to the text without using multiple TextBlock controls by using the Run tag, which can be used to split the text as shown in the following sample:
The RichTextBlock control is similar to TextBlock, but it offers more control over the formatting styles that can be applied to the text. Like the one offered by HTML, you can define paragraphs, apply different text styles, and more.
<RichTextBox><Paragraph><Bold>This is a paragraph in bold</Bold></Paragraph><Paragraph><Italic>This is a paragraph in italics</Italic><LineBreak /></Paragraph></RichTextBox>
Image
The Image control can be used to display images. You can set the Source property with a remote path (an image’s URL published on the Internet) or a local path (a file that is part of your Visual Studio project). You can’t assign a path that refers to an image stored in the local storage of the application. We’ll see later in this series how to manage this limitation.
You can also control how the image will be adapted to fill the control’s size by using the Stretch property, which can have the following values:
Uniform: The default value. The image is resized to fit the container while keeping the original aspect ratio so that the image won’t look distorted. If the container’s aspect ratio is different from the image’s, the image will look smaller than the available space.
Fill: The image is resized to fit the container, ignoring the aspect ratio. It will fill all the available space, but if the control’s size doesn’t have the same aspect ratio as the image, it will look distorted.
UniformToFill is a mix of the previous values. If the image has a different aspect ratio than the container, the image is clipped so that it can keep the correct aspect ratio and, at the same time, fill all the available space.
None: The image is displayed in its original size.
Input Controls
These controls are used to get input from users.
TextBox
TextBox is another basic XAML control, and it’s simply a way to collect text from users. The entered text will be stored in the Text property of the control. When users tap a TextBox control, the virtual keyboard is automatically opened. As a developer, you can control the keyboard’s type that is displayed according to the data types you’re collecting.
For example, you can display a numeric keyboard if users only need to input a number; or you can use an email keyboard (which provides easy access to symbols like @) if you’re collecting an email address.
You can control the keyboard’s type with the InputScope property. The list of supported values is very long and can be found in the MSDN documentation. Some of the most used are:
Text for generic text input with dictionary support.
Number for generic number input.
TelephoneNumber for specific phone number input (it’s the same keyboard that is displayed when you compose a number in the native Phone application).
EmailNameOrAddress which adds quick access to symbols like @.
Url which adds quick access to common domains like .com or .it (depending on the keyboard’s language).
Search which provides automatic suggestions.
<TextBox InputScope="TelephoneNumber" />
Tip: If the TextBox control is used to collect generic text, always remember to set the InputScope property to Text. This way, users will get support from the autocomplete and autocorrection tools.
PasswordBox
PasswordBox works exactly like the TextBox control, except that the inserted characters are automatically converted into dots so that people near the user won’t be able to read the text. As the name of the control implies, it’s typically used to collect passwords.
Theme Resources
One of a developer’s goals should be to keep the user interface of her or his application as consistent as possible with the guidelines provided by the operating system. To help achieve this goal, the SDK offers many out-of-the-box resources that can be applied to controls to get the same look and feel of the native applications. These styles are typically used with controls like TextBox, TextBlock, or RadioButton, and they provide a standard set of visual features (like font size, color, opacity, etc.) that are consistent with the other applications.
Another good reason to use theme resources is that they are aware of the theme set by users. For example, you can use the PhoneAccentBrush style if you want to give a control the same color as the phone’s accent color.
The many theme resources are split into different categories like brush resources, color resources, font names and styles, and text resources. You can find a complete list of the available styles in the MSDN documentation. They are simply applied using the Style property offered by every control, as shown in the following sample:
In this category, we can collect all the controls that we can use to interact with users, like Button, CheckBox, or RadioButton.
The text to display is set with the Content property, like in the following sample:
<Button Content="Tap me" Tap="OnClickMeClicked" />
The Content property can also be complex so that you can add other XAML controls. In the following sample we can see how to insert an image inside a button:
These controls offer many ways to interact with users. The most common events are Click, Tap, and DoubleTap.
Note: Click and Tap are the same event—they are both triggered when users press the control. Tap has been introduced in Windows Phone 7.5 to be more consistent with the touch interface, but to avoid breaking old apps, the Click event is still supported.
The Windows Phone Signature Controls
Most of the controls we’ve seen so far are part of the XAML framework and are available on every other XAML-based technology, like Silverlight, WPF, and Windows Store apps.
However, there are some controls that are available only on the Windows Phone platform, since they are specific for the mobile experience. Let’s take a look at them.
Panorama
The Panorama control is often used in Windows Phone applications since it’s usually treated as a starting point. The control gets its name because an oversized image is used as the background of the page. Users are able to swipe to the left or to the right to see the other available pages. Since the image is bigger than the size of the page, the phone applies a parallax effect that is visually pleasing for users.
The other main feature of the Panorama control is that users can get a sneak peek of the next page. The current page doesn’t occupy all of the available space because a glimpse of the next page is displayed on the right edge.
A Panorama control is typically used to provide an overview of the content that is available in an application. It’s a starting point, not a data container. For example, it’s not appropriate to use a panorama page to display all the news published on a blog. It’s better, instead, to display only the latest news items and provide a button to redirect users to another page, where they will be able to see all of them.
From a developer’s point of view, a Panorama control is composed of different pages: each one is a PanoramaItem control that contains the layout of the page.
The Panorama can have a generic title, like the application’s title (which is assigned to the Title property), while every page can have its own specific title (which is assigned to the Header property).
The Pivot control, from a technical and user interaction perspective, works in a similar way to the Panorama control—users can swipe the screen left or right to view the other pages. The difference, from the users’ point of view, is that the view will fit the screen’s size. Users can see which page is next because its header is displayed next to the current page’s header in gray.
However, the Pivot control is used for purposes not shared by the Panorama control:
To display one kind of information applied to different contexts. The previous figure is a good example. The information on each page is the same (the weather forecast), but referred to in different contexts (the cities).
To display different kinds of information that refer to the same context. A contact’s details page in the People Hub on a Windows Phone is a good example of this—you have much information (the contact’s details, social network updates, conversations, etc.), but it all belongs to the same context (the contact).
As previously anticipated, the XAML for the Pivot control works like the XAML for the Panorama control. The main control is called Pivot, while the nested controls that represent the pages are called PivotItem.
The ApplicationBar is a control placed at the bottom of the page and is used as a quick access for functions that are connected to the current view.
You can add two element types to an application bar:
Icons are always displayed (unless the ApplicationBar is minimized), and up to four can be displayed at once.
Menu items are simply text items that are displayed only when the application bar is opened. There’s no limit to the number of items that can be included.
The ApplicationBar does not behave like the other XAML controls. It is not part of the page—in fact, it’s declared outside the main Grid, the one called LayoutRoot—and it doesn’t inherit from the FrameworkElement class, like every other control. The biggest downside of this is that the control doesn’t support binding; you’ll have to rely on third-party libraries, like the implementation available in the Cimbalino Toolkit for Windows Phone.
ApplicationBar is a property of the PhoneApplicationPage class, which contains the real ApplicationBar definition. It’s not part of the standard XAML namespaces, but it’s part of the following namespace, which should already be declared in every standard Windows Phone page:
Icon buttons are declared directly inside the ApplicationBar tag. The base class is ApplicationBarIconButton, and the most important properties are Text (the icon description) and IconUri (the path of the image used as the icon), while Click is the event handler that is invoked when a user taps the button.
Menu items, instead, are grouped inside a property of the ApplicationBar control called MenuItems. You can add as many ApplicationBarMenuItem controls as you want. They behave like the button icons except that, since they’re just text, the IconUri property is missing.
Another important limitation of the ApplicationBar control is that we’re not able to assign the x:Name property to an ApplicationBarIconButton or ApplicationBarMenuItem control. This means that if we need to change a property’s value in the code behind, we can’t simply use the notation ControlName.PropertyName.
The work-around is to use the code behind to directly access the collections that contain the buttons and menu items that are available because of the ApplicationBar object. They are called Buttons and MenuItems, as you can see in the following code sample:
ApplicationBarIconButton iconButton = this.ApplicationBar.Buttons[0] as ApplicationBarIconButton;
iconButton.Text = "New text";
ApplicationBarMenuItem menuItem = this.ApplicationBar.MenuItems[0] as ApplicationBarMenuItem;
menuItem.Text = "New text";
The purpose of this sample is to get access to the first icon button and first menu item inside the ApplicationBar, and to change the value of the Text property.
In the end, there are two other ways to customize the ApplicationBar. The first is to minimize it. This way, only the three dots at the right margin will be displayed; icons won’t be visible. To achieve this, you have to set the Mode property to Minimized.
The other way is to change the opacity. You can set the Opacity property with a value between 0 (transparent) and 1 (opaque). The biggest difference is that when ApplicationBar is translucent, the page content will go below the bar and fit the entire size of the screen; when the bar is opaque, the content won’t fit all of the available screen space since the bottom of the page will be reserved for the ApplicationBar.
Displaying Collections of Data With the LongListSelector
One of the most common requirements in an application is to display a collection of items which can be retrieved from a remote service or a local database. The Windows Phone SDK has included, since the beginning, some controls for this purpose, like ItemsControl and ListBox. In Windows Phone 8, Microsoft has introduced a new and more powerful control, which previously was available as part of the Windows Phone Toolkit (you’ll find more details about this toolkit later in this article). This control is called LongListSelector and has many advantages over other similar controls:
better performance
virtualization support to avoid loading all the data at the same time, instead loading data only when it is needed
group support to turn the list into a jump list, so that data is grouped in categories and users can easily jump from one to another (for example, in the People Hub, contacts are grouped by the first letter)
Creating a Flat List
The LongListSelector control can be used like a regular ListBox to display a flat list of items, without grouping. In this case, you just need to set the IsGroupingEnabled property to false. Except for this modification, you’ll be able to use a LongListSelector like a standard ItemsControl. You’ll define the ItemTemplate property with a DataTemplate to define the item layout, and you’ll assign the collection you want to display to the ItemsSource property.
Creating a list grouped by the first letter of the items is a bit more complicated since we have to change our data source. It won’t be a flat collection of data anymore. In addition, if we want to keep the user experience consistent with other applications, we’ll need to create a jump list with all the letters of the alphabet. The groups that don’t have any members will be disabled so that users can’t tap them.
To achieve this result, Microsoft offers a class called AlphaKeyGroup<T>, which represents a letter of the alphabet and all the items that start with it. However, this class is not part of the Windows Phone SDK, and should be manually added to your project by right-clicking on your project in the Solution Explorer in Visual Studio, and choosing Add new class. The following code example is the full implementation.
public class AlphaKeyGroup<T> : List<T>
{
/// <summary>
/// The delegate that is used to get the key information.
/// </summary>
/// <param name="item">An object of type T.</param>
/// <returns>The key value to use for this object.</returns>
public delegate string GetKeyDelegate(T item);
/// <summary>
/// The key of this group.
/// </summary>
public string Key { get; private set; }
/// <summary>
/// Public constructor.
/// </summary>
/// <param name="key">The key for this group.</param>
public AlphaKeyGroup(string key)
{
Key = key;
}
/// <summary>
/// Create a list of AlphaGroup<T> with keys set by a SortedLocaleGrouping.
/// </summary>
/// <param name="slg">The </param>
/// <returns>The items source for a LongListSelector.</returns>
private static List<AlphaKeyGroup<T>> CreateGroups(SortedLocaleGrouping slg)
{
List<AlphaKeyGroup<T>> list = new List<AlphaKeyGroup<T>>();
foreach (string key in slg.GroupDisplayNames)
{
list.Add(new AlphaKeyGroup<T>(key));
}
return list;
}
/// <summary>
/// Create a list of AlphaGroup<T> with keys set by a SortedLocaleGrouping.
/// </summary>
/// <param name="items">The items to place in the groups.</param>
/// <param name="ci">The CultureInfo to group and sort by.</param>
/// <param name="getKey">A delegate to get the key from an item.</param>
/// <param name="sort">Will sort the data if true.</param>
/// <returns>An items source for a LongListSelector.</returns>
public static List<AlphaKeyGroup<T>> CreateGroups(IEnumerable<T> items, CultureInfo ci, GetKeyDelegate getKey, bool sort)
{
SortedLocaleGrouping slg = new SortedLocaleGrouping(ci);
List<AlphaKeyGroup<T>> list = CreateGroups(slg);
foreach (T item in items)
{
int index = 0;
if (slg.SupportsPhonetics)
{
//Checks whether your database has the string yomi as an item.
//If it does not, then generate Yomi or ask users for this item.
//index = slg.GetGroupIndex(getKey(Yomiof(item)));
}
else
{
index = slg.GetGroupIndex(getKey(item));
}
if (index >= 0 && index < list.Count)
{
list[index].Add(item);
}
}
if (sort)
{
foreach (AlphaKeyGroup<T> group in list)
{
group.Sort((c0, c1) => { return ci.CompareInfo.Compare(getKey(c0), getKey(c1)); });
}
}
return list;
}
}
The main features of this class are:
It inherits from List<T>, so it represents a list of elements.
It has a property called Key, which is the key that identifies the group (the letter of the alphabet).
It uses a special collection type called SortedLocaleGroup, which is able to manage the cultural differences between one language and another.
It offers a method called CreateGroups(), which is the one we’re going to use to group our data.
To better explain how to use the AlphaKeyGroup<T> class, let’s use a real example. Let’s define a collection of people that we want to group by the first letters of their names, in a similar way the People Hub does. The first step is to create a class that represents a single person:
public class Person
{
public string Name { get; set; }
public string Surname { get; set; }
public string City { get; set; }
}
Then, when the application starts, we populate the list with a set of fake data, as shown in the following sample:
void LongListSelectorAlphabetic_Loaded(object sender, RoutedEventArgs e)
{
List<Person> people = new List<Person>
{
new Person
{
Name = "John",
Surname = "Doe",
City = "Como"
},
new Person
{
Name = "Mark",
Surname = "Whales",
City = "Milan"
},
new Person
{
Name = "Ricky",
Surname = "Pierce",
City = "New York"
}
};
}
Now it’s time to use the AlphaGroupKey<T> class to convert this flat list into a grouped list by calling the CreateGroups() method.
List<AlphaKeyGroup<Person>> list = AlphaKeyGroup<Person>.CreateGroups(people,
Thread.CurrentThread.CurrentUICulture,
p => p.Name, true);
The method requires four parameters:
The collection that we want to group: In the sample, it’s the collection of Person objects we created.
The culture to use to generate the alphabet letters: The standard practice is to use the value of the Thread.CurrentThread.CurrentUICulture property, which is the primary language set by the user for the phone.
The object’s property that will be used for grouping: This is specified using a lambda expression. In the sample, the list will be grouped by the first letter of the name.
The last parameter, a Boolean type, is used to determine whether to apply ordering: If set to true, the collection will be alphabetically ordered.
What we get in return is a collection of AlphaKeyGroup<T> objects, one for each letter of the alphabet. This is the collection that we need to assign to the ItemsSource property of the LongListSelectorControl.
List<AlphaKeyGroup<Person>> list = AlphaKeyGroup<Person>.CreateGroups(people,
Thread.CurrentThread.CurrentUICulture,
p => p.Name, true);
People.ItemsSource = list;
However, this code isn’t enough—we also need to supply additional templates in XAML that are used to define the jump list layout.
The first property to set is called GroupHeaderTemplate, and it represents the header that is displayed in the list before every group. In the case of an alphabetic list, it’s the letter itself. The following XAML code shows a sample template, which recreates the same look and feel of native apps:
With this layout, the letter is placed inside a square with a background color the same as the accent color of the phone. Take note of two important things:
Some properties of the Border control use the PhoneAccentBrush resource. It’s one of the theme resources previously described, which identifies the accent color of the phone.
The Text property of the TextBlock control is bound with the Key property of the AlphaGroupKey<T> class. This way, we are able to display the group’s letter inside the square.
The second property to define is called JumpListStyle and, unlike the previous property, it’s not a template but a style. Its purpose is to define the look and feel of the jump list, which is the view that is displayed when users tap a letter. It displays all the letters of the alphabet so that users can tap one of them and quickly jump to that group.
Here is a sample definition which, again, recreates the look and feel of native applications—all the letters of the alphabet are displayed side by side in multiple rows.
This style uses two converters which are part of the Windows Phone Toolkit called JumpListItemBackgroundConverter and JumpListItemForegroundConverter. As you can see in the ItemTemplate that is applied to the control, these converters are used to define the text color and the Border background color. Their purpose is to manage empty groups. If the collection contains one or more items, it will be displayed in white with the phone’s accent color as a background. If instead, the letter is associated to a group with no items, both the text and the background will be gray. This way, users can immediately see that the group is disabled, and tapping it won’t produce any effect.
In the end, don’t forget that as for every other control based on the ItemsControl class, you’ll need to set the ItemTemplate property with a DataTemplate to define how a single item will look. The following sample shows a basic implementation, where name and surname are displayed one below the other:
Notice the HideEmptyGroups property, which can be used to hide all the groups in the list that don’t contain any items.
Creating a List Grouped by Category
In some cases, we might need to group items by a custom field instead of the first letter. Let’s look at another example. We want to group Person objects by the City field; we need, then, a class to replace the AlphaKeyGroup<T>, since it’s not suitable for our scenario.
Let’s introduce the Group<T> class, which is much simpler:
public class Group<T> : List<T>
{
public Group(string name, IEnumerable<T> items)
: base(items)
{
this.Key = name;
}
public string Key
{
get;
set;
}
}
Under the hood, this class behaves like AlphaKeyGroup<T>. It inherits from List<T>, so it represents a collection of items, and defines a group which is identified by a key (which will be the category’s name).
private List<Group<T>> GetItemGroups<T>(IEnumerable<T> itemList, Func<T, string> getKeyFunc)
{
IEnumerable<Group<T>> groupList = from item in itemList
group item by getKeyFunc(item)
into g
orderby g.Key
select new Group<T>(g.Key, g);
return groupList.ToList();
}
The previous method takes as input parameters:
the flat collection to group.
a function (expressed with a lambda expression) to set the object’s property we want to use for grouping
The following example is the code we can use to group the collection of Person objects by the City property:
List<Group<Person>> groups = GetItemGroups(people, x => x.City);
PeopleByCity.ItemsSource = groups;
The result returned by the GetItemGroups() method can be directly assigned to the ItemsSource property of the LongListSelector control.
As we did in the alphabetical grouping scenario, we still need to define the layout of the group headers and the jump list. We can reuse the resources we’ve previously defined, but, if we want to achieve a better result, we can adapt them so that the background rectangle fills the size of the category’s name, as shown in the following code example:
The two key concepts to keep in mind when you interact with a LongListSelector control (or any other control that inherits from the ItemsControl class) are:
the SelectionChanged event, which is triggered every time the user taps one of the elements on the list
the SelectedItem property, which stores the items that have been selected by the user
With the combination of these two items, you’ll be able to detect when and which item has been selected by the user, and properly respond. For example, you can redirect the user to a page where he or she can see the details of the selected item.
The previous sample shows a LongListSelector control that has been subscribed to the SelectionChanged event. The following sample, instead, shows the event handler code:
private void People_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
Person selectedPerson = People.SelectedItem as Person;
string uri = string.Format("/DetailPage.xaml?Id={0}", selectedPerson.Id);
NavigationService.Navigate(new Uri(uri, UriKind.Relative));
}
The previous sample shows a LongListSelector control that has been subscribed to the SelectionChanged event. The following sample, instead, shows the event handler code:
private void People_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
Person selectedPerson = People.SelectedItem as Person;
string uri = string.Format("/DetailPage.xaml?Id={0}", selectedPerson.Id);
NavigationService.Navigate(new Uri(uri, UriKind.Relative));
}
Thanks to the SelectedItem property, we retrieve the selected Person object and redirect the user to another page called DetailPage.xaml to display the details of the selected person. We’ll cover navigation in-depth in the next article.
The Windows Phone Toolkit
In this article we’ve seen just the basic controls, but you’ll notice that the SDK is missing many controls that are used in other applications. Most of them are available in a library called Windows Phone Toolkit, which is available on CodePlex and NuGet. It’s maintained directly by Microsoft and is a way to keep a separate, faster development process than the one needed to release a new SDK version.
Here is a brief list of the most important available controls:
ToggleSwitch: Especially helpful for settings pages, since it’s a switch that can be used to define a Boolean value (on/off).
ContextMenu: A menu that can be displayed when users tap and hold an item.
DatePicker and TimePicker: Used to select a date or time respectively.
WrapPanel: A special container that can align nested controls next to each other and automatically wrap to a new line if no space remains.
AutoCompleteBox: A special TextBox that can prompt suggestions to the user based on the text the user is typing.
ListPicker: Used to display a list of items. It is useful especially when asking users to choose between different values.
ExpanderView: Used to create elements that can be expanded using a tree structure to display other elements. A good example of this control is the Mail app; it’s used to display conversations.
MultiSelectList: Similar to a ListBox, but automatically places check boxes next to each item, allowing users to select multiple items.
PhoneTextBox: A special TextBox control with many built-in features, like support for action icons, placeholders, a character counter, etc.
HubTile: Can be used to recreate the Start screen experience offered by Live Tiles inside an application.
CustomMessageBox: A special MessageBox that offers many more options than the standard one, like button customization, custom template support, etc.
Rating: Gives users the ability to rate something inside an application. The user experience is similar to the one offered by the Store, where users can vote on an application.
SpeechTextBox: Another special TextBox that supports vocal recognition so that users can dictate text instead of type it.
The Windows Phone Toolkit also includes a Windows Phone frame replacement (the class that manages views and navigation) with built-in support for animations so that you can easily add transition effects when users move from one page of the application to another. Let’s dig deeper into this topic.
Page Transitions
In this article, we’ve learned how to animate objects that are placed inside a page. Often, one of the easiest ways to improve the look and feel of our application is to add an animation during the transition from one page to another. The Windows Phone Toolkit plays an important role in achieving this result since the standard application frame provided by the SDK, which manages all our application’s pages, doesn’t support transitions. Instead, the toolkit offers a specific frame class, called TransitionFrame, which can be used to replace the original frame class, which is called PhoneApplicationFrame.
The first step is to replace the original frame. You can do this in the App.xaml.cs file, which contains a hidden region titled Phone application initialization. If you expand it, you’ll find a method called InitializePhoneApplication() which, among other things, initializes the application’s frame with the following code:
RootFrame = new PhoneApplicationFrame();
Replacing the original frame is easy. Once you’ve installed the Windows Phone Toolkit, you can change the initialization of the RootFrame object by using the TransitionFrame class, which is part of the Microsoft.Phone.Controls namespace, as shown in the following sample:
RootFrame = new TransitionFrame();
Now you’re ready to set which animations you want to use in your pages according to the navigation type. Let’s start by looking at the following sample code, which should be added in every page you want to animate with a transition. The code has to be placed under the main PhoneApplicationPage node before the page’s layout is defined:
Transitions are added using the TransitionService, which is part of the Windows Phone Toolkit (make sure that the xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit" namespace is added to your page).
It supports two type of animations, specified using the NavigationInTransition property:
In animations, which are applied when users move to the current page
Out animations, which are applied when users move away from the current page
For every transition type, you also have the chance to specify two additional conditions:
The Backward property is used to specify the transition to use when users reach the page after pressing the Back button.
The Forward property is used to specify the transition to use when users reach the page through the regular navigation flow.
Finally, you’re able to specify which transition you want to use for every scenario. The toolkit offers a list of predefined animations, like RotateTransition to apply a rotation effect, TurnstileTransition to simulate browsing a book, or SlideTransition to apply a slide effect. Every transition offers a Mode property, which can be used to customize the effect that is applied. The previous sample shows a TurnstileTransition effect applied during every navigation, with a different effect according to the navigation’s type (backward or forward).
Customize the Turnstile Transition
The animation framework can be used not only to apply a transition to the entire page, but also to every control in the page. This scenario is supported by the TurnstileFeatherTransition control, which applies a turnstile effect to the controls in the page. You can decide which order will be used to animate and enter the controls in the page with the FeatheringIndex property.
The first step is to add a TransitionService to your page and define a set of TurnstileFeatherTransition animations, as in the following sample:
Then, you’re able to apply the TurnstileFeatherTransition.FeatheringIndex property to any control in the page and specify the order in which they will appear, starting with 0 to set the first control that will enter in the page.
In the previous sample, the three TextBlock controls will appear in the page starting with the first one (whose FeatheringIndex is equal to 0) and ending with the last one (whose FeatheringIndex is equal to 2).
Conclusion
It’s been a long journey so far and we’ve just scratched the surface; covering all the XAML features would require an entire book. In this article, we’ve considered some key concepts used in Windows Phone development:
We introduced the basic XAML concepts like properties, events, namespaces, and resources.
We learned how data binding works. It’s one of the most powerful XAML features and it’s very important to understand it to be productive.
We’ve seen some of the basic controls that are included in the SDK and how we can use them to define the user interface of our application.
We discussed some controls that are specific to the Windows Phone experience, like the Panorama, Pivot, and ApplicationBar controls.
This tutorial represents a chapter from Windows Phone 8 Succinctly, a free eBook from the team at Syncfusion.
In the previous tutorial, you created your first Swift project in Xcode, a simple to-do application. In this installment of Swift from Scratch, we're going to add the ability to create to-do items. Along the way, you will learn about actions, delegation, and properties.
Prerequisites
If you'd like to follow along with me, then make sure that you have Xcode 6.3 or higher installed on your machine. At the time of writing, Xcode 6.3 is in beta and available from Apple's iOS Dev Center to registered iOS developers.
The reason for requiring Xcode 6.3 or higher is to be able to take advantage of Swift 1.2, which Apple introduced a few days ago. Swift 1.2 introduces a number of great additions that we'll take advantage of in the rest of this series.
1. Adding Items
At the end of this tutorial, the user will be able to add new to-do items by tapping a button in the navigation bar, presenting a view with a text field and a button. Let's start by creating the view controller that will handle adding new to-do items, the AddItemViewController class.
Step 1: Create AddItemViewController
Choose New > File... from Xcode's File menu and select the Cocoa Class template from the list of iOS > Source templates.
Name the class AddItemViewController and make sure it inherits from UIViewController. Double-check that Language is set to Swift and Also create XIB file is unchecked.
Tell Xcode where you'd like to save the file for the AddItemViewController class and click Create.
Step 2: Adding Outlets and Actions
Before we create the user interface of the AddItemViewController class, we need to create an outlet for the text field and two actions, one for a cancel button in the navigation bar and another one for the create button below the text field.
Adding an outlet should be familiar by now. Create an outlet in the AddItemViewController class and name it textField as shown below.
Creating an action is very similar to creating an instance method. In fact, the @IBAction attribute is nothing more than a hint for Interface Builder. By prefixing a method with the @IBAction attribute, Interface Builder is aware of the method, which enables us to connect it in the storyboard. We'll leave the bodies of both actions empty for now.
Open Main.storyboard in the Project Navigator and drag a View Controller from the Object Library on the right. With the view controller selected, open the Identity Inspector on the right and set Custome Class > Class to AddItemViewController.
To add a navigation bar to the add item view, select the Add Item View Controller and choose Embed In > Navigation Controller from the Editor menu. This will make the Add Item View Controller the root view controller of a navigation controller.
The next step is to add a bar button item to the navigation bar of the View Controller—not the Add Item View Controller—and set its Identifier to Add in the Attributes Inspector.
When the user taps the Add button, the Add Item View Controller should be presented modally. To accomplish this, Control-Drag from the Add button to the Navigation Controller, selecting present modally from the menu that pops up. This will create a segue from the Add Item View Controller to the new Navigation Controller.
Drag a text field and a button from the Object Library and add them to the Add Item View Controller scene. Select the Add Item View Controller and connect the textField outlet with the text field and the create: action with the button. The create: action should be triggered when the Touch Up Inside event is fired. Change the title of the button to Change and add the necessary layout constraints to the text field and button.
To finish the user interface, add a bar button item to the top left of the navigation bar of the Add Item View Controller and set its Identifier to Cancel. With the Add Item View Controller selected, open the Connections Inspector and connect the cancel: action to the Cancel button.
Build and run the application by pressing Command-R to verify that everything is hooked up correctly.
2. Implement Delegate Protocol
When the user taps the Create button to add a to-do item, the add item view controller needs to notify the view controller. Delegation is a perfect solution for this scenario. The process is pretty simple.
We create a delegate protocol the ViewController class conforms to. When the AddItemViewController instance is created—when the user taps the Add button—the ViewController object is set as the delegate of the AddItemViewController instance, enabling the latter to notify the ViewController instance when a new to-do item is created. Let's break it down to better understand this process.
Open AddItemViewController.swift and declare the AddItemViewControllerDelegate protocol below the import statement at the top. The protocol declaration looks similar to a class declaration. The protocol keyword is followed by the name of the protocol.
The concept is very similar to protocols in Objective-C. The name of the protocol is AddItemViewControllerDelegate and it defines one method, controller(controller:didAddItem:).
Step 2: Declare delegate Property
The object that needs to implement the delegate protocol is the delegate of AddItemViewController. We first need to create a property for the delegate as shown in the snippet below.
class AddItemViewController: UIViewController {
@IBOutlet var textField: UITextField!
var delegate: AddItemViewControllerDelegate?
...
}
The delegate property is of type AddItemViewControllerDelegate?, an optional type, since we cannot be certain that the delegate property is not nil. Note that the name of the protocol isn't wrapped in angle brackets as in Objective-C.
Step 3: Implement Actions
The delegate method, controller(controller:didAddItem:), will be invoked in the create: action as shown below. To keep the example simple, we don't do any validation on the user's input. We safely unwrap the value stored in the delegate property, which means that the delegate method is only invoked if the delegate property is set. The value of the text field is temporarily stored in a constant named item.
@IBAction func create(sender: AnyObject) {
let item = self.textField.text
if let delegate = self.delegate {
delegate.controller(self, didAddItem: item)
}
}
The implementation of the cancel: action is easy. All we do is dismissing the AddItemViewController instance.
There's one piece of the puzzle missing though. The delegate property of the AddItemViewController instance is not being set at the moment. We can resolve this by implementing the prepareForSegue(_:sender:) method in the ViewController class. First, however, we need to revisit the storyboard.
Open Main.storyboard and select the segue connecting the Add button with the Navigation Controller. Open the Attributes Inspector and set the segue's Identifier toAddItemViewController.
Next, implement the prepareForSegue(_:sender:) method in the ViewController class as shown below. Note the override keyword prefixing the method. This should be familiar by now.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "AddItemViewController" {
let navigationController = segue.destinationViewController as? UINavigationController
let addItemViewController = navigationController?.topViewController as? AddItemViewController
if let viewController = addItemViewController {
viewController.delegate = self
}
}
}
We start by checking the identifier of the segue, making sure we're preparing for the correct segue. We then ask the segue for its destination view controller. You may expect this to be the AddItemViewController instance, but remember that we made the view controller the root view controller of a navigation controller. This means that we need to ask the navigation controller, the segue's destination view controller, for its top view controller.
The addItemViewController constant is of type AddItemViewController? because of the use of the as? keyword. In other words, by using as? we downcast the value of the topViewController property to an optional type.
In the if statement, we unwrap the optional and set the delegate property to the ViewController instance.
I'm sure you've noticed the use of several optionals in the implementation of the prepareForSegue(_:sender:) method. When interacting with Objective-C APIs, it's always better to play it safe. While sending messages to nil is perfectly fine in Objective-C, it isn't in Swift. Because of this key difference, you always need to be careful when interacting with Objective-C APIs in Swift. The above example illustrates this well.
Step 5: Implementing the AddItemViewControllerDelegate Protocol
Implementing the AddItemViewControllerDelegate protocol is similar to the implementation of the UITableViewDataSource protocol. We start by conforming the ViewController class to the protocol as shown below.
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, AddItemViewControllerDelegate {
...
}
Next, we implement the methods of the AddItemViewControllerDelegate protocol, which boils down to implementing the controller(controller:didAddItem:) method. We add the new item to the view controller's items property, reload the table view, and dismiss the add item view controller.
Build and run the application to test if you can add new items to the to-do list. We currently don't validate the user's input. As an exercise, show an alert view to the user if they tap the Create button and the text field is empty. Adding a blanc to-do isn't very useful. Right?
In this tutorial, you learned how to declare and implement a custom protocol. You also learned how to create actions and hook them up in Interface Builder. In next week's installment, we're going to complete our to-do application by adding the ability to delete to-do items and we'll also improve the application's user experience.
In the previous tutorial, you created your first Swift project in Xcode, a simple to-do application. In this installment of Swift from Scratch, we're going to add the ability to create to-do items. Along the way, you will learn about actions, delegation, and properties.
Prerequisites
If you'd like to follow along with me, then make sure that you have Xcode 6.3 or higher installed on your machine. At the time of writing, Xcode 6.3 is in beta and available from Apple's iOS Dev Center to registered iOS developers.
The reason for requiring Xcode 6.3 or higher is to be able to take advantage of Swift 1.2, which Apple introduced a few days ago. Swift 1.2 introduces a number of great additions that we'll take advantage of in the rest of this series.
1. Adding Items
At the end of this tutorial, the user will be able to add new to-do items by tapping a button in the navigation bar, presenting a view with a text field and a button. Let's start by creating the view controller that will handle adding new to-do items, the AddItemViewController class.
Step 1: Create AddItemViewController
Choose New > File... from Xcode's File menu and select the Cocoa Class template from the list of iOS > Source templates.
Name the class AddItemViewController and make sure it inherits from UIViewController. Double-check that Language is set to Swift and Also create XIB file is unchecked.
Tell Xcode where you'd like to save the file for the AddItemViewController class and click Create.
Step 2: Adding Outlets and Actions
Before we create the user interface of the AddItemViewController class, we need to create an outlet for the text field and two actions, one for a cancel button in the navigation bar and another one for the create button below the text field.
Adding an outlet should be familiar by now. Create an outlet in the AddItemViewController class and name it textField as shown below.
Creating an action is very similar to creating an instance method. In fact, the @IBAction attribute is nothing more than a hint for Interface Builder. By prefixing a method with the @IBAction attribute, Interface Builder is aware of the method, which enables us to connect it in the storyboard. We'll leave the bodies of both actions empty for now.
Open Main.storyboard in the Project Navigator and drag a View Controller from the Object Library on the right. With the view controller selected, open the Identity Inspector on the right and set Custome Class > Class to AddItemViewController.
To add a navigation bar to the add item view, select the Add Item View Controller and choose Embed In > Navigation Controller from the Editor menu. This will make the Add Item View Controller the root view controller of a navigation controller.
The next step is to add a bar button item to the navigation bar of the View Controller—not the Add Item View Controller—and set its Identifier to Add in the Attributes Inspector.
When the user taps the Add button, the Add Item View Controller should be presented modally. To accomplish this, Control-Drag from the Add button to the Navigation Controller, selecting present modally from the menu that pops up. This will create a segue from the Add Item View Controller to the new Navigation Controller.
Drag a text field and a button from the Object Library and add them to the Add Item View Controller scene. Select the Add Item View Controller and connect the textField outlet with the text field and the create: action with the button. The create: action should be triggered when the Touch Up Inside event is fired. Change the title of the button to Change and add the necessary layout constraints to the text field and button.
To finish the user interface, add a bar button item to the top left of the navigation bar of the Add Item View Controller and set its Identifier to Cancel. With the Add Item View Controller selected, open the Connections Inspector and connect the cancel: action to the Cancel button.
Build and run the application by pressing Command-R to verify that everything is hooked up correctly.
2. Implement Delegate Protocol
When the user taps the Create button to add a to-do item, the add item view controller needs to notify the view controller. Delegation is a perfect solution for this scenario. The process is pretty simple.
We create a delegate protocol the ViewController class conforms to. When the AddItemViewController instance is created—when the user taps the Add button—the ViewController object is set as the delegate of the AddItemViewController instance, enabling the latter to notify the ViewController instance when a new to-do item is created. Let's break it down to better understand this process.
Open AddItemViewController.swift and declare the AddItemViewControllerDelegate protocol below the import statement at the top. The protocol declaration looks similar to a class declaration. The protocol keyword is followed by the name of the protocol.
The concept is very similar to protocols in Objective-C. The name of the protocol is AddItemViewControllerDelegate and it defines one method, controller(controller:didAddItem:).
Step 2: Declare delegate Property
The object that needs to implement the delegate protocol is the delegate of AddItemViewController. We first need to create a property for the delegate as shown in the snippet below.
class AddItemViewController: UIViewController {
@IBOutlet var textField: UITextField!
var delegate: AddItemViewControllerDelegate?
...
}
The delegate property is of type AddItemViewControllerDelegate?, an optional type, since we cannot be certain that the delegate property is not nil. Note that the name of the protocol isn't wrapped in angle brackets as in Objective-C.
Step 3: Implement Actions
The delegate method, controller(controller:didAddItem:), will be invoked in the create: action as shown below. To keep the example simple, we don't do any validation on the user's input. We safely unwrap the value stored in the delegate property, which means that the delegate method is only invoked if the delegate property is set. The value of the text field is temporarily stored in a constant named item.
@IBAction func create(sender: AnyObject) {
let item = self.textField.text
if let delegate = self.delegate {
delegate.controller(self, didAddItem: item)
}
}
The implementation of the cancel: action is easy. All we do is dismissing the AddItemViewController instance.
There's one piece of the puzzle missing though. The delegate property of the AddItemViewController instance is not being set at the moment. We can resolve this by implementing the prepareForSegue(_:sender:) method in the ViewController class. First, however, we need to revisit the storyboard.
Open Main.storyboard and select the segue connecting the Add button with the Navigation Controller. Open the Attributes Inspector and set the segue's Identifier toAddItemViewController.
Next, implement the prepareForSegue(_:sender:) method in the ViewController class as shown below. Note the override keyword prefixing the method. This should be familiar by now.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "AddItemViewController" {
let navigationController = segue.destinationViewController as? UINavigationController
let addItemViewController = navigationController?.topViewController as? AddItemViewController
if let viewController = addItemViewController {
viewController.delegate = self
}
}
}
We start by checking the identifier of the segue, making sure we're preparing for the correct segue. We then ask the segue for its destination view controller. You may expect this to be the AddItemViewController instance, but remember that we made the view controller the root view controller of a navigation controller. This means that we need to ask the navigation controller, the segue's destination view controller, for its top view controller.
The addItemViewController constant is of type AddItemViewController? because of the use of the as? keyword. In other words, by using as? we downcast the value of the topViewController property to an optional type.
In the if statement, we unwrap the optional and set the delegate property to the ViewController instance.
I'm sure you've noticed the use of several optionals in the implementation of the prepareForSegue(_:sender:) method. When interacting with Objective-C APIs, it's always better to play it safe. While sending messages to nil is perfectly fine in Objective-C, it isn't in Swift. Because of this key difference, you always need to be careful when interacting with Objective-C APIs in Swift. The above example illustrates this well.
Step 5: Implementing the AddItemViewControllerDelegate Protocol
Implementing the AddItemViewControllerDelegate protocol is similar to the implementation of the UITableViewDataSource protocol. We start by conforming the ViewController class to the protocol as shown below.
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, AddItemViewControllerDelegate {
...
}
Next, we implement the methods of the AddItemViewControllerDelegate protocol, which boils down to implementing the controller(controller:didAddItem:) method. We add the new item to the view controller's items property, reload the table view, and dismiss the add item view controller.
Build and run the application to test if you can add new items to the to-do list. We currently don't validate the user's input. As an exercise, show an alert view to the user if they tap the Create button and the text field is empty. Adding a blanc to-do isn't very useful. Right?
In this tutorial, you learned how to declare and implement a custom protocol. You also learned how to create actions and hook them up in Interface Builder. In next week's installment, we're going to complete our to-do application by adding the ability to delete to-do items and we'll also improve the application's user experience.
When building complex applications, you'll often want to reuse the same group of views in various places of the application. One way to solve this problem is by creating a view that encapsulates the logic and layout of a group of views so you can reuse them without duplicating code in various places of the project. In this tutorial, you'll learn how to use compound views to create custom views that are easily reusable.
1. Introduction
On Android, a view composed of a group of views is called a compound view or a compound component. In this tutorial, you'll build a control to select a value from a list that scrolls from side to side. We'll name the compound a side spinner since the default view of the Android SDK to pick a value from a list is called a spinner. The following screenshot illustrates what we'll be creating in this tutorial.
2. Project Setup
To get started, you must create a new Android project with Android 4.0 as the minimum required SDK level. This project should only contain a blank activity called MainActivity. The Activity does nothing more than initializing the layout as you can see in the following code snippet.
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
The layout for MainActivity is located in the /res/layout/activity_main.xml file and it should only only contain an empty RelativeLayout in which the compound view will be displayed later.
To create a compound view, you must create a new class that manages the views in the compound view. For the side spinner, you need two Button views for the arrows and a TextView view to display the selected value.
To get started, create the /res/layout/sidespinner_view.xml layout file that we'll use for the side spinner class, making sure to wrap the three views in a <merge> tag.
Next, we need to create the SideSpinner class that inflates this layout and sets the arrows as the background images for the buttons. At this point, the compound view doesn't do anything since there is nothing to show yet.
public class SideSpinner extends LinearLayout {
private Button mPreviousButton;
private Button mNextButton;
public SideSpinner(Context context) {
super(context);
initializeViews(context);
}
public SideSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
initializeViews(context);
}
public SideSpinner(Context context,
AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
initializeViews(context);
}
/**
* Inflates the views in the layout.
*
* @param context
* the current context for the view.
*/
private void initializeViews(Context context) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.sidespinner_view, this);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// Sets the images for the previous and next buttons. Uses
// built-in images so you don't need to add images, but in
// a real application your images should be in the
// application package so they are always available.
mPreviousButton = (Button) this
.findViewById(R.id.sidespinner_view_previous);
mPreviousButton
.setBackgroundResource(android.R.drawable.ic_media_previous);
mNextButton = (Button)this
.findViewById(R.id.sidespinner_view_next);
mNextButton
.setBackgroundResource(android.R.drawable.ic_media_next);
}
}
You'll notice that the compound view extends the LinearLayout view group. This means that any layout using the compound view has access to the attributes of the linear layout. As a result, the layout for the compound view is a bit different than usual, the root tag is a <merge> tag instead of the tag for a view group like <LinearLayout> or <RelativeLayout>.
When you add the compound view to the layout of MainActivity, the tag for the compound view will act as a <LinearLayout> tag. A compound view class can derive from any class deriving from ViewGroup, but in this case the linear layout is the most appropriate since the views are laid out horizontally.
4. Add the Compound View to a Layout
At this point, the project compiles but nothing is visible since the compound view is not in the layout of MainActivity. The side spinner view must be added to the layout of the activity like any other view. The name of the tag is the full name of the SideSpinner class, including the namespace.
To add the side spinner to MainActivity, add the following to the relative layout in the /res/layout/activity_main.xml file.
The attributes available in the <SideSpinner> tag are attributes of the linear layout since the SideSpinner class we created extends the LinearLayout class. If you launch the project, the side spinner should be visible, but it doesn't contain any values yet.
5. Add Methods to the Compound View
There are still a few things missing if we want to actually use the side spinner. We should be able to add new values to the spinner, select a value, and get the selected value.
The easiest way to add new behaviors to a compound view is to add new public methods to the SideSpinner class. These methods can be used by any Activity that has a reference to the view.
private CharSequence[] mSpinnerValues = null;
private int mSelectedIndex = -1;
/**
* Sets the list of value in the spinner, selecting the first value
* by default.
*
* @param values
* the values to set in the spinner.
*/
public void setValues(CharSequence[] values) {
mSpinnerValues = values;
// Select the first item of the string array by default since
// the list of value has changed.
setSelectedIndex(0);
}
/**
* Sets the selected index of the spinner.
*
* @param index
* the index of the value to select.
*/
public void setSelectedIndex(int index) {
// If no values are set for the spinner, do nothing.
if (mSpinnerValues == null || mSpinnerValues.length == 0)
return;
// If the index value is invalid, do nothing.
if (index < 0 || index >= mSpinnerValues.length)
return;
// Set the current index and display the value.
mSelectedIndex = index;
TextView currentValue;
currentValue = (TextView)this
.findViewById(R.id.sidespinner_view_current_value);
currentValue.setText(mSpinnerValues[index]);
// If the first value is shown, hide the previous button.
if (mSelectedIndex == 0)
mPreviousButton.setVisibility(INVISIBLE);
else
mPreviousButton.setVisibility(VISIBLE);
// If the last value is shown, hide the next button.
if (mSelectedIndex == mSpinnerValues.length - 1)
mNextButton.setVisibility(INVISIBLE);
else
mNextButton.setVisibility(VISIBLE);
}
/**
* Gets the selected value of the spinner, or null if no valid
* selected index is set yet.
*
* @return the selected value of the spinner.
*/
public CharSequence getSelectedValue() {
// If no values are set for the spinner, return an empty string.
if (mSpinnerValues == null || mSpinnerValues.length == 0)
return "";
// If the current index is invalid, return an empty string.
if (mSelectedIndex < 0 || mSelectedIndex >= mSpinnerValues.length)
return "";
return mSpinnerValues[mSelectedIndex];
}
/**
* Gets the selected index of the spinner.
*
* @return the selected index of the spinner.
*/
public int getSelectedIndex() {
return mSelectedIndex;
}
The onFinishInflate method of the compound view is called when all the views in the layout are inflated and ready to use. This is the place to add your code if you need to modify views in the compound view.
With the methods you just added to the SideSpinner class, behavior for the buttons selecting the previous and next value can now be added. Replace the existing code in the onFinishInflate method with the following:
@Override
protected void onFinishInflate() {
// When the controls in the layout are doing being inflated, set
// the callbacks for the side arrows.
super.onFinishInflate();
// When the previous button is pressed, select the previous value
// in the list.
mPreviousButton = (Button) this
.findViewById(R.id.sidespinner_view_previous);
mPreviousButton
.setBackgroundResource(android.R.drawable.ic_media_previous);
mPreviousButton.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
if (mSelectedIndex > 0) {
int newSelectedIndex = mSelectedIndex - 1;
setSelectedIndex(newSelectedIndex);
}
}
});
// When the next button is pressed, select the next item in the
// list.
mNextButton = (Button)this
.findViewById(R.id.sidespinner_view_next);
mNextButton
.setBackgroundResource(android.R.drawable.ic_media_next);
mNextButton.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
if (mSpinnerValues != null
&& mSelectedIndex < mSpinnerValues.length - 1) {
int newSelectedIndex = mSelectedIndex + 1;
setSelectedIndex(newSelectedIndex);
}
}
});
// Select the first value by default.
setSelectedIndex(0);
}
With the newly created setValues and setSelectedIndex methods, we can now initialize the side spinner from our code. As with any other view, you need to find the side spinner view in the layout with the findViewById method. We can then call any public method on the view from the object returned, including the ones we just created.
The following code snippet shows how to update the onCreate method of the MainActivity class to show a list of values in the side spinner, using the setValues method. We can also select the second value in the list by default by invoking the setSelectedIndex method.
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initializes the side spinner from code.
SideSpinner fruitsSpinner;
fruitsSpinner = (SideSpinner)this
.findViewById(R.id.sidespinner_fruits);
CharSequence fruitList[] = { "Apple",
"Orange",
"Pear",
"Grapes" };
fruitsSpinner.setValues(fruitList);
fruitsSpinner.setSelectedIndex(1);
}
}
If you launch the application the side spinner should work as expected. The list of values is shown and the value Orange is selected by default.
6. Add Layout Attributes to the Compound View
The views available in the Android SDK can be modified through code, but some attributes can also be set directly in the corresponding layout. Let's add an attribute to the side spinner that sets the values the side spinner needs to display.
To create a custom attribute for the compound view, we first need to define the attribute in the /res/values/attr.xml file. Every attribute of the compound view should be grouped in a styleable with a <declare-styleable>tag. For the side spinner, the name of the class is used as shown below.
In the <attr> tag, the name attribute contains the identifier used to refer to the new attribute in the layout and the format attribute contains the type of the new attribute.
For the list of values, the reference type is used since the attribute will refer to a list of strings defined as a resource. The value types that are normally used in layouts can be used for your custom attributes, including boolean, color, dimension, enum, integer, float and string.
Here is how to define the resource for a list of strings that the values attribute of the side spinner will refer to. It must be added to the /res/values/strings.xml file as shown below.
To test the new values attribute, create a side spinner view in the MainActivity layout below the existing side spinner. The attribute must be prefixed with a namespace added to the RelativeLayout, such as xmlns:sidespinner="http://schemas.android.com/apk/res-auto". This is what the final layout in /res/layout/activity_main.xml should look like.
Finally, the SideSpinner class need to be modified to read the values attribute. The value of every attribute of the view is available in the AttributeSet object that is passed in as a parameter of the view's constructor.
To get the value of your custom values attribute, we first call the obtainStyledAttributes method of the AttributeSet object with the name of the styleable containing the attribute. This returns the list of attributes for that styleable as a TypedArray object.
We then call the getter method of the TypedArray object that has the right type for the attribute you want, passing the identifier of the attribute as a parameter. The following code block shows how to modify the constructor of the side spinner to get the list of values and set them in the side spinner.
If you launch the application, you should see two side spinners that work independently from one another.
7. Save and Restore State
The last step we need to complete is saving and restoring the state of the compound view. When an activity is destroyed and recreated, for example, when the device is rotated, the values of native views with a unique identifier are automatically saved and restored. This currently isn't true for the side spinner.
The state of the views isn't saved. The identifiers of the views in the SideSpinner class are not unique since it can be reused many times. This means that we are responsible for saving and restoring the values of the views in the compound view. We do this by implementing the onSaveInstanceState, onRestoreInstanceState, and dispatchSaveInstanceState methods. The following code block shows how to do this for the side spinner.
/**
* Identifier for the state to save the selected index of
* the side spinner.
*/
private static String STATE_SELECTED_INDEX = "SelectedIndex";
/**
* Identifier for the state of the super class.
*/
private static String STATE_SUPER_CLASS = "SuperClass";
@Override
protected Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putParcelable(STATE_SUPER_CLASS,
super.onSaveInstanceState());
bundle.putInt(STATE_SELECTED_INDEX, mSelectedIndex);
return bundle;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
Bundle bundle = (Bundle)state;
super.onRestoreInstanceState(bundle
.getParcelable(STATE_SUPER_CLASS));
setSelectedIndex(bundle.getInt(STATE_SELECTED_INDEX));
}
else
super.onRestoreInstanceState(state);
}
@Override
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
// Makes sure that the state of the child views in the side
// spinner are not saved since we handle the state in the
// onSaveInstanceState.
super.dispatchFreezeSelfOnly(container);
}
@Override
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
// Makes sure that the state of the child views in the side
// spinner are not restored since we handle the state in the
// onSaveInstanceState.
super.dispatchThawSelfOnly(container);
}
Conclusion
The side spinner is now complete. Both side spinners work as expected and their values are restored if the activity is destroyed and recreated. You can now apply what you've learned to reuse any group of views in an Android application by using compound views.
When building complex applications, you'll often want to reuse the same group of views in various places of the application. One way to solve this problem is by creating a view that encapsulates the logic and layout of a group of views so you can reuse them without duplicating code in various places of the project. In this tutorial, you'll learn how to use compound views to create custom views that are easily reusable.
1. Introduction
On Android, a view composed of a group of views is called a compound view or a compound component. In this tutorial, you'll build a control to select a value from a list that scrolls from side to side. We'll name the compound a side spinner since the default view of the Android SDK to pick a value from a list is called a spinner. The following screenshot illustrates what we'll be creating in this tutorial.
2. Project Setup
To get started, you must create a new Android project with Android 4.0 as the minimum required SDK level. This project should only contain a blank activity called MainActivity. The Activity does nothing more than initializing the layout as you can see in the following code snippet.
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
The layout for MainActivity is located in the /res/layout/activity_main.xml file and it should only only contain an empty RelativeLayout in which the compound view will be displayed later.
To create a compound view, you must create a new class that manages the views in the compound view. For the side spinner, you need two Button views for the arrows and a TextView view to display the selected value.
To get started, create the /res/layout/sidespinner_view.xml layout file that we'll use for the side spinner class, making sure to wrap the three views in a <merge> tag.
Next, we need to create the SideSpinner class that inflates this layout and sets the arrows as the background images for the buttons. At this point, the compound view doesn't do anything since there is nothing to show yet.
public class SideSpinner extends LinearLayout {
private Button mPreviousButton;
private Button mNextButton;
public SideSpinner(Context context) {
super(context);
initializeViews(context);
}
public SideSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
initializeViews(context);
}
public SideSpinner(Context context,
AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
initializeViews(context);
}
/**
* Inflates the views in the layout.
*
* @param context
* the current context for the view.
*/
private void initializeViews(Context context) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.sidespinner_view, this);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// Sets the images for the previous and next buttons. Uses
// built-in images so you don't need to add images, but in
// a real application your images should be in the
// application package so they are always available.
mPreviousButton = (Button) this
.findViewById(R.id.sidespinner_view_previous);
mPreviousButton
.setBackgroundResource(android.R.drawable.ic_media_previous);
mNextButton = (Button)this
.findViewById(R.id.sidespinner_view_next);
mNextButton
.setBackgroundResource(android.R.drawable.ic_media_next);
}
}
You'll notice that the compound view extends the LinearLayout view group. This means that any layout using the compound view has access to the attributes of the linear layout. As a result, the layout for the compound view is a bit different than usual, the root tag is a <merge> tag instead of the tag for a view group like <LinearLayout> or <RelativeLayout>.
When you add the compound view to the layout of MainActivity, the tag for the compound view will act as a <LinearLayout> tag. A compound view class can derive from any class deriving from ViewGroup, but in this case the linear layout is the most appropriate since the views are laid out horizontally.
4. Add the Compound View to a Layout
At this point, the project compiles but nothing is visible since the compound view is not in the layout of MainActivity. The side spinner view must be added to the layout of the activity like any other view. The name of the tag is the full name of the SideSpinner class, including the namespace.
To add the side spinner to MainActivity, add the following to the relative layout in the /res/layout/activity_main.xml file.
The attributes available in the <SideSpinner> tag are attributes of the linear layout since the SideSpinner class we created extends the LinearLayout class. If you launch the project, the side spinner should be visible, but it doesn't contain any values yet.
5. Add Methods to the Compound View
There are still a few things missing if we want to actually use the side spinner. We should be able to add new values to the spinner, select a value, and get the selected value.
The easiest way to add new behaviors to a compound view is to add new public methods to the SideSpinner class. These methods can be used by any Activity that has a reference to the view.
private CharSequence[] mSpinnerValues = null;
private int mSelectedIndex = -1;
/**
* Sets the list of value in the spinner, selecting the first value
* by default.
*
* @param values
* the values to set in the spinner.
*/
public void setValues(CharSequence[] values) {
mSpinnerValues = values;
// Select the first item of the string array by default since
// the list of value has changed.
setSelectedIndex(0);
}
/**
* Sets the selected index of the spinner.
*
* @param index
* the index of the value to select.
*/
public void setSelectedIndex(int index) {
// If no values are set for the spinner, do nothing.
if (mSpinnerValues == null || mSpinnerValues.length == 0)
return;
// If the index value is invalid, do nothing.
if (index < 0 || index >= mSpinnerValues.length)
return;
// Set the current index and display the value.
mSelectedIndex = index;
TextView currentValue;
currentValue = (TextView)this
.findViewById(R.id.sidespinner_view_current_value);
currentValue.setText(mSpinnerValues[index]);
// If the first value is shown, hide the previous button.
if (mSelectedIndex == 0)
mPreviousButton.setVisibility(INVISIBLE);
else
mPreviousButton.setVisibility(VISIBLE);
// If the last value is shown, hide the next button.
if (mSelectedIndex == mSpinnerValues.length - 1)
mNextButton.setVisibility(INVISIBLE);
else
mNextButton.setVisibility(VISIBLE);
}
/**
* Gets the selected value of the spinner, or null if no valid
* selected index is set yet.
*
* @return the selected value of the spinner.
*/
public CharSequence getSelectedValue() {
// If no values are set for the spinner, return an empty string.
if (mSpinnerValues == null || mSpinnerValues.length == 0)
return "";
// If the current index is invalid, return an empty string.
if (mSelectedIndex < 0 || mSelectedIndex >= mSpinnerValues.length)
return "";
return mSpinnerValues[mSelectedIndex];
}
/**
* Gets the selected index of the spinner.
*
* @return the selected index of the spinner.
*/
public int getSelectedIndex() {
return mSelectedIndex;
}
The onFinishInflate method of the compound view is called when all the views in the layout are inflated and ready to use. This is the place to add your code if you need to modify views in the compound view.
With the methods you just added to the SideSpinner class, behavior for the buttons selecting the previous and next value can now be added. Replace the existing code in the onFinishInflate method with the following:
@Override
protected void onFinishInflate() {
// When the controls in the layout are doing being inflated, set
// the callbacks for the side arrows.
super.onFinishInflate();
// When the previous button is pressed, select the previous value
// in the list.
mPreviousButton = (Button) this
.findViewById(R.id.sidespinner_view_previous);
mPreviousButton
.setBackgroundResource(android.R.drawable.ic_media_previous);
mPreviousButton.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
if (mSelectedIndex > 0) {
int newSelectedIndex = mSelectedIndex - 1;
setSelectedIndex(newSelectedIndex);
}
}
});
// When the next button is pressed, select the next item in the
// list.
mNextButton = (Button)this
.findViewById(R.id.sidespinner_view_next);
mNextButton
.setBackgroundResource(android.R.drawable.ic_media_next);
mNextButton.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
if (mSpinnerValues != null
&& mSelectedIndex < mSpinnerValues.length - 1) {
int newSelectedIndex = mSelectedIndex + 1;
setSelectedIndex(newSelectedIndex);
}
}
});
// Select the first value by default.
setSelectedIndex(0);
}
With the newly created setValues and setSelectedIndex methods, we can now initialize the side spinner from our code. As with any other view, you need to find the side spinner view in the layout with the findViewById method. We can then call any public method on the view from the object returned, including the ones we just created.
The following code snippet shows how to update the onCreate method of the MainActivity class to show a list of values in the side spinner, using the setValues method. We can also select the second value in the list by default by invoking the setSelectedIndex method.
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initializes the side spinner from code.
SideSpinner fruitsSpinner;
fruitsSpinner = (SideSpinner)this
.findViewById(R.id.sidespinner_fruits);
CharSequence fruitList[] = { "Apple",
"Orange",
"Pear",
"Grapes" };
fruitsSpinner.setValues(fruitList);
fruitsSpinner.setSelectedIndex(1);
}
}
If you launch the application the side spinner should work as expected. The list of values is shown and the value Orange is selected by default.
6. Add Layout Attributes to the Compound View
The views available in the Android SDK can be modified through code, but some attributes can also be set directly in the corresponding layout. Let's add an attribute to the side spinner that sets the values the side spinner needs to display.
To create a custom attribute for the compound view, we first need to define the attribute in the /res/values/attr.xml file. Every attribute of the compound view should be grouped in a styleable with a <declare-styleable>tag. For the side spinner, the name of the class is used as shown below.
In the <attr> tag, the name attribute contains the identifier used to refer to the new attribute in the layout and the format attribute contains the type of the new attribute.
For the list of values, the reference type is used since the attribute will refer to a list of strings defined as a resource. The value types that are normally used in layouts can be used for your custom attributes, including boolean, color, dimension, enum, integer, float and string.
Here is how to define the resource for a list of strings that the values attribute of the side spinner will refer to. It must be added to the /res/values/strings.xml file as shown below.
To test the new values attribute, create a side spinner view in the MainActivity layout below the existing side spinner. The attribute must be prefixed with a namespace added to the RelativeLayout, such as xmlns:sidespinner="http://schemas.android.com/apk/res-auto". This is what the final layout in /res/layout/activity_main.xml should look like.
Finally, the SideSpinner class need to be modified to read the values attribute. The value of every attribute of the view is available in the AttributeSet object that is passed in as a parameter of the view's constructor.
To get the value of your custom values attribute, we first call the obtainStyledAttributes method of the AttributeSet object with the name of the styleable containing the attribute. This returns the list of attributes for that styleable as a TypedArray object.
We then call the getter method of the TypedArray object that has the right type for the attribute you want, passing the identifier of the attribute as a parameter. The following code block shows how to modify the constructor of the side spinner to get the list of values and set them in the side spinner.
If you launch the application, you should see two side spinners that work independently from one another.
7. Save and Restore State
The last step we need to complete is saving and restoring the state of the compound view. When an activity is destroyed and recreated, for example, when the device is rotated, the values of native views with a unique identifier are automatically saved and restored. This currently isn't true for the side spinner.
The state of the views isn't saved. The identifiers of the views in the SideSpinner class are not unique since it can be reused many times. This means that we are responsible for saving and restoring the values of the views in the compound view. We do this by implementing the onSaveInstanceState, onRestoreInstanceState, and dispatchSaveInstanceState methods. The following code block shows how to do this for the side spinner.
/**
* Identifier for the state to save the selected index of
* the side spinner.
*/
private static String STATE_SELECTED_INDEX = "SelectedIndex";
/**
* Identifier for the state of the super class.
*/
private static String STATE_SUPER_CLASS = "SuperClass";
@Override
protected Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putParcelable(STATE_SUPER_CLASS,
super.onSaveInstanceState());
bundle.putInt(STATE_SELECTED_INDEX, mSelectedIndex);
return bundle;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
Bundle bundle = (Bundle)state;
super.onRestoreInstanceState(bundle
.getParcelable(STATE_SUPER_CLASS));
setSelectedIndex(bundle.getInt(STATE_SELECTED_INDEX));
}
else
super.onRestoreInstanceState(state);
}
@Override
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
// Makes sure that the state of the child views in the side
// spinner are not saved since we handle the state in the
// onSaveInstanceState.
super.dispatchFreezeSelfOnly(container);
}
@Override
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
// Makes sure that the state of the child views in the side
// spinner are not restored since we handle the state in the
// onSaveInstanceState.
super.dispatchThawSelfOnly(container);
}
Conclusion
The side spinner is now complete. Both side spinners work as expected and their values are restored if the activity is destroyed and recreated. You can now apply what you've learned to reuse any group of views in an Android application by using compound views.
Universal Windows apps enable you to target every Windows device in one solution. You develop once, share most of your code, and deploy on Windows, Windows Phone, or Xbox.
The goal is to maximize the reuse of code. You can share code, user controls, styles, strings, and other assets between the Windows Phone and Windows 8 projects in Visual Studio. This reduces the effort needed in building and maintaining an app for each type of device.
Introduction
From a developer's perspective, a universal Windows app is not a single binary that runs on multiple platforms. Rather, it takes the form of a Visual Studio solution containing multiple projects, one project for each targeted platform in addition to a shared project containing code and resources shared between platforms. A lot of code can be shared between the projects as Windows Phone 8.1 implements the majority of the WinRT APIs that Windows 8.1 implements.
You can create a Windows Phone application using the Silverlight runtime (version 8.0 or 8.1) or the WinRT runtime (the one from universal windows apps). The WinRT runtime enables you to create one application that will run on Windows, Windows Phone, and even Xbox One.
We are using the XAML framework to develop an app for multiple platforms. In the current version there is an API convergence of 90%, but there still is a small set not converged yet. Windows Phone features only available in the Silverlight framework are:
Lenses support
VoIP support
Camera capture task
Clipboard APIs
Lock screen wallpaper API
In this tutorial, I will use a universal Windows app template to create a Hex Clock app, a precise hexadecimal color clock. It goes through the whole 24 hours color range, from #000000 to #235959. With every tick of the clock, the app's background changes to the color corresponding to the current time converted to hexadecimal. It uses the same implementation as Hex Color JS Clock to generate the hex code of the current time.
The design was inspired by a Windows Phone 7 clock app tutorial on Tuts+. While the clock app only targets Windows Phone, we will use its design to make a similar app for Windows Phone 8.1 and Windows 8.1. The below screenshot shows what we are going to build.
In this tutorial, I will discuss the following topics which are relevant to developing universal Windows apps:
the structure of universal Windows apps
switching startup projects in Visual Studio
context switcher for universal Windows apps in the Visual Studio editor
how to write cross-platform code in the shared project
how to add support for Windows or Windows Phone to an existing project
building a universal Windows app from scratch
1. Structure of Universal Windows Apps
A universal Windows app is a collection of three projects enclosed in an optional solution folder. The Windows and Windows Phone projects are platform projects and are responsible for creating the application packages (.appx), targeting the respective platforms. These projects contain assets that are specific to the platform being targeted.
The shared project is a container for code that runs on both platforms. They don’t have a binary output, but their contents are imported by the platform projects and used as part of the build process to generate the app packages (.appx).
The screenshot below shows the solution that Visual Studio creates when you choose the project template for a Blank App (Universal Apps).
Visual Studio 2013 Update 2 introduces the new feature that is universal Windows apps. Download and install this update before you start building universal Windows apps.
2. Switching Startup Projects
When you run the solution, the project that runs is the one that's selected as the startup project. To set the startup project, right-click on the project node in the Solution Explorer and choose the option Set as Startup Project. You can quickly switch the startup project from the Debug target drop-down that now enumerates all the possible projects in the solution.
The project that you choose is shown in bold in the Solution Explorer. The available debug targets change when switching startup projects.
When the Windows project is the startup project, the Debug target drop-down displays options for the Windows Simulator or Local Machine.
When the Windows Phone project is the startup project, the drop-down displays options for Device as well as various emulators.
3. Context Switcher in Code Editor
When writing code in a shared project, you can use the project context switcher in the navigation bar to select the platform you are actively targeting, which in turn customizes the IntelliSense experience in the code editor.
If you use an API in shared code that is not supported on both platforms, an error message will identify this API when you build the project. You don't have to build the project to confirm that you’re using cross-platform APIs.
The following screenshot shows an example of the warning icons and IntelliSense for a type that is supported only in Windows Phone apps.
4. Cross-Platform Code in Shared Project
In the shared project, you typically write code that is common to both platforms. To isolate sections of code that are platform-specific, use the #ifdef directive. The constants WINDOWS_APP and WINDOWS_PHONE_APP are predefined for you.
The following are the conditional compilation constants that you can use to write platform-specific code:
C#
WINDOWS_APP
WINDOWS_PHONE_APP
C++
WINAPI_FAMILY_PC_APP
WINAPI_FAMILY_PHONE_APP
When you're writing code in the shared project, the Visual Studio code editor uses a context that targets one platform or the other. In C#, the IntelliSense that you see as you write code is specific to the context of the code editor, that is, specific to Windows or to Windows Phone.
5. Adding Support for Windows/Windows Phone
If you already have an existing Windows 8.1 application, you can use the Add Windows Phone 8.1 command to add a new Windows Phone 8.1 project and a shared project to the solution. A similar option is also available if you have a Windows Phone 8.1 application and you want to add support for Windows 8.1.
To add support for one type of device or another, in the Solution Explorer, right-click on the project and choose Add Windows Phone 8.1 or Add Windows 8.1.
Here, Visual Studio adds a new Windows Phone or Windows project to the solution. A shared project is also automatically created for you.
The following screenshot shows a solution after adding a Windows Phone project to an existing Windows project. The shared project that is added to the solution is initially empty.
Note that if you create an app using a universal Windows app template, the shared project already contains the App.xaml file.
Step 1: Move Files To the Shared Project
You can move any code that you want to share between apps to the shared project. For example, you can move the Common, DataModel, and Strings folders to the shared project. You can even move App.xaml to the shared project.
You may however receive some compiler errors about the code that you move into the shared project. You can resolve these errors by configuring your new app project to have the same set of references as your initial project.
The following screenshot shows the same assembly reference added to both projects.
If your shared code uses APIs that are specific to Windows, use the #ifdef directive with the WINDOWS_APP constant to isolate that section of code. Use the WINDOWS_PHONE_APP constant to isolate sections of code specific to Windows Phone 8.1.
Step 2: Share App.xaml
When you create a new solution for a universal Windows app, Visual Studio places App.xaml in the shared project. If you convert an existing project to a universal Windows app, you can move App.xaml to the shared project manually. You will have to set the build action property of the page to ApplicationDefinition after moving the file. Here are the steps involved:
In the Solution Explorer, in the shared project, select the App.xaml file.
Select the View>Properties window.
In the Properties window, in the Build Action drop-down list, select ApplicationDefinition.
You also have to decide how you want to open the first page of your app. If you share the App.xaml file and want to use a different start page for each app, you have to add #ifdef directives as shown below.
#if WINDOWS_APP
if (!rootFrame.Navigate(typeof(HubPage)))
#endif
#if WINDOWS_PHONE_APP
if (!rootFrame.Navigate(typeof(WindowsPhoneStartPage)))
#endif
{
throw new Exception("Failed to create initial page");
}
6. Get Started Writing a Universal Windows App
Step 1: Project Setup
Firstly, pick a project template for a universal Windows app in the New Project dialog box. The following screenshot shows the universal Windows app project templates that are currently available for C#.
Give the project a name. I will use Hex Clock Pro for my project.
Step 2: Building the User Interface
For the most part, the user interface work takes place in the platform-specific projects, allowing you to craft a user interface that look great on PC, tablets, and phones, but that share common data, resources, components, and even view-models.
Instead of building separate user interfaces for the Windows Phone 8.1 and Windows 8.1 versions of Hex Clock Pro, I define a common design in the shared project. I just have to make a few changes in the XAML of the clock app on Tuts+ to make it work for both platforms.
As discussed earlier, code which is common to both platforms can be put in the shared project. Code that uses platform-specific APIs needs to be placed in one of the platform-specific projects. You can even use #ifdef directives to include platform-specific code in a shared file.
As the Hex Clock Pro app doesn't use any APIs that are platform-specific, I can put all the code in the shared project.
Hiding the Status Bar
In MainPage.xaml.cs in the shared project, we have used the #ifdef directive to isolate code specific to Windows Phone. The code enclosed within #ifdef hides the status bar on Windows Phone.
public MainPage()
{
this.InitializeComponent();
#if WINDOWS_PHONE_APP
ApplicationView.GetForCurrentView().SetDesiredBoundsMode(ApplicationViewBoundsMode.UseCoreWindow);
#endif
}
Displaying the Current Time
I have used the DispatcherTimer class to call an initial tick when the LayoutRoot grid is loaded. The timer object calls the timer_Tick function on every tick of the clock.
try
{
DispatcherTimer timer = new DispatcherTimer();
timer.Tick += timer_Tick;
timer.Interval = new TimeSpan(0, 0, 0, 1);
timer.Start();
timer_Tick(null, null); //Call an initial tick
}
catch { }
The timer_Tick function updates the displayed time in the app and, at the same time, it updates the background color.
Updating the Background Color
The background color is set to a hexadecimal color that corresponds to the current time.
HexColour color = new HexColour(hexTime);
SolidColorBrush bgBrush = new SolidColorBrush(Color.FromArgb(color.A, color.R, color.G, color.B));
LayoutRoot.Background = bgBrush;
An object of the HexColour class is initialized with the current time, returning the corresponding RGB values. The constructor of the HexColour class sets the A, R, G, B values for the specified color.
public HexColour(string hexCode)
{
if (hexCode == null)
{
throw new ArgumentNullException("hexCode");
}
if (!Regex.IsMatch(hexCode, HEX_PATTERN))
{
throw new ArgumentException("Format must be #000000 or #FF000000 (no extra whitespace)", "hexCode");
}
// shave off '#' symbol
hexCode = hexCode.TrimStart('#');
// if no alpha value specified, assume no transparency (0xFF)
if (hexCode.Length != LENGTH_WITH_ALPHA)
hexCode = String.Format("FF{0}", hexCode);
_color = new Color();
_color.A = byte.Parse(hexCode.Substring(0, 2), NumberStyles.AllowHexSpecifier);
if (_color.A < 50)
_color.A = 50;
_color.R = byte.Parse(hexCode.Substring(2, 2), NumberStyles.AllowHexSpecifier);
_color.G = byte.Parse(hexCode.Substring(4, 2), NumberStyles.AllowHexSpecifier);
_color.B = byte.Parse(hexCode.Substring(6, 2), NumberStyles.AllowHexSpecifier);
}
Adding Animations and Effects
I have imitated the initial animation used in the previous clock app on Tuts+ and it is initialized when the LayoutRoot is loaded.
This is all we need to build the Hex Clock Pro app. The app uses 100% shared code. You just need to generate separate app packages for both platforms. The app looks very similar on Windows Phone and uses the same XAML code for its user interface.
Note that I have added all XAML and C# code in the shared project, but when I deploy either the Windows app or the Windows Phone app, the code in the shared project is merged internally with the platform-specific projects.
Conclusion
Most of the code for the Windows app and the Windows Phone app is shared, and while the user interfaces are separate, they are similar enough that building both is less work than building two user interfaces from scratch.
If I had built a Windows Phone version of Hex Clock Pro for Windows Phone 7 or 8, it would have been a lot more work since Windows Phone 7 contains no WinRT APIs and Windows Phone 8 contains only a small subset.
With Windows 10, we will see more convergence, which means one API—the WinRT API—for multiple platforms, and a high degree of fidelity between user interface elements for each platform that doesn't prevent developers from using platform-specific elements to present the best possible experience on every device. Feel free to download the tutorial's source files to use as reference. Hex Clock Pro is also available in the marketplace for Windows Phone 8.1 and Windows 8.1.
In this tutorial, we'll cover some of the core concepts of Windows Phone development, such as asynchronous programming, navigation, and managing orientation changes. We'll also take a quick look at the life cycle of a typical Windows Phone application.
Asynchronous Programming
Nowadays, asynchronous programming is a required skill for developers. In the past, most applications worked with a synchronous approach: the user started an operation and, until it was completed, the application was frozen and completely unusable.
This behavior isn’t acceptable today, especially in the mobile world. Users wouldn’t buy a phone that doesn’t allow them to answer a call because an application is stuck trying to complete an operation.
There are two approaches in Windows Phone to manage asynchronous programming: callbacks and the async/await pattern.
Callbacks
In most cases, especially in Windows Phone 8, callbacks have been replaced with asynchronous methods that use the async and await pattern. However, there are some APIs that still use the callback approach, so it’s important to learn it.
Callbacks are delegate methods that are called when an asynchronous operation is terminated. This means that the code that triggers the operation and the code that manages the result are stored in two different methods.
Let’s look at the following example of the WebClient class, one of the basic APIs of the framework that performs network operations like downloading and uploading files:
The file download (in this case, we’re downloading the HTML of a webpage) is started when we call the DownloadStringAsync() method, but we need to subscribe to the DownloadStringCompleted event, which is triggered when the download is complete, to manage the result. Usually, the event handler (the callback method) has a parameter that contains the result of the request. In the previous sample, it’s called e and its type is DownloadStringCompletedEventArgs.
Notice the message “Download has started” that is written in the Visual Studio Output window. Since the method is asynchronous, the message will be displayed immediately after the download has started since the code will continue to run until the download is completed. Then, the execution moves to the callback method.
Async and Await
The callback approach is hard to read. Unlike synchronous code, the flow of execution “jumps” from one method to another, so a developer can’t simply read line after line to understand what’s going on. In addition, if you need to write a custom method that starts a network operation and returns the downloaded file, you simply can’t do it because the logic is split in two different pieces.
The async and await pattern was introduced in C# 5.0 to resolve these issues. The Windows Runtime is heavily based on this approach and most of the APIs we’re going to see in this series use it.
When we use the async and await keywords, we’re going to write sequential code as if it’s synchronous—the compiler will execute one row after the other. To better understand what happens under the hood, keep in mind that when we start an asynchronous operation, the compiler sets a bookmark and then quits the method. This way, the UI thread is free and users can keep interacting with the application. When the asynchronous operation is completed, the compiler goes back to the bookmark and continues the execution.
The async and await pattern is based on the Task class, which is the base return type of an asynchronous method. A method can return:
Task if it’s a void method: The compiler should wait for the operation to be completed to execute the next line, but it doesn’t expect a value back.
Task<T> if the method returns a value. The compiler will wait for the operation to be completed and will return the result (whose type is T) to the main method, which will be able to perform additional operations on it.
Let’s see an example. We’re going to add to our project a library called Async for .NET, which has been developed by Microsoft and is available on NuGet. Its purpose is to add async and await support to old technologies that don’t support it since they’re not based on C# 5.0, like Windows Phone 7 or Silverlight. It’s also useful in Windows Phone 8 applications, since it adds asynchronous methods to APIs that are still based on the callback pattern.
One of these APIs is the WebClient class we’ve previously seen. By installing this library we can use the DownloadStringTaskAsync() method which supports the new pattern. This method’s return type is Task<string>. This means that the operation is asynchronous and it will return a string.
private async void OnStartDownloadClicked(object sender, RoutedEventArgs e)
{
WebClient client = new WebClient();
string result = await client.DownloadStringTaskAsync("http://wp.qmatteoq.com");
MessageBox.Show(result);
}
First, let’s note how to use this new syntax. Any method that contains an asynchronous method has to be marked with the async keyword. In this case, it’s an event handler that is invoked when the user presses a button.
Next, we need to add the await keyword as a prefix of the asynchronous method. In our example, we’ve placed it before the method DownloadStringTaskAsync(). This keyword tells the compiler to wait until the method is completed before moving on.
When the asynchronous method is started, you can assume that the compiler has set a bookmark and then quit the method because the application is still responsive. When the download is completed, the compiler returns to the bookmark and continues the execution. The download’s result is stored in the result variable, whose value is displayed on the screen using a MessageBox.
As you can see, even if the code under the hood is asynchronous and doesn’t freeze the UI, it appears synchronous: the code is executed one line at a time and the MessageBox isn’t displayed until the DownloadStringTaskAsync() method has finished its job.
The Dispatcher
Sometimes, especially when you have to deal with callback methods, the operations are executed in a background thread, which is different from the thread that manages the UI. This approach is very helpful in keeping the UI thread as free as possible so that the interface is always fast and responsive.
Sometimes a background thread may need to interact with the user interface. For example, let’s say a background thread has completed its job and needs to display the result in a TextBlock control placed in the page. If you try to do it, an UnauthorizedAccessException error with the message “Invalid cross-thread access”will be displayed.
The reason for this error is that a background thread isn’t allowed to access the controls in a page since they are managed by a different thread. The solution is to use a framework’s Dispatcher class. Its purpose is to dispatch operations to the UI thread from another thread. This way, you can interact with XAML controls without issues because the thread is the same. The following sample illustrates how to use it:
Dispatcher.BeginInvoke(() =>
{
txtResult.Text = "This is the result";
})
You simply have to pass the Action that needs to be executed on the UI thread as a parameter of the BeginInvoke() method of the Dispatcher class. In the previous sample, the Action is defined using an anonymous method.
Tip: Anonymous methods are called such because they don’t have a specific name—they simply define the operations to be performed.
Dispatcher is not needed when you work with the async and await pattern because it makes sure that even if the operation is executed in one of the free threads available in the thread pool, the result is always returned in the UI thread.
Navigation
Another important concept to learn when you develop a Windows Phone application is navigation. In most cases, your application will be made of different pages, so you need a way to move from one page to another.
The framework offers a built-in navigation system, which can be managed using the NavigationService class.
The navigation system provides, in every page, two events that can be managed to intercept the navigation events: OnNavigatedTo() is triggered when you move from another page to the current page; OnNavigateFrom() is triggered when you move from the current page to another page. Another important page event that can be subscribed is Loaded, which is triggered when the page is completely loaded. It’s commonly used to load the data that needs to be displayed on the page.
A common requirement when dealing with navigation is to pass a parameter to another page, for example, a master/detail scenario where you select a list item in the master page and display its details in a new page.
The navigation framework offers a built-in way to pass simple parameters like strings and numbers that is inherited directly from the web world: query string parameters. They’re added directly to the URL of the page where you want to redirect the user, as shown in the following sample:
You’ll be able to retrieve the parameter in the landing page by using the OnNavigatedTo event and the NavigationContext class, as in the following sample:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (NavigationContext.QueryString.ContainsKey("id"))
{
int id = int.Parse(NavigationContext.QueryString["id"]);
MessageBox.Show("The id is " + id);
}
}
The NavigationContext class offers a collection called QueryString, which contains all the available parameters. You’ll be able to get the value by using its key (which is the parameter’s name).
In some cases, simple parameters aren’t enough. Sometimes you need to pass complex objects to another page. The master/detail scenario is, also in this case, a good example. In many situations, to properly populate the detail page, you need access to the whole object that has been selected in the list.
A good approach to this is to pass the detail page a plain parameter that can be used to identify the selected item, like a unique identifier. Then, in the OnNavigatedTo event handler of the detail page, load the item with the received identifier from your data collection (which can be a database or a remote service).
Manipulating the Navigation Stack
Every application has its own navigation stack, which is the list of pages that the user has moved through while using the application. Every time the user navigates to a new page, it’s added to the stack. When the user goes back, the last page from the stack is removed. When one page is left in the stack and the user presses the Back button, the application is closed.
The NavigationService class offers a few ways to manipulate the stack. In the beginning, there’s a specific property called BackStack which is a collection of all the pages in the stack. When we discuss Fast App Resume later in this article, we’ll see one of the available methods to manipulate the stack: RemoveBackEntry(), which removes the last page from the stack.
Another important method is GoBack(), which redirects the user to the previous page in the stack. Its purpose is to avoid creating circular navigation issues in your application.
The image below illustrates an example of circular navigation and how to correct it. Let’s say that you have an application made by two pages, a main page and a settings page. You choose to use the Navigate() method of the NavigateService class for every navigation. The user navigates from the main page to the settings page, and again to the main page.
The issue here is that every time you call the Navigate() method, you add a new page to the navigation stack. When the user is on the main page and presses the Back button, he or she expects to quit the app. Instead, the second time, the user will be redirected to the Settings page since the previous navigation added the page to the stack.
The correct approach is to use the GoBack() method. Instead of adding a new page to the stack, this method simply redirects the user to the previous one. The result will be the same—the user, from the settings page, is taken back to the main page—but since there are no pages added to the stack, the application will behave as expected. If the user presses the Back button, the app will be closed.
Intercepting the Navigation: The UriMapper Class
Later in the series we’ll deal with different situations where the application is opened with a special Uri, such as using the Speech API or data sharing.
In these cases, we’re going to use a navigation feature called UriMapper, which is a class that can act as a middle man in every navigation operation. When the user navigates from one page to another (the starting page can be also be the phone’s Home screen), the class is able to intercept it and redirect the user to another page if needed.
To create a UriMapper-based class, simply add a new class to your project. It has to inherit from the UriMapperBase class. It will require you to implement the MapUri() method, which is invoked during navigation as shown in the following sample:
public class UriMapper: UriMapperBase
{
public override Uri MapUri(Uri uri)
{
string tempUri = HttpUtility.UrlDecode(uri.ToString());
if (tempUri.Contains("/FileTypeAssociation"))
{
//Manage the selected file.
return new Uri("/Pages/FilePage.xaml", UriKind.Relative);
}
else
{
return uri;
}
}
}
The MapUri() method takes in the source Uri and needs to return the new Uri to redirect the user. In the previous sample, we checked whether the source Uri contained a specific string (in this case, it’s one that belongs to the data sharing scenario we’ll see later in this series). In the affirmative case, we redirect the user to a specific page of the application that is able to manage the scenario; otherwise, we don’t change the navigation flow by returning the original Uri.
After you’ve created a UriMapper class, you’ll have to assign it to the UriMapper property of the RootFrame object that is declared in the App.xaml.cs file. You’ll have to expand the region called Phone application initialization (which is usually collapsed), and change the InitializePhoneApplication() method like in the following sample:
private void InitializePhoneApplication()
{
if (phoneApplicationInitialized)
return;
// Create the frame but don't set it as RootVisual yet; this allows the
// splash screen to remain active until the application is ready to render.
RootFrame = new PhoneApplicationFrame();
RootFrame.UriMapper = new UriMapper();
RootFrame.Navigated += CompleteInitializePhoneApplication;
// Handle navigation failures.
RootFrame.NavigationFailed += RootFrame_NavigationFailed;
// Handle reset requests for clearing the backstack.
RootFrame.Navigated += CheckForResetNavigation;
// Ensure we don't initialize again.
phoneApplicationInitialized = true;
}
The Application Life Cycle
Mobile applications have a different life cycle than traditional desktop applications due to their different requirements, like power management and performance optimization. The traditional multitasking approach that you experience in Windows doesn’t fit well to the mobile experience: the necessary power would drain the battery very quickly. Plus, if you open too many applications, you can suffer severe performance issues that will degrade the user experience.
The following figure explains the life cycle Microsoft has introduced for Windows Phone applications to satisfy the required conditions:
When an application is running, it can be suspended at any time. For example, the user can press the Start button or tap a notification to open another application. In this case, the application is “frozen” (the dormant state); it’s preserved in memory, but all the running threads and operations are stopped. In this status, the application is using system memory, but the CPU is free. This way, other applications can use the available resources without suffering performance issues.
When the user decides to go back to the application (by using the Back button or the task switcher), its instance is restored from memory and the previous threads and operations are restarted.
If users play with their device for a long time, too many applications may be opened and there won’t be enough memory left to keep other applications suspended. In this case, the operating system can start to “kill” longer-running applications to free some memory for the newly opened apps—the older apps are moved into a tombstoned state. To give users the same smooth experience as when apps are simply suspended, developers have the ability to save the state of the app when it is tombstoned and restore it later. When users open a tombstoned app, they will find the application in the exact previous state, as if it’s never been closed.
The tombstoning experience was the only one available in Windows Phone 7, but it often led to a bad user experience. Because the app was killed, the application always needed to start from scratch, causing longer loading times. In Windows Phone 7.5, Microsoft introduced the Fast Application Switching concept that has been translated into the current life cycle, where apps are put in a dormant state and are killed only if needed.
Based on this information, we can understand that applications aren’t able to run in the background—when an application is suspended, all of its running operations are canceled. Later in this series, we’ll look at available techniques for executing some operations even when the app is not running.
As a developer, you are not notified by the operating system when an application is tombstoned. This is why, in the schema shown above, you can see that the save state operation is made every time the application is suspended. You’ll always have to deal with it, since you won’t know in advance whether your app will be tombstoned.
Instead, the restore operation is made only if the application has been tombstoned. When the app is in a dormant state, the entire process is stored in memory, so there’s no data loss when the app is restored.
Since tombstoning isn’t deterministic, Visual Studio offers a way to test it. By default, applications are moved to the dormant state when suspended. You can change this behavior by right-clicking on your project and choosing Properties. In the Debug section you’ll find the Tombstone upon deactivation while debugging option. When it’s selected, applications will always be tombstoned when they’re suspended, so you can test that everything works fine and no data is lost in this scenario.
Controlling the Life Cycle
You can control the application life cycle thanks to the PhoneApplicationService class, which is declared in the App.xaml file as a lifetime object.
As you can see, the PhoneApplicationService class is registered to manage four different events that are available in the App.xaml.cs file:
Application_Launching is invoked when the app starts for the first time.
Application_Closing is invoked when the app is completely closed. It only happens when the user presses the Back button in the application’s main page.
Application_Activated is invoked when the app is resumed from a suspended state.
Application_Deactivated is invoked when the app is suspended.
Remember the “save state” and “restore state” operations in the previous figure? Usually, they’re managed in these events. State is saved when the Application_Deactivated event occurs, and it’s restored when the Application_Activated event occurs.
Specifically, one of the parameters of the Application_Activated event handler contains important information—it shows whether the application comes from a dormant or tombstoned state as demonstrated in the following sample:
private void Application_Activated(object sender, ActivatedEventArgs e)
{
if (e.IsApplicationInstancePreserved)
{
Debug.WriteLine("The app was dormant");
}
else
{
Debug.WriteLine("The app was tombstoned");
}
}
The key is the IsApplicationInstancePreserved property of the ActivatedEventArgs object, which is a Boolean. If it’s true, the app was in a dormant state; otherwise, it was killed, and it’s time to restore the data we’ve previously saved.
The PhoneApplicationService class can also be useful to store the state of your application when it’s suspended. In fact, it offers a property called State that is a collection whose type is Dictionary<string, object>. It can be used to save any object that is identified by a key.
Typically, you’re going to save data in the State property when the app is suspended, and restore the data when the app is resumed from a tombstoned state.
However, there are some important things to keep in mind:
It’s best to save data as soon as possible and not only when the Application_Deactivated event is raised. Unexpected bugs or other errors can cause users to lose data during the execution.
The content of the State property is kept in memory only if the app is resumed; if the user decides to open the application from scratch (for example, by tapping on its Tile instead of using the Back button), the collection is erased and the saved data will be lost. If you need to persist the data across multiple executions, it’s better to save to the local storage, which we’ll cover later in this series.
Fast App Resume
The life cycle previously described has a flaw: users can resume a suspended application only by using the Back button or the task switcher. If they pin a Tile for the application on the Start screen and tap on it, a new instance of the app will be opened and the suspended one will be terminated.
Windows Phone 8 has introduced a new feature called Fast App Resume, which can be activated to address the flaw previously described. Regardless of the opening point, if there’s a suspended instance, it will always be used.
Supporting Fast App Resume requires a bit of work because, by default, the Windows Phone template is made to use the old life cycle, even when the feature is activated.
The first step is to modify the manifest file, and unfortunately, it’s one of the situations where the visual editor isn’t helpful. In fact, we’ll need to manually edit the file. To do this, simply right-click the WMAppManifest.xml file inside the Properties folder and choose the View code option.
You’ll find a node called DefaultTask that tells the application which page to load first when the app starts. You’ll need to add an attribute called ActivationPolicy to set its value to Resume, like the following sample:
<Tasks><DefaultTask Name ="_default" NavigationPage="MainPage.xaml" ActivationPolicy="Resume" /></Tasks>
What happens when users tap on the main Tile of the application and the Fast App Resume has been enabled? The operating system triggers two navigations: the first one is toward the last opened page in the application, and the second one is toward the main page of the application, since the application Tile is associated with it through the default navigation Uri.
At this point, you have two choices:
You can keep using the old approach. In this case, you have to remove the last visited page from the back stack; otherwise you’ll break the navigation system. In fact, when users are on the main page of the application and press the Back button, they expect to quit the application, not go back to an old page.
You can support Fast App Resume. In this case, you have to stop the navigation to the main application page so that users are taken to the last visited page of the application.
The default Windows Phone 8 templates contain an implementation of the first approach. Open the App.xaml.cs file and look for a method called InitializePhoneApplication(), which takes care of initializing the frame that manages the various pages and the navigation system. You’ll find that the application subscribes to an event called Navigated of the RootFrame class, which is triggered every time the user has moved from one page of the application to another.
The method that is assigned as the handler of this event is called ClearBackafterReset(), which has the following definition:
private void ClearBackStackAfterReset(object sender, NavigationEventArgs e)
{
// Unregister the event so it doesn't get called again.
RootFrame.Navigated -= ClearBackStackAfterReset;
// Only clear the stack for 'new' (forward) and 'refresh' navigations.
if (e.NavigationMode != NavigationMode.New && e.NavigationMode != NavigationMode.Refresh)
return;
// For UI consistency, clear the entire page stack.
while (RootFrame.RemoveBackEntry() != null)
{
; // Do nothing.
}
}
This method does exactly what was previously described: the navigation to the main page is not canceled but, using the RemoveBackEntry() method of the RootFrame class, the page stack of the application is cleaned.
Take note of the NavigationMode property: it specifies the status of the navigation, like New when users navigate to a new page, or Back when users go back to a previous page. The key status to manage Fast App Resume is Reset, which is set when users are navigating to the main application page but an old instance of the app is being used.
We’re going to use the NavigationMode property together with some changes to the original code. The first step is to change the Navigated event handler to simply store, in a global Boolean property of the App.xaml.cs class, whether the NavigationMode is reset. We’re going to use this information in another event handler. In fact, we need to subscribe to the Navigating event of the RootFrame class. This event is similar to the Navigated one, except that it is triggered before the navigation starts and not after. This difference is important for our purpose, since we have the chance to cancel the navigation operation before it’s executed.
Here is what we do in the Navigating event handler:
In this event, we wait for a specific condition to happen: the NavigationMode is Reset and navigation to the main page is triggered. This situation occurs when users tap on the main Tile and an instance of the app is already in memory. The first navigation redirects to the last visited page, and the second one (the Reset mode) redirects to the main page. It’s this scenario that we need to manage. By setting the Cancel property of the method’s parameter, we abort the navigation to the main page and leave the user on the last-visited page of the app. The experience is exactly the same as when the user returns to the application using the Back button.
Here is how the complete code required to implement Fast App Resume looks in the App.xaml.cs file:
private bool reset;
// Do not add any additional code to this method.
private void InitializePhoneApplication()
{
if (phoneApplicationInitialized)
return;
// Create the frame but don't set it as RootVisual yet; this allows the
// splash screen to remain active until the application is ready to render.
RootFrame = new PhoneApplicationFrame();
RootFrame.Navigated += CompleteInitializePhoneApplication;
// Handle navigation failures.
RootFrame.NavigationFailed += RootFrame_NavigationFailed;
// Handle reset requests for clearing the backstack.
RootFrame.Navigated += RootFrame_Navigated;
RootFrame.Navigating += RootFrame_Navigating;
// Ensure we don't initialize again.
phoneApplicationInitialized = true;
}
void RootFrame_Navigated(object sender, NavigationEventArgs e)
{
reset = e.NavigationMode == NavigationMode.Reset;
}
void RootFrame_Navigating(object sender, NavigatingCancelEventArgs e)
{
if (reset && e.IsCancelable && e.Uri.OriginalString == "/MainPage.xaml")
{
e.Cancel = true;
reset = false;
}
}
You may be wondering why all this effort is needed to support Fast App Resume. Why isn’t it automatically implemented by the operating system? The answer is that Fast App Resume isn’t suitable for every application—you have to be very careful when you decide to support it, because you can ruin the user experience if you don’t implement it well.
For example, if your users spend most of the time in the main page of your application (for example, a social networking app), they probably prefer to go straight to the Home page when they open the application, instead of resuming what they were doing before.
Here are two tips for improving the Fast App Resume experience:
If your application has many pages and a complex navigation hierarchy, add to the inner pages a quick way to go back to the home page (like a button in the Application Bar).
Add a timing condition. If the application hasn’t been used recently, disable Fast App Resume and redirect the user to the home page.
Manage Orientation
Typically, a Windows Phone device is used in portrait mode, but there are some scenarios where users can benefit from landscape mode. For example, if your application offers a way to insert long text, it can be more convenient for users to type using the landscape keyboard. Alternatively, in other scenarios, you can use the landscape mode to provide the content with a different layout.
By default, every new page added to a Windows Phone is displayed in portrait mode and does not support the landscape mode. When you rotate the phone, nothing happens. The orientation behavior is controlled by two attributes of the PhoneApplicationPage node in the XAML:
The SupportedOrientations property defines which orientations are supported by the page. To support both orientations, you need to set the value to PortraitOrLandscape.
The Orientation property defines the default orientation of the page.
Once you’ve set the SupportedOrientations property to PortraitOrLandscape, the page’s layout will automatically adapt to landscape mode when the phone is rotated.
If you want further control (for example, because you want to deeply change the page’s layout between portrait and landscape modes), the APIs offer a page event called OrientationChanged that is raised every time the phone is rotated from portrait to landscape mode and vice versa. The event is exposed directly by the PhoneApplicationPage class, so you can subscribe it in the XAML, as in the following sample:
The event handler that will be generated in the code will return the information about the new orientation. You can use it to move or change the aspect of the controls placed in the page. The following sample shows how to set a different Height of a Button control, according to the detected orientation:
If you’re new to Windows Phone development, this article is very important, because we’ve learned some basic core concepts that we’re going to use again in later installments of this series.
Here is a quick recap:
Asynchronous programming is must-have knowledge for mobile developers. We’ve learned how callbacks and the new async and await pattern will help you keep the user interface fast and responsive.
It’s highly unlikely that your application is made of just one page, so we’ve learned how to support navigation between different pages and how to correctly manage the navigation flow.
Application life cycle is another key concept. You have to understand how a Windows Phone application works under the hood to correctly manage all the multitasking scenarios. We’ve talked about Fast Application Switching, an improvement introduced in Windows Phone 7.5 to make the suspending and resuming process faster, and Fast App Resume, a new Window Phone 8 feature that improves the resuming experience.
This tutorial represents a chapter from Windows Phone 8 Succinctly, a free eBook from the team at Syncfusion.
In this tutorial, we'll cover some of the core concepts of Windows Phone development, such as asynchronous programming, navigation, and managing orientation changes. We'll also take a quick look at the life cycle of a typical Windows Phone application.
Asynchronous Programming
Nowadays, asynchronous programming is a required skill for developers. In the past, most applications worked with a synchronous approach: the user started an operation and, until it was completed, the application was frozen and completely unusable.
This behavior isn’t acceptable today, especially in the mobile world. Users wouldn’t buy a phone that doesn’t allow them to answer a call because an application is stuck trying to complete an operation.
There are two approaches in Windows Phone to manage asynchronous programming: callbacks and the async/await pattern.
Callbacks
In most cases, especially in Windows Phone 8, callbacks have been replaced with asynchronous methods that use the async and await pattern. However, there are some APIs that still use the callback approach, so it’s important to learn it.
Callbacks are delegate methods that are called when an asynchronous operation is terminated. This means that the code that triggers the operation and the code that manages the result are stored in two different methods.
Let’s look at the following example of the WebClient class, one of the basic APIs of the framework that performs network operations like downloading and uploading files:
The file download (in this case, we’re downloading the HTML of a webpage) is started when we call the DownloadStringAsync() method, but we need to subscribe to the DownloadStringCompleted event, which is triggered when the download is complete, to manage the result. Usually, the event handler (the callback method) has a parameter that contains the result of the request. In the previous sample, it’s called e and its type is DownloadStringCompletedEventArgs.
Notice the message “Download has started” that is written in the Visual Studio Output window. Since the method is asynchronous, the message will be displayed immediately after the download has started since the code will continue to run until the download is completed. Then, the execution moves to the callback method.
Async and Await
The callback approach is hard to read. Unlike synchronous code, the flow of execution “jumps” from one method to another, so a developer can’t simply read line after line to understand what’s going on. In addition, if you need to write a custom method that starts a network operation and returns the downloaded file, you simply can’t do it because the logic is split in two different pieces.
The async and await pattern was introduced in C# 5.0 to resolve these issues. The Windows Runtime is heavily based on this approach and most of the APIs we’re going to see in this series use it.
When we use the async and await keywords, we’re going to write sequential code as if it’s synchronous—the compiler will execute one row after the other. To better understand what happens under the hood, keep in mind that when we start an asynchronous operation, the compiler sets a bookmark and then quits the method. This way, the UI thread is free and users can keep interacting with the application. When the asynchronous operation is completed, the compiler goes back to the bookmark and continues the execution.
The async and await pattern is based on the Task class, which is the base return type of an asynchronous method. A method can return:
Task if it’s a void method: The compiler should wait for the operation to be completed to execute the next line, but it doesn’t expect a value back.
Task<T> if the method returns a value. The compiler will wait for the operation to be completed and will return the result (whose type is T) to the main method, which will be able to perform additional operations on it.
Let’s see an example. We’re going to add to our project a library called Async for .NET, which has been developed by Microsoft and is available on NuGet. Its purpose is to add async and await support to old technologies that don’t support it since they’re not based on C# 5.0, like Windows Phone 7 or Silverlight. It’s also useful in Windows Phone 8 applications, since it adds asynchronous methods to APIs that are still based on the callback pattern.
One of these APIs is the WebClient class we’ve previously seen. By installing this library we can use the DownloadStringTaskAsync() method which supports the new pattern. This method’s return type is Task<string>. This means that the operation is asynchronous and it will return a string.
private async void OnStartDownloadClicked(object sender, RoutedEventArgs e)
{
WebClient client = new WebClient();
string result = await client.DownloadStringTaskAsync("http://wp.qmatteoq.com");
MessageBox.Show(result);
}
First, let’s note how to use this new syntax. Any method that contains an asynchronous method has to be marked with the async keyword. In this case, it’s an event handler that is invoked when the user presses a button.
Next, we need to add the await keyword as a prefix of the asynchronous method. In our example, we’ve placed it before the method DownloadStringTaskAsync(). This keyword tells the compiler to wait until the method is completed before moving on.
When the asynchronous method is started, you can assume that the compiler has set a bookmark and then quit the method because the application is still responsive. When the download is completed, the compiler returns to the bookmark and continues the execution. The download’s result is stored in the result variable, whose value is displayed on the screen using a MessageBox.
As you can see, even if the code under the hood is asynchronous and doesn’t freeze the UI, it appears synchronous: the code is executed one line at a time and the MessageBox isn’t displayed until the DownloadStringTaskAsync() method has finished its job.
The Dispatcher
Sometimes, especially when you have to deal with callback methods, the operations are executed in a background thread, which is different from the thread that manages the UI. This approach is very helpful in keeping the UI thread as free as possible so that the interface is always fast and responsive.
Sometimes a background thread may need to interact with the user interface. For example, let’s say a background thread has completed its job and needs to display the result in a TextBlock control placed in the page. If you try to do it, an UnauthorizedAccessException error with the message “Invalid cross-thread access”will be displayed.
The reason for this error is that a background thread isn’t allowed to access the controls in a page since they are managed by a different thread. The solution is to use a framework’s Dispatcher class. Its purpose is to dispatch operations to the UI thread from another thread. This way, you can interact with XAML controls without issues because the thread is the same. The following sample illustrates how to use it:
Dispatcher.BeginInvoke(() =>
{
txtResult.Text = "This is the result";
})
You simply have to pass the Action that needs to be executed on the UI thread as a parameter of the BeginInvoke() method of the Dispatcher class. In the previous sample, the Action is defined using an anonymous method.
Tip: Anonymous methods are called such because they don’t have a specific name—they simply define the operations to be performed.
Dispatcher is not needed when you work with the async and await pattern because it makes sure that even if the operation is executed in one of the free threads available in the thread pool, the result is always returned in the UI thread.
Navigation
Another important concept to learn when you develop a Windows Phone application is navigation. In most cases, your application will be made of different pages, so you need a way to move from one page to another.
The framework offers a built-in navigation system, which can be managed using the NavigationService class.
The navigation system provides, in every page, two events that can be managed to intercept the navigation events: OnNavigatedTo() is triggered when you move from another page to the current page; OnNavigateFrom() is triggered when you move from the current page to another page. Another important page event that can be subscribed is Loaded, which is triggered when the page is completely loaded. It’s commonly used to load the data that needs to be displayed on the page.
A common requirement when dealing with navigation is to pass a parameter to another page, for example, a master/detail scenario where you select a list item in the master page and display its details in a new page.
The navigation framework offers a built-in way to pass simple parameters like strings and numbers that is inherited directly from the web world: query string parameters. They’re added directly to the URL of the page where you want to redirect the user, as shown in the following sample:
You’ll be able to retrieve the parameter in the landing page by using the OnNavigatedTo event and the NavigationContext class, as in the following sample:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (NavigationContext.QueryString.ContainsKey("id"))
{
int id = int.Parse(NavigationContext.QueryString["id"]);
MessageBox.Show("The id is " + id);
}
}
The NavigationContext class offers a collection called QueryString, which contains all the available parameters. You’ll be able to get the value by using its key (which is the parameter’s name).
In some cases, simple parameters aren’t enough. Sometimes you need to pass complex objects to another page. The master/detail scenario is, also in this case, a good example. In many situations, to properly populate the detail page, you need access to the whole object that has been selected in the list.
A good approach to this is to pass the detail page a plain parameter that can be used to identify the selected item, like a unique identifier. Then, in the OnNavigatedTo event handler of the detail page, load the item with the received identifier from your data collection (which can be a database or a remote service).
Manipulating the Navigation Stack
Every application has its own navigation stack, which is the list of pages that the user has moved through while using the application. Every time the user navigates to a new page, it’s added to the stack. When the user goes back, the last page from the stack is removed. When one page is left in the stack and the user presses the Back button, the application is closed.
The NavigationService class offers a few ways to manipulate the stack. In the beginning, there’s a specific property called BackStack which is a collection of all the pages in the stack. When we discuss Fast App Resume later in this article, we’ll see one of the available methods to manipulate the stack: RemoveBackEntry(), which removes the last page from the stack.
Another important method is GoBack(), which redirects the user to the previous page in the stack. Its purpose is to avoid creating circular navigation issues in your application.
The image below illustrates an example of circular navigation and how to correct it. Let’s say that you have an application made by two pages, a main page and a settings page. You choose to use the Navigate() method of the NavigateService class for every navigation. The user navigates from the main page to the settings page, and again to the main page.
The issue here is that every time you call the Navigate() method, you add a new page to the navigation stack. When the user is on the main page and presses the Back button, he or she expects to quit the app. Instead, the second time, the user will be redirected to the Settings page since the previous navigation added the page to the stack.
The correct approach is to use the GoBack() method. Instead of adding a new page to the stack, this method simply redirects the user to the previous one. The result will be the same—the user, from the settings page, is taken back to the main page—but since there are no pages added to the stack, the application will behave as expected. If the user presses the Back button, the app will be closed.
Intercepting the Navigation: The UriMapper Class
Later in the series we’ll deal with different situations where the application is opened with a special Uri, such as using the Speech API or data sharing.
In these cases, we’re going to use a navigation feature called UriMapper, which is a class that can act as a middle man in every navigation operation. When the user navigates from one page to another (the starting page can be also be the phone’s Home screen), the class is able to intercept it and redirect the user to another page if needed.
To create a UriMapper-based class, simply add a new class to your project. It has to inherit from the UriMapperBase class. It will require you to implement the MapUri() method, which is invoked during navigation as shown in the following sample:
public class UriMapper: UriMapperBase
{
public override Uri MapUri(Uri uri)
{
string tempUri = HttpUtility.UrlDecode(uri.ToString());
if (tempUri.Contains("/FileTypeAssociation"))
{
//Manage the selected file.
return new Uri("/Pages/FilePage.xaml", UriKind.Relative);
}
else
{
return uri;
}
}
}
The MapUri() method takes in the source Uri and needs to return the new Uri to redirect the user. In the previous sample, we checked whether the source Uri contained a specific string (in this case, it’s one that belongs to the data sharing scenario we’ll see later in this series). In the affirmative case, we redirect the user to a specific page of the application that is able to manage the scenario; otherwise, we don’t change the navigation flow by returning the original Uri.
After you’ve created a UriMapper class, you’ll have to assign it to the UriMapper property of the RootFrame object that is declared in the App.xaml.cs file. You’ll have to expand the region called Phone application initialization (which is usually collapsed), and change the InitializePhoneApplication() method like in the following sample:
private void InitializePhoneApplication()
{
if (phoneApplicationInitialized)
return;
// Create the frame but don't set it as RootVisual yet; this allows the
// splash screen to remain active until the application is ready to render.
RootFrame = new PhoneApplicationFrame();
RootFrame.UriMapper = new UriMapper();
RootFrame.Navigated += CompleteInitializePhoneApplication;
// Handle navigation failures.
RootFrame.NavigationFailed += RootFrame_NavigationFailed;
// Handle reset requests for clearing the backstack.
RootFrame.Navigated += CheckForResetNavigation;
// Ensure we don't initialize again.
phoneApplicationInitialized = true;
}
The Application Life Cycle
Mobile applications have a different life cycle than traditional desktop applications due to their different requirements, like power management and performance optimization. The traditional multitasking approach that you experience in Windows doesn’t fit well to the mobile experience: the necessary power would drain the battery very quickly. Plus, if you open too many applications, you can suffer severe performance issues that will degrade the user experience.
The following figure explains the life cycle Microsoft has introduced for Windows Phone applications to satisfy the required conditions:
When an application is running, it can be suspended at any time. For example, the user can press the Start button or tap a notification to open another application. In this case, the application is “frozen” (the dormant state); it’s preserved in memory, but all the running threads and operations are stopped. In this status, the application is using system memory, but the CPU is free. This way, other applications can use the available resources without suffering performance issues.
When the user decides to go back to the application (by using the Back button or the task switcher), its instance is restored from memory and the previous threads and operations are restarted.
If users play with their device for a long time, too many applications may be opened and there won’t be enough memory left to keep other applications suspended. In this case, the operating system can start to “kill” longer-running applications to free some memory for the newly opened apps—the older apps are moved into a tombstoned state. To give users the same smooth experience as when apps are simply suspended, developers have the ability to save the state of the app when it is tombstoned and restore it later. When users open a tombstoned app, they will find the application in the exact previous state, as if it’s never been closed.
The tombstoning experience was the only one available in Windows Phone 7, but it often led to a bad user experience. Because the app was killed, the application always needed to start from scratch, causing longer loading times. In Windows Phone 7.5, Microsoft introduced the Fast Application Switching concept that has been translated into the current life cycle, where apps are put in a dormant state and are killed only if needed.
Based on this information, we can understand that applications aren’t able to run in the background—when an application is suspended, all of its running operations are canceled. Later in this series, we’ll look at available techniques for executing some operations even when the app is not running.
As a developer, you are not notified by the operating system when an application is tombstoned. This is why, in the schema shown above, you can see that the save state operation is made every time the application is suspended. You’ll always have to deal with it, since you won’t know in advance whether your app will be tombstoned.
Instead, the restore operation is made only if the application has been tombstoned. When the app is in a dormant state, the entire process is stored in memory, so there’s no data loss when the app is restored.
Since tombstoning isn’t deterministic, Visual Studio offers a way to test it. By default, applications are moved to the dormant state when suspended. You can change this behavior by right-clicking on your project and choosing Properties. In the Debug section you’ll find the Tombstone upon deactivation while debugging option. When it’s selected, applications will always be tombstoned when they’re suspended, so you can test that everything works fine and no data is lost in this scenario.
Controlling the Life Cycle
You can control the application life cycle thanks to the PhoneApplicationService class, which is declared in the App.xaml file as a lifetime object.
As you can see, the PhoneApplicationService class is registered to manage four different events that are available in the App.xaml.cs file:
Application_Launching is invoked when the app starts for the first time.
Application_Closing is invoked when the app is completely closed. It only happens when the user presses the Back button in the application’s main page.
Application_Activated is invoked when the app is resumed from a suspended state.
Application_Deactivated is invoked when the app is suspended.
Remember the “save state” and “restore state” operations in the previous figure? Usually, they’re managed in these events. State is saved when the Application_Deactivated event occurs, and it’s restored when the Application_Activated event occurs.
Specifically, one of the parameters of the Application_Activated event handler contains important information—it shows whether the application comes from a dormant or tombstoned state as demonstrated in the following sample:
private void Application_Activated(object sender, ActivatedEventArgs e)
{
if (e.IsApplicationInstancePreserved)
{
Debug.WriteLine("The app was dormant");
}
else
{
Debug.WriteLine("The app was tombstoned");
}
}
The key is the IsApplicationInstancePreserved property of the ActivatedEventArgs object, which is a Boolean. If it’s true, the app was in a dormant state; otherwise, it was killed, and it’s time to restore the data we’ve previously saved.
The PhoneApplicationService class can also be useful to store the state of your application when it’s suspended. In fact, it offers a property called State that is a collection whose type is Dictionary<string, object>. It can be used to save any object that is identified by a key.
Typically, you’re going to save data in the State property when the app is suspended, and restore the data when the app is resumed from a tombstoned state.
However, there are some important things to keep in mind:
It’s best to save data as soon as possible and not only when the Application_Deactivated event is raised. Unexpected bugs or other errors can cause users to lose data during the execution.
The content of the State property is kept in memory only if the app is resumed; if the user decides to open the application from scratch (for example, by tapping on its Tile instead of using the Back button), the collection is erased and the saved data will be lost. If you need to persist the data across multiple executions, it’s better to save to the local storage, which we’ll cover later in this series.
Fast App Resume
The life cycle previously described has a flaw: users can resume a suspended application only by using the Back button or the task switcher. If they pin a Tile for the application on the Start screen and tap on it, a new instance of the app will be opened and the suspended one will be terminated.
Windows Phone 8 has introduced a new feature called Fast App Resume, which can be activated to address the flaw previously described. Regardless of the opening point, if there’s a suspended instance, it will always be used.
Supporting Fast App Resume requires a bit of work because, by default, the Windows Phone template is made to use the old life cycle, even when the feature is activated.
The first step is to modify the manifest file, and unfortunately, it’s one of the situations where the visual editor isn’t helpful. In fact, we’ll need to manually edit the file. To do this, simply right-click the WMAppManifest.xml file inside the Properties folder and choose the View code option.
You’ll find a node called DefaultTask that tells the application which page to load first when the app starts. You’ll need to add an attribute called ActivationPolicy to set its value to Resume, like the following sample:
<Tasks><DefaultTask Name ="_default" NavigationPage="MainPage.xaml" ActivationPolicy="Resume" /></Tasks>
What happens when users tap on the main Tile of the application and the Fast App Resume has been enabled? The operating system triggers two navigations: the first one is toward the last opened page in the application, and the second one is toward the main page of the application, since the application Tile is associated with it through the default navigation Uri.
At this point, you have two choices:
You can keep using the old approach. In this case, you have to remove the last visited page from the back stack; otherwise you’ll break the navigation system. In fact, when users are on the main page of the application and press the Back button, they expect to quit the application, not go back to an old page.
You can support Fast App Resume. In this case, you have to stop the navigation to the main application page so that users are taken to the last visited page of the application.
The default Windows Phone 8 templates contain an implementation of the first approach. Open the App.xaml.cs file and look for a method called InitializePhoneApplication(), which takes care of initializing the frame that manages the various pages and the navigation system. You’ll find that the application subscribes to an event called Navigated of the RootFrame class, which is triggered every time the user has moved from one page of the application to another.
The method that is assigned as the handler of this event is called ClearBackafterReset(), which has the following definition:
private void ClearBackStackAfterReset(object sender, NavigationEventArgs e)
{
// Unregister the event so it doesn't get called again.
RootFrame.Navigated -= ClearBackStackAfterReset;
// Only clear the stack for 'new' (forward) and 'refresh' navigations.
if (e.NavigationMode != NavigationMode.New && e.NavigationMode != NavigationMode.Refresh)
return;
// For UI consistency, clear the entire page stack.
while (RootFrame.RemoveBackEntry() != null)
{
; // Do nothing.
}
}
This method does exactly what was previously described: the navigation to the main page is not canceled but, using the RemoveBackEntry() method of the RootFrame class, the page stack of the application is cleaned.
Take note of the NavigationMode property: it specifies the status of the navigation, like New when users navigate to a new page, or Back when users go back to a previous page. The key status to manage Fast App Resume is Reset, which is set when users are navigating to the main application page but an old instance of the app is being used.
We’re going to use the NavigationMode property together with some changes to the original code. The first step is to change the Navigated event handler to simply store, in a global Boolean property of the App.xaml.cs class, whether the NavigationMode is reset. We’re going to use this information in another event handler. In fact, we need to subscribe to the Navigating event of the RootFrame class. This event is similar to the Navigated one, except that it is triggered before the navigation starts and not after. This difference is important for our purpose, since we have the chance to cancel the navigation operation before it’s executed.
Here is what we do in the Navigating event handler:
In this event, we wait for a specific condition to happen: the NavigationMode is Reset and navigation to the main page is triggered. This situation occurs when users tap on the main Tile and an instance of the app is already in memory. The first navigation redirects to the last visited page, and the second one (the Reset mode) redirects to the main page. It’s this scenario that we need to manage. By setting the Cancel property of the method’s parameter, we abort the navigation to the main page and leave the user on the last-visited page of the app. The experience is exactly the same as when the user returns to the application using the Back button.
Here is how the complete code required to implement Fast App Resume looks in the App.xaml.cs file:
private bool reset;
// Do not add any additional code to this method.
private void InitializePhoneApplication()
{
if (phoneApplicationInitialized)
return;
// Create the frame but don't set it as RootVisual yet; this allows the
// splash screen to remain active until the application is ready to render.
RootFrame = new PhoneApplicationFrame();
RootFrame.Navigated += CompleteInitializePhoneApplication;
// Handle navigation failures.
RootFrame.NavigationFailed += RootFrame_NavigationFailed;
// Handle reset requests for clearing the backstack.
RootFrame.Navigated += RootFrame_Navigated;
RootFrame.Navigating += RootFrame_Navigating;
// Ensure we don't initialize again.
phoneApplicationInitialized = true;
}
void RootFrame_Navigated(object sender, NavigationEventArgs e)
{
reset = e.NavigationMode == NavigationMode.Reset;
}
void RootFrame_Navigating(object sender, NavigatingCancelEventArgs e)
{
if (reset && e.IsCancelable && e.Uri.OriginalString == "/MainPage.xaml")
{
e.Cancel = true;
reset = false;
}
}
You may be wondering why all this effort is needed to support Fast App Resume. Why isn’t it automatically implemented by the operating system? The answer is that Fast App Resume isn’t suitable for every application—you have to be very careful when you decide to support it, because you can ruin the user experience if you don’t implement it well.
For example, if your users spend most of the time in the main page of your application (for example, a social networking app), they probably prefer to go straight to the Home page when they open the application, instead of resuming what they were doing before.
Here are two tips for improving the Fast App Resume experience:
If your application has many pages and a complex navigation hierarchy, add to the inner pages a quick way to go back to the home page (like a button in the Application Bar).
Add a timing condition. If the application hasn’t been used recently, disable Fast App Resume and redirect the user to the home page.
Manage Orientation
Typically, a Windows Phone device is used in portrait mode, but there are some scenarios where users can benefit from landscape mode. For example, if your application offers a way to insert long text, it can be more convenient for users to type using the landscape keyboard. Alternatively, in other scenarios, you can use the landscape mode to provide the content with a different layout.
By default, every new page added to a Windows Phone is displayed in portrait mode and does not support the landscape mode. When you rotate the phone, nothing happens. The orientation behavior is controlled by two attributes of the PhoneApplicationPage node in the XAML:
The SupportedOrientations property defines which orientations are supported by the page. To support both orientations, you need to set the value to PortraitOrLandscape.
The Orientation property defines the default orientation of the page.
Once you’ve set the SupportedOrientations property to PortraitOrLandscape, the page’s layout will automatically adapt to landscape mode when the phone is rotated.
If you want further control (for example, because you want to deeply change the page’s layout between portrait and landscape modes), the APIs offer a page event called OrientationChanged that is raised every time the phone is rotated from portrait to landscape mode and vice versa. The event is exposed directly by the PhoneApplicationPage class, so you can subscribe it in the XAML, as in the following sample:
The event handler that will be generated in the code will return the information about the new orientation. You can use it to move or change the aspect of the controls placed in the page. The following sample shows how to set a different Height of a Button control, according to the detected orientation:
If you’re new to Windows Phone development, this article is very important, because we’ve learned some basic core concepts that we’re going to use again in later installments of this series.
Here is a quick recap:
Asynchronous programming is must-have knowledge for mobile developers. We’ve learned how callbacks and the new async and await pattern will help you keep the user interface fast and responsive.
It’s highly unlikely that your application is made of just one page, so we’ve learned how to support navigation between different pages and how to correctly manage the navigation flow.
Application life cycle is another key concept. You have to understand how a Windows Phone application works under the hood to correctly manage all the multitasking scenarios. We’ve talked about Fast Application Switching, an improvement introduced in Windows Phone 7.5 to make the suspending and resuming process faster, and Fast App Resume, a new Window Phone 8 feature that improves the resuming experience.
This tutorial represents a chapter from Windows Phone 8 Succinctly, a free eBook from the team at Syncfusion.
In the previous tutorial, we added the ability to create to-do items. While this addition has made the application a bit more useful, it would also be convenient to add the ability to mark items as done and delete items. That's what we'll focus on in this tutorial.
Prerequisites
If you'd like to follow along with me, then make sure that you have Xcode 6.3 or higher installed on your machine. At the time of writing, Xcode 6.3 is in beta and available from Apple's iOS Dev Center to registered iOS developers.
The reason for requiring Xcode 6.3 or higher is to be able to take advantage of Swift 1.2, which Apple introduced in February. Swift 1.2 introduces a number of great additions that we'll take advantage of in the rest of this series.
1. Deleting Items
To delete items, we need to implement two additional methods of the UITableViewDataSource protocol. We first need to tell the table view which rows can be edited by implementing the tableView(_:canEditRowAtIndexPath:) method. As you can see in the below code snippet, the implementation is straightforward. We tell the table view that every row is editable by returning true.
The second method we're interested in is tableView(_:commitEditingStyle:forRowAtIndexPath:). The implementation is a bit more complex but easy enough to grasp.
We start by checking the value of editingStyle, an enumeration of type UITableViewCellEditingStyle. We only delete an item if the value of editingStyle is equal to UITableViewCellEditingStyle.Delete.
Swift is smarter than that though. Because it knows that editingStyle is of type UITableViewCellEditingStyle, we can omit UITableViewCellEditingStyle, the name of the enumeration, and write .Delete, the member value of the enumeration that we're interested in. If you're new to enumerations in Swift, then I recommend you read this quick-tip about enumerations in Swift.
Next, we fetch the corresponding item from the items property and temporarily store its value in a constant named item. We update the table view's data source, items, by invoking removeAtIndex(index: Int) on the items property, passing in the correct index.
Finally, we update the table view by invoking deleteRowsAtIndexPaths(_:withRowAnimation:) on tableView, passing in an array with indexPath and .Right to specify the animation type. As we saw earlier, we can omit the name of the enumeration, UITableViewRowAnimation, since Swift knows the type of the second argument is UITableViewRowAnimation.
The user should now be able to delete items from the list. Build and run the application to test this.
2. Checking Off Items
To mark an item as done, we're going to add a checkmark to the corresponding row. This implies that we need to keep track of the items the user has marked as done. For that purpose, we'll declare a new property that manages this for us. Declare a variable property, checkedItems, of type [String] and initialize it with an empty array.
var checkedItems: [String] = []
In tableView(_:cellForRowAtIndexPath:), we check whether checkedItems contains the respective item by using the contains function, a global function defined in the Swift Standard Library. We pass in checkedItems as the first argument and item as the second argument. The function returns true if checkedItems contains item.
If item is found in checkedItems, we set the cell's accessoryType property to .Checkmark, a member value of the UITableViewCellAccessoryType enumeration. If item isn't found, we fall back to .None as the cell's accessory type.
The next step is to add the ability to mark an item as done by implementing a method of the UITableViewDelegate protocol, tableView(_:didSelectRowAtIndexPath:). In this delegate method, we first call deselectRowAtIndexPath(_:animated:) on tableView to deselect the row the user tapped.
// MARK: Table View Delegate Methods
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
// Fetch Item
let item = self.items[indexPath.row]
// Fetch Table View Cell
let tableViewCell = tableView.cellForRowAtIndexPath(indexPath)
// Find Index of Item
let index = find(self.checkedItems, item)
if let index = index {
self.checkedItems.removeAtIndex(index)
tableViewCell?.accessoryType = UITableViewCellAccessoryType.None
} else {
self.checkedItems.append(item)
tableViewCell?.accessoryType = UITableViewCellAccessoryType.Checkmark
}
}
We then fetch the corresponding item from items and a reference to the cell that corresponds with the tapped row. We use the find function, defined in the Swift Standard Library, to obtain the index of item in checkedItems. The find function returns an optional Int. If checkedItems contains item, we remove it from checkedItems and set the cell's accessory type to .None. If checkedItems doesn't contain item, we add it to checkedItems and set the cell's accessory type to .Checkmark.
With these additions, the user is now able to mark items as done. Build and run the application to make sure that everything is working as expected.
3. Saving State
The application currently doesn't save state between launches. To solve this, we're going to store the items and checkedItems arrays in the application's user defaults database.
Step 1: Loading State
Start by creating two helper methods loadItems and loadCheckedItems. Note the private keyword prefixing each helper method. The private keyword tells Swift that these methods are only accessible from within this source file.
// MARK: Private Helpers
private func loadItems() {
let userDefaults = NSUserDefaults.standardUserDefaults()
if let items = userDefaults.objectForKey("items") as? [String] {
self.items = items
}
}
private func loadCheckedItems() {
let userDefaults = NSUserDefaults.standardUserDefaults()
if let checkedItems = userDefaults.objectForKey("checkedItems") as? [String] {
self.checkedItems = checkedItems
}
}
The private keyword is part of Swift's access control. As the name implies, access control defines which code has access to which code. Access levels apply to methods, functions, types, etc. Apple simply refers to entities. There are three access levels, public, internal, and private.
Public: Entities marked as public are accessible by entities defined in the same module as well as other modules. This access level is ideal for exposing the interface of a framework.
Internal: This is the default access level. In other words, if no access level is specified, this access level applies. An entity with an access level of internal is only accessible by entities defined in the same module.
Private: An entity declared as private is only accessible by entities defined in the same source file. For example, the private helper methods defined in the ViewController class are only accessible by the ViewController class.
The implementation of the helper methods is simple if you're familiar with the NSUserDefaults class. For ease of use, we store a reference to the standard user defaults object in a constant named userDefaults. In the case of loadItems, we ask userDefaults for the object associated with the key "items" and downcast it to an optional array of strings. We safely unwrap the optional, which means that we store the value in the constant items if the optional is not nil, and assign the value to the items property.
If the if statement looks confusing, then have a look at a simpler version of the loadItems method in the following example. The result is identical, the only difference is conciseness.
private func loadItems() {
let userDefaults = NSUserDefaults.standardUserDefaults()
let storedItems = userDefaults.objectForKey("items") as? [String]
if let items = storedItems {
self.items = items
}
}
The implementation of loadCheckedItems is identical except for the key used to load the object stored in the user defaults database. Let's put loadItems and loadCheckedItems to use by updating the viewDidLoad method.
override func viewDidLoad() {
super.viewDidLoad()
// Set Title
self.title = "To Do"
// Load State
self.loadItems()
self.loadCheckedItems()
// Register Class for Cell Reuse
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "TableViewCell")
}
Step 2: Saving State
To save state, we implement two more private helper methods, saveItems and saveCheckedItems. The logic is similar to that of loadItems and loadCheckedItems. The difference is that we store data in the user defaults database. Make sure that the keys used in the setObject(_:forKey:) calls match those used in loadItems and loadCheckedItems.
private func saveItems() {
let userDefaults = NSUserDefaults.standardUserDefaults()
// Update User Defaults
userDefaults.setObject(self.items, forKey: "items")
userDefaults.synchronize()
}
private func saveCheckedItems() {
let userDefaults = NSUserDefaults.standardUserDefaults()
// Update User Defaults
userDefaults.setObject(self.checkedItems, forKey: "checkedItems")
userDefaults.synchronize()
}
The synchronize call isn't strictly necessary. The operating system will make sure that the data you store in the user defaults database is written to disk at some point. By invoking synchronize, however, you explicitly tell the operating system to write any pending changes to disk. This is useful during development, because the operating system won't write your changes to disk if you kill the application. It may then seem as if something is not working properly.
We need to invoke saveItems and saveCheckedItems in a number of places. To start, call saveItems when a new item is added to the list. We do this in the delegate method of the AddItemViewControllerDelegate protocol.
When the state of an item changes in the tableView(_:didSelectRowAtIndexPath:), we update checkedItems. It's a good idea to also invoke saveCheckedItems at that point.
// MARK: Table View Delegate Methods
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
// Fetch Item
let item = self.items[indexPath.row]
// Fetch Table View Cell
let tableViewCell = tableView.cellForRowAtIndexPath(indexPath)
// Find Index of Item
let index = find(self.checkedItems, item)
if let index = index {
self.checkedItems.removeAtIndex(index)
tableViewCell?.accessoryType = UITableViewCellAccessoryType.None
} else {
self.checkedItems.append(item)
tableViewCell?.accessoryType = UITableViewCellAccessoryType.Checkmark
}
// Save State
self.saveCheckedItems()
}
When an item is deleted, both items and checkedItems are updated. To save this change, we call both saveItems and saveCheckedItems.
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Fetch Item
let item = self.items[indexPath.row]
// Update Items
self.items.removeAtIndex(indexPath.row)
if contains(self.checkedItems, item) {
self.checkedItems.removeAtIndex(indexPath.row)
}
// Save State
self.saveItems()
self.saveCheckedItems()
// Update Table View
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Right)
}
}
That's it. Build and run the application to test your work. Play with the application and force quit it. When you launch the application again, the last known state should be loaded and visible.
4. Property Observers
The application's user experience is a bit lacking at the moment. When every item is deleted or when the application is launched for the first time, the user sees an empty table view. This isn't great. We can solve this by showing a message when there are no items. This will also give me the opportunity to show you another feature of Swift, property observers.
Step 1: Adding a Label
Let's start by adding a label to the user interface for showing the message. Declare an outlet named messageLabel of type UILabel in the ViewController class, open Main.storyboard, and add a label to the view controller's view.
@IBOutlet var messageLabel: UILabel!
Add the necessary layout constraints to the label and connect it with the view controller's messageLabel outlet in the Connections Inspector. Set the label's text to You don't have any to-dos. and center the label's text in the Attributes Inspector.
Step 2: Implementing a Property Observer
The message label should only be visible if items contains no elements. When that happens, we should also hide the table view. We could solve this problem by adding various checks in the ViewController class, but a more convenient and elegant approach is to use a property observer.
As the name implies, property observers observe a property. A property observer is invoked whenever a property changes, even when the new value is the same as the old value. There are two types of property observers.
willSet: invoked before the value has changed
didSet: invoked after the value has changed
For our purpose, we will implement the didSet observer for the items property. Take a look at the syntax in the following code snippet.
The construct may look a bit odd at first so let me explain what's happening. When the didSet observer is invoked, after the items property has changed, we check if the items property contains any elements. Based on the value of the hasItems constant, we update the user interface. It's as simple as that.
The didSet observer is passed a constant parameter that contains the value of the old value of the property. It is omitted in the above example, because we don't need it in our implementation. The following example shows how it could be used.
var items: [String] = [] {
didSet(oldValue) {
if oldValue != items {
let hasItems = items.count > 0
self.tableView.hidden = !hasItems
self.messageLabel.hidden = hasItems
}
}
}
The oldValue parameter in the example doesn't have an explicit type, because Swift knows the type of the items property. In the example, we only update the user interface if the old value differs from the new value.
A willSet observer works in a similar fashion. The main difference is that the parameter passed to the willSet observer is a constant holding the new value of the property. When using property observers, keep in mind that they are not invoked when the instance is initialized.
Build and run the application to make sure everything is hooked up correctly. Even though the application isn't perfect and could use a few more features, you have created your first iOS application using Swift.
Conclusion
Over the course of the last three lessons of this series, you created a functional iOS application using Swift's object-oriented features. If you have some experience programming and developing applications, then you must have noticed that the current data model has a few shortcomings, to put it lightly. Storing items as strings and creating a separate array to store an item's state is not a good idea if you're building a proper application. A better approach would be to create a separate ToDo class for modeling items and store them in the application's sandbox. That will be our goal for the next installment of this series.
In the previous tutorial, we added the ability to create to-do items. While this addition has made the application a bit more useful, it would also be convenient to add the ability to mark items as done and delete items. That's what we'll focus on in this tutorial.
Prerequisites
If you'd like to follow along with me, then make sure that you have Xcode 6.3 or higher installed on your machine. At the time of writing, Xcode 6.3 is in beta and available from Apple's iOS Dev Center to registered iOS developers.
The reason for requiring Xcode 6.3 or higher is to be able to take advantage of Swift 1.2, which Apple introduced in February. Swift 1.2 introduces a number of great additions that we'll take advantage of in the rest of this series.
1. Deleting Items
To delete items, we need to implement two additional methods of the UITableViewDataSource protocol. We first need to tell the table view which rows can be edited by implementing the tableView(_:canEditRowAtIndexPath:) method. As you can see in the below code snippet, the implementation is straightforward. We tell the table view that every row is editable by returning true.
The second method we're interested in is tableView(_:commitEditingStyle:forRowAtIndexPath:). The implementation is a bit more complex but easy enough to grasp.
We start by checking the value of editingStyle, an enumeration of type UITableViewCellEditingStyle. We only delete an item if the value of editingStyle is equal to UITableViewCellEditingStyle.Delete.
Swift is smarter than that though. Because it knows that editingStyle is of type UITableViewCellEditingStyle, we can omit UITableViewCellEditingStyle, the name of the enumeration, and write .Delete, the member value of the enumeration that we're interested in. If you're new to enumerations in Swift, then I recommend you read this quick-tip about enumerations in Swift.
Next, we fetch the corresponding item from the items property and temporarily store its value in a constant named item. We update the table view's data source, items, by invoking removeAtIndex(index: Int) on the items property, passing in the correct index.
Finally, we update the table view by invoking deleteRowsAtIndexPaths(_:withRowAnimation:) on tableView, passing in an array with indexPath and .Right to specify the animation type. As we saw earlier, we can omit the name of the enumeration, UITableViewRowAnimation, since Swift knows the type of the second argument is UITableViewRowAnimation.
The user should now be able to delete items from the list. Build and run the application to test this.
2. Checking Off Items
To mark an item as done, we're going to add a checkmark to the corresponding row. This implies that we need to keep track of the items the user has marked as done. For that purpose, we'll declare a new property that manages this for us. Declare a variable property, checkedItems, of type [String] and initialize it with an empty array.
var checkedItems: [String] = []
In tableView(_:cellForRowAtIndexPath:), we check whether checkedItems contains the respective item by using the contains function, a global function defined in the Swift Standard Library. We pass in checkedItems as the first argument and item as the second argument. The function returns true if checkedItems contains item.
If item is found in checkedItems, we set the cell's accessoryType property to .Checkmark, a member value of the UITableViewCellAccessoryType enumeration. If item isn't found, we fall back to .None as the cell's accessory type.
The next step is to add the ability to mark an item as done by implementing a method of the UITableViewDelegate protocol, tableView(_:didSelectRowAtIndexPath:). In this delegate method, we first call deselectRowAtIndexPath(_:animated:) on tableView to deselect the row the user tapped.
// MARK: Table View Delegate Methods
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
// Fetch Item
let item = self.items[indexPath.row]
// Fetch Table View Cell
let tableViewCell = tableView.cellForRowAtIndexPath(indexPath)
// Find Index of Item
let index = find(self.checkedItems, item)
if let index = index {
self.checkedItems.removeAtIndex(index)
tableViewCell?.accessoryType = UITableViewCellAccessoryType.None
} else {
self.checkedItems.append(item)
tableViewCell?.accessoryType = UITableViewCellAccessoryType.Checkmark
}
}
We then fetch the corresponding item from items and a reference to the cell that corresponds with the tapped row. We use the find function, defined in the Swift Standard Library, to obtain the index of item in checkedItems. The find function returns an optional Int. If checkedItems contains item, we remove it from checkedItems and set the cell's accessory type to .None. If checkedItems doesn't contain item, we add it to checkedItems and set the cell's accessory type to .Checkmark.
With these additions, the user is now able to mark items as done. Build and run the application to make sure that everything is working as expected.
3. Saving State
The application currently doesn't save state between launches. To solve this, we're going to store the items and checkedItems arrays in the application's user defaults database.
Step 1: Loading State
Start by creating two helper methods loadItems and loadCheckedItems. Note the private keyword prefixing each helper method. The private keyword tells Swift that these methods are only accessible from within this source file.
// MARK: Private Helpers
private func loadItems() {
let userDefaults = NSUserDefaults.standardUserDefaults()
if let items = userDefaults.objectForKey("items") as? [String] {
self.items = items
}
}
private func loadCheckedItems() {
let userDefaults = NSUserDefaults.standardUserDefaults()
if let checkedItems = userDefaults.objectForKey("checkedItems") as? [String] {
self.checkedItems = checkedItems
}
}
The private keyword is part of Swift's access control. As the name implies, access control defines which code has access to which code. Access levels apply to methods, functions, types, etc. Apple simply refers to entities. There are three access levels, public, internal, and private.
Public: Entities marked as public are accessible by entities defined in the same module as well as other modules. This access level is ideal for exposing the interface of a framework.
Internal: This is the default access level. In other words, if no access level is specified, this access level applies. An entity with an access level of internal is only accessible by entities defined in the same module.
Private: An entity declared as private is only accessible by entities defined in the same source file. For example, the private helper methods defined in the ViewController class are only accessible by the ViewController class.
The implementation of the helper methods is simple if you're familiar with the NSUserDefaults class. For ease of use, we store a reference to the standard user defaults object in a constant named userDefaults. In the case of loadItems, we ask userDefaults for the object associated with the key "items" and downcast it to an optional array of strings. We safely unwrap the optional, which means that we store the value in the constant items if the optional is not nil, and assign the value to the items property.
If the if statement looks confusing, then have a look at a simpler version of the loadItems method in the following example. The result is identical, the only difference is conciseness.
private func loadItems() {
let userDefaults = NSUserDefaults.standardUserDefaults()
let storedItems = userDefaults.objectForKey("items") as? [String]
if let items = storedItems {
self.items = items
}
}
The implementation of loadCheckedItems is identical except for the key used to load the object stored in the user defaults database. Let's put loadItems and loadCheckedItems to use by updating the viewDidLoad method.
override func viewDidLoad() {
super.viewDidLoad()
// Set Title
self.title = "To Do"
// Load State
self.loadItems()
self.loadCheckedItems()
// Register Class for Cell Reuse
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "TableViewCell")
}
Step 2: Saving State
To save state, we implement two more private helper methods, saveItems and saveCheckedItems. The logic is similar to that of loadItems and loadCheckedItems. The difference is that we store data in the user defaults database. Make sure that the keys used in the setObject(_:forKey:) calls match those used in loadItems and loadCheckedItems.
private func saveItems() {
let userDefaults = NSUserDefaults.standardUserDefaults()
// Update User Defaults
userDefaults.setObject(self.items, forKey: "items")
userDefaults.synchronize()
}
private func saveCheckedItems() {
let userDefaults = NSUserDefaults.standardUserDefaults()
// Update User Defaults
userDefaults.setObject(self.checkedItems, forKey: "checkedItems")
userDefaults.synchronize()
}
The synchronize call isn't strictly necessary. The operating system will make sure that the data you store in the user defaults database is written to disk at some point. By invoking synchronize, however, you explicitly tell the operating system to write any pending changes to disk. This is useful during development, because the operating system won't write your changes to disk if you kill the application. It may then seem as if something is not working properly.
We need to invoke saveItems and saveCheckedItems in a number of places. To start, call saveItems when a new item is added to the list. We do this in the delegate method of the AddItemViewControllerDelegate protocol.
When the state of an item changes in the tableView(_:didSelectRowAtIndexPath:), we update checkedItems. It's a good idea to also invoke saveCheckedItems at that point.
// MARK: Table View Delegate Methods
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
// Fetch Item
let item = self.items[indexPath.row]
// Fetch Table View Cell
let tableViewCell = tableView.cellForRowAtIndexPath(indexPath)
// Find Index of Item
let index = find(self.checkedItems, item)
if let index = index {
self.checkedItems.removeAtIndex(index)
tableViewCell?.accessoryType = UITableViewCellAccessoryType.None
} else {
self.checkedItems.append(item)
tableViewCell?.accessoryType = UITableViewCellAccessoryType.Checkmark
}
// Save State
self.saveCheckedItems()
}
When an item is deleted, both items and checkedItems are updated. To save this change, we call both saveItems and saveCheckedItems.
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Fetch Item
let item = self.items[indexPath.row]
// Update Items
self.items.removeAtIndex(indexPath.row)
if contains(self.checkedItems, item) {
self.checkedItems.removeAtIndex(indexPath.row)
}
// Save State
self.saveItems()
self.saveCheckedItems()
// Update Table View
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Right)
}
}
That's it. Build and run the application to test your work. Play with the application and force quit it. When you launch the application again, the last known state should be loaded and visible.
4. Property Observers
The application's user experience is a bit lacking at the moment. When every item is deleted or when the application is launched for the first time, the user sees an empty table view. This isn't great. We can solve this by showing a message when there are no items. This will also give me the opportunity to show you another feature of Swift, property observers.
Step 1: Adding a Label
Let's start by adding a label to the user interface for showing the message. Declare an outlet named messageLabel of type UILabel in the ViewController class, open Main.storyboard, and add a label to the view controller's view.
@IBOutlet var messageLabel: UILabel!
Add the necessary layout constraints to the label and connect it with the view controller's messageLabel outlet in the Connections Inspector. Set the label's text to You don't have any to-dos. and center the label's text in the Attributes Inspector.
Step 2: Implementing a Property Observer
The message label should only be visible if items contains no elements. When that happens, we should also hide the table view. We could solve this problem by adding various checks in the ViewController class, but a more convenient and elegant approach is to use a property observer.
As the name implies, property observers observe a property. A property observer is invoked whenever a property changes, even when the new value is the same as the old value. There are two types of property observers.
willSet: invoked before the value has changed
didSet: invoked after the value has changed
For our purpose, we will implement the didSet observer for the items property. Take a look at the syntax in the following code snippet.
The construct may look a bit odd at first so let me explain what's happening. When the didSet observer is invoked, after the items property has changed, we check if the items property contains any elements. Based on the value of the hasItems constant, we update the user interface. It's as simple as that.
The didSet observer is passed a constant parameter that contains the value of the old value of the property. It is omitted in the above example, because we don't need it in our implementation. The following example shows how it could be used.
var items: [String] = [] {
didSet(oldValue) {
if oldValue != items {
let hasItems = items.count > 0
self.tableView.hidden = !hasItems
self.messageLabel.hidden = hasItems
}
}
}
The oldValue parameter in the example doesn't have an explicit type, because Swift knows the type of the items property. In the example, we only update the user interface if the old value differs from the new value.
A willSet observer works in a similar fashion. The main difference is that the parameter passed to the willSet observer is a constant holding the new value of the property. When using property observers, keep in mind that they are not invoked when the instance is initialized.
Build and run the application to make sure everything is hooked up correctly. Even though the application isn't perfect and could use a few more features, you have created your first iOS application using Swift.
Conclusion
Over the course of the last three lessons of this series, you created a functional iOS application using Swift's object-oriented features. If you have some experience programming and developing applications, then you must have noticed that the current data model has a few shortcomings, to put it lightly. Storing items as strings and creating a separate array to store an item's state is not a good idea if you're building a proper application. A better approach would be to create a separate ToDo class for modeling items and store them in the application's sandbox. That will be our goal for the next installment of this series.
Becoming a smart watch app developer is a great idea, but what if you are an HTML developer? Will you be able to realize your dream without switching to a completely different platform? Do you have to abandon all of your HTML skills and start all over again? Don't worry. Tizen comes to your rescue.
Tizen is an operating system of the Linux family, targeting a range of devices from smartphones to smart watches and a lot more. While Tizen is a project within the Linux Foundation, it is guided by the Tizen Association, whose members include Samsung, Intel, and other well known companies in the technology industry.
In this tutorial, I will show you how to install and configure the Tizen SDK for Wearable and develop a smart watch application with the IDE. Let's get started.
1. Installing & Configuring the SDK
Step 1: Tizen SDK or Tizen SDK for Wearable?
Currently, two types of SDKs are available, Tizen SDK and Tizen SDK for Wearable. Since this
tutorial is about developing a standalone smart watch app, what you need is Tizen
SDK for Wearable.
You can download it from the Tizen Developers website.
You need to download an appropriate install manager that matches your operating
system and version. If you prefer an offline installation to an online one,
you need to download an SDK image too. If your operating system is Windows 8 or
Windows 8.1, you can download the installation files categorized under Windows
7. They will work on Windows 8 and 8.1 just fine.
Step 2: Requirements
Refer to Tizen's detailed instructions to read about the hardware and software requirements your computer should meet.
You can install the SDK even if your computer doesn't meet these hardware requirements. However, if you do, the smart watch emulator will be slow, resulting in poor app testing. Visit the documentation for more details. It explains how to enable Virtualization Technology (VT) in your BIOS and Data Execution Prevention on Windows.
Step 3: Installing the SDK
Run the install manager you downloaded earlier. This is an .exe file with a filename like tizen-wearable-sdk-2.2.159_windows64.exe, depending on your operating system and version.
Click Advanced to go to the next screen.
In that screen, check the SDK image radio button and navigate to the zip file containing the appropriate SDK Image. Note that I'm assuming that you prefer an offline installation and you have already downloaded the necessary SDK image to your development machine.
Select the SDK image zip file and click Open in the dialog box.
An Extracting SDK Image message will appear. Click OK after finishing the extraction.
Click Next and the License Agreement window will appear.
Agree to the license and click Next.
The configuration window will appear next. I recommend leaving all the check boxes checked and clicking Next.
Finally, when the install manager asks for a location for the installation, specify your choice by selecting a path and clicking Install.
If you have already configured your BIOS correctly, Intel Hardware Accelerated Execution Manager (Intel HAXM) will also be automatically installed during this process. If not, quit the installation process and configure the BIOS so that it can allow Intel HAXMto be installed.
Don't forget to enable Data Execution Prevention if your operating system is Windows. Resume the installation.
If you wish, you can install Intel HAXM separately. Once the installation is finished, restart your computer.
Step 4: Configuring the IDE
Browse to the folder in which you have installed the SDK and navigate to the ide subfolder. Run the executable file named IDE.
After a few minutes, a window will appear, asking for a location for the workspace to save the apps you develop. Specify a path of your choice for the location and click OK. After the configuration, the IDE should appear.
Step 5: Features of the IDE
On the left pane of the IDE, there are two windows, Project Explorer and Connection Explorer. The Project Explorer shows the projects created by the user. The Connection Explorer lists the connected devices that are currently available, emulator instances or remote devices.
Step 6: Creating an Emulator Instance
In the Connection Explorer, click on the Emulator Manager icon, the leftmost blue button.
Click Yes in the User Account Control window that appears. This will bring up the Emulator Manager window.
Click Add New and give the emulator instance a name.
When you click Confirm, the new emulator instance will be created. Click the blue button with an arrow in the emulator icon to launch the emulator.
It will take some time to launch the emulator. You should see a window with a starting screen similar to the below screenshot when it is up and running. The emulator instance should appear as an entry in the Connection Explorer.
Swipe up from the bottom middle point of the starting screen to go to the screen that displays the installed apps on the device or emulator. Since you haven't installed any apps yet, only the Settings icon is displayed.
You can go back to the previous screen or quit an app by swiping down from the top middle of the screen.
2. Developing a Simple Comic App
In this example, we're going to create a simple app to display a comic strip. Let's look at each step in turn.
Step 1: Creating a New Project
Let's create a new project in the IDE.
Go to File > New > Tizen Wearable Web Project.
In the window that appears, select Basic > Basic application and set the Project name to myapp.
Tick the Use default location check box or browse to a different location of your choice, and click Finish.
Your new project, myapp, should appear in the Project Explorer.
Click the small arrow on the left of myapp to expand the project structure.
You should see an index.htmlfile, a css subfolder, a js subfolder, and a few other files and folders.
HTML, CSS, and JavaScript form the basis for programming on the Tizen platform. If you're an HTML wizard, then you don't have to learn a new programming language to write applications for the Tizen platform. You can use your existing HTML, CSS, and JavaScript skills.
Step 2: Adding Files, Assets, and Resources
We first need to add two subfolders to the myapp project, comic and images. To do this, right-click the myapp project folder in the IDE and select New > Folder. The subfolders should appear in the expanded myappfolder in the IDE.
Download the source files for this project from GitHub and navigate to the images subfolder, which contains a number of png files. Copy the png files to the images subfolder you created a moment ago.
You can paste files to the images subfolder in the Project Explorer window by right-clicking the subfolder and selecting Paste from the popup menu.
Next, create nine HTML files with the following file names in the comic subfolder by right-clicking the comic subfolder and selecting New > File. Make sure to include the .html extension for the files.
page1.html
page2.html
...
page9.html
You should now have nine HTML files in the comic subfolder.
Step 3: Adding Code
Let's now edit the code in index.html. This file is the entry point of your application. Double-click index.html to open the file in the IDE. Change the contents of the <title> tag to <title>2nd Race</title>. Next, change the contents of the <body> tag with the following:
All we did, is add an image to the page and two buttons to navigate to the other pages since our comic will have ten pages. Once you have made these changes, save the file by selecting File > Save from the menu.
We've added some styling for body, images, and the navigation menus. Once you have made the changes, save the file.
Similarly, add code to all the other HTML files you have created. The style.css file in the css subfolder must be externally linked to all of these HTML files. If you're note sure about this step, then download the source files from GitHub and examine the source for clarification.
Step 4: Testing the App
To test your app, select the myapp project folder and, from the menu, choose Project > Build Project to build the project. Make sure the emulator instance is up and running.
Right-click the myapp folder and select Run As > Tizen Wearable Web Application to run the project in the emulator. Use the arrow buttons in the user interface to navigate to the next or previous page. Swipe down from the top of the screen to quit the app.
Conclusion
In this tutorial, we built a simple comic app with the Tizen SDK for
Wearable and ran it on the smart watch emulator that comes bundled with
the IDE. The real fun begins when you are ready to install and run it on
a physical device.
You can find a lot of additional information on the Samsung and Tizen Developers websites. What are you waiting for?