我有一个名为 ExpandableText 的可重用结构,它可以让我缩短给它的文本(因此,如果我将其限制为两行,它会添加阅读更多内容并展开),但是当文本变量在不同视图上更改时(因此文本是编辑)它识别更改但不更新变量。当我在 ExpandableText 视图中放置 onChange 并打印
newValue
和 text
值时,newValue
更新为其他视图中的更改,但 text
值仍保留旧值。我尝试将 text 设置为状态变量,然后 onChange 将其分配给 newValue,但这会导致 onChange 甚至不会被调用。我也搞乱了,但无法让 @Binding var text: String
与 init 中设置的 ShrinkText 初始值一起使用。从我读到的内容来看,这似乎是最好的选择,但我不断收到错误,无法在 _shrinkText init 中将 Binding<String>
分配给 string
。
import SwiftUI
struct ExpandableText: View {
@State private var expanded: Bool = false
@State private var truncated: Bool = false
@State private var textChanged: Bool = false
@State private var shrinkText: String
private var text: String
let font: UIFont
let lineLimit: Int
private var moreLessText: String {
if !truncated {
return ""
} else {
return self.expanded ? "Read Less" : "Read More"
}
}
init(_ text: String, lineLimit: Int, font: UIFont = UIFont(name: "Manrope-Regular", size: 16)!) {
self.text = text
self.lineLimit = lineLimit
_shrinkText = State(wrappedValue: text)
self.font = font
}
var body: some View {
VStack(alignment: .leading) {
Text(self.expanded ? text : shrinkText)
.allowsTightening(true)
.fixedSize(horizontal: false, vertical: true)
.font(Font.custom("Manrope-Regular", size: 16))
.dynamicTypeSize(...DynamicTypeSize.large)
if truncated {
Button {
withAnimation(.easeInOut) {
expanded.toggle()
}
} label: {
Text(moreLessText)
.font(Font.custom("Manrope-SemiBold", size: 16))
.dynamicTypeSize(...DynamicTypeSize.large)
}
}
}
.lineLimit(expanded ? nil : lineLimit)
.onChange(of: text) { newValue in
print("text value: \(text)")
print("new value: \(newValue)")
}
.background(
Text(text).lineLimit(lineLimit)
.background(GeometryReader { visibleTextGeometry in
Color.purple.onAppear() {
let size = CGSize(width: visibleTextGeometry.size.width, height: .greatestFiniteMagnitude)
let attributes:[NSAttributedString.Key:Any] = [NSAttributedString.Key.font: font]
var low = 0
var heigh = shrinkText.count
var mid = heigh
while ((heigh - low) > 1) {
let attributedText = NSAttributedString(string: shrinkText + moreLessText, attributes: attributes)
let boundingRect = attributedText.boundingRect(with: size, options: NSStringDrawingOptions.usesLineFragmentOrigin, context: nil)
if boundingRect.size.height > visibleTextGeometry.size.height {
truncated = true
heigh = mid
mid = (heigh + low)/2
} else {
if mid == text.count {
break
} else {
low = mid
mid = (low + heigh)/2
}
}
shrinkText = String("\(text.prefix(mid))...")
}
}
})
.hidden()
)
.font(Font.custom("Manrope-Regular", size: 16)).dynamicTypeSize(...DynamicTypeSize.large)
}
}
更新初始化:
struct ExpandableText: View {
@State private var expanded: Bool = false
@State private var truncated: Bool = false
@State private var textChanged: Bool = false
@State private var shrinkText: String
@Binding var text: String
let font: UIFont
let lineLimit: Int
private var moreLessText: String {
if !truncated {
return ""
} else {
return self.expanded ? "Read Less" : "Read More"
}
}
init(_ text: Binding<String>, lineLimit: Int, font: UIFont = UIFont(name: "Manrope-Regular", size: 16)!) {
self._text = text
self.lineLimit = lineLimit
_shrinkText = State(wrappedValue: text) //<-- error: "Cannot convert value of type 'Binding<String>' to expected argument type 'String'"
self.font = font
}
...
}
为了使
text
可从另一个视图更新,您需要将其转换为绑定。为此,您需要进行一些更改。
更换
private var text: String
在你的结构中使用 @Binding var text : String
。
将结构中的
init(_ text: String, …)
替换为init(_ text: Binding<String>, …)
。
在初始化程序中,使用代码
text
分配变量 self._text = text
请注意名称前面的下划线 - 这是避免您描述的错误消息所需要的。 这个 StackOverflow 问题 详细讨论了下划线语法的含义。
在
ExpandableText
视图的父视图中(我假设您想要修改 text
,因为 SwiftUI 通常需要将数据从父级传递到子级),添加一个状态属性: @State private var actualText : String = “Hello World!”
actualText
的内容可以在父级内部发生变异,例如响应轻击手势。
在
actualText
子视图中使用 ExpandableText
和 ExpandableText($actualText, …)
,其中省略号将替换为其他参数。 SwiftUI 中的 $ 符号用于获取状态变量的绑定,以便该变量可以与视图的子级共享,如本用例所示。
希望这有帮助。