UIScrollView – A really simple tutorial

It’s very common to have a large amount of data that we want to display on the iPhone/iPod, but there’s no way that we can fit all at the same time on the screen (even on the iPad), that’s one of the functionalities of the UIScrollView.

The UIScrollView it’s a very versatile class, you can handle zooming, panning, scrolling, etc, and I have no intention of explaining all the properties and delegates (well, if you want to know something about the UIScrollView, fell free to request it in the comments, just let me know =D ), the documentation itself is pretty good, so you should give it a look.

In this post I’ll create a very simple project with three scrollable UIViews just to get familiar with the UIScrollView, and in the next post I intend to do some simple zooming example. So, the next  post will be about the UIScrollView as well.

The basics:

IMO, there’s 2 very important properties in UIScrollView, the contentSize and the contentOffset.

The contentsize is the width and height of your content, it’s a CGSize and a property of UIScrollView, let’s say that you have an image that’s 500×500, it would not fit on the iPhone screen, right? So, set your contentsize to 500,500. If you want to add more scrollable space at the bottom or at the top, you can use the property contentInset.top and contentInset.bottom, so you can add some extra space without changing the contentsize. And why would you need these insets? Well, if you have a UINavigationBar or a UIToolBar, like the photos app, you will use this.

Every UIScrollView has a scroll indicator (it’s visible by default, but if you want, you can hide it with showsHorizontalScrollIndicator and showsVerticalScrollIndicator), to give an indication of how far in the content you are, and you can change where the indicator starts using the scrollIndicatorInsets.top just like the contentInsets.

The contentOffset is the point that is currently visible, this point represents the top left of your screen. The contentOffset discards the contentInsets, so it can happen that the contentInset is negative, that’s not a problem.

The only thing that you need to have a functional UIScrollView is the UIView that you want to display and the contentSize of this UIView, so let’s start coding…

Create a View-based Application and name it SimpleScroll. Again, I always use the Window-based Application for my projects, but let’s pick the View-based just to speed things up.

Go to your SimpleScrollViewController class, in the loadView method and create your UIScrollView, we will create it with the same width and heigh as the view from SimpleScrollViewController

UIScrollView *scroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
Enable the pagination
scroll.pagingEnabled = YES;

If the value of this property is YES, the scroll view stops on multiples of the view bounds when the user scrolls. The default value is NO.

Create all three UIViews
NSInteger numberOfViews = 3;
for (int i = 0; i < numberOfViews; i++) {
CGFloat xOrigin = i * self.view.frame.size.width;
UIView *awesomeView = [[UIView alloc] initWithFrame:CGRectMake(xOrigin, 0, self.view.frame.size.width, self.view.frame.size.height)];
awesomeView.backgroundColor = [UIColor colorWithRed:0.5/i green:0.5 blue:0.5 alpha:1];
[scroll addSubview:awesomeView];
[awesomeView release];
}

The most important part in this for is to understand the xOrigin. This will place every UIView exactly where the previous UIView has stopped, in other words, each UIView will start at the end of the previous one.

Set the UIScrollView contentSize
scroll.contentSize = CGSizeMake(self.view.frame.size.width * numberOfViews, self.view.frame.size.height);

The contentSize is just the sum of the widths of the three UIViews, if the width of each UIView is 320, and we have three UIViews, your contentSize width will be 920.

Add the UIScrollView to the SimpleScrollViewController UIView
[self.view addSubview:scroll];
[scroll release];
And you’re done 🙂
At the end, you should have something like this :
– (void)loadView {
[super loadView];
self.view.backgroundColor = [UIColor redColor];
UIScrollView *scroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
scroll.pagingEnabled = YES;
NSInteger numberOfViews = 3;
for (int i = 0; i < numberOfViews; i++) {
CGFloat xOrigin = i * self.view.frame.size.width;
UIView *awesomeView = [[UIView alloc] initWithFrame:CGRectMake(xOrigin, 0, self.view.frame.size.width, self.view.frame.size.height)];
awesomeView.backgroundColor = [UIColor colorWithRed:0.5/i green:0.5 blue:0.5 alpha:1];
[scroll addSubview:awesomeView];
[awesomeView release];
}
scroll.contentSize = CGSizeMake(self.view.frame.size.width * numberOfViews, self.view.frame.size.height);
[self.view addSubview:scroll];
[scroll release];
}
As you can see, I changed the background color of the SimpleScrollViewController UIView to red, just to make sure that you can distinguish the content of the UIScrollView.
That is the tutorial, pretty simple, huh? Now you can show all the data that you want on this small screen 😉 Next post I’ll write about how to enable zooming using a UIScrollView.
Advertisements

Automating UI Testing

Why test your app?

Every time that you change something in your code, you have to test it again, there’s a lot that can (and should) be done with OCUnit , but what should you do when you want to test the UI? It’s really boring and time-consuming doing it manually, wouldn’t it be great if you could create some script that do all this mundane job for you? Testing your app you will:
  • Avoid regressions
  • Increase your app quality, therefore your users satisfaction
  • Do something else while the script does the boring work
  • Test manually only those more interesting scenarios.

UI Automation

UI Automation is integrated with Instruments. What does it means? It means that you can use alongside other instruments and see exactly what’s happening with your app.
It’s only supported on iOS 4.x and the device must support multitasking. The simulator or the device can be used to run the tests.

Script language

UI Automation uses javascript to run the tests. I’m not sure why they choosed javascript for that, maybe there’s a lot of javascript developers around the world, compared to other script languages, but I would choose Python instead 🙂
Problem in sdk 4.0.1 and 4.0.2

There’s a bug when you try to run the test if you are using one of these sdk versions. To fix this is pretty simple, just run the following commands in the terminal
cd ~/Library/Application\ Support/iPhone\ Simulator/
ln -s 4.0.1 4.0
If you are using 4.0.2 just substitute it in the second command.

How to automate?

Every control in your application gets represented to UI Automation as an element. Some examples are:
  • Name: Accessibility labels gets translated into the name of your element
  • Value : Value of a control, text from a textfield, and so on.
  • Elements : Every element can contain zero or more elements,  like a tableview cells
  • Parent: Like elements, but one level back up

Verification

To know if the test has passed or not, use these commands :
UIALogger.logPass(“yet, it’s OK”);
UIALogger.logFail(“bummer”);

Logging

It’s very important to log everything that you are doing during the test, the more you log, the better. Every time that you interact with a control, it’s good to log what it’s being done, so when you go back to the results, it’s easier to find the problem.
To log it very simples, just call:
UIALogger.logMessage(“Log this”);
Let’s say that you want to know how your app looks like after some interactions, well, it’s pretty easy, just take a screenshot with. EDIT: This only works on the actual device.
UIATarget.localTarget().captureScreenWithName(“myScreenshot”);

Tutorial

Ok, enough talking, let’s write a very simple test case (maybe, some other time I’ll create a more complex example for UI Automation)  that will press a button and check the result in the textfield.
First, create an View-based Application project, and in the View controller, create the button and textfield, something like this:
– (void)loadView {
[super loadView];
UIButton* button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchDown];
[button setTitle:@”Click me” forState:UIControlStateNormal];
button.frame = CGRectMake(80.0, 210.0, 160.0, 40.0);
[self.view addSubview:button];
textField = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, self.view.frame.size.width – 20, 30)];
textField.borderStyle = UITextBorderStyleRoundedRect;
textField.textAlignment = UITextAlignmentCenter;
[self.view addSubview:textField];
}
– (void)click {
textField.text = @”MyTestApp”;
}
– (void)viewDidUnload {
[textField release];
textField = nil;
}
– (void)dealloc {
[textField release];
[super dealloc];
}
When you click the button, the text “MyTextApp” should appear on the textfield. Go ahead, build and run your app and see if everything is ok.
Now, create a .js file with the following content
app = UIATarget.localTarget().frontMostApp();
mainWindow = app.mainWindow();
button = mainWindow.buttons()[0];
button.tap();
var result = mainWindow.textFields()[0].value();
UIALogger.logMessage(“Will tap on button”);
if (result == “MyTestApp”) {
UIALogger.logPass(“yey, it’s OK”);
} else {
UIALogger.logFail(“bummer”);
}
UIATarget.localTarget().captureScreenWithName(“myScreenshot”);
UIALogger.logMessage(“Button tapped”);
What this script is doing is, getting the mainWindow, tapping on the button, and checking the textfield value.
Open your Instruments app (command + space, and type Instruments), and you should see this screen :
Choose the Automation template. In the next screen, on instruments, select your javascript file:
Select your application, you can find it at your project folder, inside Build->Debug-iphonesimulator (if it’s a simulator build)
Press record on the instruments,the simulador should open running your app and see how the button is clicked automatically. The result should pass and this is what you will see:

Conclusion

You can test a lot of things, like create functions in your javascript, do some  scrolling, screen rotation, pinching, and so on, even the multitask feature can be tested with this approach. So, it’s a very cool feature to have and more important, to use. Remember that it’s a good practice to restore the app to its initial state when testing, so, let’s say that you add some value, at the and of this test, remove this value and move along with the other tests (Of course, only if this value will not be used in other tests) .It’s a shame that there isn’t any recorder tool, so you could just use the app and the script would be generated automatically, but hey, it’s better than nothing, right 🙂
There’s a lot of information on the Reference Collection , you probably should take a look. This was a very simple example, just to give you a hint how the UI Testing works.
Now that you know how to create some automated tests, you can use your spare time to do something nice, like developing more apps 🙂

NSLog: Logging Tips

Sometimes it’s way better to just use a simple log then debugging your app step by step. That’s when NSLog comes in handy.
There’s a lot of ways to use NSLog in your code, I’ll show some of my favorites.
int myInt = 1;
float myFloat = 2.3;
NSString *myString = @”Hello Log”;
UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 20, 40)];
NSLog(@”Int [%i]”, myInt);
NSLog(@”Float [%f]”, myFloat);
NSLog(@”String [%@]”, myString);
NSLog(@”Object [%@]”, myView);
This is how the log will look like:
Int [1]
Float [2.300000]
String [Hello Log]
Object [<UIView: 0x5c07950; frame = (0 0; 20 40); layer = <CALayer: 0x5c07920>>]
What I’m doing there is, logging an Int, Float, NSString and an UIView. As you can see I always add some brackets in the log, and why do I do this? That’s only to know exactly what’s being logged. Imagine that I have a NSString with some white spaces, I will not know that it has the white spaces unless I add the brackets. Here’s an example :
NSString *test = @”Test   “;
NSLog(@”log: %@”, test);
Result : log: Test

NSString *test = @”Test   “;
NSLog(@”log: [%@]”, test);
Result : log: [Test   ]

Logging CGGeometry

Now, let’s say that you want to log some CGRect, CGPoint or CGSize, just transform them into NSString with :
NSStringFromCGSize
NSStringFromCGPoint
NSStringFromCGRect

Log Macro

Here’s a cool tip, although I’m not a macro fan, there’s one that’ great for logging:
#define MYLOG(fmt, …) NSLog((@”File [%s] Method [%s] Line [%d]: ” fmt),__FILE__, __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
and you can use exactly like an NSLog, like so :
MYLOG(@”Hello Log”);
What this will do is:
  • __FILE__ : file path and name.
  • __PRETTY_FUNCTION__ : name of the method.
  • __LINE__ : the line of the method
You can edit this macro as you please, remove or reorder the parameters. That’s how the output will look like :
File [/Users/user/Desktop/Log/Classes/LogAppDelegate.m] Method [-[LogAppDelegate application:didFinishLaunchingWithOptions:]] Line [25]: Hello Log
That’s some simple tips that I have to share about the NSLog, if anyone has anything else to add, please share it in the comments.
That’s it, thanks for all the fishes 🙂

iOS HelloWorld example

Horray, you’ve got your mac and your iPhone, and you want to write some code and see how it goes. You can find a lot of Hello World tutorials around the web, but I believe that 95% or those tutorials use Interface Builder, and I’m not a big fan of IB, because it does some “magic” behind the scenes, and IMO it’s really nice to know everything that’s happening within your app. IB is not useless, but let’s not enter on this topic (yet). I think that’s better to write your first app without IB so you can see what’s really going on.

If you haven’t installed Xcode yet, go to http://developer.apple.com/iphone/index.action and get it. This dmg will install everything that you need, iPhone SDK, iPhone Simulator, XCode, and a bunch of useful apps, like Instruments and so on. By default, Xcode will be installed on /Developer/Applications/Xcode.app, go there and open it (or just hit command + space and type Xcode on your spotlight).

Go ahead and create a new Xcode project. You’ll be prompt to choose some project templates, we can select View-Based Application, it has a ViewController already configured, but let’s choose the Window-Based Application. The Window-Based application is the simplest template that you can think of, it will give you a Windows and an UIApplicationDelegate that responds to UIApplication. These files are required in any iOS application, so, that’s why the Window-Based application is as simple as it gets.

Name the project Hello World and this is what you’ll get :

The template creates a lot of required files for you, but let’s focus on the HelloWorldAppDelegate.h and HelloWorldAppDelegate.m, but take a look in the main.m file, you can see that the UIApplication is being created there.

Open the HelloWorldAppDelegate.m file, and check the following method:

– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

// Override point for customization after application launch.

[window makeKeyAndVisible];

return YES;

}

This method gets called when the initial configuration of the app is done, and your app is ready to launch, so it’s there that you have to call your initials methods, like in this example, our UIViewController. You have to be careful in what kind of method you call in there, because if your app takes a while to respond it will be watchdogged. For example, if you call some method that requires a server response and the server does not responds back for a while, the iOS will kill your app, but let’s forget about this for now.

We could just add the label directly to the window, like so :

– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

// Override point for customization after application launch.

CGRect labelFrame = CGRectMake(50, 50, 200, 30);

UILabel *helloLabel = [[UILabel alloc] initWithFrame: labelFrame];

helloLabel.text = @”Hello World”;

[window addSubview:helloLabel];

[window makeKeyAndVisible];

[helloLabel release];

return YES;

}

IT’S DONE! Tutorial finished!

OK, sorry, that’s not true, I mean, you can do this, but you don’t want to do like this, believe me 😉

Let’s do it right, forget about the last piece of code, and let’s start again. Let’s create an UIViewController subclass, and add it to the window. It’s on the view controller that you will add your label, and whatever you need. Go to File -> New File (or command + N) and create an UIViewController subclass.

Let’s name it HelloWorldViewController like so:

Open the HelloWorldViewController.m, as you can see, you have the basic file structure all ready for your use (Hey, thank you, Apple guys). Search for the method named – (void)loadView, which is used to manually populate a view controller.

Let’s copy the same code that we use previously in the appDelegate

– (void)loadView {

UIView *mainView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];

CGRect labelFrame = CGRectMake(50, 50, 200, 30); 

UILabel *helloLabel = [[UILabel alloc] initWithFrame: labelFrame];

helloLabel.text = @”Hello World”;

[mainView addSubview:helloLabel];

[helloLabel release];

self.view = mainView;

[mainView release];
 

}

Let’s see what are we doing here :

  1. UIView *mainView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds; Creating our UIView with the same size of the main screen.
  2. CGRect labelFrame = CGRectMake(50, 50, 200, 30); The CGRect will be the bounds for your view.
  3. UILabel *helloLabel = [[UILabel alloc] initWithFrame: labelFrame]; This will create the UILabel class with the CGRect that we have created.
  4. helloLabel.text = @”Hello World”; Just set the string on the label text. Why add an @ at the beginning of the string? That’s to differentiate the NSString from the C String.
  5. [self.view addSubview:helloLabel]; Every UIViewController controls  a UIView (yey, it does makes sense, doesn’t it?), so, to access it you just have to use self.view and that’s it. Here we are adding the helloLabel (that is a UIView subclass) to the HelloWorldViewController UIView
  6. [helloLabel release]; Since we have added the helloLabel to the HelloWorldViewController UIView, we can release it, because the UIViewController will handle the memory management for the UILabel
  7. self.view = mainView; Adding out custom UIView to the UIViewController’s UIView.
  8. [mainView release]; The UIViewController now retains the reference to it’s UIView, so, we can release it.

We are almost done, just one more thing: we need to hook up our brand new UIViewController to the HelloWorldAppDelegate  and add it to our window.

To do it, go back to the HelloWorldAppDelegate.m and import our view controller class:

#import “HelloWorldViewController.h”

now, let’s create alloc and init our HelloWorldViewController:

HelloWorldViewController *helloController = [[HelloWorldViewController alloc] init];

add it to the window:

[window addSubview:helloController.view];

and release the HelloWorldViewController. Generally you will not want to release the view controller just yet, but in this example, there’s no problem:

[helloController release];

You will end up with something like this:

– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

HelloWorldViewController *helloController = [[HelloWorldViewController alloc] init];

[window addSubview:helloController.view];

[helloController release];

[window makeKeyAndVisible];

return YES;

}

Build and run your project (command + enter), and your should see this :

Pretty simple, huh? Click here to download the HelloWorld example 🙂

Getting started with iOS development

Getting started

OK, so, the iOS, everyone talking about it, lots and lots of great apps being released every day, probably you have downloaded them all and you start to have some excellent ideas for creating apps, but how do I start my quest for the holy grail awesome app?

Equipments.

It doesn’t really matter if you have the best idea ever, or if your technical skills are the best, if you don’t have the required equipment to start the iOS development, you can’t do much about it. If you have an intel based mac / macbook, you are good to go, I really would recommend at least one iPhone device, so you can test on it, I mean, the simulator is great, but you can’t always rely on it.

At the present moment we have 4 iPhone models (I’m not counting the iPad and the iPod touches), the perfect scenario would be have them all, but that’s not always possible, so, at least one, it’s a good start.

The iPhone Developer Program

OK, you have your mac and your iPhone, what now?

You have to register on the iPhone Developer Program. The registration itself is free, so you can download the SDK and access some Apple guides, but (yeah, it’s Apple, nothing it’s really free), if you want to test your apps on the device and make your iPhone(s) purchase worthy, you need to enroll on the standard program which costs $99 a year.

Now that you are enrolled on the program, you should bookmark these links because they will be your best friends, forget your parents, forget your girlfriend, the only ones you really can count on are these links:

iPhone Application Programming

One excellent way to get started on the iOS development is to watch the Stanford University’s iPhone Application Development on iTunes U

It’s really awesome and covers from dummy to advanced, even if you consider yourself a good iOS developer, I would recommend to watch some episodes of this.

Start developing

OK, so, enough theory, don’t try to master Objective-C all at once. Open Xcode try it for yourself and see how it goes. There’s some Stanford’s handouts  that you can practice on, but you should go beyond, search for some examples, modify it, run it again, and again, and get familiar with the language. If you are a Java developer, or something like that, you will sure find that Obj-C is really different, but in the end, I bet that you’ll like it 🙂

Have fun

Yep, that’s it, just have fun, enjoy it, Obj-C is a fun language and you should take the best from it. With that in mind, go back to your quest to develop that awesome app. and maybe you will find the holy grail.