Monday 16 August 2010

Key Value Observing and UISliders

Using Key Value Coding can be a huge code saver! Having been looking into this lately, I got rather excited.

Unfortunately UIKit controls aren't actually KVO compliant.  To track changes to their value, you have to handle the Value Changed event.

To get around this, create a new class to derive from UISlider.

I added a property that could be observed. Then in the init method, setup the event which will capture the changes to the value and broadcast them to the property and any one doing their Key Value Observing.

The header is simple :


@interface KeyValueSlider : UISlider {
}

@property int kvValue;
@end



And the implementation :


#import "KeyValueSlider.h"

@implementation KeyValueSlider

-(id) initWithFrame: (CGRect) frame {
    if (self = [super initWithFrame: frame]) {

            [self addTarget: self
                          action: @selector(valueChanged:)          
        forControlEvents: UIControlEventValueChanged];
    }

    return self;
}

- (void)valueChanged: (UISlider *) control {
    self.kvValue = control.value;
}

-(int) kvValue {
    return self.value;
}

-(void) setKvValue : (int) value {
    // Do nothing, this is just for observing.
}

@end

Then just observe the "kvValue" property.

Works a beaut!

Saturday 17 July 2010

Quick and easy UINavtigationController

I want to add a little section to the app I'm writing to allow the user to edit various settings for the app. Now these settings are a bit more complex than can be provided by the iPhones built in settings app, and I want the users to modify the settings in app as opposed to have to go to the separate Settings app.

So I decided to roll my own.

I have never used a UINavigationController control before, but it makes sense to use one for this area of my app as the settings involve navigating through various nested views - exactly what UINavigationController is designed for.

I pull out my iPhone book and read up on UINavigationController and run through a few tutorials on the net, it all seems easy enough!

Then I try to integrate this into my app.

And the problems begin.. All the tutorials assume the Navigation Controller will be managing all your views and are created in the Root View controller xib. I don't want this, I want it to be created by my settings view.

After several hours of messing about, I got bored and decided to just create one in code. Amazingly this was so easy!

Create the View Controller that you want to set off. Create a navigation controller with your View Controller, set a few options and then call presentModalViewController.

MainSettingsController *controller = [[MainSettingsController alloc] initWithNibName:@"MainSettingsController" bundle:nil];
controller.options = self.options;

UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController: controller];
nav.navigationItem.prompt = @"Settings";
controller.title = @"settings";
nav.toolbarHidden = false;
[self presentModalViewController:nav animated:YES];



You're done. No faff at all!

Wednesday 7 July 2010

Reflection

Like all emotional languages, Objective C has the ability to reflect upon itself.

This means given a class you can tell stuff about the class such as its name, what properties it has and so on.

I want to do this as I have some classes in my app and I want the user to be able to customise the options for them. Now I could of course create an XML file that contains the options that the user could change which I could then read into an NSDictionary or something and get my classes to work on them. But this is repetition.

I would much rather the options class could just read the properties of my class and display these to the user.

Reflection is slightly complicated as it needs some fairly low level C code. This is the Objective way!

 In Objective C each object is actually a struct that contains a pointer to an object called Class. This Class object contains all the information about the properties and methods that that object has.

We can access this class by calling

[ourObject class]; 


To access the reflection functions you need to

#import <objc\runtime.h>


Then the following code will get the properties and loop through them, grabbing the name from each property:


        unsigned int outCount;
        objc_property_t *classProperties = class_copyPropertyList([self class], &amp;outCount);       
        for (int i = 0; i &lt; outCount; i++)
        {
            objc_property_t property = classProperties[i];
            const char *propName = property_getName(property);

            

            // Do something with the name here ...!


        }

Sunday 12 July 2009

Reading a raw datafile

To open a file and read its data first you need to get the path to the file.

    NSBundle* bundle = [NSBundle mainBundle];

    NSString* documentsDirectory = [bundle resourcePath];

    

    NSString *path = [documentsDirectory stringByAppendingString:@"/GBPUSD5.csv"];


documentsDirectory will hold the path to the resources folder of our application. I have chucked the GBPUSD5.csv file into there. Then I get the whole path by appending the filename GBPUSD5.csv to it. Note I have had to put the / in as well..

We open the file :


    NSFileHandle *file = [NSFileHandle fileHandleForReadingAtPath:path];


and can jump to any position in the file :

    [file seekToFileOffset:0];


Then we can read the data we need :

        NSData *record = [file readDataOfLength:48];

        NSString* aStr = [[NSString alloc] initWithData:record encoding:NSASCIIStringEncoding];


aStr contains the data as a string that we can get down and boogy with!

Monday 9 February 2009

Delegates

Objects communication is generally thought of as one way. Object A will create Object B. Then Object A can talk to Object B by calling its methods. Delegates are a way the Object B can talk back to Object A. 
This is useful in asynchronous or event driven situations. For example, Object A will tell Object B to go off and do something. Object A will then get on with its life without waiting for B to finish. When Object B is finished, it will call a delegate which will tell Object A that it is finished.

Heres how it works in Objective C.

Object B will need to keep a reference to Object A. In Object B's header wack in the following :

@interface ObjectB : NSObject 

{

    id delegate;

}


- (id)delegate;

- (void)setDelegate:(id)newDelegate;



Then chuck this in the implementation.

- (id)delegate 

{

    return delegate;

}


- (void)setDelegate:(id)newDelegate 

{

    delegate = newDelegate;

}


These are the getter and setter functions that Object A call to say it is interested in hearing from Object B. It would do something like :


    objB.delegate = self;


We need to define the function that will be called when Object B wants to talk to Object A. So back to the header file of Object B. Chuck this at the end :

@interface NSObject (ShareDelegate)

    - (void)finishedLoading:(id)sender;

@end

  
This is defining a function that will take one parameter, an id to Object B. You can have whatever parameters you want... Then when Object B wants to call the function it does this :

    if ( [delegate respondsToSelector:@selector(finishedLoading:)] ) 

    {

        [ delegate finishedLoading:self];

    }


Then all Object A has to do is define a function as normal like :

    - (void)finishedLoading:(id)sender;

    {

        self.label.text = @"done";

    }


Yippeee. Quite simple really...

Sunday 8 February 2009

What's with the square brackets (calling methods)

If you look at some objective C source code, you will see lots of square brackets around. Stuff like :

occultImage.image = [UIImage imageNamed:[NSString stringWithFormat:@"glyph%d.gif", occult]];


seems pretty common.

To a C# programmer that just looks weird... and weird it is. 

But it basically is just another way of calling methods. When you see some square brackets, you know you are calling a method on a particular class or object. Just take it as [object method:parameter].

In the above example we are calling a static method called imageNamed on the UIImage class. The parameter we are passing is the result of another call, stringWithFormat on NSString.

In C#, this would be something like :


occultImage.image = UIImage.imageNamed(NSString.stringWithFormat(@"glyph%d.gif", occult));


What gets more confusing is when you call a method with more than one parameter. The second parameter has to be named. Like so :

NSCalendarDate *startDate = [endDate dateByAddingYears:0 months:0 days:-60 hours:0 minutes:0 seconds:0];


This line will call the function dateByAddingYears on the object endDate (which is another NSCalendarDate) object. Now the first parameter to the function is years. But we dont specify this, we just chuck in the value, which is 0 here. The second parameter is months. We have to specify the name of this parameter. and the same with days, hours, minutes and seconds.

So it is [object method:param1 param2name:param2 param3name:param3] 

Clearer now?

Oh yeah, in object C you aren't calling methods on objects, you are passing messages to them.

hmm...



Saturday 7 February 2009

Connecting up a label

Right to connect a label to a variable that we can access in code perform the following :

Chuck the following variable into your view controller .h file. See how you have to add it in two places, once as the variable within the brackets, and once as a property afterwards. The IBOutlet keyword is what makes this show up in interface builder so we can link it to the variable. Obviously you should call the label something more useful than just label which I've done here...

@interface MainViewController : UIViewController {

UILabel *label;

}


@property (nonatomic, retain) IBOutlet UILabel *label;


Then double click on the view.xib file to go into interface builder. Add the label to the View in interface builder. Then right click on the File's Owner icon and find the label underneath Outlets. To the right of this there is a little circle. If you click on it a + symbol appears. Drag this onto the label in the screen. The two should then be linked up nicely.

To change the text of the label in code just do something like the following :

label.text = @"Groove on people. Let love rule!";


And you're good to groove!