Running into an issue when Text won't expand with attributed string or when inside a sheet. In other words, it will expand when it's using regular string, or if it's not inside a sheet.
Anyone know a good walk-around or an explanation?
import SwiftUI
struct ExpandableAttributedText: View {
let string: String
@State private var isExpanded = false
let maxLines: Int
@Environment(\.colorScheme) private var colorScheme
var body: some View {
VStack(alignment: .leading, spacing: 8) {
Text(attributedString)
// update the it to below and it will start expanding
// Text(string)
.lineLimit(isExpanded ? nil : maxLines)
.textSelection(.enabled)
Button(action: {
isExpanded.toggle()
}) {
Text(isExpanded ? "Show Less" : "Show More")
.font(.caption)
.foregroundColor(.primary)
}
}
}
private var attributedString: AttributedString {
var attributed = htmlToAttributedString(string) ?? AttributedString(string)
attributed.font = .system(size: 16)
attributed.foregroundColor = colorScheme == .dark ? .white : .black
return attributed
}
func htmlToAttributedString(_ htmlString: String) -> AttributedString? {
guard let data = htmlString.data(using: .utf8) else {
print("Failed to convert HTML string to data")
return nil
}
do {
let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [
.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue
]
let nsAttributedString = try NSAttributedString(data: data, options: options, documentAttributes: nil)
// Convert NSAttributedString to AttributedString
return AttributedString(nsAttributedString)
} catch {
print("Error converting HTML to AttributedString: \(error)")
return nil
}
}
}
struct ContentView: View {
@State private var showModel: Bool = false
var text = """
<a href="http://n.pr/PM-digital"><em>in Apple Podcasts</em></a><em> or at </em><a href="https://n.pr/3HlREPz"><em>plus.npr.org/planetmoney</em></a><em>.</em><br/><br/>Learn more about sponsor message choices: <a href="https://podcastchoices.com/adchoices">podcastchoices.com/adchoices</a><br/><br/><a href="https://www.npr.org/about-npr/179878450/privacy-policy">NPR Privacy Policy</a>
"""
var body: some View {
VStack{
Text("The following will expand")
ExpandableAttributedText(string: text, maxLines: 4)
.padding()
Button(action: {
showModel.toggle()
}) {
Text("Open")
}
}
.sheet(isPresented: $showModel, content: {
ScrollView{
// This won't expand with attributedString, as it's in a sheet.
ExpandableAttributedText(string: text, maxLines: 4)
.padding()
}
})
}
}
#Preview {
ContentView()
}
To fix your issue, try this approach, letting the ExpandableAttributedText
View manage
the state of a var, @State private var attributedText ...
instead of using a computed property, as shown in the code.
struct ExpandableAttributedText: View {
let string: String
let maxLines: Int
@State private var isExpanded = false
@State private var attributedText = AttributedString("") // <--- here
@Environment(\.colorScheme) private var colorScheme
var body: some View {
VStack(alignment: .leading, spacing: 8) {
Text(attributedText) // <--- here
.lineLimit(isExpanded ? nil : maxLines)
.textSelection(.enabled)
Button(action: {
isExpanded.toggle()
}) {
Text(isExpanded ? "Show Less" : "Show More")
.font(.caption)
.foregroundColor(.blue)
}
}
.onAppear { // <--- here
attributedText = htmlToAttributedString(string) ?? AttributedString(string)
attributedText.font = .system(size: 16)
attributedText.foregroundColor = colorScheme == .dark ? .white : .black
}
}
func htmlToAttributedString(_ htmlString: String) -> AttributedString? {
guard let data = htmlString.data(using: .utf8) else {
print("Failed to convert HTML string to data")
return nil
}
do {
let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [
.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue
]
let nsAttributedString = try NSAttributedString(data: data, options: options, documentAttributes: nil)
// Convert NSAttributedString to AttributedString
return AttributedString(nsAttributedString)
} catch {
print("Error converting HTML to AttributedString: \(error)")
return nil
}
}
}
struct ContentView: View {
@State private var showModel: Bool = false
let text = """
<a href="http://n.pr/PM-digital"><em>in Apple Podcasts</em></a><em> or at </em><a href="https://n.pr/3HlREPz"><em>plus.npr.org/planetmoney</em></a><em>.</em><br/><br/>Learn more about sponsor message choices: <a href="https://podcastchoices.com/adchoices">podcastchoices.com/adchoices</a><br/><br/><a href="https://www.npr.org/about-npr/179878450/privacy-policy">NPR Privacy Policy</a>
"""
var body: some View {
VStack{
Text("The following will expand")
ExpandableAttributedText(string: text, maxLines: 4)
.padding()
Button(action: {
showModel.toggle()
}) {
Text("Open")
}
}
.sheet(isPresented: $showModel) {
ScrollView{
ExpandableAttributedText(string: text, maxLines: 4)
.padding()
}
}
}
}