trying to customize TableView, no example show heightForRowAtIndexPath

Discussion in 'iOS Programming' started by 1458279, Nov 28, 2011.

  1. 1458279 Suspended

    1458279

    Joined:
    May 1, 2010
    Location:
    California
    #1
    I'm playing around with a tableview and wanted to see what happens with heightForRowAtIndexPath. According to the Apple Docs, I should be able to put this method into the file where the AppDelegate is.

    I did that and put a debug stop there and it's not calling the method.

    The project is really simple:
    AppDelegate.h
    AppDelegate.m
    MainWindow.xib : this has the tableView dropped on it
    countries.db : Sqlite file to search and show in the tableView.

    I put the method in AppDelegate.m and run the program, it never calls it. So I added willDisplayCell and it doesn't get called either.

    I was under the impression that: ... If the method is there, it gets called automatically, if it's not there, stock (default) behavior takes place...

    Clearly this isn't the case.

    Question: How to you get the app to call one of these methods?




    Apple Docs:
    tableView:willDisplayCell:forRowAtIndexPath:
    Tells the delegate the table view is about to draw a cell for a particular row.

    - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
    Parameters
    tableView
    The table-view object informing the delegate of this impending event.
    cell
    A table-view cell object that tableView is going to use when drawing the row.
    indexPath
    An index path locating the row in tableView.
    Discussion
    A table view sends this message to its delegate just before it uses cell to draw a row, thereby permitting the delegate to customize the cell object before it is displayed. This method gives the delegate a chance to override state-based properties set earlier by the table view, such as selection and background color. After the delegate returns, the table view sets only the alpha and frame properties, and then only when animating rows as they slide in or out.

    Availability
    Available in iOS 2.0 and later.
    See Also
    – tableView:cellForRowAtIndexPath: (UITableViewDataSource)
    – prepareForReuse (UITableViewCell)
    Declared In
    UITableView.h
     
  2. 1458279 thread starter Suspended

    1458279

    Joined:
    May 1, 2010
    Location:
    California
    #2
    Just to help make this question clear:

    In my sampleAppDelegate.m I have the following methods:
    The code has been removed and some methods not shown.

    Code:
    -(id) init  // opens the .db file
    
    - (void)searchBar // used to get the search string, search the .db, load the results into the array 
    
    - (NSString *)tableView:(UITableView *)tableView
    titleForHeaderInSection:(NSInteger)section
    
    This never gets called!  I set a debug stopper here and it's never called.
    [B]- (CGFloat)tableView:(UITableView *)table heightForRowAtIndexPath:(NSIndexPath *)ip
    [/B]
    - (NSInteger)tableView:(UITableView *)table
     numberOfRowsInSection:(NSInteger)section
    
    
    - (UITableViewCell *)tableView:(UITableView *)tableView
             cellForRowAtIndexPath:(NSIndexPath *)ip
    
    
    - (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    
    
    - (void)applicationWillTerminate:(UIApplication *)application // closes the databse
    
    ...
    
     
  3. jnoxx macrumors 65816

    jnoxx

    Joined:
    Dec 29, 2010
    Location:
    Aartselaar // Antwerp // Belgium
    #3
    If your array is empty, that row will never gets called, also make sure your UITableview has his delegates set.
     
  4. 1458279 thread starter Suspended

    1458279

    Joined:
    May 1, 2010
    Location:
    California
    #4
    I probably should have pointed this out in the first post. This is a running app from BNR book that loads data from an SQLite.db and displays the data in the tableView.

    It uses the searchbar to find matches in the list.

    I didn't want to post the whole code because it's pretty long.

    So what I did was take a working app that displays a tableView, and started trying to play with different methods to see what they do and how they work.

    This way I know the tableView works.

    So if you take a working tableView and want to add the ability to change the height of the rows, how would you do that... I added the heightForRowAtIndexPath in the same .m file, but it never gets called.
     
  5. dejo, Nov 29, 2011
    Last edited: Nov 29, 2011

    dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #5
    Seems rather odd to put all your code in the appDelegate. A more usual approach would be to subclass UITableViewController and put all your tableView code in there.

    Anyways, if you don't have your tableView's delegate set, it won't be able to call your UITableViewDelegate methods.

    EDIT:

    That's not what the Apple docs say. You will need to learn the difference between the app delegate and your class's (in this case, UITableView) delegate.
     
  6. ArtOfWarfare macrumors 604

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #6
    Two questions:

    First, how do you know that it's never being called?

    Second, do you know if

    Code:
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)ip
    is being called ever?

    (Easy way of checking is to throw in NSLog(@"Cell is requested"); and NSLog(@"Height is requested."); ... that's what I do, although I suspect using break points would be simpler if I bothered learning how those worked...)
     
  7. 1458279 thread starter Suspended

    1458279

    Joined:
    May 1, 2010
    Location:
    California
    #7
    I setup a debug point, but after reading your post, I tried the NSLog, same results.

    I didn't want to post all the code because of the length, but here goes:

    What I'm trying to do here is to learn all the methods for this class. It might be something simple, but my understanding is:
    I have an object of class tableView, any method in class tableView should be usable by putting that method into the .m file and adding whatever custom code you might want.

    The problem is that I've seen a bunch of posts on using this, but none that has full sample code, so I think this is some stupid rookie error :D

    Thanks for any input!

    Not much to the header file
    Code:
    #import <UIKit/UIKit.h>
    #import <sqlite3.h>
    
    @interface NayshuunAppDelegate : NSObject <UIApplicationDelegate> {
        IBOutlet UITableView *countryTable;
        IBOutlet UISearchBar *searchBar;
        // Model
        NSMutableArray *continents;
        // Database stuff
        sqlite3 *database;
        sqlite3_stmt *statement;
    }
    
    @property (nonatomic, retain) IBOutlet UIWindow *window;
    @end

    Code:
    #import "NayshuunAppDelegate.h"
    
    @implementation NayshuunAppDelegate
    @synthesize window=_window;
    
    - (id)init
    {
        [super init];
        // Create the root of the tree
        continents = [[NSMutableArray alloc] init];
        // Where do the documents go?
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
        NSString *path = [paths objectAtIndex:0];
        // What would be the name of my database file?
        NSString *fullPath = [path stringByAppendingPathComponent:@"countries.db"];
        // Get a file manager for file operations
        NSFileManager *fm = [NSFileManager defaultManager];
        // Does the file already exist?
        BOOL exists = [fm fileExistsAtPath:fullPath];
        // Does it already exist?
        if (exists) {
            // no problem, no need to log anything...
            //NSLog(@"%@ exists – just opening", fullPath);
        } else {
            NSLog(@"%@ does not exist – copying and opening", fullPath);
            // Where is the starting database in the application wrapper?
            NSString *pathForStartingDB = [[NSBundle mainBundle] pathForResource:@"countries" ofType:@"db"];
            // Copy it to the documents directory
            BOOL success = [fm copyItemAtPath:pathForStartingDB toPath:fullPath error:NULL];
            if (!success) {
                NSLog(@"database copy failed");
            }
        }
        // Open the database file
        const char *cFullPath = [fullPath cStringUsingEncoding:NSUTF8StringEncoding];
        if (sqlite3_open(cFullPath, &database) != SQLITE_OK) {
            NSLog(@"unable to open database at %@", fullPath);
        }
        return self;
    }
    
    - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *) searchText
    {
        //Clear the data structures
        [continents removeAllObjects];
        // Only search if the user has typed something in
        if ([searchText length] != 0) {
            if (!statement) {
                // '?' is a placeholder for parameters
                char *cQuery = "SELECT Continent, Name, Code FROM Country "
                "WHERE Name LIKE ? ORDER BY Continent, Name";
                // Prepare the query
                if (sqlite3_prepare_v2(database, cQuery, -1, &statement, NULL) != SQLITE_OK)
                {
                    NSLog(@"query error: %s", statement);
                }
            }
            // Add % to the end of the search text
            searchText = [searchText stringByAppendingString:@"%"];
            NSLog(@"searching for %@", searchText);
            // This C string will get cleaned up automatically
            const char *cSearchText =
            [searchText cStringUsingEncoding:NSUTF8StringEncoding];
            // Replace the first (and only) parameter with the search text
            sqlite3_bind_text(statement, 1, cSearchText, -1, SQLITE_TRANSIENT);
            //Page 415
            NSString *lastContinentName = nil;
            NSMutableArray *currentNationList;
            // Loop to get all the rows
            while (sqlite3_step(statement) == SQLITE_ROW) {
                // Get the string in the first column
                const char *cContinentName =
                (const char *)sqlite3_column_text(statement, 0);
                // Convert C string into an NSString
                NSString *continentName = [[[NSString alloc] initWithUTF8String:cContinentName] autorelease];
                //Page 415 adding to the display list
                // Is this a new continent?
                if (!lastContinentName || ![lastContinentName isEqual:continentName])
                {
                    // Create an array for the nations of this new continent
                    currentNationList = [[NSMutableArray alloc] init];
                    // Put the name and the array in a dictionary
                    NSDictionary *continentalDict = [[NSDictionary alloc] initWithObjectsAndKeys: continentName, @"name", currentNationList, @"list", nil];
                    // Release array retained by the dictionary
                    [currentNationList release];
                    // Add the new continent to the array of continents
                    [continents addObject:continentalDict];
                    // Release the dictionary being retained by the array
                    [continentalDict release];
                }
                // Note the continent name so that we know if we need to make a
                // new continent dictionary next time through the loop
                lastContinentName = continentName;
                // Get the string in the second column
                const char *cCountryName = (const char *)sqlite3_column_text(statement, 1);
                // Convert C string into an NSString
                NSString *countryName = [[[NSString alloc] initWithUTF8String:cCountryName] autorelease];
                // Get the string in the third column
                const char *cCountryCode = (const char *)sqlite3_column_text(statement, 2);
                // Convert C string into an NSString
                NSString *countryCode = [[[NSString alloc] initWithUTF8String:cCountryCode] autorelease];
                // Create a dictionary for this nation
                NSMutableDictionary *countryDict = [[NSMutableDictionary alloc] init];
                [countryDict setObject:countryName forKey:@"name"];
                [countryDict setObject:countryCode forKey:@"code"];
                // Put the nation's dictionary in the list for the current continent
                [currentNationList addObject:countryDict];
                // Release the dictionary retained by the array
                [countryDict release];
            }
            // Clear the query results
            sqlite3_reset(statement);
        }
        // Load the table with the new data
        [countryTable reloadData];
    }
    
    #pragma mark Table View Data Source Methods
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
    {
        // Return the number of continents
        return [continents count];
    }
    
    - (NSString *)tableView:(UITableView *)tableView
    titleForHeaderInSection:(NSInteger)section
    {
        // Get the dictionary for the continent for this section
        NSDictionary *continentDict = [continents objectAtIndex:section];
        // Return the name of the continent
        return [continentDict objectForKey:@"name"];
    }
    
    // why isn't this being called????
    - (CGFloat)tableView:(UITableView *)table heightForRowAtIndexPath:(NSIndexPath *)ip
    {
        NSLog(@"Setting custom row height... ");
        return 44;
    }
    
    //- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
    //{
    //    
    //}
    
    - (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section
    {
        // Get the dictionary for the continent for this section
        NSDictionary *continentDict = [continents objectAtIndex:section];
        // Get the array of nations for this continent
        NSArray *nations = [continentDict objectForKey:@"list"];
        // Return the number of nations on this continent
        return [nations count];
    }
    
    //Page 418
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)ip
    {
        // Get the dictionary for the continent for this section
        NSDictionary *continentDict = [continents objectAtIndex:[ip section]];
        // Get the array of nations for this continent
        NSArray *nations = [continentDict objectForKey:@"list"];
        // Which nation is at the required row?
        NSDictionary *nationDict = [nations objectAtIndex:[ip row]];
        // What is its name?
        NSString *nationName = [nationDict objectForKey:@"name"];
        // Try to reuse an existing cell
        UITableViewCell *cell =
        [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"];
        // None available?
        if (!cell) {
            // Make a new cell
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                          reuseIdentifier:@"UITableViewCell"];
            [cell autorelease];
        }
        // Put the name of the country on the cell
        [[cell textLabel] setText:nationName];
        return cell;
    }
    
    
    #pragma mark Table View Data Source Methods
    //- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section
    //{
    //    return 0;
    //}
                
    //- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)ip
    //{
    //    return nil;
    //}
    #pragma mark application Delegate Methods
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        [self.window makeKeyAndVisible];
        // Bring up the keyboard immediately
        [searchBar becomeFirstResponder];
        return YES;
    }
    
    - (void)applicationWillTerminate:(UIApplication *)application
    {
        sqlite3_close(database);
    }            
    
    - (void)applicationWillResignActive:(UIApplication *)application
    {
        /*
         Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
         Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
         */
    }
    
    - (void)applicationDidEnterBackground:(UIApplication *)application
    {
        /*
         Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
         If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
         */
    }
    
    - (void)applicationWillEnterForeground:(UIApplication *)application
    {
        /*
         Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
         */
    }
    
    - (void)applicationDidBecomeActive:(UIApplication *)application
    {
        /*
         Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
         */
    }
    
    - (void)dealloc
    {
        [_window release];
        [super dealloc];
    }
    
    @end
    
     
  8. North Bronson macrumors 6502

    Joined:
    Oct 31, 2007
    Location:
    San José
    #8
    The AppDelegate is not the place for this kind of work. Make a custom controller class to hold your table and do the work there.
     
  9. ArtOfWarfare macrumors 604

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #9
    North Bronson is correct that putting everything in your AppDelegate is atypical and a bad practice to get into (you really should set up multiple files for the different functions your app will be performing... it makes it easier to reuse code for future projects.)

    In any event, the thing you need to add to make it work via your AppDelegate would be this:

    Code:
    @interface NayshuunAppDelegate : NSObject <UIApplicationDelegate[color=RED], UITableViewDatasource, UITableViewDelegate[/color]>
    BUT you say that you're using a template that came with Xcode, correct? The template would already have a file, called something like "ViewController" (IDK for sure what it'd be called.) If you check that .h file, it probably has the pieces of code in it's interface declaration already that I just suggested. If so, then you need to put your Table View Datasource and Table View Delegate methods in the corresponding .m file.
     
  10. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #10
    Your understanding is not right. A bunch of those methods come from the UITableViewDatasource and UITableViewDelegate protocols. Without having set your datasource and delegate properties for your tableView, these methods will not get called.

    If your goal is to learn the methods for tableView and its protocols, I'd suggest starting with a Master-Detail-based project and tweaking from there.
     
  11. 1458279 thread starter Suspended

    1458279

    Joined:
    May 1, 2010
    Location:
    California
    #11
    Ok, so it looks like I'm trying to put this in the implementation file. I did find a working example of this method in the UICatalog example from Apple.

    One of my old habits was to put a bunch of test code into one file just to get a handle on how things work. Looks like it's an old habit I'll have to break :rolleyes:
     
  12. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #12
    You are confusing the app delegate with the table view delegate. While the app delegate is part of the controller layer in an application it is view controllers that are the C in MVC in normal applications. You should read the view controller programming guide and the table view controller programming guide, from the apple documentation to understand this better.
     

Share This Page