Custom UICollection view layout - ios

I'm trying to configure a collection view to have the following custom layout shown in the attached image:
. Essentially, I want to be able to configure my collection view so it appears to be based off rows instead of columns. For example:
Row 1: 3 equally sized squares, each 1/3 the width of the screen
Row 2: 1 square that's 2/3 the width of the screen, and then 2 squares stacked on
top of each other that are each 1/3 the width of the screen.
Row 3: 1 rectangle that fills the width of the screen
Row 4: 2 equally sized squares, each 1/2 the width of the screen
I've gone through multiple UICollectionViewLayout and UICollectionViewDelegateFlowLayout tutorials, but have been unable to find the information I need.

You need to implement a custom UICollectionViewLayout.
Precompute the location of all of the squares in your repeat in prepareLayout
Return the height of your content by using the (repeat height) * (number of items in collection) / (number of items in repeat) + the height at the remainder in collectionViewContentSize (this means you should cache the content height for each item in step 1, but the height is not the height of that square (its the height of the tallest element in that row)
In layoutAttributesForElementsInRect you need to figure out what repeat row your rect starts in and what row of the repeat it ends in. You then return all of the cached layout attributes for those rows from step 1, but you have to add back a y value equal to (repeat count) / (first item index) * (repeat height).
basically its a bunch of math but when you divide it into repeating sections, the problem becomes which repeat am I in (divide) and what element within that repeat do I need (modulus), and it's much easier to solve.
I did something similar to this but with multiple repeats that could be arranged in different orders and it took a few hours to get it working without any bugs. Its a lot of math and very tedious.

Related

Horizontal Collection View with different width cells

I have a UICollectionView where the width of the cells is dynamic based on text. When I set a spacing of 5px between all cells, this is the distance between the 2 longest cells but all the other ones are a lot more spaced.
I need the spacing to be always the same, even when the cells are very small or very big.
Any idea on how to achieve this?
So far I'm setting the minimumLineSpacingForSectionAt and the size of the cell is calculated based on the width of the text.

Find the lowest positioned element

I'm trying to make constraints via Snapkit in a table view cell but my problem is I need to find out which element has max y position (The lowest one).
I have an UIImageView and next to image view UILabel elements. The label text is dynamic and could be very long or very short. Below these 2 elements, I have another one that should be aligned based on the label height, either taking image view or label.
My question is how to find which element (UIIMageView, UILabel) has a bigger Y position.
To be more clear I attached a draw with simple two cases.
Set a greaterThanOrEqualTo constraint on both elements.
In "plain language":
AnotherElement.top >= ImageView.Bottom (with constant of 12, or however much space you want)
AnotherElement.top >= Label.Bottom (with constant of 12, or however much space you want)

Resizing buttons and position on a static background image

I am working to have my buttons height and position adapt to screen size changes like the pictures shown above. The buttons themselves will remain clear and only serve as a simple way to handle taps that trigger the segues to different screens. My goal is to make it so that as the image stretches across different screen sizes, I would like the buttons to keep equal height and width and position with the windows. I know that if the windows had properties I could simply make the buttons have an equal size and width to them and be done, but as I mentioned the image is static and it has to stay that way for the time being. I've tried creating constraints for the buttons and that has only proven to be a headache and I don't know if stack views will help me here either, I know this is fairly complex, but I'm ok with that I just need some direction.
UPDATE: In an effort to follow the instructions LGP listed properly I started from step 1. As I mentioned in the comments, I believe it's simply the ratio and the constraints conflicts since when I remove one or two it works fine, but then how do I set the constraints so it fills the entire screen and maintains the ratio of the picture? Also shown are the constraint conflicts for the image view an it isn't showing the aspect ratio of the parent container view either
If you want to do it in interface builder it is not too hard. You should use spacer views and proportional sizes to position the buttons. That way, whatever size your background will have, all the elements will follow.
1. Create a container that has the same proportions as you image. Add a regular UIView and set an Aspect Ratio constraint with a multiplier of 852:568. This is the dimension of your background photo, 852 x 568 pixels, but the actual values don't matter, as long as the aspect ratio is the same. (Don't forget to also tie up the container view to however you want it in your view controller. See the UPDATE below on how to do this.)
2. Place the background image in the container. Add an image view as a child to the container. Set the constraints to touch all four edges of the container. Set the Image property to you image, and set Content Mode to Aspect Fit.
3. Add the first spacer view. Add a regular UIView to the container view (see leftmost, white view below) and set the constraints as follows:
height = 1 (not important, I used 10 in the image)
Top space to Superview = 90 (not important)
Leading space to Superview = 0
Width equal to Superview with multiplier dw:cw <- This makes it proportional! dw is the distance from the left edge to the first window/button, and cw is the width of the container. If your container is 375 wide, and your distance to the first button is 105, the multiplier will be 105:375.
4. Add the second space view. This is the vertical spacer, going from top to first button. Set it up similar as the first spacer, except make the height proportional to the containers height, and the width fixed.
5. Add the first button. Constrain its left and top edges to the spacers, then make its width and height proportional to the container.
6. Add the remaining spacers and buttons. They are all the same. Just remember where to make them proportional. All buttons are constraint to the single vertical spacer.
Finally, you should make the spacer views hidden. You can easily try it within your Storyboard by selecting different devices.
I chose to add everything on iPhone 8, but it is not really important. Here is what it looks like when I change to iPad Pro 12.9" and iPhone SE. Note how the buttons are positioned correctly. The spacer move around a little because they have partly fixed distances, but it works fine anyway.
UPDATE: Here is how to constrain the container view in the view controller's view to make the container fill the whole view and still keep its aspect ratio.
First, set the image view's (the one you added in step 2 above) Content Compression Resistance Priority to 200 for both Horizontal and Vertical. This will allow the image to compress rather then expand if it has a choice.
Second, Add the following constraints to you container:
Align Center X to Superview
Align Center Y to Superview
Equal Width to Superview >= 0
Equal Height to Superview >= 0
852:568 Ratio to View <- This you should already have!
Now the container will always center on screen, it will always fill at least the entire screen, but will also allow it to fill beyond in both X and Y.
UPDATE 2: If you want to ignore the photo's aspect ratio, and always fill the screen with the photo, you just add constraints for the container view to each side to its superview. Your container view's constraints should look like this.
In step 2 you will need to set the image's Content Mode to Scale to fill. The rest should be the same.
Use percentage based positions and size. Identify the positions of windows in percentage basis, and create the origin in x and y dimension by multiplying the percentage to the width and height of the screen. I am assuming that you are using ScaleToFill as content mode of the ImageView.
Similarly for calculating size, identify the width and height of the ImageView on percentage basis, and multiply the values in percent with the total width and height of the screen.
For example, to calculate the position of Window one-
Suppose, window1.x in percentage basis is 25% & total image view width is 400 (for example), than window1.x pixel position will be-
window1X = (25 * 400) / 100 = 100
Suppose, window1.y in percentage basis is 25% & total image view height is 300 (for example), than window1.y pixel position will be-
window1Y = (25 * 300) / 100 = 75
Suppose, width is 7% of image views width, than width in pixel will be -
window1Width = (7 * 400) /100 = 28
Suppose, height is 12% of image views height, than height in pixel will be -
window1Height = (12 * 300) /100 = 36
window1.frame = CGRectMake (window1X, window1Y, window1Width, window1Height)
Same approach for other windows, to calculate their positions(size will be same as window 1)
This approach will work across all screen resolutions, since it uses percentage based calculations & ScaleToFill as content mode for image view.

set subViews with different width horizontal in a superview

I wanted to create 6 subviews in a view, which have different width & same height. It should fit horizontal, all screen resolutions for iPad and iPhone.Can anyone please help me on this.
Without any additional logic (i.e., which views to make smaller or not if the content does not fit), you can do this like this: if the views are ligned up from left to right and numbered 1 to 6
defining the leftmost view (1) with a distance to left edge
defining the rightmost view (6) with a distance to right edge
defining the distances between the views (i.e. 1 to 2, 2 to 3 etc.)
setting the horizontal "compression resistance priority" to differing values
You can set the same height individually, or just for one view and as equal height for the other views.

Avoiding collection view "stretched" cells

I'm setting up a collection view and customizing its cell's width, inset, and inter-item spacing. With the current size that means there would be two columns in portrait and three in landscape. I let flow layout do the rotation adjustments & calculations, but the result is a stretched middle column. I think what is happening is flow layout is doing the math for the cell padding/spacing and not getting clean whole numbers. Then it adjusts the size of the actual cell by half a point. The result is an aliased & blurry looking cell.
I'm had this issue multiple times and each time - I manually calculate the values for each orientation to come out as a clean whole number. While this works, it doesn't seem very efficient. I'm wondering if there is a better way to do this, and if FlowLayout has the ability to say, give that half point to a margin or the empty space between the cells?

Resources