NSCollectionViewGridLayout in a single row with resizable collection items which maintain an aspect ratio? - objective-c

I have a NSCollectionView which uses a NSCollectionViewGridLayout to create a single row of collection view items. The row can have one or more items and can scroll horizontally is needed, i.e. one row, unlimited columns (one item per column).
The collection view items have a minimum size of 240x160 and have an aspect ration constraint of 3:2 (the width is 1.5 times the height). There is no maximum size.
All the spacing/insets are set to zero so that the items are next to each other horizontally, starting at the left-hand side of the collection view (though ideally I'd like to center the items horizontally if the total width of the items is less than the width of the collection view.)
The collection view is in the top half of a split view. When dragging the divider up/down (vertically), I'd like to resize the collection view items while maintaining the aspect ratio. The problem is that the aspect ratio constraint conflicts with the leading/trailing constraints and causes a constraint exception.
1) I've tried removing the leading/trailing constraints from the collection item view, but then the items are not flush together. I can't have gaps between the items and I can't the items overlapping each other.
2) I've tried removing the aspect ratio and the items resize correctly but don't maintain their aspect ratio.
3) I've also tried setting an aspect ratio on the collection view width such that it equals the height of the collection times the number of items times the aspect ratio, but wasn't able to get it to work correctly (and it seems kind of hack-ish). I also tried a custom layout object but I ran into different errors.
3) I was considering, but didn't try, de-activtating the aspect ratio constraint when the drag starts, and re-acivating the constraint when the drag ends but wasn't sure if this would work (it seems kind of hack-ish).
// setting up the grid layout
NSCollectionViewGridLayout* layout = [[[NSCollectionViewGridLayout alloc] init] autorelease];
layout.minimumInteritemSpacing = 0.0;
layout.minimumLineSpacing = 0.0;
layout.margins = NSEdgeInsetsMake(0.0, 0.0, 0.0, 0.0);
layout.maximumNumberOfRows = 1;
layout.maximumNumberOfColumns = 0;
layout.minimumItemSize = NSMakeSize(260.0, 160.0);
layout.maximumItemSize = NSZeroSize;
self.theCollectionView.collectionViewLayout = layout;
(not shown are the constraints in the collection view item xib file)
The desired result is to have:
a) User resizing a single row of collection view items by dragging a split view divider, and they maintain their aspect ratio while being resized.
b) Ideally, collection view items would be centered horizontally in collection view if the items don't scroll horizontally.

Related

NSCollectionView visibleRect property

The NSCollectionView displays multiple items. I have a Grid like layout.
I need to determine the items that are currently visible.
According to the documentation there is a method
- (NSArray<NSCollectionViewItem *> *)visibleItems;
with the description
The items returned by this method represent the ones that are active and currently being managed by the collection view. This array may contain items that are outside of the collection view’s actual visible rectangle. For example, it may contain items that were recently visible but have since been scrolled out of view. To test whether an item is actually visible, check to see if its frame rectangle intersects the visibleRect of the collection view.
So I need to manually calculate which items are visible based on its frame coordinates and visibleRect of the collectionView. It's fine of course.
However, what bothers me is which coordinates visibleRect returns.
Usually and it agrees with the documentation here if I have lets say
NSImage *img = [[NSImage alloc] initWithContentsOfFile:...];
[imageView setFrame:NSMakeRect(0., -100., 300., 400.)];
[imageView setImage:img];
NSLog(..., [imageView visibleRect]);
NSLog(..., [imageView frame]);
then the visible rectangle will have as expected
x=0
y=100
width = 300
height = 300
And this fully agrees with the documentation.
However, in the case of the NSCollectionView it behaves differently.
If I created items and the first item is displayed, then visibleRect will display
x = 0
y = 0
Now if I scroll a little bit down the output will be
x = 0
y = some number > 0
However, I would expect that it should return a different y value. Because
A view's visible rectangle reflects the portion of the contents that are actually displayed, in terms of the view's bounds coordinate system
Assuming that there is no spacing between items and items are identical
x = 0
y = (numItems - 1) * itemHeight
This behavior is not a problem of course. I can use these values to calculate things based on the returned values. I want to understand the reason.
What am I missing?
P.S. Essentially NSCollectionView visibleRect together with frame of its items behaves as if the origin of the coordinate system were in the top left corner with y increasing down and x increasing right.
NSCollectionView uses a flipped coordinate system.
See the flipped property of NSView and Flipped Coordinate Systems.

UITableViewCell nib with several constraint scenarios

I have a UITableViewCell nib that depending on the data it can show an image or not.
Below, the cell has 2 labels and at the right most the imageView. What I want to do is to remove the imageView (if no image is present on the data) and extend the Label all the way to the right.
Is there a way to have several constraint scenarios on the nib that I could activate/ deactivate?
Constraints on label1
leading, 2. center vertically in container, 3. fixed width
Constraints on label2
Leading space with label1, 2. Center vertically in Container
Constraints on imageview
Trailing space, 2. Top space, 3. Bottom space,
Width constraint : width <= 50 (When there is no image then width will automatically get zero),
Horizontal space between
label2 and imageview = 0 (priority = 250)
See the below GIF:

Why do I have to set VerticalScroll.Value twice?

I've got a Windows form that contains a picturebox that holds images of differing size - width is always the width of the form, height is calculated to keep the aspect ratio steady.
The up & down arrows (and home, end keys) are configured to scroll the window - but I'm finding I have to make the following call twice to 'properly' scroll:
Me.VerticalScroll.Value += 100
Me.VerticalScroll.Value += 100
The first call changes the scroll position (but not the scrollbar position), then the second changes the scrollbar position.
Why is this? Am I setting the wrong option here?

FlowPane height change measurements

I have a Stage as a form with a FlowPane. I just add Strings to the FlowPane. The main target with the flowpane is to create a "Tag" like control. This Tagcontrol is in the middle of the form.
Without or only one/two items the FlowPane should only have the same height as a TextField. When adding more and more Strings the FlowPane should increase its height till a certain maximum. While increasing the height of the FlowPane till its maximum also the height of the Stage should grow.
Now: how do I get the needed delta-height of the FlowPane when adding more and more items. For example on the first row I have two strings, on the second row i could right 5 strings depending of the string-length. If I have the delta i can use this to increase the height of the Stage easily.. but I am stuck with this calculation of flowheight-change.
I just messed around somewhere else in the code. You can just bind the height of the flowpane and you're good.

AQGridView: How to adjust UIGridViewCell margin

I'm trying to implement the AQGridView in my iPad app. Please see my below image -- sorry for the strong colors, but I think it would help you understand my problem.
Each cell (the blue box) has a margin left and right (yellow color), i.e. 8 pixels. This means that the first yellow coloum has a width of 8px, the second 16px, third 16px, and the last 8px. That's maybe fine in some situations, but I would like the yellow columns to have the same width. In my example the total width of the columns are 48 (8+16+16+8) so instead I would like each column to have a width of 12 (48/4). How can I change the AQGridView to do this?
I've tried to change the frame and bounce the AQGridView, but that did not do anything.
AQGridView actually takes the container view's coordinates and creates a strictly-defined grid from that. So when you have a 768px-wide grid view, and items which are a little under 1/3 of that in width, then it will create a rigid grid in which to place these items. By default your AQGridViewCell views will be centred within each grid square (although you can call -setResizesCellWidthToFit:YES to change that); you can adjust that positioning by implementing -gridView:adjustCellFrame:withinGridCellFrame: in your AQGridViewDelegate implementation.
In your case, every single cell has a margin of 8px on its left and right sides. On the outer edges this is seen alone, but in between pairs it appears doubled to 16px (8px from each cell).
What you want, then, is for each cell to have a 6px margin, and for the grid itself to have another 6px of margin space to the left & right of all cells. There's an API for this:
#property (nonatomic, assign) CGFloat leftContentInset;
#property (nonatomic, assign) CGFloat rightContentInset;
What you should be able to do is set myGridView.leftContentInset = 6.0; and myGridView.rightContentInset = 6.0;. The grid view will then lay out cells with a content width of 756px instead of 768px, which should give you a 6px margin around each cell. Combine that with the 6px left & right content insets and you'll have a clean 12px visible margin between every cell and the outer edges of the grid view.
Side note: the content inset properties were actually created to assist with laying out a certain number of cells per row. For instance, 1024 doesn't divide by 5, so it could end up with some fairly small cells being quite spread out since the layout grid would only contain 4 items in width. However, by adding left & right content insets of 2px each, the view would use a width of 1020 to calculate its layout grid, resulting in 5 cells per row. This is, in fact, exactly what we do in the Kobo iPad app when rotated to landscape mode; without that change, our portrait-mode 4 items per row just got spread out & it looked a little too sparse.
I managed to gain more control on the horizontal and vertical spacing by using the portraitGridCellSizeForGridView method and returning a slightly large CGRect compared to the one I used to initialise the cell in the gridView:cellForItemAtIndex: method.
I hope this helps.