Creating a grid system with UICollectionView - ios

I have a UICollectionView that I want to use as a grid. When I receive an ordered pair I want to move a CAShapeLayer circle to the appropriate spot. I have 400 UICollectionView cells to form a 20x20 grid (+, +). Is there a way to do this with UICollectionView?
This what my grid looks like…
The green dot represents ordered pair (12, 2). I got the dot in the right spot by guessing and checking what CGPoint would correspond with (12, 2).
Any help or suggestions are appreciated!
Thanks in advance!

Something like this should work assuming your grid goes to the edges of your UICollectionView. You might need to adjust the CGPoint by half the width of your circle.
let width = collectionView.bounds.width
// dimensions of the grid
let xdim: CGFloat = 20
let ydim: CGFloat = 20
// the grid coordinates of your point
let x: CGFloat = 12
let y: CGFloat = 2
let point = CGPoint(x: x * width / xdim, y: (ydim - y) * width / xdim)

Related

Scale UIVIew upwards

I have an UIView which I want to scale to double its size. I've tried these 2 commands:
self.bottomView.transform = CGAffineTransform(translationX: 0, y: -125)
This one moves to the point I want but it moves the entire view so I get a gap at the bottom.(125 is my original height)
self.bottomView.transform = CGAffineTransform(scaleX: 1, y: 2)
This one stretches the view but it stretches both ways, up and down. I want it to only stretch in an upward Y-axis direction and not to both ways.
Which one should I continue with? Is there any way to choose which way the view should stretch? Furthermore, scaleX: y: stretches the subviews as well which isn't optimal for my cause.
I think you can use the below API.
CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy)
Update only one axis: X or Y with recognizer.scale and keep the other one 1.0f to achieve one direction scale.
Here is a simple Solution that may help you -- Just Animate your stretching operation on UIView.
UIView.animate(withDuration: 0.2) {
self.bottomView.frame.size.height = self.bottomView.frame.size.height * 2
}

Using CATransform3DRotate with perspective: how to correct the 2D size increase?

I'm trying to create a paper folding effect in Swift using CALayers and CATransform3DRotate. There are some libraries out there, but those are pretty outdated and don't fit my needs (they don't have symmetric folds, for example).
My content view controller will squeeze to the right half side of the screen, revealing the menu at the left side.
Everything went well, until I applied perspective: then the dimensions I calculate are not correct anymore.
To explain the problem, I created a demo to show you what I'm doing.
This the content view controller with three squares. I will use three folds, so each square will be on a separate fold.
The even folds will get anchor point (0, 0.5) and the odd folds will get anchor point (1, 0.5), plus they'll receive a shadow.
When fully folded, the content view will be half of the screen's width.
On an iPhone 7, each fold/plane will be 125 points unfolded and 62.5 points fully folded when looked at.
To calculate the rotation needed to achieve this 62.5 points width, we can use a trigonometric function. To illustrate, look at this top-down view:
We know the original plane size (125) and the 2D width (62.5), so we can calculate the angle α using arccos:
let angle = acos(width / originalWidth)
The result is 1.04719755 rad or 60 degrees.
When using this formula with CATransform3DRotate, I get the correct result:
Now for the problem: when I add perspective, my calculation isn't correct anymore. The planes are bigger. Probably because of the now different projection.
You can see the planes are now overlapping and being clipped.
I reconstructed the desired result on the right by playing with the angle, but the correction needed is not consistent, unfortunately.
Here's the code I use. It works perfectly without perspective.
// Loop layers
for i in 0..<self.layers.count {
// Get layer
let layer = self.layers[i]
// Get dimensions
let width = self.frame.size.width / CGFloat(self.numberOfFolds)
let originalWidth = self.sourceView.frame.size.width / CGFloat(self.numberOfFolds)
// Calculate angle
let angle = acos(width / originalWidth)
// Set transform
layer.transform = CATransform3DIdentity
layer.transform.m34 = 1.0 / -500
layer.transform = CATransform3DRotate(layer.transform, angle * (i % 2 == 0 ? -1 : 1), 0, 1, 0)
// Update position
if i % 2 == 0 {
layer.position = CGPoint(x: (width * CGFloat(i)), y: layer.position.y)
} else {
layer.position = CGPoint(x: (width * CGFloat(i + 1)), y: layer.position.y)
}
}
So my question is: how do I achieve the desired result? Do I need to correct the angle, or should I calculate the projected/2D width differently?
Thanks in advance! :)

Move view in direction of specific angle

I have an angle that I am calculating based on the positioning of a view from the centre of the screen. I need a way to move the view from it's current position, off the screen in the direction of the angle.
I'm sure there is a fairly simple way of calculating a new x and y value, but I haven't been able to figure out the maths. I want to do it using an animation, but I can figure that out myself once I have the coordinates.
Anyone have any suggestions?
If you have angle you can calculate new coordinates by getting sine and cosine values. You can try out following code
let pathLength = 50 as Double // total distance view should move
let piFactor = M_PI / 180
let angle = 90 as Double // direction in which you need to move it
let xCoord = outView.frame.origin.x + CGFloat(pathLength * sin(piFactor*angle)) //outView is name of view you want to animate
let yCoord = outView.frame.origin.y + CGFloat(pathLength * cos(piFactor*angle))
UIView.animateWithDuration(1, delay: 0, options: UIViewAnimationOptions.CurveEaseInOut, animations: { () -> Void in
self.outView.frame = CGRectMake(xCoord, yCoord, self.outView.frame.size.width, self.outView.frame.size.height)
}, completion: { (Bool) -> Void in
})
To me it sounds what you need to do is convert a vector from polar representation (angle and radius) to cartesian representation (x and y coordinates) which should be fairly easy.
You already got the angle so you only need to get the radius, which is the length of the vector. In you case (if I understand it correctly) is the distance from the current center of the view that needs to be animated to it's new position. While it may be complex to know that exactly (cause this part of what you are trying to calculate) you can go on the safe side and take a large enough value that will surely throw the view out of its super view frame. The length of the superview diagonal plus the length of the animated view diagonal should do the work, or even more simple just take the sum of the height and width of both views.
Once you have the complete polar representation of the vector (angle and radius) you can use that simple formula to convert to cartesian representation (x = r * cos(a), y = r * sin(a)) and finally add that vector coordinates to the center of the view you need to animate.

How to get the X and Y of the edges of the screen in Swift

I've wrote this code down here but it's not working the way I want, I have only got the random position in the all view, my meaning is to get random positions on the edges of the screen. How can I do it?
let viewHeight = self.view!.bounds.height
let viewWidth = self.view!.bounds.width
randomYPosition = CGFloat(arc4random_uniform(UInt32(viewHeight)))
randomXPosition = CGFloat(arc4random_uniform(UInt32(viewWidth)))
Use the following property:
For Max:
view.frame.maxX
view.frame.maxY
For minimum:
view.frame.minX
view.frame.minY
If you really want the bounds of the screen, then you should see How to get the screen width and height in iOS?, which tells you that you need to look at the UIScreen object for its dimensions. In Swift, the code works out to:
let screenRect = UIScreen.mainScreen().bounds
let screenWidth = screenRect.size.width
let screenHeight = screenRect.size.height
That really gives you the screen width and height, but it'd be strange if the screen origin weren't at (0,0). If you want to be sure, you can add the origin's coordinates:
let screenWidth = screenRect.size.width + screenRect.origin.x
let screenHeight = screenRect.size.height + screenRect.origin.y
All that said, there's usually little reason to look at the screen itself. With iOS now supporting split screen, your app may only be using part of the screen. It'd make more sense to look at the app's window, or even just at the view controller's view. Since these are both UIViews, they both have a bounds property just like any other view.
If I got you correctly, what you want is random points which fall in the edges of the view. That means on the four sides of the view, either x or y is fixed. So here is the solution :
top wall random points( where y is fixed to 0, and varying x):
topRandomPoint = CGPointMake(CGFloat(arc4random_uniform(UInt32(viewWidth))), 0)
right side wall points ( where x is fixed to max-width (e.g 320), and varying y)
rightRandomPoint = CGPointMake(viewWidth, CGFloat(arc4random_uniform(UInt32(viewHeight))))
Bottom random points ( where y is fixed to max-height (e.g 568) and x varying)
bottomRandomPoint = CGPointMake(CGFloat(arc4random_uniform(UInt32(viewWidth))), viewHeight)
Left random points (where x is fixed to 0 and y is varying)
leftRandomPoint = CGPointMake(0, CGFloat(arc4random_uniform(UInt32(viewHeight))))
This should give you a random number in the range 0 - ViewHeight and 0 - ViewWidth respectively
CGFloat(arc4random_uniform(UInt32(0..viewHeight)))
CGFloat(arc4random_uniform(UInt32(0..viewWidth)))
Now you need to construct the position based on the 4 cases
X = 0, Y = Random (0 - ViewHeight) --> Left side
X = self.view!.bounds.width, Y = Random (0 - ViewHeight) --> Right side
X = Random (0 - ViewWidth), Y = 0 --> Bottom side
X = Random (0 - ViewWidth), Y = self.view!.bounds.width --> Top side

Why divide y value by 2

I've seen a few xCode projects that divide the height of an item by 2 in order to calculate the y coordinate for position. Something like this
CGFloat logoY = floorf((navBarHeight - logo.size.height) / 2.0f);
Why is it necessary to divide by 2?
As #Stonz2 said this code centers logo vertically. But it is better to use autolayout constraints to do so. Also do not forget that UIView has 'center' property which could be more convenient here. For example following code
CGFloat logoX = floorf((navBarWidth - logo.size.width) / 2.0f);
CGFloat logoY = floorf((navBarHeight - logo.size.height) / 2.0f);
logo.frame.origin = CGPointMake(logoX, logoY);
is equivalent to
logo.center = CGPointMake(floorf(navBarWidth / 2), floorf(navBarHeight / 2));
Center vertically. Otherwise it would be placed against the bottom of the view.

Resources