Unable to move annotation with PDFKit - ios

By following this reference - https://developer.apple.com/documentation/pdfkit/pdfannotation/2869800-isreadonly
I am able to edit text of the annotation but i am unable to move the annotation over the pdf page.
Is there any other property by using which I will be able to move the annotations?
I added text annotation with following code:
func addText(x: Int, y: Int, width: Int, height: Int, text: String, fontSize: CGFloat, centerText: Bool){
guard let document = pdfView.document else { return }
let lastPage = document.page(at: document.pageCount - 1)
let annotation = PDFAnnotation(bounds: CGRect(x: x , y: y, width: width, height: height), forType: .widget, withProperties: nil)
annotation.widgetFieldType = .text
annotation.widgetStringValue = text
if (centerText){ annotation.alignment = .center }
annotation.font = UIFont(name: "calibri", size: fontSize)
annotation.fontColor = .black
annotation.color = .clear
annotation.backgroundColor = .clear
annotation.isReadOnly = false
lastPage?.addAnnotation(annotation)
}

Related

Detect clicked mutable attributed string

I have this code to write a paragraph like book with numbering for each sentence , the problem I'm facing is i can't find how to color one sentence when the user clicks in any word from it
import UIKit
let descender: CGFloat = UIFont.systemFont(ofSize: 25).descender
class ViewController: UIViewController , UITextViewDelegate, UIGestureRecognizerDelegate {
var all = [NSMutableAttributedString]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let style = NSMutableParagraphStyle()
style.alignment = NSTextAlignment.justified
style.baseWritingDirection = .rightToLeft
style.lineBreakMode = .byWordWrapping
let myAttribute = [ NSAttributedString.Key.font: UIFont.systemFont(ofSize: 25)] // ,
// NSAttributedString.Key.paragraphStyle: style ,
// NSAttributedString.Key.baselineOffset: NSNumber(value: 0)]
let textView = UITextView(frame:CGRect(x: 20, y: 100, width: UIScreen.main.bounds.width - 40 , height: UIScreen.main.bounds.height))
let attributedString = NSMutableAttributedString()
Array(1..<50).forEach {
let small = $0 % 2 == 0 ? " long text part one long text part one long text part one long text part one long text part one long text part one long text part one long text part one long text part one " : "long text part two long text part twolong text part twolong text part twolong text part twolong text part twolong text part twolong text part two "
let attributedString2 = NSMutableAttributedString(string: small,attributes: myAttribute)
attributedString.append(attributedString2)
let textAttachment11 = SubTextAttachment()
textAttachment11.image = generateImageWithText(text: "\($0)")
let attrStringWithImage11 = NSAttributedString(attachment: textAttachment11)
attributedString.append(attrStringWithImage11)
}
textView.attributedText = attributedString;
self.view.addSubview(textView)
textView.isEditable = false
textView.isSelectable = true
textView.delegate = self
let tap = UITapGestureRecognizer(target: self, action: #selector(self.textTapped(_:)))
tap.delegate = self
textView.isUserInteractionEnabled = true
textView.addGestureRecognizer(tap)
}
func generateImageWithText(text: String) -> UIImage? {
let image = UIImage(named: "qqq")!
print(text," ",image.size)
let imageView = UIImageView(image: image)
imageView.backgroundColor = UIColor.clear
imageView.frame = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
let label = UILabel(frame: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))
label.font = UIFont.systemFont(ofSize: 50)
label.backgroundColor = UIColor.clear
label.textAlignment = .center
label.textColor = UIColor.black
label.text = text
UIGraphicsBeginImageContextWithOptions(label.bounds.size, false, 0)
imageView.layer.render(in: UIGraphicsGetCurrentContext()!)
label.layer.render(in: UIGraphicsGetCurrentContext()!)
let imageWithText = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return imageWithText
}
#objc func textTapped(_ sender:UITapGestureRecognizer) {
}
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
return true
}
}
class SubTextAttachment:NSTextAttachment {
override func attachmentBounds(for textContainer: NSTextContainer?, proposedLineFragment lineFrag: CGRect, glyphPosition position: CGPoint, characterIndex charIndex: Int) -> CGRect {
let height = lineFrag.size.height
var scale: CGFloat = 1.0;
let imageSize = image!.size
if (height < imageSize.height) {
scale = height / imageSize.height
}
let value = CGRect(x: 0, y: descender, width: imageSize.width * scale, height: imageSize.height * scale)
return value
}
}
I know how to change the foreground color of any sub attributed string , but how i can know that the clicked part belong to the one to be colored ?
Also is there any better way to build this UI (in terms of performance ) as with tableView/CollectionView there is a dequeueing but here there isn't ?
So any hep is greatly appreciated
With NSAttributedString , you can use CoreText to render.
Convert NSAttributedString to CTFrame, then render it.
The key part
when you click a word in paragraph,
with override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
you can get a CGPoint
with that CGPoint & CTFrame, you can know the text range clicked in the text.
then rebuild the NSAttributedString 、CTFrame & rerender
here is the code you can refer
import UIKit
import CoreText
class TextRenderView: UIView {
let frameRef:CTFrame
let theSize: CGSize
let keyOne = //...
let keyTwo = //...
let rawTxt: String
let contentPage: NSAttributedString
let keyRanges: [Range<String.Index>]
override init(frame: CGRect){
rawTxt = //...
var tempRanges = [Range<String.Index>]()
if let rangeOne = rawTxt.range(of: keyOne){
tempRanges.append(rangeOne)
}
if let rangeTwo = rawTxt.range(of: keyTwo){
tempRanges.append(rangeTwo)
}
keyRanges = tempRanges
contentPage = NSAttributedString(string: rawTxt, attributes: [NSAttributedString.Key.font: UIFont.regular(ofSize: 15), NSAttributedString.Key.foregroundColor: UIColor.black])
let calculatedSize = contentPage.boundingRect(with: CGSize(width: UI.std.width - CGFloat(15 * 2), height: UI.std.height), options: [.usesFontLeading, .usesLineFragmentOrigin], context: nil).size
let padding: CGFloat = 10
theSize = CGSize(width: calculatedSize.width, height: calculatedSize.height + padding)
let framesetter = CTFramesetterCreateWithAttributedString(contentPage)
let path = CGPath(rect: CGRect(origin: CGPoint.zero, size: theSize), transform: nil)
frameRef = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, nil)
super.init(frame: frame)
backgroundColor = UIColor.white
}
required init?(coder: NSCoder) {
fatalError()
}
override func draw(_ rect: CGRect) {
guard let ctx = UIGraphicsGetCurrentContext() else{
return
}
ctx.textMatrix = CGAffineTransform.identity
ctx.translateBy(x: 0, y: bounds.size.height)
ctx.scaleBy(x: 1.0, y: -1.0)
CTFrameDraw(frameRef, ctx)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
guard let touch = touches.first else{
return
}
let pt = touch.location(in: self)
guard let offset = parserRect(with: pt, frame: frameRef), let pos = rawTxt.index(rawTxt.startIndex, offsetBy: offset, limitedBy: rawTxt.endIndex) else{
return
}
if keyRanges[0].contains(pos){
print(0)
}
else if keyRanges[1].contains(pos){
print(1)
}
}
func parserRect(with point: CGPoint, frame textFrame: CTFrame) -> Int?{
var result: Int? = nil
let path: CGPath = CTFrameGetPath(textFrame)
let bounds = path.boundingBox
guard let lines = CTFrameGetLines(textFrame) as? [CTLine] else{
return result
}
let lineCount = lines.count
guard lineCount > 0 else {
return result
}
var origins = [CGPoint](repeating: CGPoint.zero, count: lineCount)
CTFrameGetLineOrigins(frameRef, CFRangeMake(0, 0), &origins)
for i in 0..<lineCount{
let baselineOrigin = origins[i]
let line = lines[i]
var ascent: CGFloat = 0
var descent: CGFloat = 0
var linegap: CGFloat = 0
let lineWidth = CTLineGetTypographicBounds(line, &ascent, &descent, &linegap)
let lineFrame = CGRect(x: baselineOrigin.x, y: bounds.height-baselineOrigin.y-ascent, width: CGFloat(lineWidth), height: ascent+descent+linegap + 10)
if lineFrame.contains(point){
result = CTLineGetStringIndexForPosition(line, point)
break
}
}
return result
}
}
helper method:
extension String {
func range(ns inner: String) -> NSRange{
return (self as NSString).range(of: inner)
}
}
here is the github code you can refer

Get PDF from a UILabel text mask

I'm trying to get PDF from UIView with UILabel text mask.
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 200 ))
label.text = "Label Text"
label.font = UIFont.systemFont(ofSize: 25)
label.textAlignment = .center
label.textColor = UIColor.white
let overlayView = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 200 ))
overlayView.image = UIImage(named: "erasemask.png")
label.mask = overlayView
view_process.addSubview(label)
}
func exportAsPdfFromView(){
let pdfPageFrame = CGRect(x: 0, y: 0, width: view_process.bounds.size.width, height: view_process.bounds.size.height)
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, pdfPageFrame, nil)
UIGraphicsBeginPDFPageWithInfo(pdfPageFrame, nil)
guard let pdfContext = UIGraphicsGetCurrentContext() else { return "" }
view_process.layer.render(in: pdfContext)
UIGraphicsEndPDFContext()
let path = self.saveViewPdf(data: pdfData)
print(path)
}
func saveViewPdf(data: NSMutableData) -> String {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let docDirectoryPath = paths[0]
let pdfPath = docDirectoryPath.appendingPathComponent("viewPdf.pdf")
if data.write(to: pdfPath, atomically: true) {
return pdfPath.path
} else {
return ""
}
}
but I do not get PDF with mask. I don't want to convert UIView to UImage and then convert UImage to PDF. I want to editable PDF so don't want to convert into UIImage.
Can anyone help me How to convert Masked UILabel to PDF ?
here erasemask.png
You have to draw text yourself in the pdf to make it editable.
I have slightly modified your code, and it works. You can simply copy/paste in a new app view controller to check the result.
Here is a screenshot of the document opened in AffinityDesigner. The mask is correctly applied as a layer mask, and the text is editable.
It needs some tweaking to respect exact layout.
import UIKit
class ViewController: UIViewController {
var labelFrame: CGRect { return CGRect(x: 0, y: 0, width: 200, height: 200 )}
lazy var label: UILabel = {
let label = UILabel(frame: labelFrame)
label.text = "Label Text"
label.font = UIFont.systemFont(ofSize: 25)
label.textAlignment = .center
label.textColor = UIColor.black
return label
}()
var maskImage = UIImage(named: "erasemask.png")
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(label)
let overlayView = UIImageView(frame: labelFrame)
overlayView.image = maskImage
label.mask = overlayView
exportAsPdfFromView()
}
func exportAsPdfFromView() {
let pdfPageFrame = CGRect(x: 0, y: 0, width: view.bounds.size.width, height: view.bounds.size.height)
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, pdfPageFrame, nil)
UIGraphicsBeginPDFPageWithInfo(pdfPageFrame, nil)
guard let context = UIGraphicsGetCurrentContext() else { return }
// Clip context
if let overlayImage = maskImage?.cgImage {
context.clip(to: labelFrame, mask: overlayImage)
}
// Draw String
let string: String = label.text ?? ""
let attributes: [NSAttributedString.Key: Any] = [
NSAttributedString.Key.foregroundColor: label.textColor ?? .black,
NSAttributedString.Key.font: label.font ?? UIFont.systemFont(ofSize: 25)
]
let stringRectSize = string.size(withAttributes: attributes)
let x = (labelFrame.width - stringRectSize.width) / 2
let y = (labelFrame.height - stringRectSize.height) / 2
let stringRect = CGRect(origin: CGPoint(x: x, y: y), size: stringRectSize)
string.draw(in: stringRect, withAttributes: attributes)
UIGraphicsEndPDFContext()
saveViewPdf(data: pdfData)
}
func saveViewPdf(data: NSMutableData) {
guard let docDirectoryPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let pdfPath = docDirectoryPath.appendingPathComponent("viewPdf.pdf")
print(pdfPath)
data.write(to: pdfPath, atomically: true)
}
}
EDITED
As a comparison, I have added the result by using the Apple view.render function, using code from the question ( with a blue background so we can see white text ). It clearly shows that this function does not support editable text and masking.
It exports the document as a stack of flat images and extra - useless - groups.
So I guess the only solution to keep pdf entities types is to compose the document by rendering each object.
If you need to export complex forms, it must not be too hard to make a helper that crawls in the view hierarchy and render each object content.
If you need to also render pdf with interactive buttons, you will probably need to use Annotations( available in PDFKit ). By the way you can also create editable fields with annotations, but I'm not sure it supports masking.
LAST EDIT:
Since you use an external framework to render pdf ( PDFGenerator ), it is different. You can only override the view.layer.render function that is used by the framework and use the context.clip function.
To export ui components as editable, I think they must have no superview. As soon as there is a view hierarchy, a backing bitmap is created, and all render calls are made with this bitmap as parameter, and not the PDFContext that was used to call the first render.
That's how PDFGenerator works, it crawls in the view hierarchy an render views by removing them from superview, render to the context, and move them back in hierarchy.
The big drawback of this is that it leads to bugs when the view is moved back in the hierarchy. Probably because constraints are lost, or some views like UIStackView behave differently. There is numerous opened issues around this on their github.
What I don't really understand yet is why the label below is rendered as editable even in a view hierarchy. I guess it's due to how the render function is done by Apple. Can't go further on this right now..
Custom Field Example:
It may need more work to respect exact field layout, but this is a basis for a view that is rendered as editable text. It gives the exact same result as in the first part of the answer.
It works with any mask view, not only UIImage.
It is rendered as editable even in a hierarchy.
So if you use this label instead of UILabel in the initial code of this question, it works.
// Extension to get mask view as an image
extension UIView {
var cgImage: CGImage? {
UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, contentScaleFactor);
guard let context = UIGraphicsGetCurrentContext() else { return nil }
layer.render(in: context)
let image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image?.cgImage
}
}
class PMLayer: CALayer {
// Needed to access text style
weak var label: UILabel?
override init() { super.init() }
required init?(coder: NSCoder) { super.init(coder: coder) }
override init(layer: Any) {
super.init(layer: layer)
if let pm = layer as? PMLayer {
label = pm.label
}
}
override func render(in ctx: CGContext) {
guard let label = label else { return }
// Clip context
if let mask = label.mask?.cgImage {
ctx.clip(to: label.frame, mask: mask)
}
// Draw String
let string: String = label.text ?? ""
let attributes: [NSAttributedString.Key: Any] = [
NSAttributedString.Key.foregroundColor: label.textColor ?? .black,
NSAttributedString.Key.font: label.font ?? UIFont.systemFont(ofSize: 25)
]
let stringRectSize = string.size(withAttributes: attributes)
let x = (label.frame.width - stringRectSize.width) / 2
let y = (label.frame.height - stringRectSize.height) / 2
let stringRect = CGRect(origin: CGPoint(x: x, y: y), size: stringRectSize)
string.draw(in: stringRect, withAttributes: attributes)
}
}
class PMLabel: UILabel {
override class var layerClass: AnyClass { return PMLayer.self }
// Be sure the label is correctly linked to the layer when we access it
override var layer: CALayer {
(super.layer as? PMLayer)?.label = self
return super.layer
}
}
this is my decision
import UIKit
import PDFKit
class ViewController: UIViewController {
#IBOutlet weak var view_process: UIView!
#IBOutlet weak var pdf_view: UIView!
let documentInteractionController = UIDocumentInteractionController()
#IBAction func bClick(_ sender: Any) {
exportAsPdfFromView()
}
override func viewDidLoad() {
super.viewDidLoad()
documentInteractionController.delegate = self
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 200 ))
label.text = "Label Text"
label.font = UIFont.systemFont(ofSize: 25)
label.textAlignment = .center
label.textColor = UIColor.blue
let overlayView = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 200 ))
overlayView.image = UIImage(named: "erasemask.png")
label.mask = overlayView
view_process.addSubview(label)
}
func exportAsPdfFromView(){
let pdfPageFrame = view_process.bounds//.CGRect(x: 0, y: 0, width: view_process.bounds.size.width, height: view_process.bounds.size.height)
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, pdfPageFrame, nil)
UIGraphicsBeginPDFPageWithInfo(pdfPageFrame, nil)
let pdfContext = UIGraphicsGetCurrentContext()!
view_process.layer.render(in: pdfContext)
UIGraphicsEndPDFContext()
let path = self.saveViewPdf(data: pdfData)
print(path)
self.share(url: URL(string: "file://\(path)")!)
}
func saveViewPdf(data: NSMutableData) -> String {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let docDirectoryPath = paths[0]
let pdfPath = docDirectoryPath.appendingPathComponent("viewPdf.pdf")
if data.write(to: pdfPath, atomically: true) {
return pdfPath.path
} else {
return ""
}
}
func openPdf(path: String) {
let pdfView = PDFView(frame: self.view.bounds)
pdfView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
pdf_view.addSubview(pdfView)
// Fit content in PDFView.
pdfView.autoScales = true
// Load Sample.pdf file from app bundle.
let fileURL = URL(string: path)
pdfView.document = PDFDocument(url: fileURL!)
}
func share(url: URL) {
documentInteractionController.url = url
// documentInteractionController.uti = url.typeIdentifier ?? "public.data, public.content"
// documentInteractionController.name = url.localizedName ?? url.lastPathComponent
documentInteractionController.presentPreview(animated: true)
}
}
extension ViewController: UIDocumentInteractionControllerDelegate {
func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
guard let navVC = self.navigationController else {
return self
}
return navVC
}
}

Resize font along with frame of label using pinch gesture on UILabel?

Increase or decrease font size smoothly whenever user resize label using pinch gesture on it.
Note
Without compromising quality of font
Not only transforming the scale of UILabel
With support of multiline text
Rotation gesture should work proper with pinch gesture
Reference: SnapChat or Instagram Text Editor tool
extension String {
func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font: UIFont(name: font.fontName, size: font.pointSize)!], context: nil)
return ceil(boundingBox.height)
}
func width(withConstrainedHeight height: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font: UIFont(name: font.fontName, size: font.pointSize)!], context: nil)
return ceil(boundingBox.width)
}
}
func resizeLabelToText(textLabel : UILabel)
{
let labelFont = textLabel.font
let labelString = textLabel.text
let labelWidth : CGFloat = labelString!.width(withConstrainedHeight: textLabel.frame.size.height, font: labelFont!)
let labelHeight : CGFloat = labelString!.height(withConstrainedWidth: labelWidth, font: labelFont!)
textLabel.frame = CGRect(x: textLabel.frame.origin.x, y: textLabel.frame.origin.y, width: labelWidth, height: labelHeight)
textLabel.font = labelFont
}
func pinchedRecognize(_ pinchGesture: UIPinchGestureRecognizer) {
guard pinchGesture.view != nil else {return}
if (pinchGesture.view is UILabel) {
let selectedTextLabel = pinchGesture.view as! UILabel
if pinchGesture.state == .began || pinchGesture.state == .changed {
let pinchScale = round(pinchGesture.scale * 1000) / 1000.0
if (pinchScale < 1) {
selectedTextLabel.font = selectedTextLabel.font.withSize(selectedTextLabel.font.pointSize - pinchScale)
}
else {
selectedTextLabel.font = selectedTextLabel.font.withSize(selectedTextLabel.font.pointSize + pinchScale)
}
resizeLabelToText(textLabel: selectedTextLabel)
}
}
}
I solved the problem with following code which is working fine with every aspect which are mentioned in question, similar to Snapchat and Instagram:
var pointSize: CGFloat = 0
#objc func pinchRecoginze(_ pinchGesture: UIPinchGestureRecognizer) {
guard pinchGesture.view != nil else {return}
let view = pinchGesture.view!
if (pinchGesture.view is UILabel) {
let textLabel = view as! UILabel
if pinchGesture.state == .began {
let font = textLabel.font
pointSize = font!.pointSize
pinchGesture.scale = textLabel.font!.pointSize * 0.1
}
if 1 <= pinchGesture.scale && pinchGesture.scale <= 10 {
textLabel.font = UIFont(name: textLabel.font!.fontName, size: pinchGesture.scale * 10)
resizeLabelToText(textLabel: textLabel)
}
}
}
func resizeLabelToText(textLabel : UILabel) {
let labelSize = textLabel.intrinsicContentSize
textLabel.bounds.size = labelSize
}
Call following method every time after UILabel size changes.
func labelSizeHasBeenChangedAfterPinch(_ label:UILabel, currentSize:CGSize){
let MAX = 25
let MIN = 8
let RATE = -1
for proposedFontSize in stride(from: MAX, to: MIN, by: RATE){
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
let attribute = [NSAttributedString.Key.font:UIFont.systemFont(ofSize: CGFloat(proposedFontSize))]
// let context = IF NEEDED ...
let rect = NSString(string: label.text ?? "").boundingRect(with: currentSize, options: options, attributes: attribute, context: nil)
let labelSizeThatFitProposedFontSize = CGSize(width: rect.width , height: rect.height)
if (currentSize.height > labelSizeThatFitProposedFontSize.height) && (currentSize.width > labelSizeThatFitProposedFontSize.width){
DispatchQueue.main.async {
label.font = UIFont.systemFont(ofSize: CGFloat(proposedFontSize))
}
break
}
}
}
you can try:
1 - Set maximum font size for this label
2 - Set line break to Truncate Tail
3 - Set Autoshrink to Minimum font size (minimum size)

Google Maps SDK for iOS - Cluster Manager don't show marker title on marker tap

I'm using Google Maps SDK for iOS with a custom render function for my icons. Unfortunately when I tap into the markers, no infoWindow is shown. Here's my code:
// MyViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: self.mapView, clusterIconGenerator: iconGenerator)
renderer.delegate = self
self.clusterManager = GMUClusterManager(map: self.mapView, algorithm: algorithm, renderer: renderer)
for (index, marker) in markers.enumerated() {
let markerItem = POIItem(position: marker.position, name: "Marker \(index)")
self.clusterManager.add(markerItem)
}
self.mapViewContainer.addSubview(self.mapView)
clusterManager.cluster()
}
func renderer(_ renderer: GMUClusterRenderer, willRenderMarker marker: GMSMarker) {
let pinImageView = UIImageView(image: #imageLiteral(resourceName: "pin"));
let iconView = UIView(frame: CGRect(x: 0, y: 0, width: pinImageView.frame.width, height: pinImageView.frame.height))
iconView.clipsToBounds = false
iconView.backgroundColor = .clear
iconView.addSubview(pinImageView)
iconView.layoutIfNeeded()
if let markerData = marker.userData {
if let markerClusterData = (markerData as? GMUCluster) {
let totalLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 25, height: 25))
let labelContainerView = UIView(frame: CGRect(x: 20, y: -10, width: 25, height: 25))
labelContainerView.backgroundColor = #colorLiteral(red: 0.9803921569, green: 0.7058823529, blue: 0.1176470588, alpha: 1)
totalLabel.font = totalLabel.font.withSize(12.0)
totalLabel.text = "\(markerClusterData.count)+"
totalLabel.textAlignment = .center
totalLabel.textColor = .white
labelContainerView.addSubview(totalLabel)
labelContainerView.layer.cornerRadius = 12.5
labelContainerView.layoutIfNeeded()
iconView.addSubview(labelContainerView)
iconView.bounds = iconView.frame.union(labelContainerView.frame)
}
}
marker.iconView = iconView
}
// Default POIITem implementation, exactly like Google teaches.
class POIItem: NSObject, GMUClusterItem {
var position: CLLocationCoordinate2D
var name: String!
init(position: CLLocationCoordinate2D, name: String) {
self.position = position
self.name = name
}
}
No infoWindow is shown, with or without the custom delegation method.
The marker needs a snippet to show an infowindow
Otherwise, you can create a custom infowindow in the corresponding callback

Annotation(Image & Text) subviews changes position after saving

I have created a PDF based application. Firstly, I get the PDF from document directory then convert it to Images and then programmatically set all images to vertically by using UIImageView & ScrollView.
Then I set different type of annotation like sign, text etc. When I finalised it to images and convert them to PDF with good result. I get different results as shown in below images.
Screen 1: convert original image size to UIImageView Size with aspect to fit ( because i have look like this type of screen) when i have convert original size to screen size at that time i have stored image original size and its used to when i have convert images to pdf after add annotation looking in screen 2.
Screen 2. when i have finalise or convert to pdf. result look in screen 2, change annotation or sign position.
let drawImage = jotViewController.renderImage() //sign image
signImg = drawImage
gestureView = UIView(frame: CGRect(x: 50, y: 300, width: 200, height: 110))
counter = counter + 1
gestureView.tag = counter
annotationImg = UIImageView(frame: CGRect(x: 0, y: 0, width:170, height: 80))
annotationImg.image = signImg
annotationImg.layer.cornerRadius = 5.0
// gestureView.frame = annotationImg.frame
cancelBtn.frame = CGRect(x: (annotationImg.frame.minX-10), y: (annotationImg.frame.maxY-10), width: 20, height: 20)
cancelBtn.setImage(#imageLiteral(resourceName: "cancel.png"), for: .normal)
cancelBtn.addTarget(self, action: #selector(cancelBtnAction), for: .touchUpInside)
gestureView.addSubview(cancelBtn)
zoomBtn.frame = CGRect(x: (annotationImg.frame.width-10), y: (annotationImg.frame.height-10), width: 20, height: 20)
zoomBtn.setImage(#imageLiteral(resourceName: "zoom.png"), for: .normal)
gestureView.addSubview(zoomBtn)
let zoomBtnpanGesture: UIPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(ViewController.zoomBtnPanGesture(_:)))
zoomBtnpanGesture.minimumNumberOfTouches = 1
zoomBtnpanGesture.maximumNumberOfTouches = 1
zoomBtn.addGestureRecognizer(zoomBtnpanGesture)
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.handlePanGesture))
panGesture.minimumNumberOfTouches = 1
panGesture.maximumNumberOfTouches = 1
annotationImg.backgroundColor = UIColor.lightGray.withAlphaComponent(0.5)
gestureView.addGestureRecognizer(panGesture)
annotationImg.clipsToBounds = true
gestureView.addSubview(annotationImg)
for getUIImageView in pdfUIImageViewArr {
if tag==0 {
let alert = UIAlertController(title: "Alert!!", message: "Please Select Signature Page.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
present(alert, animated: true, completion: nil)
}
if getUIImageView.tag == tag && tag != 0 {
getUIImageView.addSubview(gestureView)
}
}
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(gestureViewTapped(sender:)))
tapGesture.numberOfTapsRequired = 1
gestureView.addGestureRecognizer(tapGesture)
imageViewArr.append(annotationImg)
gestureViewArr.append(gestureView)
UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")
#IBAction func storeAsPdfAction(_ sender: AnyObject) {
self.storeToDocumentDir()
let userObj = self.storyboard?.instantiateViewController(withIdentifier: "DocumentVC") as? DocumentVC
self.navigationController?.pushViewController(userObj!, animated: true)
}
func storeToDocumentDir(){
print(pdfImage.frame)
print(view.frame)
print(gestureView.frame)
for getTextViewLbl in textViewLabelArr{
getTextViewLbl.backgroundColor = UIColor.clear
}
gestureViewArr.removeFirst()
for gestureView in gestureViewArr {
print(gestureView.frame)
zoomBtn.isHidden = true
cancelBtn.isHidden = true
// pdfImage.addSubview(gestureView)
}
pdfUIImageViewArr.remove(at: 0)
var i = 1
for getPDFImage in pdfUIImageViewArr {
getPDFImage.frame = CGRect(x: 0, y: 0, width: pdfImage.frame.size.width, height: pdfImage.frame.size.height)
getPDFImage.frame = CGRect(x: 0, y: 0, width: pageRect.size.width, height: pageRect.size.height)
getPDFImage.contentMode = UIViewContentMode.scaleToFill
//
let getImage = self.imageWithView(getPDFImage)
let fileManager = FileManager.default
let paths = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("img\(i).jpg")
i += 1
let image = getImage
print(paths)
let imageData = UIImageJPEGRepresentation(image, 0.5)
fileManager.createFile(atPath: paths as String, contents: imageData, attributes: nil)
setPdfImgArr.append(getImage)
}
self.createPdfFromView(pdfImage, imageArrAdd: setPdfImgArr, saveToDocumentsWithFileName: "\((setFileName)!)")
let pdfUrl = URL(fileURLWithPath:documentsFileName)
self.setImagesInScrollView(pdfUrl)
}
Problem Solved :
By Using UIScrollView Zoom:
override func viewWillLayoutSubviews() {
self.configureZoomScale()
}
func configureZoomScale() {
var xZoomScale: CGFloat = self.scrollView.bounds.size.width / self.pdfImage.bounds.size.width
var yZoomScale: CGFloat = self.scrollView.bounds.size.height / self.pdfImage.bounds.size.height
var minZoomScale: CGFloat = min(xZoomScale, yZoomScale)
//set minimumZoomScale to the minimum zoom scale we calculated
//this mean that the image cant me smaller than full screen
self.scrollView.minimumZoomScale = minZoomScale
//allow up to 4x zoom
self.scrollView.maximumZoomScale = 4
//set the starting zoom scale
self.scrollView.zoomScale = minZoomScale
}
func viewForZooming(in scrollView: UIScrollView) -> UIView?{
return pdfFinalImage
}

Resources