Thursday, February 17, 2011

Objective C: Terminate App when pushing the Home button on iPhone/iPad

To make sure your iPhone or iPad App does not keep running in the background after you used the Home button, this is the way to go. Terminating the app helps saving resources on your device.

1. Open your project in Xcode.


2. Find the Info-iPhone.plist or Info-iPad.plist file.


3. Left-click once on it to see the Key Value pairs.


4. Right-click on one of the entries and select Add Row.


5. As key enter UIApplicationExitsOnSuspend.


6. Right-click on the new row and select Value Type Boolean.


7. Check the value field.


Let me know if you have any trouble.

Wednesday, February 16, 2011

Objective C: alternating background color in UITableViewCell for iPhone Apps

In one of my iPhone Apps that has a UITableView I wanted to make the background color of the UITableViewCells alternating. Having two different colors in the background of the cells help to improve the readability.


As you can see in the screenshot the odd cells are grey and the even cells are white.
The changes need to be done in the DataSource method cellForRowAtIndexPath.

- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
 UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:@"cell"];
 if( nil == cell ) { 
  cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"cell"] autorelease]; 
 }
 Fruits *fruit = [fruits objectAtIndex:indexPath.row]; 
 cell.textLabel.text = fruit.fruitName; 
 
        //start of important code for background color changes
        cell.textLabel.backgroundColor = [UIColor clearColor];
 
 UIView *bgColor = [cell viewWithTag:100];
 if (!bgColor) {
  CGRect frame = CGRectMake(0, 0, 320, 44);
  bgColor = [[UIView alloc] initWithFrame:frame];
  bgColor.tag = 100; //tag id to access the view later
  [cell addSubview:bgColor];
  [cell sendSubviewToBack:bgColor];
  [bgColor release];
 }
 
 if (indexPath.row % 2 == 0){
  bgColor.backgroundColor = [UIColor colorWithRed:233.0/255.0
                green:233.0/255.0
                blue:233.0/255.0
                alpha:1.0];
 } else {
  bgColor.backgroundColor = [UIColor clearColor];
 }
 //end of important code

 return cell;
} 

Above is the whole method with all the additional code you need to get a white/grey background color alternation.

Lets go through the main changes in detail:

Make sure the cell text has a transparent background

cell.textLabel.backgroundColor = [UIColor clearColor]; 
This line makes sure your cell text has an transparent background, otherwise you end up with a result like in the screenshot below.

Create a view that holds the new background color

UIView *bgColor = [cell viewWithTag:100];
if (!bgColor) {
 CGRect frame = CGRectMake(0, 0, 320, 44);
 bgColor = [[UIView alloc] initWithFrame:frame];
 bgColor.tag = 100; //tag id to access the view later
 [cell addSubview:bgColor];
 [cell sendSubviewToBack:bgColor];
 [bgColor release];
}
First we check if the cell has already a view with tag id 100. If not, create a new view with the size of the cell(standard 320x44). Then the view gets add to the cell and send to the background.
If the view exists we don't need to create it again.

Set the color for odd and even cells

if (indexPath.row % 2 == 0){
 bgColor.backgroundColor = [UIColor colorWithRed:233.0/255.0
        green:233.0/255.0
        blue:233.0/255.0
        alpha:1.0];
} else {
 bgColor.backgroundColor = [UIColor clearColor];
}
If the cell index is even (0, 2, 4, etc.) the background color of the background color view is set to grey. Else it is set to clear (white).
If you want it to be the other way around use:
if (indexPath.row % 2 == 0){
...

Hope that helps. Please let me know if you have any problems or questions.

Monday, February 7, 2011

Objective C: Use Network Activity Indicator in iPhone Apps with GTMHTTPFetcher

The Google Toolbox HTTP Fetcher (GTMHTTPFetcher) allows you to send synchronous and asynchronous HTTP request in iPhone/iPad Apps using Objective C/Xcode.

Pretty common in those Apps is to display the network activity indicator during the request process.

The below instructions should also work for other HTTP libraries, you just need to replace the observer names accordingly.


This is how you can do it. The example tutorial below shows how to add this functionality to the RootViewController, but it could be added to any ViewController.

In the RootViewController.h


// RootViewController.h

#import <UIKit/UIKit.h>

@class GTMHTTPFetcher;

@interface RootViewController : UIViewController <UINavigationControllerDelegate> {

 int networkActivityCounter;
        ...
}

- (void)startFetchingData;
- (void)fetchData:(GTMHTTPFetcher *)fetcher finishedWithData:(NSData *)retrievedData error:(NSError *)error;

@end

Important here is the networkActivityCounter that will later hold the number of requests that are currently running.

In the RootViewController.m


// RootViewController.m

- (void)viewDidLoad{
 //add observer for fetcher start and stop events
 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
 [nc addObserver:self selector:@selector(incrementNetworkActivity:) name:kGTMHTTPFetcherStartedNotification object:nil];
 [nc addObserver:self selector:@selector(decrementNetworkActivity:) name:kGTMHTTPFetcherStoppedNotification object:nil];
 
 ...
 
}

First add the observers for the fetch start and stop events in the viewDidLoad method.

#pragma mark Network activity

- (void)incrementNetworkActivity:(NSNotification *)notify {
  ++networkActivityCounter;
  if (1 == networkActivityCounter) {
    UIApplication *app = [UIApplication sharedApplication];
    [app setNetworkActivityIndicatorVisible:YES];
  }
}

- (void)decrementNetworkActivity:(NSNotification *)notify {
  --networkActivityCounter;
  if (0 == networkActivityCounter) {
    UIApplication *app = [UIApplication sharedApplication];
    [app setNetworkActivityIndicatorVisible:NO];
  }
}

The increaseNetworkActivity is called when a fetch requested started, when the counter is at 1 the indicator gets displayed. After when the counter gets increased it doesn't need to be activated anymore, its still running.

Each time a fetch request has stopped the activity gets decreased, at 0 activity the indicator gets invisible.

- (void)dealloc {
 //remove the observer
 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
 [nc removeObserver:self];
 ...
 
 [super dealloc];
}

Don't forget to remove the observer when not needed anymore.

Friday, January 14, 2011

Objective C: Remove certain characters from NSString

Sometimes when you work in Xcode in the Cocoa Framework you want to filter a NSString so that only certain characters are left. That can be numbers or letters or special characters. Following code snippet can do that for you.

//the String with the original text
NSString *unfilteredString = @"The sum of 1 + 2 = 3.";
//initialize a string that will hold the result
NSMutableString *resultString = [NSMutableString stringWithCapacity:unfilteredString.length];
    
NSScanner *scanner = [NSScanner scannerWithString:unfilteredString];
//define the allowed characters, here only numbers from one to three, equal and plus
NSCharacterSet *allowedChars = [NSCharacterSet characterSetWithCharactersInString:@"123+="];
    
while ([scanner isAtEnd] == NO) {
 NSString *buffer;
 if ([scanner scanCharactersFromSet:allowedChars intoString:&buffer]) {
  [resultString appendString:buffer];     
 } else {
  [scanner setScanLocation:([scanner scanLocation] + 1)];
 }
}
//Print out the result String, will be 1+2=3   
NSLog (@"Result: %@", resultString);

Wednesday, January 5, 2011

Turn Eclipse project into Java project

Sometimes you have a project in Eclipse that needs to be changed into a Java Project. Lets call that furthermore "Example".

The description is made with a Dynamic Web Project as the example Java Project. With some easy and obvious changes you could also use this tutorial for a Static Web Project etc.

1. Create a dynamic web project as helper


2 Files will be needed later in the project you want to change into a Java project. We create a dynamic web project to get those two files. That is the .project and the .classpath file.

You can delete this temporary dynamic web project when we are done with everything.

To create the dynamic web project open eclipse and go to the project explorer. Right-click and select New > New Project. In the "Select a wizard" under Web choose "Dynamic Web Project".

Then hit Next and type in the next view the Project name, e.g. "Temp", and hit Finish.

2. Change the .project file of the actual project "Example"


To change the .project file you have to first navigate to the file in a file browser. In Windows you can use the Explorer to go to the project folder, in Mac OS the Finder.

The example project has already a .project file, but the file is invisible. That's why you have to make sure that you can see invisible files in your browser. Here is a good description how to show invisible files in Finder on Mac OS.

In Windows Explorer you click Extras > Folder Options > View and then select something along the lines "display invisible Folders and Files".

When the file shows up for you, you have to open it in a text editor. Then also open the .project file in the "Temp" project folder and copy the content in the "Example" .project file. But make sure that you don't override the name tag. It should still read "Example".

The file look somewhat like that:



 Example
 
 
 
 
  
   org.eclipse.wst.jsdt.core.javascriptValidator
   
   
  
  
   org.eclipse.jdt.core.javabuilder
   
   
  
  
   org.eclipse.wst.common.project.facet.core.builder
   
   
  
  
   org.eclipse.wst.validation.validationbuilder
   
   
  
 
 
  org.eclipse.jem.workbench.JavaEMFNature
  org.eclipse.wst.common.modulecore.ModuleCoreNature
  org.eclipse.wst.common.project.facet.core.nature
  org.eclipse.jdt.core.javanature
  org.eclipse.wst.jsdt.core.jsNature
 


3. Change the .classpath file of the actual project "Example"


The "Example" project has no .classpath file yet. We can copy the .classpath file from the "Temp" project into the project folder.

The .classpath is invisible as well, but with the changes above it should be visible in the file browser as well.

4. Add Project Facets


That the changes from above take effect you first have to completely shut down eclipse. After the restart of eclipse the project should already be a Java project. That the project compile correctly you need to add some required facets.

Therefore right-click in eclipse Project Explorer on the project folder. Then open Properties and go to Project Facets. Here check Dynamic Web Module and Java.

5. Add required libraries (JAR files) to the project


This steps describes how to add the 3rd party libraries that are required in the project. Normally those jar files are stored in WebContent/WEB-INF/lib.

To add them to the classpath, open the project properties like described in step 4. Then select Java Build Path. Under Libraries select "Add JARs..." and navigate to WebContent/WEB-INF/lib and mark all .jar files you need. After hitting OK the libraries are added and you are good to go.

Troubleshooting


If there are still errors in your project it could be because your project needs libraries from the Apache Tomcat. If you go right-click on the project. And then select Run As > Run on Server and go through the whole process the needed Library will be added.

Tuesday, December 7, 2010

Missing Base SDK in existing projects when upgrading to Xcode 3.2.x

This article describes how to solve the problem of Missing Base SDK. This problem happend to me after updating to Xcode 3.2.5. While researching I saw that few people had this problem also with Xcode 3.2.3 and 3.2.4. This fix should work for any of the above versions.

Downpoint for Xcode 3.2.5 is that only IOS 4.2 is supported furthermore. I will soon describe in another article how to set Xcode 3.2.4 and 3.2.5 in parallel up on your system to use older SDKs as well.

To perform the steps described below open the according project in Xcode.

1. Change the SDK of the project


1. From the Xcode menu bar, select Project > Edit Project Settings
2. Select the "Build tab"
3. Select "All Configurations" under "Configurations"
4. Under Architecture > Base SDK, choose depending on what is available the according iOS SDK (for Xcode 3.2.5 iOS 4.2)

2. Change the SDK of the target


1. Make sure the right target is selected. In the menu bar top of the screen, select Project > Active Target > "DesiredTarget"
2. From the menu, select Project > Edit Active Target "DesiredTarget"
3. Select the "Build" tab
4. Select "All Configurations" under "Configurations"
5. Under Architecture > Base SDK, choose depending on what is available the according iOS SDK (for Xcode 3.2.5 iOS 4.2)

3. Support older iOS versions


If you want to support previous iOS versions, when you perform the steps under 1. and 2. in the same windows, under Deployment > iPhone OS Deployment Target, select the lowest version you want to support. Support for iPhone OS 2.x versions is deprecated for apps through the app store.

Friday, December 3, 2010

Xcode Linking Problem: symbol(s) not found

The discussed problem


For my recent iPhone Xcode Cocoa project I setup mogenerator to autamtically get the classes created that match an entity in the xcdatamodel. When I later tried to use those objects in my ViewControllers I couldn't get the application build.

The error said something a long the lines:

Undefined symbols:
   "_OBJC_CLASS_$_Recipes", referenced from:
       objc-class-ref-to-Recipes in RootViewController.o
ld: symbol(s) not found
collect2: ld returned 1 exit status

Whereas "Recipes" stands for one of my entities in my CoreData datamodel.

The fix of the linking error

It took me hours to figure this one out. In XCode if you click on your project root in the Groups & Files View (normally on the left of the XCode window), you can see (on the right hand side of the XCode window) the list of all files in the project. As you can see below in the picture.

Make sure that in the last column of the list for each .m file (especially for the once that caused the error) the box is checked. If the box is checked the file is included in the default target that gets build.

The reasons for linking errors

If you never change anything in the target files and/or related build configuration, all your new classes should be included in the build of the target automatically.

The file that caused my error was generated with the mogenerator. The generator creates a subfolder in your Xcode project and it seems to me like all the files in there are not automatically added to the target.