I'm currently making a camera app and I can add single text from the text field on picture.
What I want is, I want to put multiple different text to the picture. So I add 4 text field to the storyboard and now I need to change something on view controller. I tried something but couldn't solve.
I have tried with this code but it's not working.
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
ImageDisplay.image = textToImage(CustomerTextBox.text!, inImage: image, atPoint: CGPoint( x: 400, y: 100)
ImageDisplay.image = textToImage(ResponsibleTextBox.text!, inImage: image, atPoint: CGPoint( x: 400, y: 200)
ImageDisplay.image = textToImage(LocationTextBox.text!, inImage: image, atPoint: CGPoint( x: 400, y: 300)
ImageDisplay.image = textToImage(DescriptionTextBox.text!, inImage: image, atPoint: CGPoint( x: 400, y: 300)
}
dismissViewControllerAnimated(true, completion: nil)
This is my screen:
and This is my working code with single textbox:
import UIKit
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var PhotoLibrary: UIButton!
#IBOutlet weak var Camera: UIButton!
#IBOutlet weak var ImageDisplay: UIImageView!
#IBOutlet weak var CustomerTextBox: UITextField!
#IBOutlet weak var ResponsibleTextBox: UITextField!
#IBOutlet weak var LocationTextBox: UITextField!
#IBOutlet weak var DescriptionTextBox: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func PhotoLibraryAction(sender: UIButton) {
let picker = UIImagePickerController()
picker.delegate = self
picker.sourceType = .PhotoLibrary
presentViewController(picker, animated: true, completion: nil)
}
#IBAction func CameraAction(sender: UIButton) {
let picker = UIImagePickerController()
picker.delegate = self
picker.sourceType = .Camera
presentViewController(picker, animated: true, completion: nil)
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
ImageDisplay.image = textToImage(CustomerTextBox.text!, inImage: image, atPoint: CGPoint( x: 400, y: 100))
}
dismissViewControllerAnimated(true, completion: nil)
}
func textToImage(drawText: NSString, inImage: UIImage, atPoint:CGPoint)->UIImage{
// Setup the font specific variables
let textColor: UIColor = UIColor.blackColor()
let textFont: UIFont = UIFont(name: "Helvetica Bold", size: 200)!
//Setup the image context using the passed image.
UIGraphicsBeginImageContext(inImage.size)
//Setups up the font attributes that will be later used to dictate how the text should be drawn
let textFontAttributes = [
NSFontAttributeName: textFont,
NSForegroundColorAttributeName: textColor,
]
//Put the image into a rectangle as large as the original image.
inImage.drawInRect(CGRectMake(0, 0, inImage.size.width, inImage.size.height))
// Creating a point within the space that is as bit as the image.
let rect: CGRect = CGRectMake(atPoint.x, atPoint.y, inImage.size.width, inImage.size.height)
//Now Draw the text into an image.
drawText.drawInRect(rect, withAttributes: textFontAttributes)
// Create a new image out of the images we have created
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()
// End the context now that we have the image we need
UIGraphicsEndImageContext()
//And pass it back up to the caller.
return newImage
}
}
In your code you are adding the text one by one and returning the full image. The process you followed is not like draw the text in the space required for text, it wrote the text on image and return the full image.
Hence modify your method to pass all the text at a time and draw all text on image and return the full image .
I am showing you the code here where i have taken static points, you can put according to your requirement.
Here is the modified code :
func textToImage(drawText: NSString, drawText1: NSString, drawText2: NSString, drawText3: NSString, inImage: UIImage, atPoint:CGPoint, atPoint1:CGPoint, atPoint2:CGPoint, atPoint3:CGPoint)->UIImage{
// Setup the font specific variables
let textColor: UIColor = UIColor.redColor();
let textFont: UIFont = UIFont(name: "Helvetica Bold", size: 200)!
//Setup the image context using the passed image.
UIGraphicsBeginImageContext(inImage.size)
//Setups up the font attributes that will be later used to dictate how the text should be drawn
let textFontAttributes = [
NSFontAttributeName: textFont,
NSForegroundColorAttributeName: UIColor.redColor(),
]
let textFontAttributes1 = [
NSFontAttributeName: textFont,
NSForegroundColorAttributeName: UIColor.greenColor(),
]
let textFontAttributes2 = [
NSFontAttributeName: textFont,
NSForegroundColorAttributeName: UIColor.purpleColor(),
]
let textFontAttributes3 = [
NSFontAttributeName: textFont,
NSForegroundColorAttributeName: UIColor.yellowColor(),
]
//Put the image into a rectangle as large as the original image.
inImage.drawInRect(CGRectMake(0, 0, inImage.size.width, inImage.size.height))
// Creating a point within the space that is as bit as the image.
let rect: CGRect = CGRectMake(atPoint.x, atPoint.y, inImage.size.width, inImage.size.height)
//Now Draw the text into an image.
drawText.drawInRect(rect, withAttributes: textFontAttributes)
//text 1
let rect1: CGRect = CGRectMake(atPoint1.x, atPoint1.y, inImage.size.width, inImage.size.height)
drawText1.drawInRect(rect1, withAttributes: textFontAttributes1)
//text 2
let rect2: CGRect = CGRectMake(atPoint2.x, atPoint2.y, inImage.size.width, inImage.size.height)
drawText2.drawInRect(rect2, withAttributes: textFontAttributes2)
//text 3
let rect3: CGRect = CGRectMake(atPoint3.x, atPoint3.y, inImage.size.width, inImage.size.height)
drawText3.drawInRect(rect3, withAttributes: textFontAttributes3)
// Create a new image out of the images we have created
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()
// End the context now that we have the image we need
UIGraphicsEndImageContext()
//And pass it back up to the caller.
return newImage
}
And call the method from the same place where are you doing it currently, but like this
ImageDisplay.image = textToImage(CustomerTextBox.placeholder!, drawText1: ResponsibleTextBox.placeholder!, drawText2: LocationTextBox.placeholder!, drawText3: DescriptionTextBox.placeholder!, inImage: image, atPoint: CGPoint( x: 400, y: 100), atPoint1: CGPoint( x: 600, y: 300), atPoint2: CGPoint( x: 400, y: 600), atPoint3: CGPoint( x: 600, y: 1000));
Here the points are static for test, change it according to you.
Output :
Hope it helps.
Happy coding ...
Related
I have big problem. I want add few UIView to text (UITextView, UILabel)
Each view contains ImageView with corner radius and text.
I want have result like this:
sample from Android
I tried:
Add image with text by NSMutableAttributedString. In this case I can't add corner radius. And all images are from external serwers so it's problem with add to text.
I tried this library: SubviewAttachingTextView. In this case when I added multiple items all items were stacked on top of each other.
Finaly I used WKWebView and I inject HTML with CSS to WebView. But in this solution I have problem with fit content to frame size and is very slow. (for me is the worst solution)
Does anyone have an idea how to develop? Maybe there are some mechanisms in SwiftUI?
You can create a Custom class for a UIView and add subviews that you need inside that, and you can call that class when ever you want, in SwiftUI you can add this very easily by implementing a Label element inside the stack.
I solved my problem. I used NSMutableAttributedString and extensions on UIImage (download images and making circular avatars)
extension UIImage {
convenience init?(withContentsOfUrl url: URL) throws {
let imageData = try Data(contentsOf: url)
self.init(data: imageData)
}
public func withRoundedCorners(radius: CGFloat? = nil) -> UIImage? {
let maxRadius = min(size.width, size.height) / 2
let cornerRadius: CGFloat
if let radius = radius, radius > 0 && radius <= maxRadius {
cornerRadius = radius
} else {
cornerRadius = maxRadius
}
UIGraphicsBeginImageContextWithOptions(size, false, scale)
let rect = CGRect(origin: .zero, size: size)
UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius).addClip()
draw(in: rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
and my sample code:
class NewNotificationsViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var testText: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
self.testText.delegate = self
let url = "image-url";
let font = UIFont.systemFont(ofSize: 22)
let attributes: [NSAttributedString.Key: Any] = [
.font: font,
.foregroundColor: UIColor.orange,
.link: "http://test.pl"
]
let myString = NSMutableAttributedString(string: "Text at the beginning ", attributes: attributes)
let imageAttachment = NSTextAttachment()
do {
imageAttachment.image = try UIImage.init(withContentsOfUrl: URL(string: url)!)
} catch {
imageAttachment.image = UIImage(named: "avatar_k")
}
imageAttachment.image = imageAttachment.image?.withRoundedCorners()
imageAttachment.bounds = CGRect(x: 0, y: -8, width: 32, height: 32)
let imageString = NSAttributedString(attachment: imageAttachment)
myString.append(imageString)
myString.append(NSAttributedString(string: " THE END!!!", attributes: attributes))
self.testText.attributedText = myString
}
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
print("DEBUG", URL.absoluteString)
return false
}
}
I am trying to create a custom pin which has a label in the center of the pin which will change with every pin. The label will just be the pin number, so if I have 8 pins each pin will have a number in the center i.e. 1st pin will have 1, 2nd pin will have 2, etc. The issue is that the regular pin appears, not the custom pin.
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
loadPins()
}
func loadPins() {
let annotation = CustomPin()
annotation.coordinate = CLLocationCoordinate2D(latitude: 30.2531419, longitude: -97.78785789999999)
mapView.addAnnotation(annotation)
}
class AnView: UIView {
#IBOutlet weak var countLabel: UILabel!
#IBOutlet weak var pinImage: UIImageView!
override func draw(_ rect: CGRect) {
//Draw function
}
}
class CustomPin: MKPointAnnotation {
var pin = AnView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
}
The annotation (your CustomPin) gets the latitude/longitude values.
The annotation view (which your code doesn't show) gets the label or image you want to show on the map.
Since the Maps and Location Programming Guide from the comments gives all of its examples in Objective-C, you might want to also look at this RayWenderlich.com tutorial for Swift help:
MapKit Tutorial: Getting Started
You can draw the number over the map pin image. Just create a function (or extension) to add text over the image like this:
func addTextOverImage(_ text: String, overImage image: UIImage) -> UIImage {
//set scale and context
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(image.size, false, scale)
image.draw(in: CGRect(origin: CGPoint.zero, size: image.size))
//set font face, size and color
let textColor = UIColor.black
let textFont = UIFont.boldSystemFont(ofSize: 12)
//define the text attributes
let paragraph = NSMutableParagraphStyle()
paragraph.alignment = .center
let textFontAttributes = [
NSFontAttributeName: textFont,
NSForegroundColorAttributeName: textColor,
NSParagraphStyleAttributeName: paragraph
] as [String : Any]
//set the text position (in the image rect)
let textPoint=CGPoint.init(x: 0.0, y: image.size.height/2)
//create the rect to draw the text
let rect = CGRect(origin: textPoint, size: image.size)
//draw the text over the image, in the rect just defined
text.draw(in: rect, withAttributes: textFontAttributes)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
//return image with text
return newImage!
}
You may need to adjust the textPoint in the function to get the text positioned where you want it to be.
I have the following functional in the app:
the user takes (or chooses) an image (hereinafter originalImage).
the originalImage is sent to some external API, which returns the array of coordinates of dots that I need to add to originalImage.
Since the dots are always located in one area (face), I want to crop the originalImage close to the face borders and display to the user only the result of crop.
After the crop result is displayed I'm adding dots to it one by one.
Here is the code that does the job (except sending image, let's say it has already happened)
class ScanResultViewController{
#IBOutlet weak var scanPreviewImageView: UIImageView!
let originalImage = ORIGINAL_IMAGE //meaning we already have it
let scanDots = [["x":123, "y":123], ["x":234, "y":234]]//total 68 coordinates
var cropRect:CGRect!
override func viewDidLoad() {
super.viewDidLoad()
self.setScanImage()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.animateScan(0)
}
func setScanImage(){
self.cropRect = self.getCropRect(self.scanDots, sourceImage:self.originalImage)
let croppedImage = self.originalImage.imageAtRect(self.cropRect)
self.scanPreviewImageView.image = croppedImage
self.scanPreviewImageView.contentMode = .ScaleAspectFill
}
func animateScan(index:Int){
let i = index
self.originalImage = self.addOnePointToImage(self.originalImage, pointImage: GREEN_DOT!, point: self.scanDots[i])
let croppedImage = self.originalImage.imageAtRect(self.cropRect)
self.scanPreviewImageView.image = croppedImage
self.scanPreviewImageView.contentMode = .ScaleAspectFill
if i < self.scanDots.count-1{
let delay = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC)))
dispatch_after(delay, dispatch_get_main_queue()) {
self.animateScan(i+1)
}
}
}
func addOnePointToImage(sourceImage:UIImage, pointImage:UIImage, point: Dictionary<String,CGFloat>)->UIImage{
let rect = CGRect(x: 0, y: 0, width: sourceImage.size.width, height: sourceImage.size.height)
UIGraphicsBeginImageContextWithOptions(sourceImage.size, true, 0)
let context = UIGraphicsGetCurrentContext()
CGContextSetFillColorWithColor(context, UIColor.whiteColor().CGColor)
CGContextFillRect(context, rect)
sourceImage.drawInRect(rect, blendMode: .Normal, alpha: 1)
let pointWidth = sourceImage.size.width/66.7
pointImage.drawInRect(CGRectMake(point["x"]!-pointWidth/2, point["y"]!-pointWidth/2, pointWidth, pointWidth), blendMode: .Normal, alpha: 1)
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return result
}
func getCropRect(points: Array<Dictionary<String,CGFloat>>, sourceImage:UIImage)->CGRect{
var topLeft:CGPoint = CGPoint(x: points[0]["x"]!, y: points[0]["y"]!)
var topRight:CGPoint = CGPoint(x: points[0]["x"]!, y: points[0]["y"]!)
var bottomLeft:CGPoint = CGPoint(x: points[0]["x"]!, y: points[0]["y"]!)
var bottomRight:CGPoint = CGPoint(x: points[0]["x"]!, y: points[0]["y"]!)
for p in points{
if p["x"]<topLeft.x {topLeft.x = p["x"]!}
if p["y"]<topLeft.y {topLeft.y = p["y"]!}
if p["x"]>topRight.x {topRight.x = p["x"]!}
if p["y"]<topRight.y {topRight.y = p["y"]!}
if p["x"]<bottomLeft.x {bottomLeft.x = p["x"]!}
if p["y"]>bottomLeft.y {bottomLeft.y = p["y"]!}
if p["x"]>bottomRight.x {bottomRight.x = p["x"]!}
if p["y"]>bottomRight.y {bottomRight.y = p["y"]!}
}
let rect = CGRect(x: topLeft.x, y: topLeft.y, width: (topRight.x-topLeft.x), height: (bottomLeft.y-topLeft.y))
return rect
}
}
extension UIImage{
public func imageAtRect(rect: CGRect) -> UIImage {
let imageRef: CGImageRef = CGImageCreateWithImageInRect(self.CGImage, rect)!
let subImage: UIImage = UIImage(CGImage: imageRef)
return subImage
}
}
The problem is that in setScanImage the desired area is accurately cropped and displayed, but when animateScan method is called a different area of the same image is cropped (and displayed) though cropRect is the same and the size of originalImage is totally the same.
Any ideas, guys?
By the way if I display originalImage without cropping it everything works smoothly.
So finally after approximately 10 hours net time (and a lot of help of the stackoverflow community:-) I managed to fix the problem:
In the function addOnePointToImage you need to change the following:
In this line:
UIGraphicsBeginImageContextWithOptions(sourceImage.size, true, 0)
you need to change the last argument (which stands for scale) to 1:
UIGraphicsBeginImageContextWithOptions(sourceImage.size, true, 1)
That totally resolves the issue.
I'm trying to combine images and flip them using the function:
UIView.transitionFromView(self.frontImageView, toView: self.backImageView, duration: 1, options: UIViewAnimationOptions.TransitionFlipFromLeft, completion: nil)
This is using swift. I have 2 views and it works fine. I would like the backImageView to be a combination of the card which looks like a card with empty white space, and a letter or an image.
I saw several posts on combining images using UIGraphicsGetImageFromCurrentImageContext()
When I do this for letters it slows the app down I think..
Here is this code used (Card is a subclass of UIView)
func combineImages( card: Card ) -> UIImage
{
let bottomImage: UIImage = UIImage( named: "cardFront" )!
var textColor: UIColor = UIColor.blackColor()
var textFont: UIFont = UIFont(name: "Muli", size: 120 )!
UIGraphicsBeginImageContext( bottomImage.size )
let textFontAttributes = [
NSFontAttributeName: textFont,
NSForegroundColorAttributeName: textColor,
]
bottomImage.drawInRect( CGRectMake(0, 0, bottomImage.size.width, bottomImage.size.height) )
var rect: CGRect = CGRectMake( 50, 48, 380, 360 )
var drawText = "\( card.pictureName )"
drawText.drawInRect( rect, withAttributes: textFontAttributes )
// Create a new image out of the images we have created
var newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()
// End the context now that we have the image we need
UIGraphicsEndImageContext()
return newImage
}
Question is, is there a better way to do this? Something faster? I believe combining images is probably the slower alternative to something simple which i could be missing.
I have just started with programming. I am trying to overlay text on an image taken with the iPhone prior to saving the image. I have found bits of code that look promising but nothing that does the job specifically, and no posts that answer the question specifically. This is my code to take an image. Without the attempt at overlaying the text I know it takes a photo.
#IBAction func handleShutterButton(sender: UIButton) {
cameraController.captureStillImage { (image, metadata) -> Void in
self.view.layer.contents = image
var project = "ProjectX"
var camNumber = "c01"
let timestamp = NSDateFormatter.localizedStringFromDate(NSDate(), dateStyle: .MediumStyle, timeStyle: .ShortStyle)
let overlay = "\(project) \(camNumber) \(timestamp)"
self.overlayText(overlay, inImage: UIImage(named: image)!, atPoint: CGPointMake(20, 20)) // This is where it fails
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
}
}
And this is the function I'm using for overlaying the text.
func overlayText(drawText: NSString, inImage: UIImage, atPoint:CGPoint)->UIImage{
// Setup the font specific variables
let textColor: UIColor = UIColor.whiteColor()
let textFont: UIFont = UIFont(name: "Helvetica Bold", size: 12)!
//Setup the image context using the passed image.
UIGraphicsBeginImageContext(inImage.size)
//Setups up the font attributes that will be later used to dictate how the text should be drawn
let textFontAttributes = [
NSFontAttributeName: textFont,
NSForegroundColorAttributeName: textColor,
]
//Put the image into a rectangle as large as the original image.
inImage.drawInRect(CGRectMake(0, 0, inImage.size.width, inImage.size.height))
// Creating a point within the space that is as big as the image.
let rect: CGRect = CGRectMake(atPoint.x, atPoint.y, inImage.size.width, inImage.size.height)
//Now Draw the text into an image.
drawText.drawInRect(rect, withAttributes: textFontAttributes)
// Create a new image out of the images we have created
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()
// End the context now that we have the image we need
UIGraphicsEndImageContext()
//And pass it back up to the caller.
return newImage
}
Any help would be appreciated.
I have made changes in your code.. Try this..
#IBAction func handleShutterButton(sender: UIButton) {
cameraController.captureStillImage { (image, metadata) -> Void in
self.view.layer.contents = image
var project = "ProjectX"
var camNumber = "c01"
let timestamp = NSDateFormatter.localizedStringFromDate(NSDate(), dateStyle: .MediumStyle, timeStyle: .ShortStyle)
let overlay = "\(project) \(camNumber) \(timestamp)"
var changedImage : UIImage = self.overlayText(overlay, inImage: image, atPoint: CGPointMake(20, 20)) // This is where it fails
UIImageWriteToSavedPhotosAlbum(changedImage, nil, nil, nil);
}
}