Can't draw a polyline via MapKit in Swift - swift

I'm trying to draw a line between two annotations on my map. The code is working, but i don't see it on map. Here is my code:
func longPressGesture()
{
let lpg = UILongPressGestureRecognizer(target: self, action: "longPressAction:")
lpg.minimumPressDuration = 1;
Map.addGestureRecognizer(lpg)
}
func longPressAction(myRecognizer : UILongPressGestureRecognizer)
{
let currPoint = myRecognizer.locationInView(Map)
let point = Map.convertPoint(currPoint, toCoordinateFromView: Map)
points.append(currPoint);
if(points.count>1)
{
let startPoint = Map.convertPoint(points[points.count-2], toCoordinateFromView: Map)
let endPoint = Map.convertPoint(currPoint, toCoordinateFromView: Map)
var lineCoords = [startPoint,endPoint]
var line = MKPolyline(coordinates: &lineCoords, count: 2)
Map.addOverlay(line)
}
let myAnnotation = MKPointAnnotation();
myAnnotation.coordinate = point
myAnnotation.title = "Test"
myAnnotation.subtitle = "Test subtitle"
Map.addAnnotation(myAnnotation);
}
mapView method(not sure if it is used, i'm new to swift and i didn't write this):
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
print(overlay)
if overlay is MKCircle {
let circle = MKCircleRenderer(overlay: overlay);
circle.strokeColor = UIColor.redColor();
circle.fillColor = UIColor(red: 255, green: 0, blue: 0, alpha: 0.1);
circle.lineWidth = 1;
return circle;
}else if overlay is MKTileOverlay {
let carte_Renderer = MKTileOverlayRenderer(overlay: overlay)
carte_Renderer.alpha = 1
return carte_Renderer
}else if overlay is MKPolyline {
let polylineRenderer = MKPolylineRenderer(overlay: overlay);
polylineRenderer.strokeColor = UIColor.blueColor();
polylineRenderer.lineWidth = 5;
return polylineRenderer;
}else {
return MKPolylineRenderer();
}
}

Ensure you've got an rendererForOverlay method, which defines how your MKPolyline objects will look.
You'll need to include theMKMapViewDelegate as well for this to work.
func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer!
{
if overlay is MKPolyline
{
let route: MKPolyline = overlay as MKPolyline
let routeRenderer = MKPolylineRenderer(polyline:route)
routeRenderer.lineWidth = 3.0
routeRenderer.strokeColor = UIColor(red: 45.0/255.0, green: 200.0/255.0, blue: 0.0/255.0, alpha: 1);
return routeRenderer
}
return nil
}

Related

How to update UIBezierpath and CAShapeLayer path with pan gesture of UIView?

'''
import UIKit
class CanvasView: UIView {
var circleViewTag = 1000
var coordinatePoints: [String] = ["243,103","534,86","243,286","426,286"] {
didSet {
self.updateCoordinateArray()
self.drawPoints()
}
}
fileprivate var coordArray: [CGPoint] = []
var shape = CAShapeLayer()
var path = UIBezierPath()
/*// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}*/
private func drawPoints() -> Void {
CommonMethods.printLog("\(coordinatePoints)")
self.layer.addSublayer(shape)
shape.opacity = 0.5
shape.lineWidth = 2
shape.lineJoin = CAShapeLayerLineJoin.miter
shape.strokeColor = UIColor.white.cgColor
shape.fillColor = UIColor(red: 1.0, green: 0.2, blue: 0.2, alpha: 0.2).cgColor
if let firstCoord = self.coordArray.first {
path.move(to: firstCoord)
}
for (index, cgPoint) in self.coordArray.enumerated() {
self.drawCircularPoint(points: cgPoint)
if index == 0 {
continue
}
path.addLine(to: cgPoint)
}
path.close()
shape.path = path.cgPath
//self.drawLineBetweenPoints()
}
private func drawCircularPoint(points: CGPoint) -> Void {
let circleView = UIView.init(frame: .zero)
circleViewTag = circleViewTag + 1
circleView.tag = circleViewTag
circleView.frame.size = CGSize.init(width: 30.0, height: 30.0)
circleView.layer.cornerRadius = 15.0
circleView.center = points
circleView.backgroundColor = .random()
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.draggedView(_:)))
panGesture.view?.tag = circleView.tag
circleView.isUserInteractionEnabled = true
circleView.addGestureRecognizer(panGesture)
self.addSubview(circleView)
}
#objc func draggedView(_ sender:UIPanGestureRecognizer){
guard let getTag = sender.view?.tag else { return }
if let viewToDrag = self.viewWithTag(getTag) as? UIView {
var currentPoint: CGPoint = .zero
if path.contains(viewToDrag.center) {
print("YES")
currentPoint = path.currentPoint
}
let translation = sender.translation(in: self)
viewToDrag.center = CGPoint(x: viewToDrag.center.x + translation.x, y: viewToDrag.center.y + translation.y)
sender.setTranslation(CGPoint.zero, in: self)
if sender.state == .began && currentPoint != .zero {
let coordinateIndex = self.coordArray.firstIndex { (cgpoint) -> Bool in
if currentPoint == cgpoint {
return true
}
return false
}
if coordinateIndex != nil {
self.coordArray[coordinateIndex!] = viewToDrag.center
self.shape.removeFromSuperlayer()
self.path.removeAllPoints()
self.setNeedsDisplay()
self.layer.addSublayer(self.shape)
self.shape.opacity = 0.5
self.shape.lineWidth = 2
self.shape.lineJoin = CAShapeLayerLineJoin.miter
self.shape.strokeColor = UIColor.white.cgColor
self.shape.fillColor = UIColor(red: 1.0, green: 0.2, blue: 0.2, alpha: 0.2).cgColor
if let firstCoord = self.coordArray.first {
path.move(to: firstCoord)
}
for (index, cgPoint) in self.coordArray.enumerated() {
//self.drawCircularPoint(points: cgPoint)
if index == 0 {
continue
}
path.addLine(to: cgPoint)
}
path.close()
shape.path = path.cgPath
}
}
}
//self.bringSubviewToFront(viewDrag)
}
private func updateCoordinateArray() -> Void {
for singleCoordinate in self.coordinatePoints {
if singleCoordinate.contains(",") == true {
let splitCoordinate = singleCoordinate.split(separator: ",")
if splitCoordinate.count == 2 {
let xPos = CGFloat(Float(splitCoordinate[0]) ?? 0.0)
let yPos = CGFloat(Float(splitCoordinate[1]) ?? 0.0)
let cgPoint = CGPoint(x: xPos, y: yPos)
self.coordArray.append(cgPoint)
}
}
}
var penultimateIndex: Int?
if let penultimateCoordinate = self.coordArray.penultimate() {
penultimateIndex = self.coordArray.firstIndex { (cgpoint) -> Bool in
if penultimateCoordinate == cgpoint {
return true
}
return false
}
}
var lastIndex: Int?
if let lastCoordinate = self.coordArray.last {
lastIndex = self.coordArray.firstIndex { (cgpoint) -> Bool in
if lastCoordinate == cgpoint {
return true
}
return false
}
}
if penultimateIndex != nil && lastIndex != nil {
self.coordArray.swapAt(penultimateIndex!, lastIndex!)
}
}
'''
I am creating a polygon using UIBezierpath and CAShapelayer. Added pan gesture on all 4 points that is UIView. When I drag the point A,B,C,D the expected behaviour is that bezierpath and CAShapelayer gets updated with the updated points. And when user drag the inner part of the shape all the path gets updated. But I am unable to update the path and shape. Can anyone help me with this?
yon set name for layer
var shape = CAShapeLayer()
shape.name = "name1"
and then, instead of updating, you can delete it by first searching by name, and then add

Why is this CoreGraphics drawing code slow?

I wrote this drawing code based on some of my other open source programs.
It separates the lines into layers based on username.
It works, it's just really slow. I think it's because I'm adding to the layers and looping through them on the same layer as I draw. This was fast in C++ and Javascript but slow in Swift.
Ignore send_point as it's not slowing down the drawing code. (The Crystal server is very fast and currently ignores the command as well.)
How can I optimize this code?
//
// GlobalDrawController.swift
// GlobalChat
//
// Created by Jonathan Silverman on 7/9/20.
// Copyright © 2020 Jonathan Silverman. All rights reserved.
//
import Cocoa
class GlobalDrawController: NSViewController {
#IBOutlet weak var drawing_view: NSView!
var gcc: GlobalChatController?
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
print("viewDidLoad: gdc")
}
}
class LineDrawer : NSView {
var newLinear = NSBezierPath()
var points : [[String : Any]] = []
var nameHash : [String : Int] = [:] // which layer is this handle on
var layerOrder : [String] = [] // which order to draw layers
var layers : [String : Any] = [:] // which points are in a layer
// var username : String = ""
var scribbling : Bool = false
var pen_color : NSColor = NSColor.black.usingColorSpace(NSColorSpace.deviceRGB)!
var pen_width : CGFloat = CGFloat(1)
func addClick(_ x: CGFloat, y: CGFloat, dragging: Bool, red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat, width: CGFloat, clickName: String) {
var point : [String : Any] = [:]
point["x"] = x
point["y"] = y
point["dragging"] = dragging
point["red"] = red
point["green"] = green
point["blue"] = blue
point["alpha"] = alpha
point["width"] = width
point["clickName"] = clickName
points.append(point)
var layerName : String = ""
if(nameHash[clickName] == nil) {
let layer = 0
nameHash[clickName] = layer
layerName = "\(clickName)_\(layer)"
let layerArray : [[String : Any]] = []
layers[layerName] = layerArray
} else {
if(dragging == false) {
let layer = nameHash[clickName]! + 1
nameHash[clickName] = layer
layerName = "\(clickName)_\(layer)"
let layerArray : [[String : Any]] = []
layers[layerName] = layerArray
} else {
let layer = nameHash[clickName]!
layerName = "\(clickName)_\(layer)"
}
}
var tempLayers = layers[layerName] as! [[String : Any]]
tempLayers.append(point)
layers[layerName] = tempLayers
if(!layerOrder.contains(layerName)) {
layerOrder.append(layerName)
}
}
func drawLineTo(_ lastPoint : CGPoint, _ endPoint : CGPoint, _ penColor : NSColor, _ penWidth : CGFloat) {
newLinear.move(to: lastPoint)
newLinear.line(to: endPoint)
penColor.set()
newLinear.lineWidth = penWidth
newLinear.stroke()
}
func redraw() {
NSColor.white.setFill() // allow configuration of this later
bounds.fill()
for layer in layerOrder {
let layerArray = layers[layer] as! [[String : Any]]
for i in 1...layerArray.count - 1 {
let lastObj = layerArray[i - 1] as [String : Any]
var lastPoint : CGPoint = CGPoint()
lastPoint.x = lastObj["x"] as! CGFloat
lastPoint.y = lastObj["y"] as! CGFloat
let thisObj = layerArray[i] as [String : Any]
var thisPoint : CGPoint = CGPoint()
thisPoint.x = thisObj["x"] as! CGFloat
thisPoint.y = thisObj["y"] as! CGFloat
if(thisObj["dragging"] as! Bool && lastObj["dragging"] as! Bool) {
let red = lastObj["red"] as! CGFloat
let green = lastObj["green"] as! CGFloat
let blue = lastObj["blue"] as! CGFloat
let alpha = lastObj["alpha"] as! CGFloat
let penColor : NSColor = NSColor.init(red: red, green: green, blue: blue, alpha: alpha)
let penWidth = lastObj["width"] as! CGFloat
drawLineTo(lastPoint, thisPoint, penColor, penWidth)
}
}
}
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
let objectFrame: NSRect = self.frame
if self.needsToDraw(objectFrame) {
// drawing code for object
redraw()
}
}
override func mouseDown(with event: NSEvent) {
let gdc = self.window?.contentViewController as! GlobalDrawController
super.mouseDown(with: event)
scribbling = true
var lastPt = convert(event.locationInWindow, from: nil)
lastPt.x -= frame.origin.x
lastPt.y -= frame.origin.y
addClick(lastPt.x, y: lastPt.y, dragging: false, red: pen_color.redComponent, green: pen_color.greenComponent, blue: pen_color.blueComponent, alpha: pen_color.alphaComponent, width: pen_width, clickName: gdc.gcc!.handle)
send_point(lastPt.x, y: lastPt.y, dragging: false, red: pen_color.redComponent, green: pen_color.greenComponent, blue: pen_color.blueComponent, alpha: pen_color.alphaComponent, width: pen_width, clickName: gdc.gcc!.handle)
}
override func mouseDragged(with event: NSEvent) {
let gdc = self.window?.contentViewController as! GlobalDrawController
super.mouseDragged(with: event)
var newPt = convert(event.locationInWindow, from: nil)
newPt.x -= frame.origin.x
newPt.y -= frame.origin.y
addClick(newPt.x, y: newPt.y, dragging: true, red: pen_color.redComponent, green: pen_color.greenComponent, blue: pen_color.blueComponent, alpha: pen_color.alphaComponent, width: pen_width, clickName: gdc.gcc!.handle)
send_point(newPt.x, y: newPt.y, dragging: true, red: pen_color.redComponent, green: pen_color.greenComponent, blue: pen_color.blueComponent, alpha: pen_color.alphaComponent, width: pen_width, clickName: gdc.gcc!.handle)
needsDisplay = true
}
override func mouseUp(with event: NSEvent) {
super.mouseUp(with: event)
scribbling = false
}
func send_point(_ x: CGFloat, y: CGFloat, dragging: Bool, red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat, width: CGFloat, clickName: String) {
let gdc = self.window?.contentViewController as! GlobalDrawController
var point : [String] = []
point.append(String(x.description))
point.append(String(y.description))
point.append(String(dragging.description))
point.append(String(red.description))
point.append(String(green.description))
point.append(String(blue.description))
point.append(String(alpha.description))
point.append(String(width.description))
point.append(String(gdc.gcc!.chat_token))
gdc.gcc?.send_message("POINT", args: point)
}
}
Its slow because I used NSBezierPath.
Here is a faster DrawLineTo method.
func drawLineTo(_ lastPoint : CGPoint, _ endPoint : CGPoint, _ penColor : NSColor, _ penWidth : CGFloat) {
guard let context = NSGraphicsContext.current?.cgContext else { return }
context.setStrokeColor(penColor.cgColor)
context.setLineWidth(penWidth)
context.move(to: lastPoint)
context.addLine(to: endPoint)
context.strokePath()
}

How to draw custom shapes on MKMapView

I need to draw custom shapes like Arc, Semi-circle? I tried the below code but it's not rendering anything on the MKMapView.
Is this the right way to draw custom shapes?
class ViewController: UIViewController {
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
mapView.delegate = self
let center = CLLocationCoordinate2D(latitude: 15.463157486154865, longitude: 73.78846049308775)
let radius = CLLocationCoordinate2D(latitude: 15.495608080208948, longitude: 73.83418584279791)
addCircle(center: center, radius: radius)
}
private func createArcPath() -> UIBezierPath {
let center = CLLocationCoordinate2D(latitude: 15.463157486154865, longitude: 73.78846049308775)
// converting the coordinates to CGPoint with respect to MKMapView.
let centerPoint = mapView.convert(center, toPointTo: self.mapView)
// Creating bezierPath of arc.
let path = UIBezierPath(arcCenter: centerPoint, radius: 6080.205481929489, startAngle: CGFloat.pi, endAngle: CGFloat.pi * 2, clockwise: true)
return path
}
}
extension ViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is MKCircle {
let arcRenderer = MKOverlayPathRenderer()
arcRenderer.path = createArcPath().cgPath
arcRenderer.strokeColor = UIColor.red
arcRenderer.fillColor = UIColor.red
arcRenderer.lineWidth = 10
arcRenderer.alpha = 1
arcRenderer.lineCap = .round
return arcRenderer
}
return MKOverlayRenderer()
}
}
extension ViewController {
private func addCircle(center ccoordinate: CLLocationCoordinate2D, radius rcoordinate: CLLocationCoordinate2D) {
let centerLocation = CLLocation.init(latitude: ccoordinate.latitude, longitude: ccoordinate.longitude)
let radiusLocation = CLLocation.init(latitude: rcoordinate.latitude, longitude: rcoordinate.longitude)
let radius = centerLocation.distance(from: radiusLocation)
let circle = MKCircle(center: ccoordinate, radius: radius)
mapView.addOverlay(circle)
}
}
Use this to add custom shape in MAPVIEW
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer
{
if overlay is MKCircle
{
print("overlay latitude: "+String(overlay.coordinate.latitude))
print("overlay longitude: "+String(overlay.coordinate.longitude))
let circleOverlay = overlay as! MKCircle
if(circleOverlay.accessibilityPath != nil)
{
let arcRenderer = MKOverlayPathRenderer()
arcRenderer.path = circleOverlay.accessibilityPath?.cgPath
arcRenderer.strokeColor = UIColor.red
arcRenderer.lineWidth = 10
arcRenderer.alpha = 0.3
return arcRenderer
}
let circle = MKCircleRenderer(overlay: overlay)
circle.strokeColor = UIColor.black
circle.fillColor = UIColor(red: 255, green: 0, blue: 0, alpha: 0.1)
circle.lineWidth = 1
circle.alpha = 0.3
return circle
}
}
After some RND I came across a library curvyRoute & used it to draw arch on MKMapView.
// Adds arch overlay to the mapView
private func addArcOverlays() {
let pointA = CLLocationCoordinate2D(latitude: 15.463157486154865, longitude: 73.78846049308775)
let pointB = CLLocationCoordinate2D(latitude: 15.495608080208948, longitude: 73.83418584279791)
mapView.addOverlay(LineOverlay(origin: pointA, destination: pointB))
let style = LineOverlayStyle(strokeColor: .red, lineWidth: 4, alpha: 1)
let arc = ArcOverlay(origin: pointA, destination: pointB, style: style)
arc.radiusMultiplier = 0.5
mapView.addOverlay(arc)
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
switch overlay {
case let lineOverlay as LineOverlay:
let linerender = MapLineOverlayRenderer(lineOverlay)
setVisibleMapRect(linerender.overlay, animated: true)
return linerender
case let polyline as MKPolyline:
let renderer = MKPolylineRenderer(overlay: polyline)
renderer.strokeColor = UIColor.yellow.withAlphaComponent(0.5)
renderer.lineWidth = 4
return renderer
default:
return MKOverlayRenderer()
}
}

inverted circle map fill in swift 4

I am trying to draw a circle on map, when user select option 1 the circle is filled with blue color, when user select option 2, the whole map will be filled with blue while the circle area is colorless. how is that possible?
`func addRadiusOverlay(forGeotification geotification: Geotification) {
mapView?.addOverlay(MKCircle(center: geotification.coordinate, radius: 300))
}`
`func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is MKCircle {
let circleRenderer = MKCircleRenderer(overlay: overlay)
circleRenderer.lineWidth = 5.0
circleRenderer.strokeColor = UIColor(red: 0/255, green: 122/255, blue: 255/255, alpha: 1.0)
circleRenderer.fillColor = circleRenderer.strokeColor!.withAlphaComponent(0.1)
return circleRenderer
}
return MKOverlayRenderer(overlay: overlay)
}`
In case of Option-2, to draw a circle with filled outside and transparent hole, use MKPolygon.polygonWithPoints:count:interiorPolygons: with interiorPolygons parameter to be the circle MKPolygon, like so:
MKPolygon(coordinates: WORLD_COORDINATES, count: WORLD_COORDINATES.count, interiorPolygons: circlePolygon)
Use following method to generate polygons
func setupRadiusOverlay(forGeotification geotification: Geotification) {
let c = makeCircleCoordinates(geotification.coordinate, radius: RADIUS)
self.option1polygon = MKPolygon(coordinates: c, count: c.count, interiorPolygons: nil)
self.option2polygon = MKPolygon(coordinates: WORLD_COORDINATES, count: WORLD_COORDINATES.count, interiorPolygons: option1polygon)
}
Use following method to add a polygon
func addRadiusOverlay(isOption2Selected: Bool) {
guard let mapView = mapView else { return }
let overlay = isOption2Selected ? self.option2polygon : self.option1polygon
if mapView.overlays.index(where: { $0 === overlay }) == nil {
mapView.removeOverlays(mapView.overlays.filter{ $0 is MKPolygon })
mapView.addOverlay(overlay)
}
}
Change delegate method mapView(_:rendererFor:)
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
guard overlay is MKPolygon else {
return MKOverlayRenderer(overlay: overlay)
}
let color = UIColor(red: 0/255, green: 122/255, blue: 255/255, alpha: 1.0)
let renderer = MKPolygonRenderer(overlay: overlay)
renderer.lineWidth = 5.0
renderer.strokeColor = color
renderer.fillColor = color.withAlphaComponent(0.1)
return renderer
}
following would be world coordinates
let WORLD_COORDINATES = [
CLLocationCoordinate2D(latitude: 90, longitude: 0),
CLLocationCoordinate2D(latitude: 90, longitude: 180),
CLLocationCoordinate2D(latitude:-90, longitude: 180),
CLLocationCoordinate2D(latitude:-90, longitude: 0),
CLLocationCoordinate2D(latitude:-90, longitude:-180),
CLLocationCoordinate2D(latitude: 90, longitude:-180)
]
And following helper method, courtesy of my old answer
func makeCircleCoordinates(_ coordinate: CLLocationCoordinate2D, radius: Double, tolerance: Double = 3.0) -> [CLLocationCoordinate2D] {
let latRadian = coordinate.latitude * .pi / 180
let lngRadian = coordinate.longitude * .pi / 180
let distance = (radius / 1000) / 6371 // kms
return stride(from: 0.0, to: 360.0, by: tolerance).map {
let bearing = $0 * .pi / 180
let lat2 = asin(sin(latRadian) * cos(distance) + cos(latRadian) * sin(distance) * cos(bearing))
var lon2 = lngRadian + atan2(sin(bearing) * sin(distance) * cos(latRadian),cos(distance) - sin(latRadian) * sin(lat2))
lon2 = fmod(lon2 + 3 * .pi, 2 * .pi) - .pi // normalise to -180..+180º
return CLLocationCoordinate2D(latitude: lat2 * (180.0 / .pi), longitude: lon2 * (180.0 / .pi))
}
}
option-2 selection yields
option-1 should do inverse :)

Swift progress view completion actions

Helllo , I am using swift. I have this code that control a progressview on my main view controller. I feed the progress view with a double var containing seconds. Once the progressview has completed i d like to perfom some actions on the main viewcontroller. but i have no idea on where and how to implement actions. here is my code :
class CounterProgressView: UIView {
let sharedDefaults = NSUserDefaults(suiteName: "group.birkyboy.TodayExtensionSharingDefaults")
private let progressLayer: CAShapeLayer = CAShapeLayer()
private var progressLabel: UILabel
required init(coder aDecoder: NSCoder) {
progressLabel = UILabel()
super.init(coder: aDecoder)
createProgressLayer()
}
override init(frame: CGRect) {
progressLabel = UILabel()
super.init(frame: frame)
createProgressLayer()
}
private func createProgressLayer() {
let startAngle = CGFloat(M_PI_2)
let endAngle = CGFloat(M_PI * 2 + M_PI_2)
let centerPoint = CGPointMake(CGRectGetWidth(frame)/2 , CGRectGetHeight(frame)/2)
var gradientMaskLayer = gradientMask()
progressLayer.path = UIBezierPath(arcCenter:centerPoint, radius: CGRectGetWidth(frame)/2 - 30.0, startAngle:startAngle, endAngle:endAngle, clockwise: true).CGPath
progressLayer.backgroundColor = UIColor.clearColor().CGColor
progressLayer.fillColor = UIColor.clearColor().CGColor
progressLayer.strokeColor = UIColor.blackColor().CGColor
progressLayer.lineWidth = 25.0
progressLayer.strokeStart = 0.0
progressLayer.strokeEnd = 0.0
gradientMaskLayer.mask = progressLayer
layer.addSublayer(gradientMaskLayer)
}
private func gradientMask() -> CAGradientLayer {
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.locations = [1.0, 1.0]
let colorTop: AnyObject = UIColor(red: 255.0/255.0, green: 0.0/255.0, blue: 0.0/255.0, alpha: 1).CGColor
let colorBottom: AnyObject = UIColor(red: 255.0/255.0, green: 0.0/255.0, blue: 0.0/255.0, alpha: 0).CGColor
let arrayOfColors: [AnyObject] = [colorTop, colorBottom]
gradientLayer.colors = arrayOfColors
return gradientLayer
}
func hideProgressView() {
progressLayer.strokeEnd = 0.0
progressLayer.removeAllAnimations()
}
func secondsToHoursMinutesSeconds (seconds : Double) -> (Double, Double, Double) {
let (hr, minf) = modf (seconds / 3600)
let (min, secf) = modf (60 * minf)
return (hr, min, 60 * secf)
}
func animateProgressView() {
progressLayer.strokeEnd = 0.0
var temps = sharedDefaults!.objectForKey("roueCounter") as? Double
println(temps)
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = CGFloat(0.0)
animation.toValue = CGFloat(1)
animation.duration = temps!
animation.delegate = self
animation.removedOnCompletion = false
animation.additive = true
animation.fillMode = kCAFillModeForwards
progressLayer.addAnimation(animation, forKey: "strokeEnd")
}
override func animationDidStop(anim: CAAnimation!, finished flag: Bool) {
}
}
And this the functions on the main viewController that call the progressview :
func secondsToHoursMinutesSeconds (seconds : Int) {
sharedDefaults!.setObject(seconds, forKey: "roueCounter")
sharedDefaults!.synchronize()
let (h, m, s) = (seconds / 3600, (seconds % 3600) / 60, (seconds % 3600) % 60)
if m < 10 {
counterInfosLabel.text = "FROM CURRENT TIME IN"
counter.text = "\(h)H0\(m)"
println ("\(h) Hours, 0\(m) Minutes")
counterProgressView.animateProgressView()
} else {
counterInfosLabel.text = "FROM CURRENT TIME IN"
counter.text = "\(h)H\(m)"
println ("\(h) Hours, \(m) Minutes")
counterProgressView.animateProgressView()
}
if h == 0 && (m <= 5 && m > 0){
counter.textColor = UIColor.redColor()
counterInfosLabel.text = "FROM CURRENT TIME IN"
PKNotification.toastBackgroundColor = UIColor.redColor()
PKNotification.toast("You are Illegal in 5 minutes")
counterProgressView.animateProgressView()
var localNotification = UILocalNotification()
localNotification.fireDate = NSDate(timeIntervalSinceNow: 1)
localNotification.alertBody = "You Are Illegal In 5 Minutes."
localNotification.timeZone = NSTimeZone.defaultTimeZone()
localNotification.soundName = "chime.mp3"
localNotification.category = "CATAGORY_1"
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
}
if h <= 0 && m <= 0 {
counterInfosLabel.text = "ILLEGAL SINCE"
counter.text = "\(-h)H\(-m)MN"
PKNotification.toastBackgroundColor = UIColor.redColor()
PKNotification.toast("You are Illegal")
var localNotification = UILocalNotification()
localNotification.fireDate = NSDate(timeIntervalSinceNow: 1)
localNotification.alertBody = "You Are Illegal !"
localNotification.timeZone = NSTimeZone.defaultTimeZone()
localNotification.soundName = "chime.mp3"
localNotification.category = "CATAGORY_1"
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
}
}
Thank you for any help you can provide me.
So the progress view knows what to do when the animation is complete, you could give your CounterProgressView a completion handler property:
var completionHandler: (() -> ())?
Then modify the animateProgressView accept and save the completion handler provided by the caller:
func animateProgressView(completionHandler: (() -> ())?) {
self.completionHandler = completionHandler
// set up and start animation
}
You could then have the animation completion delegate method call this closure:
override func animationDidStop(anim: CAAnimation!, finished flag: Bool) {
completionHandler?()
completionHandler = nil
}
Obviously, now when you start the animation, you can specify what you want to happen when the animation is complete:
counterProgressView.animateProgressView() {
// what to do when animation is done
}
Yes i think i did follow exactly :
import UIKit
class CounterProgressView: UIView {
let sharedDefaults = NSUserDefaults(suiteName: "group.birkyboy.TodayExtensionSharingDefaults")
var completionHandler: (() -> ())?
private let progressLayer: CAShapeLayer = CAShapeLayer()
private var progressLabel: UILabel
required init(coder aDecoder: NSCoder) {
progressLabel = UILabel()
super.init(coder: aDecoder)
createProgressLayer()
}
override init(frame: CGRect) {
progressLabel = UILabel()
super.init(frame: frame)
createProgressLayer()
}
private func createProgressLayer() {
let startAngle = CGFloat(M_PI_2)
let endAngle = CGFloat(M_PI * 2 + M_PI_2)
let centerPoint = CGPointMake(CGRectGetWidth(frame)/2 , CGRectGetHeight(frame)/2)
var gradientMaskLayer = gradientMask()
progressLayer.path = UIBezierPath(arcCenter:centerPoint, radius: CGRectGetWidth(frame)/2 - 30.0, startAngle:startAngle, endAngle:endAngle, clockwise: true).CGPath
progressLayer.backgroundColor = UIColor.clearColor().CGColor
progressLayer.fillColor = UIColor.clearColor().CGColor
progressLayer.strokeColor = UIColor.blackColor().CGColor
progressLayer.lineWidth = 25.0
progressLayer.strokeStart = 0.0
progressLayer.strokeEnd = 0.0
gradientMaskLayer.mask = progressLayer
layer.addSublayer(gradientMaskLayer)
}
private func gradientMask() -> CAGradientLayer {
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.locations = [1.0, 1.0]
let colorTop: AnyObject = UIColor(red: 255.0/255.0, green: 0.0/255.0, blue: 0.0/255.0, alpha: 1).CGColor
let colorBottom: AnyObject = UIColor(red: 255.0/255.0, green: 0.0/255.0, blue: 0.0/255.0, alpha: 0).CGColor
let arrayOfColors: [AnyObject] = [colorTop, colorBottom]
gradientLayer.colors = arrayOfColors
return gradientLayer
}
func hideProgressView() {
progressLayer.strokeEnd = 0.0
progressLayer.removeAllAnimations()
}
func secondsToHoursMinutesSeconds (seconds : Double) -> (Double, Double, Double) {
let (hr, minf) = modf (seconds / 3600)
let (min, secf) = modf (60 * minf)
return (hr, min, 60 * secf)
}
func animateProgressView(completionHandler: (() -> ())?) {
self.completionHandler = completionHandler
progressLayer.strokeEnd = 0.0
var temps = sharedDefaults!.objectForKey("roueCounter") as? Double
println(temps)
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = CGFloat(0.0)
animation.toValue = CGFloat(1)
animation.duration = temps!
animation.delegate = self
animation.removedOnCompletion = false
animation.additive = true
animation.fillMode = kCAFillModeForwards
progressLayer.addAnimation(animation, forKey: "strokeEnd")
}
override func animationDidStop(anim: CAAnimation!, finished flag: Bool) {
completionHandler?()
completionHandler = nil
}
}