Creating a simple UICollectionView in iOS

Steps

1) Create Master-Detail Application & Replace the MasterViewController

First we want to create a Master-Detail Application just because it sets up a Master-Detail relationship even though thats the first thing we are going to break :).  So go ahead and create a new project in XCode4 based on a Master-Detail Application type.  Use ARC, Storyboards and CoreData because we will use CoreData to store information.  Your storyboard should look like this:

Master-Detail Storyboard
Master-Detail Storyboard

Now select the Master scene until its highlighted in blue and delete it with the Delete key.  We simply replace it by dragging in a UICollectionViewController onto the storyboard in its place.  This places a UICollectionViewController scene with a small empty collectionview cell in the top left corner.  I made a few adjustments to mine and here is what it looks like but Ill go over those later:

UICollectionViewController Storyboard
UICollectionViewController Storyboard

The changes I made to it are the following:

a – Select the entire scene (again until its highlighted blue) and change its Class type from UICollectionViewController to MasterViewController.

b – Enlarged the UICollectionViewCell from 50×50 to 145×145 in the Dimension’s Inspector

Here are some clips of the Identity Inspector of the MasterViewController and Dimension’s Inspector of the UICollectionViewCell:

UICollectionViewController Identity Inspector
UICollectionViewController Identity Inspector
UICollectionViewCell Dimensions Inspector
UICollectionViewCell Dimensions Inspector

We did set the new UICollectionViewController to a new class, the MasterViewController class.  We must do the same with the UICollectionViewCell but we must create its class first.

2)  Modify the MasterViewController class with the following in the .m file:

#import “MasterViewController.h”

#import “DetailViewController.h”

#import “MyCustomCell.h”

#import “AppDelegate.h”

static NSString *CellIdentifier = @”MyCustomCell”;

@interface MasterViewController ()

{

NSMutableArray *_objectChanges;

NSMutableArray *_sectionChanges;

}

@end

Now let’s go through the methods.  First the UICollectionView methods, which are quite similar to the UITableViewController methods:

#pragma mark – UICollectionView

– (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section

{

id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];

return [sectionInfo numberOfObjects];

}

// The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:

– (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

{

MyCustomCell *cell = (MyCustomCell *)[collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];

NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];

[cell setImage:[UIImage imageWithData:[object valueForKey:@”photoImageData”]]];

return cell;

}

– (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

{

if ([[segue identifier] isEqualToString:@”showDetail”]) {

NSIndexPath *indexPath = [[self.collectionView indexPathsForSelectedItems] lastObject];

NSManagedObject *object = [[self fetchedResultsController] objectAtIndexPath:indexPath];

[[segue destinationViewController] setDetailItem:object];

}

}

The MyCustomCell Class is the one we will create in the next section.  For now we are simply filling in the cell’s image data with some fetched managed object which we will also create later.  A couple of more interesting tidbits for example; we gave our UICollectionViewCell a reuse identifier and we gave our segue an identifier as well.  We must make sure these identifiers also exist in the storyboard inspectors for the cell and segue respectively.

3) Create UICollectionViewCell Class & Connect it

Let’s go ahead and Create a New File in our project, base it off of Objective C Class.  Type in UICollectionViewCell as the subclass and name it MyCustomCell.  We are simply going to define a class for our UICollectionViewCell and once we are finished, we must go to Storyboard and set our cell to use this new class type.

Add a UIImageView property to the class so that your .h file looks like this:

#import <UIKit/UIKit.h>

@interface MyCustomCell : UICollectionViewCell{

IBOutlet UIImageView *imageView;

}

-(void)setImage:(UIImage *)image;

@end

and now implement the setter in your .m so that it looks like this:

#import “MyCustomCell.h”

@implementation MyCustomCell

-(void)setImage:(UIImage *)image{

[imageView setImage:image];

}

@end

4) Create the data model.  First let’s do the easiest part, which is creating the data model.  Select your project’s xcdatamodel file and create a new Entity, call it Snapshots if you’d like.  Now add 3 attributes to it and make them of this type:

Core Data Entity Data Model
Core Data Entity Data Model

Once that is done, we have a storage container for our data.  Let’s look at the code used by our app to access this store and manipulate it.

#pragma mark – Fetched results controller

– (NSFetchedResultsController *)fetchedResultsController

{

if (_fetchedResultsController != nil) {

return _fetchedResultsController;

}

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

// Edit the entity name as appropriate.

NSEntityDescription *entity = [NSEntityDescription entityForName:@”Snapshots” inManagedObjectContext:self.managedObjectContext];

[fetchRequest setEntity:entity];

// Set the batch size to a suitable number.

[fetchRequest setFetchBatchSize:20];

// Edit the sort key as appropriate.

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@”photoName” ascending:NO];

NSArray *sortDescriptors = @[sortDescriptor];

[fetchRequest setSortDescriptors:sortDescriptors];

// Edit the section name key path and cache name if appropriate.

// nil for section name key path means “no sections”.

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@”Master”];

aFetchedResultsController.delegate = self;

self.fetchedResultsController = aFetchedResultsController;

NSError *error = nil;

if (![self.fetchedResultsController performFetch:&error]) {

// Replace this implementation with code to handle the error appropriately.

// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

NSLog(@”Unresolved error %@, %@”, error, [error userInfo]);

abort();

}

return _fetchedResultsController;

}

– (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo

atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type

{

NSMutableDictionary *change = [NSMutableDictionary new];

switch(type) {

case NSFetchedResultsChangeInsert:

change[@(type)] = @(sectionIndex);

break;

case NSFetchedResultsChangeDelete:

change[@(type)] = @(sectionIndex);

break;

}

[_sectionChanges addObject:change];

}

– (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject

atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type

newIndexPath:(NSIndexPath *)newIndexPath

{

NSMutableDictionary *change = [NSMutableDictionary new];

switch(type)

{

case NSFetchedResultsChangeInsert:

change[@(type)] = newIndexPath;

break;

case NSFetchedResultsChangeDelete:

change[@(type)] = indexPath;

break;

case NSFetchedResultsChangeUpdate:

change[@(type)] = indexPath;

break;

case NSFetchedResultsChangeMove:

change[@(type)] = @[indexPath, newIndexPath];

break;

}

[_objectChanges addObject:change];

}

– (void)controllerDidChangeContent:(NSFetchedResultsController *)controller

{

if ([_sectionChanges count] > 0)

{

[self.collectionView performBatchUpdates:^{

for (NSDictionary *change in _sectionChanges)

{

[change enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, id obj, BOOL *stop) {

NSFetchedResultsChangeType type = [key unsignedIntegerValue];

switch (type)

{

case NSFetchedResultsChangeInsert:

[self.collectionView insertSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]]];

break;

case NSFetchedResultsChangeDelete:

[self.collectionView deleteSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]]];

break;

case NSFetchedResultsChangeUpdate:

[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]]];

break;

}

}];

}

} completion:nil];

}

if ([_objectChanges count] > 0 && [_sectionChanges count] == 0)

{

[self.collectionView performBatchUpdates:^{

for (NSDictionary *change in _objectChanges)

{

[change enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, id obj, BOOL *stop) {

NSFetchedResultsChangeType type = [key unsignedIntegerValue];

switch (type)

{

case NSFetchedResultsChangeInsert:

[self.collectionView insertItemsAtIndexPaths:@[obj]];

break;

case NSFetchedResultsChangeDelete:

[self.collectionView deleteItemsAtIndexPaths:@[obj]];

break;

case NSFetchedResultsChangeUpdate:

[self.collectionView reloadItemsAtIndexPaths:@[obj]];

break;

case NSFetchedResultsChangeMove:

[self.collectionView moveItemAtIndexPath:obj[0] toIndexPath:obj[1]];

break;

}

}];

}

} completion:nil];

}

[_sectionChanges removeAllObjects];

[_objectChanges removeAllObjects];

}

I know its long, but its pretty simple.  The first method, – (NSFetchedResultsController *)fetchedResultsController, basically opens up the store, fetches all entities by the name “Snapshots” and places them into a special object called NSFetchedResultsController.

The didChangeSection method is called when there is a change within a section.  We only have 1 section in our UICollectionView.

The didChangeObject method is called when a particular object is changed within our UICollectionView.

The controllerDidChangeContent actually manages the changes made.  Basically we update our two arrays, _sectionChanges and _objectChanges with each change in the data in order to keep our UICollectionView current.

5) Connect to Flickr API.  So what constitutes a change in those sections and objects?  There is an acronym that datastore managers use, CRUD, which basically says that everytime you create, read, update or delete you create a transaction.  Thats basically what we want to track (except for the read part :)).  So whenever we download a new photo to our datastore, update a photo or delete one, we trigger changes in objects and thus in sections.

We want to use the Flickr API to get images from the web and populate our collection view.  We are basically going to perform a fetch to Flickr API using our own key or identifier.  You must register for one at flickr.com/.

a – Get FlickrAPIKey and add it here to this string constant atop your .m file like so:

NSString *const FlickrAPIKey = @"YOURAPIKEYVALUE";

b – Add the loadFlickrPhotos method to fetch pics from the web.  So add this method:

– (void)loadFlickrPhotos{

// 1. Build your Flickr API request w/Flickr API key in FlickrAPIKey.h

NSString *urlString = [NSString stringWithFormat:@”http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=%@&tags=%@&per_page=10&format=json&nojsoncallback=1&#8243;, FlickrAPIKey, @”bayern”];

NSURL *url = [NSURL URLWithString:urlString];

// 2. Get URLResponse string & parse JSON to Foundation objects.

NSString *jsonString = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];

NSError *e = nil;

NSDictionary *results = [NSJSONSerialization JSONObjectWithData:[jsonString dataUsingEncoding:NSUTF8StringEncoding]options:NSJSONReadingMutableContainers error:&e];

photos = [[results objectForKey:@”photos”] objectForKey:@”photo”];

for (NSDictionary *photo in photos) {

// 3.a Get title for e/ photo

NSString *title = [photo objectForKey:@”title”];

[photoNames addObject:(title.length > 0 ? title : @”Untitled”)];

// 3.b Construct URL for e/ photo.

NSString *photoURLString = [NSString stringWithFormat:@”http://farm%@.static.flickr.com/%@/%@_%@_s.jpg&#8221;, [photo objectForKey:@”farm”], [photo objectForKey:@”server”], [photo objectForKey:@”id”], [photo objectForKey:@”secret”]];

[photoURLs addObject:[NSURL URLWithString:photoURLString]];

}

// Process into CoreData

[self processCoreData];

}

It basically looks for Flickr photos of Bayern and stores the results in the photos ivar.  Now we must populate our CoreData db with these data.

c – Populate our CoreData model.  Add the processCoreData method to your MasterViewController.m like so:

-(void)processCoreData{

AppDelegate *myDelegate = [[UIApplication sharedApplication] delegate];

for (NSDictionary *photoDictionary in photos){

NSManagedObject *photoModel = [NSEntityDescription insertNewObjectForEntityForName:@”AFPhotoModel” inManagedObjectContext:myDelegate.managedObjectContext];

//[photoModel setValue:[photoDictionary valueForKey:@”rating”] forKey:@”photoRating”];

[photoModel setValue:[photoDictionary valueForKey:@”title”] forKey:@”photoName”];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{

//Build URL

NSString *photoURLString = [NSString stringWithFormat:@”http://farm%@.static.flickr.com/%@/%@_%@_s.jpg&#8221;, [photoDictionary objectForKey:@”farm”], [photoDictionary objectForKey:@”server”], [photoDictionary objectForKey:@”id”], [photoDictionary objectForKey:@”secret”]];

NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:photoURLString]];

dispatch_async(dispatch_get_main_queue(), ^{

[photoModel setValue:imageData forKey:@”photoImageData”];

});

});

}

}

Voila!  Run your app!

iOS Smarties :)

iOS & Objective C Smarties Code Snippets by Marcio Valenzuela Santiapps.com
iOS & Objective C Smarties Code Snippets

Everyone loves Smarties!  And much the same way Smarties Candies make you smarter… today we are talking about Code Snippets that make you….er Smarter!  More than a source of cut/paste Objective C source code, this is meant to be a quick reference.  As such, some of these will be incomplete and I will be filling them up as I go along. 

1)   UIAlertView

UIAlertView *internetAlert = [[UIAlertView alloc] initWithTitle:@”No hay farmacias”

message:@”Favor cambie sus parámetros”

delegate:self

cancelButtonTitle:@”Cancelar”

otherButtonTitles:@”Ok”, nil];

[internetAlert show];

2)   NSNotification

//1. Register as observer of notifications in viewDidLoad

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveTestNotification:) name:@”TestNotification” object:nil];

//2. NSNotifCtr CLEANUP in viewDidUnload

[[NSNotificationCenter defaultCenter] removeObserver:self];

//4. Post notif to NSNotif in calling method OTHER CLASS

[[NSNotificationCenter defaultCenter]

postNotificationName:@”TestNotification”

object:self];

//3. Method to prove notif received…

– (void) receiveTestNotification:(NSNotification *) notification

{

if ([[notification name] isEqualToString:@”TestNotification”])

NSLog (@”Successfully received the test notification!”);

}

3)   UIActivityIndicator – NSTimer – then completion blocks

//1. Set activity indicator

UIApplication *sharedApplication = [UIApplication sharedApplication];

sharedApplication.networkActivityIndicatorVisible = YES;

//INSERT NSTimer here J

//2. Hide activity indicator

UIApplication *sharedApplication = [UIApplication sharedApplication];

sharedApplication.networkActivityIndicatorVisible = NO;

works the same with an IBOutlet UIActivityIndicator

4)   UISearchBar

UISearchBarDelegate, UISearchDisplayDelegate

@property (strong,nonatomic) NSMutableArray *filteredResultsArray;

@property (strong,nonatomic) IBOutlet UISearchBar *resultsSearchBar;

//UISearch in INIT or LOAD method

// Initialize the filteredResults array with its count of Locations NSManagedObjects

self.filteredResultsArray = [NSMutableArray arrayWithCapacity:[self.originalArray count]];

//ios6 refresh control ONLY

if ([UIRefreshControl class]) {

self.refreshControl = [[UIRefreshControl alloc] init];

[self.refreshControl addTarget:self action:@selector(reload) forControlEvents:UIControlEventValueChanged];

[self reload];

[self.refreshControl beginRefreshing];

} else {

//do nothing

}

//nORIS

if (tableView == self.searchDisplayController.searchResultsTableView) {

return [filteredResultsArray count];

} else {

return [self.originalArray count];

}

//cFRAIP

if (tableView == self.searchDisplayController.searchResultsTableView) {

// IF ITS A SEARCH TABLE…>>>>>>>>>>

//Use filteredResultsArray data

} else {

//Otherwise use self.originalArray

}

5)   Reminders & Events

/////////EVENTS

// SPECIAL CALENDAR OBJECT CLASS

#import <EventKit/EventKit.h>

+(EKEventStore*)eventStore;

+(EKCalendar*)calendar;

+(EKCalendar*)createAppCalendar;

// WHAT IT DOES

static EKEventStore* eStore = NULL;

+(EKEventStore*)eventStore{

//keep a static instance of eventStore

if (!eStore) {

eStore = [[EKEventStore alloc] init];

}

return eStore;

}

+(EKCalendar*)createAppCalendar

{

EKEventStore *store = [self eventStore];

//1 fetch the local event store source

EKSource* localSource = nil;

for (EKSource* src in store.sources) {

if (src.sourceType == EKSourceTypeCalDAV) {

localSource = src;

}

if (src.sourceType == EKSourceTypeLocal && localSource==nil) {

localSource = src;

}

}

if (!localSource) return nil;

//2 CREATE A NEW CALEDNAR FOR ITEMS!!!!!!!!!!!!!!!

EKCalendar* newCalendar = [EKCalendar calendarWithEventStore: store];

newCalendar.title = kAppCalendarTitle;

newCalendar.source = localSource;

newCalendar.CGColor = [[UIColor colorWithRed:0.8 green:0.2 blue:0.6 alpha:1] CGColor];

//3 save the calendar in the event store

NSError* error = nil;

[store saveCalendar: newCalendar commit:YES error:&error];

if (!error) {

return nil;

}

//4 store the calendar id

NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];

[prefs setValue:newCalendar.calendarIdentifier forKey:@”appCalendar”];

[prefs synchronize]; return newCalendar;

}

+(EKCalendar*)calendar{

//1

EKCalendar* result = nil;

EKEventStore *store = [self eventStore];

//2 check for a persisted calendar id

NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];

NSString *calendarId = [prefs stringForKey:@”appCalendar”];

//3

if (calendarId && (result = [store calendarWithIdentifier: calendarId])) {

return result;

}

//4 check if calendar name exists

for (EKCalendar* cal in store.calendars) {

if ([cal.title compare: kAppCalendarTitle]==NSOrderedSame) {

if (cal.immutable == NO) {

[prefs setValue:cal.calendarIdentifier

forKey:@”appCalendar”]; [prefs synchronize]; return cal;

}

}

}

//5 if no calendar is found whatsoever, create one

result = [self createAppCalendar];

//6

return result;

}

// ADD ENTRY FROM VC THRU CALEDNAR OBJECT

-(void)addItem:(NSDictionary*)item toCalendar:(EKCalendar*)calendar{

EKEvent* event = [EKEvent eventWithEventStore:[AppCalendar eventStore]];

event.calendar = calendar;

EKAlarm* myAlarm = [EKAlarm alarmWithRelativeOffset: – 06*60 ];

[event addAlarm: myAlarm];

NSDateFormatter* frm = [[NSDateFormatter alloc] init];

[frm setDateFormat:@”MM/dd/yyyy HH:mm zzz”];

[frm setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@”en_US”]];

event.startDate = [frm dateFromString:[show objectForKey:@”startDate”]];

event.endDate = [frm dateFromString:[show objectForKey:@”endDate”]];

event.title = [item objectForKey:@”title”];

event.URL = [NSURL URLWithString:[item objectForKey:@”url”]];

event.location = @”My Office”;

event.notes = [item objectForKey:@”tip”];

//add recurrence

NSNumber* weekDay = [item objectForKey:@”dayOfTheWeek”];

EKRecurrenceDayOfWeek* itemDay = [EKRecurrenceDayOfWeek dayOfWeek: [weekDay intValue]];

EKRecurrenceEnd* runFor3Months = [EKRecurrenceEnd recurrenceEndWithOccurrenceCount:12];

EKRecurrenceRule* myReccurrence = [[EKRecurrenceRule alloc] initRecurrenceWithFrequency:EKRecurrenceFrequencyWeekly interval:1

daysOfTheWeek:[NSArray arrayWithObject:itemDay] daysOfTheMonth:nil monthsOfTheYear:nil weeksOfTheYear:nil daysOfTheYear:nil setPositions:nil end:runFor3Months];

[event addRecurrenceRule: myReccurrence];

//1 save the event to the calendar

NSError* error = nil;

[[AppCalendar eventStore] saveEvent:event span:EKSpanFutureEvents commit:YES error:&error];

//2 show the edit event dialogue

EKEventEditViewController* editEvent = [[EKEventEditViewController alloc] init];

editEvent.eventStore = [AppCalendar eventStore]; editEvent.event = event;

editEvent.editViewDelegate = self;

[self presentViewController:editEvent animated:YES completion:^{

UINavigationItem* item = [editEvent.navigationBar.items objectAtIndex:0]; item.leftBarButtonItem = nil;

}];

}

//////REMINDERS

//EVENT CLASS OBJECT

– (id) init {

self = [super init];

if (self) {

_eventStore = [[EKEventStore alloc] init];

[_eventStore requestAccessToEntityType:EKEntityTypeEvent

completion:^(BOOL granted, NSError *error) {

if (granted) {

_eventAccess = YES;

} else {

NSLog(@”Event access not granted: %@”, error);

}

}];

[_eventStore requestAccessToEntityType:EKEntityTypeReminder completion:^(BOOL granted, NSError *error) {

if (granted) { _reminderAccess = YES;

} else {

NSLog(@”Reminder access not granted: %@”, error);

}

}];

//Initialize the dispatch queue

_fetchQueue = dispatch_queue_create(“com.santiapps.fetchQueue”, DISPATCH_QUEUE_SERIAL);

}

return self;

}

//LOOP THRU CALENDARS

NSArray * allCalendars = [_eventStore calendarsForEntityType:EKEntityMaskEvent |

EKEntityMaskReminder];
NSMutableArray * writableCalendars = [NSMutableArray array]; for (EKCalendar * calendar in allCalendars) {

if (calendar.allowsContentModifications) { [writableCalendars addObject:calendar];

} }

EKCalendar *calendar = eventStore.defaultCalendarForNewEvents;

//CREATE EVENT

//1

EKEvent *event = [EKEvent eventWithEventStore:eventStore]; event.title = @”Hello world!”;
event.startDate = startDate;
event.endDate = endDate;

EKAlarm *alarm = [EKAlarm alarmWithRelativeOffset:-1800]; [event addAlarm:alarm];

event.calendar = eventStore.defaultCalendarForNewEvents;

//2

NSError *err;
BOOL saved = [eventStore saveEvent:event span:EKSpanThisEvent commit:YES error:&err];

if (!saved) {
NSLog(@”Error creating the event”);

}

//REMINDER

//1

EKReminder *reminder = [EKReminder reminderWithEventStore:eventStore];

reminder.title = @”Creating my first reminder”; reminder.calendar = [eventStore defaultCalendarForNewReminders];

NSCalendar *calendar =
[NSCalendar currentCalendar];

NSUInteger unitFlags = NSEraCalendarUnit |

NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;

NSDateComponents *dueDateComponents = [calendar components:unitFlags fromDate:dueDate];

//1.5. Set the due date

reminder.dueDateComponents = dueDateComponents;

//2

NSError *err;

BOOL success =

[eventStore saveReminder:reminder commit:YES error:&err];

if (!success) {
NSLog(@”Error creating reminder”);

}

6)   Local Notifications

//test notif

NSDate *date = [NSDate dateWithTimeIntervalSinceNow:10];

UILocalNotification *localNotification = [[UILocalNotification alloc] init];

localNotification.fireDate = date;

localNotification.timeZone = [NSTimeZone defaultTimeZone];

localNotification.alertBody = @”I am a local notification!”;

localNotification.soundName = UILocalNotificationDefaultSoundName;

[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];

7)   Sort NSArrays for tableviews

-(void)sort{

NSSortDescriptor *sortDescriptor;

sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@”distance” ascending:YES] autorelease];

NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];

NSArray *sortedArray;

sortedArray = [self.annotationsToSort sortedArrayUsingDescriptors:sortDescriptors];

self.annotationsToSort = [(NSArray*)sortedArray mutableCopy];

[self.tableView performSelector:@selector(reloadData) withObject:nil afterDelay:0.5];

}

8)   Contacts

// Read Contact list xml formatted (Array of Dictionaries)

//read directory here

dispatch_async(kBgQueue , ^{

contacts = [NSArray arrayWithContentsOfURL: kContactsList];

dispatch_async(dispatch_get_main_queue(), ^{

[self.tableView reloadData]; });

});

// Do this

//NO ADD THEM TO AB

dispatch_async(kBgQueue , ^{

NSString* request = [NSString stringWithFormat:@”%@?%@”, kContactsList, ids]; //1

NSData *responseData = [NSData dataWithContentsOfURL:[NSURL URLWithString:request]]; //2

//parse vCard data

ABAddressBookRef addressBook = ABAddressBookCreate(); //1

ABRecordRef record = ABPersonCreate(); //2

NSArray* importedPeople = (__bridge_transfer NSArray*)

ABPersonCreatePeopleInSourceWithVCardRepresentation( record, (__bridge CFDataRef)responseData); //3

CFBridgingRelease(record); //4

//define few constants

__block NSMutableString* message = [NSMutableString stringWithString:

@”Contacts imported AB. Add them in Facebook: “];

NSString* serviceKey = (NSString*)kABPersonSocialProfileServiceKey;

NSString* facebookValue = (NSString*)

kABPersonSocialProfileServiceFacebook;

NSString* usernameKey = (NSString*)kABPersonSocialProfileUsernameKey;

//loop over people and get their facebook

for (int i=0;i<[importedPeople count];i++) { //1

ABRecordRef personRef = (__bridge ABRecordRef) [importedPeople objectAtIndex:i];

ABAddressBookAddRecord(addressBook, personRef, nil);

//2

ABMultiValueRef profilesRef = ABRecordCopyValue( personRef, kABPersonSocialProfileProperty);

NSArray* profiles = (__bridge_transfer NSArray*) ABMultiValueCopyArrayOfAllValues(profilesRef);

//3

for (NSDictionary* profile in profiles) { //4

NSString* curServiceValue = [profile objectForKey:serviceKey]; //5

if ([facebookValue compare: curServiceValue] == NSOrderedSame) { //6

[message appendFormat: @”%@, “, [profile objectForKey: usernameKey]];

}

}

//7

CFBridgingRelease(profilesRef);

}

//save to addressbook

ABAddressBookSave(addressBook, nil);

CFBridgingRelease(addressBook);

//show done alert

dispatch_async(dispatch_get_main_queue(), ^{ [[[UIAlertView alloc]

initWithTitle: @”Done!” message: message

delegate: nil cancelButtonTitle:@”OK” otherButtonTitles:nil] show];

});

});

9)   Mapkit

//CHECK LOCATION SERVICES

-(void)checkLocationServices{

BOOL locationAllowed = [CLLocationManager locationServicesEnabled];

if (locationAllowed==NO) {

UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@”Location Service Disabled”

message:@”Re-enable in Settings for this app.”

delegate:nil

cancelButtonTitle:@”OK”

otherButtonTitles:nil];

[alert show];

[alert release];

}

}

//GET LOCATION

-(void)getLocation{

locationManager = [[CLLocationManager alloc] init];

locationManager.delegate = self;

locationManager.distanceFilter = 10;

locationManager.desiredAccuracy = kCLLocationAccuracyBest;

[locationManager startUpdatingLocation];

//- (void)startMonitoringSignificantLocationChanges

_mapView.showsUserLocation = YES;

}

-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {

self.userLocation = [locations lastObject];

NSLog(@”userLocation is %f, %f”, self.userLocation.coordinate.latitude, self.userLocation.coordinate.longitude);

CLLocationCoordinate2D zoomLocation;

zoomLocation.latitude = self.userLocation.coordinate.latitude;

zoomLocation.longitude = self.userLocation.coordinate.longitude;

NSLog(@”Using %g,%g”, zoomLocation.latitude, zoomLocation.longitude);

//StopUpdating Location so it wont change all the time

[locationManager stopUpdatingLocation];

}

//GET ANNOTATIONS

CLLocation *pinLocation = [[CLLocation alloc] initWithLatitude:annotation.coordinate.latitude longitude:annotation.coordinate.longitude];

CLLocationDistance calculatedDistance = [pinLocation distanceFromLocation:self.userLocation];

annotation.distance = calculatedDistance/1000;

//PLOT ANNOTATIONS

-(void)plotAnnotations{

//Clean out old annotations

for (id<MKAnnotation> annotation in _mapView.annotations) {

[_mapView removeAnnotation:annotation];

}

//THIS LOGS THE LOCATIONSTOSORT ARRAY OF FVC

//NSLog(@”self.myLocationsToSort %@”, self.myLocationsToSort);

for (MyLocation *annotation in self.myLocationsToSort) {

//Add annotation to mapview

[_mapView addAnnotation:annotation];

}

}

– (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {

static NSString *identifier = @”MyLocation”;

if ([annotation isKindOfClass:[MyLocation class]]) {

//test if mapviewnil

if (_mapView == nil) {

NSLog(@”NIL”);

}

MKPinAnnotationView *annotationView = (MKPinAnnotationView *) [_mapView dequeueReusableAnnotationViewWithIdentifier:identifier];

if (annotationView == nil) {

annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];

annotationView.enabled = YES;

annotationView.canShowCallout = YES;

annotationView.image=[UIImage imageNamed:@”arrest.png”];

} else {

annotationView.annotation = annotation;

}

//instatiate a detail-disclosure button and set it to appear on right side of annotation

UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];

//[infoButton addTarget:self action:@selector(showDetailView:annotationView.annotation) forControlEvents:UIControlEventTouchUpInside];

annotationView.rightCalloutAccessoryView = infoButton;

return annotationView;

}

return nil;

}

//GET ANNOTATIONS

//ZOOM INTO LOCATION

– (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {

// We have location, do your logic of setting the map region here.

CLLocationCoordinate2D zoomLocation;

NSLog(@”location in FirstVC %@”, self.userLocation);

//Test if userLocation has a value

NSLog(@”NOCITYSELECTED vDL FVC”);

zoomLocation.latitude = self.userLocation.coordinate.latitude;

zoomLocation.longitude = self.userLocation.coordinate.longitude;

CLLocationDistance visibleDistance = 5000; // 5 kilometers

MKCoordinateRegion adjustedRegion = MKCoordinateRegionMakeWithDistance(zoomLocation, visibleDistance, visibleDistance);

[_mapView setRegion:adjustedRegion animated:YES];

}

//TAP ON PINS – ANNOTATIONS

//Called when disclosure tapped

– (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control{

if (![view.annotation isKindOfClass:[MyLocation class]])

return;

// use the annotation view as the sender

[self performSegueWithIdentifier:@”DetailVC” sender:view];

}

10)                  UICustomization/Appearance

[[UINavigationBar appearance] setShadowImage:[[UIImage alloc] init]];

[self.tabBarItem setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[UIFont fontWithName:@”Futura” size:10.0f], UITextAttributeFont, nil] forState:UIControlStateNormal];

UIImage *gradientImage32 = [[UIImage imageNamed:@”FirstCellLogo.png”] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0)];

[[UINavigationBar appearance] setBackgroundImage:gradientImage32 forBarMetrics:UIBarMetricsDefault];

[self.tabBarItem setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[UIFont fontWithName:@”Futura” size:10.0f], UITextAttributeFont, nil] forState:UIControlStateNormal];

[[UITabBar appearance] setSelectedImageTintColor:[UIColor redColor]];

11)                  LetterPress Effect

//letterpress effect the label

//NSMutableAttributedString

NSMutableAttributedString *restName = [[NSMutableAttributedString alloc] initWithString:self.attributionText.text];

[restName addAttributes:@{ NSTextEffectAttributeName : NSTextEffectLetterpressStyle, NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline] } range:NSMakeRange(0, restName.length)];

self.attributionText.attributedText = restName;

12)                  UIGesture

13)                  iCloud

14)                  Library.a

15)                  Instruments

16)                  Checklist App

17)                  UICollectionView & UIContainerView & SplitVC & Popover

18)                  SlideUp

19)                  Raised Tabbar

20)                  Passbook

21)                  AR

22)                  Export Data

23)                  TF

24)                  iAds & AppiRater

25)                  TimeCmoparator – String Parsing

26)                  UIApplicationDelegate sharedApplication & its methods

27)                  NSUserDefaults

// call the store object

NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];

// set an object for it

[prefs setObject:imageView.image forKey:@”keyToLookupImage”];

// call sync to save it

[prefs synchronize];

NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];

UIImage *retrievedImage = [prefs stringForKey:@”keyToLookupImage”];

imageView.image = retrievedImage;

28)                  Façade design pattern

29)                  Protocols & Delegates

30)                  InAppPurchases

31)                  Datepicker/uipicker

32)                  NSOperation

33)                  Date Formatter

NSDate *date = [NSDate date];

NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];

[dateFormatter setDateStyle:NSDateFormatterShortStyle];

[dateFormatter setTimeStyle:NSDateFormatterFullStyle];

NSString *formattedDateString = [dateFormatter stringFromDate:date];

NSLog(@”formattedDateString for locale %@: %@”,

[[dateFormatter locale] localeIdentifier], formattedDateString);

timeLabel.text = [dateFormatter stringFromDate:date];

34)                  Letterpress Effect

35)                  asd

First Google Glass App – Part 8 – Hello Glass!

Jumping right in, let’s create a Glass GDK project from scratch:

  1. Create New Android Project
  2. Configure GDK
  3. Imports
  4. Code
  5. qwe

To create a new project…See our Part 1 of the tutorial.

Make sure to configure the Glass GDK Sneak Peek Manually if it didn’t get configured by Android Studio or Eclipse on set up.  As it turns out, even if you create a project setting GDK as the Compile for API, it doesn’t get created as such.  You must double check in your build.gradle file (CAREFUL, there are 2 such files.  You need to modify your inner most gradle file) and make sure it looks something like this:

Google Glass GDK App Build Gradle File Settings by Marcio Valenzuela Santiapps.com
Google Glass GDK App Build Gradle File Settings by Marcio Valenzuela Santiapps.com

And you need to make sure that Android 4.0.3 GDK Sneak Peek & gdk.jar got added to your External Libraries as well.

Ok, once we have that out of the way, we need to include some imports.  In this case we need to add AudioManager support because we will be working with audio.  We also need TextToSpeech for recognizing commands and KeyEvent to respond to touches.

Add the following imports:

import android.app.Activity;
import android.content.Context;
import android.media.AudioManager;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.view.View;
import android.view.KeyEvent;
import android.widget.TextView;

import com.google.android.glass.app.Card;
import com.google.android.glass.media.Sounds;

We will create the card and its view as well as add a TextView, nothing new here.  But we will create a TextToSpeech variable as well as a context.  We init our speech engine and pass it the value to be speak.

We will also be creating an onKeyDown method to respond to taps on the touchpad.  When they DO occur we will then create an AudioManager to play a tap sound, set the text view and also set speak a new value.

So lets add the following code:

public class HelloGlassActivity extends Activity {

private Card _card;
private View _cardView;
private TextView _statusTextView;

private TextToSpeech _speech;

private Context _context = this;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Init TextToSpeech engine
_speech = new TextToSpeech(this, new TextToSpeech.OnInitListener() {
@Override
public void onInit(int status) {
_speech.speak(“Hello Glass”, TextToSpeech.QUEUE_FLUSH, null);
}
});

// An alternative way to layout the UX
setContentView(R.layout.layout_helloworld);
_statusTextView = (TextView)findViewById(R.id.status);
}

/**
* Handle the tap from the touchpad.
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
// Handle tap events.
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_ENTER:

// Status message below the main text in the alternative UX layout
AudioManager audio = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
audio.playSoundEffect(Sounds.TAP);

_statusTextView.setText(R.string.touchpad_touched);

_speech.speak(“Touchpad touched”, TextToSpeech.QUEUE_FLUSH, null);

return true;
default:
return super.onKeyDown(keyCode, event);
}
}

@Override
public void onResume() {
super.onResume();
}

@Override
public void onPause() {
super.onPause();
}

@Override
public void onDestroy() {
super.onDestroy();
}
}

Its quite a simple app but it gets your juices flowing!

Plug in your Glass device and hit the Run button!

iOS7 Sprite Kit for Game Design for iPhone & iPad

iOS 7 Series – Sprite Kit

Welcome to iOS7 and to start off, I want to kick things off with SpriteKit.  Although it deals with video games, many companies are using iOS apps as a marketing tactic to engage their users in an effort to promote their products.

SpriteKit is the most prominent feature in iOS7 so we’re going to take a quick tour.  Go ahead and create a New Project in XCode5 and select the SpriteKit template (the bottom right icon):

iOS7 SpriteKit Basics by Marcio Valenzuela Santiapps.com
iOS7 SpriteKit Basics by Marcio Valenzuela Santiapps.com

Click next and fill in your project data.  Once you are in the main XCode window notice we have the following files in the Project Navigator:

1)   AppDelegate

2)   Main.Storyboard

3)   ViewController

4)   MyScene

If you look in the AppDelegate you will see nothing new, just a UIWindow property.  The Main.Storyboard is even more eerie.  It’s nothing more than a blank scene.  Then you find the ViewController.h file, which basically imports the SpriteKit framework.  If you check in your Target Linked Libraries you can see SpriteKit in linked.

ViewController.m is where things get interesting.

– (void)viewDidLoad

{

[super viewDidLoad];

// Configure the view.

SKView * skView = (SKView *)self.view;

skView.showsFPS = YES;

skView.showsNodeCount = YES;

// Create and configure the scene.

SKScene * scene = [MyScene sceneWithSize:skView.bounds.size];

scene.scaleMode = SKSceneScaleModeAspectFill;

// Present the scene.

[skView presentScene:scene];

}

In viewDidLoad we create an SKView object and assign our viewcontroller’s view to it.  As an SKView type view, we have a property that allows us to display the fps onscreen and another one for viewing the nodes onscreen for debugging purposes.

We now create a second object of type SKScene and instantiate it with the size of our SKView object to make sure it fits.  Then we set the SKScene object’s scaleMode to AspectFill.  Finally we tell our SKView object to present our SKScene object each with its set properties.

There are a couple of extra methods that tell the app to autorotate and returns the supported orientations.

Now let’s move on to the MyScene object created because this is where the magic happens.

-(id)initWithSize:(CGSize)size {

if (self = [super initWithSize:size]) {

/* Setup your scene here */

self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];

SKLabelNode *myLabel = [SKLabelNode labelNodeWithFontNamed:@”Chalkduster”];

myLabel.text = @”Hello, World!”;

myLabel.fontSize = 30;

myLabel.position = CGPointMake(CGRectGetMidX(self.frame),

CGRectGetMidY(self.frame));

[self addChild:myLabel];

}

return self;

}

First we create an instance of self with the passed in value for size.  Next we set the background color and create a label in order to position it in the middle of the screen.

The next method reacts to screen touches using the well known touchesBegan method.

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

/* Called when a touch begins */

for (UITouch *touch in touches) {

CGPoint location = [touch locationInNode:self];

SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:@”Spaceship”];

sprite.position = location;

SKAction *action = [SKAction rotateByAngle:M_PI duration:1];

[sprite runAction:[SKAction repeatActionForever:action]];

[self addChild:sprite];

}

}

Here we loop through any touches and get their location as a CGPoint.  Then we instantiate a sprite from a file named Spaceship and set its position to the touch received.  Finally we create a rotating action and run it on the sprite and add that now rotating sprite to our SKScene.  Voila.

However, there is a very important method left out, the update method.  This method gets called every so often, many times a second actually!  So this is the method used to execute our game logic.

So let’s assume we are the Head iOS Programmer for a big soda company and you met with the Marketing Director about making a game to promote your product.  You decide game will be really simple, such as trying to catch a soda can falling from the sky at the beach.  What you need:

a)    A beach backdrop

b)   A soda can

c)    A potential client

d)   You also need a good marketing strategy to go with the game, otherwise playing the game won’t amount to much.  Such a strategy could be whoever makes the most points in the next month, gets a free 24pack or something.  But you better check with your Marketing Director first J

iOS7 SpriteKit Basics by Marcio Valenzuela Santiapps.com
iOS7 SpriteKit Basics
iOS7 SpriteKit Basics by Marcio Valenzuela Santiapps.com
iOS7 SpriteKit Basics
iOS7 SpriteKit Basics by Marcio Valenzuela Santiapps.com
iOS7 SpriteKit Basics Sprites

So let’s go ahead and google some images:

Ok so let’s plop these on screen to make sure the sizes are proportional.  We are going to be making this project for an iPhone Retina screen so the correct beach size should be the complete screen size, which is 960×640.  To put the beach image on the screen, aside from importing it into your project:

iOS7 SpriteKit Basics by Marcio Valenzuela Santiapps.com
iOS7 SpriteKit Basics

We need to add the code.  So let’s move over to our MyScene initWithSize method because that is only called once and we don’t need to change the background for this game after its been created the first time.  First remove the entire code inside the if block and replace it:

SKSpriteNode *background = [SKSpriteNode spriteNodeWithImageNamed:@”beach.png”];

background.anchorPoint = CGPointZero;

background.position = CGPointZero;

[self addChild:background];

This created a sprite called background using the beach image.  It positions the image and adds it to the scene.  If you run it you’ll get a chopped off image because the simulator is positioned vertically.  This is because we didn’t tell the app to only run in landscape mode.  So head over to your Target Settings and uncheck all orientations except Landscape Left.

iOS7 SpriteKit Basics by Marcio Valenzuela Santiapps.com
iOS7 SpriteKit Basics

FIRST NOTE: Orientation must be defined.

Now run it again and you’ll get a huge image as the background and possibly only part of it being displayed.  This is because you must tell Xcode that the image is to be used in Retina devices.  You do this by renaming the images beach@2x.png.  So go ahead and rename the image in Finder and you’ll have to remove the reference and re-add it as beach@2x.png.  Voila!

iOS7 SpriteKit Basics by Marcio Valenzuela Santiapps.com
iOS7 SpriteKit Basics

Ok so far so good!  Notice we get the node and fps count on the right bottom corner.

NOTE: Retina images are named filename@2x.png

Let’s go ahead and place the cooler on the screen to see if it looks right.  This time remember to rename your images before importing them.  After importing it, let’s add the following code, again in the initWithSize method because we only need to add the cooler once:

// ADD COOLER

SKSpriteNode *cooler = [SKSpriteNode spriteNodeWithImageNamed:@”cooler@2x.png”];

cooler.anchorPoint = CGPointZero;

cooler.position = CGPointZero;

[self addChild:cooler];

Just for your reference, my cooler png is 125×131, almost a square.  I added it and it looks fine:

iOS7 SpriteKit Basics by Marcio Valenzuela Santiapps.com
iOS7 SpriteKit Basics

As you can see, node count went up to 2 and fps has no reason to change so far.  There is something important to note here, and it’s that the cooler was added in the bottom left corner.  If you review the code, we set both the background and the cooler’s anchorPoint to CGPointZero, which is equal to 0,0.  This corresponds to the bottom left of the screen.  Thus when we position the cooler, we are telling it to put the cooler’s bottom left corner (its anchor), in the screen’s bottom left corner (its position).

NOTE: CGPointZero in SpriteKit = (0,0) = Bottom Left Corner

Ok now let’s think about the logic.  Both the background and the cooler need only appear once.  The soda can however might need to appear more than once because once the user catches the first one, we probably want to give him a second one to catch, otherwise it’ll be a short and disappointing game.  This is where the game structure gets interesting.  We won’t simply add the soda can in the initWithSize method but rather outsource its creation to a separate method.  Let’s create a method called createSodaCan:

-(void)createSodaCan{

// ADD CAN

SKSpriteNode *can = [SKSpriteNode spriteNodeWithImageNamed:@”sodacan@2x.png”];

can.anchorPoint = CGPointMake(CGRectGetMidX(can.frame), CGRectGetMidY(can.frame));

can.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));

[self addChild:can];

}

Here we are setting the can’s anchor point to its center and placing it in the center of the screen using CGPointMake and CGRectGetMidX/Y.  However if you run the app there won’t be a soda can anywhere onscreen because we haven’t called it.  One place to call it would be in a method such as the touchesBegan method, but let’s try something different.  In your initWithSize method, add the following code right after the cooler code:

//Delay to call can method

[NSTimer scheduledTimerWithTimeInterval:2.0

target:self

selector:@selector(createSodaCan)

userInfo:nil

repeats:NO];

This basically calls the createSodaCan method after 2 seconds of initializing the scene.  And two seconds after, there it is:

iOS7 SpriteKit Basics by Marcio Valenzuela Santiapps.com
iOS7 SpriteKit Basics

NOTE: Game logic can be fired off from the init method of your scene or from a user interaction method such as the touchesBegan method.

Great!  Now let’s think of what we want to do.  Im thinking we could do something like:

1)   Center the cooler at the bottom of the screen

2)   Move the soda can up to the top

3)   Have the can move from top to bottom

Ok great, so let’s do the first two, which should be quite simple.  Then we’ll worry about moving the can.  The first step is the cooler, so replace the cooler code with:

// ADD COOLER

SKSpriteNode *cooler = [SKSpriteNode spriteNodeWithImageNamed:@”cooler@2x.png”];

cooler.anchorPoint = CGPointMake(CGRectGetMidX(cooler.frame), CGRectGetMidY(cooler.frame));

cooler.position = CGPointMake(self.frame.size.width*0.5, self.frame.size.height*0.01);

[self addChild:cooler];

This will place the cooler at 50% the width of the screen and about 1% from bottom to top.  Now do something similar with the can except that we want it to be on top so it can drop:

// ADD CAN

SKSpriteNode *can = [SKSpriteNode spriteNodeWithImageNamed:@”sodacan@2x.png”];

can.anchorPoint = CGPointMake(CGRectGetMidX(can.frame), CGRectGetMidY(can.frame));

can.position = CGPointMake(self.frame.size.width/2, self.frame.size.height*0.75);

[self addChild:can];

This places the can halfway across from left to right and ¾ of the way up the screen:

iOS7 SpriteKit Basics by Marcio Valenzuela Santiapps.com
iOS7 SpriteKit Basics

NOTE: We use relative positioning of an object in the view’s frame to avoid offscreen or misplaced images on different sized screens.

Perfect!  Now how do we animate the can so it can fall?  Well there are two ways, one is using physics, which requires us to create a physics-body object and assign it to the soda can and then “turn on” gravity so to speak.  The much simpler way is to animate the can to go from the top of the screen to the bottom of the screen.

To use the latter, we need SKActions much like the rotate action in the template.  So let’s code some and see what happens.  Well I took a look at the way the template created the rotating action and started typing…XCode took care of the rest.  I reasoned that what I wanted was to “move” the sprite and XCode suggested the following:

//Move the can

SKAction *fallingAction = [SKAction moveByX:<#(CGFloat)#> y:<#(CGFloat)#> duration:<#(NSTimeInterval)#>]

So I went with that and ended up with this code for the createSodaCan method:

// ADD CAN

SKSpriteNode *can = [SKSpriteNode spriteNodeWithImageNamed:@”sodacan@2x.png”];

can.anchorPoint = CGPointMake(CGRectGetMidX(can.frame), CGRectGetMidY(can.frame));

can.position = CGPointMake(self.frame.size.width/2, self.frame.size.height*0.75);

//Move the can

SKAction *fallingAction = [SKAction moveByX:0 y:-400 duration:2.0];

[can runAction:fallingAction];

[self addChild:can];

Well that’s not half bad.  I could’ve used another method such as moveTo instead of moveBy.

Ok now let’s simulate how to actually “catch” the can in the cooler.  What we need is something called “collision detection”.  What we want to do is check to see if the rectangle around the cooler is intersecting with the rectangle around the can.  To do this, we have to do so constantly.  That’s where the update method comes in.  This method is called repeatedly before each frame is drawn.  So we need to ask it if those two rectangles are colliding.  That means we need references to both objects.  To do that we create an instance variable for each, so we add them in MyScene.h:

@interface MyScene : SKScene {

SKSpriteNode *cooler;

SKSpriteNode *can;

}

Now modify the code in initWithSize by removing the type declaration in each line where we create the cooler and the can so they look like:

cooler = [SKSpriteNode spriteNodeWithImageNamed:@”cooler@2x.png”];

can = [SKSpriteNode spriteNodeWithImageNamed:@”sodacan@2x.png”];

And now we can access those objects through their pointers in the update method as well.  So add this line to your update method:

//Check for intersection of frames for collision simulation

[self testCollisionOfSprite:can withSprite:cooler];

This means this line will be called every time the update method fires.  You can even add an NSLog into the update method to get an idea of how many times its called.  Now let’s define the testCollisionOfSprite:withSprite: method:

-(void)testCollisionOfSprite:(SKSpriteNode*)sprite1 withSprite:(SKSpriteNode*)sprite2{

CGRect sprite1Rect = [self getSizeOfSprite:sprite1];

CGRect sprite2Rect = [self getSizeOfSprite:sprite2];

if (CGRectIntersectsRect(sprite1Rect, sprite2Rect)) {

NSLog(@”They collided”);

}

}

Nothing special going on here, we are simply creating a rectangle for each of the 2 sprites passed in and checking if those rectangles intersect.  If they do, we log.  Finally the way we get those rectangles around the sprites:

– (CGRect) getSizeOfSprite:(SKSpriteNode*)sprite{

double sx = sprite.position.x;

double sy = sprite.position.y;

return CGRectMake(sx, sy, sprite.frame.size.width, sprite.frame.size.height);

}

We have a game…almost.  Let’s add some sound and make the can disappear.

Import your favorite sound and add this code right after the NSLog for They Collided:

SKAction *play = [SKAction playSoundFileNamed:@”explosion_small.caf” waitForCompletion:YES];

[can runAction:play];

can.alpha = 0;

As you noticed earlier, the collision happens many times because there is a long trajectory form the moment the rectangles begin overlapping until they stop overlapping.  This results in a repetitive sound, which is annoying.  More importantly if becomes a problem if you want to work with scoring points because if you try to +1 a point when the collision occurs, you actually end up with many more points in total!  So what is usually done in these cases is a work around that creates a BOOL flag from the start and sets it to False.  We do this in MyScene.h:

BOOL collisionOccurred;

then in the createSodaCan method we set it to FALSE:

collisionOccurred = FALSE;

Finally we modify the if test like so:

if (CGRectIntersectsRect(sprite1Rect, sprite2Rect) && !collisionOccurred) {

collisionOccurred = TRUE;

NSLog(@”They collided”);

SKAction *play = [SKAction playSoundFileNamed:@”explosion_small.caf” waitForCompletion:YES];

[can runAction:play];

can.alpha = 0;

}

This way we always check to see if collisionOccurred is FALSE, if it is then we set it to TRUE inside the if, do our stuff, thus collisionOccurred will not be FALSE again until we set it back.

It is a cumbersome solution because then you must make sure you re-set your flag to FALSE everytime you create a new can.  It even worse if you don’t really create more cans.  Many times for memory saving reasons, we simply don’t destroy the original can.  If you noticed I didn’t destroy the node, I simply made it invisible.  The reason is that now I can reuse that sprite again by repositioning it on top and making it visible again thus saving processor power.  This is a very common tactic in games where you want to have lots of enemies or coins or what not and you don’t want to have to create thousands of instances of them.

The other more elegant solution to the collision detection repetition issue is the first method we talked about collision detection.  So let’s try to cover that in as simple terms as possible.

Physics & Collision Detection

Physics introduces the notion of physical bodies that will be “attached” to our sprite nodes.  Those bodies can be of type dynamic, static or edges.  A dynamic body reacts to physics, static bodies react to physics except forces and collisions because they are meant not to move yet exist in a physical world.  Edges never move and they are usually used to represent boundaries in a game.

iOS7 SpriteKit Basics by Marcio Valenzuela Santiapps.com
iOS7 SpriteKit Basics

This means that we can try to match a physics body to a sprite node identically, such as:

iOS7 SpriteKit Basics by Marcio Valenzuela Santiapps.com
iOS7 SpriteKit Basics

which results in a very complex physics body to monitor vs simply using a tall rectangle which results in a much easier body to manipulate.  A circle is actually the easiest body to use.

So let’s start off by creating a border around our screen by adding this line at the end of our initWithSize method:

//Create border

self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];

and in the createSodaCan method add these lines to the end:

can.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:can.size.width/2];

can.physicsBody.dynamic = YES;

What we have done is create a second physicsBody for the can this time and made it of circular shape and assigned it a dynamic property.  Now let’s remove the line in the update method that calls for testCollisionOfSprite:withSprite: because we don’t need that anymore.  Now run the app and you should see the soda can actually stop near the bottom of the screen which is where the border should be.  But the border is invisible, isn’t it?  Go ahead and comment out the border line in the init method and see for yourself J.

Ok so now let’s remove our line that made the can move from top to bottom:

//[can runAction:fallingAction];

And now let gravity do the work for you, add this line at the end of your initWithSize method:

//Set Gravity

self.physicsWorld.gravity = CGPointMake(0.0, -9.8);

Cool!  But the can didn’t bounce quite so much huh!?  Well that’s probably what a can would do.  But just for fun, let’s say it was a rubber can.  Add this line after the can’s dynamic physicsBody setting to YES:

can.physicsBody.restitution = 0.5;

Neat!  Well but we’re not gonna be covering physics now.  So turn that property off for now.  We just need physics for collision detection.  This is done via collision groups.

First we adopt the SKPhysicsContactDelegate Protocol so we can set out scene as the delegate to receive notifications of collisions:

@interface MyScene : SKScene <SKPhysicsContactDelegate>{

First we set our scene as the delegate in the initWithSize method:

self.physicsWorld.contactDelegate = self;

Now we must set the categories in our can and cooler objects.  So after the cooler sprite creation in the initWithSize method, add this code to create a physics body for it and set its category mask:

//collision physics

cooler.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:cooler.size];

cooler.physicsBody.dynamic = YES;

cooler.physicsBody.categoryBitMask = coolerCategory;

cooler.physicsBody.collisionBitMask = canCategory;

cooler.physicsBody.contactTestBitMask = canCategory;

So we create a rectangular body instead of a circle and set its category to cooler but its collision mask to canCategory.

Likewise in the can creation method (createSodaCan) we use this code:

//Add physics body to can

can.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:can.size.width/2];

can.physicsBody.dynamic = YES;

//can.physicsBody.restitution = 0.5;

can.physicsBody.categoryBitMask = canCategory;

can.physicsBody.collisionBitMask = coolerCategory;

can.physicsBody.contactTestBitMask = coolerCategory;

As mentioned before, we set this body as a circle but commented out the restitution property for now.  We set its category to can and its collision mask to cooler.  We must define those categories above the @implementation line like so:

//DONT COLLIDE

static const uint32_t canCategory =  0x1 << 0;

static const uint32_t coolerCategory =  0x1 << 1;

Finally, we override the beginContact method of the protocol which reads:

//COLLISION PHYSICS

– (void)didBeginContact:(SKPhysicsContact *)contact

{

SKPhysicsBody *firstBody, *secondBody;

if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)

{

firstBody = contact.bodyA;

secondBody = contact.bodyB;

}

else {

firstBody = contact.bodyB;

secondBody = contact.bodyA;

}

if ((firstBody.categoryBitMask & coolerCategory) != 0)

{

NSLog(@”They collisionated!”);

}

}

We pass in a contact, then create 2 body references which will come in as part of that contact.  We set the contact bodies to either one and test for a collision and log something in the console.

Well this is as far as SpriteKit basics can get.  There are more advanced topics such as advanced physics, forces, particles, animations, parallax and raycasting that could be material for a Part 2 should there be enough interest in the subject.

First Google Glass App – Part 7 – Bridge to Glass App GDK Development

Before jumping into Glass dev, let’s understand how to create a Hello World project in Android Studio (AS) and run it on our device.

  1. Create New Project
  2. Get to know the guts
  3. Add Imports
  4. Add Code
  5. Tweak guts
  6. Run on Device

Create New Project

When you select New Project from the File Menu, you get this Wizard screen:

Android Studio Beginner App Development by Marcio Valenzuela Santiapps.com
Android Studio Beginner App Development

Fill in the Application Name in a natural language and the Module Name without spaces.  Make sure to select API 15 for Minimum and Target SDK but Glass Development Kit Sneak Peek for Compile with.

Click Next and in the next screen just leave everything as is (the launch icon selector screen).

Android Studio Project Launcher Icon Window by Marcio Valenzuela Santiapps.com
Android Studio Project Launcher Icon Window

After that screen, leave the Blank Activity option selected and again click Next.

Android Studio Project Blank Activity Window by Marcio Valenzuela Santiapps.com
Android Studio Project Blank Activity Window

Finally in the Activity Name, leave MainActivity.  In the Layout Name leave activity_main but also copy that activity_main over to the Fragment Layout Name, replacing fragment_main.

Android Studio Project Layout Window by Marcio Valenzuela Santiapps.com
Android Studio Project Layout Window

Everything else stays as is and Click Finish.  The reason for this last bit is that new in Android is this concept of Fragments.  This just complicates things for us at the moment so we will leave it out for now.  We must also remove the MainActivity.java fragment method later.

Android Guts

Let’s familiarize ourselves with this screen:

Android Studio Layout Google Glass Development by Marcio Valenzuela Santiapps.com
Android Studio Layout Google Glass Development

Let’s review these 10 top pointers:

  1. Your Java Classes or Android Activities
  2. Layout files in xml format
  3. Value files for storing global settings of sorts
  4. AndroidManifest is a sort of Central Registry
  5. Top level build.gradle file
  6. Low level build.gradle file
  7. Tab bar for displayed files
  8. Sync Gradle file button
  9. Run button
  10. Green = A-Ok button!
Take some time to explore these files.  If you have gone through the tutorial at:
 
 
You will be familiar with these pointers.  Otherwise, let’s take a closer look.
 
MainActivity is selected and displayed in the editor window.  This contains your default, boilerplate/template created MainActivity Class required for any app.
 
The layout folder contains your activity_main.xml file.  If you look inside that file you will see an xml file with some parameters and a TextView element.  You will also see a graphical representation of it off to the far right.
 
The values folder contains more xml files.  Most importantly, the strings.xml which contains global references to string values.  These are used throughout the app to assign string values where needed.
 
The AndroidManifest.xml file contains some general settings elements for your app such as the package name, launcher icon, activity tags which contain your declared activities and in this case, if the activity has an intent, which is like an action, it must also be specified here.  In our case we will add a Voice Trigger intent filter if there isn’t one already.
 
Build.gradle files are not to be messed around with much.  This is a new M.O. used by Android Studio to organize files in a workspace.  You must keep in mind here that there is a Top level and Low level build.gradle file so make sure you know which one you are being told to put things in.
 
The tabs display whatever files you have double clicked on the File Window on the left.
 
The Sync Gradle button is that green-blue circle with a solid circle inside and a green arrow pointing down.  Go Figure!  Its basically a sort of I’ve-made-some-changes-to-the-AndroidManifest-and/or-other-project-wide-parameters-which-require-project-workspace-reindexing (phew) button!
 
The Run button is of course the one used to build and launch the app in the emulator or device.
 
The Green=AOk button tells you all file and project inconsistencies have been resolved and that the project will build and run.  Sometimes you have resolved coding issues and this box is still red.  Just tap on the Sync Gradle button mentioned before and Just Like That, Its Magic…AOk!
 
Great now let’s look at some code…
 
Imports
 
If you expand the imports “+” sign in the MainActivity window’s left edge, you will get a list of what imports a project comes with.  Let’s just make sure they look like this:
 

import android.app.Activity;
import android.os.Bundle;
import com.google.android.glass.app.Card;

The last import as you can see is what allows us to create a Card instance, which is what Glass apps are based on.  This is what you put your info into in a Glass app.

Code

Next, simply add this method inside your public class MainActivity extends Activity statement:

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

Card card = new Card(this);
card.setText(“你 好 吗”);
card.setFootnote(“santiapps.com”);

setContentView(card.toView());
}

Here we are creating a new card, setting its text and footnote properties and setting its view to the Activity’s ContentView or the main view.

Voila!  Connect your Glass to your USB port, make sure to set it to Debug Mode ON and Run the app.  It will build and install on your Glass.

Tweak

Now let’s tweak it.  In your AndroidManifest, declare this intent by making that file look like this:

<?xml version=”1.0″ encoding=”utf-8″?>
<manifest xmlns:android=”http://schemas.android.com/apk/res/android&#8221;
package=”com.santiapps.glassapp” >

<application
android:allowBackup=”true”
android:icon=”@drawable/ic_launcher”
android:label=”@string/app_name” >
<activity
android:name=”com.santiapps.glassapp.MainActivity”
android:label=”@string/app_name” >
<intent-filter>
<action android:name=”com.google.android.glass.action.VOICE_TRIGGER” />
</intent-filter>
<meta-data android:name=”com.google.android.glass.VoiceTrigger”
android:resource=”@xml/voice_trigger” />
</activity>
</application>

</manifest>

I’ve pasted my entire file here so as to clear up as much as I can.  Basically you just need to add the intent-filter and its metadata elements.

Let’s make it more interesting!  As with our First Android App tutorial, let’s add some user interaction in Part 2.

Static Libraries in iOS – Video Tutorial

Static Library for iOS by Marcio Valenzuela Santiapps.com
Static Library for iOS

Libraries is another one of those rather obscure topics for newbie programmers, kinda like Blocks and Delegates/Protocols.  Think of libraries as just that, a resource you can use which doesn’t belong to you.  A loaner 🙂

NOTE: If you need a nice tutorial on Blocks or Delegates, check out:

Video Tutorial: Objective-C Blocks

Video Tutorial: Objective-C Protocols and Delegates

Ok back to our tutorial!  Well a library is a piece of code that you can use (by importing it into your projects) but its not yours, so you can’t really do what you want to that piece of code, except use its functionality.  In this tutorial we will create our own library and then import in into another project for its use.

The basic steps are:

1. Create your code. Usually Class method laden classes that create no state! Project must be CocoaTouch Library Framework.

2. Compile and create .a file.

3. Import that .a file into new projects.

In order to show you I prepared this small video:

StaticLibraryTut

For a more complete tutorial you can visit RW.com’s Creating a Static Library in iOS tutorial at:

http://www.raywenderlich.com/41377/creating-a-static-library-in-ios-tutorial

First Android App – Part 5

My First Android App

 

Android Studio New Main Layout First Android App Tutorial by Marcio Valenzuela Santiapps.com
Android Studio New Main Layout First Android App Tutorial

Android is based on Java much like iOS is based on ObjectiveC.  If you are coming from an iOS background, itll be a bit jarring at first.  Even though ObjC “comes from” C, C formats are a bit different.  So I thought Id start with that first.

ObjC:

[myObject methodForDoingThis:someParameter];

is a method call which refers to this declared method:

-(void)methodForDoingThis: (id)someParameter{

//1. Take the passed in parameter

//2. Do something to with that parameter value

//3. Call some other method…

//4. Or if this were a method that returned an object instead of void

//4. Return a new object value

}

C:

myObject.methodForDoingThis(someParameter);

is a method call which refers to this declared method:

public methodForDoingThis void (id someParameter) {

//1.-4. Same as above

}

So this might throw you off a bit.  Add to that the fact that their IDE, be it Eclipse or AndroidStudio (or others), or rather their project files are a little less UI-friendly.  What I mean is that where in Xcode you have editable code files in Objective C and either a storyboard or NIBs to deal with UI stuff, in Android IDEs you have an AndroidManifest.xml, various layout.xml files and AndroidActivity.java files as well as a few other ones.

Ok, let’s get started with this Hello World App.

First Android App

First let’s create a New Project in Android Studio:

Android Studio New Project First Android App Tutorial by Marcio Valenzuela Santiapps.com
First Android App Tutorial

Here you can give it an Application Name in natural language, this means with spaces or what not.  This is the human readable name.  Then give it a Module Name which is recommended to not have spaces or strange characters other than alphanumerical characters.  Finally create a package name which is in reverse domain notation (com.something.app).  That something can you be company name or just your name.

In the above example we are actually creating an Android Glass project for Google Glass.  The idea is the same, but for this Android App, select API 15 as all three, Minimum, Target && Compile.  Leave the Theme as is and click on Next.

You will be asked about launch icons and just click Next.

Android Studio New Project Launcher Icons First Android App Tutorial by Marcio Valenzuela Santiapps.com
Android Studio New Project Launcher Icons First Android App Tutorial

You will get another screen about Activities, just leave Blank Activity.

Android Studio New Project Blank Activiy First Android App Tutorial by Marcio Valenzuela Santiapps.com
Android Studio New Project Blank Activiy First Android App Tutorial

Finally you might get this window:

Android Studio New Project Fragment Layout First Android App Tutorial by Marcio Valenzuela Santiapps.com
Android Studio New Project Fragment Layout First Android App Tutorial

Here make sure to make that one change, just to make things easier.  We must also remove the MainActivity.java fragment method later.

Once this is done, remember to go ahead to the MainActivity.java class and open it.  We are going to remove one method in here:

public static class PlaceholderFragment extends Fragment {

public PlaceholderFragment() {

}

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

View rootView = inflater.inflate(R.layout.fragment_main, container, false);

return rootView;

}

}

Finally you should see a screen much like this:

Android Studio New Main Layout First Android App Tutorial by Marcio Valenzuela Santiapps.com
Android Studio New Main Layout First Android App Tutorial

Let’s review these 10 top pointers:

1. Your Java Classes or Android Activities

2. Layout files in xml format

3. Value files for storing global settings of sorts

4. AndroidManifest is a sort of Central Registry

5. Top level build.gradle file

6. Low level build.gradle file

7. Tab bar for displayed files

8. Sync Gradle file button

9. Run button

10.Green = A-Ok button!

You can run the app now, it won’t do much, but its an app J.

  1. MainActivity is selected and displayed in the editor window.  This contains your default, boilerplate/template created MainActivity Class required for any app.
  1. The layout folder contains your activity_main.xml file.  If you look inside that file you will see an xml file with some parameters and a TextView element.  You will also see a graphical representation of it off to the far right.
  1. The values folder contains more xml files.  Most importantly, the strings.xml which contains global references to string values.  These are used throughout the app to assign string values where needed.
  1. The AndroidManifest.xml file contains some general settings elements for your app such as the package name, launcher icon, activity tags which contain your declared activities and in this case, if the activity has an intent, which is like an action, it must also be specified here.  In our case we will add a Voice Trigger intent filter if there isn’t one already.
  1. Build.gradle files are not to be messed around with much.  This is a new M.O. used by Android Studio to organize files in a workspace.  You must keep in mind here that there is a Top level and Low level build.gradle file so make sure you know which one you are being told to put things in.
  1. The tabs display whatever files you have double clicked on the File Window on the left.
  1. The Sync Gradle button is that green-blue circle with a solid circle inside and a green arrow pointing down.  Go Figure!  Its basically a sort of I’ve-made-some-changes-to-the-AndroidManifest-and/or-other-project-wide-parameters-which-require-project-workspace-reindexing (phew) button!
  1. The Run button is of course the one used to build and launch the app in the emulator or device.
  1. The Green=AOk button tells you all file and project inconsistencies have been resolved and that the project will build and run.  Sometimes you have resolved coding issues and this box is still red.  Just tap on the Sync Gradle button mentioned before and Just Like That, Its Magic…AOk!

Layout Files

These determine where and how objects will be laid out onscreen.  Check out activity_main.xml and make sure it has this:

<?xml version=”1.0″ encoding=”utf-8″?>

<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android&#8221;

xmlns:tools=”http://schemas.android.com/tools&#8221;

android:layout_width=”match_parent”

android:layout_height=”match_parent”

android:orientation=”horizontal” >

</LinearLayout>

This simply tells us that we are using a Linear type layout.  And that the width and height should be stretched out to match whatever the parent is.

Now let’s actually add something onscreen by adding this code at the end of the last line but just before the </LinearLayout> closing tag:

<EditText android:id=”@+id/edit_message”

android:layout_width=”wrap_content”

android:layout_height=”wrap_content”

android:hint=”@string/edit_message” />

Ok, now we added an editable text field with id = edit_message which wraps its content and has a hint of placeholder text = whatever is in the edit_message string.

So what IS in the edit_message string?  Well, open your values folder and double click to open strings.xml.  You will see something like this:

<?xml version=”1.0″ encoding=”utf-8″?>

<resources>

<string name=”app_name”>My First App</string>

<string name=”edit_message”>Enter a message</string>

<string name=”button_send”>Send</string>

<string name=”action_settings”>Settings</string>

<string name=”title_activity_main”>MainActivity</string>

</resources>

As you can see here we have a string value for app_name, edit_message, button_send & action_settings.  You probably don’t have button_send but you will add it soon if you don’t.  This is a globally accessible xml values file.  Whenever some object looks for a string with an identifier, such as @string/xxx, it will fetch whatever is in that strings.xml file at that identifier location.

Nothing Magical here, just an app_name global identifier, a placeholder hint for the editable text field we added, one for a button and another for an action_settings action tool bar button.

So let’s add the button by going back to activity_main.xml and adding this code after the EditText block but again, before the closing </LinearLayout> tag:

<Button

        android:layout_width=”wrap_content”

        android:layout_height=”wrap_content”

        android:text=”@string/button_send” />

This should be pretty easy to understand.  This time we are adding a button with its text property set to the string value for button_send.  If you don’t have that button_send string in your strings.xml then add it in now.

Compile and run and you should see this:

Android Studio New Main Layout First Android App Tutorial by Marcio Valenzuela Santiapps.com
Android Studio New Main Layout First Android App Tutorial

As you can see they are ONLY as large as they have to be.  Both the EditText and the Button objects are only as large as their content is.

The only problem is that the text in the EditText field is, well, editable, thus it could grow longer or shrink.  We must make a small adjustment to account for this by modifying some properties of the EditText object:

<EditText

android:layout_weight=”1″

android:layout_width=”0dp”

… />

This means only add or edit the fields shown here (the rest, …, stays as is).  The layout_weight property states how much space the EditText field should take up relative to those objects around it.  So basically if EditText is 1 and Button is 1, the total is 2, out of which each will take half.

The final layout should look like this:

<?xml version=”1.0″ encoding=”utf-8″?>

<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android&#8221;

xmlns:tools=”http://schemas.android.com/tools&#8221;

android:layout_width=”match_parent”

android:layout_height=”match_parent”

android:orientation=”horizontal”>

<EditText android:id=”@+id/edit_message”

android:layout_weight=”1″

android:layout_width=”0dp”

android:layout_height=”wrap_content”

android:hint=”@string/edit_message” />

<Button

android:layout_width=”wrap_content”

android:layout_height=”wrap_content”

android:text=”@string/button_send” />

</LinearLayout>

Build & Run and see your app in all its splendor! Remember to check out Part 2 for a little user interaction before we move onto building Google Glass apps!

XCode & Git

Ok, i just started getting my feet wet in this but here is the scoop!

Git is a versioning technology which allows you to create multiple versions of your project much like TimeMachine on your Mac.  The whole point behind it is that at any time you can go back to a specific project mod and retake it from there.

The first step is to GITTIFY your folder.  You can either do this manually from the command line or from Xcode by creating a new project and check marking the Use Git Repository option.  Since you are using Xcode simply check the box for Git in your new project.

Now you create your project files and whatever.  Then move to Xcode->Organizer and click on the REPOSITORY icon which will take you to a window where you will see a project name folder on the left hand side; mine is called eraseme so I’ll use that name to mean ‘project folder’ from now on.

 

XCode Git Repository Santiapps.com by Marcio Valenzuela
XCode Git Repository Santiapps.com

You also have a yellow Branches, a yellow Remotes & blue project folder.

In the yellow Branches folder, create your master branch now by selecting Add Branch or by doing “git push eraseme master” in terminal from within your eraseme folder.

Finally go to the Remotes folder and Add Remote to enter your online repository’s url.

You’re all set!  You can begin sharing your project files now.