CGAffineTransform an UIView with UIPanGestureRecognizer - ios

I have view A, add sub on self view. And i want when i draw panGestureRegonizer on view A, view A will move follow draw.
And while moving view A will scale. View A will scale to smaller when view move to top/left/bottom/right of sreen and scale to larger when view move to center of screen.
I have try many solution, but i can not make it.
Please suggest help me?

Logic: You have coordinate system(CS) with center, x and y. When the user uses pan gesture, he/she generates sequence of points in the CS. So our task is to measure the distance between the center of the CS and users' points. When we have the furthest distance, we can calculate scale factor for our scaling view.
var center: CGPoint! //center of the CS
let maxSize: CGSize = CGSize.init(width: 100, height: 100) // maximum size of our scaling view
var maxLengthToCenter: CGFloat! //maximum distance from the center of the CS to the furthest point in the CS
private func prepareForScaling() {
self.center = self.view.center //we set the center of our CS to equal the center of the VC's view
let frame = self.view.frame
//the furthest distance in the CS is the diagonal, and we calculate it using pythagoras theorem
self.maxLengthToCenter = (frame.width*frame.width + frame.height*frame.height).squareRoot()
}
Then we need to call our setup functional to have our data ready for scaling functionality - we can do this in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
self.prepareForScaling()
}
Then we need a helper function to calculates the scaled size of our view, for user's pan gesture current position on the screen.
private func scaledSize(for location: CGPoint) -> CGSize {
//calculate location x,y differences from the center
let xDifference = location.x - self.center.x
let yDifference = location.y - self.center.y
//calculate the scale factor - note that this factor will be between 0.0(center) and 0.5(diagonal - furthest point)
//It is due our measurement - from center to view's edge. Consider multiplying this factor with your custom constant.
let scaleFactor = (xDifference*xDifference + yDifference*yDifference).squareRoot()/maxLengthToCenter
//create scaled size with maxSize and current scale factor
let scaledSize = CGSize.init(width: maxSize.width*(1-scaleFactor), height: maxSize.height*(1-scaleFactor))
return scaledSize
}
And finally, we need to modify our pan gesture action to change the size of A:
#IBAction func panGestureAction(_ sender: UIPanGestureRecognizer) {
UIView.animateKeyframes(withDuration: 0.1, delay: 0, options: UIViewKeyframeAnimationOptions.calculationModeLinear, animations: {
let location = sender.location(in: sender.view?.superview)
sender.view?.frame = CGRect.init(origin: CGPoint.init(x: 0, y: 0), size: self.scaledSize(for: location))
sender.view?.center = location
})
}

Related

How can I rotate a SCNNode about its own center using a UIPanGestureRecognizer?

I have a scene view. If allowsCameraControl is true and I pan using one finger, the model rotates about its center. I want to achieve this same result, when allowsCameraControl is false. I want to move the part, not the point of view.
I have the following function...
private func rotateModels(with startingLocation: CGPoint, endingLocation: CGPoint, node: SCNNode) {
let radius: Float = 600
let panTranslation = CGPoint(x: startingLocation.x - endingLocation.x, y: startingLocation.y - endingLocation.y)
let panTranslationDistance = sqrtf(Float(pow(panTranslation.x, 2) + pow(panTranslation.y, 2)))
let angle = 3 * asin((panTranslationDistance/2)/radius)
let axis = simd_float3(x: Float(-panTranslation.y), y: Float(-panTranslation.x), z: 0)
let quat = simd_quatf(angle: angle, axis: axis)
node.simdLocalRotate(by: quat)
}
It appears to work, but sometimes it stretches or distorts the model.
Can anyone tell me what I am doing wrong?

How do I make an image act like a map (zoom/pan/map markers)?

Swift 5 / Xcode 12.4
I've got a single png image that's downloaded into the Documents folder and then loaded at runtime (currently as UIImage). This image has to act as some type of map:
Pinch zoom
Pan
I want to place some type of map marker (e.g. a dot) in specific spots: The user can click on them (to open a popup with more information) and they move according to the zoom/pan but always stay the same size.
Not full screen but inside a specific area in my ViewController.
I already did the same thing in Android but all Java map libraries I found require tiles (I've only got a single big image), so I ended up using a "zoom/pan" library (also lets you set the maximum zoom) and created my own invisible image sublayer for the markers.
For iOS I've found the Goggle Maps SDK and the Apple MapKit so far but they both look like they load rl map data and you can't set the actual image - is this possible with either of them?
I haven't found a zoom/pan library for iOS yet (at least one that's not 5+ years old) either, so how do I best accomplish this? Write my own zoom/pan listeners and use some type of sublayer (that moves with the parent) for the map markers - is that the way to go/what UI objects do I have to use?
this will help with the pinch to zoom - https://stackoverflow.com/a/58558272/2481602
this will help you to achieve a pan - How do I pan the image inside a UIImageView?
As far as the imposed markers, that you will have to manually handle the transformations and apply the the anchor points of the marker image. the documentation here - https://developer.apple.com/documentation/coregraphics/cgaffinetransform and this supplies a loose explanation - https://medium.com/weeronline/swift-transforms-5981398b437d
it not being full screen just needs a view to hold the scrollView that will hold the map in the location on the screen that you want.
Not a full answer but hopefully this will all point you in the right direction
Use a UIScrollView for the pinch/zoom/pan. To add the markers add them to a container view atop the scroll view, and respond to scroll view changes (scrollViewDidEndZooming, scrollViewDidZoom, scrollViewDidEndDragging...) by updating the positions of the annotation views in the container - you'll need to use UIView's convert to convert between coordinate systems, setting the center of annotation views to the appropriate point converted from your scrollview to the container view. Container view should be same size as scrollview, not scrollview's content.
Or you could add the annotations into the scrollview's content but then you have to update the transforms of those views to counter-magnify them as you zoom in.
One approach...
Use "standard" scroll view zoom/pan functionality
Use image view as viewForZooming in the scroll view
add "marker views" to the scroll view as siblings of the image view (not subviews of the image view)
For the marker positions, calculate the percent location. So, for example, if your image is 1000x1000, and you want a marker at 100,200, that marker would have a "point percentage" of 0.1,0.2. When the image view is zoomed, change the frame origin of the marker by its location percentages.
Here is a complete example (done very quickly, so just to get you going)...
I used this 1600 x 1600 image, with marker locations:
A simple "marker view" class:
class MarkerView: UILabel {
var yPCT: CGFloat = 0
var xPCT: CGFloat = 0
}
And the controller class:
class ZoomWithMarkersViewController: UIViewController, UIScrollViewDelegate {
let imgView: UIImageView = {
let v = UIImageView()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
let scrollView: UIScrollView = {
let v = UIScrollView()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
var points: [CGPoint] = [
CGPoint(x: 200, y: 200),
CGPoint(x: 800, y: 300),
CGPoint(x: 500, y: 700),
CGPoint(x: 1100, y: 900),
CGPoint(x: 300, y: 1200),
CGPoint(x: 1300, y: 1400),
]
var markers: [MarkerView] = []
override func viewDidLoad() {
super.viewDidLoad()
// make sure we have an image
guard let img = UIImage(named: "points1600x1600") else {
fatalError("Could not load image!!!!")
}
// set the image
imgView.image = img
// add the image view to the scroll view
scrollView.addSubview(imgView)
// add scroll view to view
view.addSubview(scrollView)
// respect safe area
let safeG = view.safeAreaLayoutGuide
// to save on typing
let contentG = scrollView.contentLayoutGuide
NSLayoutConstraint.activate([
// scroll view inset 20-pts on each side
scrollView.leadingAnchor.constraint(equalTo: safeG.leadingAnchor, constant: 20.0),
scrollView.trailingAnchor.constraint(equalTo: safeG.trailingAnchor, constant: -20.0),
// square (1:1 ratio)
scrollView.heightAnchor.constraint(equalTo: scrollView.widthAnchor),
// center vertically
scrollView.centerYAnchor.constraint(equalTo: safeG.centerYAnchor),
// constrain all 4 sides of image view to scroll view's Content Layout Guide
imgView.topAnchor.constraint(equalTo: contentG.topAnchor),
imgView.leadingAnchor.constraint(equalTo: contentG.leadingAnchor),
imgView.trailingAnchor.constraint(equalTo: contentG.trailingAnchor),
imgView.bottomAnchor.constraint(equalTo: contentG.bottomAnchor),
// we will want zoom scale of 1 to show the "native size" of the image
imgView.widthAnchor.constraint(equalToConstant: img.size.width),
imgView.heightAnchor.constraint(equalToConstant: img.size.height),
])
// create marker views and
// add them as subviews of the scroll view
// add them to our array of marker views
var i: Int = 0
points.forEach { pt in
i += 1
let v = MarkerView()
v.textAlignment = .center
v.font = .systemFont(ofSize: 12.0)
v.text = "\(i)"
v.backgroundColor = UIColor.green.withAlphaComponent(0.5)
scrollView.addSubview(v)
markers.append(v)
v.yPCT = pt.y / img.size.height
v.xPCT = pt.x / img.size.width
v.frame = CGRect(origin: .zero, size: CGSize(width: 30.0, height: 30.0))
}
// assign scroll view's delegate
scrollView.delegate = self
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
print(#function)
guard let img = imgView.image else { return }
// max scale is 1.0 (original image size)
scrollView.maximumZoomScale = 1.0
// min scale fits the image in the scroll view frame
scrollView.minimumZoomScale = scrollView.frame.width / img.size.width
// start at min scale (so full image is visible)
scrollView.zoomScale = scrollView.minimumZoomScale
// just to make the markers "appear" nicely
markers.forEach { v in
v.center = CGPoint(x: scrollView.bounds.midX, y: scrollView.bounds.midY)
v.alpha = 0.0
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// animate the markers into position
UIView.animate(withDuration: 1.0, animations: {
self.markers.forEach { v in
v.alpha = 1.0
}
self.updateMarkers()
})
}
func updateMarkers() -> Void {
markers.forEach { v in
let x = imgView.frame.origin.x + v.xPCT * imgView.frame.width
let y = imgView.frame.origin.y + v.yPCT * imgView.frame.height
// for example:
// put bottom-left corner of marker at coordinates
v.frame.origin = CGPoint(x: x, y: y - v.frame.height)
// or
// put center of marker at coordinates
//v.center = CGPoint(x: x, y: y)
}
}
func scrollViewDidZoom(_ scrollView: UIScrollView) {
updateMarkers()
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return imgView
}
}
I'm placing the markers so their bottom-left corner is at the marker-point.
It starts like this:
and looks like this after zooming-in on marker #3:

Scale and move an UIView using UIPanGestureRecognizer

I have a UIView A. I put a icon on view A and try to use pan gesture to scale and move this view A.
I have try many solution, but i can not make it.
Please suggest help me?
More detail:
I will add more detail.
I have view A, add sub on self view. And i want when i draw panGestureRegonizer on view A, view A will move follow draw.
And while moving view A will scale. View A will scale to smaller when view move to top/left/bottom/right of sreen and scale to larger when view move to center of screen.
Let's say you have vc - ViewController and your UIView A is a subview of vc. You can add UIPanGestureRecognizer to A. Then drag the panGestureRegonizer to your vc as an action:
#IBAction func panGestureAction(_ sender: UIPanGestureRecognizer) {
//your code here
}
From the sender you can check view , location and state of the action. The state might impact your code in some cases, depending on what you are trying to achieve.
Then you need to modify the action to this:
#IBAction func panGestureAction(_ sender: UIPanGestureRecognizer) {
UIView.animateKeyframes(withDuration: 0.1, delay: 0, options: UIViewKeyframeAnimationOptions.calculationModeLinear, animations: {
let location = sender.location(in: sender.view?.superview)
sender.view?.center = location
})
}
Here sender.view?.superview equals vc.view. This code snippet will detect the pan gesture, and then will move A so A.center is matching the gesture's location. Note that duration 0.1 is giving smooth animation effect to the movement.
This will give you "move" functionality with pan gesture.
EDIT for scaling:
Logic: You have coordinate system(CS) with center, x and y. When the user uses pan gesture, he/she generates sequence of points in the CS. So our task is to measure the distance between the center of the CS and users' points. When we have the furthest distance, we can calculate scale factor for our scaling view.
var center: CGPoint! //center of the CS
let maxSize: CGSize = CGSize.init(width: 100, height: 100) // maximum size of our scaling view
var maxLengthToCenter: CGFloat! //maximum distance from the center of the CS to the furthest point in the CS
private func prepareForScaling() {
self.center = self.view.center //we set the center of our CS to equal the center of the VC's view
let frame = self.view.frame
//the furthest distance in the CS is the diagonal, and we calculate it using pythagoras theorem
self.maxLengthToCenter = (frame.width*frame.width + frame.height*frame.height).squareRoot()
}
Then we need to call our setup functional to have our data ready for scaling functionality - we can do this in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
self.prepareForScaling()
}
Then we need a helper function to calculates the scaled size of our view, for user's pan gesture current position on the screen.
private func scaledSize(for location: CGPoint) -> CGSize {
//calculate location x,y differences from the center
let xDifference = location.x - self.center.x
let yDifference = location.y - self.center.y
//calculate the scale factor - note that this factor will be between 0.0(center) and 0.5(diagonal - furthest point)
//It is due our measurement - from center to view's edge. Consider multiplying this factor with your custom constant.
let scaleFactor = (xDifference*xDifference + yDifference*yDifference).squareRoot()/maxLengthToCenter
//create scaled size with maxSize and current scale factor
let scaledSize = CGSize.init(width: maxSize.width*(1-scaleFactor), height: maxSize.height*(1-scaleFactor))
return scaledSize
}
And finally, we need to modify our pan gesture action to change the size of A:
#IBAction func panGestureAction(_ sender: UIPanGestureRecognizer) {
UIView.animateKeyframes(withDuration: 0.1, delay: 0, options: UIViewKeyframeAnimationOptions.calculationModeLinear, animations: {
let location = sender.location(in: sender.view?.superview)
sender.view?.frame = CGRect.init(origin: CGPoint.init(x: 0, y: 0), size: self.scaledSize(for: location))
sender.view?.center = location
})
}

Place anchor point at the centre of the screen while doing gestures

I have a view with an image which responds to pinch, rotation and pan gestures. I want that pinching and rotation of the image would be done with respect to the anchor point placed in the middle of the screen, exactly as it is done using Xcode iPhone simulator by pressing the options key. How can I place the anchor point in the middle of the screen if the centre of the image might be scaled and panned to a different location?
Here's my scale and rotate gesture functions:
#IBAction func pinchGesture(_ gestureRecognizer: UIPinchGestureRecognizer) {
// Move the achor point of the view's layer to the touch point
// so that scaling the view and the layer becames simpler.
self.adjustAnchorPoint(gestureRecognizer: gestureRecognizer)
// Scale the view by the current scale factor.
if(gestureRecognizer.state == .began) {
// Reset the last scale, necessary if there are multiple objects with different scales
lastScale = gestureRecognizer.scale
}
if (gestureRecognizer.state == .began || gestureRecognizer.state == .changed) {
let currentScale = gestureRecognizer.view!.layer.value(forKeyPath:"transform.scale")! as! CGFloat
// Constants to adjust the max/min values of zoom
let kMaxScale:CGFloat = 15.0
let kMinScale:CGFloat = 1.0
var newScale = 1 - (lastScale - gestureRecognizer.scale)
newScale = min(newScale, kMaxScale / currentScale)
newScale = max(newScale, kMinScale / currentScale)
let transform = (gestureRecognizer.view?.transform)!.scaledBy(x: newScale, y: newScale);
gestureRecognizer.view?.transform = transform
lastScale = gestureRecognizer.scale // Store the previous scale factor for the next pinch gesture call
scale = currentScale // Save current scale for later use
}
}
#IBAction func rotationGesture(_ gestureRecognizer: UIRotationGestureRecognizer) {
// Move the achor point of the view's layer to the center of the
// user's two fingers. This creates a more natural looking rotation.
self.adjustAnchorPoint(gestureRecognizer: gestureRecognizer)
// Apply the rotation to the view's transform.
if gestureRecognizer.state == .began || gestureRecognizer.state == .changed {
gestureRecognizer.view?.transform = (gestureRecognizer.view?.transform.rotated(by: gestureRecognizer.rotation))!
// Set the rotation to 0 to avoid compouding the
// rotation in the view's transform.
angle += gestureRecognizer.rotation // Save rotation angle for later use
gestureRecognizer.rotation = 0.0
}
}
func adjustAnchorPoint(gestureRecognizer : UIGestureRecognizer) {
if gestureRecognizer.state == .began {
let view = gestureRecognizer.view
let locationInView = gestureRecognizer.location(in: view)
let locationInSuperview = gestureRecognizer.location(in: view?.superview)
// Move the anchor point to the the touch point and change the position of the view
view?.layer.anchorPoint = CGPoint(x: (locationInView.x / (view?.bounds.size.width)!), y: (locationInView.y / (view?.bounds.size.height)!))
view?.center = locationInSuperview
}
}
EDIT
I see that people aren't eager to get into this. Let me help by sharing some progress I've made in the past few days.
Firstly, I wrote a function centerAnchorPoint which correctly places the anchor point of an image to the centre of the screen regardless of where that anchor point was previously. However the image must not be scaled or rotated for it to work.
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
view.layer.position = position
view.layer.anchorPoint = anchorPoint
}
func centerAnchorPoint(gestureRecognizer : UIGestureRecognizer) {
if gestureRecognizer.state == .ended {
view?.layer.anchorPoint = CGPoint(x: (photo.bounds.midX / (view?.bounds.size.width)!), y: (photo.bounds.midY / (view?.bounds.size.height)!))
}
}
func centerAnchorPoint() {
// Position of the current anchor point
let currentPosition = photo.layer.anchorPoint
self.setAnchorPoint(CGPoint(x: 0.5, y: 0.5), forView: photo)
// Center of the image
let imageCenter = CGPoint(x: photo.center.x, y: photo.center.y)
self.setAnchorPoint(currentPosition, forView: photo)
// Center of the screen
let screenCenter = CGPoint(x: UIScreen.main.bounds.midX, y: UIScreen.main.bounds.midY)
// Distance between the centers
let distanceX = screenCenter.x - imageCenter.x
let distanceY = screenCenter.y - imageCenter.y
// Find new anchor point
let newAnchorPoint = CGPoint(x: (imageCenter.x+2*distanceX)/(UIScreen.main.bounds.size.width), y: (imageCenter.y+2*distanceY)/(UIScreen.main.bounds.size.height))
//photo.layer.anchorPoint = newAnchorPoint
self.setAnchorPoint(newAnchorPoint, forView: photo)
let dotPath = UIBezierPath(ovalIn: CGRect(x: photo.layer.position.x-2.5, y: photo.layer.position.y-2.5, width: 5, height: 5))
layer.path = dotPath.cgPath
}
Function setAchorPoint is used here to set anchor point to a new position without moving an image.
Then I updated panGesture function by inserting this at the end of it:
if gestureRecognizer.state == .ended {
self.centerAnchorPoint()
}
EDIT 2
Ok, so I'll try to simply explain the code above.
What I am doing is:
Finding the distance between the center of the photo and the center of the screen
Apply this formula to find the new position of anchor point:
newAnchorPointX = (imageCenter.x-distanceX)/screenWidth + distanceX/screenWidth
Then do the same for y position.
Set this point as a new anchor point without moving the photo using setAnchorPoint function
As I said this works great if the image is not scaled. If it is, then the anchor point does not stay at the center.
Strangely enough distanceX or distanceY doesn't exactly depend on scale value, so something like this doesn't quite work:
newAnchorPointX = (imageCenter.x-distanceX)/screenWidth + distanceX/(scaleValue*screenWidth)
EDIT 3
I figured out the scaling. It appears that the correct scale factor has to be:
scaleFactor = photo.frame.size.width/photo.layer.bounds.size.width
I used this instead of scaleValue and it worked splendidly.
So panning and scaling are done. The only thing left is rotation, but it appears that it's the hardest.
First thing I thought is to apply rotation matrix to increments in X and Y directions, like this:
let incrementX = (distanceX)/(screenWidth)
let incrementY = (distanceY)/(screenHeight)
// Applying rotation matrix
let incX = incrementX*cos(angle)+incrementY*sin(angle)
let incY = -incrementX*sin(angle)+incrementY*cos(angle)
// Find new anchor point
let newAnchorPoint = CGPoint(x: 0.5+incX, y: 0.5+incY)
However this doesn't work.
Since the question is mostly answered in the edits, I don't want to repeat myself too much.
Broadly what I changed from the code posted in the original question:
Deleted calls to adjustAnchorPoint function in pinch and rotation gesture functions.
Placed this piece of code in pan gesture function, so that the anchor point would update its position after panning the photo:
if gestureRecognizer.state == .ended {
self.centerAnchorPoint()
}
Updated centerAnchorPoint function to work for rotation.
A fully working centerAnchorPoint function (rotation included):
func centerAnchorPoint() {
// Scale factor
photo.transform = photo.transform.rotated(by: -angle)
let curScale = photo.frame.size.width / photo.layer.bounds.size.width
photo.transform = photo.transform.rotated(by: angle)
// Position of the current anchor point
let currentPosition = photo.layer.anchorPoint
self.setAnchorPoint(CGPoint(x: 0.5, y: 0.5), forView: photo)
// Center of the image
let imageCenter = CGPoint(x: photo.center.x, y: photo.center.y)
self.setAnchorPoint(currentPosition, forView: photo)
// Center of the screen
let screenCenter = CGPoint(x: UIScreen.main.bounds.midX, y: UIScreen.main.bounds.midY)
// Distance between the centers
let distanceX = screenCenter.x - imageCenter.x
let distanceY = screenCenter.y - imageCenter.y
// Apply rotational matrix to the distances
let distX = distanceX*cos(angle)+distanceY*sin(angle)
let distY = -distanceX*sin(angle)+distanceY*cos(angle)
let incrementX = (distX)/(curScale*UIScreen.main.bounds.size.width)
let incrementY = (distY)/(curScale*UIScreen.main.bounds.size.height)
// Find new anchor point
let newAnchorPoint = CGPoint(x: 0.5+incrementX, y: 0.5+incrementY)
self.setAnchorPoint(newAnchorPoint, forView: photo)
}
The key things to notice here is that the rotation matrix has to be applied to distanceX and distanceY. The scale factor is also updated to remain the same throughout the rotation.

How to constraint a view/layer's move path to a Bezier curve path?

Before asking the question, i have searched the SO:
iPhone-Move UI Image view along a Bezier curve path
But it did not give a explicit answer.
My requirement is like this, I have a bezier path, and a view(or layer if OK), I want to add pan gesture to the view, and the view(or layer)'s move path must constraint to the bezier path.
My code is below:
The MainDrawView.swift:
import UIKit
class MainDrawView: UIView {
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
drawArc()
}
func drawArc() {
let context = UIGraphicsGetCurrentContext()
// set start point
context?.move(to: CGPoint.init(x: 0, y: 400))
//draw curve
context?.addQuadCurve(to: CGPoint.init(x: 500, y: 250), control: CGPoint.init(x: UIScreen.main.bounds.size.width, y: 200))
context?.strokePath()
}
}
The ViewController.swift:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var clickButton: UIButton!
lazy var view1:UIView = {
let view: UIView = UIView.init(frame: CGRect.init(origin: CGPoint.init(x: 0, y: 0), size: CGSize.init(width: 10, height: 10)))
view.center = CGPoint.init(x: UIScreen.main.bounds.size.width / 2.0, y: UIScreen.main.bounds.size.height / 2.0)
view.backgroundColor = UIColor.red
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
initData()
initUI()
}
func initData() {
}
func initUI() {
self.clickButton.isHidden = true
// init the view
self.view.addSubview(self.view1)
}
}
Edit -1
My deep requirment is when I use pan guester move the view/layer, the view will move on the bezier path.
If bezier path is off lines then u can find the slope of the line and for every change in x or y you can calculate the position of the bezier path
var y :Float = (slope * (xPos-previousCoord.x))+previousCoord.y; xPos is continuously changing. Similarly, u can find for x. For any closed shape with line segments, you can use this.
But if u need it for circle, then u need to convert cartesian to polar. i.e.., from coordinates u can find the angle, then from that angle, you have the radius so by using that angle u need to find cartesian coordinates from that.
θ = tan-1 ( 5 / 12 )
U need to use mainly 3 coordinates one is centre of circle, the second one is your touch point, and the last one is (touchpoint.x, centreofcircle.y). from centre of circle calculate distance between two coordinates
You have θ and radius of circle then find points using
x = r × cos( θ )
y = r × sin( θ )
Don't mistake r in the image for the radius of the circle, r in the image is the hypotenuse of three coordinates. You should calculate for every change of x in touch point.
Hope it works. But for irregular shapes I am not sure how to find.
It sounds like you'll probably have to do some calculations. When you set your method to handle the UIPanGestureRecognizer, you can get a vector back for the pan, something like this:
func panGestureRecognized(_ sender: UIPanGestureRecognizer) {
let vector:CGPoint = sender.translation(in: self.view) // Or use whatever view the UIPanGestureRecognizer was added to
}
You could then use that to extrapolate the movement along the x and y axis. I would recommend starting by getting an algebraic equation for the path you're moving the view on. To move the view along that line, you're going to have to be able to calculate a point along that line relative to the movement of the UIPanGestureRecognizer, and then update the position of the view using that calculated point along the line.
I don't know if it'll work for what you're trying to do, but if you want to try animating your view along the path, it's actually pretty easy:
let path = UIBezierPath() // This would be your custom path
let animation = CAKeyframeAnimation(keyPath: "position")
animation.path = path.cgPath
animation.fillMode = kCAFillModeForwards
animation.isRemovedOnCompletion = false animation.repeatCount = 0
animation.duration = 5.0 // However long you want
animation.speed = 2 // However fast you want
animation.calculationMode = kCAAnimationPaced
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animatingView.layer.add(animation, forKey: "moveAlongPath")

Resources