Append to a NSString

I was working on appending to some strings and there is a way to do it with NSString but you are better off working with NSMutableString instead. I have a feeling doing it with NSString, if not done correctly, will leak memory.

NSMutableString *hello = [[NSMutableString alloc] initWithString:@"Hello"];
[hello appendString:@" World"];

UIImageView in a custom cell

Make sure you have JSON Framework installed first.

This is a continuation of Click a Cell

So there has been some requests to do this. If you want to display a image in a custom cell it is pretty easy. It is about the same as doing the title and the URL to the image we did before. This will take over where click a cell left off. So you can grab the source code from there and start.

So we will be placing a UIImageView on the right part of the cell. To make it easy, we will still keep the title and the URL in the cell. So the first thing you want to do is open up ImageCell.h and we need to add the references to the new UIImageView we will use. I have commented the two lines I have added in this file

@interface ImageCell : UITableViewCell {
	UILabel *titleLabel;
	UILabel *urlLabel;
	NSInteger *itemID;
	UIImageView *imageView; // added this
}

// these are the functions we will create in the .m file

-(void)setData:(NSDictionary *)dict;
-(NSInteger)getID;

-(UILabel *)newLabelWithPrimaryColor:(UIColor *)primaryColor selectedColor:(UIColor *)selectedColor fontSize:(CGFloat)fontSize bold:(BOOL)bold;

@property (nonatomic, retain) UILabel *titleLabel;
@property (nonatomic, retain) UILabel *urlLabel;
@property (nonatomic, assign) NSInteger *itemID;
@property (nonatomic, retain) UIImageView *imageView;  // added this

@end

Now we want to create a temporary image. When doing a webservice application it is key to “die” gracefully. For example if the service is alive put the image is not there, we want to show a no picture image so something is displayed. So I am using the following image (nopic-small.png).

Save this image and then drag it into your project so Xcode knows you are using this image.

Ok now lets init the UIImageView, so open up ImageCell.m and we need to synethsize our new image object.

@synthesize imageView;

Now look for the initWithFrame method. You want to add in the following

UIImage *noPicImageSmall = [[UIImage imageNamed:@"nopic-small.png"] retain];

self.imageView = [[UIImageView alloc] initWithImage:noPicImageSmall];
[myContentView addSubview:self.imageView];
[self.imageView release];

So the above code we are creating a new UIImage that contains the our no-pic image. Then we init our image view with the contents of our no-pic UIImage. We then add it to the sub-view and release the image.

So if we click build and go right now we should get something that looks like this

You will see the no-pic images are placed in the upper left corner of the cell and way too big, so lets draw them in the correct place and the correct size.

So no look for the layoutSubviews method and add in the following right below where we drew the titleLabel and urlLabel.

self.imageView.frame = CGRectMake(boundsX + 200, 5, 30, 30);

The boundsX + 200 is the spacing on the left
The 5 is the spacing from the top
The first 30 is the width of the view
The second 30 is the height of the view

You will notice in the line above that I switched from 2 lines of code to 1. This is actually better coding practices then above if you just need the CGRectMake once.

So if you click build and go now you will get something that looks like this

Now we need to populate that UIImageView with our image in the URL for the cell. We will do that in the setData method. So that should now look like

-(void)setData:(NSDictionary *)dict {
	self.titleLabel.text = [dict objectForKey:@"title"];
	self.urlLabel.text = [dict objectForKey:@"img"];
	self.itemID = (NSInteger)[dict objectForKey:@"id"];

	// setting up the imageView now
	self.imageView.image = [UIImage imageWithData: [NSData dataWithContentsOfURL: [NSURL URLWithString: [dict objectForKey:@"img"]]]];
}

So you can see we added in the self.imageView.image = line. We are setting it to a new UIImage and loading it with data from the contents of a URL. You have to pass in a NSURL object to that, I don’t think it will accept a string. Pretty much you turn the string into a URL object and tell UIImage to grab that.

Don’t forget to dealloc the image view

- (void)dealloc {
	// make sure you free the memory
	[titleLabel dealloc];
	[urlLabel dealloc];
	[imageView dealloc];
	[super dealloc];
}

Now when you click build and go, you will see the following

You can grab the source here.

Click a Cell

Make sure you have JSON Framework installed first.

This is a continuation of JSON Error

So here is a quick tutorial on how to click on a cell and load some JSON data for that cell. So the idea is you have a unique ID for each set of data you have.

So for that I am going to change the php code to add a ID

<php
header('Content-type: application/x-json');

$id = (int)$_GET['id'];

$array = array(
        1 => array(
                'id'  => 1,  // added this
                'title' => 'Brown Dog',
                'img'   => 'http://iphone.zcentric.com/files/1.jpg',
        ),
        array(
                'id'    => 2,  // added this
                'title' => 'Black Dog',
                'img'   => 'http://iphone.zcentric.com/files/2.jpg',
        ),
        array(
                'id'    => 3,  // added this
                'title' => 'Two Dogs',
                'img'   => 'http://iphone.zcentric.com/files/3.jpg',
        ),
        array(
                'id'    => 4,  // added this
                'title' => 'Girl',
                'img'   => 'http://iphone.zcentric.com/files/4.jpg',
        ),
);

echo json_encode($array);
?>

So now lets go back to the code. In ImageCell.h we want to add a new id for the cell that we will set in our setData function and also we want to create a getID function so we know what ID we clicked on.

So here is what the .h file should look something like now

#import 

@interface ImageCell : UITableViewCell {
	UILabel *titleLabel;
	UILabel *urlLabel;
	NSInteger *itemID; // added this
}

// these are the functions we will create in the .m file

-(void)setData:(NSDictionary *)dict;
-(NSInteger)getID; // added this

-(UILabel *)newLabelWithPrimaryColor:(UIColor *)primaryColor selectedColor:(UIColor *)selectedColor fontSize:(CGFloat)fontSize bold:(BOOL)bold;

@property (nonatomic, retain) UILabel *titleLabel;
@property (nonatomic, retain) UILabel *urlLabel;
@property (nonatomic, assign) NSInteger *itemID; // added this

@end

Now open up the ImageCell.m and add the following line into the initWithFrame function

self.itemID = 0;

Now you have to synthesize that itemID

@synthesize itemID;

Now scroll down to the setData function and we need to set up the itemID with what we pass in from RootViewController. So it looks like this

-(void)setData:(NSDictionary *)dict {
	self.titleLabel.text = [dict objectForKey:@"title"];
	self.urlLabel.text = [dict objectForKey:@"img"];
	self.itemID = (int)[dict objectForKey:@"id"];
}

The reason we have to do the (int) in front of it is how objectForKey returns the data. We need to cast it into an int since we declared it as one.

Now lets create our getID function so add in the following

-(NSInteger)getID {
	return self.itemID;
}

So since we set the itemID to 0 in the init later in the code we can always do some error checking. So if it is 0 there must be a problem, else grab the data.

Now save the file and we need to create a new view. Follow the following entry for how to do that in IB. Just follow the IB sets, you can ignore the code, we will fill that in later. Also don’t forget to follow the step for creating a new ViewController!. Call the new XIB from IB JSONView and the new controller JSONViewController.m.

Now we need to create some outlets in IB.

So click on the File’s Owner icon and you can to click on the Controller Identity Tab in the Inspector. (Tools -> Identity Inspector)

Click the + icon for class Outlets and you want it to look like the following. For my example I dragged a UILabel and a UIImageView onto the main view so we need to tell the application that these will be usable variables in the code.

Now that we have the items on the view and the outlets set, we have to tell IB what the outlets will attach to. So open the Connections Inspector tab (Tools -> Connection Inspector) and you should see the new outlets there

They are jsonImage and jsonLabel. So now all you are doing is clicking on the circle to the right and dragging over to the UIImageView and releasing and that will setup the connection between the outlet and the item you placed. Do the same for the UILabel.

Now everything in IB is all setup, so lets get back to some code.

So we should already have the JSONViewController.h/.m file in place since we created the new view, so lets make it work. So open up JSONViewController.h and you want it to look like

#import 

@interface JSONViewController : UIViewController {
	IBOutlet UILabel *jsonLabel;
	IBOutlet UIImageView *jsonImage;
	NSDictionary *jsonItem;
	NSInteger *itemID;
}

-(void)setID:(NSInteger)val;

@property (nonatomic, retain) UILabel *jsonLabel;
@property (nonatomic, retain) UIImageView *jsonImage;
@property (nonatomic, retain) NSDictionary *jsonItem;
@property (nonatomic, assign) NSInteger *itemID;

@end

So you can see one new thing. The IBOutlet in front of the UIImage and UIImageView. These is basically there to tell IB that these are outlets. So everything else is nothing new. The one thing you will see is the method setID, we will use that to get in the ID of the clicked on cell. We need to know the ID of the data set we want to grab right?

Here is what my JSONViewController.m looks like

#import "JSONViewController.h"
#import <JSON/JSON.h>

@implementation JSONViewController

@synthesize jsonLabel, jsonImage, jsonItem, itemID;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
	if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
		// Initialization code

		self.itemID = 0;
	}
	return self;
}

-(void)setID:(NSInteger)val {
	self.itemID = (NSInteger *)val;
}

/*
 Implement loadView if you want to create a view hierarchy programmatically
- (void)loadView {
}
 */

- (void)viewDidLoad {
	// init the url

	NSURL *jsonURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://iphone.zcentric.com/test-json-get.php?id=%@", self.itemID, nil]];

	NSString *jsonData = [[NSString alloc] initWithContentsOfURL:jsonURL];

	if (jsonData == nil) {
		UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Webservice Down" message:@"The webservice you are accessing is down. Please try again later."  delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
		[alert show];
		[alert release];
	}
	else {
		self.jsonItem = [jsonData JSONValue]; 

		// setting up the title
		self.jsonLabel.text = [self.jsonItem objectForKey:@"title"];

		// setting up the image now
		self.jsonImage.image = [UIImage imageWithData: [NSData dataWithContentsOfURL: [NSURL URLWithString: [self.jsonItem objectForKey:@"img"]]]];
	}
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
	// Return YES for supported orientations
	return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

- (void)didReceiveMemoryWarning {
	[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
	// Release anything that's not essential, such as cached data
}

- (void)dealloc {
	[jsonLabel dealloc];
	[jsonImage dealloc];
	[self.jsonItem dealloc];
	[super dealloc];
}

@end

Now you will notice a few things

  • I do not init the jsonLabel and jsonImage anywhere. IB does this all for me
  • You can see I uncommented the viewDidLoad method and did the JSON call in there. There are a few reasons for that. First we want to make sure the class is fully inited before making the call. We do this since we want to pass in the ID of the data set.
  • You can see how I am setting up the title and the imageview in the code.

Now save that file and open up RootViewController.m

We want to make the didSelectRowAtIndexPath method to look like

 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

	 // load the clicked cell
	 ImageCell *cell = (ImageCell *)[tableView cellForRowAtIndexPath:indexPath];

	 // init the controller
	 JSONViewController *controller = [[JSONViewController alloc] initWithNibName:@"JSONView" bundle:nil];

	 // set the ID and call JSON in the controller
	 [controller setID:[cell getID]];

	 // show the view
	 [self.navigationController pushViewController:controller animated:YES];

}

The one odd thing is we are calling cellForRowAtIndexPath again when we click. This is because the cells are cached and we want to return that cell again if it already created.

That is all there is to it. If it works the view should look something like this if you click the same cell I did

As always you can grab the code here.

Using NSUserDefaults

This will go over a quick run through of using NSUserDefaults. This is the class to use to save user preferences for example. It is pretty straight forward.

Saving Data

This is how you save data.

NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
// saving a string
[prefs setObject:@"test" forKey:@"stringVal"];
// saving a int
[prefs setObject::23 forKey:@"intVal"];
// saving it all
[prefs synchronize];

Loading Data

NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
// getting the string
stringVal = [prefs stringForKey:@"stringVal"];
// getting the int
intVal = [prefs integerForKey:@"intVal"];

That is it.. pretty simple.

Create a text input

So here is a quick no real code tutorial. This is another thing that took me a bit to figure out.

For this example, I am putting a text input in a TableView. So here is my class declaration.

@interface OptionsViewController : UITableViewController <UITextFieldDelegate> { }

You need to put the UITextFieldDelegate as a reference class. Now I am going to create the text input in the .m file of the same class

// creating the input
self.usernameInput = [[UITextField alloc] initWithFrame:frame];
// setting it to a rounded border
self.usernameInput.borderStyle = UITextBorderStyleRoundedRect;
// text color is black
self.usernameInput.textColor = [UIColor blackColor];
// font size
self.usernameInput.font = [UIFont systemFontOfSize:17.0];
// the default text in light gray
self.usernameInput.placeholder = @"Username";
// background color of the box
self.usernameInput.backgroundColor = [UIColor whiteColor];
// sets up auto correction in case of spelling errors
self.usernameInput.autocorrectionType = UITextAutocorrectionTypeYes;
// shows the done key in the keyboard
self.usernameInput.returnKeyType = UIReturnKeyDone;
// shows the X button in the text input if a user wants to clear it quickly
self.usernameInput.clearButtonMode = UITextFieldViewModeWhileEditing;
// sets the delegate to the controller when we press the done so the controller knows to take control
self.usernameInput.delegate = self;
// adding it to my view
[MyContentView addSubview:self.usernameInput];
// releasing the memory
[self.usernameInput release];

That is about it!

← Previous PageNext Page →