Can't add UITableView as subview - ios

Xcode 4.3/iOS 5.1/Storyboards.
I have a UIViewController with a UITableView in the storyboard
when initializing that view
-(void) loadView {
UITableView *tTableView = [[UITableView alloc] initWithFrame:CGRectMake(20,60, 300, 300)
style:UITableViewStyleGrouped];
[tTableView setDelegate:self];
[tTableView setDataSource:self];
[tTableView setBackgroundView:[[UIView alloc] initWithFrame:self.tableView.bounds]];
self.tableView = tTableView;
self.view = tTableView; // <--- THIS WORKS
}
This works, but the TableView is stretched over the entire width of the screen. I'd like it to be of a certain size as I'm using it for a login username/password control.
I was told to add it as a sub-view, but this crashes my app with some kind of recursive calling into the same "loadView" method.
-(void) loadView {
UITableView *tTableView = [[UITableView alloc] initWithFrame:CGRectMake(20,60, 300, 300)
style:UITableViewStyleGrouped];
[tTableView setDelegate:self];
[tTableView setDataSource:self];
[tTableView setBackgroundView:[[UIView alloc] initWithFrame:self.tableView.bounds]];
self.tableView = tTableView;
[self.view. addSubview: tTableView]; // <-- THIS CRASHES
}
Why is this crashing? How can I add the UITableView to a subview where I can control its width and not have it occupy the entire width of my screen?

you need to self.view = tTableView or self.view = [UIView alloc] initWithFrame.... and then add the table view. Overriding the loadView method means you are going to create the self.view. So for your case, create an UIView, set it to self.view and then add your tableView as subview of self.view

You use the loadView method, which means you define the view by yourself.
So self.view is not defined and the controller complains that its view is not defined when it should display itself.
Either put your interface in a xib (or storyboard) and bind those values in viewDidLoad, or instanciated the self.view by hand in loadView.

Related

iOS - Why setting and displaying subviews in initWithNibName doesn't work?

I have a simple xib file which its layout is:
Which the pale green UIView is referenced to an IBOutlet view_content in my UIViewController class. Then I did the following in initWithNibName:bundle: of my ViewController.m:
self = [super initWithNibName:nibNameOrNil bundle:nil];
if (self) {
self.view_a = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
self.view_a.backgroundColor = [UIColor purpleColor];
[self.view_content addSubview:self.view_a];
self.view_b = [[UIView alloc] initWithFrame:CGRectMake(150, 150, 100, 100)];
self.view_b.backgroundColor = [UIColor blueColor];
[self.view_content addSubview:self.view_b];
self.view_c = [[UIView alloc] initWithFrame:CGRectMake(200, 200, 100, 100)];
self.view_c.backgroundColor = [UIColor greenColor];
[self.view_content addSubview:self.view_c];
}
return self;
I expected there will be 3 small squares, each with different colors as declared in the code above, but they did not show up which the view is loaded and showed. But instead if I move the addSubView: calls into viewDidLoad, they are displayed as expected. I tried to test if view_content is nil in the if-segment and seems it is nil at that time. I don't understands why this is happening and how it works, isn't the view and its components are supposed to be loaded from the xib?
Thanks for any help!
At the point initWithNibName:... is called, the view itself is not yet loaded. This is an optimization (lazy loading) that defers loading the view until it is actually needed. The view is actually loaded when viewController.view is first-time accessed, and that's when viewDidLoad is called.
Some people force the view controler to load its view by calling [viewController view] (or [self view] if it's within the view controller's class).
The reason is simple. View is not loaded at that time when you are initializing the controller means initWithNibName. So best place is viewDidLoad where your all outlet has been assigned.

How do you add a UICollectionViewController to a UIScrollview within a UIViewController?

I am trying to use this as a starting point to learn about collection view: Creating a UICollectionView programmatically
I have a viewDidLoad which has a UIScrollView created like so:
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0, 64.0,
screenWidth, screenHeight)];
How do I add in the UICollectionViewController as the following below doesn't work:
ACollectionViewControllerr *vc = [[ACollectionViewController alloc] init];
[self.view addSubview:vc];
Do you need a UIScrollView to scroll down or does uicollectionview have that functionality already?
UICollectionView inherits from UIScrollView, so you don't need to add an UIScrollView. Just add the UICollectionView to the UIViewController directly.
If you use UICollectionViewController then you don't require UIViewController.
See the documentation of UICollectionView
You're trying to add a ViewController to a view, which will not work. Try the following instead:
ACollectionViewControllerr *vc = [[ACollectionViewController alloc] init];
[self.view addSubView:vc.view];
[vc didMoveToParentViewController];

Xibs and Scrollview

I'm still pretty new at this, so bear with me. I thought I would be able to load a Xib in a ScrollView, as I have seen apps that seem to do this, but we're are talking two different classes. But I'll ask anyway - Is there any practical way to have a scrollView with a static Xib over the top, where buttons defined in the UI don't move while a view underneath does. I'm sure it's easily doable in cocos2d, but for what I want to do, it's a bit overkill.
--- Edit ---
At the risk of embarrassing myself, I tried both possible solutions. Adding a button grammatically adds a button that moves when I scroll. Adding the nib seems to keep the scroll screen from scrolling. Here's the code, without trying to add any buttons everything works fine.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(#"View Loaded");
[mdm setMapSetupInfoWithRows:60 columns:90 cellSize:32];
[mdm initMapDataWithOriginsUsingCenter:TRUE];
NSLog(#"MapViewContoller.mapArrayCount = %d",[[mdm mapArray]count]);
// create the MapView with the screen size create by MapDataManager
mapView = [[MapView alloc] initWithFrame:[mdm mapRect]];
// Create the UIScrollView to have the size of the window, matching the window (screen) size
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:[mdm windowRect]];
[scrollView setBounds:[mdm windowRect]];
// Tell the scrollview how big it is and set other options
[scrollView setContentSize:[mdm mapRect].size];
[scrollView setBounces:NO];
[scrollView setMinimumZoomScale:.5];
[scrollView setMaximumZoomScale:10];
[scrollView setDelegate:self];
[scrollView setBackgroundColor:[UIColor darkGrayColor]];
//add the MapView as a subview of the scrollView
[scrollView addSubview:mapView];
//add the scrollView to the current one....
[[self view] addSubview:scrollView];
[[NSBundle mainBundle] loadNibNamed:#"MapViewController" owner:self options:nil];
[self generNewMap];
}
Anything else I'm trying to do wrong? after looking at this more it does seem doable.
You should set things with a hierarchy like this.
UIViewController
UIScrollView
Static Buttons etc
Then in interface builder, or code, just add the static buttons etc to the self.view.
I do everything in code so it would look something like
-(void)viewDidLoad {
//add scrollview
appScroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
appScroll.pagingEnabled = YES;
[appScroll setCanCancelContentTouches:NO];
appScroll.bounds = CGRectMake(0, 0, 320, 480);
appScroll.contentSize = CGSizeMake(1600, 480);
[appScroll setScrollEnabled:YES];
appScroll.bounces = NO;
appScroll.showsHorizontalScrollIndicator = NO;
appScroll.showsVerticalScrollIndicator = NO;
appScroll.clipsToBounds = YES;
appScroll.delaysContentTouches = YES;
appScroll.center = (CGPoint){ 160, 240 };
[appScroll setBackgroundColor:[UIColor darkGrayColor]];
[self.view addSubview:appScroll];
//back
backBut = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"back.png"]];
backBut.center = (CGPoint){ 40, 430 };
backBut.transform = CGAffineTransformMakeScale(0.3,0.3);
[backBut setUserInteractionEnabled: YES];
[self.view addSubview: backBut];
}
The trick is to specify the owner when loading the XIB. For example:
Define an IBOutlet on your UIViewController
#property (nonatomic, strong) IBOutlet UIView* myScrollViewContent
Create a XIB, specify the owner as your UIViewController class
Wire the components defined in the XIB to the outlets defined in the UIViewController class
Then in code do this:
//Because the owner is 'self' the bundle loader will inject any properties defined . .
//. . . . and wired in the XIB to the owner's outlets
[[NSBundle mainBundle] loadNibNamed:#"MyXibName" owner:self options:nil];
//Now do something with self.myScrollViewContent - ie add it to the scroll view.
Custom Scroll-view sub-class
If you wanted should be able to use the same approach by creating a cusom scroll-view subclass and specifying an outlet there. . . The UIView would be loaded directly onto the sub-class, then. . . (you'd still have to add it as sub-view).
For more complex requirements, I personally like to build my views with pure code, but it is possible to arrange things neatly with XIBs.

Adding viewController views to UIScrollView

Despite the number of similar posts on this, I still am having trouble making this work correctly.
I have a two UIViewControllers which I have designed in IB with UILabels and imageviews, etc.
I would like to add these to a scroll view so they can be paged between.
I have outlets defined connecting each of the scrollViewcontrollers with their elements in IB, however, the controllers themselves, I am programatically creating from within the master view controller in viewDidLoad. After creating them, I assign some values from the master view controller than add it as a subview to the scrollViewController. However nothing displays. From the debugging i have done, when I set a break point after assigning the values, I can see that the elements are nil. They should be loaded from the xib file, but it does not seem to be read. Even as a test, I tried initializing one of the elements (eg. UILabel) and setting it to that sub view controller, but it nothing would display. Here is some code to explain a bit better.
In the master view controller
detailControllerA *tempA = [[detailControllerA alloc] initWithNibName:#"detailOverviewA" bundle:nil];
self.overviewA = tempA;
[tempA release];
self.overviewA.someLabel.text = #"Some Text";
detailScrollView.contentSize = CGSizeMake(scrollWidth, scrollHeight);
detailScrollView.pagingEnabled = YES;
[detailScrollView addSubview:self.overviewA.view];
In the detailControllerA implementation I set the frame in loadView:
-(void)loadView {
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 90)];
self.view = view;
[view release];
}
I also have the labels, etc defined with IBOutlets in detailControllerA.h and implemented to the elements in the xib file.
Any ideas why the xib is not loading correctly when created programatically?
Try in this way:
NSUInteger scrollContentCount = 0;
for (NSUInteger arrayIndex = 0;
arrayIndex < [contents count];
arrayIndex++) {
// set scorllview properties
CGRect frame;
frame.origin.x = self.mScrollView.frame.size.width * arrayIndex;
frame.origin.y = 0;
frame.size = self.mScrollView.frame.size;
myScrollView.autoresizingMask = YES;
// alloc - init PODetailsView Controller
myController = [[MyController alloc] initWithNibName:#"MyController" bundle:[NSBundle mainBundle]];
[myController.view setFrame:frame];
// add view in scroll view
[self.myScrollView addSubview:myController.view];
scrollContentCount = scrollContentCount + 1;
}
// set scroll content size
self.myScrollView.contentSize =
CGSizeMake(self.myScrollView.frame.size.width * scrollContentCount,
self.myScrollView.frame.size.height);
}
Don't release the content controller object which is in for-loop.
Set your scrollView contentSize according to your requirement.
Hope this would be helpful to you.
Interestingly enough, the -(void)loadView was the problem.
Since the view is being generated in interface builder (with the frame settings), when loadView fires, it was creating a new view, blowing away the one created in interface builder.

UINavigationController and UITableView frame issue

self.tableView = [[UITableView alloc] initWithFrame:self.view.frame style:UITableViewStylePlain];
How do I get the proper size from the view, when I have a UINavigationController and a UITabBarController?
In order to get around it I have to do:
CGRect frame = self.view.frame;
frame.origin.y -= [UIApplication sharedApplication].statusBarFrame.size.height;
tableView = [[UITableView alloc] initWithFrame:frame style:UITableViewStylePlain];
I always have to manually set the height of my views/tableviews in order to fit inside the UINavigationController and it's really annoying
This seems kinda strange, why would it not subtract the statusbar height auotmatically? It's the default style, not transparent statusbar.
Thanks
UPDATE: If I alloc the tableView AFTER the init method, just setting the frame to self.view.frame works fine. Wuzzup wit dat?
Setting the frame of the UINavigationController before the init method wouldn't work because the UINavigationController hasn't been loaded and displayed yet. The frame is only set after the controller and its view has been loaded.
You should be setting up your table (and all similar initializations) in the viewDidLoad method of the UINavigationController. If you do that you won't see any problems of this sort.

Resources