0

I am adding both text and image in UITextView to part of my app ( similar to the notes app ). The user can write a text and/or then select a picture from the photo library or take a picture ( with image picker).

I managed to add the text and the image into the UITextView. I can save the text but can’t figure out how to save the image. I have looked very similar questions but still having a hard time getting the code. I have tried to do it with userDefaults but apparently it’s not the right way. Maybe documents directory or file path but I don’t know how to do that. Any help will be much appreciate it.

This is the code I have:

class NotesViewController: UIViewController, UITextViewDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

   @IBOutlet weak var textView: UITextView!
   var pickImage: UIImage!
   var attString = NSAttributedString()

 override func viewDidLoad() {
    super.viewDidLoad()

    textView.delegate = self
    self.isModalInPresentation = true
    textView.keyboardDismissMode = .interactive  // .onDrag
    
    textView.becomeFirstResponder()
    
    textView.textColor = UIColor.self.init(red: 222/255, green: 215/255, blue: 191/255, alpha: 1.0)
    textView.font = UIFont(name: "Trebuchet MS", size: 19)
    textView.backgroundColor = UIColor.clear
    textView.tintColor = UIColor.self.init(red: 141/255, green: 124/255, blue: 85/255, alpha: 1.0)
    
    textView.layer.masksToBounds = false


    if let value = userDefault.value(forKey: myNotes) as? String {

        textView.text = value

    }
    textView.alwaysBounceVertical = true


    }

 func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {

      pickImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage

      //create and NSTextAttachment and add your image to it.
      let attachment = NSTextAttachment()
      attachment.image = pickImage

      //calculate new size.  (-20 because I want to have a litle space on the right of picture)
      let newImageWidth = (textView.bounds.size.width - 15 )
      let scale = newImageWidth/pickImage.size.width
      let newImageHeight = pickImage.size.height * scale - 20

      //resize this
      attachment.bounds = CGRect.init(x: 0, y: 0, width: newImageWidth, height: newImageHeight)

      //put your NSTextAttachment into and attributedString
      attString = NSAttributedString(attachment: attachment)

      //add this attributed string to the current position.
      textView.textStorage.insert(attString, at: textView.selectedRange.location)
      textView.selectedRange.location += 1
      
      textView.textColor = UIColor.self.init(red: 222/255, green: 215/255, blue: 191/255, alpha: 1.0)
      textView.font = UIFont(name: "Trebuchet MS", size: 19)
      textView.backgroundColor = UIColor.clear
      textView.tintColor = UIColor.self.init(red: 141/255, green: 124/255, blue: 85/255, alpha: 1.0)
      textView.keyboardDismissMode = .interactive

      picker.dismiss(animated: true, completion: nil)

 }

 func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
       picker.dismiss(animated: true, completion: nil)
    }

 @objc func saveText() {

    userDefault.setValue(textView.text, forKey: myNotes)
    dismiss(animated: true, completion: nil)
  
   }

 }
Dian
  • 115
  • 3
  • 12

1 Answers1

2

There are one important you need to know:

  • Everything you customize like add image, add icon, ... you always do on NSAttributedString not the plain text.

So at your func saveText you call:

userDefault.setValue(textView.text, forKey: myNotes)

Means that you store only the plain text from your textView which is textView.text but not storing all the special attributes combine in your textView which is textView.attributedText. That's the main reason you not getting the image with you when saving.

For do that, you need to make two function: One to store your attributed text to UserDefault and one to get from UserDefault to appear

For the first one, which is store your attributed text to UserDefault. We can not save directly NSAttributedString to UserDefault so we need to convert it to Data first and store it.

func saveAttributedTextToUserDefault(attributedText: NSAttributedString, key: String) {
    do {
        let data = try attributedText.data(from: NSRange(location: 0, length: attributedText.length), documentAttributes: [.documentType: NSAttributedString.DocumentType.rtfd])
        UserDefaults.standard.setValue(data, forKeyPath: key)
    } catch {
        print(error)
    }
}

The second one, simply get the data from UserDefault and convert it back to NSAttributedString

func getAttributedTextFromUserDefault(key: String) -> NSAttributedString {
    if let dataValue = UserDefaults.standard.value(forKey: key) as? Data {
        do {
            let attributeText = try NSAttributedString(data: dataValue, documentAttributes: nil)
            return attributeText
        } catch {
            print("error: ", error)
        }

    }

    return NSAttributedString()
}

The usage

override func viewDidLoad() {
    // your others code
    let attributedText = self.getAttributedTextFromUserDefault(key: "test")
    textView.attributedText = attributedText
}

@objc func saveText() {
    // take attributedText and save it
    self.saveAttributedTextToUserDefault(attributedText: textView.attributedText, key: "test")

    dismiss(animated: true, completion: nil)
}
Thang Phi
  • 1,641
  • 2
  • 7
  • 16
  • Thank you so much for your help! Everything works great. I was able to save text and image(s) and retrieve them. The only problem that I am facing right now is that the image doesn't resize. When I pick it, looks fine in the textView but when I save it doesn't not keep the original scale down. How do I save that in the function? You can see in the my code above that I am using NSTextAttachment(). Thanks again in advance for your help!!! – Dian Sep 29 '22 at 19:50
  • 1
    Dear @Dian, the problem you say is out of scope of this question. You should make another question or try it yourself. Hint is make a func to convert from the ``NSAttributedString`` from UserDefault and make new ``NSAttributedString`` with text and resize image. Check this https://stackoverflow.com/questions/29152660/extract-uiimage-from-nsattributed-string/29153172#29153172 – Thang Phi Sep 29 '22 at 23:02
  • Dear @bewithyou. Thanks again for the tip and the hint. I have seen the link you sent. Still don't understand it. I have been trying and reading but I am a beginner and can not get it to work with my code. – Dian Sep 30 '22 at 21:16