Tuesday, February 22, 2011

Best way to use UIImageView (Memory Optimized)

We use UIImageView a lot in our apps, right? At the most, we use it for a nice view backgrounds or even as button images.

The easiest way to set an image to the UIImageView is to use the following code:

 myImage.image = [UIImage imageNamed:@"myImage.png"];

This works. The code is short and simple and used to be my favourite code to use. But no more. Let me tell you why. For this tutorial, we are going to make use the Instruments to help you understand why this method is not memory optimized.

This tutorial also kinda teach you to check your app's Allocations (of memory) and what you should expect an app to behave in the memory world.

1. Using imageNamed will prepare a cache (depending on the size of image) which will NEVER be flushed out of the memory unless you reach a memory warning of some level, but by then its too late and your app will crash. Even when you set myImage.image = nil, the image is STILL IN MEMORY.

2. If you are using Interface Builder, and setting the image in Image View Attributes, that is also equal to imageNamed method. The image will be cached immediately when the app is ran.

3. To illustrate this, I created 2 UIImageViews in a single View. Created the IBOutlets for them so that we can easily set the image to it by code. I also add a button on the main View, so that we can switch between the 2 sub UIImageViews.

4. To illustrate this issue, 2 projects have to be created. One project uses imageNamed as the image setter, and the other uses imageWithContentsOfFile, which is the better image setter.

5. The imageNamed Project:
a) The project is simple, so I won't be explaining any of the normal codes. In this project, upon loading, we set the first UIImageView *firstImage. Upon tapping the button, we will set firstImage to nil, and then show the second UIImageView *secondImage.

b) Lets take a look at the Instruments tool of XCOde. The one we are interested now is the one called "Allocations". Allocations is a very effective tool to check your code for unreleased objects that is eating the memory when it is not used. The basic principle in the coding is, you must release what you no longer use.

c) To run it, simply Build the Project first. And then go to, Menu Run -> Run with Performance Tool -> Allocations. For the imageNamed Project, the result of the evaluation is as in the video shown below.

d) What happens when we run it is the app will cache the firstImage image first, since we used imageNamed in the viewDidload. After that, when I press the button, we can see that the "Live Bytes" increase another 3MB+ because we are using imageNamed on the second image. Note that we also write a code to set firstImage.image = nil, but the Live Bytes, remained the same. And after that even we keep on toggling the images while setting the other image to nil, it does not clear the memory at all.

___________

6. So now lets look at the better image setter, called imageWithContentsOfFile Project:
a) It is the similar project, but instead of using imageNamed, we make use of the imageWithContentsOfFile setter.

b) A little note on using it, is that you need to append a suffix of "/" at the beginning of the filename otherwise the path is incorrect and the image couldn't be loaded properly. The code is as follows:

 NSString *fullpath = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/myImage.png"];
myImage.image = [UIImage imageWithContentsOfFile:fullpath];

c)Run it the same way you did with the imageNamed project. The result of the evaluation if as in the video shown below.


(sorry for bad quality of video but its only at the beginning)

d) In this project, we will see the Live Bytes will only contain the memory of 1 image. In this case the photo of the bulls is about 3.3MB in total. And the photo of the kid on a bike is about 4.4MB in total. And as we toggle between the 2 photos, we can see the Live Bytes remain to be at lowest total in comparison to the imageNamed project.

e) While imageContents are superb for memory management, loading images this way is a little bit slow. So if you are just using a small sized UIImageViews in a larger quantity, it is probably better to use imageNamed method.

ImageName Project:


ImageContents Project:

Monday, January 17, 2011

How to Integrate iAd for OS3.2+ and Admob as Backup


I am impressed with iAd revenue given by Apple. It really surprises me. So I end up putting iAds for most of my free apps. Though, I'd like the app to be downloaded by OS3.2 users as well (wider user base). So, upon searching, I found the tutorial from Ray Wenderlich. It totally works! If your app is ran on 3.2, it will not cause a crash using this way. iAd is simply not instantiated in OS3.2.



This post is not really a step by step tutorial for the codings, but more of an explanation of what needs to be done in order to have iAd integrated in your app, with Admob as backup ad in OS3.2 and when iAd fails. Please read this post carefully. With the downloadable full project source code sample, you should be able to figure this out easily.

1. To get the Admob SDK, you need to be registered to Admob and add a new Site/Apps. Key in your app URL, and soon the Admob Publisher Code will be given and you can then download the Admob SDK built specifically for your app (basically the SDK will be pre-filled with your publisher code).

2. All you need to do is then copy 2 folders - Admob and TouchJSON into your app folder in Finder. Then drag these 2 folders in your XCode Project group. Then you need to add 5 Frameworks to your project. They are MediaPlayer, CoreGraphics, AudioToolBox, MessageUI, and QuartzCore.

3. For iAd, you need to add and weaklink the iAd.framework. Click on Targets->"App name" and get the info of it. Then go to General tab and underneath there is a list of frameworks. On the right side, there is a column called "Type". Click on the value "Required" and change to "Weak".

4. Now for the required codes to declare Admob and iAd. In .h of your viewcontroller, declare as below (read the comments in code below for more details):
#import "AdMobDelegateProtocol.h"

@class AdMobView;



@interface iAdMobViewController : UIViewController <AdBannerViewDelegate, AdmobDelegate> {

id _myAD; // we are declaring as id because OS3.2 won't know the existance of ADBannerView

AdMobView *adMobAd;

NSTimer *admobTimer;

IBOutlet UIButton *exampleBtn; // just to simulate repositioning objects

}



@property (nonatomic, retain) NSTimer *admobTimer;

@property (nonatomic, retain) id myAD;

@property (nonatomic, retain)  UIButton *exampleBtn;



-(void)createAdBannerView; // method to create iAd banner view

-(void)callAdmob; //method to create Admob view



// methods to show or hide iAd view upon receiving ad or failing to receive ad.

-(IBAction)moveAdOut; 

-(IBAction)moveAdIn;



// methods to show or hide Admob view upon receiving ad or failing to receive ad.

-(IBAction)moveAdMobOut;

-(IBAction)moveAdMobIn;



end;

Then in .m file, we need to include a header file for Admob only.
#import "AdMobView.h"

Most of the code above is pretty straight forward. Though NSTimer deserves a little explanation. Admob works a little different than iAd. For iAd, once we create the BannerView, retained it, then iOS will continue to call for adverts from Apple server every 30seconds (edit: only test iAd in SDK 4.0 is called every 30sec, actual iAd refresh is called every 3 minutes). It is done automatically. But for Admob, the SDK does not call for new ads automatically. For Admob, the ad will be called once everytime we create/alloc an AdMobView. If you see in the AdMob delegates, there is one function called requestFreshAd, though this just refresh the ad inside the view with new one WITHOUT calling the didReceiveAd delegate!

Our goal in this project is to make iAd the master ad server, and admob the secondary ad filler. So, in OS4.0, we instantiate iAd once, so the iOS will continue to send ad requests every 30 seconds. So we set the iAd to show if the ad is received, and set to create and show Admob ad if iAd fails. This cycle will continue forever. So we will be having iAd and Admob continuously showing.

Though, in OS3.2, iAd NEVER gets instantiate. So the iAd didFailToReceiveAd delegate will never get called, and that means Admob ad will never be instantiated as well! So we need to trigger the creating of Admob ad calling manually. And that's where admobTimer comes in the picture.

Testing the project can be painstaking at times. Because since we make iAd the master, and iAd Test ads fillrate is like 99%, it will take a while for Admob Test ad to appear. During running the project in simulator, monitor it together with Console so that you can see the logs every 30 seconds. Test the app with iPad Simulator 3.2 too, and it should not crash, and you should only see Admob ads in the iPad Sim3.2.

There are a few more things that is important in setting up the Admob Delegate functions.

1. The Publisher ID delegate. Key in YOUR Publisher ID in that string placeholder. (in your .m file)
- (NSString *)publisherIdForAd:(AdMobView *)adView {

return @"your admob publisher id here";  

}

2. To get the test Ads for Admob, put this delegate:
- (NSArray *)testDevices {

return [NSArray arrayWithObjects:

ADMOB_SIMULATOR_ID,                             // Simulator

nil];

}

3. To get test ads in your devices, just add your 40digits device ID as NSString into the array in the above delegate function. If you have more devices, just continue to add them to the array.
- (NSArray *)testDevices {

return [NSArray arrayWithObjects:

ADMOB_SIMULATOR_ID,                             // Simulator

@"your 40digit device ID here"

nil];

}

4. To get test ads in iAd, you don't need to do anything in Simulator. Even if you haven't added a new version with iAd enabled in iTunesConnect, you can still receive test ads in the iAds in simulator or device.



Friday, November 12, 2010

Simple Game: Tap Ball (Catch The Ball)

It has been a looong time since I wrote any tutorials / sample codes. Recently a friend asked to create a simple base for a game where there is a character moving around randomly and upon tapping it, the character stops. So I created this tutorial for it.


Initially I just wanted to put a ball as the character, but end up with a weird creature image. It is just an image with a transparent background anyway.



If you are making a game instead of just animations, you should really consider using cocos2d. I have NEVER used cocos2d yet. I design my games from just using normal codings - coregraphics. If you plan to make graphic extensive game, then OpenGL is the only answer.

Ok, to create this simple game, we need to declare some objects and some methods. Obviously we need a UIImageView for the sprite. We also need a timer and a button.


IBOutlet UIImageView *Ball1;

NSTimer *mainTimer;

IBOutlet UIButton *startButton;


To animate objects smoothly in XCode, normally we use the UIView animation routine (that commitAnimations block of code). But the problem with this code is the object's property is set immediately to the final property set, even when the object is still animating. For example,
if we animate a UIImageView such as below:

myball.frame =  CGRectMake(0,0,50,50);

[UIView beginAnimations: @"MovingTheBall" context: nil]; 

[UIView setAnimationDelegate: self];

[UIView setAnimationDuration: 1.0];

[UIView setAnimationCurve: UIViewAnimationCurveEaseInOut];

myball.frame =  CGRectMake(100,100,50,50); <---

[UIView commitAnimations];

The moment the iOS runs the code up to the <-- arrow, myball already set to location (100,100) immediately even before it completes animating the movement. So this type of movement animation is useless for a game, because in a game you need to know exactly where the ball is at any instant. Thus we need to use NSTimer and move the UIImageView sprite step by step. And at each step, we will be able to check for collisions/touch location/and so on.

Next we define the methods needed for this project.


-(IBAction)startMove;

-(void)moveBall;

-(BOOL)Intersecting:(CGPoint)loctouch:(UIImageView *)enemyimg;

Then we need a few "Game Variables"
CGPoint Destination; // to be assigned with new destination of the sprite

CGFloat xamt, yamt; // x and y steps to move 

CGFloat speed = 50; // speed 

We need a method to trigger the ball movement when we tap on the button. startMove is the method:
-(IBAction)startMove {

startButton.hidden = YES; // we don't want user to instantiate another timer, so hide the button after it has been tapped.

// assign a random location within the view of size 320x480.

Destination = CGPointMake(arc4random() % 320, arc4random() % 480); 

// calculate steps based on speed specified and distance between the current location of the sprite and the new destination

xamt = ((Destination.x - Ball1.center.x) / speed);

yamt = ((Destination.y - Ball1.center.y) / speed);

// ask timer to execute moveBall repeatedly each 0.2 seconds. Execute the timer. 

mainTimer = [NSTimer scheduledTimerWithTimeInterval:(0.02) target:self selector:@selector(moveBall) userInfo:nil repeats: YES];

} 

Link this method to the Start button. When a user tap the start button, the button will dissappear, a new location destination for the sprite is given, the steps to take all the way to the new destination are calculated and then the move timer is fired up every 0.02 seconds. (the repeats: YES part tells the iOS to repeat calling the method moveBall forever).

Let's take a look at moveBall method (read the comments inside the function):

-(void)moveBall {

 // xdist and ydist is to check the current distance of the sprite to the destination point.

        // we are only interested in the distance so direction (-ve or +ve) does not matter, hence

        // we use fabs() function

 CGFloat xdist = fabs(Destination.x-Ball1.center.x); // fabs gives always +ve value

 CGFloat ydist = fabs(Destination.y-Ball1.center.y);

 

        // Normally, to compare 2 points, we can use CGPointEqualToPoint, but that requires an

        // exact match of points. In our case, since our steps are in CGFloat, each steps make the

        // location to be a non round number, but our destination is always a round number. So

        // we cannot use CGPointEqualToPoint. Instead we just check for the distance, if it is close

        // enough to the destination, then it is time to give it a new destination.

 if ((xdist<5)&&(ydist<5)) {

  Destination = CGPointMake(arc4random() % 320, arc4random() % 480);

  xamt = ((Destination.x - Ball1.center.x) / speed);

  yamt = ((Destination.y - Ball1.center.y) / speed);

 } else {

  //continue to move it if distance is still far

  Ball1.center = CGPointMake(Ball1.center.x+xamt, Ball1.center.y+yamt);

 }

}



Now, we need to check when user touch the screen, and if user does touch it, check whether the touch intersect with the sprite or not, if it touched, then stop animation and show startButton.

To detect touches, use one of the Touches delegate functions:


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

 UITouch *touch = [[event allTouches] anyObject];

 CGPoint location = [touch locationInView:self.view];

 // if user touched ball, stop timer

 if ([self Intersecting:location :Ball1]) {

  [mainTimer invalidate];

  mainTimer = nil;

  startButton.hidden = NO;

 }

} 




As you can see, we use another custom method called Intersecting. This is my custom method that I create to check for UIImageView intersection with a point. Sorry for the names of variables, I just copied it right out of my new game. This is a function that returns a BOOLEAN result. If the ImageView and the point intersects, it will return YES, if not, NO. And yes, you can also use CGRectContainsPoint which does the same thing. :P


-(BOOL)Intersecting:(CGPoint)loctouch:(UIImageView *)enemyimg {
 CGFloat x1 = loctouch.x;

 CGFloat y1 = loctouch.y;

 

 CGFloat x2 = enemyimg.frame.origin.x;

 CGFloat y2 = enemyimg.frame.origin.y;

 CGFloat w2 = enemyimg.frame.size.width;

 CGFloat h2 = enemyimg.frame.size.height;

 

 if ((x1>x2)&&(x1<x2+w2)&&(y1>y2)&&(y1<y2+h2))

  return YES;

 else

  return NO;

 
}


So the TouchesBegan delegate just to get the point of touch, and the check that point whether it intersects with the current location of the sprite. If it intersects, then we just stop the sprite from moving (Invalidate the timer, which will stop it), and show the startButton.

That's all folks.

Wednesday, July 7, 2010

Populating iPhone Simulators With Photos

If you develop apps, most probably you will want a user to load up his/her own photo into the app. You can test the UIImagePickerController function by testing loading a photo from either the "Saved Photos" folder or "Photo Library" folder inside the Photos App. But often, after a new installation, both folders are empty.

To save the photos to Saved Photos album, you can just drag any images onto the iPhone simulator whereby it will opened automatically with Safari. Then you can click and hold on the image and an ActionSheet will slide up and you can save it. Images saved in this way are copied to the Saved Photos album. How about Photos Library folder? See below:

There are 2 ways:

1. To populate the folder Saved Photos: Copy and paste photos to this folder (if not exist, create it by yourself):

[USER]/Library/Application Support/iPhone Simulator/User/Media/DCIM/100Apple/...

2. To populate the folder Photo Library (if not exist, create it by yourself):

[USER]/Library/Application Support/iPhone Simulator/User/Media/Photos/...

Developers On MacBook Beware.

This post is not really a coding tutorial. Instead I will be sharing on how things get pretty gooey if you use a Macbook to develop iPhone apps on. You probably can guess what is the problem, yep, the DVD drive gave away its life too soon.

iPhone SDK 4 is out and Apple makes it a requirement to produce apps/updates compiled with SDK 4. It requires Snow Leopard, but my Macbook was running Leopard still! So I went to an Apple Premium Reseller and bought the Snow Leopard installer DVD. Excited, rushed home and slipped the disc inside the Macbook and it spinned for a while and spit it back out. It kept doing that forever. So it is positive, the drive is malfunctioning.

So I thought.. Great.. what now?

After hours of searching, I finally came across an answer via google - to make a clone of the DVD into a Thumbdrive! Yep. But how to do that if the Macbook DVD drive cannot read the Installer DVD? You are out of luck really. Except if you have another computer, which I have -  another desktop PC running Windows XP. Now if you have another Mac with good disc drive, it would be easy to just fire up the Airport and install remotely. But if you just have a PC, it is going to be a little bit difficult.

Now, if you put the Installer DVD into a Windows PC, the boot sector of that disc is going to hide everything else, except the Windows related programs - which are utility to Remote Install. You can do this if you have a WiFi (ie Airport). My PC only have a bluetooth, so that option is out of the window.
I also do not have a Firewire cable, which is another option to use to do Remote Install.

So, what I did was googled a Free Iso Creator software, downloaded it and generate a disc image of the Installer DVD on my PC's hard disk. The name has *.iso extension on it. rename that to *.dmg (which will be recognized by Mac OS). The size is 7.45GB. For some reason, my so called 8GB Sandisk Cruzer USB drive was a rip off. It is not really 8GB, it is 8million bytes, which is 7.40GB. So, I cannot copy this 7.45GB image file onto my USB. Crap!

Then WinRAR springs to mind. Downloaded the trial version and start to archiving the 7.45GB file into 5 pcs of ~ 1.3GB files. Then transfer them bit by bit to my Mac. Once all the 5 pieces of 1.3GB files are in my Macbook, I need to stitch them up back together.. so I downloaded UnRarX (a cool free extractor for Mac) and start combining back those files into 1 big 7.45GB installer image file.

Next, what I did was open up Disk Utility in my Macbook, plug in my "8GB" thumbdrive and do a Restore using the image file that was retrieved earlier. Remember to format the thumbdrive and partition it as GUID Apple format, which will make it bootable.

Once done, Mac OS immediately recognizes it and mount a virtual drive. Double click it and you will see the installer icon and voila! I could finally install Snow Leopard and SDK 4 on my Mac.

Hope this post would help someone in similar situation as mine. Wasted a whole 2 days just to do this. But glad that I found a way. I really don't want to spend hundreds of dollars to swap the drive or to buy external drive. Pheww...

Friday, May 14, 2010

Hacking "More..." Tableview of a TabBar Template Based App

Ok, its been a while since I wrote any tutorials, so I thought I'd write about a basic Tab Bar based template customization.

As most of newbies in iPhone App development, I too started with the basic template of Tab Bar App. It is a great foundation to create a utility like app because you do not have to care (much) about how to switch views within the app using a tab bar. But, this also means you are stuck with the layout pretty much forever.

** NO SOURCE CODE FOR THIS TUTORIAL **

After a while, my app that was using this Tab Bar template (iTronixPal - Electronics Box), has grown to be a rather extensive app with many tools which are useful for electronics enthusiasts. So in each update I felt compelled to improve upon the graphics, and just to make it better and better, as a gesture of thanks to those who bought it.

Then came the boring More... table view. Seriously? Have u looked at it? White background, black text, no shadows, no eye candy whatsoever. Blerghh.....!!

So I did a search on the Apple Docs, and found a lil something about the moreNavigationController.
Read through it, and found that you can access it as a view. Bada-bing! Once you can access a view, you can basically do anything to the view. And in particular of interest, add a subview.

So, I created a transparent image background for the tableview in More tab. I have lots of tabs, therefore my image is rather long, but the width is fixed to 320pixels. Such as below:


I arranged the icons nicely in my graphics software (I use GIMP) based on a screen capture of the original More table view. Remember to set the background to transparent. That is Alpha channel = 0.0 in the graphics software.

Then, I just add this image to the More.. tableview view by using the following codes in AppDelegate.m file inside the applicationDidFinishLaunching method. Such as below:

  //create a uiimageview object;
UIImageView *tmp = [[UIImageView alloc]  initWithImage:[UIImage imageNamed:@"MoreBg.png"]];
 //add the uiimageview as a subview of moreNavigationController
 [tabBarController.moreNavigationController.topViewController.view addSubview:tmp];

//send the imageview to back (though i think it is not sent to back)

 [tabBarController.moreNavigationController.topViewController.view sendSubviewToBack:tmp];

The code should be self explanatory. Next, you need to open the TabBarController in your Window.xib file (in interface builder), and add some spaces in the beginning of the title for each tabs (so that it does not overlap with your icons). Or if you want to customize all the titles as images , then just delete all the titles).

Here is how mine looks like (below). I even changed the background color of the More navigation controller view to gray.


Hope this helps for anyone who is looking for an easy way to do this.

This tutorial does not accompany a downloadable sample code since it is pretty simple.

Thursday, March 4, 2010

How To Use UIImagePickerController In Landscape App




My app just got rejected for displaying UIImagePickerController in the landscape mode. As Apple doc stated, UIImagePickerController (will be refered as UIIPC from now on) only works in portrait mode. For SDK 3.0+, this is no problem since i think it will auto display in portrait mode (correct me if im wrong). But for me, who is using 2.2.1 still, becomes a problem. I know there are quite a few posts about this issue with solutions but I tried all those and proves to be either "not working" or "too difficult to implement.". Especially those that suggest to apply transform to all the views object.



The problem is that the UIIPC launched in landscape mode causes the thumbnails to be stretched in devices, and appears funny in simulator. Such as below:


After few hours of banging my head on my MacBook, I came up with a simple hack that I think works ok. Here's one way how to solve this problem - very easy way without needing to apply transform to all of your objects and views. This is a sample app i created. From launch, the app starts from landscape.  

Now when I press a button to load a photo (calling UIIPC - either savedphotosalbum/photolib), the app display a "transition view" that halts the operation until the user rotates the device. Put a message/text on it to ask the user to rotate the device to proceed. At the time of this view displaying, also set a GLOBAL BOOL FLAG, (named WANTSPORTRAIT for me), to YES. (initially set to NO)

Then, when the user rotates it, detect the orientation (by the delegate function of  - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation). In this delegate function, detect the flag and if it is set, call the UIIPC and return UIInterfaceOrientationPortrait. Voila, now your UIIPC is in Portrait.

The next thing to do is to handle the selected image/photo. Once the image/photo is selected, the appropriate delegate function will be called (for 4.0, it is - - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info. In this function set WANTSPORTRAIT to NO and again, display the transition view with a message asking the user to rotate to landscape. (You can customize these messages, and even easily apply simple transform on them to make it appear nice). 

And when the user rotates it again back to landscape, the delegate detects it and return the orientation back to landscape. The project sample probably is not perfect (eg if user rotates the other way or etc, you need to modify it to handle all of orientation)

This method is accepted by Apple Reviewer. 2 of my apps are using this method and appears to be accepted by users.

Friday, December 18, 2009

Simple Ball Animation

It has been a while since I last wrote any tutorial, so here is one more. This tutorial is inspired by a noobie's post in the iphonedevsdk.com forum. I know how hard it can be to start learning to code in XCode, especially if you are new to Obj-C and also new to Mac (like I was 2 months ago). Most of tutorials that exist in the web few months ago (that I could google anyway), are mostly too simple, like a Hello World app. That just not good enough. Sometimes, a bit more variety of the Hello World app could help much more to noobies, right? So that is why I created this tutorial blog - to help noobies get further from just displaying Hello World in their app. =p




Enough ramblings, lets get started. Here is how the app will look like. Immediately upon startup, you will see a small ball moving around randomly.


Let's plan our code according to what we want to do with the app.

1. We want a ball - so we need a UIImageView AND a ball image.
2. We want it to move around  - so we need to figure out how the ball is going to move, and to which location. This will involve a little Maths.

First, lets declare the ball in the header file:


 UIImageView *myball; // ball declaration
...
@property (nonatomic, retain) IBOutlet UIImageView *myball; // ball declaration


Secondly, let's declare a method to move the ball.


 -(void)movetheball;


Now we have these two things set up, we can now able to reference the ball so that we can change its coordinate, via the method.

Before we go on to write the code in the main file (.m file), it is better we connect the ball (ie, myball) to the corresponding UIImageView in Interface Builder. Im assuming you already know how to do that, so just connect it to the File's Owner and you select the myball label appeared in the list.

Once it is connected, you can add a ball image to your resource folder. This image can be any name, but preferably of type PNG. Adding the image to your project is very easy - you just drag the image from Finder into the XCode left pane under "Resources" folder. A confirmation box will pop up and you just click Add. Once you added the image to your project, you need to display it in the UIImageView that you just placed in IB. So, in IB, select the UIImageView and goto the first tab (with a slider icon on it), and select your image from the drop down list of Image. That's all for setting up the interface side!

Now, we come to writing the code. Open up .m file of the project, and synthesize the ball, by typing:

 @synthesize myball;


Then, we need a global variable to store the coordinates of the ball, as follows (put this under @synthesize is good).

 int ballx, bally;


Note: Something about the iPhone screen/display: In portrait mode, its size is 320pixel wide and 480pixel high. A coordinate of 0,0 is at the top left hand corner (origin).

Now, in this app, we will make the ball start moving immediately after the app is loaded, so we make use the viewdidload method. Write the code as below:


 // Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
 
 ballx = arc4random() % 320; // this will create a random number from 0-320
 bally = arc4random() % 480; // this will create a random number from 0-480
        [self movetheball];
 [super viewDidLoad];
}


What the code above is doing is:
1. set x-coordinate to be a random position between 0 and 319 horizontally.
2. set y-coordinate to be a random position between 0 and 479 vertically.
3. call the movetheball method (custom method that we need to write).

Next, lets write the movetheball custom method. This method needs to do:
1. Take the UIImageView myball,
2. do animation movement on it within 1 second period.

I comment the method's code for the description.

 -(void)movetheball {
        // tell the app we are making animation. The name MovingTheBallAround is arbitrary.
 [UIView beginAnimations: @"MovingTheBallAround" context: nil]; 
        // set the delegate to self - otherwise the delegate method wont be called!
 [UIView setAnimationDelegate: self]; 
        // this line set the animation length in seconds.
 [UIView setAnimationDuration: 1.0];
        // this line specify the type of animation. EaseInOut, EaseIn, EaseOut and Linear are the options.
        // try to change it and see the effect.
 [UIView setAnimationCurve: UIViewAnimationCurveEaseInOut];
 
        // just before calling commitAnimations below, you write the code to make the changes of the
        // imageview and XCode will do the rest. It is that simple.
        // In this case, we are setting coordinates of the image to ballx and bally.
 myball.frame =  CGRectMake(ballx, bally,myball.frame.size.width,myball.frame.size.height);
 
 [UIView commitAnimations]; 
}

Next, since we want the ball to move forever, we need to move the ball again after it has finished the movement/translation. So, how do we know when the ball has finished moving? Here comes the animationdidStop DELEGATE method. Delegate method is something like a public method for a certain actions - in this case for the animation action. Whenever any animation stop, this method will be called. All you need to do is handle the messages the app sent to it.

So in the delegate method, we need to re-set the ball coordinate to another random value and then, call the movetheball method again to animate it to a new position.


 - (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(SEL)aSelector {
 if (finished) {
  // set new coordinate for ballx and bally
  ballx = arc4random() % 320; // this will create a random number from 0-320
  bally = arc4random() % 480; // this will create a random number from 0-480
   // then recall the move ball method
  [self movetheball];
 }
}

Do note that delegate methods' name are pretty much fixed. You must write the name as it is, in this case animationDidStop. Do not spell it wrongly (which is a common mistake among new coders).

Play around with the values and see what happens.


Friday, November 20, 2009

A Simple About Box

Hi fellas. I've been wanting to write 2nd tutorial for sometime, but really is pushing my own schedule to submit my 3rd app. Anyways, those who stopped by, do notice that this is a tutorial blog for Noobies. Like me. If you are looking for advanced stuffs, I'm not there yet. Perhaps sometime in the future, xcodeadvanced.blogspot.com would be born. =p


Anyways, getting back to main subject: Today's tutorial is to create a simple About Box that displays your information - such as website URL, version of the app, your name, and a logo of your company. Below is how it will look like:



Before I go on with this tutorial, I do realize that some dev likes to use a full page of UIView to display as About Box and put all fancy backgrounds etc. For me, I don't like to waste resources so much in stuffs that are not benefiting the users. So I make full use of the UIAlertView class.

Lets start:

First, I create a new project -> A Single-View Template. (I love Single View!) In this project, I gave it a name of ImageInAlert.proj.

Next, we declare an IBAction method/function in the ImageInAlertViewController.h (header file).

 -(IBAction)showAboutBox;

We do not need to know who is calling the method, so the (id)sender extension parameter is omitted. Once this is done, SAVE IT, and then open the ImageInAlertViewController.xib. Put a UIButton on it. Put a title on the UIButton, mine says "About".

Now, select the UIButton and go to Connections window and select the second tab (with a picture of an arrow pointing to the right on it) Under "Events" section, connect the "Touch Up Inside" to the File's Owner icon in the Main xib window. You need to move around your View if you can't see the Main xib window.

You should see a list pop up, select showAboutBox. And now the button is connected to our method/function. SAVE THE XIB FILE.

*Make it a habit to save every now and then.. because if you don't save, what you have typed in or changed, will not be updated in XCode, therefore can't be detected in IB, and vice versa.*

Now go back to the Main file: ImageInAlertViewController.m. Type the following code:


 -(IBAction)showAboutBox {
// Part 1
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:@"AlertView Tutorial"
message:@"\n"
@"\n"
@"\n" // Part 2
@"\n"
@"\n"
@"Created by: \n"
@"Emir F Samsuddin \n"
@"http://xcodenoobie.blogspot.com"
delegate:nil
cancelButtonTitle:@"Ok" // Part 3
otherButtonTitles:nil];
// Part 4
UIImageView *iemirlogo = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"MainIcon.png"]];
iemirlogo.contentMode = UIViewContentModeScaleToFill; // Part 5
iemirlogo.frame = CGRectMake(90, 40, 100, 120); // Part 6
[alert addSubview:iemirlogo]; // Part 7
[iemirlogo release]; // Part 8

[alert show]; // Part 9
[alert release]; // Part 10

}

Ok, Im going to explain part by part: Refer to the above label while reading below:

Part 1: Declare a name for the UIAlertView object that we are going to create. I give it a name of alert.

Part 2: All the /n are so that it skip a line. Its like pressing the Enter/Return button on your Mac. I do this so that I can have a space to put my image there later on.

Part 3: Cancelbuttontitle is the title for the cancel button (ie the button that when user pressed, will dismiss the about box)

Part 4: Here is the fancy part (well for a noobie anyways), we create an imageview object, and directly load an image from project resources into it. "MainIcon.png" is already been dragged into the Resources folder. This is the image that I will make appear at the alert view.

Part 5: Set the image property so that the image is scaled following the size of the UIImageView frame. The property name is contentMode and the value is UIViewContentModeScaleToFill

Part 6: Define the size and location of the image in the form of CGRectMake(x,y,width,height). x and y is the location of the image, measuring from the top left hand corner of the image. width and height, er.. are the Width and Height of the image frame. Try experimenting with these values to get what you finally want.

Part 7: Finally, we add the image to the AlertView. addSubview is a useful method to add anything to anything.

Part 8: We have added the image to the AlertView, so we clear/release the image from memory.

Part 9: Show the alert box. (ie, make it pop out)

Part 10: We have shown the alert to the main view, so we clear/release the alert from memory.



Tuesday, November 10, 2009

Creating Flashing Custom Button



If you used UIButton before, you will know that there are 2 iPhone built-in info buttons, which functions to go to Settings view of an app. They are those circular button with an "i" at the center of it.
If you tap on it, it will flash and the effect is pretty nice.


Now, I wanted to use this button for my app because of its flashing effect, but not for the settings purpose. So I decided to make my own flashing button. (Do note that if you use the built in info button for other purposes, your app MAY BE REJECTED, because it is not what Apple intended them to be).

Anyway, back to the main subject: Creating a flashing button. 

1. First you need to create your own button image. I used the GIMP program which ran off X11 terminal in my Mac. I love GIMP. Because its function is a lot similar like Paint Shop Pro/ PhotoShop program in my Windows box. All the tools are so familiar.

2. Once you created the button image, save it as PNG and set the background of that image as transparent - you need to know your graphics program to do this.

3. Next, create another image that resembles a white flash (or whatever color u wish). You can use the radial gradient tool to accomplish this. Make sure the background of the image is transparent too.

4. Once you have both image ready, then you can load it up in the "Resources" of your XCode project. (Just drag and drop under the Resources folder in the XCode).

5. Next, declare the white flash image in header file, this case, its the FlashingButtonViewController.h.

 
IBOutlet UIImageView *flashimage;

and also the related declaration as @property.

6. Go to FlashingButtonViewController.m main file, and add to synthesize:

 
@synthesize flashimage;

7. After that, declare the function that is going to be called when user tap the button. So we declare in FlashingButtonViewController.h:-

 
-(IBAction)custombtnclicked:(id)sender;

8. Then return back to Main file and write the function for custombtnclicked:-

 
-(IBAction)custombtnclicked:(id)sender {

[self flashit:flashimage];

}

[self flashit:flashimage]; is just a way to call another function from this function. I do it this way so that, if you have many other buttons you want to behave this way, then you don't need to rewrite a long function. So the function to flash the flashimage is:

 

-(void)flashit:(id)sender {
UIImageView *img = (UIImageView*)sender;
img.alpha = 0.8;
[UIImageView beginAnimations: @"FadeOut" context: nil];
[UIImageView setAnimationDelegate: self];
[UIImageView setAnimationDuration: 0.5];
[UIImageView setAnimationCurve: UIViewAnimationCurveEaseInOut];
img.alpha = img.alpha - 0.8;
[UIImageView commitAnimations];
}


What this function does is: declare the "sender" parameter as an imageview. Then set its alpha to 0.8 (1 is fully opaque, and 0 is invisible). Then we start to create animation of UIImageView.
We set the duration to 0.5seconds for the whole of animation of flashing. And the type is Curve Ease In and Out.This means the animation will start slowly, then fast, then back to slow before stopping.

Next, we tell XCode what to do with the image. This just return back the alpha channel back to 0 (invisible).

Basically, when user taps the button, we set the flashimage to fully opaque, then animate the fading out of the flashimage for 0.5seconds. Finally we tell XCode to execute the animation by calling commitAnimations. XCode will do the rest.

9. Now that you have all the codes ready, its left only to open up FlashingButtonViewController.xib in Interface Builder and place the buttons in and images in. But remember to place the flash image BEHIND the button, so that user can touch the button. You can use the Layout menu of XCode and selecting "Send to Back" function to send the flashimage to the back of the button. Finally, set the alpha of the UIImageView to 0, so that it is invisible.

10. Once all user interface items are created, it is time to link them to XCode variable declaration. Connect the "Touch Up Inside" of the UIButton to the function called customButtonclicked: and set the Outlet of the flash image to "flashimage".

11. Save all files and everything. Then it should be able to run and when you click the button in the simulator, it will flash and fade away.

Thats all.