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)
}
}
CodePudding user response:
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)
}