UIScrollView bounds have not yet resized in "viewDidLoad" - objective-c

I have a UIViewController subclass whose view is configured in a NIB file. The view has a UIScrollView subview.
The UIScrollView takes up almost its entire NIB file but it's superview is added as a subview to a much smaller view (configured in a different NIB) - e.g. the UIScrollView is 80% the height of it's own NIB but ends up only being 10% the height of the application's window.
When I call [scrollView bounds].size.height in the viewController's viewDidLoad method, I get the height of the scrollView relative to it's own NIB, rather than the height it ends up resizing to as dictated by its superviews (e.g. 80% of the window's height rather than 10%).
If I call [scrollView bounds].size.height later on (e.g. to handle a rotation event), I get the correct value.
How can I get the correctly re-sized value initially?

Have you tried dealing with it in viewWillAppear: or viewDidAppear?

Assuming I'm following, there must be some point after the view controller is loaded at which you grab the .view and insert it into the other subview? If so then the bounds will be adjusted then. You can't get them in viewDidLoad because the view has no way of knowing what you'll do with it in the future. Even if your call to insert it as a subview is the very first thing you do after loading the view controller, it's the view accessor that'll cause the view to load and hence the viewDidLoad, all before you've actually put the view anywhere.
Since bounds has a getter and a setter, it should be key-value coding compliant, in which case you can use viewDidLoad to register an observer on bounds and do whatever it is you need to do when the bounds change.

Related

Identifying correct window frame size for filling background color

I am developing in Cocoa, and I am currently having problems with filling the background of a NSWindowController.
I understand that subclassing is the way forward if you want to customise your cocoa app. So I created a custom NSView named whiteView and added this view as a subview to my windowController's contentView; however, there are some issues with completely filling the background of the window. Can anyone explain how I can have the color cover the complete surface area of the window's frame pls. Thank you
These are the results that I have so far.
1) This is the window when I leave it as it is, notice the white color only having covered half of the window.
2)Here is the same window again when I adjust the window far to the right and bottom. The white screen seems to stretch enough so that it covers the elements.
This is how I create the custom view
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
[[NSColor whiteColor] set];
NSRectFill([self bounds]);
}
And this how I achieve plaster the view onto my window.
WhiteView *whiteBackgroundView = [[WhiteView alloc] initWithFrame:self.window.frame];
[self.window.contentView addSubview:whiteBackgroundView positioned:NSWindowBelow relativeTo:self.window.contentView];
What do I need to do to correctly allow for my window's background to be fully covered in white?
First, the simple solution is to use -[NSWindow setBackgroundColor:] to just set the window's background color. No need for a view.
If you're still interested in how to fix the view-based approach, probably what's wrong is that you haven't set the autoresizing mask of the view to make it follow the changes in the window size. For example, you could do [whiteBackgroundView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable].
However, you could also set the whiteBackgroundView as the window's contentView rather than as a subview of it. The window's content view is always kept at the size necessary to fill the window's content rect. All of the other views of your window would be subviews of the white background view. In my opinion, this is better than making it a sibling that just happens to be at the back. Using relative ordering among siblings views to achieve a particular rendering order is a hack.
Finally, there's no reason to invoke super's implementation in your -drawRect: if the superclass is NSView itself. NSView doesn't do any drawing in its -drawRect:. Also, your subclass takes over full responsibility for the entire drawn contents of its bounds, so you'd overdraw whatever super had drawn, anyway. (Also, you need only fill dirtyRect rather than [self bounds].)
While you're at it, since your class fills its bounds, you should override -isOpaque to return YES for optimization.
Update: regarding the frame of the view: if it's not going to be the window's content view, then you want to set its frame to be its prospective superview's bounds. So, you should have used self.window.contentView.bounds if you wanted whiteBackgroundView to fill the content view.
More generally, if you want the content rect of a window, you would do [window contentRectForFrameRect:window.frame]. But if a view is going to be a window's content view, there's no need to set its frame to anything in particular. It will be resized automatically.
Update 2:
To transfer the view hierarchy from the original content view to the new content view (when you're making the white background view the content view):
NSArray* subviews = [self.window.contentView.subviews copy];
[subviews makeObjectsPerformSelector:#selector(removeFromSuperview)];
[whiteBackgroundView setSubviews:subviews];
[subviews release];
(Written for manual retain-release. If using ARC, just drop the -release invocation.)
Regarding the frame to use, as mentioned in the first update: keep in mind that the view's frame should be expressed in the coordinate system of its superview. So, as I said, self.window.contentView.bounds would work if you're putting the new view into the content view. The window's frame and content rect are in screen coordinates. They would be completely incorrect for positioning a view.

Is there a function to know the useableSize of a view?

It must take into account:
statusBar (which can be 40 points if you have hot spot)
TabBar
NavigationBar.
Basically at viewDidLoad I see that my view has a size of 320*480.
I wonder where did iOS decide that as the screen size of my screen. I use UIStoryBoard.
So, on viewDidLoad, I intended to resize that.
I am not even sure if this is the right approach.
Note: the issue I am facing doesn't seem to happen if I do not use storyBoard.
At viewDidLoad, when I use XIB, the content of self.view is correct, namely 416, instead of 480, due to UInavigationController and UITabBar
Try overriding the UIViewController viewWillLayoutSubviews method. The view's frame will be set by then.
In the viewWillLayoutSubviews method, the view controller's main view is the size you need to know. It has been adjusted for status bars and nav bars and tools bars and tab bars. It also takes into account orientation. There is no single method where you can ask what the size will be. Besides, there is no need to ask such a question. Create all the subviews you want in viewDidLoad. But lay them out based on the view's size in viewWillLayoutSubviews.

UIView subviews's layout update

In the viewWillAppear delegate method, I'm setting subview's frames with rects ( location and size).
Now, There is one subview whose content will grow with user actions.
So, I need a callback in that superview controller ( The One I am talking on first sentence) , to update the layout. In this callback, I can find out how much the size increased, and then set other subviews frame rects too.
It can be done through calling setNeedsLayout method on the view, but it requires layoutSubviews to be overridden on the UIView. Please note that it is not a delegate method, I need to have a custom view, Which I don't want to do,
Is there any delegate method for me to update the layout in the view's controller ?
UPDATE:
This need to be support on iOS 4.2
There are two UIViewController methods of possible relevance:
-viewWillLayoutSubviews
-viewDidLayoutSubviews
You can probably guess when they're called.

setNeedsDisplay and subviews

From my understanding, setNeedsDisplay only affects the view it's called on. Is there a simple way to say "update this view and all its subviews, recursively?"
In response to the comments, here's my situation: I've got a custom view
#interface ContainerView : UIView
this view does not implement drawRect. In my xib there's an instance (called container) of the ContainerView which has some (custom) subviews added to it. When in the code I say
[container setNeedsDisplay]
I expect these subviews to update. Where am I wrong?
Ok, UIView draws itself when its first displayed. CALayers do not. Calling setNeedsDisplay on a UIView marks it as dirty, this automatically redraws all SubViews as well (calling drawrect on all subviews). Calling setNeedsDisplay on a CALayer doesn't have the same effect, it wont redraw sublayers. Hope this helps.
Regards
Ref
iOS 7 Programming Pushing the Limits By Rob Napier, Mugunth Kumar
UIView Class Reference
The UIView class defines a rectangular area on the screen and the interfaces for managing the content in that area. At runtime, a view object handles the rendering of any content in its area and also handles any interactions with that content
setNeedsDisplay
Marks the receiver’s entire bounds rectangle as needing to be redrawn.
Note: If your view is backed by a CAEAGLLayer object, this method has no effect. It is intended for use only with views that use native drawing technologies (such as UIKit and Core Graphics) to render their content.
The subviews are inside the bounds of the view, so the view will ask it's subview what to display.
Have you try and encounter some case that go agains this definition?
If you are implementing your own UIView subclass you need to handle all the display yourself in drawRect:

"refresh" view on device rotation

I have my view set up in viewDidLoad. All the different frames and such of the subviews have been defined relative to self.view. Therefore it doesn't matter what size self.view is the subviews will always shrink or expand to fit (as it were).
So when I rotate my device I want the view to rotate (easy enough with shouldAutoRotateToInterfaceOrientation:...) but the subviews stay the same shape.
Calling [self viewDidLoad]; makes all the elements fit, but puts a new layer on top of the previous layout (which is obvious... but i'm just saying to explain what I mean).
Is there any way to refresh the frames of the subviews or something? I don't know what other people do to be honest. Am I going to have to put ALL of my views into the .h file as properties and do everything manually on didRotate...?
You have three options:
If autoresizing masks are good enough to position your views, assign the correct autoresizing mask to each subview when you create them.
If autoresizing masks are not sufficient, override willAnimateRotationToInterfaceOrientation:duration: and reposition your subviews in that method. I would create a custom method that takes the orientation as a parameter and is responsible for laying out all subviews. You can then call this method from willAnimateRotationToInterfaceOrientation:duration: and from viewDidLoad.
You could also create a custom UIView subclass and make your view controller's view an instance of this class. Then override layoutSubviews to position all subviews depending on the view's size. This approach implies that your custom view manages its subviews instead of the view controller.