Swift:在UITableViewCell的UITextView中插入复选框,并使用UITapGestureRecog



简介

上下文:

我正在开发我的第一个自己的应用程序之一,一种记笔记的应用程序。

基本框架很简单:我有一个UITableView,其中有单元格,其中包含一些设计元素和一个大型UITextView,用户可以在其中编写笔记。

问题:

  • 我想实现复选框功能,类似于Apple在其应用程序"Notes"中具有的功能。我希望它成为文本的"一部分",因此可以通过点击键盘上的擦除来擦除它。

  • 我已经检查了SO上关于将字符插入UITextView的帖子,该帖子有效(请参阅下面的代码(,但是在实际点击识别方面,它们的目标有所不同。但是我不知道如何检查水龙头是否在我的NSAttributedString上。

问题:

1. 当用户点击复选框时,如何换出组成复选框的字符?2. 如何让 UITapGestureRecognizer 在我的文本视图上正常工作?另请参阅编辑

编辑:

旧编辑问题:(通过silicon_valleys答案解决(

  • 我的 UITapGestureRecognizer 无法按预期工作,它似乎没有响应点击。
  • 如何检查我的复选框是否已点击并用复选标记替换字符?
  • 如何仅在用户光标所在的行的开头插入复选框?

较小的新问题:

  • 点击识别器现在可以完美运行。但是我找不到通往 1 的方法。将 NSRange 转换为 UITextRange,以便我可以替换字符或 2。使用 NSRange 在文本视图中插入一个字符。

法典:

  • 复选框插入方法toolbarCheckbox(sender: UIBarButtonItem)

    //This method is in my ViewController and adds a checkbox
    @objc func toolbarCheckbox(sender: UIBarButtonItem) {
    let checkboxCharacter: Character = "u{25EF}"
    let emptyCheckbox = " (checkboxCharacter)  "
    guard case let cell as EventsCell = tableView.cellForRow(at: sender.indexPathID!) else {return}
    var beginningOfLineForSelection = cell.eventText.selectedTextRange.flatMap {
    cell.eventText.selectionRects(for: $0).first as? UITextSelectionRect
    }?.rect.origin ?? .zero
    beginningOfLineForSelection.x = 0
    let layoutManager = cell.eventText.layoutManager
    let firstGlyphOnLine = layoutManager.glyphIndex(
    for: beginningOfLineForSelection,
    in: cell.eventText.textContainer,
    fractionOfDistanceThroughGlyph: nil)
    let newText = NSMutableAttributedString(attributedString: cell.eventText.attributedText)
    newText.insert(NSAttributedString(string: emptyCheckbox, attributes: [NSAttributedStringKey.font : UIFont(name: "Didot", size: 16)!]), at: firstGlyphOnLine)
    cell.eventText.attributedText = newText
    } 
    
  • UITextView 创建(在我的单元格类中(

    let eventText : GrowingTextView = {
    let tv = GrowingTextView(frame: .zero)
    let uncheckedBox = NSMutableAttributedString(string: checkboxCharacter, attributes: [NSAttributedStringKey.font : UIFont(name: "Didot", size: 16)!])
    uncheckedBox.append(NSMutableAttributedString(string: checkedBoxCharacter, attributes: [NSAttributedStringKey.font : UIFont(name: "Didot", size: 16)!]))
    tv.attributedText = uncheckedBox
    tv.allowsEditingTextAttributes = true
    tv.isScrollEnabled = false
    tv.textContainerInset = UIEdgeInsetsMake(1, 1, 0, 1)
    tv.translatesAutoresizingMaskIntoConstraints = false
    tv.backgroundColor = .clear
    return tv
    }()
    
  • UITapGestureRecognizer action:

    @objc func checkboxTapDone(sender: UITapGestureRecognizer) {
    guard let cell = tableView.cellForRow(at: sender.indexPathID!) as? EventsCell else { return }
    let layoutManager = cell.eventText.layoutManager
    var location = sender.location(in: cell.eventText)
    location.x -= cell.eventText.textContainerInset.left
    location.y -= cell.eventText.textContainerInset.top
    let textTapped = layoutManager.glyphIndex(for: location, in: cell.eventText.textContainer, fractionOfDistanceThroughGlyph: nil)
    let substring = (cell.eventText.attributedText.string as NSString).substring(with: NSMakeRange(textTapped, 1))
    if substring == uncheckedBox {
    }
    else if substring == checkedBox {
    }
    }
    

感谢您阅读我的帖子。

不能在UITextView的文本中间添加UIButton或其他UIControl。不过,您可以做的是使用UITextViewattributedString属性来呈现复选框字符。为此,您可以使用与复选框匹配的字符的自定义字体(选中和未选中(。您需要向UITextView添加UITapGestureRecognizer,然后转换触摸点以检测是否点击了复选框,并将复选框字符从选中复选框更改为未选中,反之亦然。您还需要记住,您必须在UITapGestureRecognizer上设置一个委托,以确保它允许与移动光标的正常触摸同时触摸,并且仅在点击位于复选框符号上时才拦截点击。

您可以在键盘上方的工具栏中添加一个按钮,该按钮会将复选框符号添加到文本中(请参阅此解决方案:如何在键盘上方添加工具栏?

我希望这有所帮助。

回答您的更多问题

  • 我的UITapGestureRecognizer没有按预期工作,它似乎没有响应水龙头。

    这是因为您尝试在所有文本视图上使用单个笔势识别器。您应该为每个UITextView创建一个新UITapGestureRecognizer。现在,当您将 添加到以后的视图中时,它将从以前的视图中删除(因此它只会检测最后一个取消排队的单元格上的点击(。

  • 如何检查我的复选框是否已点击并用复选标记替换字符?

    checkboxTapDone(sender:)里面的代码应该处理这个问题。您大部分都在那里,但您正在创建的范围应该只有 1 个字符,而不是 9 个字符。然后,您需要将substring值与选中和未选中字符的值进行比较。然后用相反的字符更新eventText.attributedText

  • 如何仅在用户光标所在的行的开头插入复选框?

    以下代码片段应帮助您确定可以插入复选框字符的范围。

var beginningOfLineForSelection = textView.selectedTextRange.flatMap {    
textView.selectionRects(for: $0).first as? UITextSelectionRect 
}?.rect.origin ?? .zero
beginningOfLineForSelection.x = 0

let layoutManager = textView.layoutManager
let firstGlyphOnLine = layoutManager.glyphIndex(
for: beginningOfLineForSelection,
in: textView.textContainer,
fractionOfDistanceThroughGlyph: nil
)
let insertCheckboxRange = NSMakeRange(firstGlyphOnLine, 0)

回答较小的新问题

  • 您没有看到replaceCharacters(in:with:)方法(该方法将NSRange作为其第一个参数(的原因是您使用的是NSAttributedString(它不可变(。您需要首先创建attributedText属性的可变副本。然后,您可以更改文本并在UITextView上再次设置。有关示例,请参阅下面的代码片段。
let newText = NSMutableAttributedString(attributedString: textView.attributedText)
newText.replaceCharacters(in: rangeOfCharactersToReplace, with: newCharacters)
textView.attributedText = newText 

最新更新