Draw Trails in Xcode - swift

I am working on a screensaver in Xcode. I have an array of points and am changing their position s every frame in the override func animateOneFrame(). How would I have the points create a fading trail similar to this: https://www.youtube.com/watch?v=fDSIRXmnVvk&t=117s&ab_channel=CodeParade
For reference, in my draw function I have this and it only displays the points, no trail. It effectively draws a black background every frame even though I don't tell it to:
override func draw(_ rect: NSRect) {
for point in 1..<PointArray.count {
drawPoint(p: PointArray[point])
}
}
private func drawPoint(p: point) {
let pixel = NSRect(x: p.position.x,
y: p.position.y,
width: 3,
height: 3)
p.color.setFill()
pixel.fill()
}

Related

How to make a fading eraser using CGMutablePath?

I have a CGMutablePath of touch event handling. I can able to erase clean path by using below code. But I want to make a fading-out eraser that is shows on below image. I tried it to make this for some days but failed to do that. Fading-out means showing less alpha around touches area of eraser.
var path = CGMutablePath()
public override func draw(_ rect: CGRect) {
guard let ctx = UIGraphicsGetCurrentContext() else { return }
ctx.saveGState()
ctx.addPath(path)
ctx.setLineCap(.round)
ctx.setLineWidth(20.0)
ctx.setBlendMode(.clear)
ctx.strokePath()
ctx.restoreGState()
}

custom overlay(renderer) is getting cut of by map tiles (in some cases)

I wrote a custom renderer to represent a min and a max Radius. In some cases the renderer is not working as expected. It looks like the overlay is getting cut of by the map tiles.
See the full video
Here is how I did it. Did I miss something?
class RadiusOverlayRenderer: MKOverlayRenderer {
override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) {
guard let overlay = self.overlay as? RadiusOverlay else {
return
}
let maxRadiusRect = self.rect(for: overlay.boundingMapRect)
.offsetBy(
dx: CGFloat(-overlay.boundingMapRect.height)/2,
dy: CGFloat(-overlay.boundingMapRect.width)/2
)
let minRadiusRect = CGRect(
x: Double(maxRadiusRect.midX)-overlay.minRadRect.width/2,
y: Double(maxRadiusRect.midY)-overlay.minRadRect.height/2,
width: overlay.minRadRect.width,
height: overlay.minRadRect.height)
let aPath = CGMutablePath()
aPath.addEllipse(in: maxRadiusRect)
aPath.addEllipse(in: minRadiusRect)
aPath.closeSubpath()
context.setFillColor(overlay.color.cgColor)
context.setAlpha(overlay.alpha)
context.addPath(aPath)
context.drawPath(using: .eoFillStroke)
}
}
Notice that only the upper left parts are clipped?
with .offsetBy you are drawing outside of the .boundingMapRect.
Remove the .offsetBy...
If you want to draw your circle at a different place, then adjust coordinate and / or boundingMapRect of your MKOverlay.

How could I get mouse click coordinates using Swift on MacOS?

I try to find out about getting mouse click coordinates to draw a line. I would like to make two clicks (first and second dots) and a line'll be create.
I analysed a lot of codes, but they are huge (for example, I'm fond of this way https://stackoverflow.com/a/47496766/9058168).
I hope drawing line in Swift isn't difficult. Could I type another variable which has mouse click coordinates instead of numeral coordinates (look at code below)? If your answer is true, how to code it? Help me, please, to make it simplier.
import Cocoa
class DrawLine: NSView {
override func draw(_ dirtyRect: NSRect) {
NSBezierPath.strokeLine(from: CGPoint(x: 20, y: 20), to: CGPoint(x: 0, y: 100))
}
}
Listen for the mouse down event and set start location and end location using it to draw the path.
import Cocoa
class DrawLine: NSView {
var startPoint:NSPoint?
var endPoint:NSPoint?
override func mouseDown(with event: NSEvent){
if startPoint == nil || self.endPoint != nil{
self.startPoint = event.locationInWindow
} else {
self.endPoint = event.locationInWindow
self.needsDisplay = true
}
}
override func draw(_ dirtyRect: NSRect) {
if let theStart = startPoint, let theEnd = endPoint{
NSBezierPath.strokeLine(from: theStart, to: theEnd)
}
}
}

Creating endless background without image

I'm creating a simple game with Swift and SpriteKit.
I want to add an endless background (vertically), I only found answers for background with images but I need to do it without image, only background color.
I thought about checking if the player is in the frame.maxY, if so, to move it back to the starting point, but I was wondering if there is a better idea.
//Does not matter which ring we chose as all ring's 'y' position is the same.
func moveBackgroundUp(){
if ((mPlayer.position.y >= self.frame.maxY) || (mRingOne.position.y >= self.frame.maxY)) {
mPlayer.position.y = 150 //mPlayers original starting point.
for ring in mRings {
ring.position.y = 350
}
}
}
Thanks in advance!
Don't just move a background up the screen, that' really isn't the way to go about it. What you should do is detect the position of the camera (assuming it moves with the player), and when it's position is about to occupy space outside of the occupied space of your current background sprite, add a new background sprite to the scene where the last one left off. Here is an example of how to do that with just a red sprite:
First add a property to the scene to track level position:
// To track the y-position of the level
var levelPositionY: CGFloat = 0.0
Now create a method to update your background:
func updateBackground() {
let cameraPos = camera!.position
if cameraPos.y > levelPositionY - (size.height * 0.55) {
createBackground()
}
}
func createBackground() {
// Create a new sprite the size of your scene
let backgroundSprite = SKSpriteNode(color: .red, size: size)
backgroundSprite.anchorPoint = CGPoint(x: 0.5, y: 0)
backgroundSprite.position = CGPoint(x: 0, y: levelPositionY)
// Replace backgroundNode with the name of your backgroundNode to add the sprite to
backgroundNode.addChild(backgroundSprite)
levelPositionY += backgroundSprite.size.height
}
Now you want to call updateBackground inside your overridden update(_:) method:
override func update(_ currentTime: TimeInterval) {
// All your other update code
updateBackground()
}
Also, make sure to create an initial background when you first create the scene:
override func didMove(to view: SKView) {
createBackground()
}
NOTE! - It's important to set the custom anchor point for the background sprite for this code to work properly. An anchor of (0.5, 0) allows the background sprite to be anchored in the middle of the scene on the x-axis, but at the bottom of the scene on the y-axis. This allows you to easily stack one on top of the other.
EDIT - I forgot to mention that it's also a good idea to conserve resources and remove any background nodes that are outside the viewable area and won't be coming back in (i.e. a continuous scrolling game where you can't go backwards). You could do that by updating your updateBackground method above:
func updateBackground() {
let cameraPos = camera!.position
if cameraPos.y > levelPositionY - (size.height * 0.55) {
createBackground()
}
// Make sure to change 'backgroundNode' to whatever the name of your backgroundNode is.
for bgChild in backgroundNode.children {
// This will convert the node's coordinates to scene's coordinates. See below for this function
let nodePos = fgNode.convert(fgChild.position, to: self)
if !isNodeVisible(bgChild, positionY: nodePos.y) {
// Remove from it's parent node
bgChild.removeFromParent()
}
}
}
func isNodeVisible(_ node: SKNode, positionY: CGFloat) -> Bool {
if !camera!.contains(node) {
if positionY < camera!.position.y - size.height * 2.0 {
return false
}
}
return true
}
So above you just loop through all the children inside your background node and detect if they are out of view, and if so remove them from the parent. Make sure to change my generic backgroundNode to whatever the name of your background node is.

Draw a graph in a NSView using values entered by the user

I'm trying to draw a graph using Swift. I have created a custom NSView and a NSView class.
In the ViewController I would like to call a method that updates the NSView and draws the complete graph using values entered by the user.
import Cocoa
class Graph: NSView {
let startPoint = NSPoint(x: 10, y: 10)
override func drawRect(dirtyRect: NSRect) {
NSColor.whiteColor().set() // set white color
NSRectFill(dirtyRect) // fill the Rect in the View
// draw axes lines
NSColor.blackColor().set()
NSBezierPath.strokeLineFromPoint(startPoint, toPoint: NSPoint(x: Double(dirtyRect.width) - 10.0, y: 10.0))
NSBezierPath.strokeLineFromPoint(startPoint, toPoint: NSPoint(x: 10.0, y: Double(dirtyRect.height) - 10.0))
}
func drawGraphicsOfNewteork(arrayOfData: [NSTextField]){
// Here I would draw in the View some lines using the arrayOfData
}
}
Have you read the documentation at all? Searching for "drawing" turns up the Cocoa Drawing Guide.
As others have stated, drawing is done from within drawRect(). The system will call it at the "right time", after you flag it by setting needsDisplay to true/YES.