You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository has been archived by the owner on Oct 30, 2018. It is now read-only.
I submitted PR #657 to allow autocomplete to submit attributed strings. The goal is to implement mentions in a more seamless manner. Here's a short gif showing improved handling. I think the majority of this could be incorporated into the framework if there is desire.
Here's my current implementation. Apologies if you don't know Swift; it should not be too hard to translate back to Objective C.
First, a global constant: let MentionAttributeStringKey = NSAttributedStringKey(rawValue: "UserMention")
A helper to generate the attributed text tags for a new mention token
The meat of this feature is additional logic for SLKTextView's shouldChangeTextIn delegate. Here I override the implementation, and fall through to super if no mention token is being modified.
override func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
guard let textView = textView as? SLKTextView else {
return true
}
var shouldReplace = false
var tokenAttrRange = NSRange()
var currentReplacementRange = range
if range.length == 0 {
// determine if user is inserting or deleting at the start of the string, with a token at the start
if range.location == 0 &&
textView.attributedText.length > 0 &&
nil != textView.attributedText.attribute(MentionAttributeStringKey, at: range.location, effectiveRange: &tokenAttrRange) {
currentReplacementRange = NSUnionRange(currentReplacementRange, tokenAttrRange)
shouldReplace = true
}
// determine if user is inserting text at the end of the string, with a token at the end
else if range.location > 0 &&
nil != textView.attributedText.attribute(MentionAttributeStringKey, at: range.location - 1, effectiveRange: &tokenAttrRange) {
currentReplacementRange = NSUnionRange(currentReplacementRange, tokenAttrRange)
shouldReplace = true
}
} else {
// search the range for any instances of the desired text attribute
textView.attributedText.enumerateAttribute(MentionAttributeStringKey, in: range,
options: .longestEffectiveRangeNotRequired, using: { (value, attrRange, stop) in
// get the attribute's full range and merge it with the original
if nil != textView.attributedText.attribute(MentionAttributeStringKey, at: attrRange.location, effectiveRange: &tokenAttrRange) {
currentReplacementRange = NSUnionRange(currentReplacementRange, tokenAttrRange)
shouldReplace = true
}
})
}
if shouldReplace {
// remove the token attr, and then replace the characters with the input str (which can be empty on a backspace)
let mutableAttributedText = textView.attributedText.mutableCopy() as! NSMutableAttributedString
let mentionAttrs = mentionAttributes()
for (key, _) in mentionAttrs {
mutableAttributedText.removeAttribute(key, range: currentReplacementRange)
}
// replace the text with the user's input, unless at the beginning of the line and the user hit backspace
if text.lengthOfBytes(using: .utf8) <= 0 && currentReplacementRange.location == 0 && currentReplacementRange.length >= mutableAttributedText.length {
mutableAttributedText.replaceCharacters(in: currentReplacementRange, with: " ")
} else {
mutableAttributedText.replaceCharacters(in: currentReplacementRange, with: text)
}
// update the string and set the cursor position to the end of the edited location
textView.attributedText = mutableAttributedText
if let cursorPosition = textView.position(from: textView.beginningOfDocument, offset: currentReplacementRange.location + text.lengthOfBytes(using: .utf8)) {
textView.selectedTextRange = textView.textRange(from: cursorPosition, to: cursorPosition)
}
return false
}
return super.textView(textView, shouldChangeTextIn: range, replacementText: text)
}
After a token is detected (by looking for the custom MentionAttributeStringKey I first strip off all added attributes. This helps prevent styling future text entry incorrectly. Then I replace the selection with the desired text (unless they hit backspace-- a completely empty textView seems to incorrectly preserve some undesired text attributes, so I insert a space). Finally, place the cursor within the updated string.
The next piece of the puzzle is to update your didSelectRowAt, where the code processes selection of autocomplete entries:
var mentionUser = "username"
var mentionName = "full name"
let range = NSMakeRange(0, mentionName.lengthOfBytes(using: .utf8))
let tokenStr = NSMutableAttributedString(string: mentionName)
let spaceStr = NSMutableAttributedString(string: " ")
// copy current text attributes. if at the end of the string, back up one character
if textView.attributedText.length > 0 {
var cursorPosition = textView.selectedRange.location
if cursorPosition >= textView.attributedText.length {
cursorPosition = textView.attributedText.length - 1
}
var attrRange = NSRange()
let attrs = textView.attributedText.attributes(at: cursorPosition, effectiveRange: &attrRange)
tokenStr.addAttributes(attrs, range: range)
spaceStr.addAttributes(attrs, range: NSMakeRange(0, 1))
}
// apply desired @mention token attributes, using helper function above
let mentionAttrs = mentionAttributes(mentionUser)
tokenStr.addAttributes(mentionAttrs, range: range)
// and finally, add a space at the end of the token using the original text attributes
tokenStr.append(spaceStr)
self.acceptAutoCompletion(attributedString: tokenStr, keepPrefix: false)
After this, things will become more customized based on your application and messaging protocol. When the user hits send, use textView.attributedText.enumerateAttribute to enumerate mentions and handle accordingly. You may probably need to do text substitutions or some other protocol-specific processing, both for sending the message and for displaying in text bubbles.
I hope this is useful for folks, and if the repo maintainers are interested in incorporating it, I'm happy to be involved.
The text was updated successfully, but these errors were encountered:
I submitted PR #657 to allow autocomplete to submit attributed strings. The goal is to implement mentions in a more seamless manner. Here's a short gif showing improved handling. I think the majority of this could be incorporated into the framework if there is desire.
Here's my current implementation. Apologies if you don't know Swift; it should not be too hard to translate back to Objective C.
First, a global constant:
let MentionAttributeStringKey = NSAttributedStringKey(rawValue: "UserMention")
A helper to generate the attributed text tags for a new mention token
The meat of this feature is additional logic for SLKTextView's shouldChangeTextIn delegate. Here I override the implementation, and fall through to super if no mention token is being modified.
After a token is detected (by looking for the custom
MentionAttributeStringKey
I first strip off all added attributes. This helps prevent styling future text entry incorrectly. Then I replace the selection with the desired text (unless they hit backspace-- a completely empty textView seems to incorrectly preserve some undesired text attributes, so I insert a space). Finally, place the cursor within the updated string.The next piece of the puzzle is to update your didSelectRowAt, where the code processes selection of autocomplete entries:
After this, things will become more customized based on your application and messaging protocol. When the user hits send, use
textView.attributedText.enumerateAttribute
to enumerate mentions and handle accordingly. You may probably need to do text substitutions or some other protocol-specific processing, both for sending the message and for displaying in text bubbles.I hope this is useful for folks, and if the repo maintainers are interested in incorporating it, I'm happy to be involved.
The text was updated successfully, but these errors were encountered: