Windows Bridge for iOS: Customizing the Surface Dial Experience
In part one of the Windows Bridge for iOS series, we created a simple to-do list app in Xcode and used the iOS bridge to bring it over to Windows 10. In part two of the series, we went on a tour of Visual Studio for iOS developers. In part three, we used the Windows Bridge for iOS to convert an iOS calculator app created using Storyboards and Auto Layout to a Universal Windows Platform app that adjusts to various form factors on Windows 10. In part four, we discussed how to mix and match UIKit with XAML controls in a bridged UWP app.
Today, we explain how to customize the Surface Dial Experience for bridged UWP apps using Objective-C projections.
Getting Started
For today’s tutorial, you will need:
- A PC running Windows 10 with Visual Studio 2015 and theWindows Bridge for iOS installed.
- A Mac running Mac OS X 10.11 with Xcode 7 installed.
- A copy of the Radial Control sample initial code.
If you don’t have a Windows 10 PC, you can download one of our pre-built evaluation virtual machines from the Windows Bridge for iOS website. Download the package for your preferred virtualization environment and you’ll be up and running in no time.
Understanding the Project Structure
With the radial control sample initial code downloaded and unzipped on your Mac development environment, open the Xcode project and briefly explore the application. The application consists of:
- AppDelegate – contains theUIApplicationInitialStartupMode Category that ensures the app scales properly on Windows 10 for a variety of form factors.
- ViewController – provides the view management infrastructure for the app’s user interface, which consists of a slider, a label that is updated with the value of the slider and a switch.
Build and run the application in the simulator and move the slider to make sure everything is properly set up.
Using vsimporter
You are now ready to run the app through the vsimporter tool. To do so, you’ll need to copy your Xcode project files to your Windows Machine (or VM). With the files copied, follow the steps under the Using vsimporter section of part three of the blog series. Once you’re done, return to your radial control project directory, which should now contain a brand new Visual Studio solution file.
Open the Visual Studio solution by double-clicking on theRadialControlSample-WinStore10.sln file and build and run the application on your local machine by clicking on the Run button on the top menu. You’ll notice the same UI we saw running in the Xcode Simulator — you now have a native iOS app running on Windows. Try moving the slider and you’ll see its value label change.
Adding a New Menu Item to the Radial Controller Tool
You will now update this app to add a custom menu item to the radial controller tool that will respond to rotate and click actions by changing the slider value and toggling the switch.
To implement radial controller features in your app, you will need the public headers for the relevant UWP frameworks. In the bridge SDK you downloaded, go to theinclude\Platform\Universal Windows\UWP directory and take a look at what you find. Each header file represents a different namespace within the Windows Runtime APIs. For our purposes, you will need APIs from Windows.UI.Input since the radial controller is an input device and all of its classes are contained in that namespace.
To include this framework – and make sure it’s only included when the code is being run on Windows – start by adding an#ifdef and the #import macros to the top of the view controller implementation file:
1
2
3
4
5
| #import "ViewController.h" #ifdef WINOBJC #import < UWP /WindowsUIInput.h> #endif |
To interact with the radial controller, you will need to add a property to the view controller of your app that will allow you to access it. In C++, wheel input devices are represented by theRadialController class. However, as you build using Objective-C projections, you will notice that the standard naming scheme for these objects has been modified to match Objective-C conventions, where classes are prefixed with the letters that constitute their containing namespace:
- Windows.UI.Input.RadialController becomesWUIRadialController
As a result, add a WUIRadialController property to the@interface section of the view controller implementation file:
1
2
3
4
5
6
7
8
9
10
11
12
13
| @interface ViewController() @property UILabel *demoTitle; @property UILabel *demoInfo; @property UISlider *slider; @property UILabel *sliderLabel; @property UISwitch *switchControl; #ifdef WINOBJC @property WUIRadialController* radialController; #endif @end |
Next, you need to get a reference to the WUIRadialControllerobject with the CreateForCurrentView method as explained in theRadialController class documentation. Looking at theWindowsUIInput.h header you’ll find the equivalent Objective-C projection under the WUIRadialController class interface:
1
2
3
| @interface WUIRadialController : RTObject [...] + (WUIRadialController*)createForCurrentView; |
Call this method at the end of the viewDidLoad method of the view controller file to instantiate the WUIRadialControllerproperty:
1
2
3
4
5
6
7
8
| - ( void )viewDidLoad { [...] #ifdef WINOBJC // Create a reference to the radial controller self.radialController = [WUIRadialController createForCurrentView]; #endif } |
Now you need to get a reference to the radial controller menu and its items. This is done via the Menu property of theRadialController class that returns a RadialControllerMenu object. Looking back at the WindowsUIInput.h header, you’ll find the equivalent Objective-C property under the WUIRadialControllerclass interface that returns a WUIRadialControllerMenu object:
1
2
3
| @interface WUIRadialController : RTObject [...] @property (readonly) WUIRadialControllerMenu* menu; |
Call this property to get a reference to the radial controller menu:
1
2
3
4
5
6
7
8
9
10
11
| - ( void )viewDidLoad { [...] #ifdef WINOBJC // Create a reference to the radial controller self.radialController = [WUIRadialController createForCurrentView]; // Get the radial controller menu WUIRadialControllerMenu* menu = self.radialController.menu; #endif } |
The menu items are accessible via the Items property of theRadialControllerMenu class. As before, the interface of theWUIRadialControllerMenu class in the WindowsUIInput.hheader gives you the equivalent Objective-C property:
1
2
3
| @interface WUIRadialControllerMenu : RTObject [...] @property (readonly) NSMutableArray* /* WUIRadialControllerMenuItem* */ items; |
Call this property to get a reference to the menu items:
1
2
3
4
5
6
7
8
9
10
| - ( void )viewDidLoad { [...] // Get the radial controller menu WUIRadialControllerMenu* menu = self.radialController.menu; // Get the menu items NSMutableArray* menuItems = menu.items; #endif } |
Next, you need to create a new RadialControllerMenuItem object to add to the menu with the projection of theCreateFromKnownIcon class method:
1
2
| @interface WUIRadialControllerMenuItem : RTObject + (WUIRadialControllerMenuItem*)createFromIcon:(NSString *)displayText icon:(WSSRandomAccessStreamReference*)icon; |
Call this method to create the new menu item:
1
2
3
4
5
6
7
8
9
10
11
| - ( void )viewDidLoad { [...] // Get the menu items NSMutableArray* menuItems = menu.items; // Create a new menu item // To use your own custom icon for the menu item, use the createFromIcon method instead WUIRadialControllerMenuItem* newMenuItem = [WUIRadialControllerMenuItem createFromKnownIcon: @"Custom Tool" value:WUIRadialControllerMenuKnownIconRuler]; #endif } |
Note that we reused an existing icon for our tool from theRadialControllerMenuKnownIcon enumeration, but you can create your own and use the CreateFromIcon method instead.
Finally, add your new menu item to the menu items array:
1
2
3
4
5
6
7
8
9
10
11
| - ( void )viewDidLoad { [...] // Create a new menu item // To use your own custom icon for the menu item, use the createFromIcon method instead WUIRadialControllerMenuItem* newMenuItem = [WUIRadialControllerMenuItem createFromKnownIcon: @"Custom Tool" value:WUIRadialControllerMenuKnownIconRuler]; #endif // Add a new menu item [menuItems addObject:newMenuItem]; } |
That’s it! Now build and run your application and press and hold the Surface Dial to see the new menu item appear.
Adding a Handler for Click Input
In this section, you will add a handler for click input that will toggle the application switch control if the radial controller is clicked when the new tool you added to the menu is selected. Taking a look at the WindowsUIInput.h header, you’ll see you need the addButtonClickedEvent: method:
1
2
3
| @interface WUIRadialController : RTObject [...] - (EventRegistrationToken)addButtonClickedEvent:(void(^)(WUIRadialController*, WUIRadialControllerButtonClickedEventArgs |
Since the callback relies on Objective-C blocks, you need to mark the self reference with the __block keyword before using it to access the switch to avoid creating a retain cycle. Add the following code at the end of the viewDidLoad method to do this:
1
2
3
4
5
6
7
8
| - ( void )viewDidLoad { [...] // Add a new menu item [menuItems addObject:newMenuItem]; __weak ViewController* weakSelf = self; // Ensures self will not be retained } |
Now you can safely toggle the switch in the radial controller click callback:
1
2
3
4
5
6
7
8
9
10
11
| - ( void )viewDidLoad { [...] __weak ViewController* weakSelf = self; // Ensures self will not be retained // Add a handler for click input from the radial controller [self.radialController addButtonClickedEvent:^(WUIRadialController* controller, WUIRadialControllerButtonClickedEventArgs* args) { [weakSelf.switchControl setOn:!(weakSelf.switchControl. on ) animated:YES]; }]; } |
You can now build and run your application, select the new menu item, and click on the radial controller to see the switch toggle.
Adding a Handler for Rotation Input
In this section, you will add a handler for rotation input that will move the application slider control if the radial controller is rotated when the new tool you added to the menu is selected. Taking a look at the WindowsUIInput.h header, you’ll see that you need the addRotationChangedEvent: method:
1
2
3
| @interface WUIRadialController : RTObject [...] - (EventRegistrationToken)addRotationChangedEvent:(void(^)(WUIRadialController*, WUIRadialControllerRotationChangedEventArgs |
As for the click event handler, simply call the method and update the slider value in the callback block:
1
2
3
4
5
6
7
8
9
10
11
12
13
| - ( void )viewDidLoad { [...] __weak ViewController* weakSelf = self; // Ensures self will not be retained [...] // Add a handler for rotation input from the radial controller [self.radialController addRotationChangedEvent:^(WUIRadialController* controller, WUIRadialControllerRotationChangedEventArgs* args) { [weakSelf.slider setValue:(weakSelf.slider.value + ([args rotationDeltaInDegrees]/360.0f)) animated:YES]; }]; } |
That’s it! Now build and run your application, select the new menu item and rotate the radial controller to see the slider value change.
Wrapping Up
Thanks for following along! You can download the completeRadial Control sample for the final project. Take a look at the following resources for more information:
- For the latest release of the Windows Bridge for iOS, see theWinObjC GitHub Repository
- For documentation and tutorials, visit the Windows Bridge for iOS wiki
- To evaluate the compatibility of your app with the bridge and evaluation virtual machines, go to the Windows Bridge for iOS website
- For sample apps and code using the bridge, check theWinObjC-Samples GitHub Repository
- For an overview of Windows wheel devices and UX and developer guidance, see Surface Dial interactions on MSDN.
- For developer documentation on the Windows input system and classes, go to the UI.Input namespace documentation on MSDN.
- To find the iOS bridge API, check the UWP header libraries.
Also, be sure to check out the other posts in our series:
Post a Comment