iOS Swift - 将属性应用于文本视图而不替换整个文本
iOS Swift - Applying attribute to text view without replacing whole text
我有一个 TextView,其中 select 一些文本并将属性应用到 selected 文本,成功了。
在使用所需更改更新 NSMutableAttributedString 后,我使用我的 TextView 并更新其属性文本:
textView.attributedText = NSMutableAttributedStringText // pseudo-example
但是这个属性替换了Text View的整个文本(保留了c之前的属性);
有没有什么方法可以只更新 textView.attributedText
更改,而不是每次进行更改时都替换整个文本?
我这周早些时候刚做的。
创建 attributedText
的可变副本,更新可变副本,创建更新字符串的不可变副本。
guard let text = textView.attributedText?.mutableCopy() as? NSMutableAttributedString else { return }
text.addAttribute(NSForegroundColorAttributeName, value: color, range: selectedRange)
textView.attributedText = text.copy() as? NSAttributedString
UITextView 的这个子类让您可以使用 UITextView 并访问后备存储的 NSMutableAttributeString,您可以在其中更新字符串范围。它还会动态刷新布局,而不必通过将新字符串分配给 UITextView.attributedString 来替换整个字符串,在这种情况下您将重新开始并丢失用户的文本选择。
您仍然可以设置 attributedText
来更新后备存储,但是如果您想修改字符串,请通过文本视图实例的 mutableAttributedText 属性 将其作为可变属性字符串访问,例如:
这让我很困惑,让我头疼了一天。例如,一旦您提供了自己的后备存储和布局,某些 UITextView 字段默认情况下就会变得残留,并且很难对其进行整理,所以我想我可以省去麻烦。
var textView = DynamicLayoutTextView()
textView.attributedText = NSAttributedString(string: "Now what?")
textView.mutableAttrText.setAttributes(attrs: attrs, range: range)
.
.
.
import UIKit
class DynamicLayoutTextView : UITextView {
var dynamicStorage = DynamicLayoutTextStorage()
override var attributedText : NSAttributedString? {
get { dynamicStorage }
set { dynamicStorage.setAttributedString(newValue!) }
}
var mutableAttributedText : NSMutableAttributedString? {
get { dynamicStorage }
}
class DynamicLayoutTextStorage : NSTextStorage {
let backingStore = NSMutableAttributedString()
override var string: String {
return backingStore.string
}
override func attributes(at location: Int,
effectiveRange range: NSRangePointer?) -> [NSAttributedString.Key : Any] {
let attributes = backingStore.attributes(at: location, effectiveRange: range)
return attributes
}
override func replaceCharacters(in range: NSRange, with str: String) {
beginEditing()
backingStore.replaceCharacters(in: range, with:str)
edited(.editedCharacters, range: range, changeInLength: (str as NSString).length - range.length)
endEditing()
}
override func setAttributes(_ attrs: [NSAttributedString.Key: Any]?, range: NSRange) {
beginEditing()
backingStore.setAttributes(attrs, range: range)
edited(.editedAttributes, range: range, changeInLength: 0)
endEditing()
}
}
required init?(coder: NSCoder) {
fatalError("Not designed to be constructed by storyboard")
}
override init(frame: CGRect, textContainer: NSTextContainer?) {
/*
* Note: We're bypassing UITextView's attributedString, textLayout, layoutManager and replacing it with
* components we manage. In the consumer, therefore it's important to update the dynamicStorage field to make changes
* to the data and attributes.
*/
textContainer!.widthTracksTextView = true
let layoutManager = NSLayoutManager()
layoutManager.addTextContainer(textContainer!)
super.init(frame: frame, textContainer: textContainer)
dynamicStorage.addLayoutManager(layoutManager)
}
convenience init(frame: CGRect) {
let textContainer = NSTextContainer(size: CGSize(width: frame.size.width, height: .greatestFiniteMagnitude))
self.init(frame: frame, textContainer: textContainer)
}
}
我有一个 TextView,其中 select 一些文本并将属性应用到 selected 文本,成功了。
在使用所需更改更新 NSMutableAttributedString 后,我使用我的 TextView 并更新其属性文本:
textView.attributedText = NSMutableAttributedStringText // pseudo-example
但是这个属性替换了Text View的整个文本(保留了c之前的属性);
有没有什么方法可以只更新 textView.attributedText
更改,而不是每次进行更改时都替换整个文本?
我这周早些时候刚做的。
创建 attributedText
的可变副本,更新可变副本,创建更新字符串的不可变副本。
guard let text = textView.attributedText?.mutableCopy() as? NSMutableAttributedString else { return }
text.addAttribute(NSForegroundColorAttributeName, value: color, range: selectedRange)
textView.attributedText = text.copy() as? NSAttributedString
UITextView 的这个子类让您可以使用 UITextView 并访问后备存储的 NSMutableAttributeString,您可以在其中更新字符串范围。它还会动态刷新布局,而不必通过将新字符串分配给 UITextView.attributedString 来替换整个字符串,在这种情况下您将重新开始并丢失用户的文本选择。
您仍然可以设置 attributedText
来更新后备存储,但是如果您想修改字符串,请通过文本视图实例的 mutableAttributedText 属性 将其作为可变属性字符串访问,例如:
这让我很困惑,让我头疼了一天。例如,一旦您提供了自己的后备存储和布局,某些 UITextView 字段默认情况下就会变得残留,并且很难对其进行整理,所以我想我可以省去麻烦。
var textView = DynamicLayoutTextView()
textView.attributedText = NSAttributedString(string: "Now what?")
textView.mutableAttrText.setAttributes(attrs: attrs, range: range)
.
.
.
import UIKit
class DynamicLayoutTextView : UITextView {
var dynamicStorage = DynamicLayoutTextStorage()
override var attributedText : NSAttributedString? {
get { dynamicStorage }
set { dynamicStorage.setAttributedString(newValue!) }
}
var mutableAttributedText : NSMutableAttributedString? {
get { dynamicStorage }
}
class DynamicLayoutTextStorage : NSTextStorage {
let backingStore = NSMutableAttributedString()
override var string: String {
return backingStore.string
}
override func attributes(at location: Int,
effectiveRange range: NSRangePointer?) -> [NSAttributedString.Key : Any] {
let attributes = backingStore.attributes(at: location, effectiveRange: range)
return attributes
}
override func replaceCharacters(in range: NSRange, with str: String) {
beginEditing()
backingStore.replaceCharacters(in: range, with:str)
edited(.editedCharacters, range: range, changeInLength: (str as NSString).length - range.length)
endEditing()
}
override func setAttributes(_ attrs: [NSAttributedString.Key: Any]?, range: NSRange) {
beginEditing()
backingStore.setAttributes(attrs, range: range)
edited(.editedAttributes, range: range, changeInLength: 0)
endEditing()
}
}
required init?(coder: NSCoder) {
fatalError("Not designed to be constructed by storyboard")
}
override init(frame: CGRect, textContainer: NSTextContainer?) {
/*
* Note: We're bypassing UITextView's attributedString, textLayout, layoutManager and replacing it with
* components we manage. In the consumer, therefore it's important to update the dynamicStorage field to make changes
* to the data and attributes.
*/
textContainer!.widthTracksTextView = true
let layoutManager = NSLayoutManager()
layoutManager.addTextContainer(textContainer!)
super.init(frame: frame, textContainer: textContainer)
dynamicStorage.addLayoutManager(layoutManager)
}
convenience init(frame: CGRect) {
let textContainer = NSTextContainer(size: CGSize(width: frame.size.width, height: .greatestFiniteMagnitude))
self.init(frame: frame, textContainer: textContainer)
}
}