MKOverlay image get rotated on MKMapView in Swift - ios

I am using MKOverlay to draw image over the 4 lat/long points
But not sure image get rotated. See board image over the MKMapView. This basket board image must be fit inside A, B, C, D co-ordinate. E is center co-ordinate.
here is the code
let upperleft = MKMapPoint(b.coordinate)
let lowerRight = MKMapPoint(d.coordinate)
let mapRect = MKMapRect(x: fmin(upperleft.x,lowerRight.x), y: fmin(upperleft.y,lowerRight.y), width: fabs(upperleft.x-lowerRight.x), height: fabs(upperleft.y-lowerRight.y));
let overlay = ImageOverlay(image: #imageLiteral(resourceName: "blank_court"), rect: mapRect, center: center.coordinate)
mapView.addOverlay(overlay)
Here is defined class
class ImageOverlay : NSObject, MKOverlay {
var coordinate: CLLocationCoordinate2D
let image:UIImage
let boundingMapRect: MKMapRect
init(image: UIImage, rect: MKMapRect, center: CLLocationCoordinate2D) {
self.image = image
self.boundingMapRect = rect
self.coordinate = center
}
}
class ImageOverlayRenderer : MKOverlayRenderer {
override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) {
guard let overlay = self.overlay as? ImageOverlay else {
return
}
let rect = self.rect(for: overlay.boundingMapRect)
UIGraphicsPushContext(context)
overlay.image.draw(in: rect)
UIGraphicsPopContext()
}
}

Related

Resize and Rotate image Annotation added in pdf

Using this Link I have added signature image annotation in PDF file
But i could not find any guide for how to rotate and resize image annotation using the button added on top of annotation image like shown in the image.
What i want to do is:
want to scale/resize signature image(make it small or large by adding this resize button)
want to rotate signature image
For Pinch to zoom i am adding pinch gesture to PDFView but that gesture zoom in / zoom out the main pdf.tried to fix it by below code but not worked.
#objc func scale(sender : UIPinchGestureRecognizer) {
print("----------Scale----------")
let touchLocation = sender.location(in: pdfContainerView)
guard let page = pdfContainerView.page(for: touchLocation, nearest: true)
else {
return
}
let locationOnPage = pdfContainerView.convert(touchLocation, to: page)
switch sender.state {
case .began:
guard let annotation = page.annotation(at: locationOnPage) else {
return
}
if annotation.isKind(of: ImageStampAnnotation.self) {
currentlySelectedAnnotation = annotation
// to disable pinch gesture for pdfview but it is not working
pdfContainerView.minScaleFactor = pdfContainerView.scaleFactor
pdfContainerView.maxScaleFactor = pdfContainerView.scaleFactor
}
case .changed,.ended:
guard let annotation = currentlySelectedAnnotation else {
return
}
let initialBounds = annotation.bounds
//scale annotation
case .cancelled:
break
default:
break
}
}
Thanks in advance!!
I tried to implement rotation, zoom, pan on PDFview.
I created a new view above the PDFAnnotation and then moved, rotated, and scaled the view as above.
about PDFAnnotation:
class PDFImageAnnotation: PDFAnnotation {
let image: UIImage
let originalBounds: CGRect
/// 0 - 360
var angle: CGFloat = 0 {
didSet {
// reload annotation
shouldDisplay = true
}
}
/// scale annotation
var scale: CGFloat = 1 {
didSet {
// Scale on the original size
let width = originalBounds.width * scale
let height = originalBounds.height * scale
// move origin
let x = bounds.origin.x - (width - bounds.width)/2
let y = bounds.origin.y - (height - bounds.height)/2
print("new ---- \(CGRect(x: x, y: y, width: width, height: height))")
// Setting the bounds will automatically re-render
bounds = CGRect(x: x, y: y, width: width, height: height)
}
}
/// move center point
var center: CGPoint = .zero {
didSet {
let x = center.x - bounds.width/2.0
let y = center.y - bounds.height/2.0
// Setting the bounds will automatically re-render
bounds = CGRect(origin: CGPoint(x: x, y: y), size: bounds.size)
}
}
public init(bounds: CGRect, image: UIImage) {
self.image = image
originalBounds = bounds
super.init(bounds: bounds, forType: .ink, withProperties: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(with box: PDFDisplayBox, in context: CGContext) {
super.draw(with: box, in: context)
print("PDFImageAnnotation bounds - \(bounds)")
guard let page = page else {
return
}
UIGraphicsPushContext(context)
context.saveGState()
// rotate annotation
// The origin of the annotation is always at the initial position
let translateX = bounds.width/2 + bounds.origin.x
let translateY = bounds.height/2 + bounds.origin.y
// The page has its own rotation Angle
let newAngle = angle + CGFloat(page.rotation)
context.translateBy(x: translateX, y: translateY)
context.rotate(by: newAngle*(CGFloat.pi/180.0))
context.translateBy(x: -translateX, y: -translateY)
// draw image
if let cgImage = image.cgImage {
context.draw(cgImage, in: bounds)
}
context.restoreGState()
UIGraphicsPopContext()
}
}
use:
extension ViewController: PDFSignAnnotationViewDelegate {
func signAnnotationView(_ view: PDFSignAnnotationView, didMove point: CGPoint) {
guard let page = pdfContainerView.currentPage,
let imageAnnotation = page.annotations.filter({$0.userName == view.identity}).first as? PDFImageAnnotation else {
return
}
let locationOnPage = self.pdfContainerView.convert(point, to: page)
imageAnnotation.center = locationOnPage
}
func signAnnotationView(_ view: PDFSignAnnotationView, didScale scale: CGFloat) {
guard let page = pdfContainerView.currentPage,
let imageAnnotation = page.annotations.filter({$0.userName == view.identity}).first as? PDFImageAnnotation else {
return
}
imageAnnotation.scale = scale
}
func signAnnotationView(_ view: PDFSignAnnotationView, didRotate angle: CGFloat) {
guard let page = pdfContainerView.currentPage,
let imageAnnotation = page.annotations.filter({$0.userName == view.identity}).first as? PDFImageAnnotation else {
return
}
print("didRotate - \(angle)")
imageAnnotation.angle = -angle
}
func signAnnotationView(_ view: PDFSignAnnotationView, close identity: String) {
guard let page = pdfContainerView.currentPage else {
return
}
guard let annotation = page.annotations.filter({$0.userName == identity}).first else {
return
}
page.removeAnnotation(annotation)
}
}
TODO:
Move and zoom the page without sending changes to the upper view
I refer to here: https://medium.com/#rajejones/add-a-signature-to-pdf-using-pdfkit-with-swift-7f13f7faad3e
Demo https://github.com/roMummy/PDFSignature/tree/master/PDFSignature

How can I provide an image for an MKPolygonRenderer?

My code correctly gets an image and provides the bounds for the mapView. I want to put the image within the bounds but I am not sure how. I am able to fill it with a color but I need help making it an image.
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolygonRenderer(overlay: overlay)
//renderer.fillColor = UIColor.red -> works
return renderer
}
How can I fill it with this image that I downloaded?https://radar.weather.gov/ridge/Conus/RadarImg/latest_radaronly.gif
Using this subclass of MKOverlayRenderer you will be able to render UIImage overlay on the actual MKPolygon
class CustomOverlayRenderer: MKOverlayRenderer {
private let overlayImage: UIImage?
init(overlay: MKOverlay, image: UIImage? = nil) {
self.overlayImage = image
super.init(overlay: overlay)
}
override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) {
guard let cgImage = self.overlayImage?.cgImage else { return }
let rect = self.rect(for: overlay.boundingMapRect)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: 0, y: -rect.height)
context.draw(cgImage, in: rect)
}
}
Just change your delegate method to this use CustomOverlayRenderer, like so:
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
return CustomOverlayRenderer(overlay: overlay, image: UIImage(named: "latest_radaronly.gif")!)
}

How to animate MKOverlayRenderer added in Mapview iOS?

I have a mapview as shown below
The lines drawn in the map are using MKPolyline. I have used apple MapKit framework to display my map. My requirement is when the user selects the annotation on Polyline, the polyline should show direction as shown below
How can I show the animation in my map view? MKOverlayRenderer inherits from NSObject, It is not possible to animate using UIView Animation. I found some link here , its in objective C for iOS 7, IS it possible in swift to do the same?
MKOverlayRender inherits from NSObject. So it is not possible to add CAlayer to MKOverlayRenderer as explained in this sample project. The steps I followed to get the following animation effect are
Subclass MKOverlayRenderer and add a custom image as overlay on the map
class MapOverlayView: MKOverlayRenderer {
var overlayImage: UIImage
var angle: CGFloat
init(overlay: MKOverlay, overlayImage:UIImage, angle: CGFloat) {
self.overlayImage = overlayImage
self.angle = angle
super.init(overlay: overlay)
}
override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) {
let mapImage = overlayImage.cgImage
let mapRect = rect(for: overlay.boundingMapRect)
// Calculate centre point on which image should be rotated
let centerPoint = CGPoint(x: mapRect.midX, y: mapRect.midY)
let a = sqrt(pow(centerPoint.x, 2.0) + pow(centerPoint.y, 2.0))
let sub1 = (centerPoint.y / a) * cos(angle / 2.0)
let sub2 = (centerPoint.x / a) * sin(angle / 2.0)
let deltaX = -2 * a * sin((0 - angle) / 2.0) * (sub1 + sub2)
let sub3 = (centerPoint.x / a) * cos(angle / 2.0)
let sub4 = (centerPoint.y / a) * sin(angle / 2.0)
let deltaY = 2 * a * sin((0 - angle) / 2.0) * (sub3 - sub4)
context.translateBy(x: deltaX, y: deltaY)
context.rotate(by: angle)
context.draw(mapImage!, in: mapRect)
}
}
class MapOverlay: NSObject, MKOverlay {
var coordinate: CLLocationCoordinate2D
var boundingMapRect: MKMapRect
var identifier: String?
init(identifier:String, coord: CLLocationCoordinate2D, rect: MKMapRect) {
self.coordinate = coord
self.boundingMapRect = rect
self.identifier = identifier
}
}
Call a timer to change alpha value of added overlays
var timer = Timer()
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(self.toggle), userInfo: nil, repeats: true)
Function to change alpha of overlay renderer
func changeAlphaValue(identifier: String) {
let overlays = self.mapView.overlays
let tag: String = identifier
for overlay in overlays {
if let overlay = overlay as? MapOverlay {
let identifier = overlay.identifier
if identifier == tag {
let renderer = mapView.renderer(for: overlay)
DispatchQueue.main.async{
renderer?.alpha = 1.0
}
}
else {
let renderer = mapView.renderer(for: overlay)
DispatchQueue.main.async{
renderer?.alpha = 0.0
}
}
}
}
}
This will give the following animation on map
The project is available on GitHub - link
https://github.com/jhurray/iOS7AnimatedMapOverlay
Above link are useful for you Or below code.
- (void)mapView:(MKMapView *)mapView didAddOverlayRenderers:(NSArray *)renderers
{
// Iterate through all overlayRenderers
for (MKOverlayRenderer *overlayRenderer in renderers)
{
// MKPolylineRenderer
// Animate a 'fade'
if ([overlayRenderer isKindOfClass:[MKPolylineRenderer class]])
{
// Get MKPolylineRenderer
MKPolylineRenderer *polylineRenderer = (MKPolylineRenderer *)overlayRenderer;
// Let's manually set alpha to 0
polylineRenderer.alpha = 0.0f;
// Animate it back to 1
dispatch_async(dispatch_get_main_queue(), ^{
[UIView animateWithDuration:0.4 animations:^{
polylineRenderer.alpha = 1.0f
}];
});
}
}
}
Thanks

MKOverlayRenderer stretches image

I would like to place an image in a map overlay which will scale along with the map. Using the code below, the image appears in the map view but it is stretched to fit the view. How can I keep the original aspect ratio of the image inside the overlay?
MapOverlay.swift
import UIKit
import MapKit
class MapOverlay: NSObject, MKOverlay {
var coordinate: CLLocationCoordinate2D
var boundingMapRect: MKMapRect
init(coord: CLLocationCoordinate2D, rect: MKMapRect) {
self.coordinate = coord
self.boundingMapRect = rect
}
}
MapOverlayView.swift
import UIKit
import MapKit
class MapOverlayView: MKOverlayRenderer {
var overlayImage: UIImage
init(overlay: MKOverlay, overlayImage:UIImage) {
self.overlayImage = overlayImage
super.init(overlay: overlay)
}
override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) {
let mapImage = overlayImage.cgImage
let mapRect = rect(for: overlay.boundingMapRect)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: 0.0, y: -mapRect.size.height)
context.draw(mapImage!, in: mapRect)
}
}
ViewController.swift
import UIKit
import MapKit
class ViewController: UIViewController {
#IBOutlet weak var mapview: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
mapview.delegate = self
let location = CLLocationCoordinate2D(latitude: 47.6062, longitude: -122.3320)
let span = MKCoordinateSpanMake(2.0, 2.0)
let region = MKCoordinateRegion(center: location, span: span)
mapview.setRegion(region, animated: true)
let rec = mapview.visibleMapRect
let overlay = MapOverlay(coord: location, rect: rec)
mapview.add(overlay)
}
}
extension ViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is MapOverlay {
let logo = UIImage(named: "swift")
let overlayView = MapOverlayView(overlay: overlay, overlayImage: logo)
return overlayView
} else {
return MKPolylineRenderer()
}
}
}
In your ViewController.swift, in viewDidLoad(),
let rec = mapview.visibleMapRect
Change this, rec should be exactly same size of the image used.
let location = //Give your location here in CLLocationCoordinate2D
//1. Show direction Using Overlays
let span = MKCoordinateSpanMake(1.0, 1.0)
let region = MKCoordinateRegion(center: location, span: span)
let mapRect: MKMapRect = helperClass.MKMapRectForCoordinateRegion(region: region)
let overlay = MapOverlay(identifier: title, coord: location, rect: mapRect)
mapView.add(overlay)
The function to get MKMapRect
func MKMapRectForCoordinateRegion(region:MKCoordinateRegion) -> MKMapRect {
let topLeft = CLLocationCoordinate2D(latitude: region.center.latitude + (region.span.latitudeDelta/2), longitude: region.center.longitude - (region.span.longitudeDelta/2))
let bottomRight = CLLocationCoordinate2D(latitude: region.center.latitude - (region.span.latitudeDelta/2), longitude: region.center.longitude + (region.span.longitudeDelta/2))
let a = MKMapPointForCoordinate(topLeft)
let b = MKMapPointForCoordinate(bottomRight)
return MKMapRect(origin: MKMapPoint(x:min(a.x,b.x), y:min(a.y,b.y)), size: MKMapSize(width: abs(a.x-b.x), height: abs(a.y-b.y)))
}
The sample project can be found in this link

my DataSource Method never called for some reason

im using small library called RSKImageCropView and it got Delegate and DataSource methods. The Delegate called as expecterd but the DataSource dont.
here is how i configure it:
let someImage = UIImage(data: blob)
let controller: RSKImageCropViewController = RSKImageCropViewController(image: someImage!, cropMode: RSKImageCropMode.Circle)
controller.delegate = self
controller.dataSource = self
self.navigationController?.pushViewController(controller, animated: true)
here is Delegate and DataSource methods:
extension GameViewController: RSKImageCropViewControllerDataSource {
func imageCropViewControllerCustomMaskRect(controller: RSKImageCropViewController) -> CGRect {
let rect = CGRect(x: 30, y: 30, width: 30, height: 30)
return rect
}
func imageCropViewControllerCustomMovementRect(controller: RSKImageCropViewController) -> CGRect {
return controller.maskRect
}
func imageCropViewControllerCustomMaskPath(controller: RSKImageCropViewController) -> UIBezierPath {
let rect = controller.maskRect;
let point1 = CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect));
let point2 = CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect));
let point3 = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
let triangle = UIBezierPath()
triangle.moveToPoint(point1)
triangle.addLineToPoint(point2)
triangle.addLineToPoint(point3)
triangle.closePath()
print("yay")
return triangle
}
}
extension GameViewController: RSKImageCropViewControllerDelegate {
func imageCropViewControllerDidCancelCrop(controller: RSKImageCropViewController) {
self.navigationController?.popViewControllerAnimated(true)
}
func imageCropViewController(controller: RSKImageCropViewController, didCropImage croppedImage: UIImage, usingCropRect cropRect: CGRect, rotationAngle: CGFloat) {
NSNotificationCenter.defaultCenter().postNotificationName("choseImage", object: croppedImage)
self.navigationController?.popViewControllerAnimated(true)
}
}
I believe you need to set the controller's cropMode to Custom for methods like imageCropViewControllerCustomMaskPath to be called, so try changing your instantiation to something like:
let controller: RSKImageCropViewController = RSKImageCropViewController(image: someImage!, cropMode: RSKImageCropMode.Custom)

Resources