Quantcast
Channel: Kodeco | High quality programming tutorials: iOS, Android, Swift, Kotlin, Unity, and more
Viewing all 4373 articles
Browse latest View live

Android Tutorial for Beginners: Part 2

$
0
0

android_tutorial_title_image

Update note: This tutorial was updated for the latest version of Android Studio by Darryl Bayliss. Original tutorial by Matt Luedke.

This tutorial is the second of three parts. If you’re looking to start from scratch, Part One is the tutorial for you!

The first part of this series covered a lot of zoomed-out Android concepts, as well as the general structure of Android projects. In this part of the tutorial, you’ll learn more “on the ground” Android: how a layout file works and the specifics of Android layouts and views.

By the time you’re done with this section, you’ll have an app with:

  • An image from a PNG file;
  • An editable text field for writing messages;
  • A button to submit your input;
  • A text view that shows your most recent message;
  • A list that displays all your messages;
  • An option to share your message through Facebook, Twitter, SMS or email; and
  • A greeting that saves and retrieves your name each time you open the app.

You should already be at the point where you have a “Hello World” app running on your emulator or device. At the end of the last section, you changed the text so that it greets you personally, like mine:

personalized_hello_world

It’s great that you’ve come this far — but now it’s time to take it to the next level! There’s a lot to do, so let’s get to it!

Getting Started

Looking ahead, the first thing you should do is check that you’re making your app as simple as possible. You don’t want to introduce additional complexity unless it’s needed since extra complexity in how something is implemented means that it takes more time and requires more work if you were to later modify the bits with the extra complexity.

First, open app/src/main/res/layout/activity_my.xml. After opening the file, you may have to switch the editor to Text mode if you can’t see the raw XML. Click the appropriate tab at the bottom of the editor pane as shown below.

switch_to_text

Note: From now on, this tutorial will always interact with XML files in text mode.

The only thing you need to remove here are the padding attributes that Android Studio automatically generates in your .xml layout. They look something like this (your values may vary slightly):

android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"

Delete these lines from your layout, your activity_my.xml file should now look like this:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MyActivity">
 
    <TextView
        android:text="@string/hello_world"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
 
</RelativeLayout>

Now, double-click on MyActivity.java on the left pane of Android Studio to take a look at your first piece of Android code.

Pro Tip: A quick way to navigate to any file in your project using OSX is to type Shift + Command + O. This is the same shortcut used in Xcode!

my_activity_code

The only lines you need to remove here are the following:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();
    if (id == R.id.action_settings) {
        return true;
    }
    return super.onOptionsItemSelected(item);
}

Be careful not to remove the extra curly brace at the bottom that closes the end of your class.

Now that you’ve finished that bit of cleaning up, you’re all set to begin. It’s time to bring your Activity to life!

XML Layout Basics

Android layouts are in XML format, in the form of a tree with a single root and a hierarchy of views. The hierarchy is very strict and straightforward: each view in the tree is termed the parent of the views it contains and the child of the view that contains it.

Open app/src/main/res/layout/activity_my.xml. You can see the XML for your activity here. There is a parent RelativeLayout and a child TextView.

Look at the TextView specifically. You can see that it contains three attributes. Of those, two are present in every view you’ll ever put into your Android layouts: layout_width and layout_height. The values for these attributes can take several forms:

  • wrap_content: This constant value specifies that the view will be just large enough to fit whatever is inside it, whether that’s an image, text or child view.
  • match_parent: This constant sets the view to be as big as its parent.
  • Explicit values: You could set the dimension to a specific number of pixels (Ex: 5px), but it is usually wiser to use density independent pixels (Ex: 5dp). A dp is a pixel on a “medium-density” (mdpi) device, and the number of actual pixels automatically scales for devices designated as low-density (ldpi), high-density (hdpi), extra-high-density (xhdpi), etc.

In other words, using straight-up pixels would result in your views being all sorts of crazy sizes, depending on whether a device has 160 pixels per inch or 300 pixels per inch, or what have you. Who knows! Let the Android system take care of the scaling and just use dp.

Note: Designations like mdpi and hdpi are only general categories. Actual pixel densities are even more variable, but they are all given the same scaling factor regardless. So dp scaling, while convenient, is not an exact science.

iOS Developers should be familiar with a similar practice of density independence, using “points” instead of pixels in their layouts to account for early iPhone screens not having Retina displays.

The final attribute of the TextView is simply text, in which you specify the text to be displayed. This attribute is a good example of how different views respond to different attributes. Adding a text attribute to a RelativeLayout or a Space wouldn’t accomplish anything because, unlike the TextView, they wouldn’t know what to do with it.

But the value of the attribute, @string/hello_world, isn’t what’s displaying, is it? What you specify in your layout file is not the actual string to be displayed but rather a string resource ID identifying the actual text. That way, all your app’s copy can be in one place – res/values/strings.xml.

You can command click on the resource ID to be brought directly to the definition in your resource file.

Now let’s look at the parent node in the XML: RelativeLayout. What’s going on there?

Relative Layouts

The layouts in iOS apps used to be in purely absolute terms, like: “Place View X at pixels (x,y)”, but now iOS developers have AutoLayout. Android developers have always needed to keep device screen sizes in mind. Layout files are very well-suited for this consideration.

The default project Studio created for you sets you up with a useful layout: a RelativeLayout. It is currently the parent layout and the TextView element is its child.

A RelativeLayout is an intuitive and powerful thing. It holds a bunch of child views and positions them in relation to each other. Here are three examples of what you can easily do with a RelativeLayout:

Example 1: Use layout_alignParentBottom and the similar attributes for top, left and right to line up a view’s edge with the corresponding edge of the RelativeLayout, which may or may not also be the edge of the screen.

layout_alignParentBottom

Example 2: You can use layout_toRightOf and the analogous attributes for left, above and below to position one View relative to another.

layout_toRightOf

Example 3: You can use layout_alignRight and the analogous attributes to align a side of one View with another.

layout_alignRight

You can see how that could be useful! For now, though, let’s move on to the layout type you’ll be using for this tutorial.

Linear Layouts

A LinearLayout needs to have an orientation specified, either horizontal or vertical. Then it lines up its children in that orientation, in the order in which they are specified in your XML.

The children of LinearLayouts don’t respond to attributes like layout_toRightOf, but they do respond to two other attributes: layout_weight and layout_gravity.

Specifying a layout_weight expands the view to a proportion of its parent so that the parent weight is the sum of all child view weights.

The layout_weight of View X
———————————————————————-
The sum of all weights of View X and its siblings

Confused? Perhaps the following image might help explain it better.

layout_weight

Notice how the full height of the parent view is split up between the child views based on the layout weight assigned to each child view.

Assigning a layout_gravity to a view sets its horizontal and vertical positions within its parent LinearLayout. For example, a view might have a layout_gravity attribute with a value like left, right, and center_vertical. The previous values can also be combined, like this: top|center_horizontal.

Then, there’s gravity, not to be confused with layout_gravity. While layout_gravity is about where you place the view itself, the gravity attribute defines how you place the content of a view within itself. If you want your text to be left or center justified, use gravity.

The following example shows how layout_gravity and gravity work in a vertical LinearLayout. Note that the top three have a layout_width of wrap_content while for the bottom three it’s set to match_parent:

layout_gravity

One handy trick, which you’ll see in just a bit, is that you can nest layouts inside each other. Cue the theme music from Inception!

You don’t want your layouts to be as multi-layered as a Christopher Nolan movie, though. So, if you start to see your nested LinearLayouts scheme getting out of hand, consider switching to a RelativeLayout.

Note: There are also performance reasons for considering a simple layout design. Layout weights require the layout inflater — the class which turns the XML into real Views — to do a tiny calculation to put the view together.

Now, modern devices won’t have any trouble rendering your basic layouts. But if you were to make an indefinitely long ListView — for instance, one full of cells making copious use of nested LinearLayouts and layout_weights — all those extra tiny calculations could add up.

Before you move on to the next section, open res/layout/activity_my.xml and change the root node from a RelativeLayout — what Android Studio gave you as a default — to a LinearLayout.

To do that, you should replace these lines:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MyActivity">

And this one at the very end of the file:

</RelativeLayout>

With This:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MyActivity">

And this:

</LinearLayout>

Accessing Views From Within Java

Layouts are primarily the domain of your XML. But there are plenty of visual elements you will want to create, destroy, change, and trigger from within your Java code!

So first, edit the TextView in activity_my.xml to match the following:

<TextView
    android:id="@+id/main_textview"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="20dp"
    android:layout_marginTop="20dp"
    android:text="@string/hello_world"/>

Notice the addition of the id attribute. Using this tag (or attribute, if you prefer) allows you to access that specific View from within your code, so you can thereafter manipulate the View via code.

There’s also a change to make in the text tag. The name of the string resource hello_world is a bit outdated now, don’t you think? Right-click on the @string/hello_world part of the line and then choose Refactor > Rename.

refactor

Then, type in textview and click Refactor.

rename_textview

This not only changes the name of the resource ID in your layout file, it also changes the original resource ID in your strings.xml file. It also renames the resource ID wherever else it might be used in the project. This is a useful trick to remember when renaming something that appears all over your project!

Now open MyActivity.java and add the following line above the onCreate method but below the MyActivity class declaration:

TextView mainTextView;

Android Studio will throw an error at you when you leave your cursor on this line that will look like this:
import warning

The TextView class hasn’t been imported into MainActivity.java yet so it doesn’t know what a TextView is. Android Studio can quickly fix that for you. Just tap Alt-Enter on your keyboard while this error popup is present to automatically import TextView.

Note: It can get tiresome very quickly having to manually import every single component of an Android App you want to use in a class. Fortunately you can automate this in the IDE settings window, which you can access by going to Android Studio > Preferences (for Mac) or File > Settings (for Windows & Linux) and then clicking Editor > Auto Import and ticking Optimize imports on the fly and Add unambiguous imports on the fly.

Next, add the following code to onCreate after the two existing lines of code:

// 1. Access the TextView defined in layout XML
// and then set its text
mainTextView = (TextView) findViewById(R.id.main_textview);
mainTextView.setText("Set in Java!");

Your MyActivity.java file should now look like this:

public class MyActivity extends Activity {
 
    TextView mainTextView;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
 
        // 1. Access the TextView defined in layout XML
        // and then set its text
        mainTextView = (TextView) findViewById(R.id.main_textview);
        mainTextView.setText("Set in Java!");
    }
 
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
 
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.my, menu);
        return true;
    }
}

Finally, run your app and look at the results!

dynamic_text

The text set via Java code now appears on screen. What are the steps you just took to make that happen?

  1. You added an id attribute to the View in XML.
  2. You used the id to access the View via your code.
  3. You called a method on the View to change its text value.

Note: You added the code to access and set the text of your TextView in the onCreate method of your Activity, meaning that the app runs all the code in that block right away when it first creates the Activity. Activities have strict lifecycles they must follow and it’s thanks to this that you can write code that runs at specific points of an Activities life. You can read more about the Activity lifecycle here.

Buttons and Listeners

It’s time to build on your TextView and get more interactive! Next up is a Button.

Add a Button to activity_my.xml, directly after your TextView:

<!-- Set OnClickListener to trigger results when pressed -->
<Button
    android:id="@+id/main_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="20dp"
    android:layout_marginLeft="20dp"
    android:text="@string/button" />

Notice there’s an XML comment above the Button, a reminder of how to trigger results.

The layout_margin attributes simply add 20 density-independent pixels of space above and to the left of the Button to keep your layout from looking cramped. Remember that the value of 20 will be scaled by the screen density of the device to get an actual pixel value.

You’ve probably noticed @string/button under the button text property appears in red. If you hover over it, you’ll see that the symbol cannot be resolved – and that’s because you haven’t yet defined it.

Open strings.xml and add the following line to the bottom to resolve this:

<string name="button">Update The TextView</string>

Next, open MyActivity.java and add the following right below the previous line you added to include a TextView variable:

Button mainButton;

Now add the following code to the end of onCreate, after the code you added earlier:

// 2. Access the Button defined in layout XML
// and listen for it here
mainButton = (Button) findViewById(R.id.main_button);
mainButton.setOnClickListener(this);

Again, you see the same three steps as when you added code to access the TextView:

  1. You add an id to the View in XML. Or, in this case, you add a view with an id attribute.
  2. You access that View in code by using the id.
  3. You call methods on that View.

This time, the method you called on the Button is setOnClickListener. What you put in the parentheses of that method becomes the answer to this question: Which Object is going to respond when this Button gets pressed?

To answer that question with simply the word this seems a little curt and unspecific, but Java knows that it means MyActivity itself is your intended listener.

This means that MyActivity has to implement the View.OnClickListener interface. If that sentence doesn’t make much sense to you, I suggest finding an intro on what an interface is and how to create one, like this one.

Note: An interface is like part of a job description. If I’m a young, ambitious Java object, and I want to be able to put a particular certification on my resume, there are a few methods I have to be comfortable and capable of performing. The interface is like the checklist I need to pass, or “implement.”

If have an iOS/Objective-C background, an interface is comparable to a protocol. In fact, in object oriented programming the words protocol and interface are used interchangeably.

Android Studio is smart and can help you do the implementation. Simply single-click on this, which is underlined in red, indicating an issue (in this case the fact that MainActivity currently does not support the necessary interface). Then, when a red light bulb appears at the beginning of the line, click on it and select Make ‘MyActivity’ implement ‘android.view.View.OnClickListener’.

implement_onclick

Simply click OK on the next dialog, which lets you know which method(s) Studio will automatically create for you.

auto_generate_method

Studio then generates the code necessary to make your MyActivity qualify as a union-certified OnClickListener.

First, it added a bit to the class declaration indicating that the Activity implements a specific interface:

public class MyActivity extends Activity implements View.OnClickListener

Second, Studio added a stub for a method you need to implement in order to get your OnClickListener license (other interfaces may require more than one method to be implemented): onClick. This method fires when your Button gets pressed.

@Override
public void onClick(View v) {
 
}

The method currently does nothing. So add the following code to onClick to make it do something:

// Test the Button
mainTextView.setText("Button pressed!");

Can you tell from the code what should happen? Run your app and see if you’re right…

button_pressed

The app now changes the text in the TextView when you press the Button. Cool! You’ll be putting this Button to even better use later — to submit input.

Adding a Visual and Nested Layouts

It’s always fun to include images in your UI. So how about adding an ImageView to show a little icon? Along the way, you’ll also get to see how a nested LinearLayout works.

First off, what image will you show? Well, it’s easiest to start with the image you’re given by default. It’s already in your project and here’s where to find it.

Use the Project Navigator to expand the src/main/res directory:

ic_launcher

You can see several directories within res, including values, which you dealt with when you edited strings.xml. Notice that several are named drawable, plus some letters that look like screen density abbreviations.

Indeed, those abbreviations correspond to the pixel density buckets used to classify Android devices in dots per inch (dpi):

  • mdpi: medium
  • hdpi: high
  • xhdpi: extra high
  • xxhdpi: extra extra high

You can even create your own xxxhdpi folder where you can place images for devices with even higher pixel densities. Personally, I think they should use Roman numerals for the next level up and call it xlhdpi, but on second thought that would probably be a terribly confusing way to go…

Look inside the drawable directories. You’ll see a file named ic_launcher.png. This is simply the default launch image you’re given, at several different sizes for different screens. The Android system will pick the right one for the device.

Now head back to activity_my.xml and replace the following section:

<!-- Set OnClickListener to trigger results when pressed -->
<Button
    android:id="@+id/main_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="20dp"
    android:layout_marginLeft="20dp"
    android:text="@string/button" />

With this:

<!-- This nested layout contains views of its own -->
<LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal">
    <!-- Set OnClickListener to trigger results when pressed -->
    <Button
        android:id="@+id/main_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_marginLeft="20dp"
        android:text="@string/button" />
    <!-- Shows an image from your drawable resources -->
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_marginLeft="20dp"
        android:src="@drawable/ic_launcher" />
    <!-- Closing tag for the horizontal nested layout -->	
</LinearLayout>

You just added a new LinearLayout inside the existing root LinearLayout layout, directly underneath the TextView as its new sibling. You also moved the existing Button into the nested layout and added a new ImageView, as well.

By wrapping your Button in a second, horizontal LinearLayout, you are able to place a Button and an ImageView side-by-side horizontally, even as the root layout has a vertical orientation.

As for the ImageView itself, the important attribute is src, to which you give your drawable image resource. Note the format you use to reference the drawable image. You need to prefix the file name of your image (minus the file type) with @drawable/.

Run the app, and you’ll see the new image right beside the button!

nested_layout_imageview

Involving the Keyboard

Now it’s time to get some user input… by introducing an EditText. This is a special subclass of TextView that opens the keyboard and displays what the user types as its content.

Add the EditText XML to activity_my.xml as a sibling to the TextView and the horizontal LinearLayout. Be careful not to get it caught inside the nested layout! Instead, add it right after the closing for the embedded linear layout and just before the closing for the root linear layout.

<!-- Displays keyboard when touched -->
<EditText
    android:id="@+id/main_edittext"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="20dp"
    android:layout_marginLeft="20dp"
    android:hint="@string/hint" />

Notice the special attribute, hint. You’re using this text as a placeholder in the input field. The app will overwrite it once the user starts typing.

As usual, you need to define the string resource for your hint in res/values/strings.xml:

<string name="hint">A Name</string>

Now open MyActivity.java and add a new variable for the EditText (below the other two existing variables):

EditText mainEditText;

Next, add the following code to the end of onCreate:

// 3. Access the EditText defined in layout XML
mainEditText = (EditText) findViewById(R.id.main_edittext);

The above code, similar to the previous code, simply gets a reference to the EditText control and saves it in the assigned variable.

Now that you have a reference to the EditText control, you need to do something with user input. Replace the current contents of onClick with the following:

	// Take what was typed into the EditText
	// and use in TextView
	mainTextView.setText(mainEditText.getText().toString()
			+ " is learning Android development!");

When mainButton is clicked, the mainTextView will now be set to display a string including the contents of mainEditText concatenated with ” is learning Android Development!”.

Run your app, and test this out!

edittext_update_textview

Now you receive user input with an EditText, submit it with a Button, and display it in a TextView. Very nice! But how about visualizing more than one piece of data at a time?

The ListView

The ListView is a useful control that visualizes a list of items. It’s analogous to a UITableView in iOS.

You define a ListView just as you would any other view in your XML. Add one to activity_my.xml as a sibling to the TextView, the horizontal LinearLayout, and the EditText by adding the following lines after the lines for the EditText control:

<!-- List whose dataset is defined in code with an adapter -->
<ListView
    android:id="@+id/main_listview"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    android:layout_marginTop="20dp" />

Wait… what? How in the world could setting layout_height to 0dp be a good idea? After all, no matter what screen you’re on, 0 is always going to scale to 0.

Well, take a look at what directly follows: a layout_weight. Since you haven’t given anything else in your layout a weight yet, the ListView is going to expand to fill as much space as possible, no matter what value you give the layout_height.

The general practice, then, is to use a value of 0 so the layout inflater has one fewer dimension to think about and can get the job done a bit quicker.

Now open MyActivity.java and, add the following variables below the ones you’ve already added above the onCreate method:

ListView mainListView;
ArrayAdapter mArrayAdapter;
ArrayList mNameList = new ArrayList();

The one for the ListView makes sense. But what about the others? The others are for supplying the ListView with data to display. All will be explained in a bit :]

But first, add the following code to the end of onCreate:

// 4. Access the ListView
mainListView = (ListView) findViewById(R.id.main_listview);
 
// Create an ArrayAdapter for the ListView
mArrayAdapter = new ArrayAdapter(this,
		android.R.layout.simple_list_item_1,
		mNameList);
 
// Set the ListView to use the ArrayAdapter
mainListView.setAdapter(mArrayAdapter);

Some of that looks familiar by now: finding the ListView using its id. But what else is going on?

mArrayAdapter is an example of an adapter, which is basically a go-between so your ListView can get the data it needs.

Note: I think of a ListView as being a picky sort, as far as Objects go. It’s great at what it does but doesn’t want to get its hands dirty with any real data. It’s all got to be prepared for it or else it’ll throw a fit.

The Adapter, then, is the enterprising Object that is able to code-switch between the rough language of the datasource and the refined dialect of the ListView.

When you create mArrayAdapter, you have to specify the Context, the target XML view for the data (simple_list_item_1), and the datasource (mNameList).

But hang on, you didn’t write anything with an id of simple_list_item_1! So where is that coming from? Also what exactly is a Context?

Notice the android.R.layout part before simple_list_item_1. There are several important concepts here, but let’s look at the R bit first. R (or, R.java, if you prefer) is a dynamically created class which gives you access to the resources in your project. If interested, you can read more about accessing resources via the R class, here.

As the linked article above explains, you can use the R class to get a resource ID by specifying a resource type and a resource name. The resource type would be something like string, drawable, or layout – matching the various resource types you see in your project. And thus, the layout part in android.R.layout.simple_list_item_1 simply specifies that you are referring to a layout resource.

But what about the android prefix? Why is it there? It is an indicator that you didn’t create the view; it’s already part of the Android platform. It represents a simple TextView that a default list cell can use.

The Context is an object that represents the current state of your App. Do you need to access a specific service for your App to use? Context is your guy. Do you need your App to show a specific View or Activity? Context is the mastermind behind it.

In this instance, Context is used to create the Views used within your ListView. Remember that layout resource you are referring to? This is the layout the context takes and converts into a view, the adapter then populates each view with a value from its datasource.

The datasource in this case is mNameList, which is simply a list of Strings. It’s initialized, but empty. So the next step is to add some data that the ListView can display.

Add the following code to the end of onClick:

// Also add that value to the list shown in the ListView
mNameList.add(mainEditText.getText().toString());
mArrayAdapter.notifyDataSetChanged();

You simply add whatever the user typed into the EditText to the list of names and then shoot a signal to the adapter to update what’s shown in the ListView.

Now run your app.

name_list

You should be able to type a name into the EditText, then see the name used in the TextView and added to a new row in the ListView when you press the Button. Cool!

Detecting List Selections

Looking at the items in a list is cool, but this is an app, and interactivity is even better! So next you’re going to set up a way to detect user selections from the list.

First, modify the class definition in MyActivity.java to add support for another interface. To do this, modify this line:

public class MyActivity extends Activity implements View.OnClickListener {

To look like this:

public class MyActivity extends Activity implements View.OnClickListener, AdapterView.OnItemClickListener {

All you’ve really done is add support for a new interface – AdapterView.OnItemClickListener, which, as the name suggests, listens for item selections from a ListView. MainActivity is really stacking up the credentials!

You should also have a red line highlighting the line where you’ve just added your new interface, Android Studio is letting you know that you haven’t actually implemented the interface yet. You can fix this easily by clicking the highlighted line, pressing Alt-Enter,clicking Implement Methods and finally clicking Ok.

activity_implement_methods

Next, add the following code to the end of onCreate:

// 5. Set this activity to react to list items being pressed
mainListView.setOnItemClickListener(this);

The above code sets MyActivity as the listener for any item clicks on mainListView.

Now replace the onItemClick Method that was automatically generated for you with the following:

@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
 
    // Log the item's position and contents
    // to the console in Debug
    Log.d("omg android", position + ": " + mNameList.get(position));
}

You may need to use your old friend Alt-Enter to import and use Log.

Well done! Your MyActivityonItemClick and can live up to the title of being a card-carrying OnItemClickListener.

activity_business_card

But what’s happening inside onItemClick? There’s a weird Log.d in there, and then something with a get(position)

Take a look at what you’re passing along to onItemClick. In particular, look at int position, which is an integer equal to the index of the item the user pressed on the list (counting up from 0).

You take that position, as well as the item at that index in your list of names, and log them. Logging is a very basic, but very useful debugging technique.

Run your app, enter a few values and add them to the list, just as before. Then select an item. There’s no visible effect for the moment.

With the app still running, look at the bottom section of Android Studio:

log output

The bottom left section of the window contains information about your Device or Emulator, what processes are running and logs of what is currently happening within those processes. These logs appear in a console called logcat. It will read off tons of stuff from your emulator or device, the majority of which is not of much interest to you at this point. The log statements you generated with your selections are here, but there’s too much noise to see them.

Here are some useful ways to filter out the noise and see only what you want:

Notice the option for Log level in a dropdown at the top of the console. When you put your Log command into code, it specifically was the Log.d command. The d is for “debug” level. The levels are:

  • v: Verbose
  • d: Debug
  • i: Info
  • w: Warning
  • e: Error

When you select a log level for logcat, it will only show messages at that level or higher. And the levels start at verbose and go up to error, in the same order as listed above. So, if you select the log level as Warning, then you’ll see all warnings and errors but nothing else.

Meanwhile, you can use the text box to the right of the log level drop-down to apply a filter and show only those messages that contain the text you typed in.

Now that you know this, set the log level to Debug and type omg android into the filter text box.

logging_item_clicks_arrow

Great! You now have a clean feed of log statements and you can detect when a certain item gets selected in your list. The ability to log will come in handy as you create more complicated apps and want to stay informed of the inner workings of your code.

The Action Bar

Your app has several different views now, and it’s time to think about other ways to add functionality. Older Android devices used to have a Menu device button that would display a bunch of options depending on the situation, but since Honeycomb in early 2011, Android has used the Action Bar to display any options for the current view.

action_bar

The Action Bar provides a familiar base for your users. Since it’s present across apps, making good use of the Action Bar means a significant part of your app’s functionality will be immediately intuitive to an Android user. Conversely, neglecting the Action Bar would confuse all your users who expect it to work — and that would be weird!

Note: Since you are only targeting Ice Cream Sandwich and later for this tutorial, you don’t have to worry about bending over backwards to get the Action Bar into your app. If for some reason your project needs to be compatible with really old versions of Android, you’ve got two good options: a compatibility library from Google and a well-regarded third-party solution called Action Bar Sherlock.

The Action Bar is already in your app — it just has no options attached to it yet. That will be your first order of business next!

Sharing

Soon you’ll have a chance to show off the fact that you’re learning Android, from within your own app! You’ll do this using an intersection of the Android concept of the Intent and the Action Bar, known as a ShareActionProvider.

One of the advantages of an Intent is that you can construct it in either a specific (explicit) or generic (implicit) manner. You used an example of the explicit type when you specifically defined the Intent that launches your app, which the manifest then identifies as MainActivity. Now you’ll see an example of the implicit type.

A generic Intention really helps in this case. After all, some of us prefer to share things with the entire world, and others with just a few friends. Rather than wondering what a potential user’s favorite social network might be and integrating them one by one, you can politely tell the Android device that you’d very much like to share a bit of content (thus expressing an Intent), and Android will graciously take it from there!

Navigate to src/main/res/menu/my.xml and open it.

menu_main

You’ll note that there’s some auto-generated XML in there, but you don’t need it. Replace the whole thing with this:

<!-- Defines the menu item that will appear on the Action Bar in MainActivity -->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Share item -->
    <item
        android:id="@+id/menu_item_share"
        android:showAsAction="ifRoom"
        android:title="Share"
        android:actionProviderClass= "android.widget.ShareActionProvider" />
</menu>

The ShareActionProvider is built-in (hence the android.widget prefix) and ready-to-use. So, when a user selects your menu item, the necessary functionality is already in place for you to make use of.

Now head over to MyActivity.java and add the following variable underneath the rest of your variables:

ShareActionProvider mShareActionProvider;
Note: Studio may be confused about which ShareActionProvider you mean, so if it asks, use android.widget.ShareActionProvider. If you have enabled auto imports then double check it has imported the right one.

Next, you need to add the following two methods to the class – the first one, onCreateOptionsMenu, might already be implemented in the class. If it is, simply replace the existing implementation with the new one.

@Override
public boolean onCreateOptionsMenu(Menu menu) {
 
    // Inflate the menu.
    // Adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.my, menu);
 
    // Access the Share Item defined in menu XML
    MenuItem shareItem = menu.findItem(R.id.menu_item_share);
 
    // Access the object responsible for
    // putting together the sharing submenu
    if (shareItem != null) {
        mShareActionProvider = (ShareActionProvider) shareItem.getActionProvider();
    }
 
    // Create an Intent to share your content
    setShareIntent();
 
    return true;
}
 
private void setShareIntent() {
 
    if (mShareActionProvider != null) {
 
        // create an Intent with the contents of the TextView
        Intent shareIntent = new Intent(Intent.ACTION_SEND);
        shareIntent.setType("text/plain");
        shareIntent.putExtra(Intent.EXTRA_SUBJECT, "Android Development");
        shareIntent.putExtra(Intent.EXTRA_TEXT, mainTextView.getText());
 
        // Make sure the provider knows
        // it should work with that Intent
        mShareActionProvider.setShareIntent(shareIntent);
    }
}
Important: If there’s an implementation of onOptionsItemSelected in the class, remove it.

Add your imports so they are recognised by your Activity.

onCreateOptionsMenu gets called once, when the activity first starts. Similar to how you specified which layout XML file you wanted to use for the activity in onCreate, you now direct the menu inflater to look at my.xml for the menu items that go on the Action Bar.

From there, you can access the menu item you defined in XML by its id, menu_item_share, and then you can access its action provider. Previously, you specified that this item’s action provider was a ShareActionProvider. So, you can safely cast to that type in your code and hang onto a reference to it via the mShareActionProvider variable.

Then, you call setShareIntent. This method creates an Intent, but not just any Intent. It creates an Intent whose action you’ve set to ACTION_SEND. It’s truly as generic as it looks: you’re going to tell Android you want to take the action of sending something.

From there, you set the Intent‘s content type, subject — used by email programs and the like, as the subject header of the message —, and text. The text matches whatever is currently in your TextView. After you’ve packed up everything the Intent needs to know, you pair it with mShareActionProvider.

This code will work, but only “kind of.” As-is, you only call setShareIntent once, at the creation of the menu. It would be much better to update the Intent whenever the TextView changes — otherwise you’re stuck with the initial message forever!

Add the following code to the end of onClick:

// 6. The text you'd like to share has changed,
// and you need to update
setShareIntent();

Here, you simply make sure that the share intent is always up-to-date.

Run the app, and try out the new sharing feature – tapping the share icon on the Action Bar should reveal a number of choices, depending on what is installed on your emulator or device.

share

The ShareActionProvider automatically puts together an array of possible avenues for sharing content based on the apps you have installed on a given device. This array of options will differ from device to device. The emulator will most likely have far fewer options for sharing, whereas you may have apps like Twitter and Facebook on an actual device and could share through those networks, too.

Remembering Your Name

Everything you’ve done so far with regard to user input only persists while the app is running. But what about between sessions? Let’s see some data persistence in action, with a new feature that will record and remember your name each time you open the app.

There are a few good options on Android to persist data, and the simplest one is SharedPreferences.

SharedPreferences stores data in key-value pairs, meaning that you specify a name (the key) for a piece of data (the value) when you save it, and you can retrieve it later by using the original key.

Let’s see how it works in action, shall we?

First, add the following constants and variable to MainActivity.java (to the same place as the previous variables):

private static final String PREFS = "prefs";
private static final String PREF_NAME = "name";
SharedPreferences mSharedPreferences;

The above sets PREF and PREF_NAME at the top of the class. You’ll use PREF as a filename to keep your SharedPreferences in a single location. You’ll use PREF_NAME as the key for storing your name in shared preferences.

Note: It’s a good practice to use variables for Strings that you’ll need/refer to multiple times. That way, if you need to change the string value later, you can do it in one place. You’ll also never have to worry about some weird spelling error creating bugs in your code.

The final line adds a variable named mSharedPreferences for storing a reference to the shared preferences class. You only need to access it in a few places, but it will be useful to hang onto it. Add the import into your Class if you haven’t already.

Next, add the following lines to the end of onCreate:

// 7. Greet the user, or ask for their name if new
displayWelcome();

The new code calls a new method, displayWelcome. So implement that by adding the following method at the end of the class:

public void displayWelcome() {
 
    // Access the device's key-value storage
    mSharedPreferences = getSharedPreferences(PREFS, MODE_PRIVATE);
 
    // Read the user's name,
    // or an empty string if nothing found
    String name = mSharedPreferences.getString(PREF_NAME, "");
 
    if (name.length() > 0) {
 
        // If the name is valid, display a Toast welcoming them
        Toast.makeText(this, "Welcome back, " + name + "!", Toast.LENGTH_LONG).show();
    }
}
Note: While you could conceivably place all the code from displayWelcome directly into onCreate and it would still work, many people, your humble author included, prefer to keep method lengths reasonable by calling separate methods for a specific task.

This also means that if the same task needs to be performed from elsewhere in code later on, you already have a handy method in place for it :]

In the new method, the first thing you do is access SharedPreferences, with MODE_PRIVATE meaning that only your OMGAndroid app can access the data stored here. This means that your saved data will not get overwritten by another application which might have used the same key as you.

Then you simply ask the preferences object for whatever value is stored using the key PREF_NAME. The second parameter for the method can be used to set a default value to be returned in case there is no value stored using the key you provide. So, you use an empty String as the default value here.

Finally, you check to see if the retrieved String actually has any content, and display a message if so. Your message takes the form of a Toast, which is a short-lived pop-up message that appears for a bit and then fades away. Give the Toast a message it should display, specify one of its built-in lengths to remain on the screen and then simply tell it to show. Easy!

Displaying the Name Dialog

What you’ve set up so far will show your name if the application can retrieve it out of the preferences. But obviously, that’s no use to you yet since you have no mechanism in place to save your name in the first place!

You’ll use a Dialog to achieve that. Dialogs are small windows that alert the user. They may contain ways for the user to provide input or make choices. You’re going to use an AlertDialog, specifically.

Add the following code to the end of displayWelcome, creating an else branch for the if condition that’s already there:

} else {
 
    // otherwise, show a dialog to ask for their name
    AlertDialog.Builder alert = new AlertDialog.Builder(this);
    alert.setTitle("Hello!");
    alert.setMessage("What is your name?");
 
    // Create EditText for entry
    final EditText input = new EditText(this);
    alert.setView(input);
 
    // Make an "OK" button to save the name
    alert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
 
        public void onClick(DialogInterface dialog, int whichButton) {
 
            // Grab the EditText's input
            String inputName = input.getText().toString();
 
            // Put it into memory (don't forget to commit!)
            SharedPreferences.Editor e = mSharedPreferences.edit();
            e.putString(PREF_NAME, inputName);
            e.commit();
 
            // Welcome the new user
            Toast.makeText(getApplicationContext(), "Welcome, " + inputName + "!", Toast.LENGTH_LONG).show();
        }
    });
 
    // Make a "Cancel" button 
    // that simply dismisses the alert
    alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
 
        public void onClick(DialogInterface dialog, int whichButton) {}
    });
 
    alert.show();
}

The app will reach this else condition when there is no valid name saved using the PREF_NAME key. You use an AlertDialog.Builder to give your AlertDialog a title, a message, and an EditText in the center for the user to type in their name.

Then, you add two buttons to the AlertDialog: a positive and a negative button. The first thing you define for each is the text displayed on the button – “OK” and “Cancel” are pretty standard choices. The second thing you define for each button is an OnClickListener.

This time your OnClickListeners are specifically DialogInterface.OnClickListeners, and you are defining them right away. Notice how the parameters for onClick are slightly different.

For the positive button’s listener, onClick does quite a bit. First, it reads the name that the user typed into the dialog’s EditText.

It then saves that name into SharedPreferences using a helper called a SharedPreferences.Editor. You simply tell the editor what to save and where, tell it to commit the changes, and that’s it!

Finally, it displays a Toast identical to the other welcoming one.

The negative button’s listener is far simpler: it does nothing! Nothing!

Run your app and check out your Dialog.

dialog

Type in your name, press OK and see the Toast greeting. From now on, your app will remember your name and greet you each time you launch it!

Where to Go From Here?

You covered a lot of UI concepts in this part of the tutorial! Take a few minutes to play around with your app a little, maybe sharing something with a friend!

You can get the full source code for this part of the tutorial on GitHub or as a .zip.

Those looking for a challenge should try:

  • Setting up the EditText to expect names as its input so that it capitalizes first letters.
  • Dismissing the keyboard associated with the EditText.
  • Making the Done button on the keyboard do the same thing as the Update TextView button.
  • Modifying font sizes on the various views.

Hopefully you have found this helpful! If you have any comments or questions, feel free to leave them below. And of course, don’t miss Part Three, in which you set up your app to interact with data online!

The Android robot is reproduced or modified from work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License.

Android Tutorial for Beginners: Part 2 is a post from: Ray Wenderlich

The post Android Tutorial for Beginners: Part 2 appeared first on Ray Wenderlich.


Learn To Code iOS Apps With Swift Tutorial 4: Your First App

$
0
0
Learn to Code iOS Apps in this Swift Tutorial - for complete beginners to programming!

Learn to Code iOS Apps in this Swift Tutorial – for complete beginners to programming!

Update note: This tutorial was updated for iOS 8 and Swift by Ry Bristow. Original post by Mike Jaoudi.

Congratulations, you made it to part 2 of our Learn to Code iOS Apps with Swift tutorial series!

In the first part of the series, you learned the basics of programming with Swift. You learned about variables, if/else statements, loops, optionals, and more.

In the second part of the series, you put your new Swift skills to the test by making a simple number guessing game.

In the third part of the series, you created a simple command-line app to record the names and ages of people.

In this fourth part of the series, it’s finally time to make your first iPhone App!

In this tutorial, you will learn how to make a simple game called Tap Me! The objective of the app will be to click a button as many times as you can in 30 seconds. Let’s get started.

Getting Started

First, open up Xcode and select Create a new Xcode project.

1-Welcome_To_Xcode_New_Project

Then, select iOS > Application > Single View Application and hit Next.

2-Select_Single_View_Application

Fill out the project options as follows:

  • Product Name: Tap Me
  • Organization Name: This field can be left blank. Or you can enter your company name.
  • Organization Identifier: Enter com.yourname, such as com.rybristow
  • Language: Swift
  • Devices: iPhone

Screen Shot 2014-07-14 at 3.27.26 PM


Storyboards

You’ll notice that this project starts out with different files than your Command Line Tool App. The first one that you are going to want to look at is called Main.storyboard.

02-Select_Storyboard

When you click on this file, it will open up a Graphical User Interface (GUI) that shows what the iPhone screen will look like when it runs the app.

03-Blank_View_Controller

Note: You may wonder why the screen looks like a square, instead of the iPhone size. In iOS development, your goal is to design your apps so that they can adapt to different screen sizes – this is called Adaptive Layout.

Basically, you lay out your user interface elements with rules like “stay this far away from the top” or “stay centered”; this way it can work on different screen sizes, from iPhone 5 to iPhone 6 Plus and so on.

Right now, the app is just plain white and boring. To make some changes to this, you have to make sure that the Utilities Menu is open. Make sure it is selected and open.

04-Utilities_Menu

Next, select the Attributes Inspector. This allows you to view and edit different attributes that have to do with the View Controller. Make sure you click on the view first to be able to view the attributes.

05-Attributes_Inspector

Let’s make it a little more interesting to look at. Change the background color to green so it’s not plain white.

06-Change_Background_Color

Now, in the menu up top choose the iPhone 6 Simulator and run the app so you can take a look at what it will look like. It may take a while to build and run, so be patient. When it finally runs, you should see a window pop up that simulates the screen of an iPhone running this app.

07-First_Run


Adding Labels to the Screen

A green screen is great and all, but your app is pretty much useless if it doesn’t have anything on the screen for the user to interact with. To add objects, you have to go to the Object Library in the Utilities Menu. The first object you should add is called a Label. Labels are just text that can be set by the program. Scroll through the Object Library until you find the Label.

08-Object_Library

Drag it onto the screen and line it up at the top center.

09-Label

You can use the Attributes Inspector to change the title of the Label which changes what the Label says. Change the title to “Time: 30″.

10-Change_The_Title

At this point your label might look truncated on screen. To fix this, click your label and choose Editor\Size to Fit (Command =).

Now, run the app and see what it looks like.

12-Not_Centered

According to your storyboard, the Label looks as though it should be centered, right? The problem is that not every iOS device has the same dimensions, so it can get messed up sometimes. What you need to do is add a constraint to the label to get it to stay in the center of the screen.

To do this, first click on the Label. Then, select the Align button at the bottom of the screen and select Horizontal Center in Container and then Add 1 Constraint.

13-Adding_Horizontal_Constraints

That determines where the label should be placed horizontally. But you need to specify where it is vertically as well. To do this, click the Pin button at the bottom of the screen and click the red bar to pin it to the top, based on its current distance and click Add 1 Constraint:

TopConstraint

Now, make another Label to be placed at the bottom of the screen to keep track of the score. The only two differences are that you are going to want the font size to be 40, the lines to be 2, and you want it 130 points tall.

14-15-Changing_Font_Size

You need to set up some constraints for this as well. See if you can figure it out on your own, but if you get stuck check the solution below!

Solution Inside SelectShow>

Build and run, and now your labels should be nicely centered:

CenteredLabels

Add a Button

To add the last object that you need to the screen, use the Object Library to add a button to the screen and position it at the very center.

16-17

Don’t forget to add constraints to the button, this time for both horizontal and vertical centers.

18-Centering_Constraints

Now, use the Attributes inspector to change the title to “Tap Me!” and the Background to a white color.

19-20

In order to get the button to actually show off more of its white background, you need to stretch it out more and then set different kinds of constraints on it. This time, you have to click on the button that says Pin and then click on the boxes that say Width and Height.

23-Constrain_Button_Size

After you do this, your button might be small with an orange border around it, representing that its current size does not match its constraints. To fix this, click the third button at the bottom and choose Update Frames:

UpdateFrames

Your button should be a nice big tappable area. Build and run, and your app should look like the following:

TappableButton

You can tap the button, but nothing happens yet. You’ll fix that next!

Linking Views to Code

Now that you have laid out your views in the Storyboard, you need to hook them up to code.

Each “screen” of an iOS app is controlled by a class called a View Controller. Xcode has already created one of these for you in the template – ViewController.swift.

You need to add two properties to this class for the labels in your game, so you can update them programmatically. To do this, add the following new properties in the ViewController class in ViewController.swift:

@IBOutlet var scoreLabel: UILabel!
@IBOutlet var timerLabel: UILabel!

These two lines create two UILabel properties. In a moment, you will hook up the views you created in the Storyboard to these properties. By marking these properties with the special @IBOutlet keyboard, the storyboard editor becomes aware of them.

Note: You might notice that you mark the labels with an exclamation point at the end. This makes the labels “implicitly unwrapped optionals”. This makes the compiler automatically unwrap the optionals before you access them, so you can treat them as-if they weren’t optional variables (even though they are).

It’s OK to do that in this case because you’re sure you’ll be setting these to the views you created in your storyboard so they won’t be nil.

Similarly, you’d like a method to be called when the button is pressed. So add the following method to the ViewController class:

@IBAction func buttonPressed()  {
  NSLog("Button Pressed")
}

Just like with @IBOutlet, @IBAction makes the storyboard editor aware of this method, so you can link the button tap event to it.

Let’s try hooking up these properties and method! Open up Main.storyboard, and check out the Document Outline. What you need to do to connect the labels is CTRL-drag from View Controller in the Document Outline to the Time label up top.

HookingUpTimeLabel

A small black box will pop up listing the Outlets. Select the one called timerLabel. This is the UILabel object that you created in the ViewController.swift file.

Screen Shot 2014-07-15 at 10.04.17 PM

Now repeat this for the score label, but hook it up to scoreLabel instead.

To connect the button to the method buttonPressed(), you have to go in the opposite direction. CTRL-drag from the button in the Storyboard to View Controller in the Document Outline.

HookingUpButton

Then, select buttonPressed().

Screen Shot 2014-07-16 at 8.33.03 PM

Now, try running the app and pressing the button a few times. Due to the NSLog statement that you used in the buttonPressed() method, the console at the bottom of the screen that you have used in previous projects should have an output every time you click the button.

24-Run_App_With_Buttons

Manipulating Labels

The real purpose of the button will not be to output a statement to the console, but to change what the core label says to add a point to it each time it is pressed. First, you should learn how to manipulate the text in a label.

Good news – it’s easy! Replace the body of buttonPressed() with the following code.

scoreLabel.text = "Pressed"

Now, when you run your app and press the button, the text at the bottom of the screen should change to say “Pressed”.

In order to use this skill to make scoreLabel keep track of the score, you will need to do a bit more work.

Keep Track of the Score

To keep track of the score, you first need to create a variable to do so. Up near the top of the class body, underneath the two @IBOutlet declarations, create a property called count to keep track of the score.

var count = 0

The next step is to insert a line of code to increment count into the top of buttonPressed():

count++

This line is the shortcut for saying:

count = count + 1

Now, change the line that sets the text of scoreLabel to:

scoreLabel.text = "Score \n\(count)"

Run your app and test it out again. When you click the button, the label at the bottom of the screen will keep track of your score. Your app is getting closer and closer to becoming a playable game!

Working with the Timer

To get the timer to work correctly for this app, you will need to create two new properties. One of these variables should be an int that keeps track of the seconds and a NSTimer variable to count down.

Add these declarations right underneath the declaration for count.

var seconds = 0
var timer = NSTimer()

In order to use these variables, you will need to create two methods. The first will be called setupGame(). Write the following method at the bottom of your class body.

func setupGame()  {
  seconds = 30
  count = 0
 
  timerLabel.text = "Time: \(seconds)"
  scoreLabel.text = "Score: \(count)"
}

This code sets everything back to its original values when the game begins and updates the labels. If you didn’t include this code, then the seconds would continue decreasing past 0 and the score would just keep adding on for the next game and never reset.

The next step for the setupGame() method is to start the timer. To do this, you use the method scheduledTimerWithTimeInterval() to get the timer to do what you want. Include this code in the setupGame() method.

timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("subtractTime"), userInfo: nil, repeats: true)

Now, make sure that you call the setupGame() method in the viewDidLoad() method by adding the line:

setupGame()

The next method that you need to write should be called subtractTime(). You will notice that you used this phrase in the selector spot when you worked with the timer in setupGame. This is what it is referring to. This method will be used to decrement the seconds variable, update the timeLabel, and cause an alert to pop up when the timer reaches 0.

Let’s start out with the things you already know how to do: decrement seconds, update timerLabel, and create an if statement for when seconds reaches 0.

func subtractTime() {
  seconds--
  timerLabel.text = "Time: \(seconds)"
 
  if(seconds == 0)  {
 
  }
}

The first thing you will want to add to the body of the if statement is something to stop the timer from counting past 0. Use the following line of code to do so:

timer.invalidate()

Build and run your game, and now the time should count down!

Countdown

Red Alert!

Next, you’ll learn how to work with alerts. When you finish coding your alert, it should look like this:

Screen Shot 2014-07-17 at 7.04.25 PM

Let’s get started. The first thing you are going to do is create a UIAlertController. Insert this line after the invalidate() statement inside the if statement.

let alert = UIAlertController(title: "Time is up!",
 message: "You scored \(count) points",
 preferredStyle: UIAlertControllerStyle.Alert)

This line determines what the alert will look like. It specifies the title, message, and style of the alert.

Next, you will need to adjust what the button says and does. To do this, you use addAction(). Type this line after the declaration of alert.

alert.addAction(UIAlertAction(title: "Play Again", style: UIAlertActionStyle.Default, handler: {
  action in self.setupGame()
}))

If you examine this line closely, you see that it uses title to determine what the button says, and you will see that handler makes the button call the setupGame() method when the button is pressed and the alert is dismissed.

Time for the last line of code! This line is what actually tells the app to present the UIAlertController. It uses presentViewController(). Type this line after alert.addAction(...).

presentViewController(alert, animated: true, completion:nil)

Now run the app and enjoy your very first iPhone app. Can you beat my score? :]

TopScore

Where to Go From Here?

The final project with full source code can be found here.

You’re now ready to move on to the next tutorial in this series, where you’ll learn how to clean up the look of your app and make it more visually appealing.

If you have any question or comments, come join the discussion on this series in the forums!

Learn To Code iOS Apps With Swift Tutorial 4: Your First App is a post from: Ray Wenderlich

The post Learn To Code iOS Apps With Swift Tutorial 4: Your First App appeared first on Ray Wenderlich.

Learn To Code iOS Apps With Swift Tutorial 5: Making it Beautiful

$
0
0
Learn to Code iOS Apps in this Swift Tutorial - for complete beginners to programming!

Learn to Code iOS Apps in this Swift Tutorial – for complete beginners to programming!

Update note: This tutorial was updated for iOS 8 and Swift by Ry Bristow. Original post by Mike Jaoudi.

Congratulations, you made it to part 2 of our Learn to Code iOS Apps with Swift tutorial series!

In the first part of the series, you learned the basics of programming with Swift. You learned about variables, if/else statements, loops, optionals, and more.

In the second part of the series, you put your new Swift skills to the test by making a simple number guessing game.

In the third part of the series, you created a simple command-line app to record the names and ages of people.

In the fourth part of the series, you created your first simple iPhone app.

In this fifth and final part of the series, how about you take the game from last time and make it look a little more visually appealing?

This part of the series will teach you how to use images for the background of different objects on the screen to spice it up a bit. Also, you will learn how to implement background music and sound effects. Let’s get started!

Getting Started

You’ll start from the project where you left it off last time, so download a copy if you don’t have it already.

I recommend that you make a copy of the project folder before you begin. This way, you will still have your original version of the app before you change what the interface looks like. This is not only good because you can compare your two finished versions, but it also allows you to have the original, working version if you mess up something while trying to change the interface.

Screen Shot 2014-07-28 at 3.56.23 PM

Then, you’ll want to download the Tap Me Resources, which is basically a collection of the images and sounds that you will need for this project. After you download it, you should open up the folder and select all of the items inside. Drag these items over to the Supporting Files folder in your Document Outline.

0Screen Shot 2014-07-28 at 8.24.45 PM

Then, make sure Copy items if needed is selected so that the project will still work on another computer or if you move/delete the resources file from your downloads folder.

1Screen Shot 2014-07-18 at 11.51.19 AM

Setting A Button’s Background Image

Rather than just having your button’s background be a solid color, it looks a lot better when you create an image in a photo editing program. To set the background image, you have to do it based on what state the button is in. Make sure the state of the button is set to default in the Attributes Inspector. This is the state of the button when nothing is happening to it.

1Screen Shot 2014-07-18 at 12.09.50 PM

Then, find the box that mentions the Background, and set it to button_tap_deselected.png

1Screen Shot 2014-07-18 at 12.46.40 PM

In your previous version of the app, you had the background of the button set to be white. Change it back to the default of no color so it doesn’t show behind the image.

1Screen Shot 2014-07-18 at 12.46.49 PM

1Screen Shot 2014-07-18 at 12.47.27 PM

Uh oh! The image is hard to read since the button title is still “Tap Me!”. Since the words are a part of the image, you can go ahead and get rid of the title.

1Screen Shot 2014-07-18 at 12.49.09 PM

1Screen Shot 2014-07-18 at 12.49.25 PM

Much better! Now, the button is legible and looks a whole lot better than it did in your previous version of the app. Rather than a boring white rectangle with some text in it, you now have a 3-dimensional-looking button with colors and better looking text.

Your next step is to set the background image for the same button when it is in the highlighted state. This is the state of the button when it is being clicked by the user. It should have the Background field set to button_tap_selected.png.

1Screen Shot 2014-07-18 at 12.53.47 PM

You may notice your button looks a little bit squished at this point. This is because you have hardcoded width and height constraints that are smaller than the image itself.

Luckily, there is an easy way to fix this. All views have something called an intrinsic content size, which you can think of as an automatic constraint that is set to the size of the element being presented. In the case of an image, it will be the size of the image itself.

So rather than having hardcoded width and height constraints, you can rely on the intrinsic content size of the image to size the image view appropriately.

Let’s try this out. In the document navigator, find the width and height constraints for the button and hit delete to remove them:

DeleteConstraints

Then, update all the frames in your view controller to match their constraints by clicking on the third icon in the bottom right and selecting All Views\Update Frames.

UpdateAllFrames

Build and run, and enjoy your big button!

BigButton

Adding Images to the Screen

Sometimes, you may want to add images to the screen that do not act as buttons. In this case, you can use a UIImageView. Find Image View in the Object Library and drag one onto the screen.

You will be using this Image View object to create a border, so you will want to position and resize it so that it stretches from the left side of the screen to the right side of the screen and touches the top part of the screen. Do the same with another Image View object for the bottom border.

Screen Shot 2014-07-18 at 1.35.13 PM

Note you should move your labels a bit to make room. The easiest way to do this is to just update their constraints using the Edit button in the size inspector:

UpdateConstraint

Using what you learned in the previous section of this tutorial, you should be able to figure out how to set the image of each object and resize it to the size you need. The only difference is the field for setting the image is called Image instead of Background. Set Image to checker_top.png for the top Image View and checker_bottom.png for the bottom Image View. The size for each should be Width as 480 and Height as 22.

Screen Shot 2014-07-18 at 1.36.46 PM

Screen Shot 2014-07-18 at 1.37.05 PM

Screen Shot 2014-07-18 at 1.44.00 PM

Screen Shot 2014-07-18 at 1.47.24 PM














You also need to set up some constraints for these image views. Try to do this on your own, but if you get stuck check the spoiler below! Hint: You will actually want to specify the width and height of the image so it stretches appropriately, not rely on the image size.

Solution Inside SelectShow>

Run the app again and enjoy your beautiful borders!

Borders

Programmatically Setting the Background Color

You don’t always have to go through Storyboard to change the way your app looks. Let’s change the background color to purple to test this out. Try adding this line inside of viewDidLoad() in ViewController.swift:

view.backgroundColor = UIColor.purpleColor()

What this line of code does is it takes the backgroundColor attribute of the view and sets it to the UIColor object returned by the method purpleColor().

Now run the app and check out what it looks like.

Screen Shot 2014-07-18 at 2.01.48 PM

Although this shows proof of concept, it still doesn’t look that great. However, lucky for you, you can also set the background of the view to an image. Let’s do this in the program again. Replace the line where you set the background color to purple with the following:

view.backgroundColor = UIColor(patternImage: UIImage(named: "bg_tile.png")!)

This line tiles the image to fill the background of the view. Run the app to see what it looks like.

Screen Shot 2014-07-18 at 2.02.48 PM

While you’re at it, go ahead and programmatically set the background of both labels as well. To do this, type in the following two lines of code.

scoreLabel.backgroundColor = UIColor(patternImage: UIImage(named: "field_score.png")!)
timerLabel.backgroundColor = UIColor(patternImage: UIImage(named: "field_time.png")!)

Run the app and see what it looks like now.

Screen Shot 2014-07-18 at 2.03.48 PM


Positioning and Sizing Labels

There are a couple things that could use some improvement here. One thing is that the positioning and sizes of the labels are off. scoreLabel is obviously too high up and its size and shape do not fit its image.

To fix this, select the top label and use the Pin button to add constraints to set its width to 133 and height to 46:

TimeConstraints

Then select the bottom label, find its current height constraint in the Project Navigator, and hit delete to remove it:

RemoveScoreCosntraint

Then use the Pin button to add constraints to set its width to 146 and height to 102:

ScoreConstraints

Finally, clear your selection, click the third button, and choose All Views in View Controller\Update Frames to apply the constraints.

One final tweak. The background that you are using for the labels makes it nearly impossible to read black text on. To solve this, change the text color to a light blue that goes well with the rest of the interface. Use these values for the best results. Make sure you do this for both timeLabel and scoreLabel.

Screen Shot 2014-07-18 at 4.53.26 PM

Also set the alignment of each label to center justified.

Now, run the app to see how much easier it is to read the newly colored text!

Screen Shot 2014-08-10 at 11.29.56 AM

Adding Sound

Music and sound effects are great ways to add character to your app. That’s the last thing this app is missing!

But first, you’ll need some sounds. Download these sounds, unzip the file, and the three sound files into your project.

The three sound files are: background music, a beep for every time the player taps the button, and a beep for every second that passes on the countdown clock, just to make the player sweat a little!

The sound playback will be handled from the view controller code, so open up ViewController.swift. Near the top of your file, you’ll notice this line

import UIKit

You will also need to use an import statement for the AVFoundation framework, which is the Apple framework responsible for playing sound and video. Add the following statement immediately following the previous import statement mentioned.

import AVFoundation

Just as importing UIKit lets you use things like UIButton and UILabel, importing AVFoundation lets you use the very useful AVAudioPlayer class. Next, you’ll need an instance variable for each of the three sounds. Add a line for each instance variable just after the declaration of other instance variables inside the class body.

var buttonBeep = AVAudioPlayer()
var secondBeep = AVAudioPlayer()
var backgroundMusic = AVAudioPlayer()

Next, you will need to add this helper method above the viewDidLoad method.

func setupAudioPlayerWithFile(file:NSString, type:NSString) -> AVAudioPlayer  {
  //1
  var path = NSBundle.mainBundle().pathForResource(file, ofType:type)
  var url = NSURL.fileURLWithPath(path!)
 
  //2
  var error: NSError?
 
  //3
  var audioPlayer:AVAudioPlayer?
  audioPlayer = AVAudioPlayer(contentsOfURL: url, error: &error)
 
  //4
  return audioPlayer!
}

This method will return an AVAudioPlayer object (declared by the ->), and takes two arguments: a file name and type. Let’s look at what it does by section.

  1. You need to know the full path to the sound file, and NSBundle.mainBundle() will tell you where in the project to look. AVAudioPlayer needs to know the path in the form of a URL, so the full path is converted to URL format.
  2. A NSError object will store an error message if something goes wrong when setting up the AVAudioPlayer. Usually nothing will go wrong, but it’s always good practice to check for errors, just in case!
  3. This is the important call to set up AVAudioPlayer. You’re passing in the URL, and the error will get filled in if something goes wrong
  4. If all goes well, the AVAudioPlayer object will be returned!

Now that you have a handy method that will set up AVAudioPlayer objects, it’s time to use it! Add this code to the viewDidLoad() method, at the top of the method before setupGame():

buttonBeep = self.setupAudioPlayerWithFile("ButtonTap", type:"wav")
secondBeep = self.setupAudioPlayerWithFile("SecondBeep", type:"wav")
backgroundMusic = self.setupAudioPlayerWithFile("HallOfTheMountainKing", type:"mp3")

At this point in viewDidLoad, you’ll have all three sounds ready to be called in your code!

The first sound, buttonBeep, should play when the button is pressed. Update the buttonPressed method to play the sound by adding this line at the end of its method body:

buttonBeep.play()

There are two other sounds to add. The secondBeep sound should be played every second when the timer ticks down. Add the call to play that sound in subtractTime() method by adding this line right before the if statement.

secondBeep.play()

You’re almost done!

The final step is to add the background music. To play the music every time a new game is started, add the play code to the setupGame() method. Add these lines to the very bottom of the method body:

backgroundMusic.volume = 0.3
backgroundMusic.play()

You can adjust the volume of the background music so the beeps can still be heard. Changing the volume attribute of the backgroundMusic is a good way to do this. It can be set from 0 (off) to 1.0 (full volume), but 0.3 is a good starting point.

Now run the app for a final time and experience the glory of your fully featured app!

FinalGame

Where To Go From Here?

Congratulations! You have just made your first iPhone app, and taken it from having very basic functionality, to being a polished looking and sounding app. Here is a copy of the finished project for you to compare to your finished project.

There are lots of things that you can modify in your app, such as adding or changing some of the graphics, adding different levels, or even modifying the sound effects – the sky is the limit!

From here, you might want to go through the iOS Apprentice series, which goes much more into depth about making iOS apps. It’s also for complete beginners, and you can get the first part for free by signing up for our newsletter.

In the meantime, if you have any questions or comments, please join the forum discussion below!

Learn To Code iOS Apps With Swift Tutorial 5: Making it Beautiful is a post from: Ray Wenderlich

The post Learn To Code iOS Apps With Swift Tutorial 5: Making it Beautiful appeared first on Ray Wenderlich.

Video Tutorial: Introduction to CloudKit Part 10: User Accounts

Unity Cheat Sheet and Reference

$
0
0

Unity Cheat Sheet and Quick Reference Now Available!

Unity Cheat Sheet and Quick Reference Now Available!

Often times when working on Unity projects, I find myself asking the same questions over and over.

What is the difference between Awake() and Start()?

What’s the process of kicking off a coroutine again?

What are some of the static Vector3 variables available to me?

These simple questions can be answered a quick Google search, but the process of answering them can break the flow of a work session. Once a flow has been broken, it can be a non-trivial affair to get back into it.

So I put together a Cheat Sheet to keep the flow alive and you can download it right here:

The sheet summarizes some of the more common used features in Unity using C#. If you are using Boo or UnityScript, well, you’re out of luck but if there’s enough demand I’m happy to put one together for you.

Here’s what the sheet covers:

  • Creating and destroying GameObjects
  • Accessing Components
  • GameObject events
  • Vector Variables
  • Timing Variables
  • Physics Events
  • Coroutine Example and Return Types

This is just a first draft of the Unity cheat sheet. If you have any suggestions or comments on how to improve it, please let me know in the comments.

Looking to learn Unity? Feel free to check out our Unity video tutorial series or read many of our written Unity tutorials as well.

Unity Cheat Sheet and Reference is a post from: Ray Wenderlich

The post Unity Cheat Sheet and Reference appeared first on Ray Wenderlich.

Preparing for the Apple Watch: a Prototype Heartbeat App

$
0
0

Start prototyping your Apple Watch apps!

With the initial release of the WatchKit SDK expected later this month, it’s not too early to start thinking about apps you might want to work on.

How can you add a small-screen component to your own apps? And what kinds of new apps can you think of that really take advantage of the watch and all its features?

We know we won’t get WatchKit until later this month, and won’t see any actual hardware until 2015, but what if you want to start preparing in advance?

Good news: to help us get started early, I have written a Watch Simulator template app that can run on iPhone and iPad! You can use the familiar interface builder tools, storyboards, and Swift code to build a prototype Apple Watch app.

In this tutorial, you’ll use the watch simulator to get a feel for what the hardware will be like. The actual WatchKit SDK might be quite different, but this at least helps you prototype some ideas, allowing you to get a good head start.

Getting Started

Apple Watch has a few nifty features that you’ll want to keep in mind as you dream up apps for it. It has a flexible display that can distinguish the difference between a light tap and a hard press, known as Force Touch. This might be useful in a space-based shooter game; taps could shoot the machine-gun, and a hard press fire a missile.

Apple Watch also has a scroll wheel on the side that is known as the Digital Crown. By spinning it, you can collect one-dimensional movement information to zoom maps and images, or scroll through a list.

The vibration motor is extremely precise. You’ll be able to feel different parts of the backface face vibrate, so for example if you are using it for GPS directions, you could feel the difference between a right turn and a left turn vibration.

Finally, Apple Watch has a sensor that uses infrared, visible-light LEDs and photodiodes to detect your heart rate. In the prototype app for this tutorial, you’re going to build something that makes use of the heart rate sensor.

Watch Simulator

You’ll use the Watch Simulator template project to provide a nice place to start. Download the Watch Simulator project, unzip the file, and open WatchSimulator.xcodeproj in Xcode.

Open Main.storyboard by selecting it in the File Navigator.

Screen Shot 2014-11-03 at 2.34.12 PM

You’ll see a simulated watch in the Main View Controller Scene and an embedded App View Controller Scene that will hold the user interface for your app. As you set up the UI, remember to add all the views to the App View Controller scene!

First, you’ll set up the label that will show the user’s heart rate. Drag a Label from the object library to the App View Controller.

Screen_Shot_2014-11-03_at_2_36_23_PM

With the label selected, open the Attributes Inspector and change the label’s text to 80 (the text, not the font size).

Then open the font popup and change Font to Custom, and then the rest of the settings to Helvetica Neue, Light, and a size of 32.

Screen Shot 2014-11-03 at 2.39.31 PM

Next, update Color to white, with 60% opacity.

Screen_Shot_2014-11-03_at_2_41_08_PM

Click the Pin button in the lower right, which looks like a TIE fighter, for the Star Wars fans. Uncheck Constrain To Margins. Set the right constraint to 4 and the bottom constraint to -1 – that is indeed negative. Change Update Frames to Items of New Constraints.

Finally, click Add 2 Constraints to add the constraints and update the layout.

Screen_Shot_2014-11-03_at_2_44_37_PM

You should see the label move to the bottom-right of the view.

Since the user’s heart rate will change, you’ll need to connect an outlet so you can change the value from code.

Open the Assistant Editor and make sure AppViewController.swift is open in the editor; if it’s not then select it from the jump bar at the top. Control-drag from the label to a spot inside the class definition.

Main_storyboard_—_Edited

Name your outlet bpmLabel and hit Connect.

Screen_Shot_2014-11-03_at_2_50_44_PM

Good work! Now you have your completed view.

Screen_Shot_2014-11-03_at_2_50_58_PM

Creating the Model

Now that view is set up, the next step is to model the data. You’ll need some place to store the heartbeat data – for this app, that’ll consist of an icon, some descriptive text, and the actual beats per minute value.

In the Project Navigator, right-click the WatchSimulator group and select New File…. Select the iOS\Source\Swift File template and click Next.

Screen Shot 2014-11-03 at 2.58.12 PM

Name the file BeatPattern.swift and click Create.

Make sure BeatPattern.swift is open and replace the contents with the following:

import UIKit
 
struct BeatPattern {
  var icon = ""
  var description = "Mid-range"
  var bpm = 80
}

This sets up the previously-mentioned properties for the struct. You’ll be using emoji rather than an image for the icon, so that’s a String rather than an instance of UIImage.

Next, add the following computed property to the bottom of the struct:

var duration: Double {
  return 60.0 / Double(bpm)
}

This property tells you how much time each heartbeat takes. For the math nerds amongst you, here’s how this is derived:

60 seconds/minute ÷ X beats/minute = Y seconds/beat

That’s all you’ll need for the model! Since you’ve provided default values for the properties, you don’t need to explicitly define an initializer. Swift will automatically create an initializer with the init(icon:description:bpm:) signature, which you’ll use to provide your own values.

Displaying Heartbeats

The app will show different icons depending on the user’s heart rate, so you’ll need to define those ranges next.

Open AppViewController.swift and add the following array property to the class:

beatPatterns

There’s some emoji there for the icons, and web browsers can have trouble displaying those characters. Instead, here’s a text file with the code snippet above. You can download it, open it in a text editor, copy it, and paste it directly into AppViewController.swift.

Next, add the following two properties to the class:

var currentBeatPattern = BeatPattern()
var currentBeatPatternIndex = 0

This will store the current beat pattern using the struct you just defined, as well as which beat pattern to use by storing the necessary index of the beatPatterns array.

You’ll need a label to show the emoji icon. Rather than use the storyboard, you can set that up in code. Add another property to the class:

let iconLabel = UILabel()

Now that you have the label, you’ll need to set it up and add it to the view. Add the following line to the end of viewDidLoad:

self.view.insertSubview(iconLabel, atIndex: 1)

This will add the icon label to the view. To set up the label, add the following method next:

override func viewWillAppear(animated: Bool) {
  super.viewWillAppear(animated)
 
  iconLabel.frame = self.view.bounds
  iconLabel.textAlignment = .Center
  iconLabel.font = UIFont.boldSystemFontOfSize(132)
}

This will set the icon label to be as big as the entire watch screen. Since the display will be a single emoji character, you’ll need a large font size of 132 points to get this to look just right.

You might wonder why this code can’t go in viewDidLoad. Although the view is loaded at that point, the subviews are not quite in place yet. By waiting just a bit longer for the call to viewWillAppear(animated:), you make sure that you cad add the new views and place them correctly.

Since this is just a simulation, you’ll cycle through the available beat patterns in sequence. Add the following method to the class to handle the cycling:

func newBeat() {
  // 1
  if ++currentBeatPatternIndex == beatPatterns.count {
    currentBeatPatternIndex = 0
  }
 
  // 2
  currentBeatPattern = beatPatterns[currentBeatPatternIndex]
 
  // 3
  bpmLabel.text = "\(currentBeatPattern.bpm)"
  iconLabel.text = currentBeatPattern.icon
}

Here’s what’s going on in this method, step-by-step:

  1. First, you increment the current index to switch to the next beat pattern. If you’ve reached the end and the new index is out of the range of the array, reset the index to zero to start from the beginning.
  2. With the new index, you set currentBeatPattern using the array.
  3. Set the values for the two labels in the view. Notice that you’re using Swift’s string interpolation to convert the Int into a String.

For now, you’ll simulate a changing heartbeat by adding a timer to call newBeat() periodically. Add the following method to the class:

override func viewDidAppear(animated: Bool) {
  super.viewDidAppear(animated)
 
  newBeat()
 
  NSTimer.scheduledTimerWithTimeInterval(8,
    target: self,
    selector: Selector("newBeat"),
    userInfo: nil,
    repeats: true)
}

This will call newBeat() once to kick things off, and then set up a timer to call newBeat() every eight seconds beyond that to update the view.

Now, build and run. Every eight seconds, you will see the color of the heart change and the beats-per-minute number update.

Screen Shot 2014-11-03 at 4.15.37 PM

Animating the Heartbeat

The final thing is to do something interesting in those eight seconds, namely a little animation to make the heartbeat look realistic. Remember, the watch will have sensors to detect the actual heart rate, so showing live data like this is a nice touch!

Add two more properties to the class:

let shrinkFactor = CGFloat(2.0 / 3)
var expandFactor: CGFloat {
  return 1.0 / shrinkFactor
}

The shrinkFactor is how much the heart will contract in an animation. The expandFactor is just the inverse of the shrinkFactor, which you’ll use for the second half of the animation.

By doing it this way you ensure that the heart never looks pixelated. Imagine you have an image that is 500 pixels. If you shrink it down to 300 and then return to to 500, you never go beyond the original resolution and you keep the image sharp. Although Emoji is a vector font, when it is rendered in the label, it gets treated as pixels just like any other image.

Next, add the following method to the class to perform the actual animation:

func beat() {
  // 1
  UIView.animateWithDuration(currentBeatPattern.duration / 2,
    delay: 0.0,
    options: .CurveEaseInOut,
    animations: {
      // 2
      self.iconLabel.transform = CGAffineTransformScale(
        self.iconLabel.transform, self.shrinkFactor, self.shrinkFactor)
    },
    completion: { _ in
      // 3
      UIView.animateWithDuration(self.currentBeatPattern.duration / 2,
        delay: 0.0,
        options: .CurveEaseInOut,
        animations: {
            // 4
            self.iconLabel.transform = CGAffineTransformScale(
              self.iconLabel.transform, self.expandFactor, self.expandFactor)
        },
        completion: { _ in
          // 5
          self.beat()
        }
      )
    }
  )
}

There are animations nested in animations here for the full effect! Here’s what’s going on in this method:

  1. The outer animation is the first half of the whole effect, so its duration is half the total duration. You also use .CurveEaseInOut animation easing because it makes the beat feel more natural.
  2. Inside the animation closure, transform the heart to it’s shrunken size.
  3. When the expand animation completes you want to kick off the second half, which is another animation. This again has half the total duration.
  4. Inside the second animation closure, the difference is you’ll use the expandFactor instead of the shrinkFactor.
  5. Finally, in the completion block of the second animation you have a recursive call to beat(). After one cycle completes, another begins.

All that’s left is to start the animation. Add the following line to the end of viewDidAppear:

beat()

That will kick off the recursive beat as soon as the view appears.

Build and run. When the simulator opens, you’ll see the heart beating. And again, after eight seconds it will move on to a new pattern.

heartbeat-apple-watch-simulator-app

Notice how the animation speed scales to the current heart rate too. Very realistic!

Where to Go From Here?

You can download the final project for this tutorial in case you got stuck along the way.

There are many additions you could make to this sample app:

  1. Add another label for the description of the beat pattern.
  2. Give the heart a random beat instead of going in through the beat patterns in order
  3. Add and handle an erratic heartbeat. Hint: use a random duration each beat
  4. Give the user a view of their average heartbeat over time
  5. And finally, if you want a real challenge, draw a rhythm strip graph of the beats

The simulator is a great tool to get you thinking about what kinds of apps you can build on such a small screen. You can use the empty starter project for this tutorial, or check out the Watch Simulator project on GitHub, which has a simple tap counter as a starter app.

You can use the Watch Simulator project to practice your ideas ahead of the release of WatchKit. WatchKit is rumoured to be released soon after this post. If you’d like to build your own apps and challenge yourself, check out the WatchKit Hackathon scheduled for November 23, 2014.

I hope you’ve enjoyed this tutorial, and welcome to the world of WATCH! :]

Preparing for the Apple Watch: a Prototype Heartbeat App is a post from: Ray Wenderlich

The post Preparing for the Apple Watch: a Prototype Heartbeat App appeared first on Ray Wenderlich.

Video Tutorial: Introduction to CloudKit Part 11: Account Metadata

Arduino Tutorial: Integrating Bluetooth LE and iOS with Swift

$
0
0
Create an Arduino Bluetooth accessory!

Create an Arduino Bluetooth accessory!

Creating machines that interact with the physical world is an incredibly satisfying thing. Controlling them via Bluetooth with your iOS device is just plain awesome!

In the past, the only way to create an Apple-approved Bluetooth device was by being part of the MFi program. With Bluetooth Low Energy 4.0, individuals and small companies can develop their own products and market them without the overhead of Apple’s MFi regulations. That means you can talk to devices over Bluetooth LE with very little configuration and code, which opens up an entire world of opportunity for iOS developers.

This tutorial will teach you how to integrate Bluetooth LE and iOS using standard off-the-shelf components and an Arduino. Since the focus of this project is building a BLE device, you’ll be using the iOS Core Bluetooth framework. If you’re unfamiliar with Core Bluetooth, check out our Introduction to Core Bluetooth: Building a Heart Rate Monitor tutorial.

Aside from the Arduino and BLE shield components listed below, you’ll also need to run the companion app on a real iOS device – that means you’ll need a paid iOS developer account.

Let the building begin!

Getting Started

Using off-the-shelf components for this build makes creating a Bluetooth device a snap. Here’s an image showing the basic elements you’ll use in this project:

All Parts Needed

A look at the parts you’ll need to complete this project.

Here’s the list of parts you’ll need to complete the project, and where to obtain them:

Black Widow BLE Shield Long Range

Note: You can use a standard Arduino Uno R3 with a USB A to B cable if you already have one. Both will work with this BLE device.

The Arduino, servo and jumper wires can be purchased together from SparkFun.com in the SparkFun Inventor’s Kit for Arduino. This kit comes with many useful components and tutorials to get you started in the hardware world.

You can find less-expensive BLE Shields other than the one listed in this tutorial, but be aware that many of them sacrifice flexibility to save on cost. The Black Widow BLE Shield allows you to program your own custom BLE Services and Characteristics onto the module. This means you can take full advantage of the Bluetooth 4.0 data structure. Plus, it gives you extra long range with the built-in BLE121LR module.

That takes care of the hardware side of things – there are a few extra software pieces to take care of as well.

Download the Xcode starter project here. The starter project includes the view controller and base Core Bluetooth implementation to save you time. You’ll add more code to interface with the BLE Shield later on in the tutorial.

Next, download and install the Arduino IDE from the Arduino Download page; it’s available for Windows, Mac OS X and Linux platforms. You’ll use the Arduino IDE to write and compile the code for the Arduino. Ensure you grab the latest release version, not the beta or nightly builds.

Note: If you’re using OS X 10.10 Yosemite, you may also need to install the Java 6 runtime. You’ll get a warning message the first time you run the Arduino IDE if this is the case. You can download Java 6 from Apple and install it to get the Arduino IDE working.

The Basic Design of your App

Your finished project will consist of an iOS app that will send messages via Bluetooth to the BLE Shield module. The module will then send the message to the Arduino board to tell the servo which position it should rotate to.

Here’s an example use-case of your project:

  1. The user of the iOS app moves the slider to the middle position.
  2. The app sends the number 90, which represents 90 degrees in this case, to the BLE Shield module using the active Bluetooth connection.
  3. The BLE Shield transfers the number 90 to the Arduino board.
  4. The Arduino board rotates the servo to the 90 degree position.

Seems pretty straightforward! To begin, you’ll work with the Arduino IDE and program the board logic.

Note: The BLE Shield should not be assembled onto the Arduino board at this time! You may encounter difficulty programming the Arduino if the BLE Shield is installed. As long as you remove it before programming the Arduino, everything should go smoothly. When you assemble or disassemble the BLE shield module or any of the wires, it’s a good idea to remove power from the board by unplugging the USB cable first.

Programming the Arduino

Start the Arduino IDE; you’ll see the editor appear with a blank document, or “sketch”, as shown below:

Arduino IDE 001

Before doing anything else, click File\Save in the top menu and save the current file to a convenient location as Arduino_Servo_Controller. This creates a folder in your save location that contains a file with a .ino file extension.

Note: Don’t rename Arduino folders unless you also rename the .ino file contained within. The Arduino IDE requires that the .ino file reside in a folder with the same name.

Before you start writing code, you’ll need to set up the IDE to communicate with the Arduino Uno board.

Select Tools\Board\Arduino Uno to let the IDE know what kind of board you’ll be dealing with. Next, connect the Uno to your computer with the USB cable as shown below:

Arduino Kit

This lets the Arduino IDE recognize the serial port to which the Uno is connected.

Note: The USB cable provides not only communication to the Arduino, but power as well. If you’re working on a future project that requires more than 500mA, you’ll have to use some other power source besides USB.

Once that’s done, select Tools\Serial Port\… and select the USB port the Arduino is connected to. Generally it’s similar to /dev/tty.usbserial… or /dev/tty.usbmodem….

At this point the Arduino IDE is ready for programming.

Add the code below to your project using the Arduino IDE:

// Arduino Bluetooth LE Servo Controlled by iOS
 
void setup() // Called only once per startup
{ 
} 
 
void loop() // Continuous loop
{
}

Arduino programs are typically split into two main functions: setup() and loop().

The setup() function does exactly what it is says: it’s called only once on startup and is a great place for setting up your hardware and software. The loop() function is called once setup() is done. loop() will be called over and over again until you reset or power down the board.

Click the Verify button (the checkmark icon) to ensure everything compiles correctly. If so, you’ll see a confirmation message similar to below:

BLE-Success-Build

Now that you have the basic framework of the Arduino program in place, it’s time to add some logic to control the board.

Illuminating an LED

The main objective of your Arduino program in this project is to receive the one-byte messages coming from the BLE Shield and use the contents of the message to set the servo’s position. But before you get too involved into the servo code, it would be nice to test your current connections.

The easiest way to do this is to write a small program that controls the built-in LED on the Uno. Think of it as the “hello world” equivalent on the Arduino. :]

Replace the code in your project with the following:

// Arduino Bluetooth LE Servo Controlled by iOS
 
int LED = 13;     // Most Arduino boards have an onboard LED on pin 13
 
void setup()  // Called only once per startup
{ 
  pinMode(LED, OUTPUT);     // Set pin as an output
  digitalWrite(LED, HIGH);  // Turn on LED (ie set to HIGH voltage)
} 
 
void loop() // Continuous loop
{
  delay(500);
  digitalWrite(LED, LOW);
  delay(500);
  digitalWrite(LED, HIGH);
}

Since most Arduino boards have an onboard LED on pin 13, you can use the illumination of the LED as confirmation that your Arduino program is executing properly. You set the LED variable to 13 to refer to that pin.

In setup(), you set the initial state of the board. pinMode(LED, OUTPUT) sets pin 13 as a digital output, which means that the voltage on this pin can be set to HIGH (+5v) or LOW (0v). digitalWrite() sets the pin’s voltage; in the code above you’ve set it to HIGH to illuminate the LED.

After setup() has finished, loop() will run continuously. delay() will wait a certain number of milliseconds which you’ll use to get the LED to blink. After a 500ms delay, you turn the LED off by writing the value LOW; after another delay, you turn the LED back on.

Click the Verify button again to compile the program and check for any syntax errors. If your program compiles without issue, you’ll see Done Compiling in the status bar of the Arduino IDE. If not, read the status bar message for an indication of where you’ve messed up, fix the error and click the Verify button to make sure you’ve squashed the bug for good.

Now it’s time to program the code to the Arduino board and test the LED.

Make sure your Arduino is connected to your computer with the USB cable then click the Upload button (the one with the right arrow icon) to program the Arduino board. Once the upload process has finished, the Done uploading message should appear in the status bar as shown below:

Click Upload. Done uploading. Success!

Check your Arduino board, and you should see the LED start to blink.

LED 13 is ON. Houston we have communication.

LED 13 is ON. Houston, we have communication.

If an error occurred, try the following things to diagnose your issue:

  • Check that Arduino Uno is selected in the Tools/Board menu.
  • Check that /dev/tty.usbserial… or /dev/tty.usbmodem… (or similar) is selected in the Tools/Serial Port menu.
  • Check that you installed any required drivers for your Arduino board as noted earlier.
  • Check that the BLE Shield is not assembled on the Arduino board during programming of the board.

If the LED works, you can be sure that the Arduino IDE is set up properly. You can now add some code to control the servo.

Interfacing With the Servo Control

A servo is a motor with a changeable rotational position. You control a servo by sending different frequencies of electric pulses to its control line.

Replace the blinking LED code in the current sketch with the following:

// Arduino Bluetooth LE Servo Controlled by iOS
 
#include <Servo.h>
 
int LED = 13;     // Most Arduino boards have an onboard LED on pin 13
Servo myservo;    // Create servo object to control the servo

The above code imports the Servo library and create a variable as an instance of the Servo class. By including the Servo library, you can create Servo objects which can be easily configured to control the servo hardware.

Next, add the following function:

void setup()  // Called only once per startup
{ 
  pinMode(LED, OUTPUT);     // Set pin as an output
  digitalWrite(LED, HIGH);  // Turn on LED (ie set to HIGH voltage)
 
  myservo.attach(9);        // Attach the servo object to pin 9
  myservo.write(0);         // Initialize servo position to 0
}

myServo.attach(9) indicates that output pin 9 of the Arduino Uno board will be connected to the servo, while write(0) sets the position of the servo to zero degrees.

That takes care of the servo initialization, but you’ll still need some code to handle the BLE communication. Before you tackle that, you should read through the following section on the fundamentals of serial communication.

Serial Communication

Serial communication is much like using a telephone. The speaker held to your ear is the receiver (RX) and the microphone is the transmitter (TX). Your transmitter is connected to the receiver of the person with whom you are talking, and your receiver is connected to their transmitter. This allows for bi-directional communication where either person can send or receive information.

Note: The Arduino’s Serial Port communicates using standard TTL voltage levels (0-5v). This type of communication requires three connections: transmit (TX), receive (RX), plus a common ground. The BLE Shield was designed to communicate with the Arduino’s serial port; therefore, its TX line will connect to the Arduino’s RX port and, conversely, its RX line will connect to the Arduino’s TX port.

By default, the Arduino uses pin 0 and pin 1 for serial communication. To communicate with the Black Widow BLE Shield, this needs to be changed to pin 4 (RX) and pin 5 (TX). This guards against any data collision when the PC is uploading to the Arduino.

Add the following include to the top of the file:

#include <SoftwareSerial.h>

This will allow you to use the serial communication library.

Add the following variable underneath the LED and myservo variable declarations:

SoftwareSerial BLE_Shield(4,5);

This will create an instance of SoftwareSerial named BLE_Shield. The two parameters are pin numbers, so the BLE shield will use RX and TX lines 4 and 5 respectively.

Add the following line to the end of the setup() function:

BLE_Shield.begin(9600);   // Setup the serial port at 9600 bps. This is the BLE Shield default baud rate.

This sets the serial communication, or baud rate, which is the speed at which data bits are transmitted. In order for two devices to communicate, they must use the same baud rate. Since the BLE Shield’s default baud rate is 9600, you set the Arduino serial port to match.

Finally, add the following function to the bottom of the sketch:

void loop()  // Continuous loop
{
  // See if new position data is available
  if (BLE_Shield.available()) {
    myservo.write(BLE_Shield.read());  // Write position to servo
  }
}

Eventually, your iOS device will be feeding data to the BLE shield. loop() has one job now: look for new bytes from the BLE shield’s RX line and pass them to the servo.

First, it calls BLE_Shield.available() to see if any bytes are available. If so, read the available bytes and write them to myServo. If there is no new data from the BLE Shield, then it loops around and checks again…and again…and again.

Save your Arduino project then click the Upload button to upload your new program to the Arduino board. The Done uploading message should appear at the bottom of the IDE. You’ll see the LED light up and stay lit, letting you know main() finished.

At this point, your Arduino code is complete and ready for communication with your iOS app via the BLE Shield. You can power down the board by unplugging the USB cable. You won’t need the Arduino IDE from this point on – the board actually saves the last-uploaded sketch to its internal memory. The next time you connect the USB cable, the sketch you just wrote will run automatically without clicking the Upload button from the Arduino IDE!

The next logical step is to get familiar with how the BLE Shield functions; you’ll cover this in the next section.

The Inner Workings of the BLE Shield

The Black Widow BLE Shield from Back Forty is based on Bluegiga’s BLE121LR module which supports Bluetooth Low Energy 4.0. The BLE121LR, with or without the Shield component, is a great module for interfacing with iOS devices. It can be programmed with custom scripts to run without the need for an additional processor.

In this project, you’ll focus on using the BLE Shield as comes out of the box. It’s preprogrammed with a BLE Service and some characteristics geared toward basic communication.

What type of communication do you think will be used between the Arduino board and BLE Shield? Hint: The answer was mentioned earlier in the Serial Communication section.

Solution Inside: Solution Inside SelectShow>

Here are the preprogrammed Service and Characteristics that come with this specific BLE device as found in the Black Widow BLE Shield – Datasheet:

  • Service
    Name: Black Widow BLE
    UUID: 025A7775-49AA-42BD-BBDB-E2AE77782966
  • Characteristic
    Name: TX
    UUID: F38A2C23-BC54-40FC-BED0-60EDDA139F47
    Description: The iOS device sends data to the BLE Shield’s TX characteristic which is then received on the RX pin of the Arduino. The maximum amount of data which can be sent at once is 20 bytes due to BLE121LR restrictions.
  • Characteristic
    Name: RX
    UUID: A9CD2F86-8661-4EB1-B132-367A3434BC90
    Description: The RX characteristic is used to receive data on the iOS Side. Data written to the BLE Shield’s TX pin will be notified/indicated on the RX characteristic on the iOS device.

Since your project is based on one-way communication from your iOS device to the Arduino board, you’ll only make use of the Black Widow BLE service and the TX characteristic.

Hardware Connections

Now it’s time to work on the hardware connections of the project. Be sure the USB cable is unplugged from the Arduino board before working with the hardware.

To make your life easier, gather the following parts below before you begin:

  • Arduino Uno Board
  • Black Widow BLE Shield
  • Servo
  • Three jumper wires

Assemble the BLE Shield onto the Arduino board as shown below:

Attaching BLE Shield to Arduino board

Attaching BLE Shield to Arduino board

Be sure to match up the pins before applying any pressure to seat the BLE Shield. Inserting misaligned pins can cause damage to the hardware.

Once you’re done, the entire assembly should look like this:

BLE Shield Assembled!

BLE Shield Assembled!

The servo motor requires three connections: +5v (red), Ground (black) and Position Signal (white). The +5v and Ground connections provide the servo with the power it needs to change and maintain its rotational position. The Position Signal connection is used to set the position of the servo. To prevent possible damage to the servo, ensure you don’t swap any of the connections.

Note: A servo’s position is based on the frequency of electric pulses sent to the servo’s Position Signal wire. If you were to connect this wire to an oscilloscope you would see a square wave pattern with a specific frequency and duty cycle. As the pulse width changes, the servo motor turns its shaft according to the width received.

Attach the three wires to the servo motor as shown in the image below:

Assembled Servo Wires

It’s a good idea to use the same color wires as the servo leads so that you don’t get mixed up as to which wire goes where.

Connect the red wire to the BLE Shield’s 5v pin. Connect the black wire to the GND pin and the white wire to pin 9 as shown below:

Wires Connected to BLE Shield

Wires Connected to BLE Shield

Your hardware is connected and ready to go. All that’s left is to finish writing your iOS app to tie it all together!

Fleshing out the iOS App

The app uses a slider to wirelessly control the position of the servo. When you move the slider button up and down, the position data will be sent to the Arduino’s BLE Shield. The Connect/Disconnect icon at the bottom will indicate the Bluetooth connection status.

Open the downloaded starter project in Xcode, plug in your iPhone and build and run your app. The app will open and you will see an image similar to the one below:

Arduino Servo App

Arduino Servo App

The app isn’t quite ready to communication with the Arduino, but that’s where you come in.

Click the Stop button in Xcode.

Starter Project Overview

When the app starts, ViewController creates an instance of BTDiscovery to begin searching for BLE devices in range with a specific Service UUID. BTDiscovery manages the discovery of and connections to Bluetooth devices. It also notifies the app when a Bluetooth device connects or disconnects.

ViewController has a vertical slider control that sends the slider’s position value to BTService and updates the Bluetooth image with the connection status. BTService handles the communication to the BLE shield module.

Before moving on, open BTService.swift — there are a few important things in this file that help the app communicate with the BLE Shield, so it’s worth taking a minute to review them.

Towards the top of the file you’ll see a couple of UUIDs and one notification name defined as constants with let.

BLEServiceUUID represents the 128-bit service UUID of the Arduino’s BLE Shield. You’ll notice that this UUID matches the Black Widow BLE Service UUID listed earlier in the tutorial. Similarly, PositionCharUUID matches the BLE Shield’s TX UUID. Both hex values must match in order for the app to discover and communicate with the BLE Shield.

Finally, you’ll use BLEServiceChangedStatusNotification to notify the rest of the app when a peripheral connects or disconnectes from the app. The main view controller of your app uses this value to toggle the connection image on the home screen.

No code needs to be added here, so you can move on. Open ViewController.swift and replace the implementation of sendPosition with the following:

// Valid position range: 0 to 180
func sendPosition(position: UInt8) {
  // 1
  if !allowTX {
    return
  }
 
  // 2
  // Validate value
  if position == lastPosition {
    return
  }
  // 3
  else if ((position < 0) || (position > 180)) {
    return
  }
 
  // 4
  // Send position to BLE Shield (if service exists and is connected)
  if let bleService = btDiscoverySharedInstance.bleService {
    bleService.writePosition(position)
    lastPosition = position
 
    // 5    
    // Start delay timer
    allowTX = false
    if timerTXDelay == nil {
      timerTXDelay = NSTimer.scheduledTimerWithTimeInterval(0.1,
        target: self,
        selector: Selector("timerTXDelayElapsed"),
        userInfo: nil,
        repeats: false)
    }
  }
}

This method is called from positionSliderChanged each time the user moves the slider. Here’s what happens in this method:

  1. Only continue if no other send occurred in the last 0.1 seconds to avoid flooding the BLE connection with too much data.
  2. Prevent the same value from being written multiple times. To keep traffic and power use to a minimum, you always want to take advantage of any opportunity to prevent sending duplicate data.
  3. Make sure that the values being sent are in the correct range. The Arduino’s Servo object accepts values between 0 and 180. If you take a peek at the storyboard file, you will see that the slider’s minimum and maximum values have been set to the same range.
  4. Ensure that the BTService exists and is ready for action, then write the slider value to the position characteristic.
  5. The timer prevents you from flooding the BLE Shield with data. First, you set allowTX to false to prevent sending more data; then you set up a timer to fire in 0.1 seconds which will set it back to true after the delay.
Note: What’s the significance of using a timer here? Well, BLE devices are low energy devices — which translates into “low horse power”. Also recall that the baud rate between the Arduino and the BLE Shield is set at 9600 bps. If you are building an application with large volumes of data, increase the baud rate if you can to preserve energy.

Now you have to implement the part of BTService that transmits the data. Open BTService.m and replace the implementation of writePosition with the following:

func writePosition(position: UInt8) {
  // See if characteristic has been discovered before writing to it
  if self.positionCharacteristic == nil {
    return
  }
 
  // Need a mutable var to pass to writeValue function
  var positionValue = position
  let data = NSData(bytes: &positionValue, length: sizeof(UInt8))
  self.peripheral?.writeValue(data, forCharacteristic: self.positionCharacteristic, type: CBCharacteristicWriteType.WithResponse)
}

To start, the method will return immediately if the proper characteristic matching the expected UUID hasn’t been discovered yet. If it has, you need to wrap the data — simply the position value — in an NSData object. Then, you write the value out to the Core Bluetooth peripheral.

Since your positionCharacteristic UUID matches the BLE Shield’s TX characteristic, the BLE Shield transmits the data to the RX of the Arduino. The Arduino code written earlier uses this piece of data to set the servo’s position.

Build your app to make sure everything compiles. You app is now complete and ready for testing!

Putting it All Together

The Arduino, BLE Shield, and servo should already be fully assembled as shown in the image below:

Arduino Servo Project Completed!

Arduino Servo Project Completed!

Plug the USB cord into the Arduino to supply power. Next make sure your iPhone’s Bluetooth is on and your iPhone is connected to your computer.

Run your app from Xcode; within a couple seconds you should see the app’s Bluetooth icon change from Disconnected to Connected and the BLE Shield’s LED should illuminate. Now move the slider up and down. You should see the servo motor rotate based on the slider movement.

(Here’s a direct link to the QuickTime movie file if you can’t see the above action video.)

Where To Go From Here?

You can download the final project here; it includes both the iOS and the Arduino code.

You’ve seen how simple it is to pass data wirelessly from the iPhone to the Arduino, and then write a sketch on the Arduino to do something with that incoming data. Hopefully this project has spurred you to dream up other projects that use Bluetooth LE and iOS.

For example, you could use this tutorial’s servo motor to automatically unlock the front door of your house. Or, you could create an iOS remote control for your water sprinklers and never forget to water your lawn again!

This project gave you a small taste of what it takes to create your own BLE device. If you’re interested in learning more about this topic, you might enjoy my book Integrating iOS Bluetooth LE with PIC18 Microcontrollers. It gives an in-depth look at creating a BLE device at the microcontroller level.

In addition, the book provides practical lessons on how to program and configure your own services and characteristics for machine control. The main project of the book is based on the iO Xtreme PCB. The book and the iO Xtreme board are available on my website at www.back-40.com.

I hope you enjoyed the tutorial. If you have any questions or comments, please join the discussion in the forums!

Arduino Tutorial: Integrating Bluetooth LE and iOS with Swift is a post from: Ray Wenderlich

The post Arduino Tutorial: Integrating Bluetooth LE and iOS with Swift appeared first on Ray Wenderlich.


Video Tutorial: Introduction to CloudKit Part 12: User Discovery

Video Tutorial: Introduction to CloudKit Part 13: Deploying

Video Tutorial: iOS App Extensions Part 0: Introduction

Video Tutorial: iOS App Extensions Part 1: Photo Extensions: Getting Started

Unity with Brian Moakley – Podcast S02 E06

$
0
0
Learn about Unity with Brian Moakley!

Learn about Unity with Brian Moakley!

Welcome back to season 2 of the raywenderlich.com podcast!

Remember that among the many changes in this season, we are now moving to a weekly (and shorter) format – so we’re cracking along with episodes (6 already)!

In this episode, we talk with Brian Moakley, employee #1 at Razeware LLC (the company behind this site), and the developer of a 18-part Introduction to Unity video tutorial series.

In this episode, we take a deep dive into Unity, a powerful tool for making 2D and 3D games on iOS, Android, Mac, Windows, and more.

[Subscribe in iTunes] [RSS Feed]

Links and References

Contact Us

Where To Go From Here?

We hope you enjoyed this podcast, and the changes we made for season 2.

Remember – we’re now on a weekly format, so stay tuned for a new episode next week! :]

Be sure to subscribe in iTunes to get access as soon as it comes out!

We’d love to hear what you think about the podcast, and any suggestions on what you’d like to hear in future episodes. Feel free to drop a comment here, or email us anytime at podcast@raywenderlich.com!

Unity with Brian Moakley – Podcast S02 E06 is a post from: Ray Wenderlich

The post Unity with Brian Moakley – Podcast S02 E06 appeared first on Ray Wenderlich.

RubyMotion Tutorial for Beginners: Part 1

$
0
0
Get Started with Ruby Motion!

Get Started with Ruby Motion!

Ask any Ruby developer, especially one with a background in Java, C or C++, and they’ll tell you just how much better their life is since discovering Ruby. Ruby’s syntax is concise, simple, and consistent, which makes it very easy to learn and to use.

Despite it’s simplicity, Ruby has extensive metaprogramming features; this makes it a very powerful language for developing scalable, developer-friendly applications.

RubyMotion helps iOS and Mac developers create full-featured, native apps that are as slick and performant as apps written in Objective-C or Swift — only they’re written in the fun, elegant and flexible Ruby language. Goodbye, Xcode! :]

In this 2-part RubyMotion tutorial series, you’ll build a simple iPhone application from scratch using RubyMotion and learn the basics of RubyMotion.

You’ll need the following basic knowledge to get the most out of this RubyMotion tutorial:

  • Experience in developing for iOS in Objective-C
  • Basic understanding of Ruby
  • Basic knowledge of CSS
  • Basic experience with Terminal

If you need a Ruby refresher, there’s a great free guide at CodeSchool. As for iOS development, we have tons of iOS tutorials to help you along.

With those prerequisites met, you’re ready to begin!

Getting Started

RubyMotion isn’t free; to get started you’ll have to purchase a license from RubyMotion. It’s $199.99, but in my opinion it’s a worthwhile investment for the time savings it offers.

Download and install RubyMotion as per the Getting Started guide on the RubyMotion site.

RubyMotion-Installing

While you’re waiting for RubyMotion to finish downloading, you can learn some of the similarities — and the differences — between RubyMotion and Ruby below.

Differentiating RubyMotion vs. Ruby

RubyMotion uses a Ruby-like syntax. In order to bridge the gap between Ruby and Objective-C, RubyMotion has a few differences from the reference implementation of Ruby – the key difference being how you name method parameters.

While Ruby 2.0 does support named parameters, RubyMotion considers the parameters as part of the method name itself, just as they are in Objective-C.

Consider the following method definitions:

def tableView(table_view, cellForRowAtIndexPath: indexPath)
  # ...
end
 
def tableView(tableView, heightForRowAtIndexPath: indexPath)
  # ...
end

In Ruby 2.0.0 and later, the second method definition would overwrite the first. However, RubyMotion would consider them as two distinct methods.

RubyMotion also handles the parameter names differently: in Ruby, the parameter key (heightForRowAtIndexPath in this case) becomes the local variable available in the method, while in RubyMotion the parameter value itself is available to the method, much like Objective-C.

Finally, RubyMotion is a compiled language; while Ruby is an interpreted language.

Introducing Pomotion

Your task in this RubyMotion tutorial is to develop a productivity tool based on the famous Pomodoro Technique. The app consists of a 25-minute countdown timer which can help you develop a more productive pattern of working for a focused period and then taking a short rest.

Open up Terminal, move to a directory of your choice (I used ~/Gavin/Code/) and run the following command:

motion create Pomotion

You should see the following output:

Create Pomotion
Create Pomotion/.gitignore
Create Pomotion/app/app_delegate.rb
Create Pomotion/Gemfile
Create Pomotion/Rakefile
Create Pomotion/resources/Default-568h@2x.png
Create Pomotion/spec/main_spec.rb

The motion command creates a basic template app containing the bare essentials to get you started.

Change to the newly created directory using the cd command as follows:

cd Pomotion

Then open the directory in Finder:

open .

You should see the following files:

Empty RubyMotion App directories

Here’s what’s in each of those files:

Gemfile

If you’ve used Cocoapods before, you can think of the Gemfile as being similar to a Podfile.

Ruby offers thousands of libraries or gems, most of which are open source code projects maintained by the Ruby community. Many of these gems are directly compatible with RubyMotion and a few are now being written especially for RubyMotion.

Using the bundler gem with this Gemfile lets you easily include other gems in your project and manage your app’s dependencies. This makes it easier to share code between developers.

Rakefile

Rake is Ruby’s answer to Make.

A Rakefile is equivalent to a Makefile. Rake lets you specify tasks, set configurations, and add dependencies within your application. Rake ships with OS X so you should be able to run it from Terminal without any problems.

Open the Rakefile in your favorite text editor; you’ll see it already has one pre-set configuration option: the app name Pomotion. There are several default configurations you can also change yourself; you can find a list of them in the RubyMotion Project Management Guide.

Head back to Terminal and run the following command:

rake config

This shows you your current configuration like so:

  background_modes       : []
  build_dir              : "./build"
  codesign_certificate   : "iPhone Developer: Gavin Morrice (X999X9X9XX)"
  delegate_class         : "AppDelegate"
  deployment_target      : "7.1"
  device_family          : :iphone
  embedded_frameworks    : []
  entitlements           : {}
  external_frameworks    : []
  files                  : ["./app/app_delegate.rb"]
  fonts                  : []
  framework_search_paths : []
  frameworks             : ["UIKit", "Foundation", "CoreGraphics"]
  icons                  : []
  identifier             : "com.yourcompany.Pomotion"
  interface_orientations : [:portrait, :landscape_left, :landscape_right]
  libs                   : []
  manifest_assets        : []
  motiondir              : "/Library/RubyMotion"
  name                   : "Pomotion"
  prerendered_icon       : false
  provisioning_profile   : "/Users/Gavin/Library/MobileDevice/Provisioning Profiles/XXXX.mobileprovision"
  resources_dirs         : ["./resources"]
  sdk_version            : "7.1"
  seed_id                : "XXXXXXXX"
  short_version          : nil
  specs_dir              : "./spec"
  status_bar_style       : :default
  version                : "1.0"
  weak_frameworks        : []
  xcode_dir              : "/Applications/Xcode.app/Contents/Developer"

Some of those configuration settings will likely be familiar to you, others, like motiondir and xcode_dir are unique to RubyMotion.

Overriding Configuration Options

You’ll override a couple of these defaults with your own preferences. Since Pomotion is still under development, the version number should probably be lower than 1.0.0. Also, you won’t support landscape orientation just yet.

Open Rakefile and add the following lines within the Motion... do block:

app.interface_orientations = [:portrait]
app.version = "0.1.0"

Your entire Rakefile should now look like this:

# -*- coding: utf-8 -*-
$:.unshift("/Library/RubyMotion/lib")
require 'motion/project/template/ios'
 
begin
  require 'bundler'
  Bundler.require
rescue LoadError
end
 
Motion::Project::App.setup do |app|
  # Use `rake config' to see complete project settings.
  app.name = 'Pomotion'
  app.interface_orientations = [:portrait]
  app.version = "0.1.0"
end

Run rake config to verify the changes you just made to the configuration. You should now see that version and interface_orientations have been changed for your app – and all it took was a quick modification to the Rakefile.

The “app” Directory

app is where most of your application’s code should live; you’ll store your views, controllers, and models there. RubyMotion will automatically load all files in this directory with a .rb extension.

The “build” Directory

build by default will contain all of your compiled code; you create it the first time you run rake against your project. However, you can specify an alternate directory for your compiled code in the build_dir parameter of your Rakefile.

From time to time you might want to clear the build directory and rebuild the whole application from scratch; simply run rake clean within your project directory in Terminal to accomplish this.

The “resources” Directory

resources is where you add any extra resources to your bundle, including icons, images, fonts and other files.

The “spec” Directory

spec is where you store specs, or tests, for your app. The Ruby community has a long-established culture of writing well-tested code, which includes writing the tests before writing any code.

You won’t cover Ruby tests in this tutorial — that’s a whole other subject best left for another time! :]

Hello World Example

No tutorial would be complete without a “Hello World” example, and this one is no exception.

Open app/app_delegate.rb and add the following lines above application:didFinishLaunchingWithOptions:

def hello_world_label
  @hello_world_label ||= begin
    frame = CGRectMake(20,200,280,40)
    label           = UILabel.alloc.initWithFrame(frame)
    label.text      = "Hello world"
    label.textColor = UIColor.whiteColor
    label.textAlignment = UITextAlignmentCenter
    label
  end
end
 
def window
  @window ||= UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
end

Here’s what the above code does:

First, you define a method named hello_world_label, which is a getter method for a UILabel that the app will display on the screen. Unlike a standard getter though, which would simply return the value of @hello_world_label, this method goes a step further. The ||= (or-equals) operator here means “if the variable on the left is truthy return its value; otherwise execute the code to the right of the operator and assign its value to the variable for future calls”.

This is a great way to lazy-load a property in that its value is only set when you actually call the method; in addition, this is a memoized method which caches its value, making future calls to this method execute faster.

Next, you define a method named window, which returns the main UIWindow object for this application. Again, you use the ||= operator to set a default value if one is not already set.

Note: The name of this method is hello_world_label, not helloWorldLabel as you might be used to seeing. Why?

In Objective-C, you usually write method variable names in CamelCase; in Ruby it’s general practice to write them in snake_case. Since RubyMotion is a hybrid between the two, you’ll often see RubyMotion developers use either CamelCase or snake_case: there doesn’t seem to be an agreed upon convention.

What’s my preference? Both! :] When writing code that’s part of the iOS API such as when defining a tableView:cellForRowAtIndexPath method, I’ll use CamelCase — even for the parameters — since that’s how it was defined in the API. I’ll use snake_case with code that’s specific to the application. This clearly delineates my code from iOS API code — and it means I can re-use my plain old Ruby classes in other non-RubyMotion applications without having to modify them.

Still in the same file, update application:didFinishLaunchingWithOptions: so that it looks like the following:

def application(application, didFinishLaunchingWithOptions: launchOptions)
  window.addSubview(hello_world_label)
  window.makeKeyAndVisible
  true
end

Here you add hello_world_label as a subview to the window object and tell the application to make the window object visible.

Save your work, then switch over to Terminal and run the following command:

rake

In a few seconds you’ll see your shiny new RubyMotion “Hello World” app running in the iOS Simulator as below:

RW_Rubymotion_Hello

Type exit in Terminal to stop both Rake and the simulator.

That takes care of your Hello World app; you can now take your new-found knowledge and press on with building Pomotion.

Adding a Main View Controller

Your first task in Pomotion is to create a main view-controller for the main screen.

To keep things organized — and to stay in line with Ruby on Rails’ file structure — you’ll create a new directory to store your controller files.

Return to Terminal and run the following command:

mkdir app/controllers

This creates a new directory controllers inside the existing app directory.

Run the following command in Terminal:

touch app/controllers/main_view_controller.rb

This creates a new file for your view controller.

Open main_view_controller.rb and add the following code:

class MainViewController < UIViewController
end

The code above creates a class MainViewController that inherits from UIViewController. Note that you’ve kept the same name for your controller class as the file that contains your class.

Next, return to app_delegate.rb and delete the entire hello_world_label method along with the code you added in application:didFinishLaunchingWithOptions:; they won’t be required for Pomotion.

Your complete app_delegate.rb should now look like this:

class AppDelegate
  def window
    @window ||= UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
  end
 
  def application(application, didFinishLaunchingWithOptions:launchOptions)
    true
  end
end

Now, add the following code to app_delegate.rb:

def main_view_controller
  @main_view_controller ||= MainViewController.alloc.initWithNibName(nil, bundle: nil)
end
 
def navigation_controller
  @navigation_controller ||= UINavigationController.alloc.
    initWithRootViewController(main_view_controller)
end

Here you simply create getter methods for the MainViewController and a UINavigationController to present it.

Finally, add the following to application:didFinishLaunchingWithOptions::

def application(application, didFinishLaunchingWithOptions:launchOptions)
  window.rootViewController = navigation_controller
  window.makeKeyAndVisible
  true
end

In the code above you tell window to display navigation_controller and become visible.

Run rake in Terminal to launch the simulator:

RW_Rubymotion_BlankVC

For now, all you’ll see is a UINavigationBar with a blank screen; The window is loading navigation_controller as expected, but the displayed view is transparent.

Note: One of the key benefits of RubyMotion is that it can be used independently of Xcode. However, what do you do for user interfaces?

One option is to create them in code, like you’ll do in this tutorial. A second option is to use Interface Builder with RubyMotion – to learn how to do that, check out this GitHub page.

Pixate Freestyle

To build your view you’ll use Pixate Freestyle. Pixate, lets you style iOS views using CSS; it’s just like styling a webpage.

To install Pixate, open Gemfile in your text editor and add the following line to the bottom:

gem 'motion-pixatefreestyle'

Run the following command in Terminal:

bundle

This updates the Gemfile.lock and installs any missing gems.

Next, create the following directory in your project:

mkdir vendor

Download the latest release of the Pixate Freestyle framework; unzip it and move PixateFreestyle.framework into your newly created vendor folder.

Note: If you encounter issues running version 2.1.4 with motion-pixatefreestyle 2.1, you might try using version 2.1.3 of the framework instead.

Finally, add the following line to your Rakefile, just before the closing end:

  app.pixatefreestyle.framework = 'vendor/PixateFreestyle.framework'

This simply tells motion-pixatefreestyle where to find the Pixate framework.

Run rake to check that everything installed properly; you should see the following:

     Build ./build/iPhoneSimulator-8.1-Development
     Build vendor/PixateFreestyle.framework
   Compile ./app/pixatefreestyle_code.rb
      Link ./build/iPhoneSimulator-8.1-Development/Pomotion.app/Pomotion
    Create ./build/iPhoneSimulator-8.1-Development/Pomotion.app/PkgInfo
    Create ./build/iPhoneSimulator-8.1-Development/Pomotion.app/Info.plist
    Create ./build/iPhoneSimulator-8.1-Development/Pomotion.app.dSYM
  Simulate ./build/iPhoneSimulator-8.1-Development/Pomotion.app

It’s time to dress up the main screen so that it looks presentable.

Adding Views Programmatically

Just like controllers, views should have a directory of their own within the app directory.

Execute the following command in Terminal:

mkdir app/views

Next, execute the following command to create a file to contain the MainView of MainViewController:

touch app/views/main_view.rb

Open main_view.rb and add the following code:

class MainView < UIView
 
  def initWithFrame(frame)
    super.tap do
      self.styleId = 'main_view'
    end
  end
 
end

MainView is a custom view for the MainViewController which inherits from UIView. Although they share the same name prefix of “Main”, this isn’t a requirement in RubyMotion; it’s just a nice convention to keep things organized in your app.

Pixate Freestyle adds two properties to all UIView subclasses: styleId and styleClass. These let you apply styles to any view. In the code above you set styleId on MainView when you override initWithFrame.

Note: tap was introduced in Ruby 1.9; it lets you call methods on an object within a block and returns that object at the end of the block’s execution. This comes in really handy with things like initializers in iOS, which should always return self…which you always remember to do, right? :]

It’s a heck of a lot easier than writing the following:

  def initWithFrame(frame)
    super
    self.styleId = 'main_view'
    return self
  end

You can now move on to styling MainView using simple CSS!

Add a new CSS stylesheet to the resources directory named default.css as follows:

touch resources/default.css

And add the following code to the new stylesheet:

#main_view {
  background-color: white;
}

This is some simple CSS that sets the background color to white for the #main_view id, which will apply to the main view you just created.

Open app/main_view_controller.rb and add the following method to the implementation of MainViewController:

def loadView
  self.view = MainView.alloc.initWithFrame(CGRectZero)
end

This should look familiar; you’re re-defining loadView in the view controller to load a custom view instance. In this case, the frame argument is CGRectZero, which sets the width and height of the view to 0.

Run rake to see your view in action; MainViewController now has a white background:

RW_Rubymotion_WhiteBG

Next you need a label on the screen to show the timer countdown as well as a button to start and stop the timer.

Open app/views/main_view.rb and add the following method:

def timer_label
  @timer_label ||= UILabel.alloc.initWithFrame(CGRectZero).tap do |label|
    label.styleId = 'timer_label'
    label.text    = '00:00'
  end
end

Just as before, you’ve defined a getterfor the label and you’ve used a memoization pattern to cache the UILabel in an instance variable. The label has a CSS style ID of timer_label and its text shows 00:00 — just like a real timer set to zero.

You set the frame to CGRectZero upon initialization since you’ll use using Pixate and CSS to style the label including setting its size and origin.

While you’re at it, you should add the timer button to your view as well.

Add the following code to app/views/main_view.rb:

def timer_button
  @timer_button ||= UIButton.buttonWithType(UIButtonTypeCustom).tap do |button|
    button.styleId = 'timer_button'
    button.setTitle('Start Timer', forState: UIControlStateNormal)
    button.setTitle("Interrupt!" , forState: UIControlStateSelected)
    button.addTarget(nextResponder, action: 'timer_button_tapped:',
      forControlEvents: UIControlEventTouchUpInside)
  end
end

In the above method you’ve set several titles for UIControlStateSelected; you’ll be using the selected state shortly. nextResponder is the target object that responds to tap events, which in this case is the MainViewController displaying the view.

You could have just as easily defined a timer_button_tapped: action in MainView but, strictly speaking, that would have been a violation of MVC. Since it’s the controller’s job to respond to user input, that’s where you should define the action.

Still in app/views/main_view.rb, modify initWithFrame as follows:

def initWithFrame(frame)
  super.tap do 
    self.styleId = 'main_view'
    addSubview(timer_label)
    addSubview(timer_button)
  end
ends

Here you add the label and button views you just created as subviews of MainView.

Finally, add the following styles to resources/default.css:

#timer_label {
  top: 160px;
  left: 60px;
  width: 200px;
  height: 60px;
  font-size: 60px;
  text-align: center;
  color: #7F7F7F;      
}
#timer_button {
  top: 230px;
  left: 60px;
  width: 200px;
  height: 40px;
  color: white;
  background-color: #007F00;
}

This styles the two views you added previously.

Run rake to launch your app; your app should now look like the following:

RW_Rubymotion_Timer1

Things are starting to look pretty nice! The timer is a bit static — your next job is to get it to count down.

Getting the Timer to Count Down

The Pomodoro Technique states that work should be accomplished in 25 minute blocks, so that’s where your timer value will start.

This value is quite important to the application’s logic, and might be referenced in more than one place; therefore it makes sense to write a helper method for NSDate so you don’t have to hardcode time calculations throughout your code.

Importing C Code into Your Project

The category below extends NSDate with an extra method, secsIn25Mins:

+ (int) secsIn25Mins  { 
  return TARGET_IPHONE_SIMULATOR ? 10 : 1500;
}

The above method simply defines an extra class method on NSDate that returns the number of seconds in 25 minutes (25 x 60 = 1500 seconds). Since you won’t want to wait a full 25 minutes every time you want to test the app during development, the method will return 10 seconds when the app runs in the simulator:

It would be a shame to have to port this code to RubyMotion, just so that you can use it in this app. This particular example is only a few lines, but other libraries or extensions could potentially be several thousand lines. Fortunately, RubyMotion lets you import Objective-C code directly into your app!

Create a new directory within the vendor directory and name it NSDate+SecsIn25Mins:

mkdir vendor/NSDate+SecsIn25Mins/

Then create two files named NSDate+SecsIn25Mins.h and NSDate+SecsIn25Mins.m:

touch vendor/NSDate+SecsIn25Mins/NSDate+SecsIn25Mins.h 
touch vendor/NSDate+SecsIn25Mins/NSDate+SecsIn25Mins.m

Open NSDate+SecsIn25Mins.h and add the following code:

#import <Foundation/Foundation.h>
 
@interface NSDate (SecsIn25Mins)
+ (int) secsIn25Mins;
@end

Now, paste the following code into NSDate+SecsIn25Mins.m:

#import "NSDate+SecsIn25Mins.h"
 
@implementation NSDate (SecsIn25Mins)
+ (int) secsIn25Mins  { 
  return TARGET_IPHONE_SIMULATOR ? 10 : 1500;
}
@end

Finally, add the following line to the bottom of Rakefile, just before the closing “end”:

  app.vendor_project('vendor/NSDate+SecsIn25Mins', :static)

Run rake to build your app and launch it in the Simulator; RubyMotion automatically includes the code you added in the vendor directory.

To see your Obj-C methods at work, run the following command in Terminal (sill in rake, with the simulator active):

NSDate.secsIn25Mins

You should see the following result returned in Terminal:

# => 10

Just as you defined in NSDate+SecsIn25Mins, this returns 10 since you’re running in the Simulator.

Note: If you run the command Time.secsIn25Mins, you’ll get a return value of 10 as well, even though Time is a Ruby class and not part of the iOS API. What’s going on?

RubyMotion cleverly merged the class hierarchies of both Ruby and iOS, so the Time class inherits from NSDate; this means the methods from both classes are available to Time objects.

Where To Go From Here?

Here is the sample project up until this point in this RubyMotion tutorial series.

Stay tuned for the next part of the series, where you’ll make the timer count down and fully wrap up this app.

In the meantime, if you have any questions or comments, please join the forum discussion below!

RubyMotion Tutorial for Beginners: Part 1 is a post from: Ray Wenderlich

The post RubyMotion Tutorial for Beginners: Part 1 appeared first on Ray Wenderlich.

RubyMotion Tutorial for Beginners: Part 2

$
0
0
Get Started with Ruby Motion!

Get Started with Ruby Motion!

Welcome back to our RubyMotion Tutorial for Beginners series!

In the first part of the series, you learned the basics of getting started with RubyMotion, and created a view controller with a few styled views.

In this second and final part of the series, you will add the rest of the logic to this app, including making the label count down and getting the app fully wrapped up.

Let’s get back in motion! :]

Building a Countdown Timer

At this point, you have a label and a button, and the label has some static text on it. You want the label to count down from 25 minutes.

It’s clear there should be some sort of object responsible for handling the countdown, but you haven’t defined that object yet. While it’s tempting to add that functionality to MainViewController, that’s not really the controller’s responsibility. The controller’s job is to respond to events and direct what should happen next.

Run the following command in Terminal:

mkdir app/models

You’ll use the models directory to store the models containing the application logic of your app.

Run the following command in Terminal to create a new class that will serve as your countdown timer:

touch app/models/pomodoro_timer.rb

Open PomodoroTimer and add the following lines of code:

class PomodoroTimer
 
end

PomodoroTimer has no superclass; it’s just a plain-old-ruby-object, or PORO for short.

The first thing PomodoroTimer needs is an attribute to store its current value. To do this, add the following lines to app/models/pomodoro_timer.rb:

  attr_accessor :count

The attr_accessor macro declares a getter and setter for count. The equivalent to this in Objective-C is:

@interface PomodoroTimer : NSObject
 
@property NSInteger count;
 
@end

Now, add the following method to app/models/pomodoro_timer.rb:

def initialize
  @count = Time.secsIn25Mins
end

By default count should be set to the number of seconds in 25 minutes, so you use the method you’ve just defined on NSDate to set this in initialize.

Weak References

PomodoroTimer also needs a delegate to report when certain events occur.

Add the following code just below the spot where you declared count:

  attr_reader :delegate

You’re using attr_reader here because the default setter for your delegate isn’t appropriate in this case. Using attr_accessor would create a setter that holds the delegate — in this case, an instance of MainViewController in a strongly referenced instance variable. But since you’re going to define PomodoroTimer as a property of MainViewController, using attr_accessor would create a circular dependency leading to memory leaks and crashes!

To avoid that mess, add the following method to pomodoro_timer.rb:

def delegate=(object)
  @delegate = WeakRef.new(object)
end

Here you define your own setter for delegate and set it as a weak reference. In Ruby, everything is an object, and weak references are no exception.

Add the following property to the PomodoroTimer class:

attr_accessor :ns_timer

This property, as the name suggests, will hold an NSTimer object that handles the countdown by firing once a second for 25 minutes.

Add the following method to the PomodoroTimer class next:

def start
  invalidate if ns_timer
  self.ns_timer = NSTimer.timerWithTimeInterval(1, target: self, 
    selector: 'decrement', userInfo: nil, repeats: true)
  NSRunLoop.currentRunLoop.addTimer(ns_timer, 
    forMode: NSDefaultRunLoopMode)
  delegate.pomodoro_timer_did_start(self) if delegate
end

This handles the creation of a new timer. Here’s what’s going on in the code above:

  1. If the PomodoroTimer already has an ns_timer instance, call invalidate.
  2. Set ns_timer to a new NSTimer instance that calls decrement once per second.
  3. Add the NSTimer to the current run loop, and if the delegate has been set then send pomodoro_timer_did_start to the delegate so it’s aware that the timer started.

You’ve yet to define PomodoroTimer#invalidate and PomodoroTimer#decrement.

Add the following below the start method you just wrote:

def invalidate
  ns_timer.invalidate
  delegate.pomodoro_timer_did_invalidate(self) if delegate
end

This method simply passes invalidate on to ns_timer and then notifies the delegate that the timer has been invalidated as long as a delegate has been set.

Finally, define the decrement method as follows:

private
 
def decrement
  self.count -= 1
  return if delegate.nil?
  if count > 0
    delegate.pomodoro_timer_did_decrement(self)
  else
    delegate.pomodoro_timer_did_finish(self)
  end
end

This simple method decrements the value of count by 1 each time it’s called. If there’s a delegate present and the count is greater than 0, it notifies the delegate that pomodoro_timer_did_decrement. If the count is 0 then it notifies the delegate that pomodoro_timer_did_finish.

Note the private directive above; since decrement should only be used internally within the class itself, you make this method private by adding the directive above the class definition.

Run rake to build and launch your app; you can now play around with the new class you defined above. To do this, execute the following command in Terminal (with rake and the Simulator active) to initialize a new PomodoroTimer and assign it to a local variable:

p = PomodoroTimer.new

Inspect the value of p.count using the commands below:

p.count

The value should be 10 as expected:

# => 10

Call start on p to start the countdown sequence as follows:

p.start

To see the countdown timer working, evaluate p.count repeatedly — but don’t wait, you only have 10 seconds! :]

p.count
# => 8
p.count
# => 6
p.count
# => 2

Now that you know your timer is working, you can use it in your app.

Adding a PomodoroTimer to MainViewController

Open main_view_controller.rb and declare the following property on MainViewController:

class MainViewController < UIViewController
 
  attr_accessor :pomodoro_timer
 
  # ...
end

This holds the timer instance for this controller.

In the first part of this tutorial series, you added nextResponder to MainView as the target for touch actions, with the action name of timer_button_tapped. It’s finally time to define that method.

Still in main_view_controller.rb, add the following code below loadView:

def timer_button_tapped(sender)
  if pomodoro_timer && pomodoro_timer.valid?
    pomodoro_timer.invalidate      
  else
    start_new_pomodoro_timer
  end
end

You call the above action when the user taps the timer_button. If pomodoro_timer has a value — i.e. is not nil — and it references a valid PomodoroTimer, then invalidate the PomodoroTimer. Otherwise, create a new PomodoroTimer instance.

Add the private directive just below the method you just added as shown below:

# ...
 
def timer_button_tapped(sender)
  if pomodoro_timer && pomodoro_timer.valid?
    pomodoro_timer.invalidate      
  else
    start_new_pomodoro_timer
  end
end
 
private
 
# ...

This separates the public and private methods.

Finally, add the following method after the private directive:

def start_new_pomodoro_timer
  self.pomodoro_timer = PomodoroTimer.new
  pomodoro_timer.delegate = self
  pomodoro_timer.start
end

start_new_pomodoro_timer assigns a new PomodoroTimer instance to pomodoro_timer, sets its delegate to self, and then starts the timer. Remember, tapping the button calls this method to you need to start the countdown as well.

Run rake to build and launch your app, then tap the Start Timer button to see what happens:

2014-09-11 16:40:58.276 Pomotion[17757:70b] *** Terminating app due to uncaught exception 'NoMethodError', reason: 'pomodoro_timer.rb:22:in `start': undefined method `pomodoro_timer_did_start' for #<MainViewController:0x93780a0> (NoMethodError)

Hmm, something’s wrong with your app. Can you guess what the problem is?

When you start pomodoro_timer, it calls delegate methods on MainViewController — but those methods don’t yet exist. In Ruby, this results in a NoMethodError exception.

Add the following delegate methods above the private keyword in main_view_controller.rb:

def pomodoro_timer_did_start(pomodoro_timer)
  NSLog("pomodoro_timer_did_start")
end
 
def pomodoro_timer_did_invalidate(pomodoro_timer)
  NSLog("pomodoro_timer_did_invalidate")
end
 
def pomodoro_timer_did_decrement(pomodoro_timer)
  NSLog("pomodoro_timer_did_decrement")
end
 
def pomodoro_timer_did_finish(pomodoro_timer)
  NSLog("pomodoro_timer_did_finish")    
end

The NSLog statements will print out a line to the console, just to show you that the methods are in fact being called.

Run rake once again and tap Start Timer; you should see the NSLog statements written out to the console as they’re called:

     Build ./build/iPhoneSimulator-8.1-Development
     Build vendor/PixateFreestyle.framework
     Build vendor/NSDate+SecsIn25Mins
   Compile ./app/controllers/main_view_controller.rb
      Link ./build/iPhoneSimulator-8.1-Development/Pomotion.app/Pomotion
    Create ./build/iPhoneSimulator-8.1-Development/Pomotion.app/Info.plist
(main)> 2014-11-13 13:52:44.778 Pomotion[9078:381797] pomodoro_timer_did_start
2014-11-13 13:52:45.779 Pomotion[9078:381797] pomodoro_timer_did_decrement
2014-11-13 13:52:46.779 Pomotion[9078:381797] pomodoro_timer_did_decrement
2014-11-13 13:52:47.779 Pomotion[9078:381797] pomodoro_timer_did_decrement
2014-11-13 13:52:48.779 Pomotion[9078:381797] pomodoro_timer_did_decrement
(nil)? 2014-11-13 13:52:49.778 Pomotion[9078:381797] pomodoro_timer_did_decrement
2014-11-13 13:52:50.778 Pomotion[9078:381797] pomodoro_timer_did_decrement
(nil)? 2014-11-13 13:52:51.778 Pomotion[9078:381797] pomodoro_timer_did_decrement

If you still get an exception, make sure you followed the instructions above about pasting the methods before the private keyword.

There’s just one more bit of housekeeping before moving on. In timer_button_tapped you ask if pomodoro_timer is valid?, but you haven’t yet defined a valid? method on PomodoroTimer; if you tap the button twice RubyMotion will throw a NoMethodError.

Add the following code just beneath start in pomodoro_timer.rb:

def valid?
  ns_timer && ns_timer.valid?
end

In this case, a valid result means that the PomodoroTimer has an NSTimer and that the timer is valid. Ensure you’ve added this method above the private directive, so that you can call this method on any instance of PomodoroTimer from within other objects.

Updating the Button

In MainView you specified that timer_button should have a different title if the state is UIControlStateSelected, but you aren’t setting that state yet. The button should become selected when the timer starts, and go back to the normal state when the timer is stopped or invalidated for any reason.

Add the following method to main_view_controller.rb:

def timer_button
  view.timer_button
end

This is just a helpful wrapper method that calls timer_button on the view. While it may seem arbitrary to do so, this actually helps the code adhere to The Law Of Demeter.

Replace the pomodoro_timer_did_start and pomodoro_timer_did_invalidate delegate methods with the following:

def pomodoro_timer_did_start(pomodoro_timer)
  NSLog("pomodoro_timer_did_start")
  timer_button.selected = true
end
 
def pomodoro_timer_did_invalidate(pomodoro_timer)
  NSLog("pomodoro_timer_did_invalidate")
  timer_button.selected = false  
end

Run rake, then tap Start Timer; you’ll see the button’s title change as shown below:

Interrupt Button

Updating the Label

The countdown timer isn’t terribly useful at present since the on-screen timer doesn’t count down yet. Fortunately, that’s your very next task!! :]

Add the following method to app/controllers/main_view_controller.rb:

def timer_label
  view.timer_label
end

This updates timer_label in MainView should be updated with the current count of pomodoro_timer each time the timer decrements.

Still in main_view_controller.rb, modify pomodoro_timer_did_decrement to look like the code below:

def pomodoro_timer_did_decrement(pomodoro_timer)
  mins = pomodoro_timer.count / 60
  secs = pomodoro_timer.count % 60
  timer_label.text = "%02d:%02d" % [mins, secs]
end

Here you take the value of pomodoro_timer.count and break it down into separate minutes and seconds values. You then set the text of timer_label to a formatted string so that the minutes and seconds values will always appear as double digits.

Run rake again and tap Start Timer; you should see the timer count down from 00:10 to 00:00 as shown below:

RW_Rubymotion_TimerLabel

It looks good, but watch the console carefully and you’ll see the timer continues to decrement past zero and pomodoro_timer_did_finish executes multiple times.

(main)> 2014-11-13 13:57:37.038 Pomotion[9408:386412] pomodoro_timer_did_start
2014-11-13 13:57:47.039 Pomotion[9408:386412] pomodoro_timer_did_finish
2014-11-13 13:57:48.038 Pomotion[9408:386412] pomodoro_timer_did_finish
2014-11-13 13:57:49.038 Pomotion[9408:386412] pomodoro_timer_did_finish

Ah — you’re not invalidating the timer. To fix this, modify pomodoro_timer_did_finish like so:

def pomodoro_timer_did_finish(pomodoro_timer)
  pomodoro_timer.invalidate
end

Now when the timer reaches zero, you’ll invalidate it from within MainViewController.

Run rake and try the above scenario again; verify that the counter stops decrementing at zero and pomodoro_timer_did_finish is called just once.

Another small issue with the timer right now is that timer_label only changes when the timer has counted down from 10 to 9; the user doesn’t get to see the initial value of 10 seconds.

To solve that, you’ll rewrite a bit of code to set the label as soon as the controller receives pomodoro_timer_did_start.

Still in main_view_controller.rb define a new private method named update_timer_label and move the code from pomodoro_timer_did_decrement into the new method as follows:

def pomodoro_timer_did_decrement(pomodoro_timer)
end
 
# ...
private
 
def update_timer_label
  mins = pomodoro_timer.count / 60
  secs = pomodoro_timer.count % 60
  timer_label.text = "%02d:%02d" % [mins, secs]
end
# ...

Now edit pomodoro_timer_did_decrement, pomodoro_timer_did_invalidate, and pomodoro_timer_did_start so they call update_timer_label as shown below:

# ...
 
def pomodoro_timer_did_start(pomodoro_timer)
  timer_button.selected = true
  update_timer_label
end
 
def pomodoro_timer_did_invalidate(pomodoro_timer)
  timer_button.selected = false
  update_timer_label
end
 
def pomodoro_timer_did_decrement(pomodoro_timer)
  update_timer_label
end
 
# ...

Run rake then tap Start Timer to see that the timer now starts from 00:10:

StartAt10

Making it Look the Part

The timer is working well, but it could do with a bit of dressing up.

Add the following code to app/controllers/main_view_controller.rb, just below loadView:

def viewDidLoad
  super
  self.title = "Pomotion"
end

Run rake and you should see the title appear at the top of the screen:

RW_Rubymotion_Title

That adds a bit of life to the app, but the navigation bar could use a face lift.

Add the following CSS to resources/default.css to brighten up the navigation bar:

navigation-bar {
  background-color: #7F0000;
}
navigation-bar title {
  color: white;
}

These two CSS values give the navigation bar a red tint,and color the title bar text white.

Run rake to see the results:

RW_Rubymotion_Navbar

Extending Core Classes

You can give your app a professional touch by gradually changing the color of the timer’s label from green to red as the timer counts down to 00:00.

To achieve this, you’ll need to create a method that will take two colors and mix them based on a specified proportion. Since this behavior is relevant to UIColor, you should add your new method as a class method there. However, this time around you can extend the class the Ruby way! :]

First, create a new directory under app called core_extensions:

mkdir app/core_extensions

This is a sensible place to define all of the extensions you add to the core iOS and Ruby classes.

Create a file in that directory named ui_color.rb:

touch app/core_extensions/ui_color.rb

The basic design of this effect is that timer_label can have one of four color combinations: all red, all green, a red-green mix, or grey (which will be used when the timer is inactive).

Add the following helper class methods to the UIColor class in app/core_extensions/ui_color.rb:

class UIColor
 
  def self.pomo_grey_color
    @pomo_grey_color ||= UIColor.colorWithRed(0.5, green: 0.5, blue: 0.5, alpha: 1.0)
  end
 
  def self.pomo_green_color
    @pomo_green_color ||= UIColor.colorWithRed(0.0, green: 0.666, blue: 0.0, alpha: 1.0)
  end
 
  def self.pomo_red_color
    @pomo_red_color ||= UIColor.colorWithRed(0.666, green: 0.0, blue: 0.0, alpha: 1.0)
  end
 
end

This makes it easier to reference the custom red, green and grey colors in Pomotion’s color scheme.

Now you need to define a class method that will mix red and green proportionally to return a new UIColor value.

First, define the following method below self.pomo_red_color:

def self.new_from_two_colors(color_1, color_2, proportion)
  # 1
  color_1_r = Pointer.new(:float)
  color_1_g = Pointer.new(:float)
  color_1_b = Pointer.new(:float)
  color_1_a = Pointer.new(:float)
 
  # 2
  color_1.getRed(color_1_r, green: color_1_g, blue: color_1_b, alpha: color_1_a)
 
  # 3
  color_2_r = Pointer.new(:float)
  color_2_g = Pointer.new(:float)
  color_2_b = Pointer.new(:float)
  color_2_a = Pointer.new(:float)
  color_2.getRed(color_2_r, green: color_2_g, blue: color_2_b, alpha: color_2_a)
 
  # 4
  new_red   = color_1_r.value + (color_2_r.value - color_1_r.value) * proportion
 
  # 5
  new_green = color_1_g.value + (color_2_g.value - color_1_g.value) * proportion
  new_blue  = color_1_b.value + (color_2_b.value - color_1_b.value) * proportion
  new_alpha = color_1_a.value + (color_2_a.value - color_1_a.value) * proportion
 
  # 6
  UIColor.colorWithRed(new_red, green: new_green, blue: new_blue, alpha: new_alpha)
end

Taking each commented section in turn, you’ll see the following:

  1. First, you initialize four Pointer objects. Pointers are handled quite differently in RubyMotion than in Objective-C — in Ruby, everything is an object and pointers are no exception. To create a new Pointer, simply initialize a new instance of the Pointer class and pass in the type as a parameter. The type can be either an Objective-C Runtime Type, or a Ruby symbol with the type name. To access the pointer’s value you simply call the value property.
  2. Next, you call the UIColor method getRed:green:blue:alpha to pluck the RGBA values for color_1 and then assign them to their respective pointers.
  3. Now do the same for color_2.
  4. The proportion parameter is a float value between 0 and 1. You can think of this as “What percentage of color_2 do we mix into color_1?”

    The following calculation determines how much red the new color should contain:

    color_1_r.value + (color_2_r.value - color_1_r.value) * proportion

    Here’s some real numbers to help make sense of the calculation above. If color_1 is bright red (RGBA: 255, 0, 0, 1) and color_2 is pure white (RGBA: 0, 0, 0, 1), then color_1_r and color_2_r will be 255 and 0 respectively. The color that’s a 50% blend of color_1 and color_2 will be 255 + (0 - 255) * 0.5 = 127.5.

  5. Perform the same calculation for the green, blue and alpha values.
  6. Finally, return a new UIColor object with the new, proportional values.

Run rake and test your methods from Terminal as below:

UIColor.pomo_red_color
# => #<UIDeviceRGBColor:0x9099e10>
UIColor.pomo_green_color
# => #<UIDeviceRGBColor:0x97b59c0>
UIColor.pomo_grey_color
# => #<UIDeviceRGBColor:0x94ed360>

That looks good — but what about new_from_two_colors?

UIColor.new_from_two_colors(UIColor.pomo_red_color, UIColor.pomo_green_color, 0.5)
# => #<UIDeviceRGBColor:0x909a510>

If you can stand to do the hexadecimal math yourself, you’ll see that the numbers above work out correctly. :]

Back in main_view_controller.rb, add the following lines to the bottom of update_timer_label to change the text color of timer_label as the timer decrements:

if pomodoro_timer.count > 0
  proportion = pomodoro_timer.count / Time.secsIn25Mins.to_f
  color = UIColor.new_from_two_colors(UIColor.pomo_red_color, UIColor.pomo_green_color, 
    proportion)
else
  color = UIColor.pomo_grey_color
end
timer_label.textColor = color

To determine the color above, you first check that pomodoro_timer‘s count is greater than zero; if so, then calculate the color proportion as the result of the time remaining divided by the total starting time.

Then assign color, which is a proportionate mixture of UIColor.pomo_red_color, and UIColor.pomo_green_color, to a new instance ofUIColor.

Finally, set textColor of timer_label to the new color value.

Note: When doing integer division in Ruby, the result is always rounded to the nearest full integer. So 4 / 5 would return 0, while 6 / 5 would return 1. To perform float division instead, simply convert the denominator to a float.

Run rake and tap the Start Timer button to see how your effect looks:

RW_Rubymotion_Green

RW_Rubymotion_Redish

Hey — that adds a lot of polish to your app! :] However, update_timer_label is starting to get a little messy and, strictly speaking, it shouldn’t be up to the controller to calculate the text color.

You can make this code a lot neater, and more MVC compliant, by extending the behavior of timer_label through defining a subclass of UILabel.

Create a new file in app/views and name it timer_label.rb using Terminal:

touch app/views/timer_label.rb

Open timer_label.rb and declare a new class TimerLabelas a subclass of UILabel:

class TimerLabel < UILabel
 
end

Your goal is to extract all of the logic from MainViewController that updates the timer_label, and instead place it in a custom method in TimerLabel. This makes the controller code much easier to follow, easier to maintain — and easier to test.

Still in app/views/timer_label.rb, add the following code to the class implementation:

def update_for_count(count)
  update_text_for_count(count)
  update_color_for_count(count)
end
 
 
private
 
def update_text_for_count(count)
  mins = count / 60
  secs = count % 60
  self.text = "%02d:%02d" % [mins, secs]
end
 
def update_color_for_count(count)
  if count > 0
    proportion = count / Time.secsIn25Mins.to_f
    color = UIColor.new_from_two_colors(UIColor.pomo_red_color, UIColor.pomo_green_color, proportion)
  else
    color = UIColor.pomo_grey_color
  end
  self.color = color
end

This defines a new public method for timer_labelupdate_for_count — which takes the count value from a PomodoroTimer object and calls two private methods: update_text_for_count and update_color_for_count.

This is the exact same update_timer_label code from MainViewController; the only difference is that you set the properties on self, instead of on update_timer_label.

This should help you appreciate how much simpler Ruby code can be, when compared to Objective-C.

Modify the implementation of main_view.rb to use your new TimerLabel class instead of UILabel as shown below:

class MainView < UIView
  # ...
 
  def timer_label
    @timer_label ||= TimerLabel.alloc.initWithFrame(CGRectZero).tap do |label|
      label.styleId = 'timer_label'
      label.text    = '00:00'
    end
  end
 
  # ...
end

Next, update update_timer_label in main_view_controller.rb to use the update_for_count method you defined above:

def update_timer_label
  timer_label.update_for_count(pomodoro_timer.count)
end

Run rake to build and launch your app; test your app to make sure it works as before. The changes you’ve made shouldn’t have any impact on the appearance or performance of your app, but should make the code easier to maintain in the future!

Updating the Start Timer Button

The title of timer_button changes depending on the state of the button. It would be nice to polish your app a little further and change the background color of the button when it’s selected.

To add this custom behavior, you’ll create a new subclass of UIButton that can indicate a selected state. Can you guess where this new custom view class should go?

If you said “the app/views directory”, you’d be right!

Create the following file using Terminal:

touch app/views/selectable_button.rb

Open selectable_button.rb add the following code:

class SelectableButton < UIButton
 
  def selected=(bool)
    super.tap do
      self.backgroundColor = bool ? UIColor.pomo_red_color : UIColor.pomo_green_color
    end
  end
 
end

In the above code, selected= takes a boolean parameter, calls super's selected= method, then sets backgroundColor to red if bool is true, and to green if it’s false.

Note: You’re calling super here, but there’s no method named selected= defined in UIButton. What’s going on?

Naming setters as set[Something] is a C-style idiom. In Ruby, the preference is to name setters like [something]= instead. RubyMotion cleverly offers support for both idioms and aliases them as well so you can choose to use one or the other as you see fit.

Ruby also offers method aliases in the form of [something]? for methods named is[Something].

Modify app/views/main_view.rb to use your new SelectableButton class instead of UIButton as shown below:

def timer_button
  @timer_button ||= SelectableButton.buttonWithType(UIButtonTypeCustom).tap do |button|
    button.styleId = 'timer_button'
    button.setTitle('Start Timer', forState: UIControlStateNormal)
    button.setTitle("Interrupt!" , forState: UIControlStateSelected)
    button.addTarget(nextResponder, action: 'timer_button_tapped:',
      forControlEvents: UIControlEventTouchUpInside)
  end
end

Run rake to build and launch your app; tap Start Timer and you’ll see the button background turn to red like so:

RW_Rubymotion_RedButton

Once the timer reaches zero, the button background returns to green.

Alerting the User

Your app is really starting to take shape — but there’s a little more functionality to implement. One of the key principles of the Pomodoro Technique is that you should take a five minute break after your 25 minute block of work is over. You should suggest this to your user so they don’t slip back into their old habits. UIAlertController is the perfect tool for this.

Add the following property method to main_view_controller.rb:

def alert_controller
  @alert_controller ||= UIAlertController.alertControllerWithTitle("Pomodoro Complete!",
    message: "Time to take a short break.",
    preferredStyle: UIAlertControllerStyleAlert).tap do |alert|
      ok_action = UIAlertAction.actionWithTitle("OK",
        style:UIAlertActionStyleDefault,
        handler: nil)
      alert.addAction(ok_action)
  end
end

You’ll want to show this alert once the timer has finished counting down to zero, so pomodoro_timer_did_finish would be a perfect place to call this method.

Add the following line to pomodoro_timer_did_finish:

self.presentViewController(alert_controller, animated: TRUE, completion: nil)

Run rake to build and launch your app, then tap Start Timer; wait ten seconds and you should see your new alert controller make an appearance like so:

RW_Rubymotion_Alert

And that’s it — you’ve completed your first RubyMotion application!

Where to Go From Here?

Hopefully you enjoyed working through this tutorial, and can appreciate the value RubyMotion provides, not just in time saved, but also in the emotional and mental energy you save by working with a friendlier, easier to use syntax.

If you would like to view the completed project, you can download a copy of it here.

To build and launch your RubyMotion app on a physical device, you can use the rake device command which you can learn more about here.

There are loads of great RubyMotion resources online; I’d recommend you check out RubyMotion Dispatch – A weekly newsletter as well as the developer documentation on the RubyMotion website.

If you have any questions or comments, feel free to join the discussion below!

RubyMotion Tutorial for Beginners: Part 2 is a post from: Ray Wenderlich

The post RubyMotion Tutorial for Beginners: Part 2 appeared first on Ray Wenderlich.


Video Tutorial: iOS App Extensions Part 2: Photo Extensions: Saving Data

WatchKit: Initial Impressions

$
0
0
WatchKit - w0t I think!

WatchKit – w0t I think!

Today has been an awesome day for iOS developers: we finally got to learn about WatchKit! :]

The Tutorial Team and I have been digging in already, and are quite excited about what we’ve seen so far.

We’re already hard at work preparing some tutorials on WatchKit (stay tuned!), but to tide you over in the meantime, I wanted to share my initial impressions.

Note that I’m not quite sure what the NDA restrictions are yet, so for now I’m basing this solely on materials available to the public from Apple’s WatchKit page.

Let’s dig in!

Overall Capabilities

Overall, WatchKit offers far more than I expected in this initial release.

Much like David Smith suggested, I had thought that the most we’d get from WatchKit at this point was the equivalent of a Today extension, but for the Apple Watch (i.e. Glances).

So I was pleasantly surprised to see that we’d be able to create custom interactive user interfaces on the Apple Watch, and have the ability to control them from our iOS App extension.

WatchKit_02

So this isn’t just a simple app extension; there’s a lot to sink our teeth into as developers. From the new Apple Watch-specific controls, to glances, actionable notifications, deep linking with Handoff, image caching and more — as a developer, this is the kind of stuff that gets me excited!

Apple Watch Architecture

One of the most surprising aspects of WatchKit for me was the overall architecture.

The way it works is your app is split into two parts:

WatchKit_03

  • Your Apple Watch contains the user interface resources (the Storyboard and static images), and processes user input, but doesn’t actually run any of your own code. In other words, your Apple Watch contains the “view”.
  • Your iPhone contains your code that responds to events like the app launching, button taps, or a switch value change. In other words, your iPhone contains the controller and model.

The cool thing is this communication between the Apple Watch and the iPhone works automatically, behind the scenes. You work the way you’re used to (connecting views to outlets) and the WatchKit SDK handles the Bluetooth communication behind the scenes. As far as your code is concerned, all those outlets are connected locally even though they’re on a completely separate device. Cool stuff!

WatchKit Layout

Another semi-surprising thing for me was the way layout is done on the Apple Watch. Auto Layout is nowhere to be found!

Did I just hear some of you rejoice? :]

Instead, you use a new system called groups. Basically, you add interface elements such as buttons and labels to a group, and it handles the layout for the items inside.

WatchKit_04

You can also nest groups inside each other for more complex layouts, and apply some slight styling like background color, margins, corner radius, and so on.

This brings back memories of Java’s BoxLayout or XAML’s StackPanel!

Glances and Notifications

As cool as the Watch App functionality is, I think Glances and Notifications are really where it’s at.

I personally think one of the most useful things about Apple Watch apps will be just to have a handy way to get notified of interesting things – like someone mentioning you on Twitter, or the Bitcoin price reaching the moon. ┗(°0°)┛

WatchKit_05

This is what Glances and notifications let you do:

  • Glances give you a quick overview of content within an app – think of them like Today extensions.
  • Notifications let you receive notifications on your watch. They are split into two levels. The first level (“short look”) just shows your app icon, and some brief text. If the user keeps their wrist raised (or taps the screen), it switches to the second level (“long look”) where you can show more detail, and even have action buttons.

Either way – from a glance or a notification, the user can tap to go to your Watch App – and using Handoff, you can even send the user to a specific view controller inside your app.

I expect we’ll be spending a lot of time working on these in the coming months!

Animation

One of the strangest things about the Apple Watch is that animation is not really supported.

To make something appear animated, you have to pre-generate a ton of images, and then cycle through like a flip-book. The era of the animated GIF is back! ;]

ClappingDude

For an example of this, check out Apple’s Lister example. In the Watch App’s Glance, you’ll see there are 360 images representing a circle animation!

Lister

This is bound to be a slightly frustrating part of the development process – and opens the door for some developer tools to make it easy to generate these animation images. At the same time, space on the Watch is limited so you’ll need to be careful about creating small and efficient animations only where it’s important.

Design Resources

If you’re a designer (or have design inclinations) there are some must reads/downloads for you.

  1. First, drop everything you’re doing and read the new Human Interface Guidelines (HIG) for the Apple Watch. It has some great guidelines that might not be immediately obvious; designing for the Apple Watch will be a whole new world.
  2. Second, you’ll definitely want to download the Apple Watch Design Resources. I was actually impressed with this collection – it’s basically a huge collection of PSDs useful for designing Apple Watch apps – things like mockups of UI controls, color and spacing guidelines, guidance on stroke widths, etc – great stuff!

Where To Go From Here?

Overall, I’m pretty excited by all the goodies we get to sink our teeth into with this WatchKit release.

Keep in mind that this is only the beginning of what will be available on the Apple Watch. According to Apple’s Press Release, later next year (read: WWDC-time) developers will be able to create fully native apps.

For us, we’ll be hard at work on written tutorials, video tutorials, and maybe even a book on WatchKit (hint hint) coming up soon. Stay tuned for some exciting announcements! :]

I’m curious to hear what your initial impressions on WatchKit are – please add your thoughts in the comments below! :]

WatchKit: Initial Impressions is a post from: Ray Wenderlich

The post WatchKit: Initial Impressions appeared first on Ray Wenderlich.

Making a Gesture-Driven To-Do List App Like Clear in Swift: Part 1/2

$
0
0
Learn how to make a stylish gesture driven to-do app like Clear!

Learn how to make a stylish gesture driven to-do app like Clear!

Update note: This tutorial was fully updated for iOS 8 and Swift by Audrey Tam. Original post by Tutorial Team member Colin Eberhardt.

This two-part tutorial series will take you through the development of a simple to-do list app that is free from buttons, toggle switches and other common user interface (UI) controls.

Instead, users will interact with your app via a set of intuitive gestures, including swipes, pull-to-add, and pinch. In eschewing the common interface components, you’ll present the user with a more striking and clutter-free interface. It’s not an empty gesture!

This tutorial is for intermediate or advanced developers – you will be doing some tricky things like working with gradient layers, performing animations, and creating a custom table view cell. If you are a beginner developer, you should start with some of our other tutorials.

If you want to make better use of gestures in your application, then this is the tutorial for you. Read on to start the hand aerobics!

Skeuomorphism and Touch Interfaces

Before diving into the code, it’s worth taking some time to discuss the role of gestures in UI design. Don’t worry – it’s a “gripping” topic!

The mobile multi-touch interface allows for much more direct interaction – and therefore much more control and expression – than does a simple mouse pointer device.

Some very cool and intuitive gestures have been developed, such as pinch/stretch, flick, pan, and tap-and-hold. But they are rarely used! (One notable exception is the pinch/stretch, which has become the standard mechanism for manipulating images.)

Despite the expressive nature of touch, we developers still fall back on the same old UI paradigms of buttons, sliders, and toggle switches. Why?

One of the reasons we continue to use these same-old UI components is due to a design philosophy known as skeuomorphism.

Ragecomic

To help users understand a visual computer interface, we design UIs to look like physical objects that the user is already familiar with. For years, we designed buttons on an iPhone screen to look like buttons in the physical world because users already know how to push buttons. Until the release of iOS 7 in the fall of 2013, Apple thoroughly embraced skeuomorphic design in its own applications, achieving almost photo-realistic representations of physical objects, such as notebooks and bookshelves.

But hey – designs can evolve as readily as technology. With iOS 7, Apple moved drastically away from skeuomorphism, removing many of the shadows and borders reminiscent of the physical world. In addition, the user experience was built around gestures more than ever before: swipe up from the bottom of the screen to reveal Control Center replaced the home button double tap. Swipe right from the left edge is encouraged over the Back button. As Apple embraces gestures in their user experience, it’s time to consider how to use gestures in our own apps.

I thoroughly recommending watching Josh Clarke’s presentation “Buttons are a Hack”, wherein he encourages developers to think more creatively about gestures and touch interactions. The next time you go to add a new control to your interface, ask yourself, “Can I perform the same function via touch?”

When an application comes along that makes good use of gestures, it is quite striking. A recent example is Clear by Realmac software. Be sure to check out the great demo on YouTube, or even better download the app to check it out.

This tutorial describes the development of a to-do list application that is very similar to Clear. The purpose of this tutorial is to encourage you to think about how to make better use of gestures in your own applications, rather than to create a clone of Clear. I encourage you to download and buy Clear, as it is a truly inspirational app.

Anyhow, I think it’s time I climbed down from my soapbox and showed you all some code!

Overview

There are several steps in this tutorial, so it might be helpful to see the overall plan before you begin. You’ll be completing 7 checkpoints:

  1. You’ll start by creating a basic UITableView with a UIViewController named ViewController that conforms to UITableViewDataSource protocol, displaying a hard-coded list of ToDoItems.
  2. Then you’ll set ViewController to conform to UITableViewDelegate protocol, and implement UITableViewDelegate methods to modulate row color from red to yellow.
  3. To fine-tune the “look”, you’ll create a custom UITableViewCell named TableViewCell, where you’ll implement a gradient effect within each table row.
  4. The first gesture you’ll implement is swipe-left-to-delete, and the first step is to add a UIPanGestureRecognizer to TableViewCell, and detect this delete gesture.
  5. TableViewCell needs to delegate the actual deletion from ToDoItems, so you’ll create a protocol named TableViewCellDelegate for ViewController to adopt
  6. Next, you’ll handle swipe-right-to-complete, which layers a strikethrough over the row text, and also changes the row color to green: you’ll implement this by writing a custom UILabel named StrikeThroughText.
  7. As a final polish, you’ll implement TableViewCell properties crossLabel and tickLabel, to display contextual cues while the user is swiping left or right. They’ll change color (red cross for delete, green tick for complete) when the user has dragged the row far enough left or right.

Getting Started

This first Checkpoint is just a basic table of to-do items so, if you’re already comfortable with doing that, download this starter project and skip down to Styling Your Cells

If you want some practice, fire up Xcode and create a new iPhone application by going to File\New\Project, selecting the iOS\Application\Single View Application template and tapping Next. On the next screen, enter ClearStyle as the product name, and fill in the other details similar to the image below:

NewProject

The standard approach to rendering scrollable lists within an iPhone application is to use a UITableView, so you’ll add a Table View to the storyboard view and connect it to an outlet in the view controller that was created in the project template.

Open Main.storyboard and drag a Table View onto the scene (view), positioning it so that it covers the whole scene:

StoryboardTableView

To ensure tableView always takes up the entire screen, pin its top, bottom, left and right edges to be 0 points from the parent view.

Next, connect the Table View to an outlet in ViewController.swift: show the Assistant Editor, select the Table View, control-drag from it into ViewController.swift, just inside the class block, and name the outlet tableView:

StoryboardOutlet

Delete the didReceiveMemoryWarning method.

Note:The eagle-eyed among you might be wondering why I used a UITableView within a UIViewController – why not use a UITableViewController? Well, I originally wrote this with a UITableViewController and everything worked fine until the very last step of Part 2, when it became unstable. The version I wrote with a UIViewController still worked well, so it’s possible there’s a subtle bug in Swift.

A to-do list is essentially a list of items rendered on the screen, so you need to create an object that represents each to-do item. In the Project Navigator, right-click ViewController.swift and select New File…, then select the iOS\Source\Cocoa Touch Class template and add a class called ToDoItem. Make it a subclass of NSObject, set Language to Swift, but don’t create a XIB file:

AddToDoItemClass

Open ToDoItem.swift and add two properties and an init method (between the curly braces { }):

// A text description of this item.
var text: String
 
// A Boolean value that determines the completed state of this item.
var completed: Bool
 
// Returns a ToDoItem initialized with the given text and default completed value. 
init(text: String) {
    self.text = text
    self.completed = false
}

A to-do item is simply a string of text and a Boolean that indicates whether the item is completed or not.

Note: Selecting a file in the Project Navigator before you create a New File causes Xcode to place the new file just below the file that you selected.

Open ViewController.swift and, in viewDidLoad, declare and initialize the toDoItems array:

class ViewController: UIViewController {
 
    @IBOutlet weak var tableView: UITableView!
    var toDoItems = [ToDoItem]()
 
    override func viewDidLoad() {
        super.viewDidLoad()
 
        if toDoItems.count > 0 {
            return
        }
        toDoItems.append(ToDoItem(text: "feed the cat"))
        toDoItems.append(ToDoItem(text: "buy eggs"))
        toDoItems.append(ToDoItem(text: "watch WWDC videos"))
        toDoItems.append(ToDoItem(text: "rule the Web"))
        toDoItems.append(ToDoItem(text: "buy a new iPhone"))
        toDoItems.append(ToDoItem(text: "darn holes in socks"))
        toDoItems.append(ToDoItem(text: "write this tutorial"))
        toDoItems.append(ToDoItem(text: "master Swift"))
        toDoItems.append(ToDoItem(text: "learn to draw"))
        toDoItems.append(ToDoItem(text: "get more exercise"))
        toDoItems.append(ToDoItem(text: "catch up with Mom"))
        toDoItems.append(ToDoItem(text: "get a hair cut"))
    }

Next, add the required UITableViewDataSource methods:

// MARK: - Table view data source
 
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return 1
}
 
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return toDoItems.count
}
 
func tableView(tableView: UITableView,
    cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("cell", 
            forIndexPath: indexPath) as UITableViewCell
        let item = toDoItems[indexPath.row]
        cell.textLabel!.text = item.text
        return cell
}

Then declare that ViewController conforms to UITableViewDataSource and UITableViewDelegate protocols:

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

And finish configuring tableView in viewDidLoad by adding these lines just below super.viewDidLoad:

tableView.dataSource = self
tableView.delegate = self
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")

And with that, Checkpoint 1 is done! Set the active scheme to iPhone 5, build and run your code, and you will be presented with the wonderfully minimalist to-do list shown below:

ToDoListFirstCut

Styling Your Cells

Before you start adding gestures, the next two steps make the list a little bit easier on the eyes. :]

You’ll use color to separate the table rows so, in ViewController‘s viewDidLoad, set the tableView‘s separator style to None. While you’re there, make the rows a little bigger to increase readability:

tableView.separatorStyle = .None
tableView.rowHeight = 50.0

Note: If you are planning to support versions of iOS prior to iOS 8, you may also need to implement heightForRowAtIndexPath. Simply returning rowHeight will be sufficient, as in the code below:

func tableView(tableView: UITableView, heightForRowAtIndexPath 
    indexPath: NSIndexPath) -> CGFloat {
    return tableView.rowHeight;
}

The UIViewController class also conforms to UITableViewDelegate. Add the code below to the end of ViewController.swift to set the background color of each row, adding slightly more green as you go:

// MARK: - Table view delegate
 
func colorForIndex(index: Int) -> UIColor {
    let itemCount = toDoItems.count - 1
    let val = (CGFloat(index) / CGFloat(itemCount)) * 0.6
    return UIColor(red: 1.0, green: val, blue: 0.0, alpha: 1.0)
}
 
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, 
                        forRowAtIndexPath indexPath: NSIndexPath) {
    cell.backgroundColor = colorForIndex(indexPath.row)
}

The color returned by colorForIndex(index:) creates a gradient effect from red to yellow, just for aesthetic purposes. Build and run the app again to see that you’ve completed Checkpoint 2:

ToDoListColored

The current implementation sets a specific color for each row. While the overall effect is a gradient color change as the user scrolls down, it’s hard to tell where one cell begins and another ends, especially towards the top.

So the next step is to add a gradient effect to each cell (i.e., row) so that it’s easier to tell the cells apart. You could easily modify the cell’s appearance in the datasource or delegate methods that you have already implemented, but a much more elegant solution is to subclass UITableViewCell and customize the cell directly.

Add a new class to the project with the iOS\Source\Cocoa Touch Class template. Name the class TableViewCell and make it a subclass of UITableViewCell. Make sure you uncheck the option to create a XIB file and set the Language to Swift.

AddCustomCell

Replace the contents of TableViewCell.swift with the following:

import UIKit
import QuartzCore
 
class TableViewCell: UITableViewCell {
 
    let gradientLayer = CAGradientLayer()
 
    required init(coder aDecoder: NSCoder) {
        fatalError("NSCoding not supported")
    }
 
    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
 
        // gradient layer for cell
        gradientLayer.frame = bounds
        let color1 = UIColor(white: 1.0, alpha: 0.2).CGColor as CGColorRef
        let color2 = UIColor(white: 1.0, alpha: 0.1).CGColor as CGColorRef
        let color3 = UIColor.clearColor().CGColor as CGColorRef
        let color4 = UIColor(white: 0.0, alpha: 0.1).CGColor as CGColorRef
        gradientLayer.colors = [color1, color2, color3, color4]
        gradientLayer.locations = [0.0, 0.01, 0.95, 1.0]
        layer.insertSublayer(gradientLayer, atIndex: 0)
    }
 
    override func layoutSubviews() {
        super.layoutSubviews()
        gradientLayer.frame = bounds
    }
}

Here you add a CAGradientLayer property and create a four-step gradient within the init method. Notice that the gradient is a transparent white at the very top, and a transparent black at the very bottom. This will be overlaid on top of the existing color background, lightening the top and darkening the bottom, to create a neat bevel effect simulating a light source shining down from the top.

Note: Still trying to get your head wrapped around how to properly shade user interfaces and other graphics to simulate lighting? Check out this lighting tutorial by Vicki.

Also notice that layoutSubviews has been overridden. This is to ensure that the newly-added gradient layer always occupies the full bounds of the frame.

Now you need to switch over to using your new custom UITableView cell in your code! Only two steps are required:

Step 1: In ViewController.swift‘s viewDidLoad, change the cell class from UITableViewCell to TableViewCell:

tableView.registerClass(TableViewCell.self, forCellReuseIdentifier: "cell")

This tells the tableView to use the TableViewCell class whenever it needs a cell with reuse identifier “cell”.

Step 2: Change the cell class cast in cellForRowAtIndexPath to TableViewCell, as follows:

let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) 
    as TableViewCell

That’s it! Since you register the class to be used to create a new table view cell in viewDidLoad(), when tableView:cellForRowAtIndexPath: next needs a table cell, your new class will be used automatically. :]

Build and run your app, and check off Checkpoint 3: your to-do items should now have a subtle gradient, making it much easier to differentiate between individual rows:

ToDoListGradient

Swipe-to-Delete

Now that your list is presentable, it’s time to add your first gesture. This is an exciting moment!

Multi-touch devices provide app developers with complex and detailed information regarding user interactions. As each finger is placed on the screen, its position is tracked and reported to your app as a series of touch events. Mapping these low-level touch events to higher-level gestures, such as pan or a pinch, is quite challenging.

A finger is not exactly the most accurate pointing device! And as a result, gestures need to have a built-in tolerance. For example, a user’s finger has to move a certain distance before a gesture is considered a pan.

Fortunately, the iOS framework provides a set of gesture recognizers that has this all covered. These handy little classes manage the low-level touch events, saving you from the complex task of identifying the type of gesture, and allowing you to focus on the higher-level task of responding to each gesture.

This tutorial will skip over the details, but if you want to learn more check out our UIGestureRecognizer tutorial.

Two small tweaks in ViewController.swift before you really get stuck into this: add this line to viewDidLoad, just after you set tableView.separatorStyle:

tableView.backgroundColor = UIColor.blackColor()

This makes the tableView black, under the cell you’re dragging.

And add this line in cellForRowAtIndexPath, after the line that creates the cell:

cell.selectionStyle = .None

This gets rid of the highlighting that happens when you select a table cell.

Open TableViewCell.swift and add the following code at the end of the overridden init method:

// add a pan recognizer
var recognizer = UIPanGestureRecognizer(target: self, action: "handlePan:")
recognizer.delegate = self
addGestureRecognizer(recognizer)

This code adds a pan gesture recognizer to your custom table view cell, and sets the cell itself as the recognizer’s delegate. Any pan events will be sent to handlePan but, before adding that method, you need to set up two properties that it will use.

Add these two properties at the top of TableViewCell.swift, right below the existing gradientLayer property:

var originalCenter = CGPoint()
var deleteOnDragRelease = false

Now add the implementation for handlePan, at the end of TableViewCell.swift:

//MARK: - horizontal pan gesture methods
func handlePan(recognizer: UIPanGestureRecognizer) {
  // 1
  if recognizer.state == .Began {
    // when the gesture begins, record the current center location
    originalCenter = center
  }
  // 2
  if recognizer.state == .Changed {
    let translation = recognizer.translationInView(self)
    center = CGPointMake(originalCenter.x + translation.x, originalCenter.y)
    // has the user dragged the item far enough to initiate a delete/complete?
    deleteOnDragRelease = frame.origin.x < -frame.size.width / 2.0
  }
  // 3
  if recognizer.state == .Ended {
    // the frame this cell had before user dragged it
    let originalFrame = CGRect(x: 0, y: frame.origin.y,
        width: bounds.size.width, height: bounds.size.height)
    if !deleteOnDragRelease {
      // if the item is not being deleted, snap back to the original location
      UIView.animateWithDuration(0.2, animations: {self.frame = originalFrame})
    }
  }
}

There’s a fair bit going on in this code. Let’s go through handlePan, section by section.

  1. Gesture handlers, such as this method, are invoked at various points within the gesture lifecycle: the start, change (i.e., when a gesture is in progress), and end. When the pan first starts, the center location of the cell is recorded in originalCenter.
  2. As the pan gesture progresses (as the user moves their finger), the method determines the offset that should be applied to the cell (to show the cell being dragged) by getting the new location based on the gesture, and offsetting the center property accordingly. If the offset is greater than half the width of the cell, you consider this to be a delete operation. The deleteOnDragRelease property acts as a flag that indicates whether or not the operation is a delete.
  3. And of course, when the gesture ends, you check the flag to see if the action was a delete or not (the user might have dragged the cell more than halfway and then dragged it back, effectively nullifying the delete operation).

Next, give the recognizer’s delegate (the table view cell) something to do by adding this UIGestureRecognizerDelegate method, below handlePan:

override func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
    if let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer {
        let translation = panGestureRecognizer.translationInView(superview!)
        if fabs(translation.x) > fabs(translation.y) {
            return true
        }
        return false
    }
    return false
}

This delegate method allows you to cancel the recognition of a gesture before it has begun. In this case, you determine whether the pan that is about to be initiated is horizontal or vertical. If it is vertical, you cancel the gesture recognizer, since you don’t want to handle any vertical pans.

This is a very important step! Your cells are hosted within a vertically scrolling view. Failure to cancel a vertical pan renders the scroll view inoperable, and the to-do list will no longer scroll.

Build and run this code, and you should find that you can now drag the items left or right. When you release, the item snaps back to the center, unless you drag it more than halfway across the screen to the left, indicating that the item should be deleted:

PseudoDelete

Note: If you find you have to drag an item much more than halfway across to the left, to get it to stick, you need to set constraints on the Table View, to stop it from overflowing the device’s window – in Main.storyboard, select the View Controller then click the Resolve Auto Layout Issues button and select All Views in View Controller\Reset to Suggested Constraints. This will make the Table View fit into the device’s window.

StoryboardResetConstraints

And that’s Checkpoint 4 done! Of course, you’ll notice that the cell just gets stuck and doesn’t actually disappear, and if you scroll it off the screen then back again, the item is still there – to complete this swipe-to-delete gesture, you need to remove the item from your list and reload the table.

The to-do items are stored in an Array within your view controller. So you need to find some way to signal to the view controller that an item has been deleted and should be removed from this array.

UI controls use protocols to indicate state change and user interactions. You can adopt the same approach here.

To add a new protocol to the project, add this code to TableViewCell.swift, below the import statements but above the class TableViewCell block:

// A protocol that the TableViewCell uses to inform its delegate of state change
protocol TableViewCellDelegate {
    // indicates that the given item has been deleted
    func toDoItemDeleted(todoItem: ToDoItem)
}

This code defines a protocol with a required method that indicates an item has been deleted.

The TableViewCell class needs to expose this delegate, and it also needs to know which model item (i.e., ToDoItem) it is rendering, so that it can pass this item to its delegate.

Add these two properties near the top of TableViewCell.swift, just below the definitions of originalCenter and deleteOnDragRelease:

// The object that acts as delegate for this cell.
var delegate: TableViewCellDelegate?
// The item that this cell renders.
var toDoItem: ToDoItem?

You declare these properties as optionals, because you’ll set their values in ViewController.swift, not in TableViewCell‘s init method.

In order to use this delegate, update the logic for handlePan in TableViewCell.swift by adding the following code to the end of the if recognizer.state == .Ended block:

if deleteOnDragRelease {
    if delegate != nil && toDoItem != nil {
        // notify the delegate that this item should be deleted
        delegate!.toDoItemDeleted(toDoItem!)
    }
}

The above code invokes the delegate method if the user has dragged the item far enough.

Now it’s time to make use of the above changes. Switch to ViewController.swift and, just above the // MARK: - Table view delegate line, add an implementation for the TableViewCellDelegate method toDoItemDeleted, to delete an item when notified:

func toDoItemDeleted(toDoItem: ToDoItem) {
  let index = (toDoItems as NSArray).indexOfObject(toDoItem)
  if index == NSNotFound { return }
 
  // could removeAtIndex in the loop but keep it here for when indexOfObject works
  toDoItems.removeAtIndex(index)
 
  // use the UITableView to animate the removal of this row
  tableView.beginUpdates()
  let indexPathForRow = NSIndexPath(forRow: index, inSection: 0)
  tableView.deleteRowsAtIndexPaths([indexPathForRow], withRowAnimation: .Fade)
  tableView.endUpdates()    
}

The above code removes the to-do item, and then uses the UITableView to animate the deletion, using one of its stock effects.

Next, scroll up to the top of ViewController.swift and declare that this class conforms to your new protocol:

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, TableViewCellDelegate {

Finally, add the following lines to the end of cellForRowAtIndexPath (right before the return statement) to set the TableViewCell‘s delegate and toDoItem properties:

cell.delegate = self
cell.toDoItem = item

Build and run your project, and delete some items, to check off Checkpoint 5:

RealDelete

Only two more Checkpoints to go! This next one is pretty long and a bit intense, with a small DIY exercise, so feel free to take a short coffee/tea break before we continue…

Swipe-to-Complete

Your to-do list application allows the user to delete items, but what about marking them as complete? For this, you’ll use a swipe-right gesture.

When an item is marked as complete, it should be rendered with a green background and strikethrough text. Unfortunately, iOS does not support strikethrough text rendering, so you are going to have to implement this yourself!

There are a few implementations of a UILabel with a strikethrough effect on StackOverflow, but all of them use drawRect and Quartz 2D to draw the strikethrough. I much prefer using layers for this sort of thing, since they make the code easier to read, and the layers can be conveniently turned on and off via their hidden property.

So you’re going to create a custom UILabel with a strikeThroughLayer and a strikeThrough flag, add this custom label to your custom cell and, in handlePan, set strikeThrough to true if the user drags the cell more than halfway to the right.

First, create a New File with the iOS\Source\Cocoa Touch Class template. Name the class StrikeThroughText, and make it a subclass of UILabel, with Language set to Swift.

Open StrikeThroughText.swift and replace its contents with the following:

import UIKit
import QuartzCore
 
// A UILabel subclass that can optionally have a strikethrough.
class StrikeThroughText: UILabel {
    let strikeThroughLayer: CALayer
    // A Boolean value that determines whether the label should have a strikethrough.
    var strikeThrough : Bool {
        didSet {
            strikeThroughLayer.hidden = !strikeThrough
            if strikeThrough {
                resizeStrikeThrough()
            }
        }
    }
 
    required init(coder aDecoder: NSCoder) {
        fatalError("NSCoding not supported")
    }
 
    override init(frame: CGRect) {
        strikeThroughLayer = CALayer()
        strikeThroughLayer.backgroundColor = UIColor.whiteColor().CGColor
        strikeThroughLayer.hidden = true
        strikeThrough = false
 
        super.init(frame: frame)
        layer.addSublayer(strikeThroughLayer)
    }
 
    override func layoutSubviews() {
        super.layoutSubviews()
        resizeStrikeThrough()
    }
 
    let kStrikeOutThickness: CGFloat = 2.0
    func resizeStrikeThrough() {
        let textSize = text!.sizeWithAttributes([NSFontAttributeName:font])
        strikeThroughLayer.frame = CGRect(x: 0, y: bounds.size.height/2, 
                           width: textSize.width, height: kStrikeOutThickness)
    }
}

strikeThroughLayer is basically a white layer that is re-positioned (by resizeStrikeThrough) according to the size of the rendered text. This layer is hidden if strikeThrough is false, and visible if strikeThrough is true. The resizeStrikeThrough method is called when strikeThrough gets set to true.

Note: Follow the next instructions carefully with regards to their order and position within the init method. You are about to create a property that is not optional. Remember that in Swift, properties that are not optional must be initialized before calling super.init.

OK, so you have your strikethrough label, but it needs to be added to your custom cell. Do that by opening TableViewCell.swift and adding two properties right below the property for deleteOnDragRelease:

let label: StrikeThroughText
let itemCompleteLayer = CALayer()

Initialize label (and get rid of the red error flag!) by adding the following code at the top of the init method, before the call to super.init:

// create a label that renders the to-do item text
label = StrikeThroughText(frame: CGRect.nullRect)
label.textColor = UIColor.whiteColor()
label.font = UIFont.boldSystemFontOfSize(16)
label.backgroundColor = UIColor.clearColor()

Configure the cell by adding these lines after the call to super.init:

addSubview(label)
// remove the default blue highlight for selected cells
selectionStyle = .None

Still in init, add the following code right before you add the pan recognizer:

// add a layer that renders a green background when an item is complete
itemCompleteLayer = CALayer(layer: layer)
itemCompleteLayer.backgroundColor = UIColor(red: 0.0, green: 0.6, blue: 0.0, 
                                            alpha: 1.0).CGColor
itemCompleteLayer.hidden = true
layer.insertSublayer(itemCompleteLayer, atIndex: 0)

The above code adds both the strikethrough label and a solid green layer to your custom cell that will be shown when an item is complete.

Now replace the existing code for layoutSubviews with the following, to layout your new itemCompleteLayer and label:

let kLabelLeftMargin: CGFloat = 15.0
override func layoutSubviews() {
    super.layoutSubviews()
    // ensure the gradient layer occupies the full bounds
    gradientLayer.frame = bounds
    itemCompleteLayer.frame = bounds
    label.frame = CGRect(x: kLabelLeftMargin, y: 0, 
                         width: bounds.size.width - kLabelLeftMargin, 
                         height: bounds.size.height)   
}

Next, add the following didSet observer for the todoItem property. This will ensure the strikethrough label stays in sync with the toDoItem.

var toDoItem: ToDoItem? {
    didSet {
        label.text = toDoItem!.text
        label.strikeThrough = toDoItem!.completed
        itemCompleteLayer.hidden = !label.strikeThrough
    }
}

Now that you’ve set the label’s text within the didSet observer, you no longer need to set it in cellForRowAtIndexPath. Open ViewController.swift and comment out that line of code:

//cell.textLabel.text = item.text;

The final thing you need to do is detect when the cell is dragged more than halfway to the right, and set the completed property on the to-do item. This is pretty similar to handling the deletion – so would you like to try that on your own? You would? OK, I’ll wait for you to give it a shot, go ahead!

…waiting…

…waiting…

…waiting…

Did you get it working? If not, take a peek:

Solution Inside: Completed Item Solution SelectShow>

All done! Now you can swipe items to complete or delete. The newly added green layer sits behind your gradient layer, so that the completed rows still have that subtle shading effect.

Build and run, and it should look something like this:

Complete

You’ve finished Checkpoint 6 and it’s starting to look sweet!

Contextual Cues

The to-do list now has a novel, clutter-free interface that is easy to use… once you know how. One small problem with gesture-based interfaces is that their functions are not as immediately obvious to the end user, as opposed to their more classic skeuomorphic counterparts.

One thing you can do to aid a user’s understanding of a gesture-based interface, without compromising on simplicity, is to add contextual cues. For a great article on contextual cues, I recommend reading this blog post by Graham Odds, which includes a number of examples.

Contextual cues often communicate functionality and behavior to the user by reacting to the user’s movements. For example, the mouse pointer on a desktop browser changes as the user moves their mouse over a hyperlink.

The same idea can be used on a touch- or gesture-based interface. When a user starts to interact with the interface, you can provide subtle visual cues that encourage further interaction and indicate the function that their gesture will invoke.

For your to-do app, a simple tick or cross that is revealed as the user pulls an item left or right will serve to indicate how to delete or mark an item as complete. So go right ahead and add them!

Add two UILabel properties to TableViewCell.swift, just below deleteOnDragRelease:

var tickLabel: UILabel!, crossLabel: UILabel!

Declare these as unwrapped optionals, as you can’t create them until after the call to super.init (because you’ll be calling a new instance method createCueLabel to create them).

Next, define a couple of constant values (that you’ll use soon), just above layoutSubviews:

let kUICuesMargin: CGFloat = 10.0, kUICuesWidth: CGFloat = 50.0

Now add the createCueLabel method for creating the cue labels, just below the layoutSubviews method:

// utility method for creating the contextual cues
func createCueLabel() -> UILabel {
    let label = UILabel(frame: CGRect.nullRect)
    label.textColor = UIColor.whiteColor()
    label.font = UIFont.boldSystemFontOfSize(32.0)
    label.backgroundColor = UIColor.clearColor()
    return label
}

And initialize your new cue labels in init by adding the following code right before the itemCompleteLayer lines:

// tick and cross labels for context cues
tickLabel = createCueLabel()
tickLabel.text = "\u{2713}"
tickLabel.textAlignment = .Right
addSubview(tickLabel)
crossLabel = createCueLabel()
crossLabel.text = "\u{2717}"
crossLabel.textAlignment = .Left
addSubview(crossLabel)

Well, that’s how I wrote this code the first time around, but something didn’t feel quite right … and then I remembered that Swift lets you nest functions! So I moved some lines around, to initialize tickLabel and crossLabel before calling super.init, and got rid of those pesky exclamation marks! Can you figure out what to do without peeking?

Solution Inside: No Unwrapped Optionals Here! SelectShow>

Rather than using image resources for the tick and cross icons, the above code uses a couple of Unicode characters. You could probably find some better images for this purpose, but these characters give us a quick and easy way of implementing this effect, without adding the overhead of images.

Note: Wondering how I knew these unicode values represented a checkmark and a cross mark? Check out this handy list of useful Unicode symbols!

Now, add the following code to the end of layoutSubviews to relocate these labels:

tickLabel.frame = CGRect(x: -kUICuesWidth - kUICuesMargin, y: 0, 
                         width: kUICuesWidth, height: bounds.size.height)
crossLabel.frame = CGRect(x: bounds.size.width + kUICuesMargin, y: 0, 
                          width: kUICuesWidth, height: bounds.size.height)

The above code positions the labels off screen, the tick to the left and the cross to the right.

Finally, add the code below to handlePan, at the end of the if recognizer.state == .Changed block, in order to adjust the alpha of the labels as the user drags the cell:

// fade the contextual clues
let cueAlpha = fabs(frame.origin.x) / (frame.size.width / 2.0)
tickLabel.alpha = cueAlpha
crossLabel.alpha = cueAlpha
// indicate when the user has pulled the item far enough to invoke the given action
tickLabel.textColor = completeOnDragRelease ? UIColor.greenColor() : UIColor.whiteColor()
crossLabel.textColor = deleteOnDragRelease ? UIColor.redColor() : UIColor.whiteColor()

The cue is further reinforced by changing the color of the tick/cross to indicate when the user has dragged the item far enough – as you’ll notice when you build and run the app again:

Cues

And with that final feature, you’ve finished Checkpoint 7 and the first part of this two-part series!

Where To Go From Here?

Here’s an example project containing all the source code from this part of the series.

What next? So far, the app only allows the user to mark items as complete or to delete them. There are clearly more gestures and features that need to be added in order to make this a fully usable application.

However, I am not too keen on the “stock” delete animation provided by UITableView. I’m sure this could be done in a slightly more eye-catching way.

Unfortunately, there is a limit to how much you can extend the UITableView, which is why part two of this series replaces this control with your own custom implementation. But since it’s a fairly large topic, you’ll have to wait for part two to find out all about it. :]

In the meantime, why not think about your own applications and how you can replace the existing controls with more interesting and natural gestures. Also, if you do use gestures, don’t forget to think about how to help your users discover them, and the possibility of using contextual cues.

And if you have any questions or comments about what you’ve done so far, please join the forum discussion below!

Making a Gesture-Driven To-Do List App Like Clear in Swift: Part 1/2 is a post from: Ray Wenderlich

The post Making a Gesture-Driven To-Do List App Like Clear in Swift: Part 1/2 appeared first on Ray Wenderlich.

Making a Gesture-Driven To-Do List App Like Clear in Swift: Part 2/2

$
0
0
Learn how to make a stylish gesture driven to-do app like Clear!

Learn how to make a stylish gesture driven to-do app like Clear!

Update note: This tutorial was fully updated for iOS 8 and Swift by Audrey Tam. Originally posted as Part 2/3 and Part 3/3 by Colin Eberhardt.

This is the second in a two-part tutorial series that takes you through developing a to-do list app that is completely free of buttons, toggle switches and other common, increasingly outdated user interface (UI) controls.

It’s nothing but swipes, pulls and pinches for this app! As I’m sure you’ve realized if you’ve been following along, that leaves a lot more room for content.

If you followed the first part of the tutorial, you should now have a stylish and minimalistic to-do list interface. Your users can mark items as complete by swiping them to the right, or delete them by swiping to the left.

Before moving on to adding more gestures to the app, this part of the tutorial will show you how to make a few improvements to the existing interactions.

Right now, the animation that accompanies a delete operation is a “stock” feature of UITableView – when an item is deleted, it fades away, while the items below move up to fill the space. This effect is a little jarring, and the animation a bit dull.

How about if instead, the deleted item continued its motion to the right, while the remaining items shuffled up to fill the space?

Ready to see how easy it can be to do one better than Apple’s stock table view animations? Let’s get started!

A Funky Delete Animation

This part of the tutorial continues on from the previous one. If you did not follow Part 1, or just want to jump in at this stage, make sure you download the code from the first part, since you’ll be building on it in this tutorial.

Open ViewController.swift and find toDoItemDeleted. Presently, the code for animating the deletion of a to-do item is as follows:

tableView.beginUpdates()
let indexPathForRow = NSIndexPath(forRow: index, inSection: 0)
tableView.deleteRowsAtIndexPaths([indexPathForRow], withRowAnimation: .Fade)
tableView.endUpdates()

This uses the “stock” UITableViewRowAnimation.Fade effect, which is a bit boring! I’d much prefer the application to use a more eye-catching animation, where the items shuffle upwards to fill the space that was occupied by the deleted item.

The UITableView manages the lifecycle of your cells, so how do you manually animate their location? It’s surprisingly easy! UITableView includes the visibleCells method, which returns an array of all the cells that are currently visible. You can iterate over these items and do what you like with them!

So, let’s replace the stock animation with something a bit more exciting.

You’re going to use block-based animations, as described in detail in our How to Use UIView Animation tutorial. In ViewController.swift), replace the current todoItemDeleted implementation with the following:

func toDoItemDeleted(toDoItem: ToDoItem) {
  let index = (toDoItems as NSArray).indexOfObject(toDoItem)
  if index == NSNotFound { return }
 
  // could removeAtIndex in the loop but keep it here for when indexOfObject works
  toDoItems.removeAtIndex(index)
 
  // loop over the visible cells to animate delete
  let visibleCells = tableView.visibleCells() as [TableViewCell]
  let lastView = visibleCells[visibleCells.count - 1] as TableViewCell
  var delay = 0.0
  var startAnimating = false
  for i in 0..<visibleCells.count {
    let cell = visibleCells[i]
    if startAnimating {
      UIView.animateWithDuration(0.3, delay: delay, options: .CurveEaseInOut,
        animations: {() in
          cell.frame = CGRectOffset(cell.frame, 0.0, 
                       -cell.frame.size.height)},
        completion: {(finished: Bool) in
          if (cell == lastView) {
            self.tableView.reloadData()
          }
        }
      )
      delay += 0.03
    }
    if cell.toDoItem === toDoItem {
      startAnimating = true
      cell.hidden = true
    }
  }
 
  // use the UITableView to animate the removal of this row
  tableView.beginUpdates()
  let indexPathForRow = NSIndexPath(forRow: index, inSection: 0)
  tableView.deleteRowsAtIndexPaths([indexPathForRow], withRowAnimation: .Fade)
  tableView.endUpdates()
}

The code above is pretty simple. It iterates over the visible cells until it reaches the one that was deleted. From that point on, it applies an animation to each cell. The animation block moves each cell up by the height of one row, with a delay that increases with each iteration.

The effect that is produced is shown in the animated gif below – the original version of this app had a problem with the green completed items flickering if you deleted an item above them, so this gif shows that this doesn’t happen now:

DeleteAnimation

That’s pretty groovy, right?

Just a note about reloadData: You might have noticed in the code above that when the animation for the very last cell completes, it calls reloadData on the UITableView. Why is this?

As mentioned previously, UITableView manages the cell lifecycle and position where cells are rendered onscreen. Moving the location of the cells, as you have done here with the delete animation, is something that the UITableView was not designed to accommodate.

If you remove the call to reloadData, delete an item, then scroll the list, you will find that the UI becomes quite unstable, with cells appearing and disappearing unexpectedly.

By sending the reloadData message to the UITableView, this issue is resolved. reloadData forces the UITableView to “dispose” of all of the cells and re-query the datasource. As a result, the cells are all located where the UITableView expects them to be.

Editing Items

Currently the to-do items are rendered using a UILabel subclass – StrikeThroughText. In order to make the items editable, you need to switch to UITextField instead.

Fortunately, this is a very easy change to make. Simply edit StrikeThroughText.swift and, in its opening class line change the superclass from UILabel to UITextField:

class StrikeThroughText: UITextField {

Unfortunately, UITextField is a little dumb, and hitting Return (or Enter) does not close the keyboard. So you have to do a bit more work here if you don’t want to be stuck with a keyboard over half of your nice, snazzy UI. :]

Switch to TableViewCell.swift and change its opening class line as follows:

class TableViewCell: UITableViewCell, UITextFieldDelegate {

Since TableViewCell contains the StrikeThroughText instance, you set it to conform to the UITextFieldDelegate protocol so that the text field notifies the table cell when the user taps Return on the keyboard. (Because StrikeThroughText is now a subclass of UITextField, it contains a delegate property that expects a class that conforms to UITextFieldDelegate.)

Still in TableViewCell.swift, add the following code to the init method, right after the call to super.init:

label.delegate = self
label.contentVerticalAlignment = .Center

The above code sets up the label’s delegate to be the TableViewCell instance. It also sets the control to center vertically within the cell. If you omit the second line, you’ll notice that the text now displays aligned to the top of each row. That just doesn’t look right. :]

Now all you need to do is implement the relevant UITextFieldDelegate methods. Add the following code:

// MARK: - UITextFieldDelegate methods
 
func textFieldShouldReturn(textField: UITextField!) -> Bool {
  // close the keyboard on Enter
  textField.resignFirstResponder()
  return false
}
 
func textFieldShouldBeginEditing(textField: UITextField!) -> Bool {
  // disable editing of completed to-do items
  if toDoItem != nil {
    return !toDoItem!.completed
  }
  return false
}
 
func textFieldDidEndEditing(textField: UITextField!) {
  if toDoItem != nil {
    toDoItem!.text = textField.text
  }
}

The above code is pretty self-explanatory, since all it does is close the keyboard when the user taps Enter, not allow the cell to be edited if the item has already been completed, and set the to-do item text once the editing completes.

Build, run, and enjoy the editing experience!

EditingItems

Note: If the Simulator is using your Mac’s keyboard instead of displaying an iPhone keyboard, select Hardware\Keyboard\Toggle Software Keyboard or press Command-K to bring up the software keyboard.

After a little bit of testing, you will probably notice one small issue. If you edit an item that is in the bottom half of the screen (or less than half for you lucky iPhone 5 or 6 owners!), when the keyboard appears, it covers the item you are editing.

This does not lead to a good user experience. The easiest way to fix this behavior is to scroll the cell being edited to the top of the list. Unfortunately, for cells at the very bottom simply setting the table’s contentOffset won’t work, as the table will always keep some cells behind the keyboard. Instead, you’ll mimic a table scroll with a translation transform on all the visible cells. But first, you’ll need the ViewController to know about the edit lifecycle. The edit lifecycle is currently only visible to the TableViewCell, but you can expose it via its protocol.

Open ViewController.swift and add this MARK group above the toDoItemDeleted method:

// MARK: - TableViewCellDelegate methods

Then add two empty methods below the toDoItemDeleted method:

func cellDidBeginEditing(editingCell: TableViewCell) {
 
}
 
func cellDidEndEditing(editingCell: TableViewCell) {
 
}

These will become TableViewCellDelegate editing lifecycle methods: the first will move the visible rows so that editingCell is at the top, while making the other rows more transparent; the second will move the rows back, restoring the other rows to totally opaque. Open TableViewCell.swift and declare these two methods in the protocol TableViewCellDelegate block:

// Indicates that the edit process has begun for the given cell
func cellDidBeginEditing(editingCell: TableViewCell)
// Indicates that the edit process has committed for the given cell
func cellDidEndEditing(editingCell: TableViewCell)

These protocol methods are simply invoked when the relevant UITextFieldDelegate method is invoked in TableViewCell.swift. Add the UITextFieldDelegate method textFieldDidBeginEditing to TableViewCell.swift:

func textFieldDidBeginEditing(textField: UITextField!) {
  if delegate != nil {
    delegate!.cellDidBeginEditing(self)
  }
}

And in textFieldDidEndEditing, add a call to the cellDidEndEditing delegate method:

func textFieldDidEndEditing(textField: UITextField!) {
  if toDoItem != nil {
    toDoItem!.text = textField.text
  }
  if delegate != nil {
    delegate!.cellDidEndEditing(self)
  }
}

At this point, it doesn’t matter whether you call cellDidEndEditing before, or after, setting the to-do item’s text property but, later in this tutorial, it might…

Now, add implementations for the new TableViewCellDelegate editing lifecycle methods:

func cellDidBeginEditing(editingCell: TableViewCell) {
  var editingOffset = tableView.contentOffset.y - editingCell.frame.origin.y as CGFloat
  let visibleCells = tableView.visibleCells() as [TableViewCell]
  for cell in visibleCells {
    UIView.animateWithDuration(0.3, animations: {() in
      cell.transform = CGAffineTransformMakeTranslation(0, editingOffset)
      if cell !== editingCell {
        cell.alpha = 0.3
      }
    })
  }
}
 
func cellDidEndEditing(editingCell: TableViewCell) {
  let visibleCells = tableView.visibleCells() as [TableViewCell]
  for cell: TableViewCell in visibleCells {
    UIView.animateWithDuration(0.3, animations: {() in
      cell.transform = CGAffineTransformIdentity
      if cell !== editingCell {
        cell.alpha = 1.0
      }
    })
  }
}

The above code animates the frame of every cell in the list in order to push the cell being edited to the top. The alpha is also reduced for all the cells other than the one being edited.

In some parts of this tutorial series, you move cells by changing their frame with CGRectOffset, whereas in the above code, you apply a transform instead. Using a transform has the big advantage that it is easy to move a cell back to its original location: you simply “zero” the translation (i.e., apply the identity), instead of having to store the original frame for each and every cell that is moved.

Build, run, and rejoice!

As a user starts editing an item, it is gracefully animated to the top of the screen. When the user hits Enter, the item gracefully slides back into place.

BetterEditMode

There is one glaring omission in the app’s functionality – the user cannot add new items to the list! Of course, I’m not sure that’s such a bad thing – I hate adding new to-dos to my never-ending list. :]

A conventional approach to this problem would most likely be to add a button with the text “Add new” on a title bar. But remember to ask yourself every time you want to add a new UI control: can I perform the same function via a gesture?

I’m guessing that you know the answer in this case, as in most cases, is YES!

The Pull-to-Add Gesture

The gestures that feel the most natural tend to play on the illusion that the phone UI is a physical object that obeys the same laws of physics as the natural world. Deleting an item from the to-do list by “pulling” it off the side of the screen feels quite natural, in the same way that you might swiftly pull a straw out in a game of KerPlunk.

The pull-down gesture has become ubiquitous in mobile apps as a means to refresh a list. The pull-down gesture feels very much like you are pulling against the natural resistance of the list, as if it were a hanging rope, in order to physically pull more items in from the top. Again, it is a natural gesture that in some way reflects how things work in the “real” world.

There has been some concern about the legality of using the pull-to-refresh gesture, due to Twitter’s user interface patent. However, the recent introduction of this feature in the iOS email application (with a gorgeous tear-drop effect), the iOS 6 SDK itself, and its popularity in the App Store means that developers are less concerned about this patent. And anyway, “Twitter agreed [with the inventor, Loren Brichter] to only use his patent defensively — the company wouldn’t sue other companies that were using pull-to-refresh in apps unless those companies sued first” (quotation from the linked theverge.com article).

Note: To learn more about iOS 6’s built-in pull-to-refresh control, check out Chapter 20 in iOS 6 by Tutorials, “What’s New with Cocoa Touch.”

Pulling down on the list to add a new item at the top is a great gesture to add to your to-do list application, so in this part of the tutorial, you’ll start with that!

You’ll add the new logic to ViewController – when you add more functionality, that class starts to get crowded, and it’s important to organize the properties and methods into logical groupings. Add a group for UIScrollViewDelegate methods, between the TableViewCellDelegate methods and the TableViewDelegate methods:

// MARK: - Table view data source
// contains numberOfSectionsInTableView, numberOfRowsInSection, cellForRowAtIndexPath
 
// MARK: - TableViewCellDelegate methods
// contains toDoItemDeleted, cellDidBeginEditing, cellDidEndEditing
 
// MARK: - UIScrollViewDelegate methods
// contains scrollViewDidScroll, and other methods, to keep track of dragging the scrollView
 
// MARK: - TableViewDelegate methods
// contains heightForRowAtIndexPath, willDisplayCell, and your helper method colorForIndex

In order to implement a pull-to-add gesture, you first have to detect when the user has started to scroll while at the top of the list. Then, as the user pulls further down, position a placeholder element that indicates where the new item will be added.

The placeholder can be an instance of TableViewCell, which renders each item in the list. So open ViewController.swift and add this line in the // MARK: – UIScrollViewDelegate methods group:

// a cell that is rendered as a placeholder to indicate where a new item is added
let placeHolderCell = TableViewCell(style: .Default, reuseIdentifier: "cell")

The above code simply sets up the property for the placeholder and initializes it.

Adding the placeholder when the pull gesture starts and maintaining its position is really quite straightforward. When dragging starts, check whether the user is currently at the start of the list, and if so, use a pullDownInProgress property to record this state.

Of course, you first have to add this new property to ViewController.swift (it goes right below the placeholderCell that you just declared):

// a cell that is rendered as a placeholder to indicate where a new item is added
let placeHolderCell = TableViewCell(style: .Default, reuseIdentifier: "cell")
// indicates the state of this behavior
var pullDownInProgress = false

Just below these two properties, add the UIScrollViewDelegate method necessary to detect the beginning of a pull:

func scrollViewWillBeginDragging(scrollView: UIScrollView!) {
  // this behavior starts when a user pulls down while at the top of the table
  pullDownInProgress = scrollView.contentOffset.y <= 0.0
  placeHolderCell.backgroundColor = UIColor.redColor()
  if pullDownInProgress {
    // add the placeholder
    tableView.insertSubview(placeHolderCell, atIndex: 0)
  }
}

If the user starts pulling down from the top of the table, the y-coordinate of the scrollView content’s origin goes from 0 to negative – this sets the pullDownInProgress flag to true. This code also sets the placeHolderCell‘s background color to red, then adds it to the tableView.

While a scroll is in progress, you need to reposition the placeholder by setting its frame in scrollViewDidScroll method. The values you need to set its frame are: x-, y-coordinates of its origin, width and height – x is 0, width is the same as the tableView.frame, but y and height depend on the cell height. In Part 1 of this tutorial, you used a constant row height by setting tableView.rowHeight, and you can use it in the method below.

Create a scrollViewDidScroll method as follows:

func scrollViewDidScroll(scrollView: UIScrollView!) {
  var scrollViewContentOffsetY = scrollView.contentOffset.y
 
  if pullDownInProgress && scrollView.contentOffset.y <= 0.0 {
    // maintain the location of the placeholder
    placeHolderCell.frame = CGRect(x: 0, y: -tableView.rowHeight,
        width: tableView.frame.size.width, height: tableView.rowHeight)
    placeHolderCell.label.text = -scrollViewContentOffsetY > tableView.rowHeight ?
        "Release to add item" : "Pull to add item"
    placeHolderCell.alpha = min(1.0, -scrollViewContentOffsetY / tableView.rowHeight)
  } else {
    pullDownInProgress = false
  }
}

Note: Swift requires a placeHolderCell.frame y-coordinate that is different from the Objective-C version of this app. In Objective-C, the placeHolderCell.frame y-coordinate is -scrollView.contentOffset.y - tableView.rowHeight, to keep it at the top of the existing table, but Swift’s y-coordinate is simply -tableView.rowHeight, i.e., its position relative to the top of the scrollView.

The code above simply maintains the placeholder as the user scrolls, adjusting its label text and alpha, depending on how far the user has dragged.

When the user stops dragging, you need to check whether they pulled down far enough (i.e., by at least the height of a cell), and remove the placeholder. You do this by adding the implementation of the UIScrollViewDelegate method scrollViewDidEndDragging::

func scrollViewDidEndDragging(scrollView: UIScrollView!, willDecelerate decelerate: Bool) {
  // check whether the user pulled down far enough
  if pullDownInProgress && -scrollView.contentOffset.y > tableView.rowHeight {
    // TODO – add a new item
  }
  pullDownInProgress = false
  placeHolderCell.removeFromSuperview()
}

As you’ll notice, the code doesn’t actually insert a new item yet. Later on, you’ll take a look at the logic required to update your array of model objects.

As you’ve seen, implementing a pull-down gesture is really quite easy! Did you notice the way that the above code adjusts the placeholder alpha and flips its text from “Pull to Add Item” to “Release to Add Item”? These are contextual cues, as mentioned in Part 1 of this series (you do remember, don’t you?).

Now build and run to see your new gesture in action:

PullDownAddNew

When the drag gesture is completed, you need to add a new ToDoItem to the toDoItems array. You’ll write a new method to do this, but where to put it? It isn’t a TableViewCellDelegate method, but its purpose is closely related to those methods, which delete and edit to-do items, so put it in that group and change the group’s title:

// MARK: - add, delete, edit methods
 
func toDoItemAdded() {
    let toDoItem = ToDoItem(text: "")
    toDoItems.insert(toDoItem, atIndex: 0)
    tableView.reloadData()
    // enter edit mode
    var editCell: TableViewCell
    let visibleCells = tableView.visibleCells() as [TableViewCell]
    for cell in visibleCells {
        if (cell.toDoItem === toDoItem) {
            editCell = cell
            editCell.label.becomeFirstResponder()
            break
        }
    }
}

This code is pretty simple – it adds a new to-do item to the start of the array, and then forces an update of the table. Then it locates the cell that renders this newly added to-do item and sends a becomeFirstResponder: message to its text label in order to go straight into edit mode.

Next, remember to replace the // TODO – add a new item in scrollViewDidEndDragging with the call to toDoItemAdded, so the if block looks like this:

if pullDownInProgress && -scrollView.contentOffset.y > tableView.rowHeight {
    toDoItemAdded()
}

The end result is that as soon as a new item is added, the user can start entering the description for their to-do item:

EditNewItem

That’s pretty slick! And it works even if you start with an empty table, or delete all the items – you can still pull down to add a new item :]

But there’s one more thing to consider: what if the user changes their mind, doesn’t type anything, and just taps Enter to get rid of the keyboard? Your table will have a cell with nothing in it! You should check for non-empty text and, if a cell’s text is empty, delete it by calling toDoItemDeleted – the deletion animation signals to the user that the app responded to their action, and didn’t just ignore it, or crash.

If you trace through the code, you’ll see that there are two places where you could check whether the user entered text – either in TableViewCell.swift‘s UITextFieldDelegate method textFieldDidEndEditing, or in ViewController.swift‘s TableViewCellDelegate method cellDidEndEditing.

This is how you’d do it in TableViewCell.swift‘s textFieldDidEndEditing:

func textFieldDidEndEditing(textField: UITextField!) {
  if delegate != nil {
    delegate!.cellDidEndEditing(self)
  }
  if toDoItem != nil {
    if textField.text == "" {
      delegate!.toDoItemDeleted(toDoItem!)
    } else {
      toDoItem!.text = textField.text
    }
  }
}

Notice that this code calls cellDidEndEditing before checking whether the user entered text – it’s just that it seems tidier to get the table cells back to “normal” before deleting the new item. In practice, both things happen so quickly that it looks the same, either way.

You might choose to check for non-empty input in textFieldDidEndEditing, because it’s closer to the (non-)event but, on the other hand, it seems presumptuous for a textField to make the decision to delete an item from the app’s data model – a case of the tail wagging the dog. It seems more proper to let the TableViewCell‘s delegate make this decision…

So this is how you do it in ViewController.swift‘s cellDidEndEditing – you just add the if block – again, I’ve placed it after restoring the cells to normal but again, it doesn’t matter in practice:

func cellDidEndEditing(editingCell: TableViewCell) {
  let visibleCells = tableView.visibleCells() as [TableViewCell]
  for cell: TableViewCell in visibleCells {
    UIView.animateWithDuration(0.3, animations: {() in
      cell.transform = CGAffineTransformIdentity
      if cell !== editingCell {
        cell.alpha = 1.0
      }
    })
  }
  if editingCell.toDoItem!.text == "" {
    toDoItemDeleted(editingCell.toDoItem!)
  }
}

If you delete the empty cell in cellDidEndEditing, then textFieldDidEndEditing must set the to-do item’s text property before it calls cellDidEndEditing, as you originally wrote it:

func textFieldDidEndEditing(textField: UITextField!) {
  if toDoItem != nil {
    toDoItem!.text = textField.text
  }
  if delegate != nil {
    delegate!.cellDidEndEditing(self)
  }
}

Build and run. Notice that if you edit an existing item to "", this code will delete it, which I think is what the user would expect.

EmptyEdit

The Pinch-To-Add Gesture

The final feature you’ll add to the app will allow the user to insert a new to-do item in the middle of the list by pinching apart two neighboring rows of the table. Designing an interface to achieve this sort of functionality without the use of gestures would probably result in something quite cluttered and clunky. In fact, for this very reason, there are not many apps that support a mid-list insert.

The pinch is a natural gesture for adding a new to-do item between two existing ones. It allows the user to quite literally part the list exactly where they want the new item to appear. To implement this feature, you’ll add a UIPinchGestureRecognizer property to ViewController and you’ll use the same placeHolderCell that you created for the drag-to-add gesture.

Open ViewController.swift and set up your pinchRecognizer at the top of the class block, just below the toDoItems property:

let pinchRecognizer = UIPinchGestureRecognizer()

Then, in viewDidLoad, just below the call to super.viewDidLoad set its handler and add it to tableView:

pinchRecognizer.addTarget(self, action: "handlePinch:")
tableView.addGestureRecognizer(pinchRecognizer)

You’re going to add quite a lot of code to ViewController.swift, to handle the pinch-to-add gesture, so set up a “skeleton” pinch-to-add methods group just before the // MARK: - UIScrollViewDelegate methods group:

// MARK: - pinch-to-add methods
 
// indicates that the pinch is in progress
var pinchInProgress = false
 
func handlePinch(recognizer: UIPinchGestureRecognizer) {
  if recognizer.state == .Began {
    pinchStarted(recognizer)
  }
  if recognizer.state == .Changed && pinchInProgress && recognizer.numberOfTouches() == 2 {
    pinchChanged(recognizer)
  }
  if recognizer.state == .Ended {
    pinchEnded(recognizer)
  }
}
 
func pinchStarted(recognizer: UIPinchGestureRecognizer) {
 
}
 
func pinchChanged(recognizer: UIPinchGestureRecognizer) {
 
}
 
func pinchEnded(recognizer: UIPinchGestureRecognizer) {
 
}

The handlePinch method is called when a pinch gesture starts, changes (i.e., the user moves their finger), and ends. This method just hands the recognizer on to helper methods, which you’ll write soon. Notice that it’s not enough for the pinch gesture to just change – you only want to handle this if there’s a pinch in progress. Only the pinchStarted method can set pinchInProgress to true, and this method won’t be called unless the user is touching in exactly two places.

In order to allow the user to pinch apart two rows of the table, you need to detect whether their fingers are touching two neighboring to-do items, keep track of how far apart they’re moving their fingers, and move the other visible cells, to provide a visual representation of the rows moving apart to make room for a new item. If the user ends the pinch gesture after parting two neighboring rows by at least the height of a table cell, then you need to figure out the index values of the two neighboring items, insert a new array item at the correct index, and handover control to your existing cell-editing code.

To do all of this, you’ll need a few more properties and helper methods:

  • a TouchPoints structure to hold the upper and lower CGPoints where the user is touching the screen
  • initialTouchPoints – a TouchPoints instance to hold the points where the user first touches the screen
  • upperCellIndex and lowerCellIndex – properties to store the index values (in the toDoItems array) of the items that the user first touches; the new item will be added at lowerCellIndex
  • pinchExceededRequiredDistance – a Bool that flags whether the user parted the rows far enough to add a new item
  • getNormalizedTouchPoints – a helper method to ensure that the upper point is really above the lower point, by swapping them if necessary
  • viewContainsPoint – a helper method that checks whether a CGPoint is in a view

And so, to work! Add the following to ViewController.swift in the // MARK: - pinch-to-add methods group, just before the pinchInProgress property:

struct TouchPoints {
  var upper: CGPoint
  var lower: CGPoint
}
// the indices of the upper and lower cells that are being pinched
var upperCellIndex = -100
var lowerCellIndex = -100
// the location of the touch points when the pinch began
var initialTouchPoints: TouchPoints!
// indicates that the pinch was big enough to cause a new item to be added
var pinchExceededRequiredDistance = false

Now add the helper methods, below the empty pinchEnded method:

// returns the two touch points, ordering them to ensure that
// upper and lower are correctly identified.
func getNormalizedTouchPoints(recognizer: UIGestureRecognizer) -> TouchPoints {
  var pointOne = recognizer.locationOfTouch(0, inView: tableView)
  var pointTwo = recognizer.locationOfTouch(1, inView: tableView)
  // ensure pointOne is the top-most
  if pointOne.y > pointTwo.y {
    let temp = pointOne
    pointOne = pointTwo
    pointTwo = temp
  }
  return TouchPoints(upper: pointOne, lower: pointTwo)
}
 
func viewContainsPoint(view: UIView, point: CGPoint) -> Bool {
    let frame = view.frame
    return (frame.origin.y < point.y) && (frame.origin.y + (frame.size.height) > point.y)
}

getNormalizedTouchPoints gets the two points from the recognizer and swaps them if pointOne is actually below pointTwo (larger y-coordinate means farther down in the tableView).

viewContainsPoint hit-tests a view to see whether it contains a point. This is as simple as checking whether the point “lands” within the frame. The cells are full-width, so this method only needs to check the y-coordinate.

Note: getNormalizedTouchPoints is another case where Swift’s y-coordinate is different from the Objective-C version. In Objective-C, the recognizer.locationOfTouch y-coordinate must be incremented (offset) by scrollView.contentOffset.y, but Swift’s y-coordinate is already offset. For example, if you have scrolled down so that items 10 to 20 are visible (scrollView.contentOffset.y is 500), the Objective-C y-coordinate of item 12 is 100 (its position in the visible part of the table) but the Swift y-coordinate of item 12 is 600 (its position in the whole table).

Your first task is to detect the start of the pinch. The two helper methods enable you to locate the cells that are touched by the user and determine whether they are neighbors. You can now fill in the details for pinchStarted:

func pinchStarted(recognizer: UIPinchGestureRecognizer) {
  // find the touch-points
  initialTouchPoints = getNormalizedTouchPoints(recognizer)
 
  // locate the cells that these points touch
  upperCellIndex = -100
  lowerCellIndex = -100
  let visibleCells = tableView.visibleCells()  as [TableViewCell]
  for i in 0..<visibleCells.count {
    let cell = visibleCells[i]
    if viewContainsPoint(cell, point: initialTouchPoints.upper) {
      upperCellIndex = i
      // highlight the cell – just for debugging!
      cell.backgroundColor = UIColor.purpleColor()
    }
    if viewContainsPoint(cell, point: initialTouchPoints.lower) {
      lowerCellIndex = i
      // highlight the cell – just for debugging!
      cell.backgroundColor = UIColor.purpleColor()
    }
  }
  // check whether they are neighbors
  if abs(upperCellIndex - lowerCellIndex) == 1 {
    // initiate the pinch
    pinchInProgress = true
    // show placeholder cell
    let precedingCell = visibleCells[upperCellIndex]
    placeHolderCell.frame = CGRectOffset(precedingCell.frame, 0.0, tableView.rowHeight / 2.0)
    placeHolderCell.backgroundColor = UIColor.redColor()
    tableView.insertSubview(placeHolderCell, atIndex: 0)
  }
}

As the inline comments indicate, the above code finds the initial touch points, locates the cells that were touched, and then checks if they are neighbors. This is simply a matter of comparing their indices. If they are neighbors, pinchInProgress is set to true, and then the app displays the cell placeholder that shows it will insert the new cell – although, at this point, you won’t see the placeholder cell, because you haven’t yet written the code that moves the rows apart.

Now build and run.

When developing multi-touch interactions, it really helps to add visual feedback for debugging purposes. In this case, it helps to ensure that the scroll offset is being correctly applied! If you place two fingers on the list, you will see the to-do items are highlighted purple:

PurpleItems

Note: While it is possible to test the app on the Simulator, you might find it easier to test this part on a device. If you do decide to use the Simulator, you can hold down the Option key on your keyboard to see where the two touch points would lie, and carefully reposition them so that things work correctly. :]

In fact, even on a device you might find this a difficult feat if you have fairly large fingers. I found that the best way to get two cells selected was to try pinching not with thumb and forefinger, but with fingers from two different hands.

These are just teething issues that you can feel free to fix by increasing the height of the cells, for instance. And increasing the height of the cells is as simple as changing the value of tableView.rowHeight.

The next step is to handle the pinch and part the list. Remember that handlePinch requires three conditions before it calls pinchChanged:

if recognizer.state == .Changed 
  && pinchInProgress 
  && recognizer.numberOfTouches() == 2 {
    pinchChanged(recognizer)
}

And pinchInProgress was set to true in pinchStarted: only if the touch points are on two neighboring items. So pinchChanged only handles the right kind of pinch:

func pinchChanged(recognizer: UIPinchGestureRecognizer) {
  // find the touch points
  let currentTouchPoints = getNormalizedTouchPoints(recognizer)
 
  // determine by how much each touch point has changed, and take the minimum delta
  let upperDelta = currentTouchPoints.upper.y - initialTouchPoints.upper.y
  let lowerDelta = initialTouchPoints.lower.y - currentTouchPoints.lower.y
  let delta = -min(0, min(upperDelta, lowerDelta))
 
  // offset the cells, negative for the cells above, positive for those below
  let visibleCells = tableView.visibleCells() as [TableViewCell]
  for i in 0..<visibleCells.count {
    let cell = visibleCells[i]
    if i <= upperCellIndex {
      cell.transform = CGAffineTransformMakeTranslation(0, -delta)
    }
    if i >= lowerCellIndex {
      cell.transform = CGAffineTransformMakeTranslation(0, delta)
    }
  }
}

The implementation for pinchChanged: determines the delta, i.e., by how much the user has moved their finger, then applies a transform to each cell in the list: positive for items below the parting, and negative for those above.

Build, run, and have fun parting the list!

PartingTheList

As the list parts, you want to scale the placeholder cell so that it appears to “spring out” from between the two items that are being parted. Add the following to the end of pinchChanged:

// scale the placeholder cell
let gapSize = delta * 2
let cappedGapSize = min(gapSize, tableView.rowHeight)
placeHolderCell.transform = CGAffineTransformMakeScale(1.0, cappedGapSize / tableView.rowHeight)
placeHolderCell.label.text = gapSize > tableView.rowHeight ? "Release to add item" : "Pull apart to add item"
placeHolderCell.alpha = min(1.0, gapSize / tableView.rowHeight)
 
// has the user pinched far enough?
pinchExceededRequiredDistance = gapSize > tableView.rowHeight

The scale transform, combined with a change in alpha, creates quite a pleasing effect:

PartingPartTwo

You can probably turn off that purple highlight now :] and, near the end of pinchStarted, set the placeHolderCell.backgroundColor to match the cell above it (instead of just redColor):

placeHolderCell.backgroundColor = precedingCell.backgroundColor

You might have noticed the property pinchExceededRequiredDistance, which is set at the end of pinchChanged. This records whether the user has “parted” the list by more than the height of one row. In this case, when the user finishes the pinch gesture (pinchEnded), you need to add a new item to the list.

But before finishing the gesture code, you need to modify the toDoItemAdded method to allow insertion of an item at any index. Look at this method in ViewController.swift and you’ll see that index 0 is hard-coded into it:

toDoItems.insert(toDoItem, atIndex: 0)

So toDoItemAddedAtIndex is easy – add an index argument and use that instead of 0 when calling the Array insert method. Replace the toDoItemAdded method with these lines:

func toDoItemAdded() {
  toDoItemAddedAtIndex(0)
}
 
func toDoItemAddedAtIndex(index: Int) {
  let toDoItem = ToDoItem(text: "")
  toDoItems.insert(toDoItem, atIndex: index)
  tableView.reloadData()
  // enter edit mode
  var editCell: TableViewCell
  let visibleCells = tableView.visibleCells() as [TableViewCell]
  for cell in visibleCells {
    if (cell.toDoItem === toDoItem) {
      editCell = cell
      editCell.label.becomeFirstResponder()
      break
    }
  }
}

As before, as soon as an item is inserted into the list, it is immediately editable.

Now to implement pinchEnded!

func pinchEnded(recognizer: UIPinchGestureRecognizer) {
  pinchInProgress = false
 
  // remove the placeholder cell
  placeHolderCell.transform = CGAffineTransformIdentity
  placeHolderCell.removeFromSuperview()
 
  if pinchExceededRequiredDistance {
    pinchExceededRequiredDistance = false
 
    // Set all the cells back to the transform identity
    let visibleCells = self.tableView.visibleCells() as [TableViewCell]
    for cell in visibleCells {
      cell.transform = CGAffineTransformIdentity
    }
 
    // add a new item
    let indexOffset = Int(floor(tableView.contentOffset.y / tableView.rowHeight))
    toDoItemAddedAtIndex(lowerCellIndex + indexOffset)
  } else {
    // otherwise, animate back to position
    UIView.animateWithDuration(0.2, delay: 0.0, options: .CurveEaseInOut, animations: {() in
      let visibleCells = self.tableView.visibleCells() as [TableViewCell]
      for cell in visibleCells {
        cell.transform = CGAffineTransformIdentity
      }
    }, completion: nil)
  }
}

This method performs two different functions. First, if the user has pinched further than the height of a to-do item, toDoItemAddedAtIndex is invoked.

Otherwise, the list closes the gap between the two items. This is achieved using a simple animation. Earlier, when you coded the item-deleted animation, you used the completion block to re-render the entire table. With this gesture, the animation returns all of the cells back to their original positions, so it’s not necessary to redraw the entire table.

In either scenario, it is important to reset the transform of each cell back to the identity transform with CGAffineTransformIdentity. This ensures the space created by your pinch gesture is removed when adding the new item. You’ll rely on the first responder animation when an item is added, but you add your own basic animation if the cells are simply closed.

Notice that the flags pinchInProgress and pinchExceededRequiredDistance are set to false as soon as their true value is no longer needed – this prevents “fall-through” insertions, for example, when the initial touch points are on non-neighboring items but one or both flags were still true from the previous insertion.

And with that, your app is finally done. Build, run, and enjoy your completed to-do list with gesture-support!

Final

Where To Go From Here?

Here’s the finished project with all of the code for the completed app.

I hope you have enjoyed this tutorial and are inspired to think about how to make better use of gestures in your own apps. Resist the urge to rely on buttons, sliders, and other tired old user interface metaphors. Think about how you can allow your users to interact using natural gestures.

To my mind the key word here is natural. All of the gestures that you have added to this to-do list feel natural, because they result in a user interface that reacts to your touch in much the same way that real objects do. This is one of the most compelling features of a touch-based interface!

If you do use gestures, bear in mind that they might not be as discoverable as a more blatant “Click to Add New” button. Think about how you can improve their discoverability via contextual cues.

In this example, the cues have all been visual, but they don’t have to be! Why not try using sounds or vibration? But please, do so in moderation.

If you want to develop this to-do app further, why not try adding a reorder function, where a tap-and-hold gesture floats an item above the list, allowing it to be dragged around. Again, think about the physics of this interaction. The item being dragged should appear larger and cast a shadow over the other items in the list.

Enjoy creating gesture-driven interfaces, and please share you stories and successes in the forum discussion below! :]

Making a Gesture-Driven To-Do List App Like Clear in Swift: Part 2/2 is a post from: Ray Wenderlich

The post Making a Gesture-Driven To-Do List App Like Clear in Swift: Part 2/2 appeared first on Ray Wenderlich.

Video Tutorial: iOS App Extensions Part 3: Photo Extensions: Loading Data

Viewing all 4373 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>