Обнаружение касания изображения, прикрепленного к NSAttributedString, в то время как UITextVIew редактирование верно
Я использую метод ниже для обнаружения касаний изображения в UITextView
,
`func textView(_ textView: UITextView, shouldInteractWith textAttachment: NSTextAttachment, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool`
Этот метод вызывается только когда textView.isEditable = false
,
Итак, я добавляю UITapGestureRecognizer
на UITextView
и он вызывает, когда пользователь нажимает на изображение внутри UITextView
, Но в этот момент я не знаю, как я могу определить, на каком изображении пользователь нажимает, если внутри больше одного UITextView
, Я тоже получаю UITextView
X и Y местоположение крана, но не знаю, как я могу получить текст или это изображение из этой точки
let TapGesture = UITapGestureRecognizer(target: self, action: #selector(tapDetected(sender:)))
TapGesture.delegate = self
textView.addGestureRecognizer(TapGesture)`
Я также пытался добавить представление в textView.addSubview
, Но я также не знаю, как я могу изменить его положение, если пользователь хочет напечатать текст до или после этого подпредставления, как будто он ведет себя так же, как NSAttributedString Images
измените свою позицию соответственно тексту.
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)
Может кто-нибудь, пожалуйста, скажите мне, как я могу обнаружить нажатие на изображение, когда редактирование верно. Или кто-нибудь знает, как я могу добавить действие (addTarget) на NSAttributedString
образ. Я также проверил iOS по умолчанию Notes
Приложение, и они делают то же самое, что мне нужно. Основной причиной этой функции я хочу добавить прикрепленные видео вариант миниатюр в UiTextView
, когда пользователь нажимает на миниатюру видео во время набора текста, видео будет автоматически воспроизводиться в плеере. Я прилагаю видео, которое я записал со своего телефона, это мой проект.
Мне нужна та же функциональность, что и на видео ниже
Спасибо
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
}
}
2 ответа
Во-первых, создайте новый NSAttributedStringKey, который вы будете использовать для идентификации вложения изображения. Затем создайте NSTextAttachment с изображением, оберните его в NSMutableAttributedString и добавьте в него пользовательский атрибут. Наконец добавьте обертку к полной NSAttributedString и присоедините UITapGestureRecognizer.
Затем, когда в селекторе на UITapGestureRecognizer просто ищите этот пользовательский тег.
Код для большинства битов:
extension NSAttributedStringKey {
static let imagePath = NSAttributedStringKey(rawValue: "imagePath")
}
... тогда при настройке отображения текста
let fullString = NSMutableAttributedString()
let imageAttachment = NSTextAttachment()
imageAttachment.image = image
let imageAttributedString: NSMutableAttributedString = NSAttributedString(attachment: imageAttachment).mutableCopy() as! NSMutableAttributedString
let customAttribute = [ NSAttributedStringKey.imagePath: imagePath ]
imageAttributedString.addAttributes(customAttribute, range: NSRange(location: 0, length: imageAttributedString.length))
fullString.append(imageAttributedString)
затем в функции, вызываемой действием tap:
@objc func onImageTap(_ sender: UITapGestureRecognizer) {
let textView = sender.view as! UITextView
let layoutManager = textView.layoutManager
// location of tap in textView coordinates
var location = sender.location(in: textView)
location.x -= textView.textContainerInset.left;
location.y -= textView.textContainerInset.top;
// character index at tap location
let characterIndex = layoutManager.characterIndex(for: location, in: textView.textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
// if index is valid
if characterIndex < textView.textStorage.length {
// check if the tap location has the custom attribute
let attributeValue = textView.attributedText.attribute(NSAttributedStringKey.imagePath, at: characterIndex, effectiveRange: nil) as? String
if let value = attributeValue {
print("You tapped on \(NSAttributedStringKey.imagePath) and the value is: \(value)")
}
}
}
Оттуда вы знаете, что касание было на изображении, и у вас есть координаты внутри рамки изображения, так что вы можете использовать эту комбинацию, чтобы выяснить, где на изображении было нажато.
Поскольку ответ Чарльза - отличный намек, я хотел бы осквернить свое приближение.
Мой способ не сильно отличается от него, вместо этого, чтобы добавить новый атрибут ключа, я использую оригинальное "вложение" в качестве ключа для получения изображения.
Итак, создайте массив изображений, обновляйте массив каждый раз, когда вы добавляете / удаляете изображения (изображения) (чтобы убедиться в правильном порядке изображений).
Создать просмотрщик изображений для просмотра изображений (вы можете искать из Интернета).
Используйте ответ Чарльза, чтобы обнаружить нажатие на изображение (я использую клавишу "вложение" вместо пользовательской клавиши).
После нажатия на изображение, открытия средства просмотра изображений и отображения текущего изображения вы должны проанализировать массив изображений для средства просмотра изображений, чтобы средство просмотра изображений могло отображать изображение в правильном порядке.
Вот фрагмент моего кода:
@objc func tapOnImage(_ sender: UITapGestureRecognizer) {
let textView = sender.view as! UITextView
let layoutManager = textView.layoutManager
var location = sender.location(in: textView)
location.x -= textView.textContainerInset.left
location.y -= memtextViewoView.textContainerInset.top
let characterIndex = layoutManager.characterIndex(for: location,
in: textView.textContainer,
fractionOfDistanceBetweenInsertionPoints: nil)
if characterIndex < textView.textStorage.length {
let attachment = textView.attributedText.attribute(NSAttributedStringKey.attachment,
at: characterIndex,
effectiveRange: nil) as? NSTextAttachment
if let attachImage = attachment {
print("tap on image: ", attachImage.image)
}
}
}
Из приведенного выше кода, вы можете найти, когда вы нажимаете на другое изображение, консоль покажет другой объект, оттуда вы можете использовать изображение делать все, что вам нужно.
Я надеюсь, что это может помочь людям, которые застряли на такого рода вопрос.
Кстати, я использую Swift 4.1 на Xcode 9,2