IBOutletCollection set ordering in Interface Builder - objective-c

I am using IBOutletCollections to group several Instances of similar UI Elements. In particular I group a number of UIButtons (which are similar to buzzers in a quiz game) and a group of UILabels (which display the score). I want to make sure that the label directly over the button updates the score. I figured that it is easiest to access them by index. Unfortunately even if I add them in the same order, they do not always have the same indexes. Is there a way in Interface Builder to set the correct ordering.

EDIT: Several commenters have claimed that more recent versions of Xcode return IBOutletCollections in the order the connections are made. Others have claimed that this approach didn't work for them in storyboards. I haven't tested this myself, but if you're willing to rely on undocumented behavior, then you may find that the explicit sorting I've proposed below is no longer necessary.
Unfortunately there doesn't seem to be any way to control the order of an IBOutletCollection in IB, so you'll need to sort the array after it's been loaded based on some property of the views. You could sort the views based on their tag property, but manually setting tags in IB can be rather tedious.
Fortunately we tend to lay out our views in the order we want to access them, so it's often sufficient to sort the array based on x or y position like this:
- (void)viewDidLoad
{
[super viewDidLoad];
// Order the labels based on their y position
self.labelsArray = [self.labelsArray sortedArrayUsingComparator:^NSComparisonResult(UILabel *label1, UILabel *label2) {
CGFloat label1Top = CGRectGetMinY(label1.frame);
CGFloat label2Top = CGRectGetMinY(label2.frame);
return [#(label1Top) compare:#(label2Top)];
}];
}

I ran with cduhn's answer and made this NSArray category.
If now xcode really preserves the design-time order this code is not really needed, but if you find yourself having to create/recreate large collections in IB and don't want to worry about messing up this could help (at run time). Also a note: most likely the order in which the objects were added to the collection had something to do with the "Object ID" you find in the Identity Inspector tab, which can get sporadic as you edit the interface and introduce new objects to the collection at a later time.
.h
#interface NSArray (sortBy)
- (NSArray*) sortByObjectTag;
- (NSArray*) sortByUIViewOriginX;
- (NSArray*) sortByUIViewOriginY;
#end
.m
#implementation NSArray (sortBy)
- (NSArray*) sortByObjectTag
{
return [self sortedArrayUsingComparator:^NSComparisonResult(id objA, id objB){
return(
([objA tag] < [objB tag]) ? NSOrderedAscending :
([objA tag] > [objB tag]) ? NSOrderedDescending :
NSOrderedSame);
}];
}
- (NSArray*) sortByUIViewOriginX
{
return [self sortedArrayUsingComparator:^NSComparisonResult(id objA, id objB){
return(
([objA frame].origin.x < [objB frame].origin.x) ? NSOrderedAscending :
([objA frame].origin.x > [objB frame].origin.x) ? NSOrderedDescending :
NSOrderedSame);
}];
}
- (NSArray*) sortByUIViewOriginY
{
return [self sortedArrayUsingComparator:^NSComparisonResult(id objA, id objB){
return(
([objA frame].origin.y < [objB frame].origin.y) ? NSOrderedAscending :
([objA frame].origin.y > [objB frame].origin.y) ? NSOrderedDescending :
NSOrderedSame);
}];
}
#end
Then include the header file as you chose to name it and the code can be:
- (void)viewDidLoad
{
[super viewDidLoad];
// Order the labels based on their y position
self.labelsArray = [self.labelsArray sortByUIViewOriginY];
}

Not sure when this changed exactly, but as of Xcode 4.2 at least, this no longer seems to be a problem. IBOutletCollections now preserve the order in which the views were added in Interface Builder.
UPDATE:
I made a test project to verify that this is the case: IBOutletCollectionTest

Not as far as I am aware.
As a workaround, you could assign each of them a tag, sequentially. Have the buttons range 100, 101, 102, etc. and the labels 200, 201, 202, etc. Then add 100 to the button's tag to get its corresponding label's tag. You can then get the label by using viewForTag:.
Alternatively, you could group the corresponding objects into their own UIView, so you only have one button and one label per view.

I found that Xcode sorts the collection alphabetically using the ID of the connection.
If you open the version editor on your nib file you can easily edit the id's (making sure they are unique otherwise Xcode will crash).
<outletCollection property="characterKeys" destination="QFa-Hp-9dk" id="aaa-0g-pwu"/>
<outletCollection property="characterKeys" destination="ahU-9i-wYh" id="aab-EL-hVT"/>
<outletCollection property="characterKeys" destination="Kkl-0x-mFt" id="aac-0c-Ot1"/>
<outletCollection property="characterKeys" destination="Neo-PS-Fel" id="aad-bK-O6z"/>
<outletCollection property="characterKeys" destination="AYG-dm-klF" id="aae-Qq-bam"/>
<outletCollection property="characterKeys" destination="Blz-fZ-cMU" id="aaf-lU-g7V"/>
<outletCollection property="characterKeys" destination="JCi-Hs-8Cx" id="aag-zq-6hK"/>
<outletCollection property="characterKeys" destination="DzW-qz-gFo" id="aah-yJ-wbx"/>
It helps if you first order your object manually in the Document Outline of IB so they show up in sequence in the the xml code.

The extension proposed by #scott-gardner is great & solves problems such as a collection of [UIButtons] not showing in the expected order. The below code is simply updated for Swift 5. Thanks really goes to Scott for this!
extension Array where Element: UIView {
/**
Sorts an array of `UIView`s or subclasses by `tag`. For example, this is useful when working with `IBOutletCollection`s, whose order of elements can be changed when manipulating the view objects in Interface Builder. Just tag your views in Interface Builder and then call this method on your `IBOutletCollection`s in `viewDidLoad()`.
- author: Scott Gardner
- seealso:
* [Source on GitHub](bit dot ly/SortUIViewsInPlaceByTag)
*/
mutating func sortUIViewsInPlaceByTag() {
sort { (left: Element, right: Element) in
left.tag < right.tag
}
}
}

It seems very random how IBOutletCollection is ordered. Maybe I am not understanding Nick Lockwood's methodology correctly - but I as well made a new project, added a bunch of UILabels, and connected them to a collection in the order they were added to the view.
After logging, I got a random order. It was very frustrating.
My workaround was setting tags in IB and then sorting the collections like so:
[self setResultRow1:[self sortCollection: [self resultRow1]]];
Here, resultRow1 is an IBOutletCollection of about 7 labels, with tags set through IB. Here is the sort method:
-(NSArray *)sortCollection:(NSArray *)toSort {
NSArray *sortedArray;
sortedArray = [toSort sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
NSNumber *tag1 = [NSNumber numberWithInt:[(UILabel*)a tag]];
NSNumber *tag2 = [NSNumber numberWithInt:[(UILabel*)b tag]];
return [tag1 compare:tag2];
}];
return sortedArray;
}
Doing this, I can now access objects by using [resultRow1 objectAtIndex: i] or such. This saves overhead of having to iterate through and compare tags every time I need to access an element.

I needed this ordering for a collection of UITextField objects for setting where the "Next" button on the keyboard would lead to (field tabbing). This is going to be an international app so I wanted the language direction to be ambiguous.
.h
#import <Foundation/Foundation.h>
#interface NSArray (UIViewSort)
- (NSArray *)sortByUIViewOrigin;
#end
.m
#import "NSArray+UIViewSort.h"
#implementation NSArray (UIViewSort)
- (NSArray *)sortByUIViewOrigin {
NSLocaleLanguageDirection horizontalDirection = [NSLocale characterDirectionForLanguage:[[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode]];
NSLocaleLanguageDirection verticalDirection = [NSLocale lineDirectionForLanguage:[[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode]];
UIView *window = [[UIApplication sharedApplication] delegate].window;
return [self sortedArrayUsingComparator:^NSComparisonResult(id object1, id object2) {
CGPoint viewOrigin1 = [(UIView *)object1 convertPoint:((UIView *)object1).frame.origin toView:window];
CGPoint viewOrigin2 = [(UIView *)object2 convertPoint:((UIView *)object2).frame.origin toView:window];
if (viewOrigin1.y < viewOrigin2.y) {
return (verticalDirection == kCFLocaleLanguageDirectionLeftToRight) ? NSOrderedDescending : NSOrderedAscending;
}
else if (viewOrigin1.y > viewOrigin2.y) {
return (verticalDirection == kCFLocaleLanguageDirectionLeftToRight) ? NSOrderedAscending : NSOrderedDescending;
}
else if (viewOrigin1.x < viewOrigin2.x) {
return (horizontalDirection == kCFLocaleLanguageDirectionTopToBottom) ? NSOrderedDescending : NSOrderedAscending;
}
else if (viewOrigin1.x > viewOrigin2.x) {
return (horizontalDirection == kCFLocaleLanguageDirectionTopToBottom) ? NSOrderedAscending : NSOrderedDescending;
}
else return NSOrderedSame;
}];
}
#end
Usage (after layout)
- (void)viewDidAppear:(BOOL)animated {
_availableTextFields = [_availableTextFields sortByUIViewOrigin];
UITextField *previousField;
for (UITextField *field in _availableTextFields) {
if (previousField) {
previousField.nextTextField = field;
}
previousField = field;
}
}

Here's an extension I created on Array<UIView> to sort by tag, e.g., useful when working w/ IBOutletCollections.
extension Array where Element: UIView {
/**
Sorts an array of `UIView`s or subclasses by `tag`. For example, this is useful when working with `IBOutletCollection`s, whose order of elements can be changed when manipulating the view objects in Interface Builder. Just tag your views in Interface Builder and then call this method on your `IBOutletCollection`s in `viewDidLoad()`.
- author: Scott Gardner
- seealso:
* [Source on GitHub](http://bit.ly/SortUIViewsInPlaceByTag)
*/
mutating func sortUIViewsInPlaceByTag() {
sortInPlace { (left: Element, right: Element) in
left.tag < right.tag
}
}
}

I used the extension proposed by #scott-gardner to order Image Views in order to display counters using individual png images of dot-matrix digits.
It worked like a charm in Swift 5.
self.dayDigits.sortUIViewsInPlaceByTag()
func updateDayDigits(countString: String){
for i in 0...4 {
dayDigits[i].image = offDigitImage
}
let length = countString.count - 1
for i in 0...length {
let char = Array(countString)[length-i]
dayDigits[i].image = digitImages[char.wholeNumberValue!]
}
}

Related

(xcode objective-c) Array - finding a center of a object in an array

I'm pretty nooby when it comes to Xcode but here goes.
I have an NSArray with 5 UIImageView's, I have a script which will pick a random UIImageView, but I need it to then output the center of the image that was chosen.
My code looks like this:
NSMutableArray *Invader = [[NSMutableArray alloc] init];
[Invader addObject:H1];
[Invader addObject:H2];
[Invader addObject:H3];
[Invader addObject:H4];
[Invader addObject:H5];
Then like this to choose where to move a new UIImageView to:
if (FIRE1 == YES){
NSObject *Inv1 = [Invader randomObject]; //randomObject picks a random Invader
InvaderBullet1.hidden = NO;
InvaderBullet1.center = CGPointMake(Inv1.center.x, Inv1.center.y)
}
The error it's giving me is Property 'center' not found on object of type 'NSObject *'
I'm hoping this makes sense to anyone and that anyone can help me.
Thanks
NSObject obviously doesn't have a center property. Though you're not adding NSObjects to your array, you're adding (I assume) UIImageViews. So if you're adding UIImages, you can remove UIImages as well.
if (FIRE1 == YES){
NSObject *Inv1 = [Invader randomObject];
if ([Inv1 isKindOfClass:[UIImageView Class]]) //randomObject picks a random Invader
((UIImageView*)InvaderBullet1).hidden = NO;
((UIImageView*)InvaderBullet1).center = CGPointMake(Inv1.center.x, Inv1.center.y)
}
You simply need to specify the correct type for your variable:
if (FIRE1 == YES){
UIImageView *Inv1 = [Invader randomObject]; //randomObject picks a random Invader
InvaderBullet1.hidden = NO;
InvaderBullet1.center = CGPointMake(Inv1.center.x, Inv1.center.y)
}
This is assuming that your randomObject method (looks like a category method on NSArray) has a return type of id. If its return type is NSObject * then you need to do a cast:
UIImageView *Inv1 = (UIImageView *)[Invader randomObject]; //randomObject picks a random Invader
(Minor note: I recommend to stick to the established naming conventions. In this case: variables usually start with a lower-case character. You're doing other people that need/want to work with your code a favor.)

Cocos2D scoring game [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Scoring System In Cocos2D
I got a reply from a question I asked earlier but I am new to coding and have no idea how to do it. Here is the reply:
"#synthesize a "score" property of type int, and a "scoreLabel" property of type CCLabelTTF.
initialize your score property to "0" in -(void)init
On line 126, increment your "score" property by 1, and set that value into your CCLabelTTF."
Can you tell me how to do this? plz. link to my other post
----- Scoring System In Cocos2D
When you synthesize a private variable (other classes cannot see it) you allow a way for other classes to see and/or modify the value of that variable.
First, you want to create the variable:
NSMutableArray *_targets;
NSMutableArray *_projectiles;
int _score;
CCLabelTTF *_scoreLabel;
Then in your init method to set the _score to 0:
-(id) init
{
if( (self=[super init] )) {
[self schedule:#selector(update:)];
_score = 0;
Then increment (add 1 to) your _score variable and set the string (the text content) of your _scoreLabel to that value.
if (CGRectIntersectsRect(projectileRect, targetRect)) {
[targetsToDelete addObject:target];
_score++;
[_scoreLabel setString:[NSString stringWithFormat:#"%d", _score]];
}
The line [_scoreLabel setString:[NSString stringWithFormat:#"%d", _score]]; is a way to convert the integer of _score to a string (NSString). It's an old C way of doing it, the %d means that whatever is going to be there should be displayed as an integer as opposed to a float (having decimal points).
It also looks like you need to "instantiate" your label and add it as a child to the layer. Instantiation is just a fancy term for creating a instance of something. Think of a "class" as a blueprint for a chair, and an "instance" as a chair created from that blueprint. Once you have the chair created (an instance), you can modify it (paint it, add/remove legs, etc).
So, to instantiate your label and add it to the layer (itself):
-(id) init
{
if( (self=[super init] )) {
[self schedule:#selector(update:)];
_score = 0;
//Create label
_scoreLabel = [CCLabelTTF labelWithString:#"0" fontName:#"Marker Felt" fontSize:16];
//Add it to a layer (itself)
[self addChild:_scoreLabel];
Create a score property in HelloWorldLayer.h after the interface declaration, like
#property (nonatomic, retain) int score;
Then synthesize it in your .m file just after the #implementation HelloWorldLayer line.
Create methods for setting and getting scores:
-(int)getScore {
return self.score;
}
-(void)setScore:(int)newScore {
self.score = newScore;
}
In the init method, set the value of the property to zero,
if( (self=[super init] )) {
//... other stuff
[self setScore:0]
}
You can update the score with the setScore method, but I suggest having another method for this that calls setScore so that you can use it at different places with a single line call, and make any changes like assigning more score in certain situations, like two collisions within half-a-second etc..
-(void)updateScore:(int)increment {
int currentScore = [self getScore];
[self setScore:(currentScore + increment)];
}
Similarly, for label,
#property (nonatomic, retain) CCLabelTTF scoreLabel; // in header
and
#synthesize scoreLabel; // in .m file
Again, in your init method, initialize the label with position, layer and initial text etc. Then you can update that text in the updateScore method.
-(void)updateScore:(int)increment {
int currentScore = [self getScore];
[self setScore:(currentScore + increment)];
[scoreLabel setString:[NSString stringWithFormat:#"Score: %i", [self getScore]]];
}
Make sure you read through the tutorial before going ahead in order to avoid confusion regarding common tasks.

NSTextField with Number Formatter not allowing decimal input

I'm trying to make a simple Cocoa application where a number is input into a textfield. When a number is typed into one textfield the other textfield will automatically update with another number (and vice versa). The textfield that I've dragged into the window is the "Text Field with Number Formatter" from interface builder. I have the textfields set as delegate which allows them to automatically update. However, the formatting does not seem to work. I can't input decimal numbers. Please help!!!
Header file here:
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate> {
IBOutlet NSTextField *number1;
IBOutlet NSTextField *number2;
}
#property (assign) IBOutlet NSWindow *window;
#end
Implementation file here:
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize window = _window;
-(void)controlTextDidChange:(NSNotification *) note {
NSTextField *changedField = [note object];
if (changedField == number1) {
float num1 = [number1 floatValue];
[number2 setFloatValue: (num1*2.0)];
}
if (changedField == number2) {
float num2 = [number2 floatValue];
[number1 setFloatValue: (num2/2.0)];
}
}
#end
This is a bit obscure, but what you are trying to do cannot really work but there is an alternative.
In your code you have:
float num1 = [number1 floatValue];
[number2 setFloatValue: (num1*2.0)];
The first line requests the current value of number1. As you have a number formatter attached to number1 it is called to format the text... and if you've entered, say, 123. at this point the formatted result is 123... and your decimal point is vanished. You'll notice you cannot enter commas (thousands separators) either.
So the fundamental problem is that after every character is typed your code is forcing it to be a valid number at that point.
The alternative is to only do your action when the user has entered a complete number. You can either write code yourself to analyze the input as each character is typed, or you can use the quick and easy method of waiting until the user hits and responding to the action.
To do the latter change your code to:
- (IBAction) textDidChange:(id)sender
{
if (sender == number1)
{
float num1 = [number1 floatValue];
[number2 setFloatValue: (num1*2.0)];
}
else if (sender == number2)
{
float num2 = [number2 floatValue];
[number1 setFloatValue: (num2/2.0)];
}
}
and connect it to the control as the "selector" and remove the "delegate" connection you already have.
This solution is less "dynamic" than what you tried, but it does work. To get the dynamic response back you'll need to write more involved code as stated above.
I know I'm very late to answer but I just ran into the exact same problem and found a nice workaround. Building upon #CRD's answer.
How I accomplished the problem is:
Store the formatter
Wipe the formatter from the textfield
read the value from the textfield
Reapply the formatter to the textfield
An example of how I did it is below:
-(void)controlTextDidChange:(NSNotification *)obj
{
if ([obj object])
{
NSTextField *field = [obj object];
NSNumberFormatter *formatter = [field formatter];
[field setFormatter:nil];
double data = [field doubleValue];
[field setFormatter:formatter];
// ...
// Do other meaningful stuff now
}
}
Not sure if it's the proper way to do it but it worked well for me.
A bit late to the party, but I just ran into this issue myself, and I think I found a concise solution that maintains dynamic updating, yet avoids the formatter kicking in too early, thus preventing the user from entering fractions:
- (void)controlTextDidChange:(NSNotification *)aNotification
{
NSTextView *fieldEditor = [aNotification.userInfo valueForKey:#"NSFieldEditor"];
CGFloat floatValue = fieldEditor.textStorage.string.floatValue;
// Getting the new value from the field editor avoids the formatter kicking in here.
// You can now use the value as you wish. Probably something like:
if (sender == number1)
{
[number2 setFloatValue: (floatValue*2.0)];
}
else if (sender == number2)
{
[number1 setFloatValue: (floatValue/2.0)];
}
}
(I typed this into the browser, so it may not be completely correct, but should be close enough. The essential parts are the first two lines: extract the field editor and extract the new value from it.

NSTextField continuous update

I can't figure out how to get an NSTextfield to update automatically, without having to press "Return" or click another text field.
My goal is to input a number into a field and have the other fields update simultaneously. I tried clicking "Continuous" in the text field attributes but it doesn't seem to do anything.
Here is my interface file:
#import <Foundation/Foundation.h>
#interface InchController : NSObject {
IBOutlet NSTextField *centimetersTextField;
IBOutlet NSTextField *inchesTextField;
IBOutlet NSTextField *feetTextField;
}
-(IBAction)convert:(id)sender;
#end
Here is my implementation file:
#import "InchController.h"
#implementation InchController
- (IBAction)convert:(id)sender {
if (sender == inchesTextField) {
float inches = [inchesTextField floatValue];
[feetTextField setFloatValue:(inches * 0.0833)];
[centimetersTextField setFloatValue:(inches * 2.54)];
}
else if (sender == feetTextField) {
float feet = [feetTextField floatValue];
[inchesTextField setFloatValue:(feet * 12)];
[centimetersTextField setFloatValue:(feet * 30.48)];
}
else if (sender == centimetersTextField) {
float centimeters = [centimetersTextField floatValue];
[inchesTextField setFloatValue:(centimeters * 0.394)];
[feetTextField setFloatValue:(centimeters * 0.033)];
}
}
#end
So here is the updated implementation file per Josh's solution. Commented out the IBAction since it is no longer needed in the implementation and interface files.
#import "LengthController.h"
#implementation LengthController
//- (IBAction) convert: (id)sender {
//}
-(void) controlTextDidChange:(NSNotification *) note {
NSTextField *changedField = [note object];
if (changedField == inchesTextField) {
float inches = [inchesTextField floatValue];
[feetTextField setFloatValue: (inches * 0.0833)];
[centimetersTextField setFloatValue: (inches * 2.54)];
}
if (changedField == centimetersTextField) {
float centimeters = [centimetersTextField floatValue];
[inchesTextField setFloatValue:(centimeters * 0.394)];
[feetTextField setFloatValue:(centimeters * 0.033)];
}
if (changedField == feetTextField) {
float feet = [feetTextField floatValue];
[inchesTextField setFloatValue:(feet * 12)];
[centimetersTextField setFloatValue:(feet * 30.48)];
}
}
#end
Make your controller the delegate of the text fields; you can set this in Interface Builder by Ctrl-dragging from the text fields to the controller.
In your controller, implement the "NSControl Delegate" method controlTextDidChange:, which will be called (as its name suggests) whenever the field's text changes. In that method, you can validate the text and, if appropriate, update the contents of the other fields.
The argument that is passed in can give you the text field which changed; you can then pass that on to your existing convert: method to reuse the code:
- (void) controlTextDidChange: (NSNotification *)note {
NSTextField * changedField = [note object];
[self convert:changedField];
}
There's nothing special about action methods. The IBAction return type evaluates to void; it's only used by Xcode to expose the method for use in Interface Builder. You can, therefore, call them just like any other method. Here, you get the appropriate field and pass it in as the sender parameter, as if the field had called the action method itself.
Depending on the complexity of the problem, bindings may be a viable solution, too.
You can define properties on a model or model controller object and hook them up to the corresponding text fields. Then, changes in the text field are immediately reflected in the properties, which can then trigger changes to other properties.
Text fields bound to these "derived" properties are then updated automatically.
Remember to "bracket" your changes to the derived properties with willChangeValueForKey: and didChangeValueForKey: so the changes are sent to observers. More here.
Of course, if you have loops in the dependencies, it gets ugly; in that case the controlTextDidChange: method mentioned in the other answers is probably better.

if else statement not working!

This code has no errors in it, but when i click the button nothing in the "if" statement
works! it doesn't crash or show errors... Btw im working in Xcode on an iphone app.
#import "MainView.h"
#implementation MainView
#synthesize MyButton2, MyMainTextLabel;
#synthesize MyButton3, MyMainTextLabel;
#synthesize MyButton2, Button2Label;
#synthesize MyButton2, Button3Label;
#synthesize MyButton3, Button2Label;
#synthesize MyButton3, Button3Label;
- (IBAction)Button2click {
if(Button2Label.text == #"Hello There!") {
MyMainTextLabel.text = #"\"Hey...!!\"";
Button3Label.text = #"What a rude Penguin!";
Button2Label.text = #"Hows a Goin?";
}
}
- (IBAction)Button3click {
if(Button3Label.text == #"Penguins SUCK!") {
MyMainTextLabel.text = #"\"DONT TEST ME!!\"";
Button3Label.text = #"Oh I Will!";
Button2Label.text = #"Sorry I didnt mean to...";
}
}
- (IBAction)buttonclick {
}
#end
You can't compare strings with ==. That will work only if they are the exact same NSString object, not if they are two identical strings. Use [buttonLabel.text isEqualToString:#"Hello There!"]
When you write:
Button2Label.text == #"Hello There!"
you are testing for pointer equality between the button's label and your static string. You want to test from string equality, not pointer equality here:
if ([Button2Label.text isEqualToString: #"Hello There!") { ... }
That said, making runtime decisions in your action methods based on the button's label is a poor design, and will run you into trouble if the button's label changes for any reason, including localization.
Switching off the sender, or the sender's tag, is the preferred pattern.
You can't use == to compare strings, it just compares the pointers.
Try:
if ([Button2Label.text compare:#"Hello There!"] == NSOrderedSame)
Most likely the if condition is failing, try negating your condition to test that theory. If it suddenly starts running, try debuging the value of the text property on your labels.