The Idea behind
As a dedicated .NET developer I don't want to mess around with "Objective-C" and so MonoTouch is the right thing for me. It allows to develop
iPhone applications using .NET with C#.
The problems I stumbled upon when starting iPhone development (which I did in "Objective-C" - OC
from now on) was that there are many samples for OC and that a lot of things work "out of the box". Switching to MonoTouch had two effects for me. The
negative: OC Samples have to be recoded and things are somehow different. The positive: back in C# I could use my existing .NET skills. .NET is
different than OC - so things have to be done in the .NET way.
The sample - and what's missing
This sample is based on an existing project (Section Control) which I built during the last week. It lacks great Design and extensive error
handling.
In this sample I'll show three common things. First the use of a navigation controller, second the support for portrait / landscape modes
and last but not least, "Dialog handling".
Contents of the sample:
- The use of a navigation controller, a very common thing - often needed
- Rearrangement of views and their content
- Simulation of Dialog behavior
- Lots of .NET things (Events, inheritance, ...)
- XIB Files
I like UI designers - but of course everything could be done in code also.
The "project"
Our project will have 2 screens, one for "Settings" and one main application view.
Parts of the sample
The Main View
The page has 3 "sections". Each of it is a view which will show different content from the same data.
Think of it as something like a contact
detail view where one shows "Global information" (Name, Gender, ...) while another part shows communication elements (Phone numbers, email
addresses, ...) and the last one shows the address (City, Street, ...).
The trick with those three parts is to rearrange them when the device
orientation changes between landscape and portrait. The result should look something like this:
Figure 1: Main View in Portrait and Landscape

NOTEWe will not build such a complex thing of course. These are screenshots of my Section Control application. But the concepts and technologies are the
same.
The Content Views
These three guys will be placed in the Main View. While two of them are pretty simple - one will be divided in two "regions" so we can change the
layout easily.
The Settings (Options) View
Also a very common thing - we will use this to show how navigation works and how we can transfer data between the views. Normally such a view
would be some kind of a table view - but in our case we will use a static view, just to keep the things simple.
The Data
To make things easy we will use a Point of Interest (POI). The data looks like this:
We have three properties, exactly the same as the number of our views.
Let's start
First we will create a new MonoTouch iPhone application. Chosse File/New/Project which brings up the following dialog:
Figure 2: The new project dialog

Use the settings (choices) as shown in the image above and MonoTouch will create the following solution.
Figure 3: The newly created project

We will later create code - first let's add all the elements to the project and do some design work. As I wrote
before we'll use XIB files to have a UI designer. MonoTouch offers a project element for this. Right click on you solution in solution Explorer and choose
"Add / New File".
Figure 4: Add a new View Controller

Repeat this step and create: VCMainView, VCInnerViewComplex, VCOptionsView.
Next let's add
four simple class files: OurNaviController, BaseInner, BaseComplexInner and OurData.
Figure 5: Add standard files (Classes, Interfaces, ...)

Figure 6: The final look of the project

Notice: IDataHolder is not used in this article!
Now we have all the classes needed to complete our
project. Before we go on I'll explain some concepts - well known by iPhone developers - but maybe new for .NET guys like me.
The graphic
shows the data for USA at the moment. And on the left side we can make a choice between Europe, USA and Asia.
iPhone basics - XIB files
XIB files are something like the xxx.designer.cs files for Windows Forms applications. MonoTouch parses these files and creates code for us. And
here is the first difference. In Windows Forms we create a control and give it an ID. With XIB we use something called "outlets". This is somehow
different, but you'll get used to it.
XIB files are use by an application called "Interface Builder". Just double click a XIB in MonoTouch to open the
file in this program.
The general handling: you select a class (most of the time the one behind "File's Owner") normally the one which is the
UIController. For an example VCMainView is the class we are using when defining "outlets" for our controls in the VCMainView XIB.
The steps to
create an "outlet" and bind it to a control:
- Drag the control to the surface (don't care about the name)
- Select "Classes" from the Library window
- Select your "Files Owner Class" (VCMainView in this case)
- Select "Outlets" from the class detail view
- Click the plus sign below the list and add an "ID" for your control
- OPTIONAL - choose the correct class for your outlet
Repeat those steps for all controls you want to reference from code. Of course it is easier to first drag all controls to the view, arrange them and
so forth and after this add all the outlets.
Anyhow - you have your controls, you have your outlets - now we have to assign them.
- In the document window select the "File's Owner"
- From the Tools menu chose "Connections". There you'll see your outlets with something like a "Radio button" on the right side
- Click on the "Radio button" and drag it to your control (where the outlet should be connected to). Tip - if you can't reach the control on your
view window (maybe it's behind another control) drag the line to the element in the Document Window.
- Drag the lines (connect) all outlets with your controls.
After saving your file you'll have access to the "outlets" in your code, just like controls in Windows Forms applications.
There are some
"get started videos" on the apple developer portal which show how this is done in "Interface Builder". If you couldn't follow the above explanations - just
watch such a video.
iPhone basics - Forms...
There is no "Forms" concept on iPhone. Instead we use Views - and View-Controllers. And since we have no "multiple windows UI" there is also
nothing like Form.Show() or Form.ShowDialog().
If you build a normal (games are different) application for the
iPhone you'll follow a pattern called MVC.
If MVC is something new for you - please use your favorite search engine for closer
information.
A View-Controller (UIViewController) normally owns a view (or some views) and controls it. It uses the view to display data and
acts as a layer between his view(s) and other controllers "above".
There are special controllers like the Navigation Controller which provide
special functionality. We will us such a Navigation Controller to display our different "Forms", which are UIViewControllers.
You will always see
one View (Controller) at a time, there are no "Dialogs" and the things are asynchronous.
So that's the point where things become a bit
different.
To display something like a dialog (in our case the Options) you either exchange the current view (controller) or (more common) use a
Navigation Controller.
Since there is no Dialog (blocking the caller) functionality we have to add some kind of notification when the "Dialog" gets
closed. Since we have a .NET environment this is not to difficult - we simply implement an event in our Options class and before we show the "Dialog"
we subscribe to this event.
Let's build the interfaces
Before we get started one note - normally I would name the controls similar (So the Title-Label is always called lblTitle
for example). In this case I use different names - just to show that there is a separation between the UI Elements and the data binding
logic.
NOTEI do this in the complex inner view. Enhancement: change the label to a text-field and the hyperlink will work!
The Main View
This view will hold three sub-views. And to see what's going on we will first create them with different colors. Later we will place our inner views
there.
Double click VCMainView.xib in MonoTouch which starts Interface Builder. Drag one "UIView" from the library to your view. Size it to 290 x
135. After it hast the correct size copy and paste it. Place it to the next position. Use copy and paste again to create the third view. Change the color of
the views and finally the things should look like below.
Figure 7: Main View Design

Also create three outlets as shown on the screenshot and don't forget to connect them with "File's Owner". Save the
file and go back to MonoTouch.
The Inner View
To create the inner views we have to manage something in Interface Builder. The inner views will be the same size as the three parts in the main
view. But unfortunately you can't change the view in Interface Builder to this size. The reason - IB thinks you have a Status bar.
Double Click
VCInnerViewSimple.xib in MonoTouch. In IB choose Tools / Attributes Inspector and set "Status Bar" to None. After this choose Tools / Size Inspector
and change the size to 290 x 135.
Figure 8: View without Status bar

Now create three Labels - place them like you want and create outlets for the "small ones" like in the
screenshot below. Don't forget to connect them. The text for the first label will show us the element we use; this is static and therefore we need no outlet
for it. The other labels will be used to display data.
Figure 9: Simple Inner View

The Complex Inner View
Like with the view before we set status bar to none and change the size to 290 x 135. Instead of placing the labels on the view we place two UIView
elements first (I colored them to make them better visible) and then we place the labels on these first "sub view" and a button on the second. We add
outlets for the 2 views for the data labels and for the button. Again I added some static text. The result should look like in the screenshot below.
Figure 10: Complex Inner View

So almost every XIB is handled - there is one left - our main window.
The Main Window
This one is a little bit different. It will hold our navigation controller - but first let it just hold a default navigation controller. To add such a thing we
choose a Navigation Controller from the Object in the library. (One of these golden circle elements). You can't drop it on the view (window) itself. Instead
you must drag it to the document window. This will add a lot of elements. A Navigation Controller, a navigation bar, a View Controller and a navigation
item. Don't worry - we get what we need.
Since we will not display a navigation bar (at the moment) we select navigation controller and in the
attributes we uncheck the Shows Navigation Bar checkbox. Notice that I change the background color of the window!
IMPORTANTThis time we do not use File's Owner to create our delegates. Instead we use App Delegate.
Figure 11: Main Window

There is one step left - change the class of the "Root View Controller" to our VCMainView.
Figure 12: Set VCMainView

The Settings / Options view
This final view will be simple - and it will have one special thing - a navigation item with two buttons.
One Done button and one Cancel button.
To do this we first drag a navigation item to the view. A Navigation item has to be dragged to the document window (like the navigation controller before).
Next drag a "Bar button item" to the Navigation Item and a second one somewhere on the document window. Set the style of the one inside the
navigation item to "Done" and give it the text Done (or OK if you like).
IMPORTANTNow we have to assign the buttons to the Navigation Item. Mark the Navigation Item and from Connections assign
them like in the screenshot below. (One should already be assigned).
Figure 13: Navigation Buttons in the Bar

Finally add labels and a switch to the view. Create the following outlets.
IMPORTANTNavigationItem must be exactly written as seen in the screenshot!
Figure 14: Options View

Notice the two labels (upper and normal case) at (almost) the same location.
Next we also add outlets to the Bar-Buttons
(Cancle / Done) and connect them.
Time to code
All design is done - it's time to code. First we will do one easy thing - add a simple handling to the Cancel and Done buttons in our
options.
Open VCOptionsView.xib.cs and add the following code:
This enables us to "close the dialog". Or in iPhone Language - to show the options the view controller is pushed in the navigation
controller - and can be removed / closed (back to previous screen) via pop.
At the moment we don't care about Done / Cancel
differences.
Next we have to show something. We'll do it (for this time) the easy way. First we add our navigation controller (the default one) to
our window and then we open the "Options Dialog".
Open Main.cs and add the following code:
Now you can run the sample in the simulator and you should get the options (nasty looking) and if you click on Done or Cancel you will
see the three colored "inner views" on our main view.
There is a (disturbing) nhavigation bar on top of the main view. If you comment out the
push of the "Options Dialog" and run again you will see that there is no bar.
The reason is that we made this bar visible in the Options
View
So we have to find a way to reverse this effect. Open VCMainView.xib.cs and add the following code.
ViewDidLoad and ViewDidAppear
ViewDidLoad gets called when the view finished loading - this is done once in the program lifecycle. ViewDidAppear
is always called when the view comes into view.
So the ones of you who is carefully reading has found a first bug in our application.
We
set the Navigation Bar to visible in the ViewDidLoad of our Options View. And yes - this would mean that on a subsequent call the bar
will no longer be visible. So we also have to add a handler in our VCOptionsView.xib.cs to do this. The two handlers will then look like this:
Now we only have to add our "Sub Views" to the Main View elements and we have all done what we can do without doing "inheritance
stuff".
Let's do this - open VCMainView.xib.cs and add the following code.
If we run our application again we will see the three guys in there. Now let's add some data.
The Data
We will have some kind of POI. And we have 3 views for it. The (self made) problem - two of them are the same class - but should display different
data. There are several ways to manage this - one could be a controller one level above the views telling them what to display. The other could be the
views controllers itself knowing what they should display. This seems to be OK for the two classes Simple and Complex View.
And I'll use this
approach (I don't say it's the best solution) to show some possible techniques.
But let's first define the data - enter the following code to
OurData.cs:
Data Display in the views
As you may have noticed from the design of our Options View we plan to display the Data Labels either normal or in upper case. Who handles this? I
say the Main View acts with Options and so it will tell its sub views how they should display the data.
And since only the Main View knows when
the options did change our sub views must provide a method that "redraws" the data if something changes. To demonstrate "two way communications" I
not pass the "is upper case" information to the sub views. Instead they will "ask" their main view about it.
We are already prepared to extend our
sub views. Let's start with the general things - the class BaseInner.cs.
This class works for all three of the sub views. As I wrote before we need
a method to "Refresh" the display and we also need a back link to the main view. And we need a property for the data.
First change the code in
BaseInner.cs to:
Notice - the using MonoTouch.UIKit needed for UIViewController. We need to derive from that class since our "inner views" are some kind
of UIViewControllers. And also notice the abstract member RefreshDisplay. Next change the base class in VCInnerViewSimple complex
to be inherited from BaseInner. This includes the implementation of RefreshDisplay - but we won't do much at the
moment.
We don't need the Initialize function provided by the template. And we also have no need for the constructors with
parameters. Instead we want to ensure that our Main View is set when we are constructed. So we remove the Initialize as well as the first two
constructors. And we will change the third constructor to require a VCMainView as parameter. And we need the base constructor
UIViewController(string nibName, NSBundle bundle) in our base class. Since bundle will (in our case) always be null we don't pass this
parameter. Instead we create the following constructor in BaseInner.cs
And our VCInnerView class will finally look like this:
The only thing left is a change to the sub view construction code in VCMainView. Doing this we will also implement a method telling if the
data label should be upper case. And of course we need options (normally a class - here simple a Boolean) and data.
This would work fine so far. But how can the two instances of VCInnerViewSimple know what they should display? No problem - we
simply implement an integer (normally an enum would be better) to decide what they should display.
Change BaseInner.cs to:
The change to the constructor means that we have to change VCInnerView again. And we are now able to implement
RefreshDisplay.
Change VCInnerViewSimple to:
Notice I also added code to display initial data.
Now change the construction in VCMainView to:
At this point you should be able to run the project and see data in the two upper sub views.
The Complex View
The main difference between simple and complex inner views is the fact that the complex one has to do some "rearrange" operations when we
change the device orientation.
But except to this it is the same as a simple inner view. So we will first change our base class to be a kind of
BaseInner add the RearangeForOrientationChange and the constructor with the three parameters.
Our BaseComplexInner.cs
should look like this:
The implementation in VCInnerViewComplex.xib.cs should look like this:
NOTEI know that the MainView assignment could also be done in BaseInner. I left it just to do something
different.
Last not Least we have to change the construction in VCMainView.xib.cs:
Displaying Options with the button
At the moment we still push our option when the application starts. As you may have predicted by the interface design we want to do this from our
Settings button in the Main view. So let's first remove the code from Main.cs (I simply comment it out with //####):
If we now start our applications it will show the main view. To bring up our options we have to add code to our button click event (in
VCInnerViewComplex).
One important thing - memory is an expensive resource, especially on hand held devices. So we will not construct (load)
our options until we need it. Maybe we have (expect this as standard) application runs where options will never be opened.
Our first problem -
the option (I decided) will be handled by VCMainView - but the button is on VCInnerViewComplex. Let's do it the .NET way - fire an event, when the button
is clicked. For this I change VCInnerViewComplex to:
Further we already know that we want to be informed on "Done" and on "Cancel" on our "Options Dialog". And we need access to the
Boolean (Setting). Let's change VCOptionsView to:
Very straightforward I would say. One thing to notice - with more complex data we would create a temporary object copy the data to it and
the view would then change this object. When the user clicks Done we transfer the modified data back to the original. So the caller must not take care
about some kind of undo.
Now we have to handle the options in our Main View. Here are the changes to VCMainView.xib.cs:
If you run the application now it almost does what we expect it to do. But the Options look a bit bad because of the "Example" (normal or
upper case). Let's fix this:
Now we can run our application and it should do all we want it to do. There is only one little thing left. If you use "Hardware / Turn right"
from the iPhone-Simulator menu the device turns - but our UI does nothing!
Orientation changes
Enabling Orientation changes in an iPhone application is very easy. The system uses a mechanism which "asks" the ViewController if it should
change the view or not. That is the place where the class OurNavigationController enters the scene.
NOTEThe main reason to implement our own navigation controller is not part of this article. It's about handling our own animation
when switching between views. I'll handle this in another article.
We will implement our own Navigation Controller - and it is easy. One thing we have to do for MonoTouch is that we implement some
constructors and "register" the class so that it can be used in XIBs too. The final code for OurNaviController looks like this:
That is the functional class - we have (a last time) to enter the Interface Designer and make a change. Double Click MainWindow.xib and
change the Class of our Navigation Controller to OurNavi...
Figure 15: Assign our own Navigation Controller

Now run the project again and turn the device (simulator) to landscape. Our view
rotates!
You can also try this with the Options Dialog visible - it works! And the Options) look correct since it only covers about half of the
landscape screen height which fits also in landscape mode. The problem is our MainView.
Let me explain what has to be done:
Figure 16: View rearrangements

The light blue frame is our space for landscape mode. So we have to arrange two different things. First the lower
view (white rectangle) has to change position and size. Since this is a sub view of the Main View we will do this there. Next (the blue arrow) the content
of this view has to be rearranged. Since the Main View has no deeper knowledge about this the sub view will do this itself.
The movement of the
view needs some position and sizes. One for landscape and one for portrait mode; portrait mode is easy - it is the size and position we get at startup.
Landscape is easy to calculate. Position Y is the same as for the first sub view - position X is 2 times position X of the first sub view (I assume the
distance between the two sub views is the same as from window border to the first frame).
Size landscape width is half of portrait width and
height is double.
iPhone tells us when it changed orientation - we handle this in the main view. For the sub view (just to show a different
approach) we use a method (RearangeForOrientationChange) call.
NOTEA method call instead of directly handling the event can also be useful for complex animations.
So let's change VCMainView.xib.cs:
When we run after this code change a rotation will bring this result:
Figure 17: Landscape Main View

This screenshot shows why I use so many colors. It enables us to see, that the "holder" in the main view change its
size, that the sub view changed its position with the parent. Only rearrangement of the Complex View is left to have a good result!
So let's
change VCInnViewComplex.xib.cs:
Figure 18: Final Landscape

Animation
This article will not cover animation in depth. But to better visualize the orientation changes and further to give you some idea how easy UI Animation
can be we will add a last few lines of code to VCMainView.xib.cs:
Anything left?
Yes - (de-)serialization of Data / Options, Push / Pop Animation,... I'll probably write a second part of this article which will contains this stuff. I hope
you enjoyed my sample
References
About Manfred Pohler
 |
Sorry, no bio is available
This author has published 5 articles on DotNetSlackers. View other articles or the complete profile here.
|
Please login to rate or to leave a comment.