daviid
daviid

Reputation: 35

Swift: Replace Strings to NSTextAttachment in UITextView after loading styled RTF

In my iOS/swift project I am loading a RTF document to a UITextView with the code below. The RTF itself contains styled text like "... blah blah [ABC.png] blah blah [DEF.png] blah..." which is loaded to the UITextView just fine.

Now, I want to replace all occurrences of [someImage.png] with an actual image as NSTextAttachment. How can I do that?

I am aware of the possibility to embed images in RTF documents, but I can not do that in this project.

if let rtfPath = Bundle.main.url(forResource: "testABC", withExtension: "rtf") 
{
    do
    {
    //load RTF to UITextView
    let attributedStringWithRtf = try NSAttributedString(url: rtfPath, options: [.documentType: NSAttributedString.DocumentType.rtf], documentAttributes: nil)
    txtView.attributedText = attributedStringWithRtf
    
    //find all "[ABC.png]" and replace with image
    let regPattern = "\\[.*?\\]"
    //now...?
    }
}

Upvotes: 0

Views: 679

Answers (1)

Larme
Larme

Reputation: 26096

Here is something that you could do.

Note: I'm not a Swift Developper, more an Objective-C one, so there may be some ugly Swift code (the try!, etc.). But it's more for the logic on using NSRegularExpression (which I used in Objective-C since it's shared in CocoaTouch)

So the main line directives:
Find where are the images placeholders.
Create a NSAttributeString/NSTextAttachment from it.
Replace the placeholder with the previous attributed string.

let regPattern = "\\[((.*?).png)\\]"
let regex = try! NSRegularExpression.init(pattern: regPattern, options: [])

let matches = regex.matches(in: attributedStringWithRtf.string, options: [], range: NSMakeRange(0, attributedStringWithRtf.length))
for aMatch in matches.reversed()
{
    let allRangeToReplace = attributedStringWithRtf.attributedSubstring(from: aMatch.range(at: 0)).string
    let imageNameWithExtension = attributedStringWithRtf.attributedSubstring(from: aMatch.range(at: 1)).string
    let imageNameWithoutExtension = attributedStringWithRtf.attributedSubstring(from: aMatch.range(at: 2)).string
    print("allRangeToReplace: \(allRangeToReplace)")
    print("imageNameWithExtension: \(imageNameWithExtension)")
    print("imageNameWithoutExtension: \(imageNameWithoutExtension)")

    //Create your NSAttributedString with NSTextAttachment here
    let myImageAttribute = ...
    attributedStringWithRtf.replaceCharacters(in: imageNameRange, with: myImageAttributeString)
}

So what's the idea?

I used a modify pattern. I hard-wrote "png", but you could change it. I added some () to get easily the interesting parts. I thought that you may wanted to retrieve the name of the image, with or without the .png, that's why I got all theses print(). Maybe because you saved it in your app, etc. If you need to add the extension as a group, you may want to add it into parenthesis in your regPattern and check what aMatch.range(at: ??) to call. for using Bundle.main.url(forResource: imageName, withExtension: imageExtension)

I used the matches.reversed() because if you modify the length of the "match" with a replacement of different length, the previous ranges will be off. So starting from the end could do the trick.

Some code to transform a UIImage into NSAttributedString through NSTextAttachment: How to add images as text attachment in Swift using nsattributedstring

Upvotes: 1

Related Questions