Parse content at profile load to allow for better text layout
This commit is contained in:
parent
c42537d885
commit
c76c6fd0f1
3 changed files with 68 additions and 39 deletions
|
@ -54,6 +54,11 @@ struct Post: Decodable, Identifiable {
|
|||
}
|
||||
}
|
||||
|
||||
struct ParsedPostContainer {
|
||||
let post: Post
|
||||
let contentAttributed: NSAttributedString
|
||||
}
|
||||
|
||||
let mediaURL = "https://homepages.cae.wisc.edu/~ece533/images/airplane.png"
|
||||
|
||||
let testMedia = Media(id: 0,
|
||||
|
|
|
@ -7,44 +7,60 @@ extension String {
|
|||
}
|
||||
}
|
||||
|
||||
/// A custom view to use NSAttributedString in SwiftUI
|
||||
struct AttributedText: UIViewRepresentable {
|
||||
final class AttributedTextComponent: UIViewRepresentable {
|
||||
let string: NSAttributedString
|
||||
|
||||
init(_ string: NSAttributedString) {
|
||||
self.string = string
|
||||
}
|
||||
|
||||
public func makeUIView(context: UIViewRepresentableContext<AttributedTextComponent>) -> UILabel {
|
||||
UILabel()
|
||||
}
|
||||
|
||||
func updateUIView(_ uiView: UILabel, context: Context) {
|
||||
|
||||
uiView.backgroundColor = .clear
|
||||
uiView.numberOfLines = 0
|
||||
uiView.lineBreakMode = .byWordWrapping
|
||||
uiView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
||||
uiView.attributedText = string
|
||||
}
|
||||
}
|
||||
|
||||
let html: String
|
||||
struct AttributedText: View {
|
||||
let html: NSAttributedString
|
||||
let component: AttributedTextComponent
|
||||
|
||||
init(_ html: String) {
|
||||
@State var height: CGFloat = 0.0
|
||||
|
||||
init(_ html: NSAttributedString) {
|
||||
self.html = html
|
||||
self.component = AttributedTextComponent(html)
|
||||
}
|
||||
|
||||
public func makeUIView(context: UIViewRepresentableContext<AttributedText>) -> UILabel {
|
||||
let textView = UILabel()
|
||||
textView.backgroundColor = .clear
|
||||
textView.lineBreakMode = .byWordWrapping
|
||||
textView.numberOfLines = 0
|
||||
textView.lineBreakMode = .byWordWrapping
|
||||
textView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
||||
var body: some View {
|
||||
component.onAppear {
|
||||
let label = UILabel()
|
||||
|
||||
DispatchQueue.main.async {
|
||||
let data = Data(self.html.utf8)
|
||||
if let attributedString = try? NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil) {
|
||||
textView.attributedText = attributedString
|
||||
}
|
||||
}
|
||||
label.numberOfLines = 0
|
||||
label.lineBreakMode = .byWordWrapping
|
||||
label.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
||||
|
||||
return textView
|
||||
label.attributedText = self.html
|
||||
label.sizeToFit()
|
||||
|
||||
self.height = label.frame.height + 15.0 // for padding
|
||||
}.frame(minWidth: 0.0, maxWidth: .infinity, minHeight: 0.0, maxHeight: height)
|
||||
}
|
||||
}
|
||||
|
||||
struct PostView: View {
|
||||
let post: Post
|
||||
let post: ParsedPostContainer
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
HStack {
|
||||
RemoteImage(type: .url(URL(string: post.avatarUrl.encodeUrl()!)!), errorView: { error in
|
||||
RemoteImage(type: .url(URL(string: post.post.avatarUrl.encodeUrl()!)!), errorView: { error in
|
||||
Text(error.localizedDescription)
|
||||
}, imageView: { image in
|
||||
image
|
||||
|
@ -55,12 +71,12 @@ struct PostView: View {
|
|||
}).frame(width: 50.0, height: 50.0).padding(.leading)
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
if post.isReblogged() {
|
||||
Text(post.username + " reblogged from " + post.originalUsername!).foregroundColor(.gray)
|
||||
if post.post.isReblogged() {
|
||||
Text(post.post.username + " reblogged from " + post.post.originalUsername!).foregroundColor(.gray)
|
||||
}
|
||||
|
||||
if post.getTitle() != nil {
|
||||
Text(post.getTitle()!)
|
||||
if post.post.getTitle() != nil {
|
||||
Text(post.post.getTitle()!)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,7 +84,7 @@ struct PostView: View {
|
|||
}.frame(maxWidth: .infinity)
|
||||
|
||||
VStack {
|
||||
ForEach(post.media) { media in
|
||||
ForEach(post.post.media) { media in
|
||||
if !media.url.isEmpty {
|
||||
VStack {
|
||||
RemoteImage(type: .url(URL(string: media.url.encodeUrl()!)!), errorView: { error in
|
||||
|
@ -83,15 +99,15 @@ struct PostView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
AttributedText(post.getContent()).frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
|
||||
}
|
||||
|
||||
AttributedText(post.contentAttributed)
|
||||
}.frame(maxWidth: .infinity)
|
||||
}
|
||||
}
|
||||
|
||||
struct PostView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
return PostView(post: fooPost)
|
||||
return PostView(post: ParsedPostContainer(post: fooPost, contentAttributed: NSAttributedString()))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,11 +3,11 @@ import SwiftUI
|
|||
struct ProfileView: View {
|
||||
let username: String
|
||||
|
||||
@State var posts: [Post] = []
|
||||
@State var posts: [ParsedPostContainer] = []
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
List(posts) { post in
|
||||
List(posts, id: \.post.id) { post in
|
||||
PostView(post: post)
|
||||
}
|
||||
}.navigationBarTitle(username + "'s Feed").onAppear {
|
||||
|
@ -25,8 +25,15 @@ struct ProfileView: View {
|
|||
|
||||
let decodedPosts = try decoder.decode(Posts.self, from: jsonData)
|
||||
|
||||
var postArray = [ParsedPostContainer]()
|
||||
|
||||
for post in decodedPosts.posts {
|
||||
let container = ParsedPostContainer(post: post, contentAttributed: (try? NSAttributedString(data: post.getContent().data(using: .utf8)!, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil))!)
|
||||
postArray.append(container)
|
||||
}
|
||||
|
||||
DispatchQueue.main.sync {
|
||||
self.posts = decodedPosts.posts
|
||||
self.posts = postArray
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
|
@ -39,6 +46,7 @@ struct ProfileView: View {
|
|||
|
||||
struct ProfileView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
return ProfileView(username: "foobar", posts: [fooPost, fooPostReblog])
|
||||
//return ProfileView(username: "foobar", posts: [fooPost, fooPostReblog])
|
||||
Text("hello, world!")
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue