当通过 attributedText 值更改添加 lineSpacing 时,TextView UIViewRepresentable 重置 UndoManager
TextView UIViewRepresentable resets UndoManager when lineSpacing added via attributedText value change
我正在使用此处概述的 TextView UIViewRepresentable https://www.appcoda.com/swiftui-textview-uiviewrepresentable/。
除行间距问题外,一切正常。 SwiftUI的lineSpacing修饰符好像对它没有影响。因此,我通过将以下内容添加到 UIViewRepresentable 来解决这个问题
func updateUIView(_ uiView: UITextView, context: Context)
:
let style = NSMutableParagraphStyle()
style.lineSpacing = 4
let attributes = [NSAttributedString.Key.paragraphStyle : style]
uiView.attributedText = NSAttributedString(string: self.text, attributes:attributes)
这是 Adjusting the line spacing of UITextView 的建议。
因此,完整的函数如下所示:
func updateUIView(_ uiView: UITextView, context: Context) {
uiView.text = text
// line spacing
let style = NSMutableParagraphStyle()
style.lineSpacing = 4
let attributes = [NSAttributedString.Key.paragraphStyle : style]
uiView.attributedText = NSAttributedString(string: self.text, attributes:attributes)
}
这可以完成工作,但会导致 UndoManager 重置。也就是说,只要我做了任何更改,UndoManager 就不会相信有任何事情可以撤消(或重做)。根据我的搜索,这似乎是更改 attributedText 值的一般副作用。我想知道是否有可用的解决方法,是对我的方法进行调整还是实现 lineSpacing 而不 重置 UndoManager 状态的完全不同的方法。
更新:尝试了 Asperi 的建议,但结果喜忧参半。
这是TextView和对应Coordinator的完整代码:
import SwiftUI
struct TextView: UIViewRepresentable {
// MARK: Bindings
@Binding var text: String
@Binding var textStyle: UIFont.TextStyle
// MARK: -
// MARK: Functions
func makeUIView(context: Context) -> UITextView {
let textView = UITextView()
textView.backgroundColor = UIColor.clear
textView.delegate = context.coordinator
textView.autocapitalizationType = .sentences
textView.isSelectable = true
textView.isUserInteractionEnabled = true
textView.adjustsFontForContentSizeCategory = true
return textView
}
func makeCoordinator() -> Coordinator {
Coordinator($text)
}
func updateUIView(_ uiView: UITextView, context: Context) {
let storage = uiView.textStorage
storage.beginEditing()
// line spacing
let style = NSMutableParagraphStyle()
style.lineSpacing = 4
let attributes = [NSAttributedString.Key.paragraphStyle : style]
storage.replaceCharacters(in: NSRange(location: 0, length: storage.length),
with: NSAttributedString(string: self.text, attributes:attributes))
storage.endEditing()
}
// MARK: -
// MARK: Internal classes
class Coordinator: NSObject, UITextViewDelegate {
// MARK: Local
var text: Binding<String>
// MARK: -
init(_ text: Binding<String>) {
self.text = text
}
// MARK: -
// MARK: Functions
func textViewDidChange(_ textView: UITextView) {
self.text.wrappedValue = textView.text
}
}
}
如果我在 textViewDidChange 中保留 self.text.wrappedValue = textView.text(取自顶部链接的 AppCoda 教程),则推荐无效。但是,如果我删除它,它似乎可以工作,但还有其他问题,每当刷新视图(我认为)时,文本会自动重置为会话开始时的原始状态——例如,如果我尝试切换到另一个应用程序,我可以在这样做之前看到文本重置,或者当我打开一块 UI 时,例如,它降低了 TextView 的不透明度。
由于 Ivan 在 中的回答,我可能偶然发现了正确的方法。
在 func makeUIView(context: Context) -> UITextView
中,我需要为 TextView UIViewRepresentable 添加:
let spacing = NSMutableParagraphStyle()
spacing.lineSpacing = 4
let attr = [NSAttributedString.Key.paragraphStyle : spacing]
textView.typingAttributes = attr
我正在使用此处概述的 TextView UIViewRepresentable https://www.appcoda.com/swiftui-textview-uiviewrepresentable/。
除行间距问题外,一切正常。 SwiftUI的lineSpacing修饰符好像对它没有影响。因此,我通过将以下内容添加到 UIViewRepresentable 来解决这个问题
func updateUIView(_ uiView: UITextView, context: Context)
:
let style = NSMutableParagraphStyle()
style.lineSpacing = 4
let attributes = [NSAttributedString.Key.paragraphStyle : style]
uiView.attributedText = NSAttributedString(string: self.text, attributes:attributes)
这是 Adjusting the line spacing of UITextView 的建议。
因此,完整的函数如下所示:
func updateUIView(_ uiView: UITextView, context: Context) {
uiView.text = text
// line spacing
let style = NSMutableParagraphStyle()
style.lineSpacing = 4
let attributes = [NSAttributedString.Key.paragraphStyle : style]
uiView.attributedText = NSAttributedString(string: self.text, attributes:attributes)
}
这可以完成工作,但会导致 UndoManager 重置。也就是说,只要我做了任何更改,UndoManager 就不会相信有任何事情可以撤消(或重做)。根据我的搜索,这似乎是更改 attributedText 值的一般副作用。我想知道是否有可用的解决方法,是对我的方法进行调整还是实现 lineSpacing 而不 重置 UndoManager 状态的完全不同的方法。
更新:尝试了 Asperi 的建议,但结果喜忧参半。
这是TextView和对应Coordinator的完整代码:
import SwiftUI
struct TextView: UIViewRepresentable {
// MARK: Bindings
@Binding var text: String
@Binding var textStyle: UIFont.TextStyle
// MARK: -
// MARK: Functions
func makeUIView(context: Context) -> UITextView {
let textView = UITextView()
textView.backgroundColor = UIColor.clear
textView.delegate = context.coordinator
textView.autocapitalizationType = .sentences
textView.isSelectable = true
textView.isUserInteractionEnabled = true
textView.adjustsFontForContentSizeCategory = true
return textView
}
func makeCoordinator() -> Coordinator {
Coordinator($text)
}
func updateUIView(_ uiView: UITextView, context: Context) {
let storage = uiView.textStorage
storage.beginEditing()
// line spacing
let style = NSMutableParagraphStyle()
style.lineSpacing = 4
let attributes = [NSAttributedString.Key.paragraphStyle : style]
storage.replaceCharacters(in: NSRange(location: 0, length: storage.length),
with: NSAttributedString(string: self.text, attributes:attributes))
storage.endEditing()
}
// MARK: -
// MARK: Internal classes
class Coordinator: NSObject, UITextViewDelegate {
// MARK: Local
var text: Binding<String>
// MARK: -
init(_ text: Binding<String>) {
self.text = text
}
// MARK: -
// MARK: Functions
func textViewDidChange(_ textView: UITextView) {
self.text.wrappedValue = textView.text
}
}
}
如果我在 textViewDidChange 中保留 self.text.wrappedValue = textView.text(取自顶部链接的 AppCoda 教程),则推荐无效。但是,如果我删除它,它似乎可以工作,但还有其他问题,每当刷新视图(我认为)时,文本会自动重置为会话开始时的原始状态——例如,如果我尝试切换到另一个应用程序,我可以在这样做之前看到文本重置,或者当我打开一块 UI 时,例如,它降低了 TextView 的不透明度。
由于 Ivan 在 中的回答,我可能偶然发现了正确的方法。
在 func makeUIView(context: Context) -> UITextView
中,我需要为 TextView UIViewRepresentable 添加:
let spacing = NSMutableParagraphStyle()
spacing.lineSpacing = 4
let attr = [NSAttributedString.Key.paragraphStyle : spacing]
textView.typingAttributes = attr