My code below places a behind a drawing area. So as you can see in the image below. Anything I draw goes in front of the lines. I want to make so whatever i draw goes behind the lines. All of my code is attached below. The image is in the assets folder as well.
class ViewController: UIViewController {
var editBtn = UIButton()
var canVasView = UIView()
var path = UIBezierPath()
var startPoint = CGPoint()
var touchPoint = CGPoint()
func addArrowImageToButton(button: UIButton, arrowImage:UIImage = #imageLiteral(resourceName: "graph.png") ) {
let btnSize:CGFloat = 32
let imageView = UIImageView(image: arrowImage)
let btnFrame = button.frame
button.bringSubviewToFront(imageView)
}
override func viewDidAppear(_ animated: Bool) {
self.addArrowImageToButton(button: editBtn)
}
override func viewDidLoad() {
super.viewDidLoad()
setup()
draw()
view.addSubview(editBtn)
view.addSubview(canVasView)
editBtn.backgroundColor = UIColor.orange
canVasView.backgroundColor = UIColor.purple
editBtn.translatesAutoresizingMaskIntoConstraints = false
canVasView.translatesAutoresizingMaskIntoConstraints = false
let myLayer = CALayer()
let myImage = UIImage(named: "graph.png")?.cgImage
myLayer.frame = CGRect(x: 40, y: -80, width: 300, height: 300)
myLayer.contents = myImage
canVasView.layer.addSublayer(myLayer)
self.view.addSubview(canVasView)
NSLayoutConstraint.activate ([
canVasView.trailingAnchor.constraint(equalTo: view.centerXAnchor, constant :175),
canVasView.topAnchor.constraint(equalTo: view.centerYAnchor, constant : 100),
canVasView.widthAnchor.constraint(equalToConstant: 350),
canVasView.heightAnchor.constraint(equalToConstant: 180),
editBtn.trailingAnchor.constraint(equalTo: view.centerXAnchor, constant :175),
editBtn.topAnchor.constraint(equalTo: view.centerYAnchor, constant : 0),
editBtn.widthAnchor.constraint(equalToConstant: 350),
editBtn.heightAnchor.constraint(equalToConstant: 180),
])
editBtn.addTarget(self, action: #selector(didTapEditButton), for: .touchUpInside)
}
#objc func didTapEditButton() {
path.removeAllPoints()
canVasView.layer.sublayers = nil
canVasView.setNeedsDisplay()
self.addArrowImageToButton(button: editBtn)
}
func setup(){
editBtn.layer.cornerRadius = 20
canVasView.clipsToBounds = true
canVasView.isMultipleTouchEnabled = false
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
if let point = touch?.location(in: canVasView){
startPoint = point
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
if let point = touch?.location(in: canVasView){
touchPoint = point
}
path.move(to: startPoint)
path.addLine(to: touchPoint)
startPoint = touchPoint
draw()
}
func draw() {
let strokeLayer = CAShapeLayer()
strokeLayer.fillColor = nil
strokeLayer.lineWidth = 5
strokeLayer.strokeColor = UIColor.blue.cgColor
strokeLayer.path = path.cgPath
canVasView.layer.addSublayer(strokeLayer)
canVasView.setNeedsDisplay()
}
}
To draw behind the lines image you need to add your stroke layer behind it.
To do so, declare myLayer outside of viewDidLoad then change your draw function to:
func draw() {
let strokeLayer = CAShapeLayer()
strokeLayer.fillColor = nil
strokeLayer.lineWidth = 5
strokeLayer.strokeColor = UIColor.blue.cgColor
strokeLayer.path = path.cgPath
canVasView.layer.insertSublayer(strokeLayer, below: myLayer) //draw behind myLayer
canVasView.setNeedsDisplay()
}
Related
My swift code below draws a horiziontal way the horizontal line is at a angle that does not change. Think like a x axis. I want to draw a line in the exact opposite way. Think of the y axis. The line is drawn at
bezier.addLine(to: CGPoint(x: point.x, y: startPoint!.y))
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
var startPoint: CGPoint?
let shapeLayer: CAShapeLayer = {
let shapeLayer = CAShapeLayer()
shapeLayer.lineWidth = 4
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = UIColor.blue.cgColor
return shapeLayer
}()
override func viewDidLoad() {
super.viewDidLoad()
imageView.backgroundColor = .systemOrange
imageView.layer.addSublayer(shapeLayer)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
startPoint = touch?.location(in: imageView)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard var touch = touches.first else { return }
if let predicted = event?.predictedTouches(for: touch)?.last {
touch = predicted
}
updatePath(in: imageView, to: touch)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
updatePath(in: imageView, to: touch)
let image = UIGraphicsImageRenderer(bounds: imageView.bounds).image { _ in
imageView.drawHierarchy(in: imageView.bounds, afterScreenUpdates: true)
}
shapeLayer.path = nil
imageView.image = image
}
}
private extension ViewController {
func updatePath(in view: UIView, to touch: UITouch) {
let point = touch.location(in: view)
guard view.bounds.contains(point) else { return }
let bezier = UIBezierPath()
bezier.move(to: startPoint!)
bezier.addLine(to: CGPoint(x: point.x, y: startPoint!.y))
shapeLayer.path = bezier.cgPath
}
}
Actually you move on to startPoint and trying to add line to the same static point ... how it can add line to the same point on which you are currently residing ... add some value to Y while static X position position to add line
bezier.move(to: startPoint!)
bezier.addLine(to: CGPoint(x: startPoint!.x, y: point.y))
I've been trying to create a simple game in SK where the ball would bounce off the tops of trampolines that spawn gradually. Collisions work fine with the first trampoline but I'm having problems registering it when ball hits second trampoline spawned in run-time. Anyone might have a clue what I've been doing wrong?
Here is the code.
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var ball: SKShapeNode?
var trampoline: SKShapeNode?
var cam: SKCameraNode?
var hasHit: Bool?
var hasCollided: Bool?
var hasMoved: Bool?
func createBall() {
ball = SKShapeNode(circleOfRadius: 50)
ball?.position = CGPoint(x: 0, y: -600)
ball?.fillColor = .red
ball?.strokeColor = .red
let ballPhysics = SKPhysicsBody(circleOfRadius: 50)
ball?.physicsBody = ballPhysics
ballPhysics.categoryBitMask = 1
ballPhysics.collisionBitMask = 0
ballPhysics.contactTestBitMask = 0
ballPhysics.affectedByGravity = false //NO GRAVITY!
ballPhysics.isDynamic = true
addChild(ball!)
}
func createTrampoline(x: CGFloat, y: CGFloat) {
let rect = CGRect(x: x, y: y, width: 100, height: 20)
trampoline = SKShapeNode(rect: rect)
trampoline?.fillColor = .blue
trampoline?.strokeColor = .blue
let trampolinePhysics = SKPhysicsBody(rectangleOf: CGSize(width: 100, height: 20))
trampoline?.physicsBody = trampolinePhysics
trampolinePhysics.categoryBitMask = 2
trampolinePhysics.collisionBitMask = 0
trampolinePhysics.contactTestBitMask = 1
trampolinePhysics.affectedByGravity = false //NO GRAVITY!
trampolinePhysics.isDynamic = true
addChild(trampoline!)
}
override func didMove(to view: SKView) {
physicsWorld.contactDelegate = self
cam = SKCameraNode()
self.camera = cam
self.backgroundColor = .darkText
hasHit = false
hasCollided = false
hasMoved = false
createBall()
createTrampoline(x: -50, y: -10)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touches: AnyObject in touches {
let location = touches.location(in: self)
let move = SKAction.moveTo(x: location.x, duration: 0.1)
ball?.run(move)
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if (!hasMoved!) {
let vect = CGVector(dx: 0, dy: 600)
ball?.physicsBody?.applyImpulse(vect)
ball?.physicsBody?.affectedByGravity = true
hasMoved = true
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
if (hasHit == true) {
cam?.position.y = (ball?.position.y)!
}
}
func didBegin(_ contact: SKPhysicsContact) {
let vect = CGVector(dx: 0, dy: 1500)
if (((contact.bodyB.node?.position.y)! - (contact.bodyA.node?.position.y)!) > 0) {
if (!hasCollided!){
hasHit = true
hasCollided = true
print("collision")
contact.bodyB.applyImpulse(vect)
createTrampoline(x: -50, y: ((ball?.position.y)! + CGFloat(3000)))
}
} else {
print("bad coll")
hasCollided = false
}
}
}
Thanks in advance.
I would like the user to touch 2 points and then a line is drawn between those two points. Here is what I have so far:
func drawline(){
let context = UIGraphicsGetCurrentContext()
context!.beginPath()
context?.move(to: pointA)
context?.addLine(to: pointB)
context!.strokePath()
}
pointA is the first point the user touched and pointB is the second point. I get the error:
thread 1:EXC_BREAKPOINT
Thanks in advance for your help.
To draw a line between two points the first thing you need is get the CGPoints from the current UIView, there are several ways of achieve this. I going to use an UITapGestureRecognizer for the sake of the sample to detect when you make a tap.
The another step is once you have the two points saved draw the line between the two points, and for this again you can use the graphics context as you try before or use CAShapeLayer.
So translating the explained above we get the following code:
class ViewController: UIViewController {
var tapGestureRecognizer: UITapGestureRecognizer!
var firstPoint: CGPoint?
var secondPoint: CGPoint?
override func viewDidLoad() {
super.viewDidLoad()
tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.showMoreActions(touch:)))
tapGestureRecognizer.numberOfTapsRequired = 1
view.addGestureRecognizer(tapGestureRecognizer)
}
func showMoreActions(touch: UITapGestureRecognizer) {
let touchPoint = touch.location(in: self.view)
guard let _ = firstPoint else {
firstPoint = touchPoint
return
}
guard let _ = secondPoint else {
secondPoint = touchPoint
addLine(fromPoint: firstPoint!, toPoint: secondPoint!)
firstPoint = nil
secondPoint = nil
return
}
}
func addLine(fromPoint start: CGPoint, toPoint end:CGPoint) {
let line = CAShapeLayer()
let linePath = UIBezierPath()
linePath.move(to: start)
linePath.addLine(to: end)
line.path = linePath.cgPath
line.strokeColor = UIColor.red.cgColor
line.lineWidth = 1
line.lineJoin = kCALineJoinRound
self.view.layer.addSublayer(line)
}
}
The above code is going to draw a line every time two points are selected and you can customize the above function as you like.
I hope this help you.
Draw line in Swift 4.1
class MyViewController: UIViewController {
#IBOutlet weak var imgViewDraw: UIImageView!
var lastPoint = CGPoint.zero
var red: CGFloat = 0.0
var green: CGFloat = 0.0
var blue: CGFloat = 0.0
var brushWidth: CGFloat = 10.0
var opacity: CGFloat = 1.0
var isSwiping:Bool!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK: Touch events
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
isSwiping = false
if let touch = touches.first{
lastPoint = touch.location(in: imgViewDraw)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
isSwiping = true;
if let touch = touches.first{
let currentPoint = touch.location(in: imgViewDraw)
UIGraphicsBeginImageContext(self.imgViewDraw.frame.size)
self.imgViewDraw.image?.draw(in: CGRect(x:0, y:0,width:self.imgViewDraw.frame.size.width, height:self.imgViewDraw.frame.size.height))
UIGraphicsGetCurrentContext()?.move(to: CGPoint(x: lastPoint.x, y: lastPoint.y))
UIGraphicsGetCurrentContext()?.addLine(to: CGPoint(x: currentPoint.x, y: currentPoint.y))
UIGraphicsGetCurrentContext()?.setLineCap(CGLineCap.round)
UIGraphicsGetCurrentContext()?.setLineWidth(self.brushWidth)
UIGraphicsGetCurrentContext()?.setStrokeColor(red: red, green: green, blue: blue, alpha: 1.0)
UIGraphicsGetCurrentContext()?.strokePath()
self.imgViewDraw.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
lastPoint = currentPoint
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if(!isSwiping) {
// This is a single touch, draw a point
UIGraphicsBeginImageContext(self.imgViewDraw.frame.size)
self.imgViewDraw.image?.draw(in: CGRect(x:0, y:0,width:self.imgViewDraw.frame.size.width, height:self.imgViewDraw.frame.size.height))
UIGraphicsGetCurrentContext()?.setLineCap(CGLineCap.round)
UIGraphicsGetCurrentContext()?.setLineWidth(self.brushWidth)
UIGraphicsGetCurrentContext()?.move(to: CGPoint(x: lastPoint.x, y: lastPoint.y))
UIGraphicsGetCurrentContext()?.addLine(to: CGPoint(x: lastPoint.x, y: lastPoint.y))
UIGraphicsGetCurrentContext()?.setStrokeColor(red: red, green: green, blue: blue, alpha: 1.0)
UIGraphicsGetCurrentContext()?.strokePath()
self.imgViewDraw.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
}
}
I want to make a curve like, when user moved their finger, the curve changed its radian, but the code I did was seems something wrong, with many curve when I moved my finger:(
Here was my code below, and followed the terrible build's screenshot. Actually I also want to how to implement this function with UIPanGestureRecognizer.
import SpriteKit
import AVFoundation
import UIKit
class Home: SKScene {
var happiness:CGFloat {
return currentPoint.y
}
var currentPoint = CGPoint(x: 0, y: 1024)
override func didMoveToView(view: SKView) {
backgroundColor = UIColor.blueColor()
}
func drawhappiness() {
let pathNew2 = CGPathCreateMutable()
let startx:CGFloat = 268
let starty:CGFloat = 1024
let cp1x:CGFloat = 568
let cp1y:CGFloat = happiness
let cp2x:CGFloat = 968
let cp2y:CGFloat = happiness
let endx:CGFloat = 1292
let endy:CGFloat = 1024
CGPathMoveToPoint(pathNew2, nil, startx, starty)
CGPathAddCurveToPoint(pathNew2, nil, cp1x, cp1y, cp2x, cp2y, endx, endy)
let shapeNew2 = SKShapeNode()
shapeNew2.path = pathNew2
shapeNew2.strokeColor = UIColor.greenColor()
shapeNew2.lineWidth = 10
addChild(shapeNew2)
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch:AnyObject in touches {
currentPoint = touch.locationInNode(self)
}
}
override func update(currentTime: NSTimeInterval) {
drawhappiness()
}
}
You are adding a new shape node to the scene each time you move your finger. To fix this, create a single shape with global scope in Home, configure the shape in didMoveToView (i.e., set line width, color), and update the shape's path property in drawhappiness.
For example,
class Home: SKScene {
let shapeNew2 = SKShapeNode()
var currentPoint = CGPoint(x: 0, y: 1024)
var happiness:CGFloat {
return currentPoint.y
}
override func didMoveToView(view: SKView) {
backgroundColor = UIColor.blueColor()
shapeNew2.strokeColor = UIColor.greenColor()
shapeNew2.lineWidth = 10
addChild(shapeNew2)
}
func drawhappiness() {
let pathNew2 = CGPathCreateMutable()
let startx:CGFloat = 268
let starty:CGFloat = 1024
let cp1x:CGFloat = 568
let cp1y:CGFloat = happiness
let cp2x:CGFloat = 968
let cp2y:CGFloat = happiness
let endx:CGFloat = 1292
let endy:CGFloat = 1024
CGPathMoveToPoint(pathNew2, nil, startx, starty)
CGPathAddCurveToPoint(pathNew2, nil, cp1x, cp1y, cp2x, cp2y, endx, endy)
shapeNew2.path = pathNew2
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch:AnyObject in touches {
currentPoint = touch.locationInNode(self)
}
}
override func update(currentTime: NSTimeInterval) {
drawhappiness()
}
}
I am trying to allow users to crop an image. The problem is the cropped image is not the same as the view. Here are two screenshots to show what I mean.
pre crop screen:
!http://i58.tinypic.com/4rap9u.png
after crop
!http://i58.tinypic.com/2nrpjlj.png
Here is the code I use to create the new cropped image. The UIImageView is inside a UIScrollView to allow user to zoom and pan. Even when the image has not been zoomed it does not work correctly. Ideally I would like to allow the user to move the dashed square around the screen to select a portion of the image to crop, as well as zoom in/out.
func croppedImage() -> UIImage?
{
var drawRect: CGRect = CGRectZero
var cropRect : CGRect!
cropRect = photoFrameView?.frame
zoom = imgScroll.zoomScale
println(zoom)
drawRect.size = imgPreview.bounds.size
drawRect.origin.x = round(-cropRect.origin.x * zoom)
drawRect.origin.y = round(-cropRect.origin.y * zoom)
cropRect.size.width = round(cropRect.size.width*zoom)
cropRect.size.height = round(cropRect.size.height*zoom)
cropRect.origin.x = round(cropRect.origin.x)
cropRect.origin.y = round(cropRect.origin.y)
UIGraphicsBeginImageContextWithOptions(cropRect.size, false, UIScreen.mainScreen().scale)
self.imgPreview.image?.drawInRect(drawRect)
var result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext();
return result
}
here is where I crop the image:
let cImg = croppedImage()!
UIImageWriteToSavedPhotosAlbum(cImg, nil, nil, nil);
imgPreview.image = cImg
self.imgPreview.contentMode = .ScaleAspectFit
Also here is the code for my UIView subclass(photoFrameView). Not sure if it is needed.
import UIKit
class mycropView: UIView {
var lastLocation:CGPoint = CGPointMake(0, 0)
override init(frame: CGRect) {
super.init(frame: frame)
// Initialization code
var panRecognizer = UIPanGestureRecognizer(target:self, action:"detectPan:")
self.gestureRecognizers = [panRecognizer]
self.addDashedBorder()
self.backgroundColor = UIColor.clearColor()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func detectPan(recognizer:UIPanGestureRecognizer) {
var translation = recognizer.translationInView(self.superview!)
self.center = CGPointMake(lastLocation.x + translation.x, lastLocation.y + translation.y)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
// Promote the touched view
self.superview?.bringSubviewToFront(self)
// Remember original location
lastLocation = self.center
}
override func hitTest(point: CGPoint, withEvent e: UIEvent?) -> UIView? {
if let result = super.hitTest(point, withEvent:e) {
println("hit inside")
return result
} else {
self.removeFromSuperview()
}
return nil
}
}
extension UIView {
func addDashedBorder() {
let color = UIColor.yellowColor().CGColor
let shapeLayer:CAShapeLayer = CAShapeLayer()
let frameSize = self.frame.size
let shapeRect = CGRect(x: 0, y: 0, width: frameSize.width, height: frameSize.height)
shapeLayer.bounds = shapeRect
shapeLayer.position = CGPoint(x: frameSize.width/2, y: frameSize.height/2)
shapeLayer.fillColor = UIColor.clearColor().CGColor
shapeLayer.strokeColor = color
shapeLayer.lineWidth = 2
shapeLayer.lineJoin = kCALineJoinRound
shapeLayer.lineDashPattern = [6,3]
shapeLayer.path = UIBezierPath(roundedRect: shapeRect, cornerRadius: 5).CGPath
self.layer.addSublayer(shapeLayer)
}
}