diff --git a/MobileFort/MobileFort/Common.swift b/MobileFort/MobileFort/Common.swift index 1044280..028f855 100644 --- a/MobileFort/MobileFort/Common.swift +++ b/MobileFort/MobileFort/Common.swift @@ -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, diff --git a/MobileFort/MobileFort/PostView.swift b/MobileFort/MobileFort/PostView.swift index d1be143..59cc806 100644 --- a/MobileFort/MobileFort/PostView.swift +++ b/MobileFort/MobileFort/PostView.swift @@ -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) -> 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 - - init(_ html: String) { - self.html = html - } - - public func makeUIView(context: UIViewRepresentableContext) -> UILabel { - let textView = UILabel() - textView.backgroundColor = .clear - textView.lineBreakMode = .byWordWrapping - textView.numberOfLines = 0 - textView.lineBreakMode = .byWordWrapping - textView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) +} - 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 - } - } - - return textView +struct AttributedText: View { + let html: NSAttributedString + let component: AttributedTextComponent + + @State var height: CGFloat = 0.0 + + init(_ html: NSAttributedString) { + self.html = html + self.component = AttributedTextComponent(html) + } + + var body: some View { + component.onAppear { + let label = UILabel() + + label.numberOfLines = 0 + label.lineBreakMode = .byWordWrapping + label.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + + 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())) } } diff --git a/MobileFort/MobileFort/ProfileView.swift b/MobileFort/MobileFort/ProfileView.swift index b6c5381..0b4295f 100644 --- a/MobileFort/MobileFort/ProfileView.swift +++ b/MobileFort/MobileFort/ProfileView.swift @@ -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!") } }