I have a UIViewController which loads a subview from a nib and adds it to a subview in its view hierarchy. Now this is working all fine in the iPhone story board, but in the iPad one although I do see the view but as the viewWillAppear is not called the UIImageView on the view is not initialised.
Here's the code from the main viewcontroller (the one that loads the subview)
SubViewController *controller = [[SubViewController alloc] init];
NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:#"x"] owner:controller options:nil];
UIView *subView = bundle[0];
[self addChildViewController:controller];
if (isPhone)
{
CGRect frame;
frame.origin.x = 0;
frame.origin.y = 0;
frame.size = self.scrollView.frame.size;
view.frame = frame;
[self.scrollView addSubview:view];
}
else
{
view.frame = self.viewSlot.frame;
[self.viewSlot addSubview:view];
}
The only difference is on the iPhone it is added to a UIScrollView whereas on the iPad it is added to a UIView.
viewSlot and scrollView are both outlets which are properly initialised and work. Even on the iPad I do see my view (I've changed its background colour) it's only the initialisation (viewWillAppear) that does not run.
After a bit of playing I've managed to get to the bottom of this. First of all I chose to initialise the controller in a different manner:
SubViewController *controller = [[SubViewController alloc] initWithNibName:[#"X" stringByAppendingString:idiom] bundle:nil];
I've got an exception that revealed the problem: the view outlet wasn't set. Lesson of this story: don't just alloc - init a controller, use the initWithNibName initialiser. It's still weird that due to some not yet discovered difference between iPhone/iPad the earlier approach still worked on the iPhone.
Related
I wanted to post a gif but apparently I don't have enough reputation. Oh well, whatever; I was using UIPageViewController, but for some reason I decided to go with a more manual solution by using UIScrollView and adding the views of UITableViewControlllers to the corresponding offsets (pages). I have 4 UItableViewControllers on each page (the views of table view controllers) and all of these are added to the container view controller (which has the UIScrollView) as child view controllers.
The actual problem is when I made the switch, table views began refusing to go all the way down and part of the final table view cell stays trimmed by the end of the screen when the scrolling ends.
So, wanted to ask if anyone came across something like this before of know how to get rid of this. I know I could always use a library, but I want to learn. Here is some code:
_containerScrollView = [[UIScrollView alloc] initWithFrame:self.view.frame];
_containerScrollView.contentSize = CGSizeMake(_containerScrollView.frame.size.width * 4, 0.0f);
_containerScrollView.pagingEnabled = YES;
[self.view addSubview:_containerScrollView];
UITableViewController *vc1 = [[UIViewController alloc] init];
UITableViewController *vc2 = [self.storyboard instantiateViewControllerWithIdentifier:#"trendingViewController"];
UITableViewController *vc3 = [self.storyboard instantiateViewControllerWithIdentifier:#"placesViewController"];
UITableViewController *vc4 = [self.storyboard instantiateViewControllerWithIdentifier:#"favoritesViewController"];
self.rallyViewControllers = [NSArray vc1, vc2, vc3, vc4, nil];
[self addViewControllers];
Other methods;
- (void)addViewControllers{
if (self.rallyViewControllers == nil) {
return;
}
for (int i = 0; i < [self.rallyViewControllers count]; i++) {
UIViewController* viewController = [self.rallyViewControllers objectAtIndex:i];
[self addChildViewController:viewController];
[viewController didMoveToParentViewController:self];
[_containerScrollView addSubview:viewController.view];
}
}
This is called in viewDidLayoutSubviews
- (void)configureFrames{
if (self.rallyViewControllers == nil) {
return;
}
CGFloat width = _containerScrollView.frame.size.width;
CGFloat height = _containerScrollView.frame.size.height;
for (int i = 0; i < [self.rallyViewControllers count]; i++) {
UIViewController *viewController = [self.rallyViewControllers objectAtIndex:i];
viewController.view.frame = CGRectMake(i * width, 0.0f, width, height);
}
}
I should state upfront that I didnt completely understand your description of
"table views began refusing to go all the way down and part of the
final table view cell stays trimmed by the end of the screen when the
scrolling ends."
My answer is based on an issue I faced before. My setup is a uitableview in the storyboard container view ( without any parent scrollview)
I faced this issue where part of the tableview was cut off and I could not see about 5 bottom rows.
Turns out I did not have any constraints setup between the parent container view and the tableview.
To determine if your tableviews are rendering fine, get your project running in XCode and then press on the below button
This button will then pause your app and give you a visual stack of the the different views that are currently rendered in your app. You can then see if any of the children ( in your case tableviews) are rendered outside the frame of the parent view in which case that portion will not be visible. This indicates that you either dont have constraints (or) the current constraints you have are incorrect.
I got it. The problem was when I added the view controllers views to the scroll view, they returned dimensions as if there was no navigation bar. But they were still positioned under the navigation bar, which caused total view to have 64 pt more height than the screen could show. when I manually subtracted 64 pt from views, and it was fixed. But since that is a very crude way of doing this, I then tried to fix it by fiddling with auto-layout, which ended up fine.
I have almost lost what's left of my sanity today ladies and gentelmen. Interface Builder is about to take it away.
I am new in iOS dev and going through a book right now with all kinds of different tasks.
What I am trying to do is to call a view using a double tap. This isn't about gesture recognizer and the taps, it is purely about the outlet.
1) I have a xib file, where a small menu is drawn.
2) Upon a double tap gesture I want this menu to come out.
This is the way I got it working for me:
- (void)displayPanel:(UIGestureRecognizer *)gesture{
CGRect frame = CGRectMake(0, self.window.frame.size.height-100, self.window.frame.size.width, 90);
NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:#"colorMenuView" owner:self options:nil];
UIView *colorView = [nibContents lastObject];
[colorView setFrame:frame];
[self.window addSubview:colorView];
[self.window setNeedsDisplay];
}
A nice little menu is drawn at the bottom.
But I want to do that not with the NSBundle's last object, I don't think it is a good approach. I want to do this by making an outlet connection to the UIView that holds my menu.
(I would've posted some pictures but I haven't got enough reputation yet)
When I try to make a connection I can't, the outlet isn't highlighting
When I change the Class of the View from UIView to CustomView I can connect the subviews, such as labels and buttons, which does seem to be how it should work. I still cannot connect the view though.
Now I add another standard view to the IB. And try to make a connection. It works! And if I try to connect the Custom View, it doesn't.
Now I change the Custom View back to standard. Neither of views can be connected.
Ok lets change one of the views to the Custom and connect another one. It works.
Let's do the implementation of another way of calling the menu now:
- (void)displayPanel:(UIGestureRecognizer *)gesture{
CGRect frame = CGRectMake(0, self.window.frame.size.height-100, self.window.frame.size.width, 90);
[[NSBundle mainBundle] loadNibNamed:#"colorMenuView" owner:self options:nil];
UIView *menu = self.menuView; // the outlet
self.menuView.frame = frame;
[self.window addSubview:menu];
[self.window setNeedsDisplay];
}
I have no errors for the chunk above. But the menu is still not shown. And it's nil, so it was never actually initiated.
Please help me to understand 2 things:
1) why IB does what it does?
2) why is the menu I finally got connected is nil?
In your second sample you set owner param of loadNibNamed to be self, but doesn't seem to have properly configured File's owner placeholder in the xib file. This results in nil outlet reference.
You should setup File's owner placeholder of colorMenuView xib to be an instance of specific class, eg. ColorMenuViewController (using Identity inspector). This will let you connect view outlet of used view controller with your top level view. This view can be then loaded using one of following approaches:
// 1
ColorMenuViewController* vc = [[ColorMenuViewController alloc] initWithNibName:#"colorMenuView" bundle:[NSBundle mainBundle]];
UIView* colorView = vc.view;
// 2
ColorMenuViewController* vc = [ColorMenuViewController new];
[[NSBundle mainBundle] loadNibNamed:#"colorMenuView" owner:vc options:nil];
UIView* colorView = vc.view;
Your base class, which implements gesture recognizer actions, can be used as the File's owner as well.
I have a custom split view controller, designed to work as much like apple's as possible.
I use this split view in three different places. Two of these places, the master is a view simply a UIViewController with a UIView attached, and created programmatically.
The third example however is a UIViewController with a XIB.
The problem is that the first two split views adjust their widths correctly. However the screen with the XIB does not. I'm not doing anything fancy, just trying to set the content view of my master with a view that is passed in to it. And setting the autoresizing masks as such:
// create container views
masterView = [[VS_MasterView alloc] init];
masterView.frame = CGRectMake(0, 0, masterViewWidth, self.view.frame.size.height);
masterView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
[self.view addSubview:masterView];
// get the views
UIViewController *masterViewController = (UIViewController *)[_viewControllers objectAtIndex:0];
UIViewController *detailViewController = (UIViewController *)[_viewControllers objectAtIndex:1];
// set their bounds
[masterViewController willMoveToParentViewController:self];
[self addChildViewController:masterViewController];
[masterViewController didMoveToParentViewController:self];
masterViewController.view.frame = masterView.bounds;
masterViewController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[masterView addSubview:masterViewController.view];
is there some sort of special case with XIB files and resizing them in this situation? I don't feel like there should be.
thanks
This is seemingly a bug, but i'm wondering if anyone can think of a workaround.
On iPad, you present a view controller as a UIModalPresentationFormSheet. This view controller is extending UITabBarController and has enough controllers to automatically display the "more" tab bar button. Once you tap on the more button it will display the list correctly, but as soon as you tap on 'edit' it presents the edit view larger then the actual form sheet (cropped inside the form sheet), causing the content to be out of view, including the toolbar with the "done" button. The only way to dismiss is to force quit the app.
To verify that it's not something specific to my app I started a single view project, and presented a simple modal view. This modal view controller extends UITabBarController and has the following init method:
- (id)init {
self = [super init];
if (self) {
self.modalPresentationStyle = UIModalPresentationFormSheet;
NSMutableArray *controllers = [NSMutableArray array];
for (int i = 0; i< 15; i++) {
UIViewController *vc = [[UIViewController alloc] init];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
vc.title = [NSString stringWithFormat:#"view %i", i];
[controllers addObject:nav];
}
self.viewControllers = controllers;
}
return self;
}
I also tried adding the modalPresentationStyle to moreNavigationController with no change.
Good day, dizy.
A nice challenge you've made. Here is a solution, maybe it's a bit hardcore, but it works.
I've done as you wrote – subclassed UITabBarController and presented it as a modal view controller. And run into the same problem. When tapping "edit" button in "More" screen UITabBarCustomizeView appears and it's frame is inadequate.
So I've done the following. I've made MyModalTabBarVC a delegate of itself and implemented tabBarController:willBeginCustomizingViewControllers: method:
- (void)tabBarController:(UITabBarController *)tabBarController
willBeginCustomizingViewControllers:(NSArray *)viewControllers
{
UIView *modalView = self.view;
CGRect bounds = modalView.bounds;
UIView *customizationView = [[modalView subviews] objectAtIndex:1];
UIView *customizationNavBar = [[customizationView subviews] objectAtIndex:0];
CGRect navBarFrame = [customizationNavBar frame];
navBarFrame.size.width = bounds.size.width;
customizationNavBar.frame = navBarFrame;
customizationView.frame = bounds;
}
So when this method is called UITabBarCustomizeView is already created. And a wrong frame can be changed manually. If you log po [self.view subviews] at the start you'll get:
(id) $1 = 0x06c6a940 <__NSArrayM 0x6c6a940>(
<UITransitionView: 0xd744ab0; frame = (0 0; 540 571); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0xd744b50>>,
<UITabBarCustomizeView: 0x6c5e570; frame = (0 -384; 768 1004); animations = { position=<CABasicAnimation: 0x6c569a0>; }; layer = <CALayer: 0x6c618d0>>,
<UITabBar: 0xd744110; frame = (0 571; 540 49); autoresize = W+TM; layer = <CALayer: 0xd742b80>>,
)
PS. This solution doesn't fix animation. As you can see from log, corrupted animation is already created and charged. I hope that canceling it and adding a new one, appropriate, will not be a problem.
The modal view's viewController must be causing the glitch.
You could try to:
hide the tab bar while editing and un-hiding it when the done button
is pressed.
create a custom toolbar for the view controller, this could be done
with a UIView, so that it's set always be on top of the view.
resize your individual tabs. Best way to do this is to create your
own custom tab bar with a UIViewController and IBActions connected
to UIButtons with IBOutlets.
Why would you have so many tabs in the modalPresentationStyle? I personally would use push segue instead.
Try pushing to a new set of view controllers that are under their own navigation controller as well. There would be more room for the tab bar. To get back, put a back button in the toolbar that pops the push, or pushes back to the original.
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.