Realitykit / ArKit make a collision only between 2 objects - swiftui

I have a ball and a cylinder. When i tap on the ball it moves in forward position. When the ball hits the cylinder they collide and the cylinder moves and changes color. Thats working fine.
I have 2 problems:
When i start the app the cylinder is constantly colliding and changes color immediately, so even when the ball is not near it. It looks like its colliding with something else.
The second thing is, there will be more cylinders in the scene, how can i make a collision event only between 2 objects. Lets say there are 2 cylinders in the scene how can ik set a filter or group.
import SwiftUI
import RealityKit
import ARKit
import FocusEntity
import Combine
struct ContentView : View {
var body: some View {
ARViewContainer()
}
}
struct ARViewContainer: UIViewRepresentable {
func makeUIView(context: Context) -> ARView {
let view = ARView()
let session = view.session
let config = ARWorldTrackingConfiguration()
config.planeDetection = .horizontal
session.run(config)
let coachingOverlay = ARCoachingOverlayView()
coachingOverlay.autoresizingMask = [.flexibleWidth, .flexibleHeight]
coachingOverlay.session = session
coachingOverlay.goal = .horizontalPlane
view.addSubview(coachingOverlay)
context.coordinator.view = view
session.delegate = context.coordinator
view.addGestureRecognizer(UITapGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handleTap)))
return view
}
func updateUIView(_ uiView: ARView, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator()
}
//coordinator class
class Coordinator: NSObject,ARSessionDelegate {
weak var view: ARView?
var focusEntity: FocusEntity?
#Published var sceneIsPlaced:Bool = false //is de scene reeds geplaats na openen app
#Published var subscriptions: [AnyCancellable] = []
func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
guard let view = self.view else { return }
self.focusEntity = FocusEntity(on: view, style: .classic(color: .yellow))
}
//na tap op het scherm, wat te doen -> creeer locatie en plaats het object
#objc func handleTap(recognizer: UITapGestureRecognizer) {
guard let view = self.view, let focusEntity = self.focusEntity else { return }
//tap location van de tap gesture
let tapLocation = recognizer.location(in: self.view)
//Create Anchor
let anchor = AnchorEntity()
let importModel = try! Entity.load(named: "cilinder") //alle objects!
importModel.position = focusEntity.position
importModel.scale = SIMD3(repeating: 0.5)
//The cylinder
let kolomMiddleModel = importModel.findEntity(named: "cilinder")!.children[0] as! (ModelEntity & HasPhysicsBody & HasCollision)
let materialKolomMiddle = SimpleMaterial(color: .yellow, isMetallic: true)
kolomMiddleModel.model?.materials = [materialKolomMiddle]
kolomMiddleModel.generateCollisionShapes(recursive: false)
let physics = PhysicsBodyComponent(massProperties: .default, material: .default, mode: .dynamic)
kolomMiddleModel.components.set(physics)
//MAKE A BALL
let materialsBall = SimpleMaterial(color: .red, isMetallic: true)
let ballModel = ModelEntity(mesh: .generateSphere(radius: 0.1),
materials: [materialsBall])
as (Entity & HasPhysicsBody & HasCollision)
ballModel.position = [-0.1, -1.0,-0.1]
ballModel.generateCollisionShapes(recursive: true)
ballModel.name = "ballModel"
//LIGHTS --> let op voor de shadow moet de plane van occlusion material met dynamical lightning op tue
let directionalLight = DirectionalLight()
directionalLight.light.color = .white
directionalLight.light.intensity = 4000
directionalLight.light.isRealWorldProxy = true
directionalLight.shadow?.maximumDistance = 1.5
directionalLight.shadow?.depthBias = 7.0
directionalLight.orientation = simd_quatf(angle: .pi/1.5, axis: [0,1,0])
//maak een anchor voor het licht
let lightAnchor = AnchorEntity(world: [0, 0, 2.5])
lightAnchor.addChild(directionalLight)
//COLLISION EVENTS
DispatchQueue.main.async {
//collision of kolomModel
view.scene.subscribe(to: CollisionEvents.Began.self,
on: kolomMiddleModel) { _ in
print("Collision kolomModel detected!")
let material = SimpleMaterial(color: .red, isMetallic: true)
kolomMiddleModel.model?.materials = [material]
}.store(in: &self.subscriptions)
}
// view.installGestures(for: ballModel)
//
//PLACE SCENE IF NOT PLACED ALREADY
if !sceneIsPlaced {
//ADD MODELS TO ANCHOR
anchor.addChild(importModel)
anchor.addChild(ballModel)
anchor.addChild(lightAnchor)
view.scene.addAnchor(anchor)
sceneIsPlaced = true
//If the scene is already placed get the taplocation
}else{
if let locationEntity = view.entity(at: tapLocation) {
let entityTapped = locationEntity as! ModelEntity
//MAKE THE BALL GO FORWARD
print("entity tapped \(entityTapped)")
entityTapped.physicsBody = .init()
entityTapped.physicsBody?.mode = .kinematic
entityTapped.physicsMotion = PhysicsMotionComponent(linearVelocity: [0, 0, -0.5],
angularVelocity: [1, 3, 5])
}
}
}
}
}

Here is an example of two objects in RealityKit that can only collide with each other:
let sphere = ModelEntity(mesh: .generateSphere(radius: 0.1), materials: [SimpleMaterial()])
sphere.collision = CollisionComponent(shapes: [ShapeResource.generateSphere(radius: 0.1)])
sphere.collision?.filter = CollisionFilter(group: 1 << 2, categories: .default, mask: .default)
let cube = ModelEntity(mesh: .generateBox(size: [0.1, 0.1, 0.1]), materials: [SimpleMaterial()])
cube.collision = CollisionComponent(shapes: [ShapeResource.generateBox(size: [0.1, 0.1, 0.1])])
cube.collision?.filter = CollisionFilter(group: 1 << 1, categories: .default, mask: .default)
sphere.collision?.filter.collisionGroup = 1 << 1
cube.collision?.filter.collisionGroup = 1 << 2
arView.scene.addAnchor(sphere)
arView.scene.addAnchor(cube)
This code creates two objects: a sphere and a cube. Each object is given a CollisionComponent with a corresponding ShapeResource and CollisionFilter. The CollisionFilter for each object specifies that only objects in a different collisionGroup can collide with it. As a result, the sphere and cube can only collide with each other, not with other objects in the scene.
See more information about CollisionGroups here:
https://developer.apple.com/documentation/realitykit/collisionfilter

Related

Phantom Button in SpriteKit Scene

I have a simple game in which players get three rounds to achieve the highest score . The gameScene exists inside a SwiftUI View and is created like this:
var gameScene: SKScene {
let scene = NyonindoGameScene(
size: CGSize(
width: UIScreen.main.bounds.width,
height: UIScreen.main.bounds.height
)
)
scene.viewModel = self.viewModel
scene.scaleMode = .aspectFill
return scene
}
It is called from the body of the view (inside a GeometryReader inside a ZStack) using SpriteView(). The code was working great until I tested on a new iPhone 13, which gave me all kinds of quirky and unexpected behaviors. I won't elaborate on them now as I have fixed most, but I am still left with a "phantom" start button. It is designed to display different text depending on the round being played (viz.: "Start," "Try Again," "Last Chance") using a var that is accurately counting rounds. However, I get this at the end of the first round:
When this Frankenstein button gets tapped, the new round begins. HOWEVER, SKPhysicsContactDelegate didBegin(_:) does not get called and collisions are ignored. (In my general bafflement here, I don't know if this is a separate issue or one that will go away when I solve the overlapping button problem.)
In any case, here is the relevant code for the startButton:
func addStartButton(text: String) {
startButton.removeFromParent() // added as one of many failed remedies
let startButtonLabel = SKLabelNode(text: text)
startButtonLabel.fontName = SKFont.bold
startButtonLabel.fontSize = 40.0
startButtonLabel.fontColor = UIColor.white
startButtonLabel.position = CGPoint(x: 0, y: -12)
startButton.position = CGPoint(x:self.frame.midX, y:self.frame.midY)
startButton.zPosition = 3
startButton.addChild(startButtonLabel)
addChild(startButton)
}
The initial start button is called like this in didMove(to view: SKView):
if attempts == 0 {
addStartButton(text: "Start")
}
And the buttons for the second and third round are called inside a gameOver() function like this:
if attempts == 1 {
startButton.removeFromParent() // again, this is overkill as it gets removed before this...
let text: String = "Try Again!"
addStartButton(text: text)
}
if attempts == 2 {
startButton.removeFromParent()
let text: String = "Last Chance!"
addStartButton(text: text)
}
I originally had a switch statement instead of the two if statements, but that generated the same problem. Print statements to the console suggest that only one button is being called for each round, but the results suggest something different.
Any clues? (Apologies if I haven't provided enough code for an assessment.)
why are you removing the button? change it's label:
class TTESTGameScene: SKScene {
var allBoxes: [SKSpriteNode] = []
var startButton: SKShapeNode = SKShapeNode(rect: CGRect(x: 0, y: 0, width: 200, height: 43), cornerRadius: 20)
override func didMove(to view: SKView) {
physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
view.allowsTransparency = true
self.backgroundColor = .clear
view.alpha = 1.0
view.isOpaque = true
view.backgroundColor = SKColor.clear.withAlphaComponent(0.0)
let nextButton = SKShapeNode(rect: CGRect(x: 0, y: view.frame.maxY - 40, width: 66, height: 33), cornerRadius: 20)
nextButton.fillColor = .yellow
nextButton.name = "nextButton"
let nextLabel = SKLabelNode(text: "")
nextLabel.fontSize = 40.0
nextLabel.fontColor = UIColor.white
nextLabel.position = CGPoint(x: 0, y: -12)
nextButton.addChild(nextLabel)
addChild(nextButton)
startButton.fillColor = .red
startButton.name = "startButton"
let startButtonLabel = SKLabelNode(text: "000")
startButtonLabel.fontSize = 30.0
startButtonLabel.fontColor = UIColor.white
startButtonLabel.horizontalAlignmentMode = .center
startButtonLabel.position = CGPoint(x: startButton.frame.size.width/2, y: 10)
startButtonLabel.name = "startButtonLabel"
startButton.position = CGPoint(x:self.frame.midX - startButton.frame.size.width/2, y:self.frame.midY)
startButton.zPosition = 3
startButton.addChild(startButtonLabel)
addChild(startButton)
}
var attempts: Int = 0
func nextLevel() {
//startButton.removeFromParent() // added as one of many failed remedies
var text = ""
if attempts == 0 {
text = "Start"
}
else if attempts == 1 {
text = "Try Again!"
}
else if attempts == 2 {
text = "Last Chance!"
}
if let label = startButton.childNode(withName: "//startButtonLabel") as? SKLabelNode {
label.text = text
attempts += 1
attempts = attempts > 2 ? 0:attempts
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let location = touch.location(in: self.view)
let sceneTouchPoint = self.convertPoint(fromView: location)
let touchedNode = self.atPoint(sceneTouchPoint)
print(touchedNode.name)
if touchedNode.name == "nextButton" {
nextLevel()
}
}
}
// A sample SwiftUI creating a GameScene and sizing it
// at 300x400 points
struct TTESTContentView: View {
var scene: SKScene {
let scene = TTESTGameScene()
scene.size = CGSize(width: 300, height: 400)
scene.scaleMode = .aspectFill
return scene
}
var body: some View {
SpriteView(scene: scene)
.frame(width: 300, height: 400)
//.ignoresSafeArea()
}
}
struct ContentViewTest_Previews: PreviewProvider {
static var previews: some View {
TTESTContentView()
}
}

SwiftUI: HitTest on Scenekit

Goal: SceneKit hit test with SwiftUI (instead of UIKit)
Problem: When I embed the default ship scene on a SwiftUI "UIViewRepresentable", the example handleTap function doesn't work. and I get his error:
"Argument of '#selector' refers to instance method 'handleTap' that is not exposed to Objective-C"
How an I create a hit test, and pass data to another SwiftUI view?
import SwiftUI
import SceneKit
var handleTap: (() -> Void)
struct ScenekitView : UIViewRepresentable {
let scene = SCNScene(named: "ship.scn")!
func makeUIView(context: Context) -> SCNView {
// create and add a camera to the scene
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
scene.rootNode.addChildNode(cameraNode)
// place the camera
cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)
// retrieve the ship node
let ship = scene.rootNode.childNode(withName: "ship", recursively: true)!
// retrieve the SCNView
let scnView = SCNView()
return scnView
}
func updateUIView(_ scnView: SCNView, context: Context) {
scnView.scene = scene
// allows the user to manipulate the camera
scnView.allowsCameraControl = true
// show statistics such as fps and timing information
scnView.showsStatistics = true
// configure the view
scnView.backgroundColor = UIColor.black
// add a tap gesture recognizer
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
scnView.addGestureRecognizer(tapGesture)
}
func handleTap(_ gestureRecognize: UIGestureRecognizer) {
// retrieve the SCNView
let scnView = SCNView()
// check what nodes are tapped
let p = gestureRecognize.location(in: scnView)
let hitResults = scnView.hitTest(p, options: [:])
// check that we clicked on at least one object
if hitResults.count > 0 {
// retrieved the first clicked object
let result = hitResults[0]
// get material for selected geometry element
let material = result.node.geometry!.firstMaterial
// highlight it
SCNTransaction.begin()
SCNTransaction.animationDuration = 0.5
// on completion - unhighlight
SCNTransaction.completionBlock = {
SCNTransaction.begin()
SCNTransaction.animationDuration = 0.5
material?.emission.contents = UIColor.black
SCNTransaction.commit()
}
material?.emission.contents = UIColor.green
SCNTransaction.commit()
}
}
}
#if DEBUG
struct ScenekitView_Previews : PreviewProvider {
static var previews: some View {
ScenekitView()
}
}
#endif
Just hit this issue myself and finally found a solution: make a dummy struct that pulls from a class that actually holds your SCNView.
This works for me:
struct ScenekitView : UIViewRepresentable {
let scenekitClass = ScenekitClass()
func makeUIView(context: Context) -> SCNView {
return scenekitClass.view
}
func updateUIView(_ scnView: SCNView, context: Context) {
// your update UI view contents look like they can all be done in the initial creation
}
}
class ScenekitClass {
let view = SCNView()
let scene = SCNScene(named: "ship.scn")!
init() {
// create and add a camera to the scene
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
scene.rootNode.addChildNode(cameraNode)
// place the camera
cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)
// attach the scene
view.scene = scene
// allows the user to manipulate the camera
view.allowsCameraControl = true
// show statistics such as fps and timing information
view.showsStatistics = true
// configure the view
view.backgroundColor = UIColor.black
// add a tap gesture recognizer
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
view.addGestureRecognizer(tapGesture)
}
#objc func handleTap(_ gestureRecognize: UIGestureRecognizer) {
// check what nodes are tapped
let p = gestureRecognize.location(in: view)
let hitResults = view.hitTest(p, options: [:])
// check that we clicked on at least one object
if hitResults.count > 0 {
// retrieved the first clicked object
let result = hitResults[0]
// get material for selected geometry element
let material = result.node.geometry!.firstMaterial
// highlight it
SCNTransaction.begin()
SCNTransaction.animationDuration = 0.5
// on completion - unhighlight
SCNTransaction.completionBlock = {
SCNTransaction.begin()
SCNTransaction.animationDuration = 0.5
material?.emission.contents = UIColor.black
SCNTransaction.commit()
}
material?.emission.contents = UIColor.green
SCNTransaction.commit()
}
}
}
Based on this question.
For whatever reason, the SwiftUI SceneView does not conform to the SCNSceneRenderer protocol. If it did, then it would not be necessary to make use of a UIViewRepresentable (or NSViewRepresentable for macOS) view.
I have a complete example app, for macOS, here:
https://github.com/Thunor/HitTestApp
SceneView has a delegate argument. You can use a SCNSceneRenderDelegate to capture the SCNSceneRenderer and use it for hit testing. Here's an example:
import SwiftUI
import SceneKit
import Foundation
class RenderDelegate: NSObject, SCNSceneRendererDelegate {
// dummy render delegate to capture renderer
var lastRenderer: SCNSceneRenderer!
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
// store the renderer for hit testing
lastRenderer = renderer
}
}
class Model: ObservableObject {
let scene = SCNScene(named: "scene.usdz")!
let renderDelegate = RenderDelegate()
}
struct ContentView: View {
#ObservedObject var model = Model()
var body: some View {
SceneView(scene: model.scene, options: [.allowsCameraControl, .autoenablesDefaultLighting], delegate: model.renderDelegate)
.gesture(
SpatialTapGesture(count: 1)
.onEnded(){ event in
// hit test
guard let renderer = model.renderDelegate.lastRenderer else { return }
let hits = renderer.hitTest(event.location, options: nil)
if let tappedNode = hits.first?.node {
// do something
}
}
)
}
}

How adapt code to work with a Tab Bar Controller

I'm redoing the navigation of my app to be based on a custom UITabBarController. The tab bar opens the various ViewController. This is working fine, however Im now getting errors with the code in the ViewController that was previously working.
The new customTabBarController
import UIKit
class CustomTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let feedController = feedVC() //Name of the view controller
let firstNavigationController = UINavigationController(rootViewController: feedController)
firstNavigationController.title = "Feed"
firstNavigationController.tabBarItem.image = UIImage(named: "feed_icon")
let clubController = moreVC() //Name of the view controller
let secondNavigationController = UINavigationController(rootViewController: clubController)
secondNavigationController.title = "Club"
secondNavigationController.tabBarItem.image = UIImage(named: "club_icon")
let recordController = moreVC() //Name of the view controller
let thirdNavigationController = UINavigationController(rootViewController: recordController)
thirdNavigationController.title = "Record"
thirdNavigationController.tabBarItem.image = UIImage(named: "record_icon")
let profileController = moreVC() //Name of the view controller
let fourthNavigationController = UINavigationController(rootViewController: profileController)
fourthNavigationController.title = "Profile"
fourthNavigationController.tabBarItem.image = UIImage(named: "profile_icon")
let moreController = moreVC() //Name of the view controller
let fifthNavigationController = UINavigationController(rootViewController: moreController)
fifthNavigationController.title = "More"
fifthNavigationController.tabBarItem.image = UIImage(named: "more_icon")
viewControllers = [firstNavigationController, secondNavigationController, thirdNavigationController, fourthNavigationController, fifthNavigationController]
tabBar.isTranslucent = false
// Color of menu bar set in AppDelegate.swift
}
}
feedVC
import UIKit
import Firebase
class feedVC: UIViewController {
#IBOutlet weak var tableView: UITableView!
var activityArray = [Activity]()
var userdataArray = [Userdata]()
var cellSpacingHeight: CGFloat = 10 // Sets the spacing between the cells
override func viewDidLoad() {
super.viewDidLoad()
if Auth.auth().currentUser == nil {
let authVC = self.storyboard?.instantiateViewController(withIdentifier: "authVC") as? authVC
self.present(authVC!, animated: true, completion: nil)
}
tableView.delegate = self
tableView.dataSource = self
}
The error I get is:
Thread 1: EXC_BREAKPOINT (code=1, subcode=0x102eb7b50) linked to the below code in the feedVC.
tableView.delegate = self
tableView.dataSource = self
The second error I get is related to recordVC
import UIKit
import MapKit
class recordVC: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
var tileRenderer: MKTileOverlayRenderer!
// set initial location in Aspøya
let initialLocation = CLLocation(latitude: 63.011018, longitude: 7.914721)
// Set the zoom level of the location
let regionRadius: CLLocationDistance = 1000
func centerMapOnLocation(location: CLLocation) {
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate,
regionRadius, regionRadius)
mapView.setRegion(coordinateRegion, animated: true)
}
func setupTileRenderer() {
// Fetching the map file from URL below. The {x}, {y}, and {z} are replaced at runtime by an individual tile’s coordinate. The z-coordinate, or zoom-level is specified by how much the user has zoomed in the map. The x and y are the index of the tile given the section of the Earth shown. A tile needs to be supplied for every x and y for each zoom level supported.
let template = "https://opencache.statkart.no/gatekeeper/gk/gk.open_gmaps?layers=norgeskart_bakgrunn&zoom={z}&x={x}&y={y}&format=image/png"
// Creates the overlay
let overlay = MKTileOverlay(urlTemplate: template)
// Indicates the tiles are opaque and replace the default map tiles
overlay.canReplaceMapContent = true
// Adds the overlay to the mapView
mapView.add(overlay, level: .aboveLabels)
// Creates a tile renderer which handles the drawing of the tiles.
tileRenderer = MKTileOverlayRenderer(tileOverlay: overlay)
}
This give a similar error of: hread 1: EXC_BREAKPOINT (code=1, subcode=0x10561bb50) for this code line
// Adds the overlay to the mapView
mapView.add(overlay, level: .aboveLabels)
For the first one you have to use UITableViewDelegate and UITableViewDataSource like below :
class feedVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
....
}
For the second, You have to do UI operations on main thread.
DispatchQueue.main.async {
mapView.add(overlay, level: .aboveLabels)
}

iOS App Mapview Line Draw

Having problem in displaying polyline on the mapview
Following this tutorial
MapView Tutorial
Attached is my code.
Annotation is appearing on the map but unable to call the renderer method. Though the delegate is there.
Main Problem: Unable to draw line between two coordinates
Console Output: 2017-02-06 22:54:56.770584 MapTest[2329:805733] [LogMessageLogging] 6.1 Unable to retrieve CarrierName. CTError: domain-2, code-5, errStr:((os/kern) failure)
Here is the code
import UIKit
import MapKit
class ViewController: UIViewController,MKMapViewDelegate {
#IBOutlet weak var myMap: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
// 1.
myMap.delegate = self
// 2.
let sourceLocation = CLLocationCoordinate2D(latitude: 40.759011, longitude: -73.984472)
let destinationLocation = CLLocationCoordinate2D(latitude: 40.748441, longitude: -73.985564)
// 3.
let sourcePlacemark = MKPlacemark(coordinate: sourceLocation, addressDictionary: nil)
let destinationPlacemark = MKPlacemark(coordinate: destinationLocation, addressDictionary: nil)
// 4.
let sourceMapItem = MKMapItem(placemark: sourcePlacemark)
let destinationMapItem = MKMapItem(placemark: destinationPlacemark)
// 5.
let sourceAnnotation = MKPointAnnotation()
sourceAnnotation.title = "Times Square"
if let location = sourcePlacemark.location {
sourceAnnotation.coordinate = location.coordinate
}
let destinationAnnotation = MKPointAnnotation()
destinationAnnotation.title = "Empire State Building"
if let location = destinationPlacemark.location {
destinationAnnotation.coordinate = location.coordinate
}
// 6.
self.myMap.showAnnotations([sourceAnnotation,destinationAnnotation], animated: true )
// 7.
let directionRequest = MKDirectionsRequest()
directionRequest.source = sourceMapItem
directionRequest.destination = destinationMapItem
directionRequest.transportType = .automobile
// Calculate the direction
let directions = MKDirections(request: directionRequest)
// 8.
directions.calculate {
(response, error) -> Void in
guard let response = response else {
if let error = error {
print("Error: \(error)")
}
return
}
let route = response.routes[0]
self.myMap.add((route.polyline), level: MKOverlayLevel.aboveRoads)
let rect = route.polyline.boundingMapRect
self.myMap.setRegion(MKCoordinateRegionForMapRect(rect), animated: true)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
print("Line 85 is being called......start...")
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = UIColor.red
renderer.lineWidth = 4.0
print("Line 85 is being called.......end..")
return renderer
}
}
Your rendererForOverlay function has the wrong syntax; Xcode told me this when testing your code. Use
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer
Instead, and a line will be drawn between the two points.

draw animated circle in swift 3

Refrence : https://stackoverflow.com/a/26578895/6619234
how to erase and redraw circle on click evnet?
i tried to call addCircleView method on click event but circle is overlapping every time.
class CircleClosing: UIView {
var circleLayer: CAShapeLayer!
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.clear
// Use UIBezierPath as an easy way to create the CGPath for the layer.
// The path should be the entire circle.
let circlePath : UIBezierPath!
circlePath = UIBezierPath(arcCenter: CGPoint(x: frame.size.width / 2.0, y: frame.size.height / 2.0), radius: (frame.size.width - 5)/2, startAngle: 0.0, endAngle: CGFloat(M_PI * 2.0), clockwise: true)
// Setup the CAShapeLayer with the path, colors, and line width
circleLayer = CAShapeLayer()
circleLayer.path = circlePath.cgPath
circleLayer.fillColor = UIColor.clear.cgColor
circleLayer.strokeColor = UIColor.blue.cgColor
circleLayer.lineWidth = 20.0;
// Don't draw the circle initially
circleLayer.strokeEnd = 0.0
// Add the circleLayer to the view's layer's sublayers
}
override func layoutSubviews()
{
let frame = self.layer.bounds
circleLayer.frame = frame
layer.addSublayer(circleLayer)
}
required init?(coder aDecoder: NSCoder)
{ super.init(coder: aDecoder) }
func animateCircle(duration: TimeInterval) {
// We want to animate the strokeEnd property of the circleLayer
let animation = CABasicAnimation(keyPath: "strokeEnd")
// Set the animation duration appropriately
animation.duration = duration
// Animate from 0 (no circle) to 1 (full circle)
animation.fromValue = 0
animation.toValue = 1
// Do a linear animation (i.e. the speed of the animation stays the same)
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
// Set the circleLayer's strokeEnd property to 1.0 now so that it's the
// right value when the animation ends.
circleLayer.strokeEnd = 1.0
// Do the actual animation
circleLayer.add(animation, forKey: "animateCircle")
}
}
Add in your subview
func addCircleView() {
var circleView : CircleClosing!
let diceRoll = CGFloat(510) //CGFloat(Int(arc4random_uniform(7))*50)
let diceRolly = CGFloat(70)
let circleWidth = CGFloat(40)
let circleHeight = circleWidth
// Create a new CircleView
circleView = CircleClosing(frame:CGRect(x:diceRoll,y: diceRolly,width: circleWidth,height: circleHeight) )
view.addSubview(circleView)
// Animate the drawing of the circle over the course of 1 second
circleView.animateCircle(duration: 20.0)
}
Thanks in Advance
var circleView : CircleClosing!
func addCircleView() {
let diceRoll = CGFloat(510) //CGFloat(Int(arc4random_uniform(7))*50)
let diceRolly = CGFloat(70)
let circleWidth = CGFloat(40)
let circleHeight = circleWidth
//Add this line here to remove from superview
circleView.removeFromSuperview()
circleView = CircleClosing(frame:CGRect(x:diceRoll,y: diceRolly,width: circleWidth,height: circleHeight) )
view.addSubview(circleView)
// Animate the drawing of the circle over the course of 1 second
circleView.animateCircle(duration: 20.0)
}