How to write latitude and longitude on image in Swift 3.0? - swift3

I followed below code and it is not at all showing the image. what would be the wrong here? I was trying to debug newimage shows nil.
I was unable to resolve it.
func textOnImage(text: NSString, atPoint: CGPoint,capturedImage:UIImage?) -> UIImage? {
if let capturedImage = capturedImage {
// Setup the font specific variables
let textColor = UIColor.white
let textFont = UIFont(name: "Helvetica", size: 14)!
// Setup the image context using the passed image
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(capturedImage.size, false, scale)
// Setup the font attributes that will be later used to dictate how the text should be drawn
let textFontAttributes = [
NSFontAttributeName: textFont,
NSForegroundColorAttributeName: textColor,
] as [String : Any]
// Create a point within the space that is as bit as the image
let rect = CGRect(x: atPoint.x, y: atPoint.y, width: capturedImage.size.width, height: capturedImage.size.height)
// Draw the text into an image
text.draw(in: rect, withAttributes: textFontAttributes)
// Create a new image out of the images we have created
let newImage = UIGraphicsGetImageFromCurrentImageContext()
// End the context now that we have the image we need
UIGraphicsEndImageContext()
//Pass the image back up to the caller
return newImage!
}
return nil
}

Include below line
capturedImage.draw(in: CGRect(x: 0, y: 0, width: capturedImage.size.width, height:capturedImage.size.height))
so your method looks like
func textOnImage(text: NSString, atPoint: CGPoint,capturedImage:UIImage?) -> UIImage?{
if let capturedImage = capturedImage{
// Setup the font specific variables
let textColor = UIColor.white
let textFont = UIFont(name: "Helvetica", size: 14)!
// Setup the image context using the passed image
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(capturedImage.size, false, scale)
// Setup the font attributes that will be later used to dictate how the text should be drawn
let textFontAttributes = [
NSFontAttributeName: textFont,
NSForegroundColorAttributeName: textColor,
] as [String : Any]
capturedImage.draw(in: CGRect(x: 0, y: 0, width: capturedImage.size.width, height:capturedImage.size.height))
// Create a point within the space that is as bit as the image
let rect = CGRect(x: atPoint.x, y: atPoint.y, width: capturedImage.size.width, height: capturedImage.size.height)
// Draw the text into an image
text.draw(in: rect, withAttributes: textFontAttributes)
// Create a new image out of the images we have created
let newImage = UIGraphicsGetImageFromCurrentImageContext()
// End the context now that we have the image we need
UIGraphicsEndImageContext()
//Pass the image back up to the caller
return newImage!
}
return nil
}
}

Related

How do I add my parameter to the function where the property initializers run before 'self'?

Error: Cannot use instance member 'makeRandomImage' within property initializer; property initializers run before 'self' is available
I am trying to create "cgImage" with "makeRandomImage(width: 1024, height: 1024)!" but for some reason there is an error. I am converting a ciImage to a uiImage for my image classifier, but it won't let me add it inside the function parameters.
import SwiftUI
import Accelerate
class ImageClassifier: ObservableObject {
#Published private var classifier = Classifier()
var imageClass: String? {
classifier.results
}
func makeRandomImage(width: Int, height: Int) -> CGImage? {
let imageFormat = vImage_CGImageFormat(
bitsPerComponent: 8,
bitsPerPixel: 8 * 3,
colorSpace: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue))!
let arrayDescriptor = BNNSNDArrayDescriptor.allocate(
randomIn: Pixel_8(0) ... 255,
shape: .matrixRowMajor(width * imageFormat.componentCount,
height))
let imageBuffer = vImage_Buffer(data: arrayDescriptor.data,
height: vImagePixelCount(height),
width: vImagePixelCount(width),
rowBytes: width)
return try? imageBuffer.createCGImage(format: imageFormat)
}
let cgImage = makeRandomImage(width: 1024,
height: 1024)!
let uiImage = UIImage(cgImage: cgImage)
// MARK: Intent(s)
func detect(uiImage: uiImage) {
guard let ciImage = CIImage (image: uiImage) else { return }
classifier.detect(ciImage: ciImage)
}
}
As the error suggests, you can't call functions on self during the initialization stage of a struct or class.
Luckily, since your makeRandomImage function doesn't make use of any other properties from the struct, it could easily be made static, which can be accessed during init.
Secondly, you'll run into the same issue with your uiImage variable, which you can make into a computed property:
static func makeRandomImage(width: Int, height: Int) -> CGImage? {
let imageFormat = vImage_CGImageFormat(
bitsPerComponent: 8,
bitsPerPixel: 8 * 3,
colorSpace: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue))!
let arrayDescriptor = BNNSNDArrayDescriptor.allocate(
randomIn: Pixel_8(0) ... 255,
shape: .matrixRowMajor(width * imageFormat.componentCount,
height))
let imageBuffer = vImage_Buffer(data: arrayDescriptor.data,
height: vImagePixelCount(height),
width: vImagePixelCount(width),
rowBytes: width)
return try? imageBuffer.createCGImage(format: imageFormat)
}
let cgImage = Self.makeRandomImage(width: 1024,
height: 1024)!
var uiImage: UIImage { UIImage(cgImage: cgImage) }
All that being said, this is a pretty expensive thing to run on a SwiftUI View init, which should be a very inexpensive operation.
You should consider running this in something like onAppear or task instead.

how to apply the func for textfield underline code to all text fields

I want to reuse the code for all the text field in my screen there is the function i have written for a single textfield out of all how do I apply it to all the text field how to customise it so that only single code will work for all: code is Here:
func textfieldborder()
{
let border = CALayer()
let width = CGFloat(1.0)
border.borderColor = UIColor.darkGray.cgColor
border.frame = CGRect(x: CGFloat(0), y: textname.frame.size.height - width, width: textname.frame.size.width, height: textname.frame.size.height)
border.borderWidth = width
textname.layer.addSublayer(border)
textname.layer.masksToBounds = true
}
You should make your function accept a parameter of type UITextField.
func textfieldborder(_ textField: UITextField) {
let border = CALayer()
let width = CGFloat(1.0)
border.borderColor = UIColor.darkGray.cgColor
border.frame = CGRect(x: CGFloat(0), y: textField.frame.size.height - width, width: textField.frame.size.width, height: textField.frame.size.height)
border.borderWidth = width
textField.layer.addSublayer(border) // note that you are setting properties of textField the parameter now!
textField.layer.masksToBounds = true
}
You can call this method on all the text fields you want a border on:
textfieldBorder(textname)
textfieldBorder(textFoo)
textfieldBorder(textBar)

Draw a rolling line in a UIView

I have the following view in IB (Using Swift 3)
The green UIImageView is nested inside a UIView.
When I press begin, I'd ike to draw a line, indication the sound level currently beeing recorded.
I'm very new to Core Graphics, and have found the following code in SO that explains how to draw on a UYIImageView.
func drawLine(from fromPoint: CGPoint, to toPoint: CGPoint) {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, 0)
plotArea.image?.draw(in: view.bounds)
let context = UIGraphicsGetCurrentContext()
context?.move(to: fromPoint)
context?.addLine(to: toPoint)
context?.setLineCap(CGLineCap.round)
context?.setLineWidth(brushWidth)
context?.setStrokeColor(red: red, green: green, blue: blue, alpha: 1.0)
context?.setBlendMode(CGBlendMode.normal)
context?.strokePath()
plotArea.image = UIGraphicsGetImageFromCurrentImageContext()
plotArea.alpha = opacity
UIGraphicsEndImageContext()
}
This draws a nice line, when called, inside the green UIImageView (called plotArea). What I want to happen is that the line draws on top of the UIView (called rangeView) (indicating to the user, that when the line is over the green imageview, he's at the correct level)
Can anyone point me towards refactoring my drawLine function to draw on the UIView in stead of the UIImageView
When I solve that, I will also need for the line to be drawn continuously - meaning that when it reaches 2/3 of the view, it should continue drawing at that fixed x coordinate, and disappear to the left (like a rolling line)
Right now I'm calling the drawLine func every 0.1 seconds with this function:
func animatePin() {
let viewHeight = self.rangeView.frame.height
let ypos = viewHeight/maxDb*CGFloat(self.calculateLevel())
let p = CGPoint(x:self.lastPoint.x+10, y: ypos)
self.drawLine(from: self.lastPoint, to: p)
self.lastPoint = p
}
EDIT: Solved the first part, by calling this method in stead:
func drawLineFromPoint(start : CGPoint, toPoint end:CGPoint, ofColor lineColor: UIColor, inView view:UIView) {
let path = UIBezierPath()
path.move(to: start)
path.addLine(to: end)
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = lineColor.cgColor
shapeLayer.lineWidth = 1.0
view.layer.addSublayer(shapeLayer)
}
EDIT2 - Managed to get the line "rolling" by refactoring the function to
func drawLineFromPoint(start : CGPoint, toPoint end:CGPoint, ofColor lineColor: UIColor, inView view:UIView) {
let path = UIBezierPath()
path.move(to: start)
path.addLine(to: end)
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = lineColor.cgColor
shapeLayer.lineWidth = 1.0
if(end.x > view.frame.width*0.95){
let newRect = CGRect(x: view.frame.origin.x-10, y: view.frame.origin.y, width: view.frame.width+10, height: view.frame.height)
view.frame = newRect
}
if(start != CGPoint.zero){
view.layer.addSublayer(shapeLayer)
}
}
Function to draw the line:
func drawLineFromPoint(start : CGPoint, toPoint end:CGPoint, ofColor lineColor: UIColor, inView view:UIView) {
//create a path
let path = UIBezierPath()
path.move(to: start)
path.addLine(to: end)
//create a shape, and add the path to it
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = lineColor.cgColor
shapeLayer.lineWidth = 1.0
//if we are at the end of the view, move the view left by 10, and add the 10 to the right, making it roll
if(end.x > view.frame.width*0.95){
let newRect = CGRect(x: view.frame.origin.x-10, y: view.frame.origin.y, width: view.frame.width+10, height: view.frame.height)
view.frame = newRect
}
//wait till there iss data to show, so we don't get a huge spike from 0.0
if(start != CGPoint.zero){
view.layer.addSublayer(shapeLayer)
}
}

draw animated circle in swift 3

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

Spacing around NSTextAttachment

How do I make spacing around NSTextAttachments like in the example below?
In the example No spacing is the default behaviour I get when I append a NSTextAttachment to a NSAttributedString.
This worked for me in Swift
public extension NSMutableAttributedString {
func appendSpacing( points : Float ){
// zeroWidthSpace is 200B
let spacing = NSAttributedString(string: "\u{200B}", attributes:[ NSAttributedString.Key.kern: points])
append(spacing)
}
}
The above answers no longer worked for me under iOS 15. So I ended up creating and adding an empty "padding" attachment in between the image attachment and attributed text
let padding = NSTextAttachment()
//Use a height of 0 and width of the padding you want
padding.bounds = CGRect(width: 5, height: 0)
let attachment = NSTextAttachment(image: image)
let attachString = NSAttributedString(attachment: attachment)
//Insert the padding at the front
myAttributedString.insert(NSAttributedString(attachment: padding), at: 0)
//Insert the image before the padding
myAttributedString.insert(attachString, at: 0)
NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] init];
// append NSTextAttachments instance to attributedText...
// then add non-printable string with NSKernAttributeName attributes
unichar c[] = { NSAttachmentCharacter };
NSString *nonprintableString = [NSString stringWithCharacters:c length:1];
NSAttributedString *spacing = [[NSAttributedString alloc] initWithString:nonprintableString attributes:#{
NSKernAttributeName : #(4) // spacing in points
}];
[attributedText appendAttributedString:spacing];
// finally add other text...
Add spacing to image(For iOS 15).
extension UIImage {
func imageWithSpacing(insets: UIEdgeInsets) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(
CGSize(width: self.size.width + insets.left + insets.right,
height: self.size.height + insets.top + insets.bottom), false, self.scale)
UIGraphicsGetCurrentContext()
let origin = CGPoint(x: insets.left, y: insets.top)
self.draw(at: origin)
let imageWithInsets = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return imageWithInsets
}
}
This extension makes it easy to add the space you need. It also works on iOS 16.
extension NSMutableAttributedString {
func appendSpace(_ width: Double) {
let space = NSTextAttachment()
space.bounds = CGRect(x:0, y: 0, width: width, height: 0)
append(NSAttributedString(attachment: space))
}
}
// usages
let example = NSMutableAttributedString()
example.appendSpace(42)
...