Wednesday, 29 June 2011

Question: What does this "^" symbol mean?

Saw a few tutorials (e.g. this one from the App Code Blog) using this "new" method for animation that works only on iOS4 and above... Don't quite understand what the hell does that "^" symbol mean.... Looks like still got lots of things to learn...


    [UIView animateWithDuration:0.5 delay:0.0 options: UIViewAnimationCurveEaseOut
                     animations:^
     {
         navigationBar.frame = navBarFrame;
     }
                     completion:^(BOOL finished)
     {
         NSLog(@"Done!");
     }
     ];

Tuesday, 28 June 2011

Crash caused by missing ":"

Was testing an array of buttons all using the same "buttonPressed" method to handle the click action, and based on tag value to work out which button been clicked. It keep crashing whenever I clicked any of the buttons and couldn't work out why.


    UIButton *button;
    
    [self createNormalButton: button 
                      atPosX: locX
                      atPosY: locY 
                   withWidth: widthValue
                  withHeight: heightValue  
                 withBGColor: [UIColor clearColor
              withTitleColor: [UIColor clearColor
                     withTag: tagValue 
                   withTitle@""
                  withSelfIDself 
                withActionID@selector(buttonPressed)
                   ifEnabledYES
                      inViewself.view];



- (IBAction) buttonPressed: (id) sender
{
    
    int whichButton;
    
    whichButton = (int) ((UIButton *)sender).tag;
    
    NSLog(@"Clicked button tag=%d",whichButton);
}



The error message is as below:


2011-06-28 23:35:57.374 NoNIBTest1[1758:207] -[TestBedViewController buttonPressed]: unrecognized selector sent to instance 0x4e07340
2011-06-28 23:35:57.377 NoNIBTest1[1758:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[TestBedViewController buttonPressed]: unrecognized selector sent to instance 0x4e07340'



Took me a while to realise because this "buttonPressed" method is now accepting/expecting an extra parameter "(id) sender", I have to add an extra ":" at the end when passing the selector ID for action.

So it should be "@selector(buttonPressed:)" with the extra ":" at the end. Quite strange compared to what I learned before in other programming languages.


    UIButton *button;
    
    [self createNormalButton: button 
                      atPosX: locX
                      atPosY: locY 
                   withWidth: widthValue
                  withHeight: heightValue  
                 withBGColor: [UIColor clearColor
              withTitleColor: [UIColor clearColor
                     withTag: tagValue 
                   withTitle: @""
                  withSelfID: self 
                withActionID: @selector(buttonPressed:)
                   ifEnabled: YES
                      inViewself.view];
    


How to hide the status bar by code

This might be a simple one, and mostly been done by changing the plist file. Saw this example using code to hide the status bar on the top of the screen, so quickly note it down in case I need to use/find it again in the future...


- (void)applicationDidFinishLaunching:(UIApplication *)application {
    
    [application setStatusBarHidden:YES];
    
    [window addSubview:viewController.view];
    [window makeKeyAndVisible];
}

Monday, 27 June 2011

First working No NIB (NIBless) Drawing project using Quartz 2D

It took me a while after looking at different examples on the net and books - finally got it working. My first No NIB Drawing project using Quartz 2D.

The whole project code as below. Also learned from book to use "#define" to declare the MAX_X and MAX_Y values. May be if running the same code on different devices with different resolution (iPad, iPhone X, ...etc) or when user turned the device into different orientation can somehow work out the Max resolution and adjust it accordingly in the future?

And yes, that looks like a tic-tac-toe game... Still haven't work out how to do the other stuffs yet... ha ha... let's wait and see...



#import <UIKit/UIKit.h>

#define MAX_X 320.0
#define MAX_Y 480.0

@interface draw2D:UIView
@end

@implementation draw2D

- (id)initWithFrame:(CGRect)frame {
    if (self == [super initWithFrame:frame]) {
        // Initialization code
    }
    return self;
}

- (void)drawRect:(CGRect)rect {
        
    /* Set the color */ 
    [[UIColor brownColor] set];
    /* Get the current graphics context */ 
    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    /* Set the width for the line */ 
    CGContextSetLineWidth(currentContext, 5.0f);

    CGContextMoveToPoint(currentContext, 0, (MAX_Y/3));
    CGContextAddLineToPoint(currentContext, MAX_X, (MAX_Y/3));
    CGContextStrokePath(currentContext);

    CGContextMoveToPoint(currentContext, 0, (MAX_Y/3)*2);
    CGContextAddLineToPoint(currentContext, MAX_X, (MAX_Y/3)*2);
    CGContextStrokePath(currentContext);
    
    CGContextMoveToPoint(currentContext, (MAX_X/3), 0);
    CGContextAddLineToPoint(currentContext, (MAX_X/3), MAX_Y);
    CGContextStrokePath(currentContext);

    CGContextMoveToPoint(currentContext, (MAX_X/3)*2, 0);
    CGContextAddLineToPoint(currentContext, (MAX_X/3)*2, MAX_Y);
    CGContextStrokePath(currentContext);
}
@end

@interface TestBedAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow *window;
    
}
@property (nonatomic, retain) UIWindow *window;

@end

@implementation TestBedAppDelegate

@synthesize window;

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];   
    
    draw2D *view = [[draw2D alloc] initWithFrame:CGRectMake(0.0, 0.0, MAX_X, MAX_Y)];
    
    [self.window addSubview:view];
    [view release];
    
    [self.window makeKeyAndVisible];
    
}

- (void)dealloc {
    [window release];
    [super dealloc];
}
@end

int main(int argc, char *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, @"TestBedAppDelegate");
[pool release];
return retVal;
}

Sunday, 26 June 2011

Question: Differences in handling touch event between UIView and UIImageView?

I was studying about the "touch" events on iOS4 and looking at some examples from various areas, including books and net. Have a few little projects listed below.

Project 1

This code below is from chapter 8 of iPhone Developer's cookbook 2nd Edition by Erica Sadun. I quite like Erica's style of putting everything in one single "main.m" file, may be not suitable for bigger projects but this is definitely less confusing for beginners like me. You can see it uses a "UIImageView" class called "DragView" to handle the touch events of each flower images. You can freely drag each individual flower images around. (Note: there's a NIB file required, please see link to download source code far below).


/*
 Erica Sadun, http://ericasadun.com
 iPhone Developer's Cookbook, 3.0 Edition
 BSD License, Use at your own risk
 */

#import <UIKit/UIKit.h>

#define COOKBOOK_PURPLE_COLOR [UIColor colorWithRed:0.20392f green:0.19607f blue:0.61176f alpha:1.0f]

@interface DragView : UIImageView
{
CGPoint startLocation;
}
@end

@implementation DragView
- (id) initWithImage: (UIImage *) anImage
{
if (self = [super initWithImage:anImage])
self.userInteractionEnabled = YES;
return self;
}

- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
// Calculate and store offset, and pop view into front if needed
CGPoint pt = [[touches anyObject] locationInView:self];
startLocation = pt;
[[self superview] bringSubviewToFront:self];
}

- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
// Calculate offset
CGPoint pt = [[touches anyObject] locationInView:self];
float dx = pt.x - startLocation.x;
float dy = pt.y - startLocation.y;
CGPoint newcenter = CGPointMake(self.center.x + dx, self.center.y + dy);

// Set new location
self.center = newcenter;
}
@end


@interface TestBedViewController : UIViewController
@end

@implementation TestBedViewController
#define MAXFLOWERS 12

CGPoint randomPoint() 
{
int half = 32; // half of flower size
int freesize = 240 - 2 * half; // inner area
return CGPointMake(random() % freesize + half, random() % freesize + half);
}

- (void) viewDidLoad
{
self.navigationController.navigationBar.tintColor = COOKBOOK_PURPLE_COLOR;
srandom(time(0));
// Add the flowers to random points on the screen
for (int i = 0; i < MAXFLOWERS; i++)
{
NSString *whichFlower = [[NSArray arrayWithObjects:@"blueFlower.png", @"pinkFlower.png", @"orangeFlower.png", nil] objectAtIndex:(random() % 3)];
DragView *dragger = [[DragView alloc] initWithImage:[UIImage imageNamed:whichFlower]];
dragger.center = randomPoint();
[self.view addSubview:dragger];
[dragger release];
}

}
@end

@interface TestBedAppDelegate : NSObject <UIApplicationDelegate>
@end

@implementation TestBedAppDelegate
- (void)applicationDidFinishLaunching:(UIApplication *)application {
UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:[[TestBedViewController alloc] init]];
[window addSubview:nav.view];
[window makeKeyAndVisible];
}
@end

int main(int argc, char *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, @"TestBedAppDelegate");
[pool release];
return retVal;
}






Project 2


Then based on Chapter 7 example code from the iPhone Application Development for iOS4 Visual QuickStart guide book, I created this little NIB-less project with only 1 file: "main.m" which has 3 boxes that you can drag around.

Compared with the previous one, you can see it's using UIView instead of UIImageView. The "touchesMoved" method is also cleaner as you don't need to calculate the offset as the UIImageView ones above.





#import <UIKit/UIKit.h>

@interface TestBedViewController : UIViewController
{
UIView *redBox;
UIView *blueBox;
UIView *greenBox;
}
@end

@implementation TestBedViewController

- (void)loadView {
    
self.view=[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
self.view.backgroundColor=[UIColor whiteColor];
    
float boxSize = 100.0;
CGRect redBoxRect = CGRectMake(0,180,boxSize,boxSize);
redBox = [[UIView alloc] initWithFrame:redBoxRect];
redBox.backgroundColor = [UIColor redColor];
    
CGRect blueBoxRect = CGRectMake(110,180,boxSize,boxSize);
blueBox = [[UIView alloc] initWithFrame:blueBoxRect];
blueBox.backgroundColor = [UIColor blueColor];
    
CGRect greenBoxRect = CGRectMake(220,180,boxSize,boxSize);
greenBox = [[UIView alloc] initWithFrame:greenBoxRect];
greenBox.backgroundColor = [UIColor greenColor];
[self.view addSubview:redBox];
[self.view addSubview:blueBox];
[self.view addSubview:greenBox];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    
UITouch *touch = [[event allTouches] anyObject];
CGPoint currentPoint = [touch locationInView:self.view];
if (touch.view != self.view)
[touch view].center = currentPoint;
    
}
- (void)dealloc {
[redBox release];
redBox = nil;
[blueBox release];
blueBox = nil;
    
[greenBox release];
greenBox = nil;
    
[super dealloc];
}

@end

@interface TestBedAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow *window;
    TestBedViewController *viewController;
}
@property (nonatomic, retain) UIWindow *window;
@property (nonatomic, retain) TestBedViewController *viewController;

@end

@implementation TestBedAppDelegate

@synthesize window;
@synthesize viewController;

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];   
self.viewController = [TestBedViewController alloc];
[self.window addSubview:self.viewController.view];
    [self.window makeKeyAndVisible];
    
}

- (void)dealloc {
    [viewController release];
    [window release];
    [super dealloc];
}
@end

int main(int argc, char *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, @"TestBedAppDelegate");
[pool release];
return retVal;
}


My questions then are:
(1) Which is more useful/more frequently used/better?
(2) If I have mixed UIView and UIImageView objects, can I merged them into one method?
(3) I can think about scenarios which you create a little "sprite" which moves around the screen (don't know how to do that yet :-( ...) and user can touch and drag it around the screen. AS it's an image (??) then it should be using the UIImageView way, right?

Still too many unknowns at this early stage... So, iOS programming in indeed not that easy I guess...



The sample code from iPhone Developer's cookbook 2nd Edition is in chapter 8 of http://github.com/erica/iphone-3.0-cookbook-/tree.


The sample code from iPhone Application Development for iOS4 Visual QuickStart guide is in chapter 7 of http://objective-d.com/?page_id=160/.