Jak dodać tekst do obrazu w iOS Swift?


82

Rozejrzałem się i nie udało mi się dowiedzieć, jak wziąć tekst, nałożyć go na obraz, a następnie połączyć je w jeden UIImage.

Wyczerpałem Google przy użyciu wyszukiwanych haseł, które przychodzą mi do głowy, więc jeśli ktoś ma rozwiązanie lub przynajmniej wskazówkę, może je wskazać, byłby bardzo wdzięczny.

Odpowiedzi:


186

Ok ... wymyśliłem to:

func textToImage(drawText: NSString, inImage: UIImage, atPoint: CGPoint) -> UIImage{

    // Setup the font specific variables
    var textColor = UIColor.whiteColor()
    var textFont = UIFont(name: "Helvetica Bold", size: 12)!

    // Setup the image context using the passed image
    let scale = UIScreen.mainScreen().scale
    UIGraphicsBeginImageContextWithOptions(inImage.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,
    ]

    // Put the image into a rectangle as large as the original image
    inImage.drawInRect(CGRectMake(0, 0, inImage.size.width, inImage.size.height))

    // Create a point within the space that is as bit as the image
    var rect = CGRectMake(atPoint.x, atPoint.y, inImage.size.width, inImage.size.height)

    // Draw the text into an image
    drawText.drawInRect(rect, withAttributes: textFontAttributes)

    // Create a new image out of the images we have created
    var newImage = UIGraphicsGetImageFromCurrentImageContext()

    // End the context now that we have the image we need
    UIGraphicsEndImageContext()

    //Pass the image back up to the caller
    return newImage

}

Aby to nazwać, wystarczy przekazać obraz:

textToImage("000", inImage: UIImage(named:"thisImage.png")!, atPoint: CGPointMake(20, 20))

Poniższe linki pomogły mi w zrozumieniu tego:

Swift - Rysowanie tekstu za pomocą drawInRect: withAttributes:

Jak pisać tekst na obrazie w Objective-C (iOS)?

Pierwotnym celem było stworzenie dynamicznego obrazu, który mógłbym wykorzystać AnnotaionViewnp. Do umieszczenia ceny w danym miejscu na mapie i to wyszło świetnie. Mam nadzieję, że pomoże to komuś, kto spróbuje zrobić to samo.

Dla Swift 3:

 func textToImage(drawText text: NSString, inImage image: UIImage, atPoint point: CGPoint) -> UIImage {
    let textColor = UIColor.white
    let textFont = UIFont(name: "Helvetica Bold", size: 12)!

    let scale = UIScreen.main.scale
    UIGraphicsBeginImageContextWithOptions(image.size, false, scale)

    let textFontAttributes = [
        NSFontAttributeName: textFont,
        NSForegroundColorAttributeName: textColor,
        ] as [String : Any]
    image.draw(in: CGRect(origin: CGPoint.zero, size: image.size))

    let rect = CGRect(origin: point, size: image.size)
    text.draw(in: rect, withAttributes: textFontAttributes)

    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return newImage!
 }

Dla Swift 4:

 func textToImage(drawText text: String, inImage image: UIImage, atPoint point: CGPoint) -> UIImage {
    let textColor = UIColor.white
    let textFont = UIFont(name: "Helvetica Bold", size: 12)!

    let scale = UIScreen.main.scale
    UIGraphicsBeginImageContextWithOptions(image.size, false, scale)

    let textFontAttributes = [
        NSAttributedStringKey.font: textFont,
        NSAttributedStringKey.foregroundColor: textColor,
        ] as [NSAttributedStringKey : Any]
    image.draw(in: CGRect(origin: CGPoint.zero, size: image.size))

    let rect = CGRect(origin: point, size: image.size)
    text.draw(in: rect, withAttributes: textFontAttributes)

    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return newImage!
 }

Dla Swift 5:

func textToImage(drawText text: String, inImage image: UIImage, atPoint point: CGPoint) -> UIImage {
    let textColor = UIColor.white
    let textFont = UIFont(name: "Helvetica Bold", size: 12)!

    let scale = UIScreen.main.scale
    UIGraphicsBeginImageContextWithOptions(image.size, false, scale)

    let textFontAttributes = [
        NSAttributedString.Key.font: textFont,
        NSAttributedString.Key.foregroundColor: textColor,
        ] as [NSAttributedString.Key : Any]
    image.draw(in: CGRect(origin: CGPoint.zero, size: image.size))

    let rect = CGRect(origin: point, size: image.size)
    text.draw(in: rect, withAttributes: textFontAttributes)

    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return newImage!
}

1
superpomocne! dziękuję za wysłanie kodu. i tak dobrze skomentowane.
Aspen

czy te teksty lub ich kontenery mogą reagować na zdarzenia związane z dotknięciem?
PPrasai

1
@JadeSync, jeśli obiekt, do którego importujesz obraz, jest klikalny, to tak. Ale to nie tekst jest klikalny, to kontener, w którym używane są końcowe obrazy, musiałby być klikalny. Właściwie to właśnie zrobiłem z tym obrazem, ponieważ potrzebowałem sposobu na umieszczenie na mapie klikalnego znacznika, a marker musiał mieć tekst na obrazie. Znacznik ma delegata zdarzenia kliknięcia, więc nie jest to obraz, na który klikają, ale znacznik, który go zawiera.
Christopher Wade Cantley

2
Jeśli ustawię punkt w prawym dolnym rogu obrazu, a tekst jest za długi, wykracza poza obraz. Jakieś sugestie, jak wyrównać tekst do lewej strony punktu zamiast rozciągać się poza obraz w prawo?
kurczak,

Natknąłem się również na ten problem i szczerze mówiąc, zależy to od rodzaju i rozmiaru czcionki oraz rozmiaru obrazu, w którym chcesz, aby tekst zaczynał się na obrazie, ponieważ możesz nie chcieć nadepnąć na cień PNG. To była próba i błąd, który wymagał dodania znaku, ustalenia, o ile pikseli przesunąć punkt początkowy, aby go wprowadzić, a następnie wykonać szybkie obliczenia, aby obliczyć przesuwny punkt początkowy na podstawie liczby znaków (w górę maksymalnie w zależności od tego, jak wygląda). Nie chciałem, aby było to zbyt skomplikowane, więc odpowiedziałem na to pytanie.
Christopher Wade Cantley,

20

Moje proste rozwiązanie:

func generateImageWithText(text: String) -> UIImage? {
    let image = UIImage(named: "imageWithoutText")!

    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.backgroundColor = UIColor.clear
    label.textAlignment = .center
    label.textColor = UIColor.white
    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
}

Dziękuję za kod, ale jak dostosować rozmiar czcionki, każdy rozmiar obrazu jest inny z inną długością tekstu. Jak mogę uczynić to dynamicznym.
iPhoneDev,

Myślę, że powinieneś użyć Autoshrink. Ustaw maksymalny rozmiar czcionki dla maksymalnej szerokości etykiety i dodaj: label.adjustsFontSizeToFitWidth = true & label.minimumScaleFactor = 0.5
Darkngs

czy istnieje sposób, aby dodać tekst etykiety wyrównany do któregoś z rogów?
Ashh

11

Możesz także zrobić CATextLayer.

    // 1
let textLayer = CATextLayer()
textLayer.frame = someView.bounds

// 2
let string = String(
  repeating: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce auctor arcu quis velit congue dictum. ", 
  count: 20
)

textLayer.string = string

// 3
let fontName: CFStringRef = "Noteworthy-Light"
textLayer.font = CTFontCreateWithName(fontName, fontSize, nil)

// 4
textLayer.foregroundColor = UIColor.darkGray.cgColor
textLayer.isWrapped = true
textLayer.alignmentMode = kCAAlignmentLeft
textLayer.contentsScale = UIScreen.main.scale
someView.layer.addSublayer(textLayer)

https://www.raywenderlich.com/402-calayer-tutorial-for-ios-getting-started


1
Uwielbiam rzeczy Raya. Dzięki za alternatywną metodę!
Christopher Wade Cantley


1
Warto o tym wspomnieć, jeśli ktoś chce wyrównać tekst w pionie, patrz stackoverflow.com/questions/4765461/ ...
JKRT

5

Stworzyłem rozszerzenie do używania go wszędzie:

import Foundation
import UIKit
extension UIImage {

    class func createImageWithLabelOverlay(label: UILabel,imageSize: CGSize, image: UIImage) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(CGSize(width: imageSize.width, height: imageSize.height), false, 2.0)
        let currentView = UIView.init(frame: CGRect(x: 0, y: 0, width: imageSize.width, height: imageSize.height))
        let currentImage = UIImageView.init(image: image)
        currentImage.frame = CGRect(x: 0, y: 0, width: imageSize.width, height: imageSize.height)
        currentView.addSubview(currentImage)
        currentView.addSubview(label)
        currentView.layer.render(in: UIGraphicsGetCurrentContext()!)
        let img = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return img!
    }

}

Użycie: W dowolnym miejscu w kontrolerze ViewController, w którym masz rozmiar i etykietę do dodania, użyj go w następujący sposób -

let newImageWithOverlay = UIImage.createImageWithLabelOverlay(label: labelToAdd, imageSize: size, image: editedImage)

1
Niesamowite! Dzięki za wkład w to.
Christopher Wade Cantley,

Pokazuje białe tło, nawet jeśli użyłem przezroczystego obrazu
EI Captain v2.0

Co robisz, umieszczając go w widoku lub przesyłając na serwer?
Ankit Kumar Gupta

Po prostu weź zdjęcie i pokaż je w widoku obrazu. Pokazuje białe tło
EI Captain v2.0

Twój widok obrazu i jego nadzór muszą mieć czyste tło. Jeśli wyślesz go na serwer w formacie png, będzie tak, jak chcesz. Zrób to i sprawdź. Lub spróbuj zapisać go lokalnie.
Ankit Kumar Gupta

3

Dla Swift 4:

func textToImage(drawText text: NSString, inImage image: UIImage, atPoint point: CGPoint) -> UIImage {


    let scale = UIScreen.main.scale
    UIGraphicsBeginImageContextWithOptions(image.size, false, scale)

    image.draw(in: CGRect(origin: CGPoint.zero, size: image.size))

    let rect = CGRect(origin: point, size: image.size)

    let paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.alignment = .center



    let attrs = [NSAttributedStringKey.font: UIFont(name: "Helvetica Bold", size: 12)!,NSAttributedStringKey.foregroundColor : UIColor.white , NSAttributedStringKey.paragraphStyle: paragraphStyle]


    text.draw(with: rect, options: .usesLineFragmentOrigin, attributes: attrs, context: nil)



    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return newImage!
}

1

W twoim początkowym pytaniu nie widzę niczego, co sugerowałoby, że należy to zrobić wyłącznie w kodzie - dlaczego więc po prostu nie dodać UILabel w narzędziu do tworzenia interfejsów i dodać ograniczenia, aby nadać mu taką samą długość i szerokość jak obraz, wyśrodkować go w pionie i poziomo (lub jakkolwiek potrzebujesz), usuń tekst etykiety, ustaw czcionkę tekstu, rozmiar, kolor itp. w razie potrzeby (w tym zaznaczenie Autoshrink z jakimkolwiek minimalnym rozmiarem lub skalą, jakiej potrzebujesz) i upewnij się, że tło jest przezroczyste.

Następnie po prostu podłącz go do IBOutlet i ustaw tekst w kodzie zgodnie z potrzebami (np. W viewWillAppear lub używając podejścia ViewModel i ustawiając go podczas inicjalizacji widoku / kontrolera widoku).


0

Wypróbowałem te podstawowe komponenty. Mam nadzieję, że to zadziała.

func imageWithText(image : UIImage, text : String) -> UIImage {

        let outerView = UIView(frame: CGRect(x: 0, y: 0, width: image.size.width / 2, height: image.size.height / 2))
        let imgView = UIImageView(frame: CGRect(x: 0, y: 0, width: outerView.frame.width, height: outerView.frame.height))
        imgView.image = image
        outerView.addSubview(imgView)

        let lbl = UILabel(frame: CGRect(x: 5, y: 5, width: outerView.frame.width, height: 200))
        lbl.font = UIFont(name: "HelveticaNeue-Bold", size: 70)
        lbl.text = text
        lbl.textAlignment = .left
        lbl.textColor = UIColor.blue

        outerView.addSubview(lbl)

        let renderer = UIGraphicsImageRenderer(size: outerView.bounds.size)
        let convertedImage = renderer.image { ctx in
            outerView.drawHierarchy(in: outerView.bounds, afterScreenUpdates: true)

        }
        return convertedImage
    }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.