Ios – How to put a UITableView into a UIView

iosuitableviewuiview

I am trying to put a UITableView into a UIView. The UIView will contain some other stuff as well, so I want to want to define a part of the UIView to be used by the UITableView. Reading on this and other sites, I am using "UIViewController" and delegates "".

My questions are:

  • Why do I not see any UITable with the code below?

  • Why do I see a UITable when I change in InformatieSectieViewController.h

    @interface InformatieSectieViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
    

    to

    @interface InformatieSectieViewController : UIViewTableController <UITableViewDataSource, UITableViewDelegate>
    

    (UIViewController to UITableViewController)? (This UITable then takes up the whole UIView).

  • How do I get a UITableView correctly into a UIView.

  • What also surprises me; when I remove the < UITableViewDataSource, UITableViewDelegate> part from @interface InformatieSectieViewController : UIViewController < UITableViewDataSource, UITableViewDelegate>, I would expect a warning on the lines (in InformatieSectieViewController.m):

    tabView.delegate = self;
    tabView.dataSource = self;
    

    But I do not get this warning.

Here is the code I am using:

InformatieSectie.h

#import <UIKit/UIKit.h>
@interface InformatieSectie : NSObject
@property (strong, nonatomic) NSString *header;
-(UIView*)createInformatieSectie;
@end

InformatieSectie.m

#import "InformatieSectie.h"
#import "InformatieSectieViewController.h"
#import <QuartzCore/QuartzCore.h>
@implementation InformatieSectie
@synthesize header;
@synthesize footer;
-(UIView*)createInformatieSectie{
    UIView * view = [[UIView alloc] initWithFrame:CGRectMake(0, 200, breedte, hoogte)];
    //(add headerlabel at pos)
    [view addSubview:headerLabel];

    InformatieSectieViewController *svc = [[InformatieSectieViewController alloc] init];

    [view addSubview:svc.view];
    return view;
}
@end

And this is the second class where I define the UITableView:

InformatieSectieViewController.h

#import <Foundation/Foundation.h>
@interface InformatieSectieViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, retain) UITableView *tabView;
@end

InformatieSectieViewController.m

#import "InformatieSectieViewController.h"
#import <QuartzCore/QuartzCore.h>
@implementation InformatieSectieViewController
@synthesize tabView;

-(void)loadView {
    tabView = [[UITableView alloc] initWithFrame:CGRectMake(100, 100, 20, 20)];
    tabView.delegate = self;
    tabView.dataSource = self;
    tabView.layer.borderWidth = 10.0;
    tabView.layer.borderColor = [UIColor yellowColor].CGColor;
    self.view = tabView;
    self.view.layer.backgroundColor = [UIColor magentaColor].CGColor;
    [super loadView];     
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 1;
}        

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    // (return some cell)
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // (Nothing yet)
}
@end

Best Solution

A couple of observations:

  1. You asked:

    When I remove the <UITableViewDataSource, UITableViewDelegate> part from

        @interface InformatieSectieViewController : UIViewController < UITableViewDataSource, UITableViewDelegate>
    

    I would expect a warning on the lines (in InformatieSectieViewController.m):

        tabView.delegate = self;
        tabView.dataSource = self;
    

    But I do not get this warning.

    Yes, that's curious. If you use UIViewController, you really should see those warnings. If you have other warnings, sometime you won't see some subsequent warnings. Or sometimes if you edit the header, don't save and/or build, you might not see the warning until you do. Or try selecting "Clean" from the "Build" menu, and then re-build, and see if you get the warning.

    Regardless, even if you don't see these warnings, you should still define it to conform to those two protocols, as it will make your code completion in Xcode easier.

    If you use a UITableViewController as the base class, though, you don't need to define it as conforming to those two protocols because it already conforms to UITableViewDataSource and UITableViewDelegate.

  2. How to add tableview as subview

    And in answer to the broader question of how to add the table view to a view with other stuff on it, rather than doing the following in loadView:

    self.view = tabview;
    

    You should, instead, be doing the following in loadView:

    self.view = [[UIView alloc] init];
    [self.view addSubview:tabview];
    

    Or if you are not building the view programmatically in loadView, but rather using a NIB or storyboard, you can then just do the following in viewDidLoad:

    [self.view addSubview:tabview];
    

    Regardless, by making sure you have another a main UIView, and your UITableView is a subview, you can therefore add other controls to that main view.

  3. View and view controller hierarchies

    Looking further into your code sample, I see that you're creating the controller, grabbing its view, adding that view as a subview, and then letting the controller fall out of scope. That's a really bad practice. If ARC, you will get exceptions. If non-ARC, you'll leak if you ever dismiss this view (not a problem, though, if this is your top level view).

    Worse, if you do this, your view controller won't receive certain events (notably rotation events, but possibly others). See WWDC 2011 - Implementing UIViewController Containment for discussion of importance of keeping your view and view controller hierarchies synchronized.

    If this is your top level controller, you should set this controller to be the rootViewController, e.g. in your app delegate:

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    
        // Override point for customization after application launch.
    
        // create your controller any way you want
    
        self.viewController = [[InformatieSectieViewController alloc] init];
    
        // if not using ARC, it should be
        //
        // self.viewController = [[[InformatieSectieViewController alloc] init] autorelease];
    
        // by defining this as your root view controller, you'll be assured that the controller
        // is now part of the controller hierarchy
    
        self.window.rootViewController = self.viewController;
    
        [self.window makeKeyAndVisible];
    
        return YES;
    }
    

    If this view controller is not the top level controller, you would create it in the presenting controller, and either do a modal or push segue from the presenting view controller, e.g.:

    - (void)transitionToNextViewController
    {
        UIViewController *controller = [[InformatieSectieViewController alloc] init];
    
        // again, if not using ARC, that line should be:
        //
        // UIViewController *controller = [[[InformatieSectieViewController alloc] init] autorelease];
    
        [self presentViewController:controller animated:YES completion:nil];
    
        // or
        // [self.navigationController pushViewController:controller animated:YES];
    }
    

    Or, in the unlikely event that you're using a custom container view controller, you'd do something like:

    - (void)addChild
    {
        UIViewController *controller = [[InformatieSectieViewController alloc] init];
    
        // again, if not using ARC, that line should be:
        //
        // UIViewController *controller = [[[InformatieSectieViewController alloc] init] autorelease];
    
        [self addChildViewController:controller];
        controller.view.frame = CGRectMake(x, y, width, height);
        [self.view addSubview:controller.view];
        [controller didMoveToParentViewController:self];
    }
    

    Any one of those three techniques will ensure that your view controller hierarchy will be synchronized with your view hierarchy. I know this seems like it's overkill, but you'll run into problems later if you don't handle it in one of these three ways.

I know there's a lot here, but I hope it helps. I apologize if it's confusing.

Related Question