Swift CGPoint in relation to another CGPoint - ios

I am trying to have two UIButtons. One is 8 points from the edge of the UIView they are inside and the second one is 4 points from the start of the first. I am currently doing this
let buttonFrames: CGSize = CGSizeMake(56, 42)
let buttonRightPosition: CGPoint = CGPointMake(frame.size.width - 8, frame.size.height - 62)
let buttonLeftPosition: CGPoint = CGPointMake(buttonRightPosition - 60, frame.size.height)
Both buttons will have the frame 56x42 but I will also add size to fit both buttons. So, that is why I need to adjust the second one accordingly. When I do this an error occurs in variable buttonLeftPosition. I am trying to find any way to place a view in relation to another view in the same class.

buttonRightPosition is a CGPoint. In your last line, when you are trying to create the buttonLeftPosition CGPoint, you have to use buttonRightPosition.x to access its x position..
Note: In Swift I would use the CGSize and CGPoint initializers instead of CGSizeMake() or CGPointMake()
let buttonFrames = CGSize(width: 56, height: 42)
let buttonRightPosition = CGPoint(x: frame.size.width - 8, y: frame.size.height - 62)
let buttonLeftPosition = CGPoint(x: buttonRightPosition.x - 60, y: frame.size.height)

Related

Find CGPoint of view inside UIScrollView depending of UIScrollView's center point

I have a UIScrollView with an UIImageView inside. The main goal is to move the UIImageView inside de UIScrollView like drag, zoom, etc.
Let's imagine that scrollview's frame is 100x100.
The UIImageView's size in 250x100. So, we can drag from left to right without zooming, and we when zoom in, we can drag from top to bottom.
I know the center of the UIScrollView = (50,50) => CGPoint
My question is: how can I get the CGPoint of the UIImageView equivalent to the center CGPoint of the scrollview ?
Here is, I hope, an helpful description of the scene:
Thank you a lot for your help guys!
Use the convert method of UIView to convert a point from the scroll view to the image view:
let imageViewPoint = scrollView.convert(CGPoint(x: 50, y: 50), to: imageView)
Thanks to rmaddy for you help.
Here is the solution:
//FYI: focusPoint.scale is the zoomScale of the UIScrollView.
let originVisible = scrollView.contentOffset
let convertedPoint = scrollView.convert(originVisible, to: imageView).scaledBy(scale: focusPoint.scale)
let imageViewPoint = CGPoint(x: convertedPoint.x + scrollView.center.x,
y: convertedPoint.y + scrollView.center.y)
extension CGPoint {
func scaledBy(scale: CGFloat) -> CGPoint {
return CGPoint(x: x * scale, y: y * scale)
}
}

Unable to add the SKNode image buttons inside a scrollable layout in Swift spritekit

Hi I'm totally new to swift and spritekit and having trouble with setting the buttons like the sample below inside a scrollable view or layout (if there is such a thing in spritekit). I have only experience in Android development and I want to know if there is a way to add the SKNode buttons inside a scrollable like android's ScrollView? I want to add the SKNodes vertically in a scrollable. For example, how to set the SKLabels or SKNode images into a Scollable ? Sorry for my poor English.
scoreLabel.fontSize = 15
scoreLabel.position = CGPoint(x: self.size.width * 0.5 , y: (self.size.height * 0.5) + 100)
scoreLabel.zPosition = 2
scoreLabel.fontColor = UIColor.whiteColor()
//titleLabel.color = UIColor.blackColor()
self.addChild(scoreLabel)
startLabel.name = "hard"
startLabel.text = "Hard"
startLabel.fontSize = 30
startLabel.position = CGPoint(x: self.size.width * 0.5, y: 300)
startLabel.zPosition = 2
startLabel.fontColor = UIColor.whiteColor()
self.addChild(startLabel)
startLabel2.name = "previousButton"
startLabel2.text = "Normal"
startLabel2.fontSize = 30
startLabel2.position = CGPoint(x: self.size.width * 0.5, y: 250)
startLabel2.zPosition = 2
startLabel2.fontColor = UIColor.whiteColor()
self.addChild(startLabel2)
you can easily mixup sprite kit and uikit for your situation UICollectionView is best add UICollectionView as a subview and pass data to that UICollectionView for example images and text remember UICollectionView is a part of uikit so SKNode can't be added to UICollectionView but you can add any view as a subview to your sprite kit SKScene for more information read
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIKit_Framework/
https://developer.apple.com/library/ios/samplecode/CollectionView-Simple/Introduction/Intro.html

Difference between bounds.size.width and bounds.width in swift?

What is the difference between bounds.size.width and bounds.width in swift? Will they return the same thing?
Thanks!
bounds is a CGRect struct property for a UIView that contains 2 nested structs within it; CGPoint and CGSize. These represent the points of origin for the view (x and y), and the size of the view in height and width specified in points respectively.
If you have a UIView that's 100 x 100, then: bounds.width will return 100, and bounds.size.width will also return 100. Basically they will return the same CGFloat values even if your CGRect has negative width and height values.

Objective-C Translate UIImageView on z-axis [duplicate]

I'm looking to perform a perspective transform on a UIView (such as seen in coverflow)
Does anyonew know if this is possible?
I've investigated using CALayer and have run through all the pragmatic programmer Core Animation podcasts, but I'm still no clearer on how to create this kind of transform on an iPhone.
Any help, pointers or example code snippets would be really appreciated!
As Ben said, you'll need to work with the UIView's layer, using a CATransform3D to perform the layer's rotation. The trick to get perspective working, as described here, is to directly access one of the matrix cells of the CATransform3D (m34). Matrix math has never been my thing, so I can't explain exactly why this works, but it does. You'll need to set this value to a negative fraction for your initial transform, then apply your layer rotation transforms to that. You should also be able to do the following:
Objective-C
UIView *myView = [[self subviews] objectAtIndex:0];
CALayer *layer = myView.layer;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform.m34 = 1.0 / -500;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, 45.0f * M_PI / 180.0f, 0.0f, 1.0f, 0.0f);
layer.transform = rotationAndPerspectiveTransform;
Swift 5.0
if let myView = self.subviews.first {
let layer = myView.layer
var rotationAndPerspectiveTransform = CATransform3DIdentity
rotationAndPerspectiveTransform.m34 = 1.0 / -500
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, 45.0 * .pi / 180.0, 0.0, 1.0, 0.0)
layer.transform = rotationAndPerspectiveTransform
}
which rebuilds the layer transform from scratch for each rotation.
A full example of this (with code) can be found here, where I've implemented touch-based rotation and scaling on a couple of CALayers, based on an example by Bill Dudney. The newest version of the program, at the very bottom of the page, implements this kind of perspective operation. The code should be reasonably simple to read.
The sublayerTransform you refer to in your response is a transform that is applied to the sublayers of your UIView's CALayer. If you don't have any sublayers, don't worry about it. I use the sublayerTransform in my example simply because there are two CALayers contained within the one layer that I'm rotating.
You can only use Core Graphics (Quartz, 2D only) transforms directly applied to a UIView's transform property. To get the effects in coverflow, you'll have to use CATransform3D, which are applied in 3-D space, and so can give you the perspective view you want. You can only apply CATransform3Ds to layers, not views, so you're going to have to switch to layers for this.
Check out the "CovertFlow" sample that comes with Xcode. It's mac-only (ie not for iPhone), but a lot of the concepts transfer well.
Swift 5.0
func makeTransform(horizontalDegree: CGFloat, verticalDegree: CGFloat, maxVertical: CGFloat,rotateDegree: CGFloat, maxHorizontal: CGFloat) -> CATransform3D {
var transform = CATransform3DIdentity
transform.m34 = 1 / -500
let xAnchor = (horizontalDegree / (2 * maxHorizontal)) + 0.5
let yAnchor = (verticalDegree / (-2 * maxVertical)) + 0.5
let anchor = CGPoint(x: xAnchor, y: yAnchor)
setAnchorPoint(anchorPoint: anchor, forView: self.imgView)
let hDegree = (CGFloat(horizontalDegree) * .pi) / 180
let vDegree = (CGFloat(verticalDegree) * .pi) / 180
let rDegree = (CGFloat(rotateDegree) * .pi) / 180
transform = CATransform3DRotate(transform, vDegree , 1, 0, 0)
transform = CATransform3DRotate(transform, hDegree , 0, 1, 0)
transform = CATransform3DRotate(transform, rDegree , 0, 0, 1)
return transform
}
func setAnchorPoint(anchorPoint: CGPoint, forView view: UIView) {
var newPoint = CGPoint(x: view.bounds.size.width * anchorPoint.x, y: view.bounds.size.height * anchorPoint.y)
var oldPoint = CGPoint(x: view.bounds.size.width * view.layer.anchorPoint.x, y: view.bounds.size.height * view.layer.anchorPoint.y)
newPoint = newPoint.applying(view.transform)
oldPoint = oldPoint.applying(view.transform)
var position = view.layer.position
position.x -= oldPoint.x
position.x += newPoint.x
position.y -= oldPoint.y
position.y += newPoint.y
print("Anchor: \(anchorPoint)")
view.layer.position = position
view.layer.anchorPoint = anchorPoint
}
you only need to call the function with your degree. for example:
var transform = makeTransform(horizontalDegree: 20.0 , verticalDegree: 25.0, maxVertical: 25, rotateDegree: 20, maxHorizontal: 25)
imgView.layer.transform = transform
You can get accurate Carousel effect using iCarousel SDK.
You can get an instant Cover Flow effect on iOS by using the marvelous and free iCarousel library. You can download it from https://github.com/nicklockwood/iCarousel and drop it into your Xcode project fairly easily by adding a bridging header (it's written in Objective-C).
If you haven't added Objective-C code to a Swift project before, follow these steps:
Download iCarousel and unzip it
Go into the folder you unzipped, open its iCarousel subfolder, then select iCarousel.h and iCarousel.m and drag them into your project navigation – that's the left pane in Xcode. Just below Info.plist is fine.
Check "Copy items if needed" then click Finish.
Xcode will prompt you with the message "Would you like to configure an Objective-C bridging header?" Click "Create Bridging Header"
You should see a new file in your project, named YourProjectName-Bridging-Header.h.
Add this line to the file: #import "iCarousel.h"
Once you've added iCarousel to your project you can start using it.
Make sure you conform to both the iCarouselDelegate and iCarouselDataSource protocols.
Swift 3 Sample Code:
override func viewDidLoad() {
super.viewDidLoad()
let carousel = iCarousel(frame: CGRect(x: 0, y: 0, width: 300, height: 200))
carousel.dataSource = self
carousel.type = .coverFlow
view.addSubview(carousel)
}
func numberOfItems(in carousel: iCarousel) -> Int {
return 10
}
func carousel(_ carousel: iCarousel, viewForItemAt index: Int, reusing view: UIView?) -> UIView {
let imageView: UIImageView
if view != nil {
imageView = view as! UIImageView
} else {
imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 128, height: 128))
}
imageView.image = UIImage(named: "example")
return imageView
}

Understand convertRect:toView:, convertRect:FromView:, convertPoint:toView: and convertPoint:fromView: methods

I'm trying to understand the functionalities of these methods. Could you provide me with a simple use case to understand their semantics?
From the documentation, for example, convertPoint:fromView: method is described as follows:
Converts a point from the coordinate system of a given view to that of the receiver.
What does the coordinate system mean? What about the receiver?
For example, does it make sense using convertPoint:fromView: like the following?
CGPoint p = [view1 convertPoint:view1.center fromView:view1];
Using NSLog utility, I've verified that p value coincides with view1's center.
Thank you in advance.
EDIT: For those interested, I've created a simple code snippet to understand these methods.
UIView* view1 = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 150, 200)];
view1.backgroundColor = [UIColor redColor];
NSLog(#"view1 frame: %#", NSStringFromCGRect(view1.frame));
NSLog(#"view1 center: %#", NSStringFromCGPoint(view1.center));
CGPoint originInWindowCoordinates = [self.window convertPoint:view1.bounds.origin fromView:view1];
NSLog(#"convertPoint:fromView: %#", NSStringFromCGPoint(originInWindowCoordinates));
CGPoint originInView1Coordinates = [self.window convertPoint:view1.frame.origin toView:view1];
NSLog(#"convertPoint:toView: %#", NSStringFromCGPoint(originInView1Coordinates));
In both cases self.window is the receiver. But there is a difference. In the first case the convertPoint parameter is expressed in view1 coordinates. The output is the following:
convertPoint:fromView: {100, 100}
In the second one, instead, convertPoint is expressed in superview (self.window) coordinates. The output is the following:
convertPoint:toView: {0, 0}
Each view has its own coordinate system - with an origin at 0,0 and a width and height. This is described in the bounds rectangle of the view. The frame of the view, however, will have its origin at the point within the bounds rectangle of its superview.
The outermost view of your view hierarchy has it's origin at 0,0 which corresponds to the top left of the screen in iOS.
If you add a subview at 20,30 to this view, then a point at 0,0 in the subview corresponds to a point at 20,30 in the superview. This conversion is what those methods are doing.
Your example above is pointless (no pun intended) since it converts a point from a view to itself, so nothing will happen. You would more commonly find out where some point of a view was in relation to its superview - to test if a view was moving off the screen, for example:
CGPoint originInSuperview = [superview convertPoint:CGPointZero fromView:subview];
The "receiver" is a standard objective-c term for the object that is receiving the message (methods are also known as messages) so in my example here the receiver is superview.
I always find this confusing so I made a playground where you can visually explore what the convert function does. This is done in Swift 3 and Xcode 8.1b:
import UIKit
import PlaygroundSupport
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Main view
view.backgroundColor = .black
view.frame = CGRect(x: 0, y: 0, width: 500, height: 500)
// Red view
let redView = UIView(frame: CGRect(x: 20, y: 20, width: 460, height: 460))
redView.backgroundColor = .red
view.addSubview(redView)
// Blue view
let blueView = UIView(frame: CGRect(x: 20, y: 20, width: 420, height: 420))
blueView.backgroundColor = .blue
redView.addSubview(blueView)
// Orange view
let orangeView = UIView(frame: CGRect(x: 20, y: 20, width: 380, height: 380))
orangeView.backgroundColor = .orange
blueView.addSubview(orangeView)
// Yellow view
let yellowView = UIView(frame: CGRect(x: 20, y: 20, width: 340, height: 100))
yellowView.backgroundColor = .yellow
orangeView.addSubview(yellowView)
// Let's try to convert now
var resultFrame = CGRect.zero
let randomRect: CGRect = CGRect(x: 0, y: 0, width: 100, height: 50)
/*
func convert(CGRect, from: UIView?)
Converts a rectangle from the coordinate system of another view to that of the receiver.
*/
// The following line converts a rectangle (randomRect) from the coordinate system of yellowView to that of self.view:
resultFrame = view.convert(randomRect, from: yellowView)
// Try also one of the following to get a feeling of how it works:
// resultFrame = view.convert(randomRect, from: orangeView)
// resultFrame = view.convert(randomRect, from: redView)
// resultFrame = view.convert(randomRect, from: nil)
/*
func convert(CGRect, to: UIView?)
Converts a rectangle from the receiver’s coordinate system to that of another view.
*/
// The following line converts a rectangle (randomRect) from the coordinate system of yellowView to that of self.view
resultFrame = yellowView.convert(randomRect, to: view)
// Same as what we did above, using "from:"
// resultFrame = view.convert(randomRect, from: yellowView)
// Also try:
// resultFrame = orangeView.convert(randomRect, to: view)
// resultFrame = redView.convert(randomRect, to: view)
// resultFrame = orangeView.convert(randomRect, to: nil)
// Add an overlay with the calculated frame to self.view
let overlay = UIView(frame: resultFrame)
overlay.backgroundColor = UIColor(white: 1.0, alpha: 0.9)
overlay.layer.borderColor = UIColor.black.cgColor
overlay.layer.borderWidth = 1.0
view.addSubview(overlay)
}
}
var ctrl = MyViewController()
PlaygroundPage.current.liveView = ctrl.view
Remember to show the Assistant Editor (⎇⌘⏎) in order to see the views, it should look like this:
Feel free to contribute more examples here or in this gist.
Here's an explanation in plain English.
When you want to convert the rect of a subview (aView is a subview of [aView superview]) to the coordinate space of another view (self).
// So here I want to take some subview and put it in my view's coordinate space
_originalFrame = [[aView superview] convertRect: aView.frame toView: self];
Every view in iOS have a coordinate system. A coordinate system is just like a graph, which has x axis(horizontal line) and y axis(vertical line). The point at which the lines interesect is called origin. A point is represented by (x, y). For example, (2, 1) means that the point is 2 pixels left, and 1 pixel down.
You can read up more about coordinate systems here - http://en.wikipedia.org/wiki/Coordinate_system
But what you need to know is that, in iOS, every view has it's OWN coordinate system, where the top left corner is the origin. X axis goes on increasing to the right, and y axis goes on increasing down.
For the converting points question, take this example.
There is a view, called V1, which is 100 pixels wide and 100 pixels high. Now inside that, there is another view, called V2, at (10, 10, 50, 50) which means that (10, 10) is the point in V1's coordinate system where the top left corner of V2 should be located, and (50, 50) is the width and height of V2. Now, take a point INSIDE V2's coordinate system, say (20, 20). Now, what would that point be inside V1's coordinate system? That is what the methods are for(of course you can calculate themselves, but they save you extra work). For the record, the point in V1 would be (30, 30).
Hope this helps.
Thank you all for posting the question and your answers: It helped me get this sorted out.
My view controller has it's normal view.
Inside that view there are a number of grouping views that do little more than give their child views a clean interaction with auto layout constraints.
Inside one of those grouping views I have an Add button that presents a popover view controller where the user enters some information.
view
--groupingView
----addButton
During device rotation the view controller is alerted via the UIPopoverViewControllerDelegate call popoverController:willRepositionPopoverToRect:inView:
- (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView *__autoreleasing *)view
{
*rect = [self.addButton convertRect:self.addbutton.bounds toView:*view];
}
The essential part that comes from the explanation given by the first two answers above was that the rect I needed to convert from was the bounds of the add button, not its frame.
I haven't tried this with a more complex view hierarchy, but I suspect that by using the view supplied in the method call (inView:) we get around the complications of multi-tiered leaf view kinds of ugliness.
I used this post to apply in my case. Hope this will help another reader in the future.
A view can only see its immediate children and parent views. It can't see its grand parents or its grandchildren views.
So, in my case, I have a grand parent view called self.view, in this self.view I have added subviews called self.child1OfView, self.child2OfView. In self.child1OfView, I have added subviews called self.child1OfView1, self.child2OfView1.
Now if I physically move self.child1OfView1 to an area outside the boundary of self.child1OfView to anther spot on self.view, then to calculator the new position for the self.child1OfView1 within the self.view:
CGPoint newPoint = [self.view convertPoint:self.child1OfView1.center fromView:self.child1OfView];
You can see below code so you can understand that how it actually works.
let scrollViewTemp = UIScrollView.init(frame: CGRect.init(x: 10, y: 10, width: deviceWidth - 20, height: deviceHeight - 20))
override func viewDidLoad() {
super.viewDidLoad()
scrollViewTemp.backgroundColor = UIColor.lightGray
scrollViewTemp.contentSize = CGSize.init(width: 2000, height: 2000)
self.view.addSubview(scrollViewTemp)
let viewTemp = UIView.init(frame: CGRect.init(x: 100, y: 100, width: 150, height: 150))
viewTemp.backgroundColor = UIColor.green
self.view.addSubview(viewTemp)
let viewSecond = UIView.init(frame: CGRect.init(x: 100, y: 700, width: 300, height: 300))
viewSecond.backgroundColor = UIColor.red
self.view.addSubview(viewSecond)
self.view.convert(viewTemp.frame, from: scrollViewTemp)
print(viewTemp.frame)
/* First take one point CGPoint(x: 10, y: 10) of viewTemp frame,then give distance from viewSecond frame to this point.
*/
let point = viewSecond.convert(CGPoint(x: 10, y: 10), from: viewTemp)
//output: (10.0, -190.0)
print(point)
/* First take one point CGPoint(x: 10, y: 10) of viewSecond frame,then give distance from viewTemp frame to this point.
*/
let point1 = viewSecond.convert(CGPoint(x: 10, y: 10), to: viewTemp)
//output: (10.0, 210.0)
print(point1)
/* First take one rect CGRect(x: 10, y: 10, width: 20, height: 20) of viewSecond frame,then give distance from viewTemp frame to this rect.
*/
let rect1 = viewSecond.convert(CGRect(x: 10, y: 10, width: 20, height: 20), to: viewTemp)
//output: (10.0, 210.0, 20.0, 20.0)
print(rect1)
/* First take one rect CGRect(x: 10, y: 10, width: 20, height: 20) of viewTemp frame,then give distance from viewSecond frame to this rect.
*/
let rect = viewSecond.convert(CGRect(x: 10, y: 10, width: 20, height: 20), from: viewTemp)
//output: (10.0, -190.0, 20.0, 20.0)
print(rect)
}
I read the answer and understand the mechanics but I think the final example is not correct. According to the API doc, the center property of a view contains the known center point of the view in the superview’s coordinate system.
If this is the case, than I think it would not make sense to try to ask the superview to convert the center of a subview FROM the subview coordinate system because the value is not in the subview coordinate system. What would make sense is to do the opposite i.e. convert from the superview coordinate system to that of a subview...
You can do it in two ways (both should yield the same value):
CGPoint centerInSubview = [subview convertPoint:subview.center fromView:subview.superview];
or
CGPoint centerInSubview = [subview.superview convertPoint:subview.center toView:subview];
Am I way off in understanding how this should work?
One more important point about using these APIs. Be sure that the parent view chain is complete between the rect you are converting and the to/from view.
For example - aView, bView, and cView -
aView is a subview of bView
we want to convert aView.frame to cView
If we try to execute the method before bView has been added as a subview of cView, we will get back a bunk response. Unfortunately there is no protection built into the methods for this case. This may seem obvious, but it is something to be aware of in cases where the conversion goes through a long chain of parents.

Resources