Is there a way for a custom NSView to know whether it is embedded in a NSScrollView or not?
I am creating a custom NSView for displaying some content.
When my view is placed in a window or another view, its size is fixed and the content is clipped to available size.
When my view is placed in a NSScrollView its size must be adjusted according to content so it can be scrolled if necessary.
I know I can add a member in my view that specifies the NSScrollView that hosts my view and set this member manually in code, but I was wondering if there is another way?
You didn't check the methods of NSView?
#property(readonly, strong) NSScrollView *enclosingScrollView;
or
var enclosingScrollView: NSScrollView? { get }
The nearest ancestor scroll view that contains the current view.
If the current view is not embedded inside a scroll view, the value of this property is nil. This property does not contain the current view if the current view is itself a scroll view. It always contains an ancestor scroll view.
Related
I got an NSTabView inside an NSView. That NSView is in an NSClipView, which in turn is in an NSScrollView. It looks like this (NSTabView in green, and NSView in red):
As you can see, the content of NSTabView gets clipped, and no scrollbars appear (since the view doesn't expand beyond the window).
How can I make NSTabView take up as much space as it needs (doesn't clip out), and expand the NSView with it? Then, NSScrollView can deal with the scrolling of the overgrown NSView.
Since my content changes dynamically, I don't want to put in some hard values for the width and height of NSTabView's superview.
This is only part of it; here's now the overall hierarchy looks:
I want the NSTabView's superview to be scrollable instead of clipping out, like this:
The setup I'll describe is for an NSTabView that will pin to the top, left, and right sides of the scroll view. Note the NSTabView could be replaced with any other NSView, the setup is the same.
Starting with you putting a scroll view into the xib/storyboard, you'll have NSScrollView -> NSClipView -> NSView (document view). Constrain the NSScrollView to the edges of the window. Drop your NSTabView onto the NSView instance. Add constraints so that your NSTabView edge constraints equal the NSView and define a height constraint either explicitly or implicitly with other content inside the tab view that defines it.
Personally I like to change the NSView instance (document view) layout to use constraints, by default it uses autoresizing masks and this makes it difficult to keep it in sync with the NSTabView. We want the document view to be pinned to the top, left, and right sides of the scroll view. The size of this view is what determines the scrollable region so we want it to be the same size as the NSTabView so the height of the tab view will determine the scrollable area.
To change this, select the document view, and under the Size Inspector we want to change the "Layout" type to "Automatic".
Lastly, add constraints to the top, left, and right and you should be good to go.
If you want the scroll view to start at the top rather than the bottom, you should subclass the document view and override isFlipped:
class FlippedView: NSView {
override var isFlipped: Bool { true }
}
I'm trying to create a scrollable view with multiple controls inside it. For this I'm wrapping a Custom View control inside a NSScrollView and set the size of the custom view to about the same as the scroll view.
However if I place other controls inside the custom view in Interface Builder they don't appear in the custom view when running. Why is that?
If I place a button in a Custom View that is not wrapped in a NSScrollView it works but I want the custom view to be scrollable in case the window height is too small to show all controls.
After dragging in an NSScrollView in Interface Builder, look at the outline view in the xib window. You'll see that you now have a Bordered Scroll View, which contains a ClipView, which then contains a NSView.
a. Make sure your Custom View have been added as a subview to the NSView. If you add them at a higher level, you wont get the behavior you want.
or
b. Another option is to simply change the class of the NSView to your custom view class. Select the View that is inside the BorderedScrollView->ClipView, then click the 3rd tab from left (in the upper right of Xcode window) where you can type in your custom class name in the field labeled "Class".
I'd like to center an NSCollectionView in its enclosing scroll view (horizontally & vertically).
How can I determine the ideal size of the collection view so that all item views fit?
It appears that a collection view will always resize itself to fill the entire document view when enclosed in a scroll view.
Also, if not enclosed in a scroll view, the collection view won't change its frame when new items are added.
I would suggest you to customise the NSCollection View. All you can do is override "drawRect" Method of NSCollectionView and inset its own rect. Below is what will fix your issue
Steps to implement:
Create a Class "NSModifiedCollectionView" Where it inherits from NSCollection View and do the following.
#interface NSModifiedCollectionView : NSCollectionView
#end
#implementation NSModifiedCollectionView
- (void) drawRect:(NSRect)dirtyRect
{
//You can inset more than 9.0 as per your needs but this will make you fit exactly
NSRect insetRec = NSInsetRect(dirtyRect, 9.0, 9.0);
[super drawRect:insetRec];}
2: Drag and Drop a NSCollectionView from you object list to the Xib and modify its custom class to "NSModifiedCollectionView". Now Run and check it should work as you expect.
Is there a simple way to get an NSScrollView to adapt to its document view changing size when using auto layout?
I have tried to call both setNeedsUpdateConstraints: and setNeedsLayout: on the document view, the clip view and the scroll view, without any results.
fittingSize of the document view reports the correct size.
The problem is that the document view, which holds subviews, is not re-sized when the subviews change their size, even if they call invalidateIntrinsicContentSize. The contents of the document view are hence clipped to the original size of the document view as they grow. The document view is created in a nib and set as the scroll view's document view in an awakeFromBib method.
I was hoping that the document view frame would automatically be adjusted when its fittingSize changes, and the scrollbars updated accordingly.
NSPopover does something similar - provided that the subviews of the content controller's view have the constraints set right and various content hugging values are high enough (higher than the hidden popover window's hight constraint priority, for one).
The problem of course is that when adding the document view, Cocoa will automatically create some hard constraints in the view that the document view is inserted into, i.e., the clip view.
So the answer to my own question is simple, just use:
// Assume self.docView is an IBOutlet populated with
// an NSView subclass
self.docView.translatesAutoresizingMaskIntoConstraints = NO;
before you add the document view to the scroll view:
self.scrollView.documentView = self.docView;
Now, since there are no auto-generated constraints on the layout of the document view in the clip view, you will need to add them explicitly. Otherwise, the doc view's contents will just be rendered at their intrinsic size in the upper left corner of the scroll view.
I subclassed UITableViewController and called it FeaturedGamesViewController. Ok, I also have a navigation controller to which I added this FeaturedGamesViewController as the root. All pretty standard stuff that you do in the App Delegate.
Also note that there is no NIB file created for FeaturedGamesViewController. Just subclassing UITableViewController and calling initWithStyle sets the style of the table and sets the dataSource and delegate and size automatically. Data Source and Delegate are obviously set to FeaturedGamesViewController.
- (id)init
{
// Call the superclass's designated initializer
[super initWithStyle:UITableViewStyleGrouped];
}
OK, You see that I have set the table size to "Grouped". In Landscape view on my iPad it has about 20 pixels of space to the top, left and right (Sorry can't post screen shot because I am new here and the system won't let me until I have accumulated a certain number of points)
I DO NOT want that! This is Landscape so I expect it to fill up the all the space between the navigation bar and the tab bar below. What is worse is that I have faked a grid with a Custom UITableViewCell but the space to the left and right make it so that if you click on that space, the entire row is selected thus betraying the sense that this is a grid.
Now I figure I should resize the table view in viewDidLoad or something but I don't know how. I cannot do initWithFrame because of potential memory leaks (and possibly resetting dataSource and delegate and autoresizeMask properties that were already set) so there must be a setter or something to reset the origin of the tableview to just beneath the Navigation bar and filling up the entire screen with size 1024X748. How do you do dynamically reset the size of the table view?
Then I got really frustrated and I decided to do it via a Nib file, that way I can set the the orientation to landscape and set simulated Navigation and Tab bars and fill the rest of the space with the table view. This worked! If you are curious how to create a table view with a Nib after you have subclassed UITableViewController WITHOUT a nib, here is how:
http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/TableView_iPhone/CreateConfigureTableView/CreateConfigureTableView.html#//apple_ref/doc/uid/TP40007451-CH6-SW10
Go to the paragraph right before "Creating a Table View Programmatically".
OK, when I did that, my landscape view of the "grid" looks filled up the entire space between the navigation bar at the top and the tab bar at the bottom just like I wanted.
I was fiddling with this some more and I found out that in my "non nib" version (the problematic one), I had set the table style to "grouped". When I changed it to "plain", it worked!!! But here is the thing though: In the nib version, "grouped" or "plain" gives the correct layout with the table occupying the whole space. So what gives?
Sorry for the long question but I guess what I am asking is:
1) How do you programmatically reset the size of the table view without introducing potential memory leaks or affecting other properties already set for you (ex: dataSource, delegate, autoResizeMask - these are set for you just because you subclassed UITableViewController)?
2) How do you do that for any view in general?
3) Why does "plain" style fill the layout as desired whereas "grouped" style gives the weird layout. Note that it this is not a problem in the Nib version.
Thanks for your patience!
Answer for (2), and hence for (1):
A UIView's frame is in a local coordinate system of its superview. A common way to make a view fit its superview is
CGRect bounds = [[self superview] bounds];
[self setFrame:bounds];
You should do this inside layoutSubviews.