I'm using Below method for detecting taps on image in UITextView
.
`func textView(_ textView: UITextView, shouldInteractWith textAttachment: NSTextAttachment, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool`
This method only call when textView.isEditable = false
.
So then i add UITapGestureRecognizer
on UITextView
and it calls when user tap on image inside UITextView
. But that point i don't know how i can detect on which image user tap if there is more than one inside UITextView
. I also get UITextView
x and y location of tap but dont know how i can get text or if it is image from these point
let TapGesture = UITapGestureRecognizer(target: self, action: #selector(tapDetected(sender:)))
TapGesture.delegate = self
textView.addGestureRecognizer(TapGesture)`
I also tried to add a view in textView.addSubview
. But i also don't know how i can change its position if user want to type a text before or after this subview, like it behave same as NSAttributedString Images
change its position accordingly text.
let imgRect : UIBezierPath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 30, height: 30))
textView.textContainer.exclusionPaths = [imgRect]
let spacerView : UIView = UIView.init(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
spacerView.backgroundColor = .red
textView.addSubview(spacerView)
Can anyone please tell me how i can detect tap on image while editing is true. Or anyone know how i can add action(addTarget) on NSAttributedString
image. I'v also checked iOS default Notes
App and they are doing same thing as i need. The main reason behind this feature i want to add attach videos Thumbnails option in UiTextView
, when user tap on video thumbnail while typing, video will automatically play in player. I'm attaching video that i recorded from my phone, This is my project.
I need exact same functionality that is on Below Video
Thanks
import UIKit
class ViewController: UIViewController,UITextViewDelegate,UIGestureRecognizerDelegate {
@IBOutlet var textView: UITextView!
@IBOutlet var imageView: UIImageView!
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
textView.resignFirstResponder()
print("touchesBegan")
}
override func viewDidLoad() {
super.viewDidLoad()
let TapGesture = UITapGestureRecognizer(target: self, action: #selector(tapDetected(sender:)))
TapGesture.delegate = self
textView.addGestureRecognizer(TapGesture)
let imgRect : UIBezierPath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 30, height: 30))
textView.textContainer.exclusionPaths = [imgRect]
let spacerView : UIView = UIView.init(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
spacerView.backgroundColor = .red
textView.addSubview(spacerView)
textView.attributedText.addObserver(self, forKeyPath: "image", options: .new, context: nil)
textView.attributedText.addObserver(self, forKeyPath: "image", options: .initial, context: nil)
textView.attributedText.addObserver(self, forKeyPath: "image", options: .old, context: nil)
textView.attributedText.addObserver(self, forKeyPath: "image", options: .prior, context: nil)
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
@IBAction func addImage(_ sender: Any) {
var attributedString :NSMutableAttributedString!
attributedString = NSMutableAttributedString(attributedString:textView.attributedText)
let textAttachment = NSTextAttachment()
textAttachment.image = UIImage(named: "taylor")
let oldWidth = textAttachment.image!.size.width;
//I'm subtracting 10px to make the image display nicely, accounting
//for the padding inside the textView
let scaleFactor = (oldWidth / (textView.frame.size.width - 10))
textAttachment.image = UIImage(cgImage: textAttachment.image!.cgImage!, scale: scaleFactor, orientation: .up)
let attrStringWithImage = NSAttributedString(attachment: textAttachment)
attributedString.append(attrStringWithImage)
textView.attributedText = attributedString;
}
@objc func tapDetected(sender: UITapGestureRecognizer) {
print("Tap On Image")
print("Tap Location",sender.location(in: sender.view))
guard case let senderView = sender.view, (senderView is UITextView) else {
return
}
// calculate layout manager touch location
let textView = senderView as! UITextView, // we sure this is an UITextView, so force casting it
layoutManager = textView.layoutManager
var location = sender.location(in: textView)
location.x -= textView.textContainerInset.left
location.y -= textView.textContainerInset.top
print("location",location)
let textContainer = textView.textContainer,
characterIndex = layoutManager.characterIndex(for: location, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil),
textStorage = textView.textStorage
guard characterIndex < textStorage.length else {
return
}
}
func textViewDidChange(_ textView: UITextView) {
print("textViewDidChange")
}
func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
print("textViewShouldBeginEditing")
return true
}
func textViewDidBeginEditing(_ textView: UITextView) {
print("textViewDidBeginEditing")
}
func textViewDidEndEditing(_ textView: UITextView) {
print("textViewDidBeginEditing")
}
func textViewShouldEndEditing(_ textView: UITextView) -> Bool {
print("textViewShouldEndEditing")
return true
}
func textViewDidChangeSelection(_ textView: UITextView) {
print("textViewDidChangeSelection")
print("selectedText", textView.selectedRange.location)
print("textView.attributedText.containsAttachments(in: textView.selectedRange",textView.attributedText.containsAttachments(in: textView.selectedRange))
print("textView.attributedText.attributedSubstring(from: textView.selectedRange)",textView.attributedText.attributedSubstring(from: textView.selectedRange))
let img = textView.getParts()
for i in img {
if let image = i as? UIImage {
imageView.image = image
}
}
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
print("observeValueobserveValueobserveValueobserveValueobserveValue keyPath \(String(describing: keyPath)) change \(String(describing: change)) context \(String(describing: context)) ")
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
print("textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String)")
return true
}
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
print("textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool ")
return true
}
func textView(_ textView: UITextView, shouldInteractWith textAttachment: NSTextAttachment, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
print("textView(_ textView: UITextView, shouldInteractWith textAttachment: NSTextAttachment, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool")
imageView.image = textAttachment.image
return true
}
func textView(_ textView: UITextView, shouldInteractWith textAttachment: NSTextAttachment, in characterRange: NSRange) -> Bool {
print("textView(_ textView: UITextView, shouldInteractWith textAttachment: NSTextAttachment, in characterRange: NSRange) -> Bool")
return true
}
}
extension UITextView {
func getParts() -> [AnyObject] {
var parts = [AnyObject]()
let attributedString = self.attributedText
let range = self.selectedRange//NSMakeRange(0, (attributedString?.length)!)
attributedString?.enumerateAttributes(in: range, options: NSAttributedString.EnumerationOptions(rawValue: 0)) { (object, range, stop) in
if object.keys.contains(NSAttributedStringKey.attachment) {
if let attachment = object[NSAttributedStringKey.attachment] as? NSTextAttachment {
if let image = attachment.image {
parts.append(image)
} else if let image = attachment.image(forBounds: attachment.bounds, textContainer: nil, characterIndex: range.location) {
parts.append(image)
}
}
} else {
let stringValue : String = attributedString!.attributedSubstring(from: range).string
if (!stringValue.trimmingCharacters(in: .whitespaces).isEmpty) {
parts.append(stringValue as AnyObject)
}
}
}
return parts
}
}