Xamarin iOS: Learning by Examples: Part#2


This is a continuation of a previous post.

Create iOS Project

  • Right click on the Solution name (DemoAppName) -> Add -> Add New Project… -> iOS -> App -> General -> Single View App -> Next.
  • Name: DemoAppiOS
  • Organization Identifier: com.wordpress.tausiq (Give your own reverse domain name)
  • For now we are going to work on only iPhone. So, deselect iPad.
  • Select Target carefully. Your application needs minimum of this version to work. I am using iOS 8.3
  • Allow some time to eventually get the status, “Packages successfully added.” on the status bar (Top middle)

iOS Framework Structure

References: This folder contains files from .NET framework. Also it contains Xamarin.iOS, that is the wrapper around iOS framework and provides us the access to use them through C#/.NET

Main.cs: This is the entry point of project. The equivalent

AppDelegate.cs: The equivalent class of AppDelegate.swift in iOS. This is the application instance of the app.

Main.storyboard: Same as iOS storyboard. Actually you can use this same file in Xcode and add/edit components as you like. Xamarin studio can also handle to edit this file.

ViewController.cs: Same as iOS. ViewControllers contains the functionality of the UI. Each ViewController in storyboard is usually associated with one ViewController file.

*.plist: There are two plist files. Entitlements.plist and Info.plist. Former one contains the permissions for the app and later one contains the configuration information of the app.

To run this application first we need to select this project and make it a startup project. Right click on DemoAppiOS -> Set As Startup Project.

Run

Create some UI

in iOS we can create view using different ways. Such as,

  • Storyboard
  • XIB
  • Code

We will use Storyboard in Xamarin.iOS for a number of advantages

  • Overview of the whole projects UI/flow
  • Segue
  • Auto Layout etc.

Task

  1. Open Main.storyboard. There should be a View Controller by default, if not, Search and drag a View Controller from “Toolbox” window.
  2. Drag other UI controls (ImageView, Label, Button) on the View Controller.

Screen Shot 2016-01-31 at 5.51.59 PM.png

Tips:

  1. Skin can be changes using VIEW AS dropdown
  2. Constraints can be set using Recommended constraint option.
  3. Give a name of each UI component. Select the component -> Properties window -> Identity section -> Name. UI binding is automatic. That is, you can reference each component from code by using it’s name. [This binding is actually happening in ViewController.designer.cs file. It is an auto generated file, do not make an attempt to edit this file.]
using System;

using UIKit;
using AudioToolbox;
using DemoAppName.Core;

namespace DemoAppiOS
{
	public partial class ViewController : UIViewController
	{

		public User aUser {
			get;
			set;
		}

		public ViewController (IntPtr handle) : base (handle)
		{
			UserDataService dataService = new UserDataService ();
			aUser = dataService.GetUserById (1);
		}

		public override void ViewDidLoad ()
		{
			base.ViewDidLoad ();
			// Perform any additional setup after loading the view, typically from a nib.

			BindUI ();
		}

		private void BindUI()
		{
			// Create a /Images folder under DemoApp.iOS
			ImgView.Image = UIImage.FromFile ("Images/" + aUser.ProfileImagePath);

			lblDescription.Text = aUser.Email;

			btnClickMe.TouchUpInside += (object sender, EventArgs e) => {
				new UIAlertView("Alert Title", "Alert Message", null, "OK", null).Show ();
			};
		}

		public override void DidReceiveMemoryWarning ()
		{
			base.DidReceiveMemoryWarning ();
			// Release any cached data, images, etc that aren't in use.
		}
	}
}

Display a Table View

I am assuming that audience know how to create a TableView in iOS. I will discuss only how to display a TableView using Xamarin.

    1. Drag a Table View Controller or Table View to the existing controller.
    2. Add a folder named /DataSources and create a class inside it, named: UserTableDataSource.cs
using System;
using UIKit;
using System.Collections.Generic;
using DemoAppName.Core;
using Foundation;

namespace DemoAppiOS
{
	public class UserTableDataSources : UITableViewSource
	{
		private List<User> users;
		NSString cellIdentifier = new NSString ("UserCell");

		public UserTableDataSources (List<User> users, UITableViewController tvController)
		{
			this.users = users;
		}

		public override nint RowsInSection (UITableView tableView, nint section)
		{
			return users.Count;
		}

		public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
		{
			UITableViewCell cell = tableView.DequeueReusableCell (cellIdentifier) as UITableViewCell;

			if (cell == null) {
				cell = new UITableViewCell (UITableViewCellStyle.Default, cellIdentifier);
			}

			var user = users [indexPath.Row];

			cell.TextLabel.Text = user.FirstName;
			cell.ImageView.Image = UIImage.FromFile ("Images" + user.ProfileImagePath);
			cell.DetailTextLabel = user.Email;

			return cell;
		}

	};
}

    1. Select the Table View Controller -> Properties window -> Identity section -> Enter Name: “UserTableViewController” and press Enter. Xamarin will create a class named “UserTableViewController”.
using Foundation;
using System;
using System.CodeDom.Compiler;
using UIKit;
using DemoAppName.Core;

namespace DemoAppiOS
{
	partial class UserTableViewController : UITableViewController
	{
		UserDataService uService = new UserDataService();

		public UserTableViewController (IntPtr handle) : base (handle)
		{
		}

		public override void ViewDidLoad()
		{
			var users = uService.GetAllUsers ();
			var dataSource = new UserTableDataSources (users, this);
			TableView.Source = dataSource;
		}
	}
}

Custom cell in Table View

Create a folder named: “ui” and create a class named: UserTableCell. This class will consists of Three label and one Image.

using System;
using UIKit;
using Foundation;
using ObjCRuntime;
using CoreText;
using CoreLocation;
using CoreTelephony;
using System.Drawing;
using CoreGraphics;
using CoreImage;

namespace DemoAppiOS
{
	public class UserTableCell: UITableViewCell
	{
		UILabel lblFirstName;
		UILabel lblLastName;
		UILabel lblEmail;
		UIImageView imgV;

		public UserTableCell ()
		{
		}

		public UserTableCell (NSString cellId) : base (UITableViewCellStyle.Default, cellId)
		{
			SelectionStyle = UITableViewCellSelectionStyle.Default;

			ContentView.BackgroundColor = UIColor.DarkGray;

			lblFirstName = new UILabel () {
				TextColor = UIColor.Black,
				BackgroundColor = UIColor.Clear
			};

			lblLastName = new UILabel () {
				TextColor = UIColor.Blue,
				BackgroundColor = UIColor.Clear
			};

			lblEmail = new UILabel () {
				Font = UIFont.FromName ("AmeraicanTypeWriter", 14f),
				TextAlignment = UITextAlignment.Center,
				BackgroundColor = UIColor.Clear
			};

			imgV = new UIImageView ();

			ContentView.Add (lblFirstName);
			ContentView.Add (lblLastName);
			ContentView.Add (lblEmail);
			ContentView.Add (imgV);
		}

		// set the layout (UI component's) position
		public override void LayoutSubViews ()
		{
			base.LayoutSubviews ();

			imgV.Frame = new RectangleF (8, 8, (float)ContentView.Bounds.Width - 8, 160);
			lblFirstName.Frame = new RectangleF (8, 168, (float)ContentView.Bounds.Width - 8, 48);
			lblLastName.Frame = new RectangleF (8, 224, (float)ContentView.Bounds.Width - 8, 48);
			lblEmail.Frame = new RectangleF (8, 280, (float)ContentView.Bounds.Width - 8, 48);
		}

		// set the values in UI
		public void SetValues (string firstName, string lastName, string email, UIImage img)
		{
			imgV.Image = img;
			lblFirstName.Text = firstName;
			lblLastName.Text = lastName;
			lblEmail.Text = email;
		}
	}
}

We also need to update UserTableDataSources.cs file and incorporate UserTableCell.

using System;
using UIKit;
using System.Collections.Generic;
using DemoAppName.Core;
using Foundation;

namespace DemoAppiOS
{
	public class UserTableDataSources : UITableViewSource
	{
		private List<User> users;

		// This string must match with identifier of each cell
		NSString cellIdentifier = new NSString ("UserCell");

		public UserTableDataSources (List<User> users, UITableViewController tvController)
		{
			this.users = users;
		}

		public override nint RowsInSection (UITableView tableView, nint section)
		{
			return users.Count;
		}

		public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
		{
			UserTableCell cell = tableView.DequeueReusableCell (cellIdentifier) as UserTableCell;

			if (cell == null) {
				cell = new UserTableCell (cellIdentifier);
			}

			cell.SetValues (
				users [indexPath.Row].FirstName,
				users [indexPath.Row].LastName,
				users [indexPath.Row].Email,
				UIImage.FromFile ("Images/" + users [indexPath.Row].ProfileImagePath) );

			return cell;
		}

	};
}

Do not forget to update cell identifier in storyboard of cell

Moving to next screen

Navigating to next screen usually follows 4 types of pattern.

  1. Single View (Stack)
  2. Tab
  3. Master details
  4. Navigation drawer

Task:

  1. Drag a “Navigation Controller” in storyboard.
  2. Delete “Root View Controller” comes with it.
  3. Drag the starting segue to point to the “Navigation Controller”
  4. Control + Drag from Navigation Controller to ViewController and create a segue. Relationship: Root
  5. Control + Drag from ViewController to UserTableViewController and create a segue. Manual Segue: Show
  6. Drag another View controller from Toolbox window and name it “UserDetailViewController”. Select the View controller -> Properties window -> Identity -> Class -> UserDetailsViewController -> Press enter. A class with named UserDetailViewController.cs will be created automatically.
  7. Control + Drag from UserTableViewController cell to UserDetailsViewController and create a segue. Manual Segue: Show. Select the segue -> Properties window -> Identifier -> Segue: UserDetailsSegue

Finally it should look like this,

Screen Shot 2016-02-01 at 7.33.18 PM.png

Update UserDetailsViewController.cs like below,

using Foundation;
using System;
using System.CodeDom.Compiler;
using UIKit;
using DemoAppName.Core;

namespace DemoAppiOS
{
	public partial class UserDetailViewController : UIViewController
	{
		public User SelectedItem;

		public UserDetailViewController (IntPtr handle) : base (handle)
		{
		}
	}
}

Update UserTableDataSources.cs and UserTableViewController.cs as following,

using System;
using UIKit;
using System.Collections.Generic;
using DemoAppName.Core;
using Foundation;

namespace DemoAppiOS
{
	public class UserTableDataSources : UITableViewSource
	{
		private List<User> users;

		// This string must match with identifier of each cell
		NSString cellIdentifier = new NSString ("UserCell");

		public UserTableDataSources (List<User> users, UITableViewController tvController)
		{
			this.users = users;
		}

		public override nint RowsInSection (UITableView tableView, nint section)
		{
			return users.Count;
		}

		public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
		{
			UserTableCell cell = tableView.DequeueReusableCell (cellIdentifier) as UserTableCell;

			if (cell == null) {
				cell = new UserTableCell (cellIdentifier);
			}

			cell.SetValues (
				users [indexPath.Row].FirstName,
				users [indexPath.Row].LastName,
				users [indexPath.Row].Email,
				UIImage.FromFile ("Images/" + users [indexPath.Row].ProfileImagePath));

			return cell;
		}

		public User GetItem(int index)
		{
			return users [index];
		}

	};
}

using Foundation;
using System;
using System.CodeDom.Compiler;
using UIKit;
using DemoAppName.Core;

namespace DemoAppiOS
{
	partial class UserTableViewController : UITableViewController
	{
		UserDataService uService = new UserDataService ();

		public UserTableViewController (IntPtr handle) : base (handle)
		{
		}

		public override void ViewDidLoad ()
		{
			var users = uService.GetAllUsers ();
			var dataSource = new UserTableDataSources (users, this);
			TableView.Source = dataSource;
		}

		public override void PrepareForSegue (UIStoryboardSegue segue, NSObject sender)
		{
			base.PrepareForSegue ();

			if (segue.Identifier == "UserDetailsSegue") {
				var userDetailsVC = segue.DestinationViewController as UserDetailViewController;

				if (userDetailsVC != null) {
					var data = TableView.Source as UserTableDataSources;
					var selectedRow = TableView.IndexPathForSelectedRow;
					var item = data.GetItem (selectedRow.Row);
					userDetailsVC.SelectedItem = item;
				}
			}
		}
	}
}

Display a Modal View Controller

If we would like to display the UserDetailViewController as a Modal view controller, then we need to do things manually. That is, We will call the UserDetailViewController to display and we need to write code to dismiss that as well to go back to previous screen.

Lets delete the PrepareForSegue method and write this method instead in UserTableViewController.cs

		public async void onSelectedTableRow(User selectedUser)
		{
			UserDetailViewController userDetailVC = this.Storyboard.InstantiateViewController ("UserDetailViewController")
			as UserDetailViewController;

			if (userDetailVC != null) {
				userDetailVC.ModalTransitionStyle = UIModalTransitionStyle.PartialCurl;
				userDetailVC.SelectedItem = selectedUser;

				await PresentViewControllerAsync (userDetailVC, true);
			}

		}

Now we need a call onSelectedTableRow method from UserDataSources.cs file


using System;
using UIKit;
using System.Collections.Generic;
using DemoAppName.Core;
using Foundation;

namespace DemoAppiOS
{
	public class UserTableDataSources : UITableViewSource
	{
		private List<User> users;

		// This string must match with identifier of each cell
		NSString cellIdentifier = new NSString ("UserCell");

		UserTableViewController userTVC;

		public UserTableDataSources (List<User> users, UITableViewController tvController)
		{
			this.users = users;
			userTVC = tvController;
		}

		public override nint RowsInSection (UITableView tableView, nint section)
		{
			return users.Count;
		}

		public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
		{
			UserTableCell cell = tableView.DequeueReusableCell (cellIdentifier) as UserTableCell;

			if (cell == null) {
				cell = new UserTableCell (cellIdentifier);
			}

			cell.SetValues (
				users [indexPath.Row].FirstName,
				users [indexPath.Row].LastName,
				users [indexPath.Row].Email,
				UIImage.FromFile ("Images/" + users [indexPath.Row].ProfileImagePath));

			return cell;
		}

		public User GetItem(int index)
		{
			return users [index];
		}

		public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
		{
			var selectedItem = users [indexPath.Row];
			userTVC.onSelectedTableRow (selectedItem);
			tableView.DeselectRow (indexPath, true);
		}

	};
}

From UserDetailViewController.cs we need to call the following method at some point. Preferably after clicking on a button,

this.DismissModalViewController(true);

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s