Showing posts with label cocoa. Show all posts
Showing posts with label cocoa. Show all posts

Sunday, January 24, 2010

Cocoa and Delegates

Delegates are probably one of the toughest concepts for iPhone/OSX developer newcomers to grasp. I'm going to try to keep it simple, and demonstrate how to build your own delegate protocol into your classes.

A delegate is merely a design pattern in Objective-C. Simply put, it provides a way for objects to "listen" for interesting things happening in your object and act on them without your object knowing anything about the object(s) listening. Delegates are used heavily in the Cocoa environment. Most often you will see delegates used to trigger methods on a UIViewController, although they can be implemented on anything.

Let's think for a moment what type of problem this solves. Let's say we have a UIViewController which has a UITableView in its view. When a table row is tapped, we want to slide a new view onto the screen. We don't want UITableView handling this task, so how do we accomplish this?

The UIViewController needs to handle the new view (thus it's name.) So the UITableView needs to somehow inform the UIViewController that a row was tapped so it can take action. Therefore, the UIViewController needs to be the delegate of the UITableView. So we need to setup two things: We need to inform the UITableView what it's delegate object is, and we need to inform the delegate object what delegate protocol(s) it conforms to.

We begin by setting the delegate property of the UITableView to the UIViewController object. If you are using Interface Builder, you can simply connect the delegate property of the UITableView to the UIViewController (typically the File's Owner.) Otherwise you might handle this in the viewDidLoad{} of the UIViewController, something like:

self.tableView.delegate = self;


You will also want to tell the view controller that it understands the UITableViewDelegate protocol, so you would put this in the UIViewController .h interface declaration:

@interface MyViewController : UIViewController
<UITableViewDelegate> {}
(Note: if your view controller is an extension of UITableViewController, you will not need to declare the UITableViewDelegate protocol.)

And you are set! Now when you implement a method of the UITableViewDelegate in the UIViewController, this method will trigger when something happens in the table view for that delegate method. So for instance, you put this in your UIViewController .m file:


- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// push the new view onto the stack here
}
When a row is selected in the table, this method will get triggered.

So now begs the question, how do you add your own delegate protocol to your custom classes? Here is an example. Let's say you want to be able to trigger methods of other classes when interesting things happen in your class. The first thing you need to do is define what delegate methods are available to the delegate object. You would first put something like this at the top of your class .h file, above the implementation declaration:


@protocol MyCustomClassDelegate <NSObject>;
- (void)didClickDone:(UIButton *)button;
@optional
-(void)didClickCancel:(UIButton *)button;
@end


Where MyCustomClassDelegate is the name of your class delegate protocol (your class name appended with "Delegate"), and didClickDone and didClickCancel are the names of your two delegate methods. You can add as many methods as you wish. You not need to implement these methods in your class, that is up to the delegate to do. Notice the line with @optional. This means that the following methods are optional for the delegate to implement.

Now we need to setup the delegate property:


@interface MyCustomClass : NSObject {
id <MyCustomClassDelegate> delegate;
}

@property (nonatomic, assign)
id <MyCustomClassDelegate> delegate;

And finally in the .m file, synthesize the delegate:


@synthesize delegate;


Note we do not need to release the delegate in the dealloc method, as this is not a retained object, it is just an assigned id.

Ok, for the final part of your class, you need to fire off the delegate method when something interesting happens. For instance, someone clicks the done button. In your IBAction for the done button, you do just that:


-(IBAction)done:(UIButton *)button {
[self.delegate didClickDone:button];
}


And so on, for each delegate method. Ok, that is it for the class. Now, for the delegate class. To be the delegate of MyCustomClass, you need to declare that you implement the MyCustomClassDelegate protocol (.h file):


@interface MyViewController:
UIViewController <MyCustomClassDelegate> { }


And then, we implement the delegate methods (.m file):


-(void)didClickDone:(UIButton *)button {
// do something here!
}


One last thing to note, a class can be the delegate for several objects, just comma-separate the delegates:


@interface MyViewController:
UIViewController <MyCustomClassDelegate,SomeOtherDelegate> { }



I hope that helps clear up delegates a bit! Please leave your comments below.

Thursday, May 14, 2009

Camera image orientation

I was having a confusing problem where images taken from the iPhone camera were being displayed in the wrong orientation. I would take a photo in portrait and save the data to disk. Later when I retrieved the photo it would show up rotated 90 degrees.

The iPhone knows what orientation a photo is taken, and this orientation depends on how the camera is being held when you snap the photo. You can get the orientation value on a UIImage from the property imageOrientation.

When you save the photo, it is possible that this information could get lost, depending on the way you save it. If it does get lost, the UIImageView will assume the photo was taken as a left-side landscape photo, and display it with that orientation.

I ran into a problem when trying to save the camera image with UIImagePNGRepresentation(). For some reason this loses the orientation info. I switched to UIImageJPEGRepresentation() and all is well now.

So basically if you run into the problem, you have 2 choices: Be sure you use a format that preserves orientation data, or rotate the UIImage data directly before saving it to a file.

Wednesday, May 13, 2009

Rotating view around arbitrary point the easy way

I have a UIImageView that I want to rotate around a given point. By default, the layer will rotate around the center of the UIImageView. Trying to change this anchor point involves a bit of work, either move at the beginning and reposition all the view elements, or translating the view, rotating, and translating back. The last option works fine, unless you are trying to use core animation to smoothly rotate the view.

I found out a much easier solution to the whole problem. You could call it a workaround, but it works well without disturbing the view you want rotated.

Basically, you create a new UIView. Place the center point where you want the rotation to occur. Change the size of the view so that the view you want rotated will fit inside. Make your view (you want rotated) a subview of the new view. Now, just rotate the new view, and your subview will be rotated correctly.

Example: lets say we have a UIImageView that is 320x480 (the size of the whole iPhone screen). We want it to rotate from the bottom center instead of its middle. Create a new UIView that is 0,0,320,960. This is basically double the height of the screen, positioned so the bottom half is off the screen, which places the center point right where we want it. Now place your UIImageView inside the new UIView in the top half (visible on the screen.) Now when you rotate the UIView, it will pivot on the bottom center of the screen, and your UIImageView will rotate along with it, as expected.

Sunday, March 29, 2009

iPhone 2.2.1 available fonts

Here is a list of the fonts available to the iPhone as of SDK 2.2.1:


Family: Courier
Font: Courier
Font: Courier-BoldOblique
Font: Courier-Oblique
Font: Courier-Bold
Family: AppleGothic
Font: AppleGothic
Family: Arial
Font: ArialMT
Font: Arial-BoldMT
Font: Arial-BoldItalicMT
Font: Arial-ItalicMT
Family: STHeiti TC
Font: STHeitiTC-Light
Font: STHeitiTC-Medium
Family: Hiragino Kaku Gothic ProN
Font: HiraKakuProN-W6
Font: HiraKakuProN-W3
Family: Courier New
Font: CourierNewPS-BoldMT
Font: CourierNewPS-ItalicMT
Font: CourierNewPS-BoldItalicMT
Font: CourierNewPSMT
Family: Zapfino
Font: Zapfino
Family: Arial Unicode MS
Font: ArialUnicodeMS
Family: STHeiti SC
Font: STHeitiSC-Medium
Font: STHeitiSC-Light
Family: American Typewriter
Font: AmericanTypewriter
Font: AmericanTypewriter-Bold
Family: Helvetica
Font: Helvetica-Oblique
Font: Helvetica-BoldOblique
Font: Helvetica
Font: Helvetica-Bold
Family: Marker Felt
Font: MarkerFelt-Thin
Family: Helvetica Neue
Font: HelveticaNeue
Font: HelveticaNeue-Bold
Family: DB LCD Temp
Font: DBLCDTempBlack
Family: Verdana
Font: Verdana-Bold
Font: Verdana-BoldItalic
Font: Verdana
Font: Verdana-Italic
Family: Times New Roman
Font: TimesNewRomanPSMT
Font: TimesNewRomanPS-BoldMT
Font: TimesNewRomanPS-BoldItalicMT
Font: TimesNewRomanPS-ItalicMT
Family: Georgia
Font: Georgia-Bold
Font: Georgia
Font: Georgia-BoldItalic
Font: Georgia-Italic
Family: STHeiti J
Font: STHeitiJ-Medium
Font: STHeitiJ-Light
Family: Arial Rounded MT Bold
Font: ArialRoundedMTBold
Family: Trebuchet MS
Font: TrebuchetMS-Italic
Font: TrebuchetMS
Font: Trebuchet-BoldItalic
Font: TrebuchetMS-Bold
Family: STHeiti K
Font: STHeitiK-Medium
Font: STHeitiK-Light

Wednesday, March 25, 2009

Perl Compatible Regular Expressions with Cocoa

If you want Perl Compatible Regular Expressions with Cocoa, Christopher Bess has created ObjPCRE, a library that makes PCRE easy in Cocoa.

However, there isn't much for documentation, so I thought I'd at least show how to get started. Implementation is pretty straight forward. First, you need to add the following files to your XCode project:

libpcre.a
pcre.h
objpcre.h
objpcre.m

You can find the libpcre.a file in the pcre static lib download, and the other three files are in the source file download. I created a new "PCRE" group folder in my XCode project and dropped them all in there.

Now, its just a matter of using it. So first, lets create a one-liner that search/replaces text in a string. We'll search for "string" and replace it with "foobar".

#import "objpcre.h"

NSString *myText = @"This is my string of text.";
NSLog(@"text before: %@", myText);
[[ObjPCRE regexWithPattern:@"string"] replaceAll:&myText replacement:@"foobar"];
NSLog(@"text after: %@", myText);


There you go, your first one-line to search/replace a string of text inline with perl regular expressions. Now this isn't very interesting, as no regular expressions were used. So now, let's try something useful. How about a regular expression that removes all HTML tags from the string. Lets try to think of a regex that will match every HTML tag:

<.*>

Ok that one is pretty basic. It says match <, followed by zero or more of ANY character, followed by >. This could cause a problem because it can match too much, such as multiple html tags along with any text between them. So we'll go with something a bit more restrictive:

<\w+[^>]*>

Now we will only match <, followed by one or more word characters (letter, number, underscore), followed by zero or more characters that are NOT >, followed by >.


We still have a problem though, this will not match closing HTML tags.

</?\w+[^>]*>

There, now we match tags with 0 or 1 "/" after the opening tag.

Notice that backslashes must be escaped inside @"double quotes", so we use two of them in the string.

#import "objpcre.h"

NSString *myText = @"<title>This is my <b>string</b> of <class name="foo">text</class>.</title>";
NSLog(@"text before: %@", myText);
[[ObjPCRE regexWithPattern:@"</?\\w+[^>]*>"] replaceAll:&myText replacement:@""];
NSLog(@"text after: %@", myText);


And now for something a bit trickier. Let's try extracting all words within [brackets] in the text. This is where ObjPCRE could use some more features! But for now, here is how we accomplish this task. First the regular expression that matches the tags:

\[\w+\]

The brackets have special meaning to PCRE, so we have to escape them. This matches [, followed by one or more word characters, followed by ]. But, lets say we want to capture just the text, without the brackets. We put parenthesis around each subpattern we want to capture. These will have no affect on the regex.

\[(\w+)\]

And now we put this into code. Remember to escape backslashes.

NSString *myText = @"This is [some] more [text] to parse.";

ObjPCRE *pcre = [ObjPCRE regexWithPattern:@"\\[(\\w+)\\]"];

int start = 0;
int len = 0;
int offset = 0;
int i = 0;
while([pcre regexMatches:myText options:0 startOffset:offset]) {
for(i=0; i<[pcre matchCount]; i++) {
NSLog(@"match %d: %@",i,[pcre match:myText atMatchIndex:i]);
}
[pcre match:&start length:&len atMatchIndex:0];
offset = start + len;
}


We call regexMatches for each [bracket] pattern it finds. For each of those we loop over the subpatterns and echo them. The first subpattern is the entire match, followed by each parenthesized subpattern (which we have only one.)

Alright, so that's a start! To continue, check all the functions available in objpcre.h, and also see the documentation on PCRE for all the good regex stuff.

Tuesday, March 24, 2009

Reset a UIScrollView

This has to be the most puzzling thing I've come across in iphone app building thus far. I wanted to "reset" a UIScrollView after pinch zooming and scrolling. It turns out, there is no way to reset the zoom factor on the contentView in a UIScrollView. At least not in the current SDK. You must completely replace the subview of the scroll view to work around it.

I wanted this transition to happen smoothly, so I used some core animation. Here is some working code to get the scroll view to "reset". In my app, I implemented this after a double tap, and after an orientation change.

self.scrollView is the UIScrollView, and self.imageView is the UIImageView subview.


- (void) resetImageZoom {

// animate the transition
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(resetAnimFinish:finished:context:)];
[UIView setAnimationDuration:0.4];

// reset the scrollview and the current image view
self.scrollView.transform = CGAffineTransformIdentity;
self.scrollView.contentOffset = CGPointZero;
self.imageView.frame = self.scrollView.frame;
self.imageView.center = self.scrollView.center;
self.scrollView.contentSize = self.imageView.frame.size;

[UIView commitAnimations];

}

-(void)resetAnimFinish:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {

// make a copy of the image view
UIImageView *copy = [[UIImageView alloc] initWithImage:self.imageView.image];
copy.autoresizingMask = self.imageView.autoresizingMask;
copy.contentMode = self.imageView.contentMode;
copy.frame = self.imageView.frame;
copy.center = self.imageView.center;

// replace the current image view with our copy
[self.imageView removeFromSuperview];
self.imageView = copy;
[self.scrollView addSubview:copy];
[copy release];
}

If you wanted, you could extend UIImageView an implement the NSCopying protocol, then just use the copy convenience method. I chose to copy the object a bit more manually here.

Sunday, March 22, 2009

stripping HTML with objective-c/cocoa

I was looking for a simple way to strip HTML from an NSString. Since NSString has no native regular expression support, I had to resort to other means. I found many posts requiring regex support libs and/or libxml2 support. Bleh. Then I found this simple solution using NSScanner. It worked well for me.

It's not super smart though. I'm guessing it will bork on any stray < or > tags in the text that are not part of HTML markup. Make sure they are escaped.

http://www.rudis.net/content/2009/01/21/flatten-html-content-ie-strip-tags-cocoaobjective-c

I made my own small addition, optionally trimming whitespace too.


- (NSString *)flattenHTML:(NSString *)html trimWhiteSpace:(BOOL)trim {

NSScanner *theScanner;
NSString *text = nil;

theScanner = [NSScanner scannerWithString:html];

while ([theScanner isAtEnd] == NO) {

// find start of tag
[theScanner scanUpToString:@"<" intoString:NULL] ;
// find end of tag
[theScanner scanUpToString:@">" intoString:&text] ;

// replace the found tag with a space
//(you can filter multi-spaces out later if you wish)
html = [html stringByReplacingOccurrencesOfString:
[ NSString stringWithFormat:@"%@>", text]
withString:@" "];

} // while //

// trim off whitespace
return trim ? [html stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] : html;

}

Tuesday, January 27, 2009

Renaming an XCode Project from Command Line

UPDATE: As of Xcode 3.2 you can rename projects from the Projects->Rename dropdown.

Earlier I had posted how to manually rename an XCode project. I have now written a BASH shell script to handle everything automatically. You can find it here.

Wednesday, December 17, 2008

Renaming an XCode project

UPDATE: As of Xcode 3.2 you can rename projects from the Projects->Rename dropdown.

UPDATE: Be sure to see my post about the shell script I wrote to rename from the command line.


For those wanting to rename their XCode project, here how you do it manually:

1) copy your project to a new folder, rename the folder to your new project name, delete the build directory in the new folder.

2) drag the entire new project folder onto the TextMate text editor

3) In TextMate, remove (delete references) to anything non-text such as images and sounds. [UPDATE: TextMate will ignore the non-text files, so step 3 is unnecessary.]

4) Replace in Project (old project name) with (new project name), save all files. Close TextMate.

5) Now drag the (project name).xcodeproj file into Text Mate, repeat Replace in Project processm, save all files. Close Text Mate. If you have spaces in your project name, look for all instances of your project with spaces replaced with underscores too.

6) Rename all files containing (old project name) to (new project name). Be sure to check the Classes directory too. If your project name contains spaces, look for all instances of filenames with spaces replaced with underscores too.

That's it! Your new project should be good to go. I don't think cleaning of targets is necessary, since the build directory is removed (correct me if I'm wrong though!)

Tuesday, October 28, 2008

Getting URL from UIWebView

It thought I'd post how to get the current URL string from a UIWebView object. Here you go:


NSString *currentURL
= myWebView.request.URL.absoluteString;


That is assuming myWebView is your UIWebView object.

To open a web page in Safari from a UIWebView, do this:


[[UIApplication sharedApplication]
openURL:myWebView.request.URL];


Thursday, October 16, 2008

Objective-C Crash Course for PHP developers

I've been dabbling around in Objective-C lately, and I thought I'd share some of my experiences with it. It took some time to get my head around a lot of the basic programming syntax. I thought I'd share some things I've learned, and how it compares to PHP.

First and foremost, Objective-C is a compiled language, whereas PHP is a scripted language. Objective-C requires compiling into a binary before it is executed. There is no need to compile PHP, as the script is interpreted at run-time.

PHP was once strictly a procedural language, but over time many object-oriented language features have been added. Objective-C is an Object-Oriented programming language structure that is built on top of C, which is a procedural language. You can freely mix C with Objective-C (as well as C++), much the same that you can mix PHP procedural functions with PHP objects and classes.

Objective-C is foremost an Object-Oriented language. That means, everything is an object. Even a simple string is an object of NSString. And yes, you can mix C code and use plain procedural functions if you wish. Normally you can do everything you need with objects.

First, a "Hello World" application in PHP, then the same one in Objective-C.

The PHP version:


// HelloWorld.class.php
class HelloWorld
{
// instance variables go here

// methods go here
public function printHelloDate()
{
printf("Hello World! at %s",
strftime('%Y-%m-%d %H:%M:S %Z'));
}
}



// hello.php
include_once('HelloWorld.class.php');
$hw = new HelloWorld;
$hw->printHelloDate;


The Objective-C version:

HelloWorld.h header file:


#import <Foundation/Foundation.h>

@interface HelloWorld : NSObject {
// instance vars go here
}

// class methods go here
- (void)printHelloDate;

@end


HelloWorld.m implementation file:


#import "HelloWorld.h"

@implementation HelloWorld

- (void)printHelloDate
{
NSLog(@"Hello world! at %@",
[NSCalendarDate calendarDate]);
}

@end


main.m file:


#import <Foundation/Foundation.h>
#import "HelloWorld.h"

int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool =
[[NSAutoreleasePool alloc] init];

HelloWorld *hw = [[HelloWorld alloc] init];
[hw autorelease];

[hw printHelloDate];

[pool release];
return 0;
}


If you have had any experience programming in C, much of this will look familiar. Objective-C is after all, just an extension of the C programming language, adding in all the OO goodness of Objective-C to the already existing C syntax. I'm going to refer to Objective-C as OC from here on out.

Notice that comments are identical in PHP, OC, and C. // for single line comments, and /* */ for multi-line comments. Easy enough. Now, lets take a look at the OC code, starting with the header file:


#import <Foundation/Foundation.h>


#import is much the same as include_once() in PHP. If a file gets imported more than once, it is ignored.

OC is made up of header files (.h) and source files (.m) Typically, each header file is paired with a source file. The header file declares the instance variables and methods of your classes, whereas the methods are implemented in the source (.m) file. You need only import the .h file, as the .m file is compiled by XCode automatically.

Foundation.h is an existing OC framework of classes. It includes much of the fundamental objects you will want when developing Objective-C. There are many other frameworks to choose from too. The core of PHP is procedural code, so there is no "foundation" to include. All of the basic PHP functionality is available at the outset of any PHP script (excluding special extensions and libraries of your own.)


@interface HelloWorld : NSObject {
// instance vars go here
}


The @interface line describes the name of your class, and also describes what object it is inherited from. Here we have a class named "HelloWorld" and it inherits NSObject, which is the most basic of objects. It is the top object of the inheritance tree. In PHP, there is no need to declare what object an non-extended class is inherited from.

In the curly braces of the @interface line, you declare what instance variables your class will contain. In our example, we have none declared.


// class methods go here
- (void)printHelloDate;

@end


After the curly brace and before the @end symbol, you declare your class methods, and the arguments they may take. The minus "-" at the beginning of the line declares that this is an instance method. If it began with a "+" it would be a class method. In PHP the "+" methods would be referred to as static methods. That is, a method called directly from the class, not from an object instance.

(void) is the value that is returned from the method. This method does not return a value.

@end defines the end of the method declarations.

And now the implementation (.m) file:


#import "HelloWorld.h"


We import our HelloWorld.h header file.


@implementation HelloWorld


Between the @implementation and @end lines, we implement the methods of our class.


- (void)printHelloDate


This is an instance method, denoted by the "-" at the beginning. A "+" would have denoted a class method, or "static" method in PHP jargon. This method does not return anything, so (void) is used as the return declaration.


{
NSLog(@"Hello world! at %@",
[NSCalendarDate calendarDate]);
}


This is the body of our method. NSLog is the OC equivalent to the PHP's and C's printf function. %@ is a placeholder for our string object. NSCalendarDate is a class from the Foundation.h framework, and we are calling the calendarDate method of the class. It will return a string object containing a date for our NSLog.


@end


This marks the end of our implementation methods.

And now for the main.m file:


#import <Foundation/Foundation.h>
#import "HelloWorld.h"


Here we import our Foundation.h file. (Note we could have left this out, since the HelloWorld.h file imports it already.) Then we import the header file of our HelloWorld class.


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


As in C, every OC program must have a main() function. This is where the compiled binary will begin its execution, passing in any arguments given from the command line. In PHP, arguments to a script are captured in the $argv variable.


NSAutoreleasePool * pool =
[[NSAutoreleasePool alloc] init];



This is part of the memory management of OC. Every object in OC retains a count of how many references are attached to it. When an object is first instantiated, a reference count of 1 is placed on the object. Each time the object is retained, its reference count is incremented. Each time it is released, it is decremented. Once the ref count reaches zero, the object is destroyed. The autorelease pool is an OC convention used for memory management. When you autorelease an object (as opposed to just releasing it), it gets added to the pool of objects to be released at a later time. This way if something else needs to pick up your object before it is destroyed, it can, and you don't have to remember to release it again. OC 2.0 also has garbage collection features that help automate the tedious task of memory management. In PHP, memory management is automagically handled for you.


HelloWorld *hw = [[HelloWorld alloc] init];


This is where your object is instantiated. [HelloWorld alloc] means we are calling the static method "alloc" of the HelloWorld class (a special method available to all OC classes.) alloc does the memory allocation for your object. Then, the init method is called of the instantiated object. This is much like the __construct() method of PHP objects, where initialization is handled, as well as initialization of any superclasses. The object is then given a pointer (of type HelloWorld) named *hw. Now we reference our object with hw from here onward. Note that in OC all objects require pointers to reference them.


[hw autorelease];


Here we hand our object to the autorelease pool. Now I don't have to remember to release it later, and it's still available for me to use. The autorelease pool is kind of pointless for our HelloWorld app, but since the autorelease pool is an integral part of OC programming, I left it in. (I could have just did [hw release] after I was done with it.)



[hw printHelloDate];


This is where we call the printHelloDate method of our class. In OC jargon, they call this a "message". I don't know exactly why, it's just calling a method of the object. When you see [object message] in OC, that is exactly what it is. The object is on the left, the message (method name) is on the right.

While we're on the subject of calling methods, You may see methods with parameters, but in OC they are named. Here is an example:

PHP:


$rectangle->setSize(10,20,true);


Objective-C:

[rectangle setX:10 Y:20 visible:YES]


Here you see exactly what the parameters are for. In PHP I may have guessed that the first two params were X and Y, but the third would be a mystery without digging into the class. In OC, I can plainly see the first arg is X, the second is Y, and the third is setting the visiblity. Also in OC, you will typically see YES and NO used for booleans, not true and false.

One other tidbit: in Objective-C, the parameter names are part of the method name. So in PHP, the above method name is setSize(), whereas in Objective-C, the method name is setX:Y:visible.


[pool release];


And here we are releasing the autorelease pool, which will inevitably clean up our object from memory.


return 0;


Our main function is expecting an integer to be returned, so we'll return a zero.

That's about it. If there are errors, send me a comment and I'll get them corrected. Thanks!