Showing posts with label Sample Code. Show all posts
Showing posts with label Sample Code. Show all posts

Saturday, June 11, 2011

iCloud App Sample and entitlement code-sign error

iOS 5 beta has iCloud support now, but getting the entitlement code-sign to work has some steps that are missing from Apple Documentation.

You should use the new Xcode 4.2 for iOS 5 SDK and with iOS5 on an iDevice.

1) You need to create a new App ID in the iOS Provisional Portal, if you use an existing ID, it must not be a wildcard one. For example, you create a new APP ID called com.yourcompany.icloudtest and the App Name is called icloudtest
When you create the New App ID, you will be asked whether to Generate New or select an existing one for the Prefix (existing prefix can have the benefit of sharing the same keychain access or may be iCloud Document folder). Suppose we generate a new prefix in this case. And the new generated prefix is 99Z9Z98987

2) After created the New App ID, you should first enable iCloud for that New App ID and then create the a New Provisional Profile for that New App icloudtest.

3) Suppose the new Development Profile called "iCloudTest" is created and downloaded to the Mac.

4) Launch Xcode and created an empty Universal Application called "icloudtest" and the Bundle Identifier should be com.yourcompany.icloudtest



5) Import the Development Profile "iCloudTest.mobileprovision" to Xcode and to iPhone/iPad which should be iOS 5 beta

6) Click the Build Settings of the Project and Select the code sign identity to the "iCloudTest" Provisional Profile

7) Click the Summary Settings of the Project and scroll down to entitlements and click the Custom Entitlements, Xcode will create a file called icloudtest.entitlements


8) Click the icloudtest.entitlements in the Project Explorer and add these key rows (Right Click -> Add Row) in the entitlements file
com.apple.developer.ubiquity-kvstore-identifier is String Type
com.apple.developer.ubiquity-container-identifiers is Array Type



9) Enter the Team-ID.com.yourcompany.icloudtest into the Value column. The important part is where to get the Team-ID ?

10) Don't confuse with the Team-ID with the App ID that the development portal generated for you. If you look at the "iCloudTest.mobileprovision" file downloaded as in step 5 above. You will see something like this

Yes, ZZZZ826ZZ2 is the Team-ID where 99Z9Z989Z7 is the App ID in this case

iCloudTest.mobileprovision Select all

<key>Entitlements</key>
<dict>
<key>application-identifier</key>
<string>99Z9Z989Z7.com.yourcompany.icloudtest</string>
<key>com.apple.developer.ubiquity-container-identifiers</key>
<array>
<string>ZZZZ826ZZ2.*</string>
</array>
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
<string>ZZZZ826ZZ2.*</string>
<key>get-task-allow</key>
<true/>
<key>keychain-access-groups</key>
<array>
<string>99Z9Z989Z7.*</string>
</array>
</dict>



The Team-ID can also be found at Your Account area of Member Center
After login the iOS Dev Center, click
Member Center (in top navigation bar)-> Your Account -> Program Membership -> Individual ID



11) Enter the Team-ID + Bundle Identifier in the icloudtest.entitlements as below



icloudtest.entitlements Select all

<!--?xml version="1.0" encoding="UTF-8"?-->

<plist version="1.0">
<dict>
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
<string>ZZZZ826ZZ2.com.yourcompany.icloudtest</string>
<key>com.apple.developer.ubiquity-container-identifiers</key>
<array>
<string>ZZZZ826ZZ2.com.yourcompany.icloudtest</string>
</array>
</dict>
</plist>


12) Add these code in the icloudtestAppDelegate.m to test this iCloud App

icloudtestAppDelegate.m Select all

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

NSFileManager *fileManager = [NSFileManager defaultManager];
// Team-ID + Bundle Identifier
NSURL *iCloudURL = [fileManager URLForUbiquityContainerIdentifier:@"ZZZZ826ZZ2.com.yourcompany.icloudtest"];
NSLog(@"%@", [iCloudURL absoluteString]);

NSUbiquitousKeyValueStore *cloudStore = [NSUbiquitousKeyValueStore defaultStore];
[cloudStore setString:[iCloudURL absoluteString] forKey:@"iCloudURL"];
[cloudStore synchronize]; // Important as it stores the values you set before on iCloud

UILabel *myLabel = [[UILabel alloc] initWithFrame:CGRectMake(0,round(self.window.bounds.size.height/4.0),self.window.bounds.size.width,round(self.window.bounds.size.height/8.0))];
myLabel.font = [UIFont fontWithName:@"Marker Felt" size:round(self.window.bounds.size.width/20.0)];
myLabel.numberOfLines = 4;
myLabel.text =[ @"iCloudURL=" stringByAppendingFormat:@"%@", [cloudStore stringForKey:@"iCloudURL"]];
myLabel.backgroundColor = [UIColor clearColor];
myLabel.textColor = [UIColor whiteColor];
myLabel.textAlignment = UITextAlignmentCenter;
[self.window addSubview:myLabel];

[self.window makeKeyAndVisible];
return YES;
}



13) Run it in iOS device
.
.
.

Friday, September 3, 2010

How to calculate Easter Day in iPhone OS

Recently, I have to implement the calculation of Easter Day.

Here is what I got

Easter Day is always the Sunday after the full moon that occurs after the spring equinox on March 21. This full moon may happen on any date between March 21 and April 18 inclusive. If the full moon falls on a Sunday, Easter Day if the Sunday following. But Easter Day cannot be earlier than March 22 or later than April 25.

To find the the full moon day, I have used the NSChineseCalendar in iOS 4.

and my implementation is here
eastercal.m Select all

// eastercal.m
#import <Foundation/Foundation.h>
#include <stdio.h>

int main( int argc, char *argv[] )
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSDate *today = [NSDate date]; // today
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSUInteger unitFlags = NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit;
NSDateComponents *gregorianComps = [gregorian components:unitFlags fromDate:today];
if (argc==2) {
[gregorianComps setYear:atoi(argv[1])];
}
[gregorianComps setDay:21];
[gregorianComps setMonth:3];
NSDate *easterStartDate = [gregorian dateFromComponents:gregorianComps]; //March 21 for the year

NSCalendar *chinese = [[NSCalendar alloc] initWithCalendarIdentifier:NSChineseCalendar];
// convert from gregorian calendar to chinese calendar
NSDateComponents *chineseComps = [chinese components:unitFlags fromDate:easterStartDate];
NSDate *easterStartDateChineseDate = [chinese dateFromComponents:chineseComps];

NSDate *easterStartDateChineseDateTemp = easterStartDateChineseDate;
if ([chineseComps day] >=15) { // 15 is the full month day in Chinese Calendar
NSDateComponents *offsetComponents = [[NSDateComponents alloc] init];
[offsetComponents setMonth:1]; // set the next month
easterStartDateChineseDateTemp = [chinese dateByAddingComponents:offsetComponents toDate:easterStartDateChineseDate options:0];
}
NSDateComponents *dayComponents = [chinese components:NSDayCalendarUnit fromDate:easterStartDateChineseDateTemp];
NSDateComponents *componentsToAdd = [[NSDateComponents alloc] init];
[componentsToAdd setDay: (15 - [dayComponents day])];
// find the next full month date
NSDate *springEquinoChineseDate = [chinese dateByAddingComponents:componentsToAdd toDate:easterStartDateChineseDateTemp options:0];

NSDateComponents *springEquinoChineseComps = [chinese components:unitFlags fromDate:springEquinoChineseDate];


NSDateComponents *diffComps = [chinese components:NSDayCalendarUnit fromDate:easterStartDateChineseDate toDate:springEquinoChineseDate options:0];
NSInteger diffDays = [diffComps day];

// calculate the days difference from the March 21 to the next full month day
NSLog(@"diffDays is %ld",diffDays);

NSDateComponents *daysToAdd = [[NSDateComponents alloc] init];
[daysToAdd setDay:diffDays];
NSDate *springEquinoGregorianDate = [gregorian dateByAddingComponents:daysToAdd toDate:easterStartDate options:0];

// convert the next full month date from ChineseDate to GregorianComps
NSDateComponents *springEquinoGregorianComps = [gregorian components:unitFlags fromDate:springEquinoGregorianDate];

NSLog(@"springEquinoGregorian is %ld %ld %ld",[springEquinoGregorianComps year], [springEquinoGregorianComps month], [springEquinoGregorianComps day]);

int weekday = [springEquinoGregorianComps weekday];
NSDate *easterSundayGregorianDateTemp = springEquinoGregorianDate;
NSDateComponents *offsetGregorianComponents = [[NSDateComponents alloc] init];
if (weekday == 7) {
[offsetGregorianComponents setWeek:2]; // If the full moon falls on a Sunday, Easter Day if the Sunday following
}
else {
[offsetGregorianComponents setWeek:1];
}
easterSundayGregorianDateTemp = [gregorian dateByAddingComponents:offsetGregorianComponents toDate:springEquinoGregorianDate options:0];
NSDateComponents *weekdayComponents = [gregorian components:NSWeekdayCalendarUnit fromDate:easterSundayGregorianDateTemp];
NSDateComponents *componentsToSubtract = [[NSDateComponents alloc] init];
[componentsToSubtract setDay: 0 - ([weekdayComponents weekday] - 1)];
NSDate *easterDate = [gregorian dateByAddingComponents:componentsToSubtract toDate:easterSundayGregorianDateTemp options:0];

NSDateComponents *easterComps = [gregorian components:unitFlags fromDate:easterDate];
NSLog(@"Easter Date is %d %d %d",[easterComps year], [easterComps month], [easterComps day]);
[pool drain];
return 0;
}



This code does not run on Mac OS 10.5 or iOS 3 as the ChineseCalendar was not implemented.
It will only run on Mac OS 10.6 or iOS 4


iPhone-4:root# gcc -I/var/toolchain/sys30/usr/include -F/var/toolchain/sys30/System/Library/Frameworks -L/var/toolchain/sys30/usr/lib -framework Foundation eastercal.m -o eastercal
iPhone-4:root# ldid -S eastercal
iPhone-4:root# ./eastercal 2011
eastercal[3005:107] diffDays is 27
eastercal[3005:107] springEquinoGregorian is 2011 4 17
eastercal[3005:107] Easter Date is 2011 4 24

.
.
.

Saturday, April 10, 2010

Local Notification Sample Code (OS 4.0 only)

Start a new Window-based Application Project called LocalPush

Add an instance variable bgTask in LocalPushAppDelegate


@interface LocalPushAppDelegate : NSObject {
    UIWindow *window;
    UIBackgroundTaskIdentifier bgTask;
}


It will fire up a Local Notification to remind you one min before the event which is 2 minutes due from now

LocalPushAppDelegate.m Select all

//
// LocalPushAppDelegate.m
// LocalPush
//

@interface ToDoItem : NSObject {
NSInteger year;
NSInteger month;
NSInteger day;
NSInteger hour;
NSInteger minute;
NSInteger second;
NSString *eventName;
}

@property (nonatomic, readwrite) NSInteger year;
@property (nonatomic, readwrite) NSInteger month;
@property (nonatomic, readwrite) NSInteger day;
@property (nonatomic, readwrite) NSInteger hour;
@property (nonatomic, readwrite) NSInteger minute;
@property (nonatomic, readwrite) NSInteger second;
@property (nonatomic, copy) NSString *eventName;

@end

@implementation ToDoItem

@synthesize year, month, day, hour, minute, second, eventName;

@end

#import "LocalPushAppDelegate.h"

@implementation LocalPushAppDelegate

@synthesize window;

#define ToDoItemKey @"EVENTKEY1"
#define MessageTitleKey @"MSGKEY1"


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSLog(@"application: didFinishLaunchingWithOptions:");
// Override point for customization after application launch

UILocalNotification *localNotif = [launchOptions
objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];

if (localNotif) {
NSString *itemName = [localNotif.userInfo objectForKey:ToDoItemKey];
// [viewController displayItem:itemName]; // custom method
application.applicationIconBadgeNumber = localNotif.applicationIconBadgeNumber-1;
NSLog(@"has localNotif %@",itemName);
}
else {
[[UIApplication sharedApplication] cancelAllLocalNotifications];
NSDate *now = [NSDate date];
NSLog(@"now is %@",now);
NSDate *scheduled = [now dateByAddingTimeInterval:120] ; //get x minute after
NSCalendar *calendar = [NSCalendar currentCalendar];

unsigned int unitFlags = NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit|NSHourCalendarUnit|NSMinuteCalendarUnit;
NSDateComponents *comp = [calendar components:unitFlags fromDate:scheduled];

NSLog(@"scheduled is %@",scheduled);

ToDoItem *todoitem = [[ToDoItem alloc] init];

todoitem.day = [comp day];
todoitem.month = [comp month];
todoitem.year = [comp year];
todoitem.hour = [comp hour];
todoitem.minute = [comp minute];
todoitem.eventName = @"Testing Event";

[self scheduleNotificationWithItem:todoitem interval:1];
NSLog(@"scheduleNotificationWithItem");
}
[window makeKeyAndVisible];
return YES;
}

- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notif {
NSLog(@"application: didReceiveLocalNotification:");
NSString *itemName = [notif.userInfo objectForKey:ToDoItemKey];
NSString *messageTitle = [notif.userInfo objectForKey:MessageTitleKey];
// [viewController displayItem:itemName]; // custom method
[self _showAlert:itemName withTitle:messageTitle];
NSLog(@"Receive Local Notification while the app is still running...");
NSLog(@"current notification is %@",notif);
application.applicationIconBadgeNumber = notif.applicationIconBadgeNumber-1;

}

- (void) _showAlert:(NSString*)pushmessage withTitle:(NSString*)title
{

UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:title message:pushmessage delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
[alertView show];
if (alertView) {
[alertView release];
}
}


- (void)scheduleNotificationWithItem:(ToDoItem *)item interval:(int)minutesBefore {
NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
NSDateComponents *dateComps = [[NSDateComponents alloc] init];
[dateComps setDay:item.day];
[dateComps setMonth:item.month];
[dateComps setYear:item.year];
[dateComps setHour:item.hour];
[dateComps setMinute:item.minute];
NSDate *itemDate = [calendar dateFromComponents:dateComps];
[dateComps release];


UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif == nil)
return;
localNotif.fireDate = [itemDate dateByAddingTimeInterval:-(minutesBefore*60)];
NSLog(@"fireDate is %@",localNotif.fireDate);
localNotif.timeZone = [NSTimeZone defaultTimeZone];

localNotif.alertBody = [NSString stringWithFormat:NSLocalizedString(@"%@ in %i minutes.", nil),
item.eventName, minutesBefore];
localNotif.alertAction = NSLocalizedString(@"View Details", nil);

localNotif.soundName = UILocalNotificationDefaultSoundName;
localNotif.applicationIconBadgeNumber = 1;
// NSDictionary *infoDict = [NSDictionary dictionaryWithObject:item.eventName forKey:ToDoItemKey];
NSDictionary *infoDict = [NSDictionary dictionaryWithObjectsAndKeys:item.eventName,ToDoItemKey, @"Local Push received while running", MessageTitleKey, nil];
localNotif.userInfo = infoDict;

[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
NSLog(@"scheduledLocalNotifications are %@", [[UIApplication sharedApplication] scheduledLocalNotifications]);
[localNotif release];
}

- (NSString *) checkForIncomingChat {
return @"javacom";
};

- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(@"Application entered background state.");
// UIBackgroundTaskIdentifier bgTask is instance variable
// UIInvalidBackgroundTask has been renamed to UIBackgroundTaskInvalid
NSAssert(self->bgTask == UIBackgroundTaskInvalid, nil);

bgTask = [application beginBackgroundTaskWithExpirationHandler: ^{
dispatch_async(dispatch_get_main_queue(), ^{
[application endBackgroundTask:self->bgTask];
self->bgTask = UIBackgroundTaskInvalid;
});
}];

dispatch_async(dispatch_get_main_queue(), ^{
while ([application backgroundTimeRemaining] > 1.0) {
NSString *friend = [self checkForIncomingChat];
if (friend) {
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif) {
localNotif.alertBody = [NSString stringWithFormat:
NSLocalizedString(@"%@ has a message for you.", nil), friend];
localNotif.alertAction = NSLocalizedString(@"Read Msg", nil);
localNotif.soundName = @"alarmsound.caf";
localNotif.applicationIconBadgeNumber = 1;
NSDictionary *infoDict = [NSDictionary dictionaryWithObjectsAndKeys:@"Your Background Task works",ToDoItemKey, @"Message from javacom", MessageTitleKey, nil];
localNotif.userInfo = infoDict;
[application presentLocalNotificationNow:localNotif];
[localNotif release];
friend = nil;
break;
}
}
}
[application endBackgroundTask:self->bgTask];
self->bgTask = UIBackgroundTaskInvalid;
});
}


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


@end




.
.
.

Wednesday, April 15, 2009

APNS : Pushing tweets to iPhone

see here :
http://arstechnica.com/apple/guides/2009/04/pushing-tweets-to-your-iphone-with-apple-push-notifications.ars

You can modify it to get the RSS feed as well. see code sample here

pushtweet.m Select all

while (1 > 0)
{

TreeNode *root = [[XMLParser sharedInstance] parseXMLFromURL: [NSURL URLWithString:URL_STRING]];
TreeNode *found = nil;
for (TreeNode *node in [root children])
{
if (![[node key] isEqualToString:@"channel"]) continue;
if ([[node key] isEqualToString:@"channel"])
{
found = nil;
for (TreeNode *node2 in [node children]) {
// [node2 dump];
if ([[node2 key] isEqualToString:@"item"]) {
found = node2;
break;
}
}
if (found) break;
}
}

if (found)
{
NSString *testString = [NSString stringWithFormat:@"%@:%@", [found leafForKey:@"title"], [found leafForKey:@"link"]];
NSString *prevString = [NSString stringWithContentsOfFile:TWEET_FILE encoding:NSUTF8StringEncoding error:nil];
if (![prevString isEqualToString:testString])
{
// Update with the new tweet information
NSLog(@"\nNew RSS title from %@:\n \"%@\"\n\"%@\"\n", [found leafForKey:@"title"], [[found leafForKey:@"description"] substringToIndex:30], [found leafForKey:@"link"]);

// Save the unmessed tweet to the ~/.tweet file
[testString writeToFile:TWEET_FILE atomically:YES encoding:NSUTF8StringEncoding error:nil];

// handle reserved stuff. There's got to be a better way to escape
testString = [testString stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
testString = [testString stringByReplacingOccurrencesOfString:@"'" withString:@""];
testString = [testString stringByReplacingOccurrencesOfString:@":" withString:@"-"];
testString = [testString stringByReplacingOccurrencesOfString:@"{" withString:@"("];
testString = [testString stringByReplacingOccurrencesOfString:@"}" withString:@")"];

// push it
system([PUSH_CMD UTF8String]);
}
}

[NSThread sleepForTimeInterval:(double) delay];
if (SHOW_TICK) printf("tick\n");
}


There is also a Mac Xcode project sample on how to push from Desktop App here
http://stefan.hafeneger.name/download/PushMeBabySource.zip
 
 
 

Monday, April 6, 2009

OpenGL ES for iPhone : Part 3 with Accelerometer control

In this part 3, we will add the accelerometer control to move the position of ellipse object that we have created in part 2 of the Tutorial.



1) UIAccelerometerDelegate
We need to add the UIAccelerometerDelegate protocol to the EAGLView and implement the accelerometer: didAccelerate: method as below


@interface EAGLView : UIView <UIAccelerometerDelegate>

- (void)accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration;


We need to configure and start the accelerometer in the setupView method

[[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / kAccelerometerFrequency)];
[[UIAccelerometer sharedAccelerometer] setDelegate:self];


2) Accelerometer values
Inside the accelerometer: didAccelerate: method, we add a low-pass filter in the accelerometer values. This low-pass filter codes are sourced from the GLGravity Sample Code from Apple.

//Use a basic low-pass filter in the accelerometer values
accel[0] = acceleration.x * kFilteringFactor + accel[0] * (1.0 - kFilteringFactor);
accel[1] = acceleration.y * kFilteringFactor + accel[1] * (1.0 - kFilteringFactor);
accel[2] = acceleration.z * kFilteringFactor + accel[2] * (1.0 - kFilteringFactor);


The meaning of accelerometer values:

acceleration.x = Roll. It corresponds to roll, or rotation around the axis that runs from your home button to your earpiece. Values vary from 1.0 (rolled all the way to the right) to -1.0 (rolled all the way to the left).

acceleration.y = Pitch. Place your iPhone on the table and mentally draw a horizontal line about half-way down the screen. That's the axis around which the Y value rotates. Values go from 1.0 (the headphone jack straight down) to -1.0 (the headphone jack straight up).

acceleration.z = Face up/face down. It refers to whether your iPhone is face up (-1.0) or face down (1.0). When placed on it side, either the side with the volume controls and ringer switch, or the side directly opposite, the Z value equates to 0.0.

3) Control on movement of the ellipse is using the variables moveX and moveY and the ellipse position will be changed according to acceleration.x (that is accel[0]) and acceleration.y (that is accel[1]) values that passed from the Accelerometer control after the low-pass filter. The larger the absolute value of acceleration.x/acceleration.y, the greater for the magnitude for the value of moveX/moveY and thus the faster the ellipse will change its position to that direction. As the object should not move beyond the screen view, the ellipseData.pos.x and ellipseData.pos.y values will be governed by the boundaries of the screen.

 ellipseData.pos.x += moveX;
 if (accel[0] > -0.1 & accel[0] < 0.1 ) {
   moveX = 0.0f;
 }
 else {
  moveX = 10.0f * accel[0];
 }

 ellipseData.pos.y += moveY;
 if (accel[1] > -0.1 & accel[1] < 0.1 ) {
   moveY = 0.0f;
 }
 else {
   moveY = -10.0f * accel[1];
 }


4) Conditional compilation code for the iPhone Simulator and on-screen debug info
As iPhone Simulator does not have Accelerometer control, we have added the code that will change the ellipse position inside this compiler directive, so that the ellipse will keep moving on the iPhone Simulator.
  #if TARGET_IPHONE_SIMULATOR

Moroever, we have added a UILabel to the code so that we can read the Accelerometer values while we debug the program on actual device. This UILabel can be disabled using this define directive.
  #undef DEBUGSCREEN

5) The source codes are here, you just need to create a new project from OpenGL ES Application template of XCode and copy the source codes of EAGLView.h and EAGLView.m from below and paste them for Build & Go in XCode. The accelerometer control can only be tested on actual device.



EAGLView.h Select all

// EAGLView.h
// OpenGL ES Tutorial - Part 3 by javacom


// To enable Debug NSLog, add GCC_PREPROCESSOR_DEFINITIONS DEBUGON in Project Settings for Debug Build Only and replace NSLog() with DEBUGLOG()
#ifdef DEBUGON
#define DEBUGLOG if (DEBUGON) NSLog
#else
#define DEBUGLOG
#endif

#define DEBUGSCREEN

#import <UIKit/UIKit.h>
#import <OpenGLES/EAGL.h>
#import <OpenGLES/ES1/gl.h>
#import <OpenGLES/ES1/glext.h>

typedef struct
{
BOOL rotstop; // stop self rotation
BOOL touchInside; // finger tap inside of the object ?
BOOL scalestart; // start to scale the obejct ?
CGPoint pos; // position of the object on the screen
CGPoint startTouchPosition; // Start Touch Position
CGPoint currentTouchPosition; // Current Touch Position
GLfloat pinchDistance; // distance between two fingers pinch
GLfloat pinchDistanceShown; // distance that have shown on screen
GLfloat scale; // OpenGL scale factor of the object
GLfloat rotation; // OpenGL rotation factor of the object
GLfloat rotspeed; // control rotation speed of the object
} ObjectData;

/*
This class wraps the CAEAGLLayer from CoreAnimation into a convenient UIView subclass.
The view content is basically an EAGL surface you render your OpenGL scene into.
Note that setting the view non-opaque will only work if the EAGL surface has an alpha channel.
*/
@interface EAGLView : UIView {

@private
/* The pixel dimensions of the backbuffer */
GLint backingWidth;
GLint backingHeight;

EAGLContext *context;

/* OpenGL names for the renderbuffer and framebuffers used to render to this view */
GLuint viewRenderbuffer, viewFramebuffer;

/* OpenGL name for the depth buffer that is attached to viewFramebuffer, if it exists (0 if it does not exist) */
GLuint depthRenderbuffer;

NSTimer *animationTimer;
NSTimeInterval animationInterval;

@public
ObjectData squareData;
ObjectData ellipseData;
GLfloat ellipseVertices[720];
CGFloat initialDistance;
UIAccelerationValue accel[3];
GLfloat moveX, moveY;
#ifdef DEBUGSCREEN
UILabel *textView;
#endif
}

@property NSTimeInterval animationInterval;

@property (nonatomic) ObjectData squareData;
@property (nonatomic) ObjectData ellipseData;
@property CGFloat initialDistance;
#ifdef DEBUGSCREEN
@property (nonatomic, assign) UILabel *textView;
#endif

- (void)startAnimation;
- (void)stopAnimation;
- (void)drawView;
- (void)setupView;

@end


EAGLView.m Select all

// EAGLView.m
// OpenGL ES Tutorial - Part 3 by javacom
//
#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGLDrawable.h>

#import "EAGLView.h"

#include <math.h>

// Macros
#define degreesToRadians(__ANGLE__) (M_PI * (__ANGLE__) / 180.0)
#define radiansToDegrees(__ANGLE__) (180.0 * (__ANGLE__) / M_PI)

CGFloat distanceBetweenPoints (CGPoint first, CGPoint second) {
CGFloat deltaX = second.x - first.x;
CGFloat deltaY = second.y - first.y;
return sqrt(deltaX*deltaX + deltaY*deltaY );
};

CGFloat angleBetweenPoints(CGPoint first, CGPoint second) {
// atan((top - bottom)/(right - left))
CGFloat rads = atan((second.y - first.y) / (first.x - second.x));
return radiansToDegrees(rads);
}

CGFloat angleBetweenLines(CGPoint line1Start, CGPoint line1End, CGPoint line2Start, CGPoint line2End) {

CGFloat a = line1End.x - line1Start.x;
CGFloat b = line1End.y - line1Start.y;
CGFloat c = line2End.x - line2Start.x;
CGFloat d = line2End.y - line2Start.y;

CGFloat rads = acos(((a*c) + (b*d)) / ((sqrt(a*a + b*b)) * (sqrt(c*c + d*d))));

return radiansToDegrees(rads);
}

#define USE_DEPTH_BUFFER 0

// CONSTANTS
#define kMinimumTouchLength 30
#define kMaximumScale 7.0f
#define kMinimumPinchDelta 15
#define kAccelerometerFrequency 100.0 // Hz
#define kFilteringFactor 0.1


// A class extension to declare private methods
@interface EAGLView ()

@property (nonatomic, retain) EAGLContext *context;
@property (nonatomic, assign) NSTimer *animationTimer;

- (BOOL) createFramebuffer;
- (void) destroyFramebuffer;

@end


@implementation EAGLView

@synthesize context;
@synthesize animationTimer;
@synthesize animationInterval;
@synthesize squareData;
@synthesize ellipseData;
@synthesize initialDistance;
#ifdef DEBUGSCREEN
@synthesize textView;
#endif

// You must implement this method
+ (Class)layerClass {
return [CAEAGLLayer class];
}


//The GL view is stored in the nib file. When it's unarchived it's sent -initWithCoder:
- (id)initWithCoder:(NSCoder*)coder {

if ((self = [super initWithCoder:coder])) {

// Get the layer
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;

eaglLayer.opaque = YES;
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];

context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];

if (!context || ![EAGLContext setCurrentContext:context]) {
[self release];
return nil;
}

animationInterval = 1.0 / 60.0;
[self setupView];
}
return self;
}

// These are four methods touchesBegan, touchesMoved, touchesEnded, touchesCancelled and use to notify about touches and gestures

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/*
NSUInteger numTaps = [[touches anyObject] tapCount]; // number of taps
NSUInteger numTouches = [touches count]; // number of touches
*/
UITouch *touch = [[touches allObjects] objectAtIndex:0];

DEBUGLOG(@"TouchBegan event counts = %d ",[[event touchesForView:self] count]);
DEBUGLOG(@"TouchBegan tounches counts = %d ",[touches count]);
if ([touches count]== 2) {
NSArray *twoTouches = [touches allObjects];
UITouch *first = [twoTouches objectAtIndex:0];
UITouch *second = [twoTouches objectAtIndex:1];
initialDistance = distanceBetweenPoints([first locationInView:self], [second locationInView:self]);
squareData.rotstop = YES;
squareData.touchInside = NO;
}
else if ([touches count]==[[event touchesForView:self] count] & [[event touchesForView:self] count] == 1) {
squareData.startTouchPosition = [touch locationInView:self];
if (distanceBetweenPoints([touch locationInView:self], squareData.pos) <= kMinimumTouchLength * squareData.scale) {
DEBUGLOG(@"Square Touch at %.2f, %.2f ",squareData.pos.x,squareData.pos.y);
squareData.rotstop = YES;
squareData.touchInside = YES;
}
}

}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[touches allObjects] objectAtIndex:0];
squareData.currentTouchPosition = [touch locationInView:self];
if ([touches count]== 2) {
NSArray *twoTouches = [touches allObjects];
UITouch *first = [twoTouches objectAtIndex:0];
UITouch *second = [twoTouches objectAtIndex:1];

// Calculate the distance bewtween the two fingers(touches) to determine the pinch distance
CGFloat currentDistance = distanceBetweenPoints([first locationInView:self], [second locationInView:self]);

squareData.rotstop = YES;
squareData.touchInside = NO;

if (initialDistance == 0.0f)
initialDistance = currentDistance;
if (currentDistance - initialDistance > kMinimumPinchDelta) {
squareData.pinchDistance = currentDistance - initialDistance;
squareData.scalestart = YES;
DEBUGLOG(@"Outward Pinch %.2f", squareData.pinchDistance);
}
else if (initialDistance - currentDistance > kMinimumPinchDelta) {
squareData.pinchDistance = currentDistance - initialDistance;
squareData.scalestart = YES;
DEBUGLOG(@"Inward Pinch %.2f", squareData.pinchDistance);
}
}
else if ([touches count]==[[event touchesForView:self] count] & [[event touchesForView:self] count] == 1) {
if (squareData.touchInside) {
// Only move the square to new position when touchBegan is inside the square
squareData.pos.x = [touch locationInView:self].x;
squareData.pos.y = [touch locationInView:self].y;
DEBUGLOG(@"Square Move to %.2f, %.2f ",squareData.pos.x,squareData.pos.y);
squareData.rotstop = YES;
}
}
}


- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if ([touches count] == [[event touchesForView:self] count]) {
initialDistance = squareData.pinchDistanceShown = squareData.pinchDistance = 0.0f;
squareData.rotstop = squareData.touchInside = squareData.scalestart = NO;
DEBUGLOG(@"touchesEnded, all fingers up");
}
else {
initialDistance = squareData.pinchDistanceShown = squareData.pinchDistance = 0.0f;
squareData.scalestart = NO;
DEBUGLOG(@"touchesEnded");
}
}


- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
initialDistance = squareData.pinchDistanceShown = squareData.pinchDistance = 0.0f;
squareData.rotstop = squareData.touchInside = squareData.scalestart = NO;
DEBUGLOG(@"touchesCancelled");
}

- (void)setupView { // new method for intialisation of variables and states

// Enable Multi Touch of the view
self.multipleTouchEnabled = YES;

//Configure and start accelerometer
[[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / kAccelerometerFrequency)];
[[UIAccelerometer sharedAccelerometer] setDelegate:self];
#if TARGET_IPHONE_SIMULATOR
moveX = 2.0f;
moveY = 3.0f;
#else
moveX = 0.0f;
moveY = 0.0f;
#endif

#ifdef DEBUGSCREEN
UIColor *bgColor = [[UIColor alloc] initWithWhite:1.0f alpha:0.0f];
textView = [[UILabel alloc] initWithFrame:CGRectMake(10.0f, 350.0f, 300.0f, 96.0f)];
textView.text = [NSString stringWithFormat:@"-Accelerometer Data-"];
textView.textAlignment = UITextAlignmentLeft;
[textView setNumberOfLines:4];
textView.backgroundColor = bgColor;
textView.font = [UIFont fontWithName:@"Arial" size:18];
[self addSubview:textView];
[self bringSubviewToFront:textView];
#endif


// Initialise square data
squareData.rotation = squareData.pinchDistance = squareData.pinchDistanceShown = 0.0f;
ellipseData.rotation = 0.0f;
squareData.scale = 1.0f;
squareData.rotstop = squareData.touchInside = squareData.scalestart = NO;
squareData.pos.x = 160.0f;
squareData.pos.y = 240.0f;
squareData.pinchDistance = 0.0f;
squareData.rotspeed = 1.0f;

// Initialise ellipse data
ellipseData.rotation = 0.0f;
ellipseData.rotstop = ellipseData.touchInside = ellipseData.scalestart = NO;
ellipseData.pos.x = 160.0f;
ellipseData.pos.y = 100.0f;
ellipseData.rotspeed = -4.0f;

// calculate the vertices of ellipse
const GLfloat xradius = 35.0f;
const GLfloat yradius = 25.0f;
for (int i = 0; i < 720; i+=2) {
ellipseVertices[i] = (cos(degreesToRadians(i)) * xradius) + 0.0f;
ellipseVertices[i+1] = (sin(degreesToRadians(i)) * yradius) + 0.0f;
// DEBUGLOG(@"ellipseVertices[v%d] %.1f, %.1f",i, ellipseVertices[i], ellipseVertices[i+1]);
}

// setup the projection matrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

// Setup Orthographic Projection for the 320 x 480 of the iPhone screen
glOrthof(0.0f, 320.0f, 480.0f, 0.0f, -1.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);

}

- (void)drawView {

// Define the square vertices
const GLfloat squareVertices[] = {
-20.0f, -20.0f,
20.0f, -20.0f,
-20.0f, 20.0f,
20.0f, 20.0f,
};

// Define the colors of the square vertices
const GLubyte squareColors[] = {
255, 255, 0, 255,
0, 255, 255, 255,
0, 0, 0, 0,
255, 0, 255, 255,
};


// Define the colors of the ellipse vertices
const GLubyte ellipseColors[] = {
233, 85, 85, 255,
233, 85, 85, 255,
233, 85, 85, 255,
233, 85, 85, 255,
233, 85, 85, 255,
};


[EAGLContext setCurrentContext:context];
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
glViewport(0, 0, backingWidth, backingHeight);

// Clear background color
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);

// draw the square
glLoadIdentity();
glTranslatef(squareData.pos.x, squareData.pos.y, 0.0f);
glRotatef(squareData.rotation, 0.0f, 0.0f, 1.0f);
glScalef(squareData.scale, squareData.scale, 1.0f);
glVertexPointer(2, GL_FLOAT, 0, squareVertices);
glColorPointer(4, GL_UNSIGNED_BYTE, 0, squareColors);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

// draw the ellipse
glLoadIdentity();
glTranslatef(ellipseData.pos.x, ellipseData.pos.y, 0.0f);
glRotatef(ellipseData.rotation, 0.0f, 0.0f, 1.0f);
glVertexPointer(2, GL_FLOAT, 0, ellipseVertices);
glColorPointer(4, GL_UNSIGNED_BYTE, 0, ellipseColors);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glDrawArrays(GL_TRIANGLE_FAN, 0, 360); // the ellipse has 360 vertices

// control the square rotation
if (!squareData.rotstop) {
squareData.rotation += squareData.rotspeed;
if(squareData.rotation > 360.0f)
squareData.rotation -= 360.0f;
else if(squareData.rotation < -360.0f)
squareData.rotation += 360.0f;
}

// control the ellipse rotation
if (!ellipseData.rotstop) {
ellipseData.rotation += ellipseData.rotspeed;
if(ellipseData.rotation > 360.0f)
ellipseData.rotation -= 360.0f;
else if(ellipseData.rotation < -360.0f)
ellipseData.rotation += 360.0f;
}

// control the square scaling
if (squareData.scalestart && squareData.scale <= kMaximumScale) {
GLfloat pinchDelta = squareData.pinchDistance - squareData.pinchDistanceShown;
if (squareData.pinchDistance != 0.0f) {
squareData.scale += pinchDelta/30;
squareData.pinchDistanceShown = squareData.pinchDistance;
if (squareData.scale >= kMaximumScale) {
squareData.scale = kMaximumScale;
squareData.pinchDistanceShown = squareData.pinchDistance = 0.0f;
squareData.scalestart = NO;
} else if (squareData.scale <= 1.0f) {
squareData.scale = 1.0f;
squareData.pinchDistanceShown = squareData.pinchDistance = 0.0f;
squareData.scalestart = NO;
}
DEBUGLOG(@"scale is %.2f",squareData.scale);
}
}

// control the ellipse movement
#if TARGET_IPHONE_SIMULATOR
ellipseData.pos.x += moveX;
if (ellipseData.pos.x >= 290.f) {
moveX = -2.0f;
}
else if (ellipseData.pos.x <= 30.f) {
moveX = 2.0f;
}

ellipseData.pos.y += moveY;
if (ellipseData.pos.y >= 450.f) {
moveY = -1.5f;
}
else if (ellipseData.pos.y <= 55.f) {
moveY = 3.5f;
}
#else
ellipseData.pos.x += moveX;
if (accel[0] > -0.1 & accel[0] < 0.1 ) {
moveX = 0.0f;
}
else {
moveX = 10.0f * accel[0];
}

ellipseData.pos.y += moveY;
if (accel[1] > -0.1 & accel[1] < 0.1 ) {
moveY = 0.0f;
}
else {
moveY = -10.0f * accel[1];
}
#endif
if (ellipseData.pos.x >= 290.f) {
ellipseData.pos.x = 290.0f;
}
else if (ellipseData.pos.x <= 30.f) {
ellipseData.pos.x = 30.0f;
}
if (ellipseData.pos.y >= 450.f) {
ellipseData.pos.y = 450.0f;
}
else if (ellipseData.pos.y <= 55.f) {
ellipseData.pos.y = 55.0f;
}


glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
}

- (void)accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration
{
/*
The meaning of acceleration values for firmware 2.x
acceleration.x = Roll. It corresponds to roll, or rotation around the axis that runs from your home button to your earpiece.
Values vary from 1.0 (rolled all the way to the right) to -1.0 (rolled all the way to the left).

acceleration.y = Pitch. Place your iPhone on the table and mentally draw a horizontal line about half-way down the screen.
That's the axis around which the Y value rotates.
Values go from 1.0 (the headphone jack straight down) to -1.0 (the headphone jack straight up).

acceleration.z = Face up/face down.
It refers to whether your iPhone is face up (-1.0) or face down (1.0).
When placed on it side, either the side with the volume controls and ringer switch, or the side directly opposite
, the Z value equates to 0.0.
*/

//Use a basic low-pass filter in the accelerometer values
accel[0] = acceleration.x * kFilteringFactor + accel[0] * (1.0 - kFilteringFactor);
accel[1] = acceleration.y * kFilteringFactor + accel[1] * (1.0 - kFilteringFactor);
accel[2] = acceleration.z * kFilteringFactor + accel[2] * (1.0 - kFilteringFactor);

#ifdef DEBUGSCREEN
textView.text = [NSString stringWithFormat:
@"X (roll, %4.1f%%): %f\nY (pitch %4.1f%%): %f\nZ (%4.1f%%) : %f",
100.0 - (accel[0] + 1.0) * 50.0, accel[0],
100.0 - (accel[1] + 1.0) * 50.0, accel[1],
100.0 - (accel[2] + 1.0) * 50.0, accel[2]
];
#endif
}

- (void)layoutSubviews {
[EAGLContext setCurrentContext:context];
[self destroyFramebuffer];
[self createFramebuffer];
[self drawView];
}


- (BOOL)createFramebuffer {

glGenFramebuffersOES(1, &viewFramebuffer);
glGenRenderbuffersOES(1, &viewRenderbuffer);

glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer];
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);

glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);

if (USE_DEPTH_BUFFER) {
glGenRenderbuffersOES(1, &depthRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);
}

if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
DEBUGLOG(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
return NO;
}

return YES;
}


- (void)destroyFramebuffer {

glDeleteFramebuffersOES(1, &viewFramebuffer);
viewFramebuffer = 0;
glDeleteRenderbuffersOES(1, &viewRenderbuffer);
viewRenderbuffer = 0;

if(depthRenderbuffer) {
glDeleteRenderbuffersOES(1, &depthRenderbuffer);
depthRenderbuffer = 0;
}
}


- (void)startAnimation {
self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES];
}


- (void)stopAnimation {
self.animationTimer = nil;
}


- (void)setAnimationTimer:(NSTimer *)newTimer {
[animationTimer invalidate];
animationTimer = newTimer;
}


- (void)setAnimationInterval:(NSTimeInterval)interval {

animationInterval = interval;
if (animationTimer) {
[self stopAnimation];
[self startAnimation];
}
}


- (void)dealloc {

[self stopAnimation];

if ([EAGLContext currentContext] == context) {
[EAGLContext setCurrentContext:nil];
}

[context release];
[super dealloc];
}

@end

.
.
.

Sunday, April 5, 2009

OpenGL ES for iPhone : Part 2 with touch controls

We will continue to explore OpenGL ES for iPhone by interacting with input controls. This tutorial is for iPhone Development Environment, so the touch screen control is the major and fun part to play with.

In this tutorial, we will draw two objects using OpenGL ES and implement touch controls to drag and move an object around the screen and also to pinch and resize the object independently.


Similar to the first tutorial, we need to create a new project using the OpenGL ES Application template from XCode. We want to draw a new ellipse object but there is no direct function to draw an ellipse / circle in OpenGL ES, so we need to use a loop to calculate the vertices as below.

1) Draw an ellipse / a circle

We use degreesToRadians, cos and sin functions to calculate the required vertices positions. If you want a circle, just put yradius = xradius.

GLfloat ellipseVertices[720];

// calculate the vertices of ellipse
const GLfloat xradius = 35.0f;
const GLfloat yradius = 25.0f;
for (int i = 0; i < 720; i+=2) {
    ellipseVertices[i] = (cos(degreesToRadians(i)) * xradius) + 0.0f;
    ellipseVertices[i+1] = (sin(degreesToRadians(i)) * yradius) + 0.0f;
}


and we will use GL_TRIANGLE_FAN to draw the ellipse.

glDrawArrays(GL_TRIANGLE_FAN, 0, 360); // the ellipse has 360 vertices


2) Create data structure

We will create a C structure to hold the states of the two objects, so that we can independently control the status and behavior of the two objects

typedef struct
{
    BOOL rotstop;                  // stop self rotation
    BOOL touchInside;              // finger tap inside of the object ?
    BOOL scalestart;               // start to scale the obejct ?
    CGPoint pos;                   // position of the object on the screen
    CGPoint startTouchPosition;    // Start Touch Position
    CGPoint currentTouchPosition;  // Current Touch Position
    GLfloat pinchDistance;         // distance between two fingers pinch
    GLfloat pinchDistanceShown;    // distance that have shown on screen
    GLfloat scale;                 // OpenGL scale factor of the object
    GLfloat rotation;              // OpenGL rotation factor of the object
    GLfloat rotspeed;              // control rotation speed of the object
} ObjectData;

@public
    ObjectData squareData;
    ObjectData ellipseData;


3) Implement new method (void)setupView;

We need to create a new method so that we can do some initialization codes after the EAGLView is created. The setupView is called after - (id)initWithCoder:(NSCoder*)coder method

We also put some OpenGL code inside setupView method. We need to separate the codes into two setupView and drawView methods, and put the initialization codes to the setupView so that it is only called once after init. The drawing codes for after each layoutSubviews refreshes are still in drawView method.

We also need to change the Orthographic Projection in order to match with the iPhone screen resolution 320 x 480

  // Setup Orthographic Projection for the 320 x 480 of the iPhone screen
  glOrthof(0.0f, 320.0f, 480.0f, 0.0f, -1.0f, 1.0f);


4) Enable multitouch and implement the touch control events

To enable multitouch in the EAGLView, as it is not enabled by default
  self.multipleTouchEnabled = YES;

and we also need to implement these standard four touch control methods for EAGLView in order to handle drag and pinch

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
It sends the touchesBegan:withEvent: message when one or more fingers touch down on the screen.
We record the starting position of the pinch and drag here

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
It sends the touchesMoved:withEvent: message when one or more fingers move.
We handle the pinch and drag gestures here

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
It sends the touchesEnded:withEvent: message when one or more fingers lift up from the screen.
We reset the status of the touch controls when all fingers are lifted up

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
It sends the touchesCancelled:withEvent: message when the touch sequence is cancelled by a system event, such as an incoming phone call.


5) Control the moving and scaling of OpenGL ES objects independently

We have the following codes to draw the objects in OpenGL ES and by changing the variables *.rotation, *.scale of the 2 objects through the touch control events, we can change the location and scale of the 2 objects independently.


  // draw the square
  glLoadIdentity();
  glTranslatef(squareData.pos.x, squareData.pos.y, 0.0f);
  glRotatef(squareData.rotation, 0.0f, 0.0f, 1.0f);
  glScalef(squareData.scale, squareData.scale, 1.0f);
  glVertexPointer(2, GL_FLOAT, 0, squareVertices);
  glColorPointer(4, GL_UNSIGNED_BYTE, 0, squareColors);
  glEnableClientState(GL_VERTEX_ARRAY);
  glEnableClientState(GL_COLOR_ARRAY);
  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

  // draw the ellipse
  glLoadIdentity();
  glTranslatef(ellipseData.pos.x, ellipseData.pos.y, 0.0f);
  glRotatef(ellipseData.rotation, 0.0f, 0.0f, 1.0f);
  glVertexPointer(2, GL_FLOAT, 0, ellipseVertices);
  glColorPointer(4, GL_UNSIGNED_BYTE, 0, ellipseColors);
  glEnableClientState(GL_VERTEX_ARRAY);
  glEnableClientState(GL_COLOR_ARRAY);
  glDrawArrays(GL_TRIANGLE_FAN, 0, 360);  // the ellipse has 360 vertices


6) Enable NSLog for Debug build and disable for Release build

Use the following define to turnon NSLog output for Debug build and turnoff for Release build
You need to add this user-defined in Project Settings (in Debug build only)
GCC_PREPROCESSOR_DEFINITIONS    DEBUGON


Or if you have an older XCode template version, it should be like this




#ifdef DEBUGON
#define DEBUGLOG if (DEBUGON) NSLog
#else
#define DEBUGLOG
#endif


Then you should use DEBUGLOG() instead of NSLog() in your code. Another hint is that you can define DEBUGLOG1, DEBUGLOG2,... etc, in this case, you can turn on / off individual sections of NSLog.

7) The source codes are here, you just need to create a new project from OpenGL ES Application template of XCode and copy the source codes of EAGLView.h and EAGLView.m from below and paste them for Build & Go in XCode.



We did not implement the ellipse object to move, may be it's your turn to explore and enhance it. Feel free to add double-tap or multi-fingers swipe controls or add any new objects to the project. Enjoy OpenGL ES coding in iPhone !

P.S. In case you weren’t already aware, this is how to emulate multi-touch in the iPhone Simulator that accompanies the iPhone SDK.

- Option-Click: Allows you to pinch in and out.
- Option-Shift-Click: Allows you to perform a two-fingers drag.

EAGLView.h Select all

// EAGLView.h
// OpenGL ES Tutorial - Part 2 by javacom

// To enable Debug NSLog, add GCC_PREPROCESSOR_DEFINITIONS DEBUGON in Project Settings for Debug Build Only and replace NSLog() with DEBUGLOG()
#ifdef DEBUGON
#define DEBUGLOG if (DEBUGON) NSLog
#else
#define DEBUGLOG
#endif

#import <UIKit/UIKit.h>
#import <OpenGLES/EAGL.h>
#import <OpenGLES/ES1/gl.h>
#import <OpenGLES/ES1/glext.h>

typedef struct
{
BOOL rotstop; // stop self rotation
BOOL touchInside; // finger tap inside of the object ?
BOOL scalestart; // start to scale the obejct ?
CGPoint pos; // position of the object on the screen
CGPoint startTouchPosition; // Start Touch Position
CGPoint currentTouchPosition; // Current Touch Position
GLfloat pinchDistance; // distance between two fingers pinch
GLfloat pinchDistanceShown; // distance that have shown on screen
GLfloat scale; // OpenGL scale factor of the object
GLfloat rotation; // OpenGL rotation factor of the object
GLfloat rotspeed; // control rotation speed of the object
} ObjectData;

/*
This class wraps the CAEAGLLayer from CoreAnimation into a convenient UIView subclass.
The view content is basically an EAGL surface you render your OpenGL scene into.
Note that setting the view non-opaque will only work if the EAGL surface has an alpha channel.
*/
@interface EAGLView : UIView {

@private
/* The pixel dimensions of the backbuffer */
GLint backingWidth;
GLint backingHeight;

EAGLContext *context;

/* OpenGL names for the renderbuffer and framebuffers used to render to this view */
GLuint viewRenderbuffer, viewFramebuffer;

/* OpenGL name for the depth buffer that is attached to viewFramebuffer, if it exists (0 if it does not exist) */
GLuint depthRenderbuffer;

NSTimer *animationTimer;
NSTimeInterval animationInterval;

@public
ObjectData squareData;
ObjectData ellipseData;
GLfloat ellipseVertices[720];
CGFloat initialDistance;
}

@property NSTimeInterval animationInterval;

@property (nonatomic) ObjectData squareData;
@property (nonatomic) ObjectData ellipseData;
@property CGFloat initialDistance;

- (void)startAnimation;
- (void)stopAnimation;
- (void)drawView;
- (void)setupView;

@end



EAGLView.m Select all

// EAGLView.m
// OpenGL ES Tutorial - Part 2 by javacom
//
#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGLDrawable.h>

#import "EAGLView.h"

#include <math.h>

// Macros
#define degreesToRadians(__ANGLE__) (M_PI * (__ANGLE__) / 180.0)
#define radiansToDegrees(__ANGLE__) (180.0 * (__ANGLE__) / M_PI)

CGFloat distanceBetweenPoints (CGPoint first, CGPoint second) {
CGFloat deltaX = second.x - first.x;
CGFloat deltaY = second.y - first.y;
return sqrt(deltaX*deltaX + deltaY*deltaY );
};

CGFloat angleBetweenPoints(CGPoint first, CGPoint second) {
// atan((top - bottom)/(right - left))
CGFloat rads = atan((second.y - first.y) / (first.x - second.x));
return radiansToDegrees(rads);
}

CGFloat angleBetweenLines(CGPoint line1Start, CGPoint line1End, CGPoint line2Start, CGPoint line2End) {

CGFloat a = line1End.x - line1Start.x;
CGFloat b = line1End.y - line1Start.y;
CGFloat c = line2End.x - line2Start.x;
CGFloat d = line2End.y - line2Start.y;

CGFloat rads = acos(((a*c) + (b*d)) / ((sqrt(a*a + b*b)) * (sqrt(c*c + d*d))));

return radiansToDegrees(rads);
}

#define USE_DEPTH_BUFFER 0

#define kMinimumTouchLength 30
#define kMaximumScale 7.0f
#define kMinimumPinchDelta 15


// A class extension to declare private methods
@interface EAGLView ()

@property (nonatomic, retain) EAGLContext *context;
@property (nonatomic, assign) NSTimer *animationTimer;

- (BOOL) createFramebuffer;
- (void) destroyFramebuffer;

@end


@implementation EAGLView

@synthesize context;
@synthesize animationTimer;
@synthesize animationInterval;
@synthesize squareData;
@synthesize ellipseData;
@synthesize initialDistance;

// You must implement this method
+ (Class)layerClass {
return [CAEAGLLayer class];
}


//The GL view is stored in the nib file. When it's unarchived it's sent -initWithCoder:
- (id)initWithCoder:(NSCoder*)coder {

if ((self = [super initWithCoder:coder])) {

// Get the layer
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;

eaglLayer.opaque = YES;
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];

context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];

if (!context || ![EAGLContext setCurrentContext:context]) {
[self release];
return nil;
}

animationInterval = 1.0 / 60.0;
[self setupView];
}
return self;
}

// These are four methods touchesBegan, touchesMoved, touchesEnded, touchesCancelled and use to notify about touches and gestures

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/*
NSUInteger numTaps = [[touches anyObject] tapCount]; // number of taps
NSUInteger numTouches = [touches count]; // number of touches
*/
UITouch *touch = [[touches allObjects] objectAtIndex:0];

DEBUGLOG(@"TouchBegan event counts = %d ",[[event touchesForView:self] count]);
DEBUGLOG(@"TouchBegan tounches counts = %d ",[touches count]);
if ([touches count]== 2) {
NSArray *twoTouches = [touches allObjects];
UITouch *first = [twoTouches objectAtIndex:0];
UITouch *second = [twoTouches objectAtIndex:1];
initialDistance = distanceBetweenPoints([first locationInView:self], [second locationInView:self]);
squareData.rotstop = YES;
squareData.touchInside = NO;
}
else if ([touches count]==[[event touchesForView:self] count] & [[event touchesForView:self] count] == 1) {
squareData.startTouchPosition = [touch locationInView:self];
if (distanceBetweenPoints([touch locationInView:self], squareData.pos) <= kMinimumTouchLength * squareData.scale) {
DEBUGLOG(@"Square Touch at %.2f, %.2f ",squareData.pos.x,squareData.pos.y);
squareData.rotstop = YES;
squareData.touchInside = YES;
}
}

}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[touches allObjects] objectAtIndex:0];
squareData.currentTouchPosition = [touch locationInView:self];
if ([touches count]== 2) {
NSArray *twoTouches = [touches allObjects];
UITouch *first = [twoTouches objectAtIndex:0];
UITouch *second = [twoTouches objectAtIndex:1];

// Calculate the distance bewtween the two fingers(touches) to determine the pinch distance
CGFloat currentDistance = distanceBetweenPoints([first locationInView:self], [second locationInView:self]);

squareData.rotstop = YES;
squareData.touchInside = NO;

if (initialDistance == 0.0f)
initialDistance = currentDistance;
if (currentDistance - initialDistance > kMinimumPinchDelta) {
squareData.pinchDistance = currentDistance - initialDistance;
squareData.scalestart = YES;
DEBUGLOG(@"Outward Pinch %.2f", squareData.pinchDistance);
}
else if (initialDistance - currentDistance > kMinimumPinchDelta) {
squareData.pinchDistance = currentDistance - initialDistance;
squareData.scalestart = YES;
DEBUGLOG(@"Inward Pinch %.2f", squareData.pinchDistance);
}
}
else if ([touches count]==[[event touchesForView:self] count] & [[event touchesForView:self] count] == 1) {
if (squareData.touchInside) {
// Only move the square to new position when touchBegan is inside the square
squareData.pos.x = [touch locationInView:self].x;
squareData.pos.y = [touch locationInView:self].y;
DEBUGLOG(@"Square Move to %.2f, %.2f ",squareData.pos.x,squareData.pos.y);
squareData.rotstop = YES;
}
}
}


- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if ([touches count] == [[event touchesForView:self] count]) {
initialDistance = squareData.pinchDistanceShown = squareData.pinchDistance = 0.0f;
squareData.rotstop = squareData.touchInside = squareData.scalestart = NO;
DEBUGLOG(@"touchesEnded, all fingers up");
}
else {
initialDistance = squareData.pinchDistanceShown = squareData.pinchDistance = 0.0f;
squareData.scalestart = NO;
DEBUGLOG(@"touchesEnded");
}
}


- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
initialDistance = squareData.pinchDistanceShown = squareData.pinchDistance = 0.0f;
squareData.rotstop = squareData.touchInside = squareData.scalestart = NO;
DEBUGLOG(@"touchesCancelled");
}

- (void)setupView { // new method for intialisation of variables and states

// Enable Multi Touch of the view
self.multipleTouchEnabled = YES;

// Initialise square data
squareData.rotation = squareData.pinchDistance = squareData.pinchDistanceShown = 0.0f;
ellipseData.rotation = 0.0f;
squareData.scale = 1.0f;
squareData.rotstop = squareData.touchInside = squareData.scalestart = NO;
squareData.pos.x = 160.0f;
squareData.pos.y = 240.0f;
squareData.pinchDistance = 0.0f;
squareData.rotspeed = 1.0f;

// Initialise ellipse data
ellipseData.rotation = 0.0f;
ellipseData.rotstop = ellipseData.touchInside = ellipseData.scalestart = NO;
ellipseData.pos.x = 160.0f;
ellipseData.pos.y = 100.0f;
ellipseData.rotspeed = -4.0f;

// calculate the vertices of ellipse
const GLfloat xradius = 35.0f;
const GLfloat yradius = 25.0f;
for (int i = 0; i < 720; i+=2) {
ellipseVertices[i] = (cos(degreesToRadians(i)) * xradius) + 0.0f;
ellipseVertices[i+1] = (sin(degreesToRadians(i)) * yradius) + 0.0f;
DEBUGLOG(@"ellipseVertices[v%d] %.1f, %.1f",i, ellipseVertices[i], ellipseVertices[i+1]);
}

// setup the projection matrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

// Setup Orthographic Projection for the 320 x 480 of the iPhone screen
glOrthof(0.0f, 320.0f, 480.0f, 0.0f, -1.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);

}

- (void)drawView {

// Define the square vertices
const GLfloat squareVertices[] = {
-20.0f, -20.0f,
20.0f, -20.0f,
-20.0f, 20.0f,
20.0f, 20.0f,
};

// Define the colors of the square vertices
const GLubyte squareColors[] = {
255, 255, 0, 255,
0, 255, 255, 255,
0, 0, 0, 0,
255, 0, 255, 255,
};


// Define the colors of the ellipse vertices
const GLubyte ellipseColors[] = {
233, 85, 85, 255,
233, 85, 85, 255,
233, 85, 85, 255,
233, 85, 85, 255,
233, 85, 85, 255,
};


[EAGLContext setCurrentContext:context];
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
glViewport(0, 0, backingWidth, backingHeight);

// Clear background color
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);

// draw the square
glLoadIdentity();
glTranslatef(squareData.pos.x, squareData.pos.y, 0.0f);
glRotatef(squareData.rotation, 0.0f, 0.0f, 1.0f);
glScalef(squareData.scale, squareData.scale, 1.0f);
glVertexPointer(2, GL_FLOAT, 0, squareVertices);
glColorPointer(4, GL_UNSIGNED_BYTE, 0, squareColors);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

// draw the ellipse
glLoadIdentity();
glTranslatef(ellipseData.pos.x, ellipseData.pos.y, 0.0f);
glRotatef(ellipseData.rotation, 0.0f, 0.0f, 1.0f);
glVertexPointer(2, GL_FLOAT, 0, ellipseVertices);
glColorPointer(4, GL_UNSIGNED_BYTE, 0, ellipseColors);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glDrawArrays(GL_TRIANGLE_FAN, 0, 360); // the ellipse has 360 vertices

// control the square rotation
if (!squareData.rotstop) {
squareData.rotation += squareData.rotspeed;
if(squareData.rotation > 360.0f)
squareData.rotation -= 360.0f;
else if(squareData.rotation < -360.0f)
squareData.rotation += 360.0f;
}

// control the ellipse rotation
if (!ellipseData.rotstop) {
ellipseData.rotation += ellipseData.rotspeed;
if(ellipseData.rotation > 360.0f)
ellipseData.rotation -= 360.0f;
else if(ellipseData.rotation < -360.0f)
ellipseData.rotation += 360.0f;
}

// control the square scaling
if (squareData.scalestart && squareData.scale <= kMaximumScale) {
GLfloat pinchDelta = squareData.pinchDistance - squareData.pinchDistanceShown;
if (squareData.pinchDistance != 0.0f) {
squareData.scale += pinchDelta/30;
squareData.pinchDistanceShown = squareData.pinchDistance;
if (squareData.scale >= kMaximumScale) {
squareData.scale = kMaximumScale;
squareData.pinchDistanceShown = squareData.pinchDistance = 0.0f;
squareData.scalestart = NO;
} else if (squareData.scale <= 1.0f) {
squareData.scale = 1.0f;
squareData.pinchDistanceShown = squareData.pinchDistance = 0.0f;
squareData.scalestart = NO;
}
DEBUGLOG(@"scale is %.2f",squareData.scale);
}
}

glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
}

- (void)layoutSubviews {
[EAGLContext setCurrentContext:context];
[self destroyFramebuffer];
[self createFramebuffer];
[self drawView];
}


- (BOOL)createFramebuffer {

glGenFramebuffersOES(1, &viewFramebuffer);
glGenRenderbuffersOES(1, &viewRenderbuffer);

glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer];
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);

glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);

if (USE_DEPTH_BUFFER) {
glGenRenderbuffersOES(1, &depthRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);
}

if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
DEBUGLOG(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
return NO;
}

return YES;
}


- (void)destroyFramebuffer {

glDeleteFramebuffersOES(1, &viewFramebuffer);
viewFramebuffer = 0;
glDeleteRenderbuffersOES(1, &viewRenderbuffer);
viewRenderbuffer = 0;

if(depthRenderbuffer) {
glDeleteRenderbuffersOES(1, &depthRenderbuffer);
depthRenderbuffer = 0;
}
}


- (void)startAnimation {
self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES];
}


- (void)stopAnimation {
self.animationTimer = nil;
}


- (void)setAnimationTimer:(NSTimer *)newTimer {
[animationTimer invalidate];
animationTimer = newTimer;
}


- (void)setAnimationInterval:(NSTimeInterval)interval {

animationInterval = interval;
if (animationTimer) {
[self stopAnimation];
[self startAnimation];
}
}


- (void)dealloc {

[self stopAnimation];

if ([EAGLContext currentContext] == context) {
[EAGLContext setCurrentContext:nil];
}

[context release];
[super dealloc];
}

@end

.
.
.

Thursday, March 26, 2009

Sample Source Code : iPhone OS 3.0 peer to peer bluetooth connectivity

Just saw a sample source code from the apple dev forum to sharing the testing the p2p bluetooth

What you need to do is to create a new project from OpenGL ES Application of iPhone OS 3.0 SDK template called it BluetoothSample
and past the codes as below and test, it should work on 2 iPhone 3G and iPod Touch 2nd gen only.

Then you need to add GameKit framework to build and go

The source is from Travis True

BluetoothSampleAppDelegate.h Select all

//
// BluetoothSampleAppDelegate.h
// BluetoothSample
//

#import "EAGLView.h"
#import <GameKit/GameKit.h>

@interface BluetoothSampleAppDelegate : NSObject {
UIWindow *window;
EAGLView *glView;

GKPeerPickerController *picker;
GKSession *session;

int myNumber;
NSData *myData;
UILabel *textView;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet EAGLView *glView;
@property (nonatomic, retain) GKPeerPickerController *picker;
@property (nonatomic, retain) GKSession *session;

- (void)mySendData;

@end



BluetoothSampleAppDelegate.m Select all

//
// BluetoothSampleAppDelegate.m
// BluetoothSample
//

#import "BluetoothSampleAppDelegate.h"

@implementation BluetoothSampleAppDelegate

@synthesize picker;
@synthesize session;
@synthesize window;
@synthesize glView;

- (void)applicationDidFinishLaunching:(UIApplication *)application {

// setup the text view
myNumber = 0;
textView = [[UILabel alloc] initWithFrame:CGRectMake(10.0f, 10.0f, 640.0f, 12.0f)];
textView.text = [NSString stringWithFormat:@"myNumber: %i\n", myNumber];
[window addSubview:textView];
[window bringSubviewToFront:textView];

// start the EAGLView
glView.animationInterval = 1.0 / 60.0;
[glView startAnimation];

// allocate the NSData
myData = [[NSData alloc] initWithBytes:&myNumber length:sizeof(int)];


// allocate and setup the peer picker controller
picker = [[GKPeerPickerController alloc] init];
picker.delegate = self;
picker.connectionTypesMask = GKPeerPickerConnectionTypeNearby | GKPeerPickerConnectionTypeOnline;
[picker show];
}


- (void)applicationWillResignActive:(UIApplication *)application {
glView.animationInterval = 1.0 / 5.0;
}


- (void)applicationDidBecomeActive:(UIApplication *)application {
glView.animationInterval = 1.0 / 60.0;
}


- (void)peerPickerController:(GKPeerPickerController *)picker didSelectConnectionType:(GKPeerPickerConnectionType)type {
if(type == GKPeerPickerConnectionTypeOnline) {
[self.picker dismiss];
[self.picker release];
self.picker = nil;
// Display your own UI here.
}
}


- (GKSession *) peerPickerController:(GKPeerPickerController *)picker
sessionForConnectionType:(GKPeerPickerConnectionType)type {
session = [[GKSession alloc] initWithSessionID:@"FR" displayName:nil sessionMode:GKSessionModePeer];
session.delegate = self;

return session;
}


- (void)session:(GKSession *)session peer:(NSString *)peerID didChangeState:(GKPeerConnectionState)state {
switch (state) {
case GKPeerStateConnected:
[self.session setDataReceiveHandler :self withContext:nil];
[self mySendData]; // start off by sending data upon connection
break;

case GKPeerStateDisconnected:
break;
}
}


- (void)peerPickerController:(GKPeerPickerController *)picker didConnectToPeer:(NSString *)peerID {
printf("connection was successful! start the game.\n");
}


- (void)peerPickerControllerDidCancel:(GKPeerPickerController *)picker {
printf("connection attempt was canceled\n");
}


- (void)mySendData {
// allocate the NSData
myNumber++;
myData = [[NSData alloc] initWithBytes:&myNumber length:sizeof(int)];
[session sendDataToAllPeers :myData withDataMode:GKSendDataReliable error:nil];
printf("send data: %i\n", myNumber);
textView.text = [NSString stringWithFormat:@"myNumber: %i\n", myNumber];
}


- (void) receiveData:(NSData *)data fromPeer:(NSString *)peer inSession: (GKSession *)session context:(void *)context
{
// Read the bytes in data and perform an application-specific action, then free the NSData object
[data getBytes:&myNumber length:sizeof(int)];
printf("received data: %i from: %s\n", myNumber, [peer UTF8String]);
textView.text = [NSString stringWithFormat:@"myNumber: %i\n", myNumber];

[self mySendData];
}


- (void)dealloc
{
[picker release];
[session release];
[textView release];
[glView release];
[window release];
[super dealloc];
}


@end

Sunday, January 25, 2009

iPhone Sample Source Code : UISlider UITextField UISwitch & UISegmentedControl



This is a sample source code (no interface builder file) implementing the one similar to Setting Bundle for UIKit Controls
UISlider, UITextField, UISwitch & UISegmentedControl

main.m Select all

// main.m
// Settings
//

#import <UIKit/UIKit.h>

int main(int argc, char *argv[]) {

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


AppDelegate.h Select all

// AppDelegate.h
// Settings
//

#import <UIKit/UIKit.h>
#pragma mark -
#pragma mark MainViewController
#pragma mark -

@interface MainViewController : UITableViewController <UITextFieldDelegate>{

UITextField *activeTextField;
}

- (id) init;
- (void) dealloc;
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

@end

#pragma mark -
#pragma mark AppDelegate
#pragma mark -

@interface AppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
MainViewController *viewController;
UINavigationController *navigationController;
}

@property (nonatomic, retain) UIWindow *window;
@property (nonatomic, retain) MainViewController *viewController;

@end


AppDelegate.m Select all

// AppDelegate.m
// Settings
//
//

#define DEBUG_BUILD

#ifdef DEBUG_BUILD
#define DEBUGLOG(x) x
#define LogMethod() NSLog(@"%@ %@", self, NSStringFromSelector(_cmd))
#else
#define DEBUGLOG(x)
#define LogMethod()
#endif

#import "AppDelegate.h"

#pragma mark -
#pragma mark MainViewController
#pragma mark -

@implementation MainViewController

- (id) init {
self = [ super initWithStyle: UITableViewStyleGrouped ];

if (self != nil) {
self.title = @"Settings";
}
return self;
}

- (void) loadView {
LogMethod();
[ super loadView ];
}

- (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation
{
LogMethod();
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

- (void)didReceiveMemoryWarning {
LogMethod();
[ super didReceiveMemoryWarning ];
}

- (void)dealloc {
// [ shipStabilityControl release ];
[ super dealloc ];
}

#pragma mark - UITableView delegates

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 3;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
switch (section) {
case(0):
return 4;
break;
case(1):
return 3;
break;
case(2):
return 1;
break;
}

return 0;
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
switch (section) {
case(0):
return @"Game Settings";
break;
case(1):
return @"Advanced Settings";
break;
case(2):
return @"About";
break;
}
return nil;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = [ NSString stringWithFormat: @"%d:%d", [ indexPath indexAtPosition: 0 ], [ indexPath indexAtPosition:1 ]];

UITableViewCell *cell = [ tableView dequeueReusableCellWithIdentifier: CellIdentifier];

if (cell == nil) {
cell = [ [ [ UITableViewCell alloc ] initWithFrame: CGRectZero reuseIdentifier: CellIdentifier] autorelease ];

cell.selectionStyle = UITableViewCellSelectionStyleNone;

switch ([ indexPath indexAtPosition: 0]) {
case(0):
switch([ indexPath indexAtPosition: 1]) {
case(0):
{
UISlider *musicVolumeControl = [ [ UISlider alloc ] initWithFrame: CGRectMake(170, 0, 125, 50) ];
musicVolumeControl.minimumValue = 0.0;
musicVolumeControl.maximumValue = 10.0;
musicVolumeControl.tag = 0;
musicVolumeControl.value = 3.5;
musicVolumeControl.continuous = YES;
[musicVolumeControl addTarget:self action:@selector(sliderAction:) forControlEvents:UIControlEventValueChanged];
[ cell addSubview: musicVolumeControl ];
cell.textLabel.text = @"Music Volume"; // OS3
// cell.text = @"Music Volume";
[ musicVolumeControl release ];
}
break;
case(1):
{
UISlider *gameVolumeControl = [ [ UISlider alloc ] initWithFrame: CGRectMake(170, 0, 125, 50) ];
gameVolumeControl.minimumValue = 0.0;
gameVolumeControl.maximumValue = 10.0;
gameVolumeControl.tag = 1;
gameVolumeControl.value = 3.5;
gameVolumeControl.continuous = YES;
[gameVolumeControl addTarget:self action:@selector(sliderAction:) forControlEvents:UIControlEventValueChanged];
[ cell addSubview: gameVolumeControl ];
cell.textLabel.text = @"Game Volume"; // OS3
// cell.text = @"Game Volume";
[ gameVolumeControl release ];
}
break;
case(2):
{
UISegmentedControl *difficultyControl = [ [ UISegmentedControl alloc ] initWithFrame: CGRectMake(170, 5, 125, 35) ];
[ difficultyControl insertSegmentWithTitle: @"Easy" atIndex: 0 animated: NO ];
[ difficultyControl insertSegmentWithTitle: @"Hard" atIndex: 1 animated: NO ];
difficultyControl.selectedSegmentIndex = 0;
difficultyControl.tag = 2;
[difficultyControl addTarget:self action:@selector(segmentAction:) forControlEvents:UIControlEventValueChanged];
[ cell addSubview: difficultyControl ];
cell.textLabel.text = @"Difficulty"; // OS3
// cell.text = @"Difficulty";
[difficultyControl release];
}
break;
case(3):
{
UISegmentedControl *actionControl = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects: @"Check", @"Search", @"Tools", nil]];
actionControl.frame = CGRectMake(145, 5, 150, 35);
actionControl.selectedSegmentIndex = 1;
actionControl.tag = 3;
[actionControl addTarget:self action:@selector(segmentAction:) forControlEvents:UIControlEventValueChanged];
actionControl.segmentedControlStyle = UISegmentedControlStyleBar;

[cell addSubview:actionControl];
cell.textLabel.text = @"Actions"; // OS3
// cell.text = @"Actions";
[actionControl release];
}
break;
}
break;
case(1):
switch ([ indexPath indexAtPosition: 1 ]) {
case(0):
{
UITextField *playerTextField = [ [ UITextField alloc ] initWithFrame: CGRectMake(150, 10, 145, 34) ];
playerTextField.adjustsFontSizeToFitWidth = YES;
playerTextField.textColor = [UIColor blackColor];
playerTextField.font = [UIFont systemFontOfSize:17.0];
playerTextField.placeholder = @"";
playerTextField.backgroundColor = [UIColor clearColor];
playerTextField.autocorrectionType = UITextAutocorrectionTypeNo; // no auto correction support
playerTextField.autocapitalizationType = UITextAutocapitalizationTypeNone; // no auto capitalization support
playerTextField.textAlignment = UITextAlignmentRight;
playerTextField.keyboardType = UIKeyboardTypeDefault; // use the default type input method (entire keyboard)
playerTextField.returnKeyType = UIReturnKeyDone;
playerTextField.tag = 0;
playerTextField.delegate = self;

playerTextField.clearButtonMode = UITextFieldViewModeNever; // no clear 'x' button to the right
playerTextField.text = @"";
[ playerTextField setEnabled: YES ];
[ cell addSubview: playerTextField ];
cell.textLabel.text = @"Player"; // OS3
// cell.text = @"Player";
[playerTextField release];
}
break;
case(1):
{
UISwitch *resetControl = [ [ UISwitch alloc ] initWithFrame: CGRectMake(200, 10, 0, 0) ];
resetControl.on = YES;
resetControl.tag = 1;
[resetControl addTarget:self action:@selector(switchAction:) forControlEvents:UIControlEventValueChanged];
[ cell addSubview: resetControl ];
cell.textLabel.text = @"Reset"; // OS3
// cell.text = @"Reset";
[resetControl release];
}
break;
case(2):
{
UISwitch *debugControl = [ [ UISwitch alloc ] initWithFrame: CGRectMake(200, 10, 0, 0) ];
debugControl.on = NO;
debugControl.tag = 2;
[debugControl addTarget:self action:@selector(switchAction:) forControlEvents:UIControlEventValueChanged];
[ cell addSubview: debugControl ];
cell.textLabel.text = @"Debug"; // OS3
// cell.text = @"Debug";
[debugControl release];
}
break;
}
break;
case(2):
{
UITextField *versionControl = [ [ UITextField alloc ] initWithFrame: CGRectMake(170, 10, 125, 38) ];
versionControl.text = @"1.0.0 Rev. B";
[ cell addSubview: versionControl ];

[ versionControl setEnabled: YES ];
versionControl.tag = 2;
versionControl.delegate = self;
cell.textLabel.text = @"Version"; // OS3
// cell.text = @"Version";
[versionControl release];
}
break;
}
}

return cell;
}

#pragma mark ControlEventTarget Actions


- (void)segmentAction:(UISegmentedControl*)sender
{
if ([activeTextField canResignFirstResponder])
[activeTextField resignFirstResponder];

DEBUGLOG(NSLog(@"segmentAction: sender = %d, segment = %d", [sender tag], [sender selectedSegmentIndex]));
}

- (void)sliderAction:(UISlider*)sender
{
if ([activeTextField canResignFirstResponder])
[activeTextField resignFirstResponder];
DEBUGLOG(NSLog(@"sliderAction: sender = %d, value = %.1f", [sender tag], [sender value]));
}

- (void)switchAction:(UISwitch*)sender
{
if ([activeTextField canResignFirstResponder])
[activeTextField resignFirstResponder];
DEBUGLOG(NSLog(@"switchAction: sender = %d, isOn %d", [sender tag], [sender isOn]));
}


#pragma mark <UITextFieldDelegate> Methods

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
activeTextField = textField;
DEBUGLOG(NSLog(@"textFieldShouldBeginEditing: sender = %d, %@", [textField tag], [textField text]));
return YES;
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
DEBUGLOG(NSLog(@"textFieldDidEndEditing: sender = %d, %@", [textField tag], [textField text]));
}

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
DEBUGLOG(NSLog(@"textFieldShouldReturn: sender = %d, %@", [textField tag], [textField text]));
activeTextField = nil;
[textField resignFirstResponder];
return YES;
}


@end

#pragma mark -
#pragma mark AppDelegate
#pragma mark -

@implementation AppDelegate

@synthesize window;
@synthesize viewController;

- (void)applicationDidFinishLaunching:(UIApplication *)application
{
LogMethod();

// If you want the status bar to be hidden at launch use this:
// application.statusBarHidden = YES;
//
// To set the status bar as black, use the following:
// application.statusBarStyle = UIStatusBarStyleBlackOpaque;


// Create window
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

// this helps in debugging, so that you know "exactly" where your views are placed;
// if you see "red", you are looking at the bare window, otherwise use black
// window.backgroundColor = [UIColor redColor];

viewController = [ [ MainViewController alloc ] init ];

navigationController = [ [ UINavigationController alloc ] initWithRootViewController: viewController ];

/* Anchor the view to the window */
[window addSubview:[navigationController view]];

/* Make the window key and visible */
[window makeKeyAndVisible];
}

- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
{
// low on memory: do whatever you can to reduce your memory foot print here
}


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

@end