How to remove overlay between two annotations in mapView iOS - swift

Here is the example picture:
Now I want to remove the overlay between 5 & 6 annotations. Here is the code snippet what I tried so far:
Function to create polyline
func createPolyline() {
if (arrTempFootStepsLocation.count>0){ //This array contains all the coordinates from starting annonation 1 to 6
let points:[CLLocationCoordinate2D] = arrTempFootStepsLocation
let geodesic = MKGeodesicPolyline(coordinates: points, count: points.count)
mapService.addOverlay(geodesic)
}
}
Function to create annotations
func addAnnotations() {
if let arrProof = arrTempProof{ //This array contains 6 coordinates which are taken from arrTempFootStepsLocation only to create annotation
for (index , modelPhotoProof) in arrProof.enumerated() {
if (modelPhotoProof.locationPoint?.latitude != 0.0 && modelPhotoProof.locationPoint?.longitude != 0.0){
let CLLCoordType = CLLocationCoordinate2D(latitude: modelPhotoProof.locationPoint!.latitude, longitude: modelPhotoProof.locationPoint!.longitude)
let anno = MyMKPointAnnotation()
anno.tag = index
anno.coordinate = CLLCoordType
mapService.addAnnotation(anno)
}
}
}
}
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
if view.annotation is MyMKPointAnnotation {
if arrRemovalAnnotationPoint.count<2{ //This array contains selected annotation coordinates
arrRemovalAnnotationPoint.append((view.annotation?.coordinate)!)
print("Annotation point inserted.")
}
}
}
Delete overlay between two annotations
#IBAction func btnDeletePolylinesPressed(_ sender:UIButton){
if arrRemovalAnnotationPoint.count == 2 {
//Find the index of those two annotations from arrTempFootStepsLocation
let firstAnnoIndex = arrTempFootStepsLocation.index(where: {$0.latitude == arrRemovalAnnotationPoint.first?.latitude && $0.longitude == arrRemovalAnnotationPoint.first?.longitude})
let lastAnnoIndex = arrTempFootStepsLocation.index(where: {$0.latitude == arrRemovalAnnotationPoint.last?.latitude && $0.longitude == arrRemovalAnnotationPoint.last?.longitude})
//prepare array to remove coordinates between those two annotations
if let first = firstAnnoIndex, let last = lastAnnoIndex{
var arrToRemoved = [CLLocationCoordinate2D]()
if first > last {
arrToRemoved = Array(arrTempFootStepsLocation[last...first])
for i in 0..<arrToRemoved.count{
let itemToBeRemoved = arrToRemoved[i]
for j in 0..<arrTempFootStepsLocation.count - 1 {
let itemOriginal = arrTempFootStepsLocation[j]
if (itemToBeRemoved.latitude == itemOriginal.latitude && itemToBeRemoved.longitude == itemOriginal.longitude){
arrTempFootStepsLocation.remove(at: j)
}
}
}
mapService.removeOverlays(mapService.overlays)
self.perform(#selector(self.createPolyline()), with: nil, afterDelay: 0.8)
}
else {
arrToRemoved = Array(arrTempFootStepsLocation[first...last])
for i in 0..<arrToRemoved.count{
let itemToBeRemoved = arrToRemoved[i]
for j in 0..<arrTempFootStepsLocation.count - 1 {
let itemOriginal = arrTempFootStepsLocation[j]
if (itemToBeRemoved.latitude == itemOriginal.latitude && itemToBeRemoved.longitude == itemOriginal.longitude){
arrTempFootStepsLocation.remove(at: j)
}
}
}
mapService.removeOverlays(mapService.overlays)
self.perform(#selector(self.createPolyline()), with: nil, afterDelay: 0.8)
}
}
arrRemovalAnnotationPoint.removeAll()
}
}
Now the problem is when I press delete button its doesn't clear all coordinates between these annotations. Now when I redraw the polyline it still connects between these annotations.
Whats wrong in my code? Is there any other way to do this? Any help will be appreciated.

remove the particular index in (arrRemovalAnnotationPoint) array

Related

Cluster, MKMapView show many time same annotation

So, I have a swift application (swift 3) who use a map, in this map I have many annotation who gets up to date when user change region of map.
I wanted to use the pod "Cluster", I add it and when I zoom in to show what was in the cluster, the annotations appear several times almost at the same place (some time it form a circle, see picture)
I have already tried to filter my list to get only once instance of annotation in cluster but it doesn't works.
This method is call each time the user change region :
func createAnnotations(pois list:[PlaceModel]) {
poiAnnotations.removeAll()
let filteredAnnotations = mapView.annotations.filter { annotation in
//if annotation is MKUserLocation { return false } // don't remove MKUserLocation
if let temp = annotation.subtitle, let value = temp {
return value == "Place"
}
return false
}
clusterManager.remove(poiAnnotations)
clusterManager.remove(filteredAnnotations)
for poi in list {
let centerLocation = CLLocation(latitude: mapView.centerCoordinate.latitude, longitude: mapView.centerCoordinate.longitude)
if poi.getLocation().distance(from: centerLocation) > Double(Constants.POI_Radius) {
continue
}
let annotation = Annotation()
annotation.coordinate = CLLocationCoordinate2D(latitude: poi.latitude!, longitude: poi.longitude!)
annotation.title = poi.name!
annotation.subtitle = "Place"
annotation.type = .image( Images.MapPins.velhop)
// For first value
if poiAnnotations.isEmpty {
poiAnnotations.append(annotation)
}
for pois in poiAnnotations {
if poiAnnotations.contains(where: {$0.title == poi.name}) {
// Do nothing
} else {
poiAnnotations.append(annotation)
}
}
}
clusterManager.add(poiAnnotations)
}
And Here the place where the clusters and the annotations are created :
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if let annotation = annotation as? ClusterAnnotation {
let identifier = "Cluster"
var view = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
// Type for cluster
let color = UIColor(red: 255/255, green: 149/255, blue: 0/255, alpha: 1)
let type:ClusterAnnotationType = .color(color, radius: 25)
if let view = view as? BorderedClusterAnnotationView {
view.annotation = annotation
view.configure(with: type)
} else {
view = BorderedClusterAnnotationView(annotation: annotation, reuseIdentifier: identifier, type: type, borderColor: .white)
}
return view
} else {
let identifier = "Pin"
var view = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKPinAnnotationView
if let view = view {
view.annotation = annotation
} else {
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
}
return view
}
}
I tried several things that did not succeed..
Thanks in advance.

I want to loop over all the annotations from my Cluster Annotation

I'm using the cocoapod FBAnnotationClusteringSwift and it's possible to group my annotations together. However I want to loop over all those annotations that are clustered together when the cluster annotation is tapped.
How do I do this?
func mapView(mapView: MKMapView, didSelectAnnotationView view: MKAnnotationView) {
if (view.annotation!.isKindOfClass(FBAnnotationCluster) == true){
//I WANT TO LOOP OVER ALL ANNOTATIONS IN THE CLUSTER HERE
}
if (view.annotation!.isKindOfClass(ItemAnnotation) == true){
let annotation = view.annotation! as? ItemAnnotation
if let annotation = annotation, let item = annotation.item, d = delegate{
d.itemAnnotationPressed(item)
}
}
}
func mapView(mapView: MKMapView, didSelectAnnotationView view: MKAnnotationView) {
if (view.annotation!.isKindOfClass(FBAnnotationCluster) == true){
let annotation = view.annotation! as? FBAnnotationCluster
var itemListFromAnnotation = [Item]()
for annotation in (annotation?.annotations)! {
let itemAnnotation = annotation as? ItemAnnotation
itemListFromAnnotation.append((itemAnnotation?.item)!)
}
if let d = delegate{
d.itemClusterAnnotationPressed(itemListFromAnnotation)
}
}
if (view.annotation!.isKindOfClass(ItemAnnotation) == true){
mapView.deselectAnnotation(view.annotation, animated: false)
let annotation = view.annotation! as? ItemAnnotation
if let annotation = annotation, let item = annotation.item, d = delegate{
d.itemAnnotationPressed(item)
}
}
}

MapKit Display Annotation Clusters and Along With Non-Clustered Annotations

I am new to iOS development and currently using the FBAnnotationClusteringSwift to cluster makers. My app needs to have two annotations which will not be clustered, as they indicate the source and destination addresses. The remaining annotations must be clustered as they represent stations.
What I want to achieve is something like the image bellow, where "A" is my source address, and the clusters represent stations:
However what is happening is, as the clusters are created, the Annotation that represents a non-clustered annotation (the source address) disappears as following:
If the library is mixing all annotations together, that is pretty bad, there should be a way to separate those that I've added through clusteredAnnotationsWithinMapRect (stations) and those markers that were already added before (addresses). Here is the relevant part of the current code:
var markerClusterer: FBClusteringManager?
ViewController ... {
override func viewDidLoad() {
super.viewDidLoad()
...
self.markerClusterer = FBClusteringManager()
self.markerClusterer!.delegate = self
}
func cellSizeFactorForCoordinator(coordinator:FBClusteringManager) -> CGFloat{
return 1.0
}
func mapView(mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
// Here is just like the example code from FBAnnotationClusterSwift
var stations: [MKAnnotation] = fetchStations(...)
if (stations.count > 0) {
NSOperationQueue().addOperationWithBlock({
let scale:Double = Double(self.mapView.bounds.size.width) / self.mapView.visibleMapRect.size.width
self.markerClusterer!.addAnnotations(stations)
var annotationArray = stations
// Needed to do that otherwise the clusters never "explode" into pins
if (scale <= 0.06) {
annotationArray = self.markerClusterer!.clusteredAnnotationsWithinMapRect(self.mapView.visibleMapRect, withZoomScale:scale)
}
self.markerClusterer!.displayAnnotations(annotationArray, onMapView: self.mapView)
})
}
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
var reuseId = ""
if (annotation.isKindOfClass(FBAnnotationCluster)) {
reuseId = "Cluster"
var clusterView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
if clusterView == nil {
clusterView = FBAnnotationClusterView(annotation: annotation, reuseIdentifier: reuseId, options: nil)
} else {
clusterView!.annotation = annotation
}
return clusterView
} else if (annotation.isKindOfClass(AddressPointAnnotation)) {
reuseId = "AddressPin"
var addressPinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
if addressPinView == nil {
addressPinView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
addressPinView!.canShowCallout = true
}
else {
addressPinView!.annotation = annotation
}
let addressPin = annotation as! AddressPointAnnotation
addressPinView!.image = UIImage(named: addressPin.icon)
return addressPinView
} else if (annotation is Station) {
reuseId = "StationPin"
var stationPinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
if stationPinView == nil {
stationPinView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
}
else {
stationPinView!.annotation = annotation
}
let stationPin = annotation as! Station
stationPinView!.image = UIImage(named: "station")
return stationPinView
} else {
return nil
}
}
}
// Annotation for the stations
class Station: NSObject, MKAnnotation {
var id: Int = 0
var availableBikes: Int = 0
var availableBikeStands: Int = 0
var coordinate: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 39.208407, longitude: -76.799555)
}
// Annotations for addresses
class AddressPointAnnotation: MKPointAnnotation {
var icon: String!
}
class Address: NSObject {
var marker: AddressPointAnnotation?
func addMarker(coordinate: CLLocationCoordinate2D) {
marker = AddressPointAnnotation()
marker!.coordinate = coordinate
// ViewController is passed to the Address, so it can add itself to the map
self.controller.mapView.addAnnotation(marker!)
if (self.direction == SOURCE) {
marker!.title = "Starting address"
marker!.icon = "from"
} else {
marker!.title = "Destination address"
marker!.icon = "to"
}
self.controller.mapView.addAnnotation(marker!)
}
}
Any help, idea, or comment is more than welcome. Thanks.

Why does my SCNAction sequence stop working intermittently?

I'm trying to move an SCNNode around a scene, constrained to a GKGridGraph. Think along the lines of PacMan but in 3D.
I have a ControlComponent which handles the movement of my SCNNode. The logic should go like this...
Set the queuedDirection property.
If the isMoving flag is false, call the move() method
Use the GKGridGraph to evaluate the next move
3.1 If the entity can move to the GKGridGraphNode using the direction property, nextNode = nodeInDirection(direction)
3.2 If the entity can move to the GKGridGraphNode using the queuedDirection property nextNode = nodeInDirection(queuedDirection)
3.3 If the entity can not move to a node with either direction, set the isMoving flag to false and return.
Create the moveTo action
Create a runBlock which calls the move() method
Apply the moveTo and runBlock actions as a sequence to the SCNNode
I've pasted in the full class below. But I'll explain the problem I'm having first. The above logic works, but only intermitently. Sometimes the animation stops working almost immediatley, and sometimes it runs for up to a minute. But at some point, for some reason, it just stops working - setDirection() will fire, move() will fire , the SCNNode will move once space in the specified direction and then the move() method just stops being called.
I'm not 100% convinced my current approach is correct so I'm happy to hear if there's a more idiomatic SceneKit/GameplayKit way to do this.
Here's the full class, but I think the important bit's are the setDirection() and move() methods.
import GameplayKit
import SceneKit
enum BRDirection {
case Up, Down, Left, Right, None
}
class ControlComponent: GKComponent {
var level: BRLevel!
var direction: BRDirection = .None
var queuedDirection: BRDirection?
var isMoving: Bool = false
var speed: NSTimeInterval = 0.5
//----------------------------------------------------------------------------------------
init(level: BRLevel) {
self.level = level
super.init()
}
//----------------------------------------------------------------------------------------
func setDirection( nextDirection: BRDirection) {
self.queuedDirection = nextDirection;
if !self.isMoving {
self.move()
}
}
//----------------------------------------------------------------------------------------
func move() {
let spriteNode: SCNNode = (self.entity?.componentForClass(NodeComponent.self)!.node)!
var nextNode = nodeInDirection( direction )
if let _ = self.queuedDirection {
let attemptedNextNode = nodeInDirection(self.queuedDirection! )
if let _ = attemptedNextNode {
nextNode = attemptedNextNode
self.direction = self.queuedDirection!
self.queuedDirection = nil
}
}
// Bail if we don't have a valid next node
guard let _ = nextNode else {
self.direction = .None
self.queuedDirection = nil
self.isMoving = false
return
}
// Set flag
self.isMoving = true;
// convert graphNode coordinates to Scene coordinates
let xPos: Float = Float(nextNode!.gridPosition.x) + 0.5
let zPos: Float = Float(nextNode!.gridPosition.y) + 0.5
let nextPosition: SCNVector3 = SCNVector3Make(xPos, 0, zPos)
// Configure actions
let moveTo = SCNAction.moveTo(nextPosition, duration: speed)
let repeatAction = SCNAction.runBlock( { _ in self.move() } )
let sequence = SCNAction.sequence([ moveTo, repeatAction ])
spriteNode.runAction( sequence )
}
//----------------------------------------------------------------------------------------
func getCurrentGridGraphNode() -> GKGridGraphNode {
// Acces the node in the scene and gett he grid positions
let spriteNode: SCNNode = (self.entity?.componentForClass(NodeComponent.self)!.node)!
// Account for visual offset
let currentGridPosition: vector_int2 = vector_int2(
Int32( floor(spriteNode.position.x) ),
Int32( floor(spriteNode.position.z) )
)
// return unwrapped node
return level.gridGraph.nodeAtGridPosition(currentGridPosition)!
}
//----------------------------------------------------------------------------------------
func nodeInDirection( nextDirection:BRDirection? ) -> GKGridGraphNode? {
guard let _ = nextDirection else { return nil }
let currentGridGraphNode = self.getCurrentGridGraphNode()
return self.nodeInDirection(nextDirection!, fromNode: currentGridGraphNode)
}
//----------------------------------------------------------------------------------------
func nodeInDirection( nextDirection:BRDirection?, fromNode node:GKGridGraphNode ) -> GKGridGraphNode? {
guard let _ = nextDirection else { return nil }
var nextPosition: vector_int2?
switch (nextDirection!) {
case .Left:
nextPosition = vector_int2(node.gridPosition.x + 1, node.gridPosition.y)
break
case .Right:
nextPosition = vector_int2(node.gridPosition.x - 1, node.gridPosition.y)
break
case .Down:
nextPosition = vector_int2(node.gridPosition.x, node.gridPosition.y - 1)
break
case .Up:
nextPosition = vector_int2(node.gridPosition.x, node.gridPosition.y + 1)
break;
case .None:
return nil
}
return level.gridGraph.nodeAtGridPosition(nextPosition!)
}
}
I'll have to answer my own question. First off, it's a bad question because I'm trying to do things incorrectly. The two main mistakes I made were
My compnent was trying to do too much
I wasn't using the updateWithDeltaTime method.
This is how the code and behaviour should be structured using GameplayKit's entity component structure. I'll try to epxlain how all the prices fit together at the end.
NodeComponent
This component is responsible for managing the actual SCNNode that represents my game character. I've moved the code for animating the character out of the ControlComponent and into this component.
class NodeComponent: GKComponent {
let node: SCNNode
let animationSpeed:NSTimeInterval = 0.25
var nextGridPosition: vector_int2 {
didSet {
makeNextMove(nextGridPosition, oldPosition: oldValue)
}
}
init(node:SCNNode, startPosition: vector_int2){
self.node = node
self.nextGridPosition = startPosition
}
func makeNextMove(newPosition: vector_int2, oldPosition: vector_int2) {
if ( newPosition.x != oldPosition.x || newPosition.y != oldPosition.y ){
let xPos: Float = Float(newPosition.x)
let zPos: Float = Float(newPosition.y)
let nextPosition: SCNVector3 = SCNVector3Make(xPos, 0, zPos)
let moveTo = SCNAction.moveTo(nextPosition, duration: self.animationSpeed)
let updateEntity = SCNAction.runBlock( { _ in
(self.entity as! PlayerEntity).gridPosition = newPosition
})
self.node.runAction(SCNAction.sequence([moveTo, updateEntity]), forKey: "move")
}
}
}
Note that every time the components gridPosition property is set, the makeNextMove method is called.
Control Component
My initial example was trying to do too much. This compnents sole responsibility now is to evaluate the next gridPosition for it's entity's NodeComponent. Note that because of updateWithDeltaTime, it will evaluate the next move as often as that method is called.
class ControlComponent: GKComponent {
var level: BRLevel!
var direction: BRDirection = .None
var queuedDirection: BRDirection?
init(level: BRLevel) {
self.level = level
super.init()
}
override func updateWithDeltaTime(seconds: NSTimeInterval) {
self.evaluateNextPosition()
}
func setDirection( nextDirection: BRDirection) {
self.queuedDirection = nextDirection
}
func evaluateNextPosition() {
var nextNode = self.nodeInDirection(self.direction)
if let _ = self.queuedDirection {
let nextPosition = self.entity?.componentForClass(NodeComponent.self)?.nextGridPosition
let targetPosition = (self.entity! as! PlayerEntity).gridPosition
let attemptedNextNode = self.nodeInDirection(self.queuedDirection)
if (nextPosition!.x == targetPosition.x && nextPosition!.y == targetPosition.y){
if let _ = attemptedNextNode {
nextNode = attemptedNextNode
self.direction = self.queuedDirection!
self.queuedDirection = nil
}
}
}
// Bail if we don't have a valid next node
guard let _ = nextNode else {
self.direction = .None
return
}
self.entity!.componentForClass(NodeComponent.self)?.nextGridPosition = nextNode!.gridPosition
}
func getCurrentGridGraphNode() -> GKGridGraphNode {
// Access grid position
let currentGridPosition = (self.entity as! PlayerEntity).gridPosition
// return unwrapped node
return level.gridGraph.nodeAtGridPosition(currentGridPosition)!
}
func nodeInDirection( nextDirection:BRDirection? ) -> GKGridGraphNode? {
guard let _ = nextDirection else { return nil }
let currentGridGraphNode = self.getCurrentGridGraphNode()
return self.nodeInDirection(nextDirection!, fromNode: currentGridGraphNode)
}
func nodeInDirection( nextDirection:BRDirection?, fromNode node:GKGridGraphNode? ) -> GKGridGraphNode? {
guard let _ = nextDirection else { return nil }
guard let _ = node else { return nil }
var nextPosition: vector_int2?
switch (nextDirection!) {
case .Left:
nextPosition = vector_int2(node!.gridPosition.x + 1, node!.gridPosition.y)
break
case .Right:
nextPosition = vector_int2(node!.gridPosition.x - 1, node!.gridPosition.y)
break
case .Down:
nextPosition = vector_int2(node!.gridPosition.x, node!.gridPosition.y - 1)
break
case .Up:
nextPosition = vector_int2(node!.gridPosition.x, node!.gridPosition.y + 1)
break;
case .None:
return nil
}
return level.gridGraph.nodeAtGridPosition(nextPosition!)
}
}
GameViewController
Here's where everything pieces together. There's a lot going on in the class, so I'll post only what's relevant.
class GameViewController: UIViewController, SCNSceneRendererDelegate {
var entityManager: BREntityManager?
var previousUpdateTime: NSTimeInterval?;
var playerEntity: GKEntity?
override func viewDidLoad() {
super.viewDidLoad()
// create a new scene
let scene = SCNScene(named: "art.scnassets/game.scn")!
// retrieve the SCNView
let scnView = self.view as! SCNView
// set the scene to the view
scnView.scene = scene
// show statistics such as fps and timing information
scnView.showsStatistics = true
scnView.delegate = self
entityManager = BREntityManager(level: level!)
createPlayer()
configureSwipeGestures()
scnView.playing = true
}
func createPlayer() {
guard let playerNode = level!.scene.rootNode.childNodeWithName("player", recursively: true) else {
fatalError("No player node in scene")
}
// convert scene coords to grid coords
let scenePos = playerNode.position;
let startingGridPosition = vector_int2(
Int32( floor(scenePos.x) ),
Int32( floor(scenePos.z) )
)
self.playerEntity = PlayerEntity(gridPos: startingGridPosition)
let nodeComp = NodeComponent(node: playerNode, startPosition: startingGridPosition)
let controlComp = ControlComponent(level: level!)
playerEntity!.addComponent(nodeComp)
playerEntity!.addComponent(controlComp)
entityManager!.add(playerEntity!)
}
func configureSwipeGestures() {
let directions: [UISwipeGestureRecognizerDirection] = [.Right, .Left, .Up, .Down]
for direction in directions {
let gesture = UISwipeGestureRecognizer(target: self, action: Selector("handleSwipe:"))
gesture.direction = direction
self.view.addGestureRecognizer(gesture)
}
}
func handleSwipe( gesture: UISwipeGestureRecognizer ) {
let controlComp = playerEntity!.componentForClass(ControlComponent.self)!
switch gesture.direction {
case UISwipeGestureRecognizerDirection.Up:
controlComp.setDirection(BRDirection.Up)
break
case UISwipeGestureRecognizerDirection.Down:
controlComp.setDirection(BRDirection.Down)
break
case UISwipeGestureRecognizerDirection.Left:
controlComp.setDirection(BRDirection.Left)
break
case UISwipeGestureRecognizerDirection.Right:
controlComp.setDirection(BRDirection.Right)
break
default:
break
}
}
// MARK: SCNSceneRendererDelegate
func renderer(renderer: SCNSceneRenderer, updateAtTime time: NSTimeInterval) {
let delta: NSTimeInterval
if let _ = self.previousUpdateTime {
delta = time - self.previousUpdateTime!
}else{
delta = 0.0
}
self.previousUpdateTime = time
self.entityManager!.update(withDelaTime: delta)
}
}
Entity Manager
I picked up this tip following this Ray Wenderlich tutorial. Essentially, it's a bucket to keep all your entities and components in to simplify to work of managing and updating them. I highly reccomend giving that tutorial a walthrough to understand this better.
class BREntityManager {
var entities = Set<GKEntity>()
var toRemove = Set<GKEntity>()
let level: BRLevel
//----------------------------------------------------------------------------------
lazy var componentSystems: [GKComponentSystem] = {
return [
GKComponentSystem(componentClass: ControlComponent.self),
GKComponentSystem(componentClass: NodeComponent.self)
]
}()
//----------------------------------------------------------------------------------
init(level:BRLevel) {
self.level = level
}
//----------------------------------------------------------------------------------
func add(entity: GKEntity){
if let node:SCNNode = entity.componentForClass(NodeComponent.self)!.node {
if !level.scene.rootNode.childNodes.contains(node){
level.scene.rootNode.addChildNode(node)
}
}
entities.insert(entity)
for componentSystem in componentSystems {
componentSystem.addComponentWithEntity(entity)
}
}
//----------------------------------------------------------------------------------
func remove(entity: GKEntity) {
if let _node = entity.componentForClass(NodeComponent.self)?.node {
_node.removeFromParentNode()
}
entities.remove(entity)
toRemove.insert(entity)
}
//----------------------------------------------------------------------------------
func update(withDelaTime deltaTime: NSTimeInterval) {
// update components
for componentSystem in componentSystems {
componentSystem.updateWithDeltaTime(deltaTime)
}
// remove components
for curRemove in toRemove {
for componentSystem in componentSystems {
componentSystem.removeComponentWithEntity(curRemove)
}
}
toRemove.removeAll()
}
}
So how does all this fit together
The ControlComponent.setDirection() method can be called at any time.
If an entity or component implements an updateWithDeltaTime method, it should be called every frame. It took me a little while to figure out how to do this with SceneKit as most GameplayKit example's are set up for SpriteKit, and SKScene has a very conventient updatet method.
For SceneKit, We have to make the GameVierwController the SCNSceneRendererDelegate for the SCNView. Then, using the rendererUpdateAtTime method, we can call updateAtDeltaTime on the Entity Manager which handles calling the same method on all entities and components every frame.
Note You have to manually set the playing property to true for this to work.
Now we move to the actual animation. ControlComponent is evaluating what the NodeComponents next grid position should be every frame, using the direction property (You can ignore the queuedDirection property, it's an implementation detail).
This means that the NodeComponents.makeNextMove() method is also being called every frame. Whenever newPosition and oldPosition are not the same, the animation is applied. And whenever the animation finishes, the node's gridPosition is updated.
Now, as to why my intial method for animating my SCNNode didn't work, I've no idea. But at least it forced me to better understand how to use GameplayKit's Entity/Component design.

check current location is in MkPolygons

I am working in an IOS 7 project ,it contains a location checking (current location is in given polygons).
I am Using the following code to check the condition
Created an array of MKPolygons
for(MKPolygon *poly in self.polygonArray)
{
[self checkTheLocationIsInPolygon:currentLocation polygon:poly];
}
- (void)checkTheLocationIsInPolygon:(CLLocation*)aLocation polygon:(MKPolygon*)aPolygon
{
CLLocationCoordinate2D coordinate = {aLocation.coordinate.latitude, aLocation.coordinate.longitude};
MKMapPoint mapPoint = MKMapPointForCoordinate(coordinate);
CGMutablePathRef mpr = CGPathCreateMutable();
MKMapPoint *polygonPoints = aPolygon.points;
size_t nCount = aPolygon.pointCount;
for (int p = 0; p < nCount; p++)
{
MKMapPoint mp = polygonPoints[p];
if (p == 0)
CGPathMoveToPoint(mpr, NULL, mp.x, mp.y);
else
CGPathAddLineToPoint(mpr, NULL, mp.x, mp.y);
}
CGPoint mapPointAsCGP = CGPointMake(mapPoint.x, mapPoint.y);
BOOL pointIsInPolygon = CGPathContainsPoint(mpr, NULL, mapPointAsCGP, FALSE);
CGPathRelease(mpr);
if(pointIsInPolygon == YES)
{
//IN
}
else
{
//Out
}
}
This code is working correctly(pointIsInPolygon return YES/NO correctly) for the first polygon .Then the next iteration (Next polygon from array) pointIsInPolygon gives the previous state means, it return NO if the first polygon was outside the location and it return YES if the first polygon was inside the location .
How to fix this issue?
If anybody know, please give me a suggestion
Swift is simple, you need to do the following, attention in the example is not implemented MKPolygon Array for mutiple MKPolygon on a map
// Init array for any MKPolygons
var arrayMKPolygon : NSMutableArray = []
override func viewWillAppear(animated: Bool) {
// Add one or more than one
self.setMKPolygon()
let tapGesture = UITapGestureRecognizer(target: self, action: "revealRegionDetailsWithPressOnMap:")
tapGesture.numberOfTapsRequired = 1
tapGesture.numberOfTouchesRequired = 1
self.mapView.addGestureRecognizer(tapGesture)
}
func setMKPolygon(){
// Poinst for polygon -> (or NSArray)
for() {
----> (dynamic for example web service) var points = [CLLocationCoordinate2DMake(41.000512, -109.050116),
CLLocationCoordinate2DMake(41.002371, -102.052066),
CLLocationCoordinate2DMake(36.993076, -102.041981),
CLLocationCoordinate2DMake(36.99892, -109.045267)]
// Polygon
let poly: MKPolygon = MKPolygon(coordinates: &points, count: 4)
// Add polygon
mapView.addOverlay(poly)
// Add objecto to Array
arrayMKPolygon.addObject(poly)
}
}
func revealRegionDetailsWithPressOnMap(sender: UITapGestureRecognizer) {
let touchLocation = sender.locationInView(self.mapView)
let locationCoordinate = self.mapView.convertPoint(touchLocation, toCoordinateFromView: self.mapView)
print("Tapped at lat: \(locationCoordinate.latitude) long: \(locationCoordinate.longitude)")
for i in 0...arrayMKPolygon.count - 1 {
let polygonView = MKPolygonRenderer(overlay: arrayMKPolygon[i] as! MKOverlay)
let mapPoint = MKMapPointForCoordinate(locationCoordinate)
let circlePoint = polygonView.pointForMapPoint(mapPoint)
let mapCoordinateIsInCircle : Bool = CGPathContainsPoint(polygonView.path, nil, circlePoint, false)
if mapCoordinateIsInCircle{
print("Yes, is within index --> \(i)")
}
else{
print("NO")
}
}
}
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is MKPolygon {
let polygonView = MKPolygonRenderer(overlay: overlay)
polygonView.strokeColor = UIColor.magentaColor()
polygonView.fillColor = UIColor.yellowColor().colorWithAlphaComponent(0.15)
polygonView.lineWidth = 1.5;
return polygonView
}
return MKPolygonRenderer(overlay: overlay)
}