How to move line with finger? - ios

I am defining the following variables in my ViewController for a line.
var scanlineRect = CGRect.zero
var scanlineStartY: CGFloat = 0
var scanlineStopY: CGFloat = 0
var topBottomMargin: CGFloat = 30
var scanLine: UIView = UIView()
I can draw a line using UIView and can move it in vertical direction using UIView.animate method. How to move a line dragging it with finger?
func drawLine()
{
self.view.addSubview(scanLine)
scanLine.backgroundColor = UIColor.black
scanlineRect = CGRect(x: 15, y: 0, width: self.view.frame.width - 30, height: 5)
scanlineStartY = topBottomMargin
scanlineStopY = self.view.frame.height - topBottomMargin
scanLine.frame = scanlineRect
scanLine.center = CGPoint(x: scanLine.center.x, y: scanlineStartY)
scanLine.isHidden = false
}
func Move(Location : CGPoint)
{
scanLine.isHidden = false
weak var weakSelf = scanLine
UIView.animate(withDuration: 3.0, animations: {
weakSelf!.center = CGPoint(x: weakSelf!.center.x, y: Location.y)
}, completion: nil)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
var lastPoint = CGPoint.zero
if let touch = touches.first
{
lastPoint = touch.location(in: self.view)
}
Move(Location: lastPoint)
}
override func viewDidLoad()
{
super.viewDidLoad()
drawLine()
}

Try using touchesMoved instead of touchesBegan
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
guard let location = touch?.location(in: self.view) else{
return
}
scanLine.frame.origin = CGPoint(x: location.x-scanLine.frame.size.width/2, y: location.y-scanLine.frame.size.height/2)
}
updated
If you want only move in y axis only you need to change this line
scanLine.frame.origin = CGPoint(x: location.x-scanLine.frame.size.width/2, y: location.y-scanLine.frame.size.height/2)
use this instead note that scanline.frame.origin.x is unchanged
scanLine.frame.origin = CGPoint(x: scanLine.frame.origin.x, y: location.y-scanLine.frame.size.height/2)

Related

How to draw a line from one point to the next point in Swift

I would like to know how to draw a line from one point to another where the user touch the first point and the next point he touches, it create a line in between. I know that using UIBezierPath will be useful in here but I am still new to Swift and iOS platform so any guidance will helpful.
I have created a function called drawLineFromPoint but does not work for me.
import UIKit
import GLKit
class ViewController: GLKViewController {
private var context: EAGLContext?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
EAGLcontext()
// Gesture Code
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first{
let position = touch.location(in: view)
var dot = UIView(frame: CGRect(x: position.x, y: position.y, width: 10, height: 10))
dot.backgroundColor = .red
view.addSubview(dot)
// View the x and y coordinates
print(position)
}
}
//Not sure how to do this part ???
func drawLineFromPoint(start : CGPoint, toPoint end:CGPoint, ofColor lineColor: UIColor, inView view:UIView) {
let path = UIBezierPath()
path.move(to: start)
path.addLine(to: end)
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = UIColor.green.cgColor
shapeLayer.lineWidth = 1.0
view.layer.addSublayer(shapeLayer)
}
//Create EAGL Context for the GLKView
private func EAGLcontext() {
context = EAGLContext(api: .openGLES2)
EAGLContext.setCurrent(context)
if let view = self.view as? GLKView, let context = context {
view.context = context
delegate = self
}
}
override func glkView(_ view: GLKView, drawIn rect: CGRect) {
//Set the color
glClearColor(0.85, 0.85, 0.85, 1.0)
glClear(GLbitfield(GL_COLOR_BUFFER_BIT))
}
}
extension ViewController: GLKViewControllerDelegate {
func glkViewControllerUpdate(_ controller: GLKViewController) {
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Based On your code I changed some things and this code Works please check.
class ViewController: UIViewController {
var lastPosition: CGPoint?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Gesture Code
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first{
let position = touch.location(in: view)
if let lastPosition = self.lastPosition {
self.drawLineFromPoint(start: lastPosition, toPoint: position, ofColor: UIColor.red, inView: self.view)
}
self.lastPosition = position
// View the x and y coordinates
let dot = UIView(frame: CGRect(x: position.x, y: position.y, width: 10, height: 10))
dot.backgroundColor = .red
view.addSubview(dot)
print(position)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first, let lastPosition = self.lastPosition{
let position = touch.location(in: view)
self.drawLineFromPoint(start: lastPosition, toPoint: position, ofColor: UIColor.red, inView: self.view)
self.lastPosition = position
let dot = UIView(frame: CGRect(x: position.x, y: position.y, width: 10, height: 10))
dot.backgroundColor = .red
view.addSubview(dot)
}
}
//Not sure how to do this part ???
func drawLineFromPoint(start : CGPoint, toPoint end:CGPoint, ofColor lineColor: UIColor, inView view:UIView) {
let path = UIBezierPath()
path.move(to: start)
path.addLine(to: end)
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = UIColor.green.cgColor
shapeLayer.lineWidth = 1.0
view.layer.addSublayer(shapeLayer)
}
}

How can I alter my code to make my sprite, which rotates in a circular orbit, jump up then back down when a user taps the screen?

I'm trying to make my orange circle sprite, which orbits in a circular motion, jump at ant point in its rotation and fall back down again when the user taps the screen but can't seem to figure out what I'm doing wrong. Please help.
I've tried taking the advice to somewhat similar post o here but have had no luck.
class GameScene: SKScene {
let sprite = SKSpriteNode(imageNamed: "circle")
let player = SKSpriteNode(imageNamed: "player")
var node2AngularDistance: CGFloat = 0
override func didMove(to view: SKView) {
backgroundColor = SKColor(red: 94.0/255, green: 63.0/255, blue: 107.0/255, alpha: 1)
physicsWorld.gravity = CGVector(dx: 0, dy: 0)
sprite.size = CGSize(width: 150, height: 150)
sprite.position = CGPoint(x: 0,y: 0)
sprite.physicsBody = SKPhysicsBody(circleOfRadius: 10)
player.size = CGSize(width: 100, height: 100)
player.position = CGPoint(x: 0+50, y: 0)
player.physicsBody = SKPhysicsBody(circleOfRadius: 10)
self.addChild(sprite)
self.addChild(player)
}
//dont touch blue lines that discard code and don't allow you to undo
override func update(_ currentTime: TimeInterval) {
let dt: CGFloat = 1.0/60.0 //Delta Time
let period: CGFloat = 3 //Number of seconds it takes to complete 1 orbit.
let orbitPosition = sprite.position //Point to orbit.
let orbitRadius = CGPoint(x: 150, y: 150) //Radius of orbit.
let normal = CGVector(dx:orbitPosition.x + CGFloat(cos(self.node2AngularDistance))*orbitRadius.x ,dy:orbitPosition.y + CGFloat(sin(self.node2AngularDistance))*orbitRadius.y);
self.node2AngularDistance += (CGFloat(Double.pi)*2.0)/period*dt;
if (abs(self.node2AngularDistance)>CGFloat(Double.pi)*2) {
self.node2AngularDistance = 0
}
player.physicsBody!.velocity = CGVector(dx:(normal.dx-player.position.x)/dt ,dy:(normal.dy-player.position.y)/dt);
//func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
//sprite.position = (touches.first! as! UITouch).location(in: self)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchDown(atPoint: t.location(in: self)) }
}
func touchDown(atPoint pos: CGPoint) {
jump()
}
func jump() {
player.texture = SKTexture(imageNamed: "player")
player.physicsBody?.applyImpulse(CGVector(dx: 0, dy: 500))
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchUp(atPoint: t.location(in: self)) }
}
func touchUp(atPoint pos: CGPoint) {
player.texture = SKTexture(imageNamed: "player")
}
}
The expected results would be a small circle sprite called player that rotates in circular orbit around a larger circle called sprite and when the user taps the screen, the player(small circle) would jump up then back down at any point in its orbit.The actual results are the the circle rotates around the larger circle but does not jump up when I click on the screen in the iPhone simulator.
A jumpping state needs to record as : isJumpping. If isJumpping, the update will not perform, if this is what you need.
The second issue is after touching up, the player falls back to the orbital. A simple math is given here as the player directly falling to the circle.
But if the situation is complicated, you may consider to use PhysicsField to simulate the situation.
This is running in a Xcode 10 and playground
import UIKit
import PlaygroundSupport
import SpriteKit
import simd
class GameScene: SKScene {
let sprite = SKSpriteNode(imageNamed: "circle")
let player = SKSpriteNode(imageNamed: "player")
var node2AngularDistance: CGFloat = 0
override func didMove(to view: SKView) {
print(100)
// backgroundColor = SKColor(red: 94.0/255, green: 63.0/255, blue: 107.0/255, alpha: 1)
backgroundColor = SKColor.black
physicsWorld.gravity = CGVector(dx: 0, dy: 0)
sprite.size = CGSize(width: 150, height: 150)
sprite.position = CGPoint(x: 0,y: 0)
sprite.physicsBody = SKPhysicsBody(circleOfRadius: 10)
player.size = CGSize(width: 100, height: 100)
player.position = CGPoint(x: 0+50, y: 0)
player.physicsBody = SKPhysicsBody(circleOfRadius: 10)
self.anchorPoint = CGPoint.init(x: 0.5, y: 0.5)
self.addChild(sprite)
self.addChild(player)
}
//dont touch blue lines that discard code and don't allow you to undo
override func update(_ currentTime: TimeInterval) {
guard !isJumpping else {
return
}
let dt: CGFloat = 1.0/60.0 //Delta Time
let period: CGFloat = 3 //Number of seconds it takes to complete 1 orbit.
let orbitPosition = sprite.position //Point to orbit.
let orbitRadius = CGPoint(x: 150, y: 150) //Radius of orbit.
let currentDistance = distance(double2(x: Double(orbitPosition.x), y: Double(orbitPosition.y)), double2(x: Double(player.position.x), y: Double(player.position.y)))
if ( currentDistance > 150){
player.physicsBody!.velocity = CGVector(dx:(orbitPosition.x - player.position.x) ,dy:(orbitPosition.y - player.position.y));
let x = Double(player.position.x) - Double(orbitPosition.x)
let y = Double(player.position.y) - Double(orbitPosition.y)
self.node2AngularDistance = CGFloat(acos(x/currentDistance))
if y < 0 {
self.node2AngularDistance = 2 * CGFloat.pi - self.node2AngularDistance }
return;
}
let normal = CGVector(dx:orbitPosition.x + CGFloat(cos(self.node2AngularDistance))*orbitRadius.x ,dy:orbitPosition.y + CGFloat(sin(self.node2AngularDistance))*orbitRadius.y);
self.node2AngularDistance += (CGFloat(Double.pi)*2.0)/period*dt;
if (abs(self.node2AngularDistance)>CGFloat(Double.pi)*2) {
self.node2AngularDistance = 0
}
player.physicsBody!.velocity = CGVector(dx:(normal.dx-player.position.x)/dt ,dy:(normal.dy-player.position.y)/dt);
//func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
//sprite.position = (touches.first! as! UITouch).location(in: self)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchDown(atPoint: t.location(in: self)) }
}
func touchDown(atPoint pos: CGPoint) {
jump()
}
private var isJumpping : Bool = false
func jump() {
isJumpping.toggle()
player.texture = SKTexture(imageNamed: "player")
player.physicsBody?.applyImpulse(CGVector(dx: (self.player.position.x - self.sprite.position.x) / 100.0, dy: (self.player.position.y - self.sprite.position.y) / 100.0))
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchUp(atPoint: t.location(in: self)) }
}
func touchUp(atPoint pos: CGPoint) {
isJumpping.toggle()
player.texture = SKTexture(imageNamed: "player")
}
}
class GameViewCointroller: UIViewController{
var skview: SKView!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
skview = SKView.init(frame: view.bounds)
let scene = GameScene.init(size: view.frame.size)
skview.presentScene(scene)
view.addSubview(skview)
}
}
PlaygroundPage.current.liveView = GameViewCointroller()

Drawing a rectangle on UIImageView using Core Graphics

I want to let the User to draw a rectangle on UIImageView
I added two variables for first an last touch locations
I added this function:-
func draw(from: CGPoint, to: CGPoint) {
UIGraphicsBeginImageContext(imageView.frame.size)
context = UIGraphicsGetCurrentContext()
context?.setStrokeColor(UIColor(red: 0, green: 0, blue: 0, alpha: 1.0).cgColor)
context?.setLineWidth(5.0)
let currentRect = CGRect(x: from.x,
y: from.y,
width: to.x - from.x,
height: to.y - from.y)
context?.addRect(currentRect)
context?.drawPath(using: .stroke)
context?.strokePath()
imageView.image?.draw(in: self.imageView.frame)
imageView.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
I add the method to (touchMoved) it draws many rectangles
I add the method to (touchEnded) it draws one, but it does not appear when the user move the touch
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
firstTouchLocation = touch.location(in: self.view)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
lastTouchLocation = touch.location(in: self.view)
draw(from: firstTouchLocation, to: lastTouchLocation)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
lastTouchLocation = touch.location(in: self.view)
draw(from: firstTouchLocation, to: lastTouchLocation)
}
}
I want to let the user extend the rectangle when touchMoved and draw when the touchEnded.
You are replacing your image with a new image composed of the previous image plus a rectangle drawn over it. Rather than drawing the image from the image view, draw the original image.
Alternatively, you could render the the rectangle as a shape layer and just update that shape layer's path:
class ViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
private let shapeLayer: CAShapeLayer = {
let _shapeLayer = CAShapeLayer()
_shapeLayer.fillColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0).cgColor
_shapeLayer.strokeColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1).cgColor
_shapeLayer.lineWidth = 3
return _shapeLayer
}()
private var startPoint: CGPoint!
override func viewDidLoad() {
super.viewDidLoad()
imageView.layer.addSublayer(shapeLayer)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
startPoint = touches.first?.location(in: imageView)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let startPoint = startPoint, let touch = touches.first else { return }
let point: CGPoint
if let predictedTouch = event?.predictedTouches(for: touch)?.last {
point = predictedTouch.location(in: imageView)
} else {
point = touch.location(in: imageView)
}
updatePath(from: startPoint, to: point)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let startPoint = startPoint, let touch = touches.first else { return }
let point = touch.location(in: imageView)
updatePath(from: startPoint, to: point)
imageView.image = imageView.snapshot(afterScreenUpdates: true)
shapeLayer.path = nil
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
shapeLayer.path = nil
}
private func updatePath(from startPoint: CGPoint, to point: CGPoint) {
let size = CGSize(width: point.x - startPoint.x, height: point.y - startPoint.y)
let rect = CGRect(origin: startPoint, size: size)
shapeLayer.path = UIBezierPath(rect: rect).cgPath
}
}
Where:
extension UIView {
func snapshot(afterScreenUpdates: Bool = false) -> UIImage {
UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0)
drawHierarchy(in: bounds, afterScreenUpdates: afterScreenUpdates)
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
}
This is not only simpler, but more efficient, too.
That yields:
By the way, I might suggest using predictive touches in touchesMoved. On a device (not the simulator) that can yield a slightly more responsive UI.

Image gets blurry and zoomed out when erasing

I am using functionality of erasing image and my code is like below. You can see Video HERE. Here current_sticker_img is my Imageview
NOTE: I am also using Pan Gesture for zooming image
//MARK: Eraser touch event
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if btn_eraser.isSelected == true || btn_repaint.isSelected == true{
let touch : UITouch = touches.first!
lastpoint = touch.location(in: current_sticker_img)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if btn_eraser.isSelected == true || btn_repaint.isSelected == true{
let touch = touches.first!
let currentPoint : CGPoint = touch.location(in: current_sticker_img)
let isPointInsideView = current_sticker_img.point(inside: currentPoint, with: event)
if isPointInsideView
{
UIGraphicsBeginImageContext(current_sticker_img.frame.size)
// UIGraphicsBeginImageContextWithOptions(CGSize(width: (current_sticker_img.image?.size.width)!, height: (current_sticker_img.image?.size.height)!), false, (current_sticker_img.image?.scale)!)
current_sticker_img
.image?.draw(in: CGRect(x: 0, y: 0, width: (current_sticker_img.frame.size.width), height: (current_sticker_img.frame.size.height)))
UIGraphicsGetCurrentContext()!.setLineCap(.round)
UIGraphicsGetCurrentContext()?.setLineWidth(Current_slider_value_for_Eraser)
UIGraphicsGetCurrentContext()?.setShadow(offset: CGSize(width: CGFloat(0), height: CGFloat(0)), blur: 0, color: UIColor.clear.cgColor )
UIGraphicsGetCurrentContext()!.setBlendMode(.clear)
UIGraphicsGetCurrentContext()?.move(to: CGPoint(x: CGFloat(lastpoint.x), y: CGFloat(lastpoint.y)))
UIGraphicsGetCurrentContext()?.addLine(to: CGPoint(x: CGFloat(currentPoint.x), y: CGFloat(currentPoint.y)))
UIGraphicsGetCurrentContext()!.strokePath()
current_sticker_img.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
lastpoint = currentPoint
}
}
}
What I am doing wrong ? Please help me with it.
Thank you

Why when I draw something it doesn't appear where my finger was moving?

The line appears a couple inches away from where I wanted it to be drawn. Im making a drawing app in SpriteKit and I try to draw something on the bottom of my UIImageView but it appears couple inches away from where my finger was. Why does that happen?
class GameScene: SKScene {
var drawImageView: UIImageView! = UIImageView()
var lastPoint = CGPoint.zero
var swiped = false
override func didMove(to view: SKView) {
drawImageView.frame = previewCamera.bounds
previewCamera.addSubview(drawImageView)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
swiped = false
if let touch = touches.first {
lastPoint = touch.location(in: self.view)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
swiped = true
if let touch = touches.first {
let currentPoint = touch.location(in: self.view)
draw(fromPoint: lastPoint, toPoint: currentPoint)
lastPoint = currentPoint
}
}
func draw(fromPoint:CGPoint,toPoint:CGPoint) {
UIGraphicsBeginImageContext((self.view?.frame.size)!)
drawImageView.image?.draw(in: CGRect(x: 0, y: 0, width: (self.view?.frame.width)!, height: (self.view?.frame.height)!))
let context = UIGraphicsGetCurrentContext()
context?.move(to: CGPoint(x: fromPoint.x, y: fromPoint.y))
context?.addLine(to: CGPoint(x: toPoint.x, y: toPoint.y))
context?.setBlendMode(CGBlendMode.normal)
context?.setLineCap(CGLineCap.round)
context?.setLineWidth(7)
context?.setStrokeColor(UIColor(red: red, green: green, blue: blue, alpha: 1.0).cgColor)
context?.strokePath()
drawImageView.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if !swiped {
draw(fromPoint: lastPoint, toPoint: lastPoint)
}
}
}

Resources