views:

123

answers:

1

I'm using http://github.com/facebook/three20 in my iPhone application. I've followed the instructions to include the framework in my project. I've verified it twice now (at the recommendations of people on the google group and IRC). I get the following error when some of the Three20 code attempts to use a Cataloged selector on the UIView:

-[TTSearchTextField ancestorOrSelfWithClass:]: unrecognized selector sent to instance 0x3a85ee0'

This exception is triggered when I touch the text field.

Here's the kicker for me, if I don't assign a datasource to the TTSearchTextField it doesn't trigger the the error. So, logically I figured it must be my dataSource.

  1. I placed a break point on every function in my data source.
  2. Ran the program, as expected the init was called and the accessor delegates was called.
  3. Tapped the text field, no calls to my dataSource and the run-time exception occurs.

I'm stumped on this one. I've never had issues using external libs with Catalogs in them before. I can provide more information on the project setup, just ask for specifics since there's quite a bit of information there.

Code I'm using the create the TTSearchTextField:

// setup Country
self.searchFieldCountry = [[TTSearchTextField alloc]initWithFrame:CGRectMake(156, 145, 146, 37)];
self.searchFieldCountry.borderStyle = UITextBorderStyleRoundedRect;
self.searchFieldCountry.placeholder = @"Country";
self.searchFieldCountry.autocapitalizationType = UITextAutocapitalizationTypeNone;
OLLocationSearchDataSource *ds = [[OLLocationSearchDataSource alloc]init];
self.searchFieldCountry.dataSource = ds;
[self.view addSubview:self.searchFieldCountry];
[ds release];

Code for my DataSource:

#import "OLLocation.h"
#import "AppSession.h"

@implementation OLLocation

@synthesize locationData = _locationData;

@synthesize locationMode,country,region;

- (NSMutableArray*)delegates {
    if (!_delegates) {
        _delegates = TTCreateNonRetainingArray();
    }
    return _delegates;
}

- (BOOL)isLoadingMore {
    return NO;
}

- (BOOL)isOutdated {
    return NO;
}

- (BOOL)isLoaded {
    return !!_AllLocationData;
}

- (BOOL)isLoading {
    return NO;
}

- (BOOL)isEmpty {
    return !_AllLocationData.count;
}

- (void)load:(TTURLRequestCachePolicy)cachePolicy more:(BOOL)more {
}

- (void)invalidate:(BOOL)erase {
}

- (void)cancel {
    // cancel a search if possible
}

- (void)loadNames {
    _AllLocationData = [[AppSession getAppSession] getCountries];
}

- (void)search:(NSString*)text {
    [self cancel];

    self.locationData = [NSMutableArray array];

    if (text.length) {
        text = [text lowercaseString];
        for (NSString* name in _AllLocationData) {
            if ([[name lowercaseString] rangeOfString:text].location == 0) {
                [_locationData addObject:name];
            }
        }
    }
}

- (void)dealloc {
    [super dealloc];
}

@end

@implementation OLLocationSearchDataSource

@synthesize locations = _locations;

-(id)init {
    if (self = [super init]) {
        _locations = [[OLLocation alloc] init];
        _locations.locationMode = OLLocationModeCountry;
        self.model = _locations;
    }
    return self;
}

-(void)dealloc {
    TT_RELEASE_SAFELY(_locations);
    [super dealloc];
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// TTTableViewDataSource

- (void)tableViewDidLoadModel:(UITableView*)tableView {
    self.items = [NSMutableArray array];

    for (NSString* name in _locations.locationData) {
        TTTableItem* item = [TTTableTextItem itemWithText:name URL:@"http://google.com"];
        [_items addObject:item];
    }
}

- (void)search:(NSString*)text {
    [_locations search:text];
}

- (NSString*)titleForLoading:(BOOL)reloading {
    return @"Searching...";
}

- (NSString*)titleForNoData {
    return @"No names found";
}

@end
+2  A: 

I don't think it's your data source. -ancestorOrSelfWithClass: is a category method that extends UIView and is added in UIViewAdditions.h. The reason you're getting the error when adding your data source is probably that whatever code sends the -ancestorOrSelfWithClass: message probably isn't called if the data source is unassigned.

When compiling static libraries for inclusion into iPhone applications, under 2.x you needed to add -ObjC to your Other Linker Flags to make sure category methods were linked into the library. In 3.x, that doesn't seem to work properly, so -all_load needed to be added as well to the Other Linker Flags.

Brad Larson
Indeed. That's exactly what the people on IRC and in the mailing list suggested. This is a 3.x project and I have both -ObjC and -all_load in my Other Linker Flags. Any other suggestions? I wonder if there's a way to confirm it's actually applying those flags at compile time...
jsapara
I debugged the building process for this project and found that it was not using the -all_load on linking. The project clearly defines it, but it wasn't getting applied to any of our build targets. I went in and added the -all_load to the targets and it now works.I went back to our previous projects that use Three20, the Linker flags are definitely defined in the Project. I created a new project and threw in a quick Three20 UI widget, added the linker flags to the Project (not the Target). Works just fine, so there must be something odd/corrupt with out Xcode file. Either way, it's solved.
jsapara